diff --git a/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx b/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx index 880b94736952c3775b9c83654f0ff44794503dad..d582aaba6951dedda3a219f009a622314584ce95 100644 --- a/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx +++ b/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx @@ -5,6 +5,7 @@ import { useViewModelNavigation } from "../../../view-model-designer/method-mana import MonacoEditor from '../../../monaco-editor/monaco-editor.component'; import './custom-class-editor.scss'; import { CustomClassEditorProps, customClassEditorProps } from "./custom-class-editor.props"; +import { useLocation } from "../../../../composition/use-location"; export default defineComponent({ name: 'FCustomClassEditor', @@ -27,10 +28,13 @@ export default defineComponent({ const monacoEditorRef = ref(); /** 自定义样式Dom */ formSchema.module.customClass ??= {}; + const { customClass } = formSchema.module; /** 当前编辑器内的样式代码 */ const currentClassCode = ref(); + const { getUrlParam } = useLocation(); + /** 视图模型页签的样式 */ const viewModelTabClass = computed(() => (viewModelTabId: string) => { const showActiceClass = activeViewModel.value?.id === viewModelTabId; @@ -100,8 +104,17 @@ export default defineComponent({ updateCurrentClassCode(); } + function customStyleFilePath() { + const {code:formCode, projectName} = formSchema.module; + const basePath = getUrlParam('id').split('/').slice(0,3).join('/'); + return `/apps${basePath}/web/${projectName}/${formCode}/${formCode}.css`; + } + onBeforeMount(() => { updateCurrentClassCode(); + if (!formSchema.module.customStyleFile) { + formSchema.module.customStyleFile = customStyleFilePath(); + } }); context.expose({ @@ -136,15 +149,19 @@ export default defineComponent({ return (
- +
{renderViewModelNavgation()}
- +
+
+ 部署路径: + +
); }; diff --git a/packages/designer/src/components/components/form-designer/form-designer.component.tsx b/packages/designer/src/components/components/form-designer/form-designer.component.tsx index 3d34615e25327b68aa6fe08ead2f414463d898e8..c1db9972b987a192179551eb772589bd6c349f9c 100644 --- a/packages/designer/src/components/components/form-designer/form-designer.component.tsx +++ b/packages/designer/src/components/components/form-designer/form-designer.component.tsx @@ -135,7 +135,7 @@ export default defineComponent({ const customClassEditorClass = computed(() => { return { - 'pl-2 pr-2 d-flex': true, + 'pl-2 pr-2 d-flex ml-2': true, 'f-designer-view-tabs-item': true, 'active': activeDesignerView.value === 'customClassEditor' }; @@ -287,8 +287,8 @@ export default defineComponent({ class={formDesignerViewClass.value}>可视化设计器
onChangeDesignerView('formDesignerCode')} class={formDesignerCodeViewClass.value}>设计时代码
- {/*
onChangeDesignerView('customClassEditor')} - class={customClassEditorClass.value}>自定义样式
*/} + {
onChangeDesignerView('customClassEditor')} + class={customClassEditorClass.value}>自定义样式
} diff --git a/packages/designer/src/components/types/metadata.ts b/packages/designer/src/components/types/metadata.ts index 9bdb4b9737c0930d701ba07becc5560c1e4a3ab2..63c47cd679e031aadf98e0ea91fc2455de01fa01 100644 --- a/packages/designer/src/components/types/metadata.ts +++ b/packages/designer/src/components/types/metadata.ts @@ -86,6 +86,8 @@ export interface FormMetaDataModule { // 自定义样式 customClass: any; + customStyleFile?: string; + // 外部模块声明 extraImports: Array<{ name: string; path: string }>; diff --git a/packages/farris-theme/theme/components/datepicker.scss b/packages/farris-theme/theme/components/datepicker.scss index ed707613a30cc58769422a213158fb85426b0c1d..d458f7ce301b1bf8b80ab120f1ad8b6adb021fb5 100644 --- a/packages/farris-theme/theme/components/datepicker.scss +++ b/packages/farris-theme/theme/components/datepicker.scss @@ -230,7 +230,7 @@ } &.f-datepicker-weeknbr { - color: f-text-05; + color: var(--f-text-05); } // &.f-datepicker-range-color { diff --git a/packages/farris-theme/theme/components/time-picker.scss b/packages/farris-theme/theme/components/time-picker.scss index ea482790518a085a70c421048563a70dab3228c6..fbd6fabf8df4eb11c9a4dcd67d2e01b3b3c5a2bc 100644 --- a/packages/farris-theme/theme/components/time-picker.scss +++ b/packages/farris-theme/theme/components/time-picker.scss @@ -170,7 +170,6 @@ ul { width: 100%; - max-height: 206px; margin: 0; padding: 0; list-style: none; @@ -193,7 +192,7 @@ &:last-child::after { display: block; - height: 202px; + height: 206px; content: ''; } diff --git a/packages/ui-vue/components/button-edit/src/button-edit.component.tsx b/packages/ui-vue/components/button-edit/src/button-edit.component.tsx index 5c4fc26fbc60df6845eff3bb9ebfb14c20e8fc04..085c82e69e3e393bbfd99e43ed4032f0d8c23c06 100644 --- a/packages/ui-vue/components/button-edit/src/button-edit.component.tsx +++ b/packages/ui-vue/components/button-edit/src/button-edit.component.tsx @@ -131,6 +131,7 @@ export default defineComponent({ const renderPopupContent = getPopupRender(props, context, popupComposition); const componentInstance = { + displayText, commitValue, elementRef: buttonEditRef, hidePopup, @@ -172,6 +173,7 @@ export default defineComponent({
+ { context.slots.precontent?.() } {renderEditor()} {renderButtonGroup()}
diff --git a/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx b/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx index 6bb562017360229f7f1752737ea10676f21ece70..89de18f29299dfcd6be0ff573364c51063d7bb07 100644 --- a/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx +++ b/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx @@ -1,6 +1,7 @@ import { ref, SetupContext } from "vue"; import { ButtonEditProps } from "../button-edit.props"; import { UseButton, UseClear } from "../composition/types"; +import { debounce } from "lodash-es"; export default function ( props: ButtonEditProps, @@ -12,6 +13,9 @@ export default function ( const { enableClearButton, showClearButton, onClearValue } = useClearComposition; const buttonHandleElement = ref(); + const onClickAppendButton = debounce(($event) => { + onClickButton($event); + }, props.buttonBehavior === 'Modal'? 0: 200); return { renderButtonGroup: () => { @@ -24,12 +28,12 @@ export default function ( )} { context.slots.buttonContent ? {context.slots.buttonContent()} : props.buttonContent ? onClickAppendButton($event)} onMouseenter={onMouseEnterButton} onMouseleave={onMouseLeaveButton} > : null }
diff --git a/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx b/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx index 97c879ab66062653ad07aafc67246fb0729bfcb1..325d1eac799d4280665b51aaa1ad1b2fbdd9b564 100644 --- a/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx +++ b/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx @@ -21,6 +21,24 @@ export default function ( } }); + const isComposing = ref(false); + const compositionstart = (event) => { + event.preventDefault(); + isComposing.value = true; + }; + + const compositionend = (event) => { + event.preventDefault(); + isComposing.value = false; + onInput(event); + }; + + function onValueChanged($event) { + if (!isComposing.value) { + onInput($event); + } + } + return () => { return ; }; } diff --git a/packages/ui-vue/components/button-edit/src/composition/use-clear.ts b/packages/ui-vue/components/button-edit/src/composition/use-clear.ts index 90e9470c48a55cb67621f9d5374ae41f4c060f46..e9cfc159d16fdee81c2ab386fe62ae575f671645 100644 --- a/packages/ui-vue/components/button-edit/src/composition/use-clear.ts +++ b/packages/ui-vue/components/button-edit/src/composition/use-clear.ts @@ -21,7 +21,7 @@ export function useClear( props: ButtonEditProps, context: SetupContext, modelValue: Ref, - hasFocusedTextBox: ComputedRef, + hasFocusedTextBox: Ref, displayText: Ref, useTextBoxCompostion: UseTextBox ): UseClear { @@ -44,6 +44,7 @@ export function useClear( $event.stopPropagation(); if (flag1 || flag2) { changeTextBoxValue(''); + displayText.value = ''; toggleClearIcon(!showClearButton.value); context.emit('clear'); } @@ -66,6 +67,7 @@ export function useClear( if (!enableClearButton.value) { return; } + toggleClearIcon(false); } diff --git a/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts b/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts index 91a54ed85e9ffdbc463b49650377c43b11c9482d..501eb2f49316070642700741e171c60028d7f459 100644 --- a/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts +++ b/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts @@ -108,7 +108,7 @@ export function useTextBox( } function onInput($event: Event) { - context.emit('input', ($event.target as HTMLInputElement).value); + context.emit('input', $event); const newValue = ($event.target as HTMLInputElement).value; displayText.value = newValue; if (modelValue.value !== newValue) { diff --git a/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx b/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx index 4881cf5a634c0d88be67a75cf6b3e1b16f0dd433..aca52805067beff4ce049d72f1a862d46c5ab74c 100644 --- a/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx +++ b/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx @@ -88,7 +88,6 @@ export default defineComponent({ maxLength={props.maxLength} tabIndex={props.tabIndex} enableTitle={props.enableTitle} - multiSelect={props.multiSelect} inputType={props.multiSelect ? 'tag' : 'text'} popupOnClick={true} v-model={displayText.value} @@ -101,6 +100,7 @@ export default defineComponent({ popupMinWidth={props.minPanelWidth}> {showPopover.value && (props.repositoryToken); @@ -81,6 +88,7 @@ export default defineComponent({ onSelectionChange={onSelectionChange} columnOption={{ fitColumns: true, fitMode: 'expand' }} rowOption={rowOption} + selection={selectOptions} rowNumber={{enable:false}} > diff --git a/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json b/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json index a98ff0ea73afe28fb9ccbd6fddbccb9d6c581a78..4eddbe962b7846f3e41d5c79551f5303b24de309 100644 --- a/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json +++ b/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json @@ -132,6 +132,11 @@ "description": "", "type": "string", "default": "id" + }, + "multiSelect": { + "description": "", + "type": "boolean", + "default": false } }, "required": [ diff --git a/packages/ui-vue/components/component/src/component.component.tsx b/packages/ui-vue/components/component/src/component.component.tsx index 079d6a38cff4a4e50d9d3ed76722ab94908ac6cd..4f080d68e86faaa661b5ee36f91220955bdbc04e 100644 --- a/packages/ui-vue/components/component/src/component.component.tsx +++ b/packages/ui-vue/components/component/src/component.component.tsx @@ -1,4 +1,4 @@ -import { SetupContext, defineComponent, onBeforeMount, onMounted } from 'vue'; +import { SetupContext, defineComponent, onBeforeMount, onMounted, ref } from 'vue'; import { ComponentPropsType, componentProps } from './component.props'; export default defineComponent({ @@ -6,14 +6,18 @@ export default defineComponent({ props: componentProps, emits: ['init', 'afterViewInit'], setup(props: ComponentPropsType, context) { + const elementRef = ref(); onBeforeMount(() => { context.emit('init', props.id); }); onMounted(() => { + if (elementRef.value && props.code) { + elementRef.value.setAttribute('scope-'+ props.code.toLowerCase(), ''); + } context.emit('afterViewInit', props.id); }); return () => { - return
{context.slots.default && context.slots.default()}
; + return
{context.slots.default && context.slots.default()}
; }; } }); diff --git a/packages/ui-vue/components/component/src/component.props.ts b/packages/ui-vue/components/component/src/component.props.ts index d681b0fcf664272e269ae89516ce1076abe8db78..d8db90857fa2a03a8e0bc589a8458ef83f6432cd 100644 --- a/packages/ui-vue/components/component/src/component.props.ts +++ b/packages/ui-vue/components/component/src/component.props.ts @@ -10,7 +10,8 @@ export const componentProps = { customClass: { type: String, default: '' }, customStyle: { type: String, default: '' }, componentType: { type: String, default: '' }, - formColumns: { type: Number, default: 4 } + formColumns: { type: Number, default: 4 }, + code: { type: String, default: '' } } as Record; export type ComponentPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/components.ts b/packages/ui-vue/components/components.ts index 2464a1cffd50733a6105a15036f24a7375607e35..14d7226db1affdb5641cbd056000ae7610312b14 100644 --- a/packages/ui-vue/components/components.ts +++ b/packages/ui-vue/components/components.ts @@ -53,7 +53,7 @@ export { default as FLayout } from './layout'; export type { LayoutProps } from './layout'; export { default as FLoading, FLoadingService } from './loading'; export type { LoadingProps } from './loading'; -export { default as FModal, F_MODAL_SERVICE_TOKEN, FModalService } from './modal'; +export { default as FModal, FModalService, F_MODAL_SERVICE_TOKEN } from './modal'; export type { ModalProps } from './modal'; export { default as FMessageBox, FMessageBoxService } from './message-box'; export type { MessageBoxProps, MessageType } from './message-box'; diff --git a/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx b/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx index 9346b013b81afbf117f29f7360a3574521f1e90b..aa516b6fa978c348b882dc205d83017802ec7a22 100644 --- a/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx @@ -22,169 +22,183 @@ export default defineComponent({ props: calendarNavbarProps, emits: ['clickMonth', 'clickYear', 'prePage', 'preRecord', 'nextRecord', 'nextPage'] as (string[] & ThisType) | undefined, setup(props: CalendarNavbarProps, context: SetupContext) { - return () => { - const ariaLabelPrevMonth = ref(props.ariaLabelPrevMonth); - const ariaLabelNextMonth = ref(props.ariaLabelNextMonth); - const dateFormat = ref(props.dateFormat); - const disablePrePageButton = ref(props.disablePrePage); - const disablePreRecordButton = ref(props.disablePreRecord); - const disableNextRecordButton = ref(props.disableNextRecord); - const disableNextPageButton = ref(props.disableNextPage); - const activeMonth = ref(props.activeMonth as ActiveMonth); - const years = ref(props.years); - const selectingMonth = ref(props.selectingMonth); - const selectingYear = ref(props.selectingYear); - const selectMode = ref(props.selectMode); - - const yearSelector = ref(true); - const monthSelector = ref(true); - - watch( - () => props.activeMonth, - () => { - activeMonth.value = { - month: props.activeMonth?.month, - year: props.activeMonth?.year, - displayTextOfMonth: props.activeMonth?.displayTextOfMonth, - displayTextOfYear: props.activeMonth?.displayTextOfMonth - }; - } - ); - - const navbarClass = computed(() => { - const classObject = { - 'f-datepicker-header': true, - monthYearSelBarBorder: selectingMonth.value || selectingYear.value - } as Record; - return classObject; - }); - - const prePageButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disablePrePageButton.value - } as Record; - return classObject; - }); - - const shouldShowNavRecordButton = computed(() => { - return !selectingMonth.value && !selectingYear.value; - }); - - const preRecordButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disablePreRecordButton.value - } as Record; - return classObject; - }); - - const nextRecordButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disableNextRecordButton.value - } as Record; - return classObject; - }); - - const nextPageButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disableNextPageButton.value - } as Record; - return classObject; - }); - - const navbarSelectYearButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-yearLabel': yearSelector.value, - 'f-datepicker-labelBtnNotEdit': !yearSelector.value - } as Record; - return classObject; - }); - - const navbarSelectMonthButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-monthLabel': monthSelector.value, - 'f-datepicker-labelBtnNotEdit': !monthSelector.value - } as Record; - return classObject; - }); - - const formateType = computed(() => { - const yIndex = dateFormat.value ? dateFormat.value.indexOf('yyyy') : 0; - const mIndex = dateFormat.value ? dateFormat.value.indexOf('MM') : 0; - return yIndex > mIndex ? `${'MM'}-${'yyyy'}` : `${'yyyy'}-${'MM'}`; - }); - - function navigateToPreviousPage($event: MouseEvent) { - $event.stopPropagation(); - context.emit('prePage'); - } - - function navigateToPreviousRecord($event: MouseEvent) { - $event.stopPropagation(); - context.emit('preRecord'); - } - - function onClearActiveYear($event: MouseEvent) { - $event.stopPropagation(); - context.emit('clickYear'); - } - - function onClickActiveMonth($event: MouseEvent) { - $event.stopPropagation(); - context.emit('clickMonth'); - } - - function navigateToNextRecord($event: MouseEvent) { - $event.stopPropagation(); - context.emit('nextRecord'); - } - function navigateToNextPage($event: MouseEvent) { - $event.stopPropagation(); - context.emit('nextPage'); + const ariaLabelPrevMonth = ref(props.ariaLabelPrevMonth); + const ariaLabelNextMonth = ref(props.ariaLabelNextMonth); + const dateFormat = ref(props.dateFormat); + const disablePrePageButton = ref(props.disablePrePage); + const disablePreRecordButton = ref(props.disablePreRecord); + const disableNextRecordButton = ref(props.disableNextRecord); + const disableNextPageButton = ref(props.disableNextPage); + const activeMonth = ref(props.activeMonth as ActiveMonth); + const years = ref(props.years); + const selectingMonth = ref(props.selectingMonth); + const selectingYear = ref(props.selectingYear); + const selectMode = ref(props.selectMode); + + const yearSelector = ref(true); + const monthSelector = ref(true); + + + watch(() => props.selectingMonth, (newValue, oldValue) => { + selectingMonth.value = newValue; + }); + + watch(() => props.selectingYear, (newValue, oldValue) => { + selectingYear.value = newValue; + }); + + watch(() => props.years, (newValue, oldValue) => { + years.value = newValue; + }); + + watch( + () => props.activeMonth, + () => { + activeMonth.value = { + month: props.activeMonth?.month, + year: props.activeMonth?.year, + displayTextOfMonth: props.activeMonth?.displayTextOfMonth, + displayTextOfYear: props.activeMonth?.displayTextOfYear + }; } + ); + + const navbarClass = computed(() => { + const classObject = { + 'f-datepicker-header': true, + monthYearSelBarBorder: selectingMonth.value || selectingYear.value + } as Record; + return classObject; + }); + + const prePageButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disablePrePageButton.value + } as Record; + return classObject; + }); + + const shouldShowNavRecordButton = computed(() => { + return !selectingMonth.value && !selectingYear.value; + }); + + const preRecordButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disablePreRecordButton.value + } as Record; + return classObject; + }); + + const nextRecordButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disableNextRecordButton.value + } as Record; + return classObject; + }); + + const nextPageButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disableNextPageButton.value + } as Record; + return classObject; + }); + + const navbarSelectYearButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-yearLabel': yearSelector.value, + 'f-datepicker-labelBtnNotEdit': !yearSelector.value + } as Record; + return classObject; + }); + + const navbarSelectMonthButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-monthLabel': monthSelector.value, + 'f-datepicker-labelBtnNotEdit': !monthSelector.value + } as Record; + return classObject; + }); + + const formateType = computed(() => { + const yIndex = dateFormat.value ? dateFormat.value.indexOf('yyyy') : 0; + const mIndex = dateFormat.value ? dateFormat.value.indexOf('MM') : 0; + return yIndex > mIndex ? `${'MM'}-${'yyyy'}` : `${'yyyy'}-${'MM'}`; + }); + + function navigateToPreviousPage($event: MouseEvent) { + $event.stopPropagation(); + context.emit('prePage'); + } + + function navigateToPreviousRecord($event: MouseEvent) { + $event.stopPropagation(); + context.emit('preRecord'); + } + + function onClearActiveYear($event: MouseEvent) { + $event.stopPropagation(); + context.emit('clickYear'); + } + + function onClickActiveMonth($event: MouseEvent) { + $event.stopPropagation(); + context.emit('clickMonth'); + } + + function navigateToNextRecord($event: MouseEvent) { + $event.stopPropagation(); + context.emit('nextRecord'); + } + + function navigateToNextPage($event: MouseEvent) { + $event.stopPropagation(); + context.emit('nextPage'); + } + + function renderSelectYearButton() { + return ( + + ); + } - function renderSelectYearButton() { - return ( + function renderSelectMonthButton() { + return ( + !selectingYear.value && + selectMode.value !== 'month' && ( - ); - } - - function renderSelectMonthButton() { - return ( - !selectingYear.value && - selectMode.value !== 'month' && ( - - ) - ); - } + ) + ); + } + return () => { return (
diff --git a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx index 6e1c6d22e5c27689e2cb5e7ccbe9623e58b8add5..9c67836b6f5fc84f3ee3f08747e4dab684048d2c 100644 --- a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { computed, defineComponent, ref, SetupContext, watch } from "vue"; +import { computed, defineComponent, ref, SetupContext, watch, watchEffect } from "vue"; import { DatePickerCalendarProps, datePickerCalendarProps } from "./calendar.props"; import { DateObject, MonthTag, Period, SelectMode } from "../../types/common"; import { CalendarWeekItem, CalenderDayItem } from "../../types/calendar"; @@ -30,31 +30,35 @@ export default defineComponent({ const enableKeyboadNavigate = ref(props.enableKeyboadNavigate); const enableMarkCurrent = ref(props.enableMarkCurrent); const enablePeriod = ref(props.enablePeriod); - const firstDayOfTheWeek = ref(props.firstDayOfTheWeek); const selected = ref(props.selected); const selectedPeriod = ref(props.selectedPeriod); const selectedWeek = ref(props.selectedWeek); const selectMode = ref(props.selectMode as SelectMode); const showWeekNumber = ref(props.showWeekNumber); - const weekTitle = ref(props.weekTitle); - - const shouldShowWeekTitle = computed(() => { - return showWeekNumber.value && firstDayOfTheWeek.value === 'Mon'; - }); - + const weekTitle = ref(props.weekTitle || '周'); + watch(() => props.dates, () => { dates.value = props.dates; }); + watch(() => props.selected, () => { selected.value = props.selected; }); - + watch(() => props.enablePeriod, (newValue, oldValue) => { if (newValue !== oldValue) { enablePeriod.value = newValue; } }); + watch(() => props.selectedPeriod, (newValue) => { + selectedPeriod.value = newValue; + }); + + watch(() => props.selectedWeek, (newValue) => { + selectedWeek.value = newValue; + }); + const { equal, inPeriod, isInitializedDate, equalOrEarlier, isPoint } = useCompare(); const { getKeyCodeFromEvent } = useEvent(); @@ -69,16 +73,29 @@ export default defineComponent({ return classObject; }; - const shouldShowWeekNumber = computed(() => { - return showWeekNumber.value && firstDayOfTheWeek.value === 'Mon'; - }); - + function isDateInPeriod(day: DateObject) { return inPeriod(day, selectedPeriod.value); } function isDateRangeBeginOrEndSame(day: DateObject) { - return !!selectedPeriod.value && isPoint(selectedPeriod.value, day); + if (!selectedPeriod.value) { + return false; + } + const selectedDateRange = { + from: { + year: selectedPeriod.value.from.year, + month: selectedPeriod.value.from.month, + day: selectedPeriod.value.from.day + }, + to: { + year: selectedPeriod.value.to.year, + month: selectedPeriod.value.to.month, + day: selectedPeriod.value.to.day + } + }; + + return !!selectedPeriod.value && isPoint(selectedDateRange, day); } function isDateSame(day: DateObject) { @@ -90,11 +107,8 @@ export default defineComponent({ const dayContainerClass = (currentDay: CalenderDayItem, weekIndex: number, dayIndex: number) => { const showDateRange = selectMode.value !== 'week' && currentDay.tag === MonthTag.current && - ( - ( - enablePeriod.value && isDateInPeriod(currentDay.date) && !isDateRangeBeginOrEndSame(currentDay.date) - ) || currentDay.range - ); + ((enablePeriod.value && isDateInPeriod(currentDay.date) && !isDateRangeBeginOrEndSame(currentDay.date)) || currentDay.range); + const notInCurrentMonth = currentDay.tag === MonthTag.previous || currentDay.tag === MonthTag.next; const classObject = { 'f-datepicker-range': showDateRange, @@ -109,10 +123,7 @@ export default defineComponent({ const dayClass = (currentDay: CalenderDayItem) => { const isSelected = selectMode.value !== 'week' && currentDay.tag === MonthTag.current && - ((!enablePeriod.value && - isDateSame(currentDay.date)) || - (enablePeriod.value && - isDateRangeBeginOrEndSame(currentDay.date))); + ((!enablePeriod.value && isDateSame(currentDay.date)) || (enablePeriod.value && isDateRangeBeginOrEndSame(currentDay.date))); const shouldMarkCurrentDay = currentDay.isCurrent && enableMarkCurrent.value; const shouldHight = currentDay.highlight && @@ -163,13 +174,14 @@ export default defineComponent({ function onMouseEnter(target: CalenderDayItem) { if (selectedPeriod.value && isInitializedDate(selectedPeriod.value.from) && - !isInitializedDate(selectedPeriod.value.to) + (!isInitializedDate(selectedPeriod.value.to) || JSON.stringify(selectedPeriod.value.from) === JSON.stringify(selectedPeriod.value.to)) ) { + const { from } = selectedPeriod.value; dates.value.forEach((week: CalendarWeekItem) => { week.days.forEach((item: CalenderDayItem) => { - item.range = !!selectedPeriod.value && ( - (equalOrEarlier(selectedPeriod.value.from, item.date) && equalOrEarlier(item.date, target.date)) || - (equalOrEarlier(item.date, selectedPeriod.value.from) && equalOrEarlier(target.date, item.date)) + item.range = ( + (equalOrEarlier(from, item.date) && equalOrEarlier(item.date, target.date)) || + (equalOrEarlier(item.date, from) && equalOrEarlier(target.date, item.date)) ); }); }); @@ -193,7 +205,7 @@ export default defineComponent({ - {shouldShowWeekTitle.value && + {showWeekNumber.value && @@ -216,7 +228,7 @@ export default defineComponent({ return ( onClickWeek(payload, week)}> { - shouldShowWeekNumber.value && ( + showWeekNumber.value && ( diff --git a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts index e27a940ec86421169a073b4f7b62e652db548df9..acac33ce993cf1a7175a5160bac350f97458ff47 100644 --- a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { CalendarWeekItem } from '../../types/calendar'; -import { weekDays } from '../../types/common'; +import { FirstDayOfTheWeek } from '../../types/common'; export const datePickerCalendarProps = { dates: { Type: Array, default: [] }, - daysInWeek: { Type: Array, default: weekDays }, + daysInWeek: { Type: Array, default: [] }, enableKeyboadNavigate: { Type: Boolean, default: true }, enableMarkCurrent: { Type: Boolean, default: true }, enablePeriod: { Type: Boolean, default: false }, - firstDayOfTheWeek: { Type: Boolean, default: 'Sun.' }, + firstDayOfTheWeek: { Type: String as PropType, default: FirstDayOfTheWeek.Sunday }, selected: { Type: Object, default: null }, selectedPeriod: { Type: Object, default: null }, selectedWeek: { Type: Object, default: null }, diff --git a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx index ebdad418a1d5b5c6befa4774d6c7a819b8755f3a..276b496a73c5709d2eb0024466ea081b2abe7848 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx @@ -25,24 +25,28 @@ import YearView from '../year/year.component'; import { useDisableDate } from "../../composition/use-disable-date"; import { UseDisableDate } from "../../composition/types"; import useCalendar from "../../composition/use-calendar"; -import { DateObject } from "../../types/common"; +import { DateObject, Period, WeekDays, WeekDayLabels } from "../../types/common"; import { useDate } from "../../composition/use-date"; -import { ActiveMonth } from "../../types/month"; +import { ActiveMonth, MonthViewItem } from "../../types/month"; import { useMonth } from "../../composition/use-month"; import { useYear } from "../../composition/use-year"; import { useDateFormat, formatTime, getTimeObject } from '@farris/ui-vue/components/common'; -import { CalenderDayItem } from "../../types/calendar"; import { useDisableMonth } from "../../composition/use-disable-month"; +import { CalendarWeekItem, CalenderDayItem } from "../../types/calendar"; +import { useNormalizeDate } from "../../composition/use-normalize-date"; +import { useCompare } from "../../composition/use-compare"; +import { useWeek } from "../../composition/use-week"; +import { useNumber } from "../../composition/use-number"; export default defineComponent({ name: 'FDateView', props: datePickerContainerProps, emits: ['datePicked', 'confirm'] as (string[] & ThisType) | undefined, setup(props: DatePickerContainerProps, context: SetupContext) { - const timePicker = ref(); const timeValue = ref(''); + const timeRangeValue = ref(''); /** 模式 */ const displayMode = ref(props.mode); const top = ref(props.top); @@ -60,12 +64,11 @@ export default defineComponent({ /** 存储格式 */ const valueFormat = ref(props.valueFormat); - const secondaryYears = ref([]); const selectingMonth = ref(false); - const selectingSecondaryMonth = ref(''); + const selectingSecondaryMonth = ref(false); const selectingYear = ref(false); - const selectingSecondaryYear = ref(''); - const selectTime = ref(); + const selectingSecondaryYear = ref(false); + const selectingTime = ref(false); const disablePrePage = ref(false); const disablePreRecord = ref(false); const disableNextRecord = ref(false); @@ -73,11 +76,9 @@ export default defineComponent({ const disableSecondaryPrePage = ref(false); const disableSecondaryPreRecord = ref(false); const disableSecondaryNextRecord = ref(false); - const disableSecondaryNextPage = ref(false); - /** 二级日期 */ - const secondaryDates = ref(props.secondaryDates); + const disableSecondaryNextPage = ref(false); /** 每日 */ - const daysInWeek = ref(props.daysInWeek); + const weekDays = ref([]); /** 是否允许键盘定位 */ const enableKeyboadNavigate = ref(props.enableKeyboadNavigate); /** 是否启用标记当前 */ @@ -94,12 +95,9 @@ export default defineComponent({ const dateValue = ref(props.value); /** 选择周 */ const selectedWeek = ref(props.selectedWeek); - /** 选择期限 */ - const selectedPeriod = ref(props.selectedPeriod); /** 每周标题 */ const weekTitle = ref(props.weekTitle); - /** 二级月份 */ - const secondaryMonths = ref(props.secondaryMonths); + /** 选择的月份 */ const selectedMonth = ref(props.selectedMonth); /** 最早年限 */ @@ -119,6 +117,27 @@ export default defineComponent({ /** 禁用周末 */ const disableWeekends = ref(props.disableWeekends); + const { getToday, getDateObject, getTimeValue, getEndTimeValue, emptyDate } = useDate(); + const { setNewDateRange, getActiveMonth, getTimeStr, getMonthAndYear } = useNormalizeDate(props); + const selectedPeriod = ref({ from: { ...emptyDate() }, to: { ...emptyDate()} }); + + + function getWeekDays() { + const dayLabels: any = []; + const firstDayIndex = WeekDays.indexOf(firstDayOfTheWeek.value); + if (firstDayIndex !== -1) { + let idx: number = firstDayIndex; + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < WeekDays.length; i++) { + dayLabels.push(WeekDayLabels[WeekDays[idx]]); + idx = WeekDays[idx] === 'sa' ? 0 : idx + 1; + } + } + return dayLabels; + } + + weekDays.value = getWeekDays(); + const refDisableDate: UseDisableDate = useDisableDate( minYear.value, maxYear.value, @@ -134,9 +153,13 @@ export default defineComponent({ const { isMonthDisabledByDisableSince, isMonthDisabledByDisableUntil } = useDisableMonth(); const { generateCalendar } = useCalendar(refDisableDate); - const { getToday, getDateObject } = useDate(); const { generateMonths, getNextMonth, getPreviousMonth, daysInMonth } = useMonth(); + const { getNowWeekTime } = useWeek(); + const { getWeekNumber } = useNumber(); + + const { generateYears } = useYear(); + const { equalOrEarlier, isInitializedDate, isDateEarlier } = useCompare(); const today = getToday(); const selectedDateObj = computed(() => { @@ -159,12 +182,15 @@ export default defineComponent({ month: selectedDateObj.value.month ? selectedDateObj.value.month : undefined, }; - const initSecondaryYear = today.month === 12 ? (today.year || 1) + 1 : today.year; - const initSecondaryMonth = (today.month || 1) < 12 ? (today.month || 1) + 1 : 1; + + const { endMonth } = getMonthAndYear(); + + const initSecondaryYear = endMonth.year; + const initSecondaryMonth = endMonth.month; const secondaryActiveMonth = ref({ year: initSecondaryYear, month: initSecondaryMonth, - displayTextOfMonth: nameOfMonths.value[today.month || '1'], + displayTextOfMonth: nameOfMonths.value[initSecondaryMonth], displayTextOfYear: `${initSecondaryYear}` }); @@ -182,6 +208,12 @@ export default defineComponent({ return weekItems; }); + const currentDates = ref(dates.value); + + watch(() => dates.value, (newValue, oldValue) => { + currentDates.value = newValue; + }); + const secondaryRealDates = computed(() => { return generateCalendar( secondaryActiveMonth.value.month, @@ -195,6 +227,10 @@ export default defineComponent({ showWeekNumber.value ); }); + const secondaryDates = ref(secondaryRealDates.value); + watch(() => secondaryRealDates.value, (newValue, oldValue) => { + secondaryDates.value = newValue; + }); const months = computed(() => { const monthViewItems = generateMonths( @@ -205,6 +241,11 @@ export default defineComponent({ ); return monthViewItems; }); + const currentMonths = ref(months.value); + watch(() => months.value, (newValue, oldValue) => { + currentMonths.value = newValue; + }); + const years = computed(() => { const yearViewItems = generateYears( @@ -217,6 +258,40 @@ export default defineComponent({ ); return yearViewItems; }); + const currentYears = ref(years.value); + watch(() => years.value, (newValue, oldValue) => { + currentYears.value = newValue; + }); + + + const secondYears = computed(() => { + return generateYears( + secondaryActiveMonth.value.year, + { year: secondaryActiveMonth.value.year, month: secondaryActiveMonth.value.month }, + minYear.value, + maxYear.value, + disableSince.value, + disableUntil.value + ); + }); + const secondaryYears = ref(secondYears.value); + watch(() => secondYears.value, (newValue, oldValue) => { + secondaryYears.value = newValue; + }); + + const secondMonths = computed(() => { + const monthViewItems = generateMonths( + nameOfMonths.value, + { year: secondaryActiveMonth.value.year, month: secondaryActiveMonth.value.month }, + disableSince.value, + disableUntil.value + ); + return monthViewItems; + }); + const secondaryMonths = ref(secondMonths.value); + watch(() => secondMonths.value, (newValue, oldValue) => { + secondaryMonths.value = newValue; + }); const containerClass = computed(() => { const classObject = { @@ -233,7 +308,7 @@ export default defineComponent({ } }); - function getWidth() { return enablePeriod.value ? '' : props.showTime ? '487px' : '287px'; }; + function getWidth() { return enablePeriod.value && selectMode.value !== 'week' ? '575px' : props.showTime && selectMode.value !== 'week' ? '487px' : '287px'; }; const containerStyle = computed(() => { const styleObject = { @@ -254,65 +329,153 @@ export default defineComponent({ $event.stopPropagation(); } - function navigateToPreviousPage($event: any, inPeriod: boolean) { - const previousYear = activeMonth.value.year - (selectingYear.value ? 10 : 1); - const previous = { - year: previousYear, - month: activeMonth.value.month, - displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], - displayTextOfYear: `${previousYear}` - }; + function navigateToPreviousYear($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + const previousYear = activeMonth.value.year - (selectingYear.value ? 10 : 1); + const previous = { + year: previousYear, + month: activeMonth.value.month, + displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], + displayTextOfYear: `${previousYear}` + }; + activeMonth.value = previous; + } else { + const previousYear = secondaryActiveMonth.value.year - (selectingSecondaryYear.value ? 10 : 1); + const previous = { + year: previousYear, + month: secondaryActiveMonth.value.month, + displayTextOfMonth: nameOfMonths.value[secondaryActiveMonth.value.month || '1'], + displayTextOfYear: `${previousYear}` + }; + secondaryActiveMonth.value = previous; + } + } - activeMonth.value = previous; + function navigateToPreviousMonth($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + const previousMonthDate = getPreviousMonth(activeMonth.value.month, activeMonth.value.year); + const previous = { + year: previousMonthDate.year || 1, + month: previousMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[previousMonthDate.month || '1'], + displayTextOfYear: `${previousMonthDate.year}` + }; + activeMonth.value = previous; + } else { + const previousMonthDate = getPreviousMonth(secondaryActiveMonth.value.month, secondaryActiveMonth.value.year); + const previous = { + year: previousMonthDate.year || 1, + month: previousMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[previousMonthDate.month || '1'], + displayTextOfYear: `${previousMonthDate.year}` + }; + secondaryActiveMonth.value = previous; + } } - function navigateToPreviousMonth($event: any, inPeriod: boolean) { - const previousMonthDate = getPreviousMonth(activeMonth.value.month, activeMonth.value.year); - const previous = { - year: previousMonthDate.year || 1, - month: previousMonthDate.month || 1, - displayTextOfMonth: nameOfMonths.value[previousMonthDate.month || '1'], - displayTextOfYear: `${previousMonthDate.year}` - }; + function navigateToNextMonth($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + const nextMonthDate = getNextMonth(activeMonth.value.month, activeMonth.value.year); + const next = { + year: nextMonthDate.year || 1, + month: nextMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[nextMonthDate.month || '1'], + displayTextOfYear: `${nextMonthDate.year}` + }; + activeMonth.value = next; + } else { + const nextMonthDate = getNextMonth(secondaryActiveMonth.value.month, secondaryActiveMonth.value.year); + const next = { + year: nextMonthDate.year || 1, + month: nextMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[nextMonthDate.month || '1'], + displayTextOfYear: `${nextMonthDate.year}` + }; + secondaryActiveMonth.value = next; + } + } - activeMonth.value = previous; + function navigateToNextYear($event: any, isSecondCalendar: boolean) { + if(!isSecondCalendar) { + let nextYear = activeMonth.value.year + 1; + if (selectingYear.value) { + nextYear = years.value[3][0].year + 2; + } + const next = { + year: nextYear, + month: activeMonth.value.month, + displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], + displayTextOfYear: `${nextYear}` + }; + activeMonth.value = next; + } else { + let nextYear = secondaryActiveMonth.value.year + 1; + if (selectingSecondaryYear.value) { + nextYear = years.value[3][0].year + 2; + } + const next = { + year: nextYear, + month: secondaryActiveMonth.value.month, + displayTextOfMonth: nameOfMonths.value[secondaryActiveMonth.value.month || '1'], + displayTextOfYear: `${nextYear}` + }; + secondaryActiveMonth.value = next; + } } - function navigateToNextMonth($event: any, inPeriod: boolean) { - const nextMonthDate = getNextMonth(activeMonth.value.month, activeMonth.value.year); - const next = { - year: nextMonthDate.year || 1, - month: nextMonthDate.month || 1, - displayTextOfMonth: nameOfMonths.value[nextMonthDate.month || '1'], - displayTextOfYear: `${nextMonthDate.year}` - }; + function navigateToMonthView(isSecondCalendar: boolean) { + if (!isSecondCalendar) { + selectingMonth.value = !selectingMonth.value; + selectingYear.value = false; + } else { + selectingSecondaryMonth.value = !selectingSecondaryMonth.value; + selectingSecondaryYear.value = false; + } + } - activeMonth.value = next; + function navigateToYearView(isSecondCalendar: boolean) { + if (!isSecondCalendar) { + selectingYear.value = selectMode.value === 'year' ? true : !selectingYear.value; + if (selectMode.value === 'month') { + selectingMonth.value = !selectingMonth.value; + } else { + selectingMonth.value = false; + } + } else { + selectingSecondaryYear.value = selectMode.value === 'year' ? true : !selectingSecondaryYear.value; + if (selectMode.value === 'month') { + selectingSecondaryMonth.value = !selectingSecondaryMonth.value; + } else { + selectingSecondaryMonth.value = false; + } + } } - function navigateToNextPage($event: any, inPeriod: boolean) { - let nextYear = activeMonth.value.year + 1; - if (selectingYear.value) { - nextYear = years.value[3][0].year + 2; + function navigateToTimeView() { + if (selectedPeriod.value) { + const { from, to } = selectedPeriod.value; + const beginIsInitializedDate = isInitializedDate(from); + const endIsInitializedDate = isInitializedDate(to); + + const now = new Date(); + if (!beginIsInitializedDate || !endIsInitializedDate) { + if (!beginIsInitializedDate) { + selectedPeriod.value.from = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }; + } + + selectedPeriod.value.to = { ...selectedPeriod.value.from }; + timeValue.value = getTimeStr(now); + timeRangeValue.value = getTimeStr(now); + } } - const next = { - year: nextYear, - month: activeMonth.value.month, - displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], - displayTextOfYear: `${nextYear}` - }; - activeMonth.value = next; - } - function navigateToMonthView(inPeriod: boolean) { - selectingMonth.value = !selectingMonth.value; + selectingTime.value = !selectingTime.value; + selectingMonth.value = false; selectingYear.value = false; - } - function navigateToYearView(inPeriod: boolean) { - selectingMonth.value = selectMode.value === 'month' ? !selectingMonth.value : false; - selectingYear.value = selectMode.value === 'year' ? true : !selectingYear.value; + selectingSecondaryYear.value = false; + selectingSecondaryMonth.value = false; } @@ -324,7 +487,7 @@ export default defineComponent({ 'disable-pre-record': disablePreRecord.value, 'disable-next-record': disableNextRecord.value, 'disable-next-page': disableNextPage.value, - 'years': years.value, + 'years': currentYears.value, 'selecting-month': selectingMonth.value, 'selecting-year': selectingYear.value, 'select-mode': selectMode.value @@ -346,23 +509,27 @@ export default defineComponent({ }; }); - function renderNavBar(inPeriod: boolean, calendarNavBarProps: any) { + function renderNavBar(calendarNavBarProps: any, isSecondCalendar: boolean) { return ( navigateToPreviousPage($event, inPeriod)} - onPreRecord={($event: any) => navigateToPreviousMonth($event, inPeriod)} - onNextRecord={($event: any) => navigateToNextMonth($event, inPeriod)} - onNextPage={($event: any) => navigateToNextPage($event, inPeriod)} - onClickMonth={($event: any) => navigateToMonthView(inPeriod)} - onClickYear={($event: any) => navigateToYearView(inPeriod)} + onPrePage={($event: any) => navigateToPreviousYear($event, isSecondCalendar)} + onPreRecord={($event: any) => navigateToPreviousMonth($event, isSecondCalendar)} + onNextRecord={($event: any) => navigateToNextMonth($event, isSecondCalendar)} + onNextPage={($event: any) => navigateToNextYear($event, isSecondCalendar)} + onClickMonth={($event: any) => navigateToMonthView(isSecondCalendar)} + onClickYear={($event: any) => navigateToYearView(isSecondCalendar)} > ); } const shouldShowCalendarView = computed(() => { - return (!selectingMonth.value && !selectingYear.value && !selectTime.value) || - (enablePeriod.value && selectMode.value !== 'week' && !selectingSecondaryMonth.value - && !selectingSecondaryYear.value && !selectTime.value); + return (isSecondCalendar: boolean) => { + if (isSecondCalendar) { + return selectMode.value !== 'week' && !selectingSecondaryMonth.value && !selectingSecondaryYear.value && !selectingTime.value; + } else { + return !selectingMonth.value && !selectingYear.value && !selectingTime.value; + } + }; }); const shouldShowMonthView = computed(() => { @@ -373,26 +540,26 @@ export default defineComponent({ return selectingYear.value && !selectingMonth.value; }); - watchEffect(() => { - if (shouldShowCalendarView.value) { - activeMonth.value = { - year: selectedMonth.value.year || 1, - month: selectedMonth.value.month || 1, - displayTextOfMonth: nameOfMonths.value[selectedMonth.value.month || '1'], - displayTextOfYear: `${selectedMonth.value.year}` - }; - } - }); + // watchEffect(() => { + // if (shouldShowCalendarView.value) { + // activeMonth.value = { + // year: selectedMonth.value.year || 1, + // month: selectedMonth.value.month || 1, + // displayTextOfMonth: nameOfMonths.value[selectedMonth.value.month || '1'], + // displayTextOfYear: `${selectedMonth.value.year}` + // }; + // } + // }); const primaryCalendarProps = computed(() => { return { - dates: dates.value, - daysInWeek: daysInWeek.value, + dates: currentDates.value, + daysInWeek: weekDays.value, enableKeyboadNavigate: enableKeyboadNavigate.value, enableMarkCurrent: enableMarkCurrent.value, enablePeriod: enablePeriod.value, firstDayOfTheWeek: firstDayOfTheWeek.value, - selected: selectedDate.value, + selected: (dateValue.value || JSON.stringify(today) !== JSON.stringify(selectedDate.value)) ? selectedDate.value : null, selectedPeriod: selectedPeriod.value, selectedWeek: selectedWeek.value, selectMode: selectMode.value, @@ -403,8 +570,8 @@ export default defineComponent({ const secondaryCalendarProps = computed(() => { return { - dates: secondaryRealDates.value, - daysInWeek: daysInWeek.value, + dates: secondaryDates.value, + daysInWeek: weekDays.value, enableKeyboadNavigate: enableKeyboadNavigate.value, enableMarkCurrent: enableMarkCurrent.value, enablePeriod: enablePeriod.value, @@ -417,28 +584,32 @@ export default defineComponent({ }; }); - function onClickDay($event: { event: Event, dayItem: CalenderDayItem }, isPeriod: boolean, type: string) { + function onClickDay($event: { event: Event, dayItem: CalenderDayItem }, isSecondCalendar: boolean) { const { event, dayItem } = $event; const currentDate = dayItem.date; if (dayItem.tag === 1) { - navigateToPreviousMonth(event, isPeriod); + navigateToPreviousMonth(event, isSecondCalendar); } else if (dayItem.tag === 3) { - navigateToNextMonth(event, isPeriod); + navigateToNextMonth(event, isSecondCalendar); } - if (!props.showTime) { - if (type === 'start') { - selectedDate.value = currentDate; - } else { - selectedSecondDate.value = currentDate; + // 区间 + if (enablePeriod.value) { + const { from, to, emit } = setNewDateRange(selectedPeriod.value, currentDate); + selectedPeriod.value = { from, to }; + if (!props.showTime && emit) { + context.emit('datePicked', { startDate: from, endDate: to }); } - context.emit('datePicked', currentDate); } else { - const { year, month, day } = currentDate; - selectedDate.value.year = year; - selectedDate.value.month = month; - selectedDate.value.day = day; + if (!props.showTime) { + context.emit('datePicked', currentDate); + } else { + const { year, month, day } = currentDate; + selectedDate.value.year = year; + selectedDate.value.month = month; + selectedDate.value.day = day; + } } const { year, month } = currentDate; @@ -449,33 +620,90 @@ export default defineComponent({ } - function onClickWeek($event: any) { } + function onClickWeek(weekData: CalendarWeekItem) { + const showTime = valueFormat.value.toLowerCase().indexOf('hh:mm') > -1; + selectedWeek.value = { numberInTheYear: weekData.numberInTheYear, year: weekData.year }; + + if (showTime) { + weekData.days[0].date.hour = 0; + weekData.days[0].date.minute = 0; + weekData.days[0].date.second = 0; + + weekData.days[6].date.hour = 23; + weekData.days[6].date.minute = 59; + weekData.days[6].date.second = 59; + } + + setNewDateRange(selectedPeriod.value, weekData.days[0].date); + const { from, to, emit } = setNewDateRange(selectedPeriod.value, weekData.days[6].date); + selectedPeriod.value = { from, to }; + if (emit) { + context.emit('datePicked', { startDate: from, endDate: to }); + } + } function onKeyDownCalendar($event: any) { } - function onMouseEnterCalendar($event: any, isPeriod: boolean) { } + function onMouseEnterCalendar(date: DateObject, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const w of dates.value) { + for (const day of w.days) { + day.range = + (equalOrEarlier(selectedPeriod.value.from, day.date) && equalOrEarlier(day.date, date)) || + (equalOrEarlier(day.date, selectedPeriod.value.from) && equalOrEarlier(date, day.date)); + } + } + + currentDates.value = cloneDeep(dates.value); + } else { + for (const w of secondaryRealDates.value) { + for (const day of w.days) { + day.range = + (equalOrEarlier(selectedPeriod.value.from, day.date) && equalOrEarlier(day.date, date)) || + (equalOrEarlier(day.date, selectedPeriod.value.from) && equalOrEarlier(date, day.date)); + } + } + secondaryDates.value = cloneDeep(secondaryRealDates.value); + } + } - function onMouseLeaveCalendar($event: any, isPeriod: boolean) { } + function onMouseLeaveCalendar($event: any, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const w of dates.value) { + for (const day of w.days) { + day.range = false; + } + } + currentDates.value = cloneDeep(dates.value); + } else { + for (const w of secondaryRealDates.value) { + for (const day of w.days) { + day.range = false; + } + } + secondaryDates.value = cloneDeep(secondaryRealDates.value); + } + } - function renderCalender(inPeriod: boolean, calendarProps: any, type = 'start') { - return shouldShowCalendarView.value && - onClickDay(payload, inPeriod, type)} + function renderCalender(calendarProps: any, isSecondCalendar: boolean) { + return onClickDay(payload, isSecondCalendar)} onClickWeek={(payload: any) => onClickWeek(payload)} onKeyDown={(payload: any) => onKeyDownCalendar(payload)} - onMouseEnter={(payload: any) => onMouseEnterCalendar(payload, inPeriod)} - onMouseLeave={(payload: any) => onMouseLeaveCalendar(payload, inPeriod)} + onMouseEnter={(payload: any) => onMouseEnterCalendar(payload, isSecondCalendar)} + onMouseLeave={(payload: any) => onMouseLeaveCalendar(payload, isSecondCalendar)} >; }; const monthProps = computed(() => { return { - months: months.value, + months: currentMonths.value, enableMarkCurrent: enableMarkCurrent.value, enableKeyboadNavigate: enableKeyboadNavigate.value, enablePeriod: enablePeriod.value, selected: selectedMonth.value, - selectedPeriod: selectedPeriod.value + selectedPeriod: selectedPeriod.value, + selectMode: selectMode.value }; }); @@ -490,36 +718,100 @@ export default defineComponent({ }; }); - function onClickMonth(currentMonth: DateObject, isPeriod: boolean) { - selectingMonth.value = false; - selectingYear.value = false; - selectedMonth.value = currentMonth; - activeMonth.value = { - month: currentMonth.month || 1, - displayTextOfMonth: nameOfMonths.value[currentMonth.month || '1'], - year: currentMonth.year || 1, - displayTextOfYear: `${currentMonth.year}` - }; + const selectRangeMonth = (currentMonth: DateObject) => { + const { from: begin, to: end } = selectedPeriod.value; + const isBeginMonthInitialized: boolean = isInitializedDate(begin); + const isEndMonthInitialized: boolean = isInitializedDate(end); + if (isBeginMonthInitialized && isEndMonthInitialized) { + // both already selected - set begin date and reset end date + selectedPeriod.value.from = { year: currentMonth.year, month: currentMonth.month }; + selectedPeriod.value.to = emptyDate(); + } else if(!isBeginMonthInitialized) { + selectedPeriod.value.from = { year: currentMonth.year, month: currentMonth.month }; + } else { + const firstDateEarlier: boolean = isDateEarlier({ year: currentMonth.year, month: currentMonth.month }, begin); + if (firstDateEarlier) { + const _date = selectedPeriod.value.from; + selectedPeriod.value.to = _date; + selectedPeriod.value.from = { year: currentMonth.year, month: currentMonth.month }; + } else { + selectedPeriod.value.to = { year: currentMonth.year, month: currentMonth.month }; + } - if (selectMode.value === 'month') { - const selectedMonth = cloneDeep(currentMonth); - selectedMonth.day = 1; - context.emit('datePicked', selectedMonth); + context.emit('datePicked', { startDate: selectedPeriod.value.from, endDate: selectedPeriod.value.to }); + } + } + + function onClickMonth(currentMonth: DateObject, isSecondCalendar: boolean) { + if(!isSecondCalendar) { + activeMonth.value = { + month: currentMonth.month || 1, + displayTextOfMonth: nameOfMonths.value[currentMonth.month || '1'], + year: currentMonth.year || 1, + displayTextOfYear: `${currentMonth.year}` + }; + if(selectMode.value === 'month') { + if (!enablePeriod.value) { + context.emit('datePicked', currentMonth); + } else { + selectRangeMonth(currentMonth); + } + } else { + selectingMonth.value = false; + selectingYear.value = false; + selectedMonth.value = currentMonth; + } + } else { + secondaryActiveMonth.value = { + month: currentMonth.month || 1, + displayTextOfMonth: nameOfMonths.value[currentMonth.month || '1'], + year: currentMonth.year || 1, + displayTextOfYear: `${currentMonth.year}` + }; + + if(selectMode.value === 'month') { + selectRangeMonth(currentMonth); + } else { + selectingSecondaryMonth.value = false; + selectingSecondaryYear.value = false; + } } } function onKeyDownMonthView($event: any) { } - function onMouseEnterMonthView($event: any, isPeriod: boolean) { } + function onMouseEnterMonthView(cell: MonthViewItem, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const item of months.value) { + for (const month of item) { + month.range = + (equalOrEarlier(selectedPeriod.value.from, month.date) && equalOrEarlier(month.date, cell.date)) || + (equalOrEarlier(month.date, selectedPeriod.value.from) && equalOrEarlier(cell.date, month.date)); + } + } + currentMonths.value = cloneDeep(months.value); + } else { + for (const item of secondMonths.value) { + for (const month of item) { + month.range = + (equalOrEarlier(selectedPeriod.value.from, month.date) && equalOrEarlier(month.date, cell.date)) || + (equalOrEarlier(month.date, selectedPeriod.value.from) && equalOrEarlier(cell.date, month.date)); + } + } - function onMouseLeaveMonthView($event: any, isPeriod: boolean) { } + secondaryMonths.value = cloneDeep(secondMonths.value); + } + } + + function onMouseLeaveMonthView($event: MonthViewItem, isSecondCalendar: boolean) { + } - function renderMonth(inPeriod: boolean, monthProps: any) { + function renderMonth(monthProps: any, isSecondCalendar: boolean) { return onClickMonth(payload, inPeriod)} + onClick={(payload: any) => onClickMonth(payload, isSecondCalendar)} onKeyDownMonthView={(payload: any) => onKeyDownMonthView(payload)} - onMouseEnterMonthView={(payload: any) => onMouseEnterMonthView(payload, inPeriod)} - onMouseLeaveMonthView={(payload: any) => onMouseLeaveMonthView(payload, inPeriod)} + onMouseEnter={(payload: any) => onMouseEnterMonthView(payload, isSecondCalendar)} + onMouseLeave={(payload: any) => onMouseLeaveMonthView(payload, isSecondCalendar)} >; } @@ -530,7 +822,8 @@ export default defineComponent({ enableMarkCurrent: enableMarkCurrent.value, enablePeriod: enablePeriod.value, selected: selectedDate.value, - selectedPeriod: selectedPeriod.value + selectedPeriod: selectedPeriod.value, + selectMode: selectMode.value }; }); @@ -545,42 +838,101 @@ export default defineComponent({ }; }); - function onClickYear(currentYear: DateObject, isPeriod: boolean) { - selectingMonth.value = true; - selectingYear.value = false; - activeMonth.value = { - month: activeMonth.value.month, - displayTextOfMonth: activeMonth.value.displayTextOfMonth, - year: currentYear.year || 1, - displayTextOfYear: `${currentYear.year}` - }; + function onClickYear(currentYear: DateObject, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + activeMonth.value = { + month: activeMonth.value.month, + displayTextOfMonth: activeMonth.value.displayTextOfMonth, + year: currentYear.year || 1, + displayTextOfYear: `${currentYear.year}` + }; - if (selectMode.value === 'year') { - const selectedYear = cloneDeep(currentYear); - selectedYear.month = 1; - selectedYear.day = 1; - context.emit('datePicked', selectedYear); + if (selectMode.value === 'year') { + if (!enablePeriod.value) { + context.emit('datePicked', currentYear); + } else { + selectRangeMonth(currentYear); + } + } else { + selectingMonth.value = true; + selectingYear.value = false; + } + + } else { + secondaryActiveMonth.value = { + month: secondaryActiveMonth.value.month || 1, + displayTextOfMonth: secondaryActiveMonth.value.displayTextOfMonth, + year: currentYear.year || 1, + displayTextOfYear: `${currentYear.year}` + }; + + if(selectMode.value === 'year') { + selectRangeMonth(currentYear); + } else { + selectingSecondaryMonth.value = true; + selectingSecondaryYear.value = false; + } } + } function onKeyDownYearView($event: any) { } - function onMouseEnterYearView($event: any, isPeriod: boolean) { } + function onMouseEnterYearView(cell: any, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const item of years.value) { + for (const year of item) { + year.range = + (equalOrEarlier(selectedPeriod.value.from, year.date) && + equalOrEarlier(year.date, cell.date)) || + (equalOrEarlier(year.date, selectedPeriod.value.from) && + equalOrEarlier(cell.date, year.date)); + } + } + currentYears.value = cloneDeep(years.value); + } else { + for (const item of secondYears.value) { + for (const year of item) { + year.range = + (equalOrEarlier(selectedPeriod.value.from, year.date) && + equalOrEarlier(year.date, cell.date)) || + (equalOrEarlier(year.date, selectedPeriod.value.from) && + equalOrEarlier(cell.date, year.date)); + } + } + secondaryYears.value = cloneDeep(secondYears.value); + } + } - function onMouseLeaveYearView($event: any, isPeriod: boolean) { } + function onMouseLeaveYearView($event: any, isSecondCalendar: boolean) { } function onConfirm() { - if (props.showTime) { - context.emit('confirm', selectedDate.value); - } else { - const resultDate = { startDate: {}, endDate: {} }; - if (selectedDate.value) { - resultDate.startDate = selectedDate.value; + // 区间 + if (enablePeriod.value && selectedPeriod.value) { + const isBeginEmptyDate = !isInitializedDate(selectedPeriod.value.from); + const isEndEmptyDate = !isInitializedDate(selectedPeriod.value.to); + + + if (isBeginEmptyDate || isEndEmptyDate) { + const now = new Date(); + if (isBeginEmptyDate) { + selectedPeriod.value.from = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }; + } + + selectedPeriod.value.to = { ...selectedPeriod.value.from }; } - if (selectedSecondDate.value) { - resultDate.endDate = selectedSecondDate.value; + + if (props.showTime) { + const { hour, minute, second } = getTimeValue(timeValue.value, true); + selectedPeriod.value.from = {...selectedPeriod.value.from, ...{ hour, minute, second } }; + + const { hour: endHour, minute: endMinute, second: endSecond } = getEndTimeValue(timeRangeValue.value); + selectedPeriod.value.to = {...selectedPeriod.value.to, ...{ hour: endHour, minute: endMinute, second: endSecond } }; } - context.emit('confirm', resultDate); + const { from, to } = selectedPeriod.value; + context.emit('datePicked', { startDate: from, endDate: to}); + } else { + context.emit('confirm', selectedDate.value); } } @@ -605,6 +957,12 @@ export default defineComponent({ }; selectedDate.value = cloneDeep(today); selectedMonth.value = { year: today.year, month: today.month }; + + onClickMonth(selectedDate.value, false); + + if (selectMode.value === 'month') { + context.emit('datePicked', selectedMonth.value); + } } function navigateToCurrentYear($event: MouseEvent) { @@ -615,22 +973,28 @@ export default defineComponent({ displayTextOfYear: `${today.year}` }; selectedDate.value = cloneDeep(today); + + onClickYear(selectedDate.value, false); + + if (selectMode.value === 'year') { + context.emit('datePicked', selectedDate.value); + } } const shouldShowSecondCalendar = computed(() => { return enablePeriod.value && selectMode.value !== 'week'; }); - const showConfirmButton = computed(() => (shouldShowCalendarView.value && shouldShowSecondCalendar.value) || props.showTime); + const showConfirmButton = computed(() => props.showTime && selectMode.value !== 'week'); - function renderYear(inPeriod: boolean, yearProps: any) { + function renderYear(yearProps: any, isSecondCalendar: boolean) { return onClickYear(payload, inPeriod)} + onClick={(payload: any) => onClickYear(payload, isSecondCalendar)} onKeyDownYearView={(payload: any) => onKeyDownYearView(payload)} - onClickPreRecord={(payload: any) => navigateToPreviousMonth(payload, inPeriod)} - onClickNextRecord={(payload: any) => navigateToNextMonth(payload, inPeriod)} - onMouseEnterYearView={(payload: any) => onMouseEnterYearView(payload, inPeriod)} - onMouseLeaveYearView={(payload: any) => onMouseLeaveYearView(payload, inPeriod)} + onClickPreRecord={(payload: any) => navigateToPreviousMonth(payload, isSecondCalendar)} + onClickNextRecord={(payload: any) => navigateToNextMonth(payload, isSecondCalendar)} + onMouseEnter={(payload: any) => onMouseEnterYearView(payload, isSecondCalendar)} + onMouseLeave={(payload: any) => onMouseLeaveYearView(payload, isSecondCalendar)} >; } @@ -652,8 +1016,53 @@ export default defineComponent({ } onMounted(() => { - if (props.showTime) { - formatDate(); + if (enablePeriod.value) { + const { dateRange, beginDateActiveMonth, endDateActiveMonth, beginTime, endTime, selectedWeekInfo} = getActiveMonth(dateValue.value); + selectedPeriod.value = dateRange; + if (beginDateActiveMonth) { + activeMonth.value = beginDateActiveMonth; + } + if (endDateActiveMonth) { + secondaryActiveMonth.value = endDateActiveMonth; + } + + if (beginTime) { + timeValue.value = beginTime; + } + + if (endTime) { + timeRangeValue.value = endTime; + } + + if (selectedWeekInfo) { + selectedWeek.value = selectedWeekInfo; + } + } else { + if (props.showTime) { + const dateAndTimeFormatList = valueFormat.value.split(' '); + const timeFormat = dateAndTimeFormatList[1] || 'HH:mm:ss'; + timeValue.value = formatTo(dateValue.value, timeFormat); + formatDate(); + } + } + + if (selectMode.value === 'month') { + navigateToMonthView(false); + if (enablePeriod.value) { + navigateToMonthView(true); + } + } else { + if (selectMode.value === 'year') { + navigateToYearView(false); + if (enablePeriod.value) { + const secondYear = secondaryActiveMonth.value.year; + if (currentYears.value.length && secondYear < currentYears.value[3][2].year && secondYear > currentYears.value[0][0].year) { + secondaryActiveMonth.value.year = secondYear + 10; + } + + navigateToYearView(true); + } + } } if (selectMode.value === 'month') { @@ -665,16 +1074,46 @@ export default defineComponent({ } }); - const onValueChangeHandler = (textValue: TimeValueText) => { - timeValue.value = textValue.text; - const { hour, minute, second } = getTimeObject(timeValue.value); - if (selectedDate.value) { - selectedDate.value.hour = hour || 0; - selectedDate.value.minute = minute || 0; - selectedDate.value.second = second || 0; + const onValueChangeHandler = (textValue: TimeValueText, isSecondCalendar = false) => { + const dateAndTimeFormatList = valueFormat.value.split(' '); + const timeFormat = dateAndTimeFormatList[1] || 'HH:mm:ss'; + const convertedDate = convertToDate(textValue.text, timeFormat); + const hour = convertedDate?.getHours() || 0; + const minutes = convertedDate?.getMinutes() || 0; + const seconds = convertedDate?.getSeconds() || 0; + + if (!isSecondCalendar) { + timeValue.value = textValue.text; + if (enablePeriod.value && selectedPeriod.value) { + selectedPeriod.value.from.hour = hour; + selectedPeriod.value.from.minute = minutes; + selectedPeriod.value.from.second = seconds; + } else { + if (selectedDate.value) { + selectedDate.value.hour = hour; + selectedDate.value.minute = minutes; + selectedDate.value.second = seconds; + } + } + + } else { + timeRangeValue.value = textValue.text; + if (enablePeriod.value && selectedPeriod.value) { + selectedPeriod.value.to.hour = hour; + selectedPeriod.value.to.minute = minutes; + selectedPeriod.value.to.second = seconds; + } } }; + function renderTimePicker(isSecondCalendar: boolean) { + return onValueChangeHandler(timeValue, isSecondCalendar)} + >; + } + const btnTodayStyle = computed(() => { return (date: any) => { const todayIsDisable = date.day == null || refDisableDate.isDisabledDate(props.showTime ? date : { year: date.year, month: date.month, day: date.day }); @@ -707,6 +1146,31 @@ export default defineComponent({ }; }); + function selectCurrentWeek() { + const { from: begin, to: end } = getNowWeekTime(new Date(), firstDayOfTheWeek.value); + const year = new Date().getFullYear(); + const weekNumber = getWeekNumber(begin); + const showTime = valueFormat.value.toLowerCase().indexOf('hh:mm') > -1; + selectedWeek.value = { numberInTheYear: weekNumber, year }; + + if (showTime) { + begin['hour'] = 0; + begin['minute'] = 0; + begin['second'] = 0; + + end['hour'] = 23; + end['minute'] = 59; + end['second'] = 59; + } + + setNewDateRange(selectedPeriod.value, begin); + const { from, to, emit } = setNewDateRange(selectedPeriod.value, end); + selectedPeriod.value = { from, to }; + if (emit) { + context.emit('datePicked', { startDate: from, endDate: to }); + } + } + return () => { return (
onClickContainer(payload)} >
- {renderNavBar(false, primaryCalendarNavBarProps.value)} - {renderCalender(false, primaryCalendarProps.value)} - {selectingMonth.value && renderMonth(false, monthProps.value)} - {selectingYear.value && renderYear(false, yearProps.value)} - + {renderNavBar(primaryCalendarNavBarProps.value, false)} + {shouldShowCalendarView.value(false) && renderCalender(primaryCalendarProps.value, false)} + {selectingMonth.value && renderMonth(monthProps.value, false)} + {selectingYear.value && renderYear(yearProps.value, false)} + {selectingTime.value && renderTimePicker(false)}
- {props.showTime &&
{timeValue.value}
-
} { - shouldShowSecondCalendar.value && -
- {renderNavBar(true, secondaryCalendarNavBarProps.value)} - {renderCalender(true, secondaryCalendarProps.value, 'end')} - {selectingSecondaryMonth.value && renderMonth(true, secondMonthProps.value)} - {selectingSecondaryYear.value && renderYear(true, secondYearProps.value)} + shouldShowSecondCalendar.value && selectMode.value !== 'week' && +
+ {renderNavBar(secondaryCalendarNavBarProps.value, true)} + {shouldShowCalendarView.value(true) && renderCalender(secondaryCalendarProps.value, true)} + {selectingSecondaryMonth.value && renderMonth(secondMonthProps.value, true)} + {selectingSecondaryYear.value && renderYear(secondYearProps.value, true)} + {selectingTime.value && renderTimePicker(true)}
} { showConfirmButton.value &&
- - 确定 - + {!enablePeriod.value && shouldShowCalendarView.value(false) && } + {!enablePeriod.value &&shouldShowMonthView.value && } + {!enablePeriod.value &&shouldShowYearView.value && } + {enablePeriod.value && selectMode.value === 'day' &&
+
+ } + 确定
+ } + {!enablePeriod.value && !showConfirmButton.value &&
+ {shouldShowCalendarView.value(false) && } + {shouldShowMonthView.value && } + {shouldShowYearView.value && } +
} + {enablePeriod.value && selectMode.value === 'week' &&
+ {shouldShowMonthView.value && } + {shouldShowYearView.value && } + {!shouldShowMonthView.value && !shouldShowYearView.value && } +
}
); }; diff --git a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts index 7e9205a40d21e96c948cec776b0e24dfbd83c24f..8a67621de271b7cac7dbbc72a325ebcdce3fc18d 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts @@ -17,7 +17,7 @@ import { ExtractPropTypes, PropType } from 'vue'; import { createPropsResolver } from '@farris/ui-vue/components/dynamic-resolver'; import { CalendarWeekItem } from '../../types/calendar'; -import { DateObject, Period, SelectMode, weekDays } from '../../types/common'; +import { DateObject, FirstDayOfTheWeek, Period, SelectMode, WeekDays } from '../../types/common'; import { defaultNameOfMonths, MonthViewItem } from '../../types/month'; import { YearViewItem } from '../../types/year'; import { schemaMapper } from '../../schema/schema-mapper'; @@ -39,7 +39,6 @@ export const datePickerContainerProps = { dateFormat: { type: String, default: 'yyyy-MM-dd' }, valueFormat: { type: String, default: 'yyyy-MM-dd' }, dates: { type: Array, default: [] }, - daysInWeek: { type: Array, default: weekDays }, /** 禁用日期 */ disableDates: { Type: Array, default: [] }, /** 禁用范围 */ @@ -57,7 +56,7 @@ export const datePickerContainerProps = { /** 是否启用标记当前 */ enableMarkCurrent: { type: Boolean, default: true }, /** 每周第一天 */ - firstDayOfTheWeek: { type: String, default: 'Sun.' }, + firstDayOfTheWeek: { type: String as PropType, default: FirstDayOfTheWeek.Sunday }, /** 高亮日期 */ highlightDates: { Type: Array, default: [] }, /** 是否高亮周六 */ @@ -84,8 +83,7 @@ export const datePickerContainerProps = { selectedSecondDate: { type: Object, default: null }, /** 选择的月份 */ selectedMonth: { type: Object, default: null }, - /** 选择期限 */ - selectedPeriod: { type: Object, default: null }, + /** 选择周 */ selectedWeek: { type: Object, default: null }, /** 选择方式 */ @@ -93,7 +91,7 @@ export const datePickerContainerProps = { /** 显示第几周 */ showWeekNumber: { type: Boolean, default: false }, /** 每周标题 */ - weekTitle: { type: String, default: 'Week' }, + weekTitle: { type: String, default: '周' }, /** 年份 */ years: { Type: Array>, default: [[]] }, /** 日期时间值 */ diff --git a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx index 60aac6437000302a5e321d1d94d3681f584b7316..e00c3c30e508ca5a55f962264e2ee6fa948908af 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx @@ -5,11 +5,12 @@ import { useDateFormat } from "@farris/ui-vue/components/common"; import { DateRangeProps, dateRangeProps } from "./date-range.props"; import FDatePickerContainer from '../date-picker-container/date-picker-container.component'; import { DateObject } from "../../types/common"; +import { useDate } from "../../composition/use-date"; export default defineComponent({ name: 'FDateRange', props: dateRangeProps, - emits: ['update:modelValue', 'confirm'] as (string[] & ThisType) | undefined, + emits: ['update:modelValue', 'confirm', 'clear'] as (string[] & ThisType) | undefined, setup(props: DateRangeProps, context) { const modelValue = ref(props.modelValue); /** 最晚年限 */ @@ -30,22 +31,22 @@ export default defineComponent({ const showWeekNumber = ref(props.showWeekNumber); /** 显示方式 */ const selectMode = ref(props.selectMode); - /** 是否显示时间 */ - // const displayTime = ref(props.displayTime); /** 禁用周末 */ const disableWeekends = ref(props.disableWeekends); /** 禁用特定日 */ const disableWeekdays = ref(props.disableWeekdays); /** 自...禁用 */ - const disableSince = ref([props.disableSince]); + const disableSince = ref(props.disableSince); /** 禁用至该范围 */ - const disableUntil = ref([props.disableUntil]); + const disableUntil = ref(props.disableUntil); /** 展示日期范围 */ const shouldPopupContent = ref(false); // const popoverRef = ref(); const dateRef = ref(); + const showClearButton = ref(false); + const { formatTo } = useDateFormat(); const startDisplayDate = computed(() => formatTo(modelValue.value.split('~')?.[0], props.displayFormat)); @@ -66,12 +67,21 @@ export default defineComponent({ 'input-group': true, // 'f-state-disable': disabled.value, 'f-state-editable': true, + 'align-items-center': true // 'f-state-readonly': readonly.value, // 'f-state-focus': hasFocused.value }; return classObject; }); + const clearButtonStyles = computed(() => { + const styles: Record = { width: '22px', height: '22px'}; + if (!showClearButton.value) { + styles.visibility = 'hidden'; + } + return styles; + }); + const showDateRangePanel = () => { popoverRef.value?.show(dateRef.value); }; @@ -95,8 +105,28 @@ export default defineComponent({ const onConfirm = (dateValue: any) => { const { startDate, endDate } = dateValue; - const startDateValueString = `${startDate.year}-${startDate.month}-${startDate.day}`; - const endDateValueString = `${endDate.year}-${endDate.month}-${endDate.day}`; + let startDateValueString = `${startDate.year}-${startDate.month}-${startDate.day}`; + let endDateValueString = `${endDate.year}-${endDate.month}-${endDate.day}`; + + if(props.showTime){ + const { hour, minute, second } = startDate; + startDateValueString = `${startDateValueString} ${hour || 0}:${minute || 0}:${second || 0}`; + + const { hour: endHour, minute: endMinute, second: endSecond } = endDate; + endDateValueString = `${endDateValueString} ${endHour || 0}:${endMinute || 0}:${endSecond || 0}`; + } + + if (selectMode.value === 'month') { + startDateValueString = `${startDate.year}-${startDate.month}`; + endDateValueString = `${endDate.year}-${endDate.month}`; + } + + if (selectMode.value === 'year') { + startDateValueString = `${startDate.year}`; + endDateValueString = `${endDate.year}`; + } + + const formatStartDate = formatTo(startDateValueString, props.valueFormat); const formatEndDate = formatTo(endDateValueString, props.valueFormat); const resultDate = formatStartDate + '~' + formatEndDate; @@ -107,29 +137,55 @@ export default defineComponent({ }; const onDatePicked = (dateValue: any) => { + onConfirm(dateValue); + }; + const onClear = () => { + modelValue.value = ''; + closeCalendarPanel(); + context.emit('update:modeValue', ''); + context.emit('clear'); }; + onMounted(() => { + + }); + + function onMouseEnter() { + showClearButton.value = !!startDisplayDate.value; + } + + function onMouseLeave() { + showClearButton.value = false; + } + return () => { return <>
-
- - - +
+
+ + + +
+ {props.enableClear && + } '} onClick={onClickButton} - // onMouseenter={onMouseEnterButton} onMouseleave={onMouseLeaveButton} >
@@ -137,13 +193,9 @@ export default defineComponent({ {shouldPopupContent.value && - {context.slots.default?.()} onDatePicked(dateValue)} onConfirm={(dateValue: string) => onConfirm(dateValue)} diff --git a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts index 00cd73951452a56f7be01ce276aeb99e35d8ac76..7b2d92c2dc3ccd45fc63d63194abf5aa5e6f01b8 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts @@ -3,20 +3,6 @@ import { datePickerProps } from '../../..'; export const dateRangeProps = { ...datePickerProps, - /** 显示第二个日期输入框 */ - showEndDate: { type: Boolean, default: true }, - /** 显示中间图标 */ - showMiddleIcon: { type: Boolean, default: true }, - /** 开始日期 */ - startDateValue: { type: String }, - /** 结束日期 */ - endDateValue: { type: String }, - /** 绑定值 */ - modelValue: { type: String, default: '' }, - /** 展示日期范围面板 */ - showPeriod: { type: Boolean, default: true }, - /** 启用删除 */ - enableClear: {type: Boolean, default: true}, } as Record; export type DateRangeProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx b/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx index 19a27991eab06ecd7ef6a3a3784f432c6e7ab6eb..09f99ea972e668462c17bb2754f98891fe328f8d 100644 --- a/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx @@ -41,6 +41,10 @@ export default defineComponent({ selectedMonth.value = props.selected; }); + watch(() => props.selectedPeriod, (newValue) => { + selectedMonthPeriod.value = newValue; + }); + function onClick($event: Event, target: MonthViewItem) { $event.stopPropagation(); @@ -99,8 +103,8 @@ export default defineComponent({ function isMonthRangeBeginOrEndSame(month: DateObject) { return !!selectedMonthPeriod.value && ( - equal({ year: selectedMonthPeriod.value.from.month }, month) || - equal({ year: selectedMonthPeriod.value.to.month }, month) + equal({ year: selectedMonthPeriod.value.from.year, month: selectedMonthPeriod.value.from.month }, month) || + equal({ year: selectedMonthPeriod.value.to.year, month: selectedMonthPeriod.value.to.month }, month) ); } @@ -112,7 +116,7 @@ export default defineComponent({ const classObject = { 'f-datepicker-month-cell': true, 'f-datepicker-current': monthViewItem.isCurrent && enableMarkCurrent.value, - 'f-datepicker-selected': (!enablePeriod.value && isMonthSame(monthViewItem.date)) || + 'f-datepicker-selected': ((!enablePeriod.value || props.selectMode === 'week') && isMonthSame(monthViewItem.date)) || (enablePeriod.value && isMonthRangeBeginOrEndSame(monthViewItem.date)), 'f-datepicker-disabled': monthViewItem.disable, 'f-datepicker-range': isMonthInPeriod(monthViewItem.date) || monthViewItem.range diff --git a/packages/ui-vue/components/date-picker/src/components/month/month.props.ts b/packages/ui-vue/components/date-picker/src/components/month/month.props.ts index f9a937ce8b7b9aa03a87addda91219eb3b2846c4..881c31e5c135c11f12a7d943ebad47ed8a0fdffb 100644 --- a/packages/ui-vue/components/date-picker/src/components/month/month.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/month/month.props.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { MonthViewItem } from '../../types/month'; +import { SelectMode } from '../../types/common'; export const monthProps = { months: { Type: Array>, default: [[]] }, @@ -22,7 +23,8 @@ export const monthProps = { enableKeyboadNavigate: { Type: Boolean, default: true }, enablePeriod: { Type: Boolean, default: false }, selected: { Type: Object, default: null }, - selectedPeriod: { Type: Object, default: null } + selectedPeriod: { Type: Object, default: null }, + selectMode: { Type: String as PropType, default: 'day' }, }; export type MonthProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx b/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx index 8a804dcee09df08db205507a870fc559c54de231..9a88fa1c0e4f87a27c2f51c7551363ac526d768e 100644 --- a/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx @@ -70,7 +70,7 @@ export default defineComponent({ const classObject = { 'f-datepicker-year-cell': true, 'f-datepicker-current': target.isCurrent && enableMarkCurrent.value, - 'f-datepicker-selected': (!selectedYearPeriod.value && isSelectedYear(target.date)) || + 'f-datepicker-selected': ((!enablePeriod.value || props.selectMode === 'week') && isSelectedYear(target.date)) || (enablePeriod.value && hasSameYearInPeriod(target.date)), 'f-datepicker-disabled': target.disable, 'f-datepicker-range': isYearInPeriod(target.date) || target.range diff --git a/packages/ui-vue/components/date-picker/src/components/year/year.props.ts b/packages/ui-vue/components/date-picker/src/components/year/year.props.ts index 5c34704e5d3149748e91792a1297468ac5f4b12d..d848706d7d4d31a3d8298bab1ad6fad49b9a7dce 100644 --- a/packages/ui-vue/components/date-picker/src/components/year/year.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/year/year.props.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { YearViewItem } from '../../types/year'; +import { SelectMode } from '../../types/common'; export const yearProps = { years: { Type: Array>, default: [[]] }, @@ -22,7 +23,8 @@ export const yearProps = { enableMarkCurrent: { Type: Boolean, default: true }, enablePeriod: { Type: Boolean, default: false }, selected: { Type: Object, default: null }, - selectedPeriod: { Type: Object, default: null } + selectedPeriod: { Type: Object, default: null }, + selectMode: { Type: String as PropType, default: 'day' }, }; export type YearPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/composition/types.ts b/packages/ui-vue/components/date-picker/src/composition/types.ts index 60043b9b1a69c4eb3c6d41fceef4f482b551b9cb..646379a1fe089b9b1becbeb3b24fec52a807ec17 100644 --- a/packages/ui-vue/components/date-picker/src/composition/types.ts +++ b/packages/ui-vue/components/date-picker/src/composition/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { CalendarWeekItem } from "../types/calendar"; -import { DateObject, Period, MarkedDates, MarkStatus } from "../types/common"; +import { DateObject, Period, MarkedDates, MarkStatus, DataRangePicked, FirstDayOfTheWeek } from "../types/common"; import { DateModel } from "../types/date-model"; import { ActiveMonth, MonthViewItem, NameOfMonths } from "../types/month"; import { YearViewItem } from "../types/year"; @@ -52,12 +52,15 @@ export interface UseDate { getNearDate: (now: DateObject, min: DateObject, max: DateObject) => DateObject; - getToday(): DateObject; + getToday(): Required; getDateObject(dateString: string, dateFormat: string): DateObject; getMinDate(value: Date | string | null | undefined): DateObject; getMaxDate(value: Date | string | null | undefined): DateObject; + + getTimeValue(timeValue: string, isDateRange?: boolean) : { hour: number, minute: number, second: number }; + getEndTimeValue(timeValue: string) : { hour: number, minute: number, second: number }; } export interface UseNumber { @@ -73,6 +76,11 @@ export interface UseNumber { export interface UseNormalizeDate { normalizeDate: (dateString: string, useValueFormat: boolean) => DateObject; + normalizeDateRange:(dateRangeString: string) => Period; + setNewDateRange: (period: Period, newDate: DateObject) => DataRangePicked; + getActiveMonth: (dateValue: string) => { dateRange: Period, beginDateActiveMonth?: any, endDateActiveMonth?:any, beginTime?: any, endTime?: any, selectedWeekInfo?: any }; + getTimeStr:(date: Date) => string; + getMonthAndYear:() => { startMonth: {month: number, year: number }, endMonth: {month: number, year: number }}; } export interface UseCompare { @@ -124,7 +132,7 @@ export interface UseMark { } export interface UseWeek { - getNowWeekTime: (date: Date) => Period; + getNowWeekTime: (date: Date, firstDayOfWeek: FirstDayOfTheWeek) => Period; } export interface UseDisableTime { diff --git a/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts b/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts index 52843559350ed831b54a118e2aa6d3b205e24ecb..a6014d3f29e7f0596bff563494a7134dc8ee1266 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DateObject, MarkedDates, MarkStatus, MonthTag, weekDays } from '../types/common'; +import { DateObject, FirstDayOfTheWeek, MarkedDates, MarkStatus, MonthTag, WeekDays } from '../types/common'; import { CalendarWeekItem, CalenderDayItem } from "../types/calendar"; import { useDate } from './use-date'; import { useMark } from './use-mark'; @@ -30,7 +30,7 @@ export default function useCalendar({ isDisabledDate }: UseDisableDate): UseCale function getSundayIndex(firstDayOfWeek: string): number { // Index of Sunday day - const dayIdx = weekDays.indexOf(firstDayOfWeek); + const dayIdx = WeekDays.indexOf(firstDayOfWeek); return dayIdx > 0 ? 7 - dayIdx : 0; } @@ -123,7 +123,9 @@ export default function useCalendar({ isDisabledDate }: UseDisableDate): UseCale day++; } } - const numberInTheYear: number = showWeekNumber && firstDayOfWeek === 'Mon.' ? getWeekNumber(days[0].date) : 0; + + const dateIndex = firstDayOfWeek === FirstDayOfTheWeek.Monday ? 0 : 6; + const numberInTheYear: number = getWeekNumber(days[dateIndex].date); dates.push({ days, numberInTheYear, year }); } return dates; diff --git a/packages/ui-vue/components/date-picker/src/composition/use-date.ts b/packages/ui-vue/components/date-picker/src/composition/use-date.ts index b8ca85ef15e2778e96ee44c1f32207327ed2aa07..62ae0f363b73e44a92f888dcdb61edc74eb4c256 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-date.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-date.ts @@ -15,7 +15,7 @@ */ import { isValid } from "date-fns"; -import { DateObject, weekDays } from "../types/common"; +import { DateObject, WeekDays } from "../types/common"; import { UseDate } from "./types"; export function useDate(): UseDate { @@ -76,7 +76,7 @@ export function useDate(): UseDate { } function getWeekdayIndex(weekday: string): number { - return weekDays.indexOf(weekday); + return WeekDays.indexOf(weekday); } function getTimeInMilliseconds(date: DateObject): number { @@ -97,7 +97,7 @@ export function useDate(): UseDate { return max; } - function getToday(): DateObject { + function getToday(): Required { const date: Date = new Date(); return { year: date.getFullYear(), @@ -199,8 +199,52 @@ export function useDate(): UseDate { return getMinOrMaxDateObject(value, false); } + function getTimeValue(timeValue: string, isDateRange = false): { hour: any, minute: any, second: any } { + if (timeValue) { + const timeArr = timeValue.replace('时', ':').replace('分', ':').replace('秒', '').split(':'); + if (timeArr.length >= 2) { + return { + hour: timeArr[0], + minute: timeArr[1], + second: timeArr[2] ? timeArr[2] : 0 + }; + } + } else { + const nowDate = new Date(); + const hour = nowDate.getHours(); + const minute = nowDate.getMinutes(); + const second = nowDate.getSeconds(); + + if (isDateRange) { + return { hour: 0, minute: 0, second: 0 }; + } else { + return { hour, minute, second }; + } + } + + return { hour: 0, minute: 0, second: 0 }; + } + + function getEndTimeValue(timeValue: string): { hour: any, minute: any, second: any } { + if (timeValue) { + const timeArr_range = timeValue.replace('时', ':').replace('分', ':').replace('秒', '').split(':'); + if (timeArr_range.length >= 2) { + return { + hour: timeArr_range[0], + minute: timeArr_range[1], + second: timeArr_range[2] ? timeArr_range[2] : 0 + }; + } + } + + const hour = 23; + const minute = 59; + const second = 59; + return { hour, minute, second }; + } + return { emptyDate, getDate, getDate2, getDayNumber, getEpocTime, getNearDate, getWeekdayIndex, getTimeInMilliseconds, getToday, getDateObject, getMinDate, - getMaxDate + getMaxDate, getTimeValue, getEndTimeValue }; } diff --git a/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts b/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts index 2be38ae892bd650fd60b5e939fcbbcb07a6d085f..c994b5ab3bef60d41eb4f77e9c202acc0239d8d3 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts @@ -15,20 +15,22 @@ */ import { ref } from 'vue'; import { DatePickerProps } from "../date-picker.props"; -import { DateObject, Period } from "../types/common"; +import { DataRangePicked, DateObject, FirstDayOfTheWeek, Period } from "../types/common"; import { DateFormatInfo, UseNormalizeDate } from "./types"; import { useDateFormat } from "./use-date-format"; import { useNumber } from "./use-number"; import { useCompare } from "./use-compare"; import { NameOfMonths } from "../types/month"; import { useDisableDate } from "./use-disable-date"; +import { useDate } from "./use-date"; export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { - const { displayFormat, minYear, maxYear, selectMode, valueFormat, displayTime, periodDelimiter } = props; - const { getDateValue } = useDateFormat(); - const { getNumberByValue, getMonthNumberByMonthName } = useNumber(); - const { equalOrEarlier, isInitializedDate } = useCompare(); + const { displayFormat, minYear, maxYear, selectMode, valueFormat, showTime, periodDelimiter } = props; + const { emptyDate, getToday } = useDate(); + const { getDateValue, preZero } = useDateFormat(); + const { getNumberByValue, getMonthNumberByMonthName, getWeekNumber } = useNumber(); + const { equalOrEarlier, isInitializedDate, isDateEarlier } = useCompare(); const { isDisabledDate } = useDisableDate( props.minYear, props.maxYear, @@ -43,8 +45,36 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { const nameOfMonths = ref(props.nameOfMonths); const daysInMonth: Array = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + function getMonthAndYear() { + const today = getToday(); + + switch (selectMode) { + case 'month': + return { + startMonth: { month: 0, year: today.year }, + endMonth: { month: 0, year: today.year + 1 } + }; + case 'year': + return { + startMonth: { month: 0, year: today.year }, + endMonth: { month: 0, year: today.year + 10 } + }; + default: { + const startMonth = { month: today.month, year: today.year }; + let endMonth = { month: today.month + 1, year: today.year }; + if (today.month >= 12) { + endMonth = { month: 1, year: today.year + 1 }; + } + + return { startMonth, endMonth }; + } + } + } + + function normalizeInputDate(dateString: string, useValueFormat = false) { - let shouldDisplayTime = displayTime; + let shouldDisplayTime = showTime; let actualFormat = displayFormat; let targetDateString = dateString; if (useValueFormat) { @@ -53,7 +83,7 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { if (targetDateString[targetDateString.length - 1] === '-' || targetDateString[targetDateString.length - 1] === ':') { targetDateString = targetDateString.substring(0, targetDateString.length - 1); } - if (!displayTime && valueFormat.toLocaleLowerCase().indexOf('hh:mm') > -1) { + if (!showTime && valueFormat.toLocaleLowerCase().indexOf('hh:mm') > -1) { shouldDisplayTime = true; } } @@ -141,7 +171,7 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { if (year < minYear || year > maxYear || month < 1 || month > 12) { return normalizedDate; } - const date: DateObject = displayTime ? { year, month, day, hour, minute, second } : { year, month, day }; + const date: DateObject = showTime ? { year, month, day, hour, minute, second } : { year, month, day }; if (isDisabledDate(date)) { return normalizedDate; @@ -174,7 +204,7 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { to: { year: 0, month: 0, day: 0 } }; if (dateRangeStr && dateRangeStr.length) { - const dates: Array = dateRangeStr.split(periodDelimiter); + const dates: Array = dateRangeStr.split(periodDelimiter || '~'); if (dates && dates.length === 2) { const [fromDate, toDate] = dates; const from: DateObject = normalizeDate(fromDate, true); @@ -189,5 +219,97 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { return period; } - return { normalizeDate }; + function setNewDateRange(period: Period, date: DateObject): DataRangePicked { + const { from: begin, to: end } = period; + const isBeginDateInitialized: boolean = isInitializedDate(begin); + const isEndDateInitialized: boolean = isInitializedDate(end) && JSON.stringify(begin) !== JSON.stringify(end); + + if (isBeginDateInitialized && isEndDateInitialized) { + period.from = date; + period.to = emptyDate(); + } else if (!isBeginDateInitialized) { + period.from = date; + + if (!isEndDateInitialized) { + period.to = date; + } + } else { + const firstDateEarlier: boolean = isDateEarlier(date, begin); + if (firstDateEarlier) { + const _date = begin; + period.to = _date; + period.from = date; + } else { + period.to = date; + } + + return { from: period.from, to: period.to, emit: true }; + } + return { from: period.from, to: period.to }; + } + + function getTimeStr(date: Date): string { + const hour = preZero(date.getHours()); + const minute = preZero(date.getMinutes()); + const second = preZero(date.getSeconds()); + return `${hour}:${minute}:${second}`; + } + + function getActiveMonth(dateRangeValue: string) { + const dateRange = normalizeDateRange(dateRangeValue); + const beginDate = dateRange.from; + const endDate = dateRange.to; + + if (isInitializedDate(beginDate) && isInitializedDate(endDate)) { + + const hour = preZero(beginDate.hour || 0); + const minute = preZero(beginDate.minute || 0); + const second = preZero(beginDate.second || 0); + const beginTimeValue = `${hour}:${minute}:${second}`; + const _hour = preZero(endDate.hour || 0); + const _minute = preZero(endDate.minute || 0); + const _second = preZero(endDate.second || 0); + const endTimeValue = `${_hour}:${_minute}:${_second}`; + + const beginDateActiveMonth = { + year: beginDate.year || 1, + month: beginDate.month || 1, + displayTextOfMonth: nameOfMonths.value[beginDate.month || '1'], + displayTextOfYear: `${beginDate.year}` + }; + + const endDateActiveMonth = { + year: endDate.year || 1, + month: endDate.month || 1, + displayTextOfMonth: nameOfMonths.value[endDate.month || '1'], + displayTextOfYear: `${endDate.year}` + }; + + if(beginDate.month === endDate.month && beginDate.year === endDate.year) { + if (endDateActiveMonth.month >= 12) { + endDateActiveMonth.month = 1; + endDateActiveMonth.year = endDateActiveMonth.year + 1; + } else { + endDateActiveMonth.month = endDateActiveMonth.month + 1; + if (selectMode === 'month') { + endDateActiveMonth.year = endDateActiveMonth.year + 1; + } + endDateActiveMonth.displayTextOfMonth = nameOfMonths.value[endDateActiveMonth.month]; + } + } else if (selectMode === 'month' && beginDate.year === endDate.year) { + endDateActiveMonth.year = endDateActiveMonth.year + 1; + } + endDateActiveMonth.displayTextOfYear = `${endDateActiveMonth.year}`; + + const dateObj = props.firstDayOfTheWeek === FirstDayOfTheWeek.Sunday ? endDate: beginDate; + const numberInTheYear = getWeekNumber(dateObj); + const selectedWeekInfo = { numberInTheYear, year: dateObj.year }; + + return { beginDateActiveMonth, endDateActiveMonth, dateRange, beginTime: beginTimeValue, endTime: endTimeValue, selectedWeekInfo }; + } + + return { dateRange }; + } + + return { normalizeDate, normalizeDateRange, setNewDateRange, getActiveMonth, getTimeStr, getMonthAndYear }; } diff --git a/packages/ui-vue/components/date-picker/src/composition/use-week.ts b/packages/ui-vue/components/date-picker/src/composition/use-week.ts index facb37e7576df8941998ea16380b001f0e43555d..2b40969034a285f827f874353ae3441fcf41e59c 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-week.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-week.ts @@ -13,25 +13,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Period } from "../types/common"; +import { FirstDayOfTheWeek, Period } from "../types/common"; import { UseWeek } from "./types"; export function useWeek(): UseWeek { - function getNowWeekTime(date: Date): Period { - date.setDate(date.getDate() - ((date.getDay() + 6) % 7)); - const from = { - year: date.getFullYear(), - month: date.getMonth() + 1, - day: date.getDate() - }; - date.setDate(date.getDate() + 6); - const to = { - year: date.getFullYear(), - month: date.getMonth() + 1, - day: date.getDate() - }; - return { from, to }; + function getNowWeekTime(date: Date, firstDayOfWeek: FirstDayOfTheWeek) { + if (firstDayOfWeek === FirstDayOfTheWeek.Monday) { + date.setDate(date.getDate() - ((date.getDay() + 6) % 7)); + const from = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + date.setDate(date.getDate() + 6); + const to = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + return { from, to }; + } else { + if (date.getDay() !== 0) { + date.setDate(date.getDate() - date.getDay()); + } + + const begin = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + + date.setDate(date.getDate() + 6 - date.getDay()); + const end = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + + return { + from: begin, + to: end + }; + } } return { getNowWeekTime }; diff --git a/packages/ui-vue/components/date-picker/src/date-picker.component.tsx b/packages/ui-vue/components/date-picker/src/date-picker.component.tsx index fb8556fc7d8eeef13a5b9ed20413e9da8f958852..2c1a0b9ed43e697c932ef0f185ea5995048cc3af 100644 --- a/packages/ui-vue/components/date-picker/src/date-picker.component.tsx +++ b/packages/ui-vue/components/date-picker/src/date-picker.component.tsx @@ -62,8 +62,7 @@ export default defineComponent({ const showWeekNumber = ref(props.showWeekNumber); /** 显示方式 */ const selectMode = ref(props.selectMode); - /** 是否显示时间 */ - const displayTime = ref(props.displayTime); + /** 禁用周末 */ const disableWeekends = ref(props.disableWeekends); /** 禁用特定日 */ @@ -88,13 +87,21 @@ export default defineComponent({ }); function setModelValue(dateValue: string) { + if (enablePeriod.value && modelValue.value !== dateValue) { + modelValue.value = dateValue; + realValue.value = dateValue; + context.emit('update:modelValue', dateValue); + context.emit('datePicked', dateValue); + return; + } const formattedValue = formatTo(dateValue, props.valueFormat); - if (realValue.value !== formattedValue) { + if (modelValue.value !== formattedValue) { modelValue.value = formattedValue; realValue.value = formattedValue; context.emit('update:modelValue', formattedValue); - context.emit('datePicked', realValue.value); + context.emit('datePicked', formattedValue); } + } function closeCalendarPanel() { @@ -104,22 +111,33 @@ export default defineComponent({ } function onDatePicked(dateValue: DateObject) { - for(const key in {...dateValue}) { - if (dateValue[key] != null) { - dateValue[key] = dateValue[key].toString().padStart(2, '0'); - } - } - - let dateValueString = `${dateValue.year}-${dateValue.month}-${dateValue.day}`; - if (props.showTime) { - dateValueString += ` ${dateValue.hour}:${dateValue.minute}:${dateValue.second}`; + if (!enablePeriod.value && typeof dateValue === 'object') { + let dateValueString = `${dateValue.year}-${dateValue.month}-${dateValue.day}`; + if (props.showTime) { + dateValueString += ` ${dateValue.hour}:${dateValue.minute}:${dateValue.second}`; + } + + if (selectMode.value === 'month') { + dateValueString = `${dateValue.year}-${dateValue.month}`; + } + + if (selectMode.value === 'year') { + dateValueString = `${dateValue.year}`; + } + + setModelValue(dateValueString); + } else { + if (typeof dateValue === 'string') { + setModelValue(dateValue); + } } - setModelValue(dateValueString); + closeCalendarPanel(); } function onClearDate() { setModelValue(''); + closeCalendarPanel(); } function setMaxAndMinYear() { @@ -153,9 +171,9 @@ export default defineComponent({ }); onMounted(() => { - if (modelValue.value) { - setModelValue(modelValue.value); - } + // if (modelValue.value) { + // setModelValue(modelValue.value); + // } setMaxAndMinYear(); }); @@ -167,9 +185,9 @@ export default defineComponent({ return () => { return enablePeriod.value ? - - : + onConfirm={onConfirm} + onClear={onClearDate} + > : , default: [] }, /** 是否高亮周六 */ @@ -64,7 +61,7 @@ export const datePickerProps = { /** 定界符 */ // periodDelimiter: { Type: String, default: '' }, /** 显示方式 */ - selectMode: { Type: String, default: 'day' }, + selectMode: { Type: String as PropType, default: 'day' }, /** 存储格式 */ valueFormat: { Type: String, default: 'yyyy-MM-dd' }, /** 显示格式 */ @@ -87,18 +84,13 @@ export const datePickerProps = { * 是否展示时分秒 */ showTime: { type: Boolean, default: false }, - /** 显示第二个日期输入框 */ - showEndDate: { type: Boolean, default: true }, + showWeekNumber: {type: Boolean, default: false}, + weekTitle: { type: String, default: '周' }, /** 显示中间图标 */ showMiddleIcon: { type: Boolean, default: true }, - /** 开始日期 */ - startDateValue: { type: String }, - /** 结束日期 */ - endDateValue: { type: String }, - /** 展示日期范围面板 */ - showPeriod: { type: Boolean, default: true }, /** 启用删除 */ enableClear: { type: Boolean, default: true }, + firstDayOfTheWeek:{ type: String as PropType, default: FirstDayOfTheWeek.Sunday } } as Record; export type DatePickerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/schema/date-picker.schema.json b/packages/ui-vue/components/date-picker/src/schema/date-picker.schema.json index 8ab816c75d5293f0dcb32f2690fb9c4e96c96927..6e9321eb1e6d640e08a920790c2b4cb7c3ad7387 100644 --- a/packages/ui-vue/components/date-picker/src/schema/date-picker.schema.json +++ b/packages/ui-vue/components/date-picker/src/schema/date-picker.schema.json @@ -88,6 +88,11 @@ "description": "", "type": "boolean", "default": false + }, + "firstDayOfWeek": { + "description": "", + "type": "string", + "default": "Sun" } }, "required": [ diff --git a/packages/ui-vue/components/date-picker/src/types/common.ts b/packages/ui-vue/components/date-picker/src/types/common.ts index 7f06d1c6b30ecb28c25e5138e72ba8183a4bba9b..f8333d0059db5b2ebbc0d592d4e97220c8608b6a 100644 --- a/packages/ui-vue/components/date-picker/src/types/common.ts +++ b/packages/ui-vue/components/date-picker/src/types/common.ts @@ -27,6 +27,12 @@ export interface Period { to: DateObject; } +export interface DataRangePicked { + from: DateObject; + to: DateObject; + emit?: boolean +} + export interface MarkStatus { marked: boolean; color: string; @@ -37,13 +43,30 @@ export interface MarkedDates { color: string; } +const SUN = 'Sun'; +const MON = 'Mon'; +const TUE = 'Tue'; +const WED = 'Wed'; +const THU = 'Thu'; +const FRI = 'Fri'; +const SAT = 'Sat'; + +export const WeekDays: Array = [SUN, MON, TUE, WED, THU, FRI, SAT]; + + export type SelectMode = 'day' | 'week' | 'month' | 'year'; // export const weekDays = ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thur', 'Fri.', 'Sat.']; -export const weekDays = ['日', '一', '二', '三', '四', '五', '六']; +// export const weekDays = ['日', '一', '二', '三', '四', '五', '六']; +// export const WeekDayLabels = { su: 'Sun', mo: 'Mon', tu: 'Tue', we: 'Wed', th: 'Thu', fr: 'Fri', sa: 'Sat' }; +export const WeekDayLabels = { Sun: '日', Mon: '一', Tue: '二', Wed: '三', Thu: '四', Fri: '五', Sat: '六' }; export enum MonthTag { previous = 1, current = 2, next = 3 } +export enum FirstDayOfTheWeek { + Sunday = 'Sun', + Monday = 'Mon', +}; diff --git a/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx b/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx index 148ba5431e8abea4406cb12764389f37d9b4438f..9f7cd20c39df3f4105e100c3d122ed172d4ce642 100644 --- a/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx +++ b/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx @@ -129,6 +129,11 @@ const FDynamicView = defineComponent({ ...resolveModels(viewSchema), ...resolveExtraProps(viewSchema) }; + + if (componentKey === 'component') { + viewProps['code'] = schema.value?.module?.code; + } + const props = { ...viewProps, key: viewSchema.id, @@ -354,6 +359,8 @@ const FDynamicView = defineComponent({ if (!frameComponent) { return null; } + + frameComponent['formCode'] = schema.value?.module?.code; return frameComponent; } diff --git a/packages/ui-vue/components/field-selector/src/field-selector.component.tsx b/packages/ui-vue/components/field-selector/src/field-selector.component.tsx index d84c047b8551c21e1656060efce971cbba9039a8..3d1f3fba4f58fff482fd37f0730036fa9ba4b1e5 100644 --- a/packages/ui-vue/components/field-selector/src/field-selector.component.tsx +++ b/packages/ui-vue/components/field-selector/src/field-selector.component.tsx @@ -135,7 +135,7 @@ export default defineComponent({ return ( { + app.component(FLanguageTextbox.name as string, FLanguageTextbox); +}; +FLanguageTextbox.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { + componentMap.lookup = FLanguageTextbox; + propsResolverMap.lookup = propsResolver; +}; + +FLanguageTextbox.registerDesigner = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record) => { + componentMap.lookup = FLanguageTextboxDesign; + propsResolverMap.lookup = propsResolver; +}; + +export { FLanguageTextbox }; +export default FLanguageTextbox as typeof FLanguageTextbox & Plugin; diff --git a/packages/ui-vue/components/language-textbox/src/components/language-contents.component.tsx b/packages/ui-vue/components/language-textbox/src/components/language-contents.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cd221bf8a1e2ced49b8fcae0eb4cc340eb5f2380 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/components/language-contents.component.tsx @@ -0,0 +1,38 @@ +import { defineComponent, ref, watch } from "vue"; +import { FInputGroup } from "@farris/ui-vue/components/input-group"; +import { LanguageItem } from "../types"; + +export default defineComponent({ + name: 'LanguageContents', + props: { + languages: { type: Array, default: [] }, + modelValue: { type: Object, default: null } + }, + emits: ['update:modelValue'], + setup(props, context) { + + const languages = ref(props.languages); + const data = ref(props.modelValue); + + return () => { + return
    + { languages.value?.map((item: LanguageItem) => { + return
  • +
    +
    + +
    + +
    +
    +
    +
  • ; + }) } +
; + }; + } +}); diff --git a/packages/ui-vue/components/language-textbox/src/designer/language-textbox.design.component.tsx b/packages/ui-vue/components/language-textbox/src/designer/language-textbox.design.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..614d4c17b97f492e4c3e3e745422fad2efc6c210 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/designer/language-textbox.design.component.tsx @@ -0,0 +1,17 @@ +import { defineComponent } from "vue"; + +export default defineComponent({ + name: 'FLanguageTextboxDesign', + setup() { + return () => { + return
+
+ +
+ +
+
+
; + }; + } +}); diff --git a/packages/ui-vue/components/language-textbox/src/language-textbox.component.tsx b/packages/ui-vue/components/language-textbox/src/language-textbox.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3708fb96749c1aabe5f3c578303f24742b0f6512 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/language-textbox.component.tsx @@ -0,0 +1,58 @@ +import { computed, defineComponent, inject, onMounted, ref, toRefs, watch } from "vue"; + +import FButtonEdit from '@farris/ui-vue/components/button-edit'; + +import { languageTextBoxProps } from "./language-textbox.props"; +import LanguageContents from "./components/language-contents.component"; + +export default defineComponent({ + name: 'FLanguageTextbox', + props: languageTextBoxProps, + emits: ['update:modelValue'], + setup(props, context) { + const currentLang = inject('LocaleID', 'zh-CHS'); + const { disabled, readonly, editable, modelValue, languages } = toRefs(props); + + const displayText = computed(() => modelValue.value[currentLang]); + const buttonEditorRef = ref(); + + function onDisplayTextChange($event: any) { + modelValue.value[currentLang] = $event; + } + + const showPopover = computed(() => { + const popoverInstance = buttonEditorRef.value?.popoverRef; + if (popoverInstance) { + return popoverInstance.shown; + } + return false; + }); + + onMounted(() => { + + }); + + return () => { + return + {showPopover.value && } + ; + } + } +}); diff --git a/packages/ui-vue/components/language-textbox/src/language-textbox.props.ts b/packages/ui-vue/components/language-textbox/src/language-textbox.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..8956485b9715a92ec5a47d634e362f3429e73b6e --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/language-textbox.props.ts @@ -0,0 +1,25 @@ +import { ExtractPropTypes, PropType } from "vue"; +import { LanguageData, LanguageItem, LanguageTextMaxLength } from "./types"; +import { createPropsResolver } from "@farris/ui-vue/components/dynamic-resolver"; + +import { schemaMapper } from './schema/schema-mapper'; +import languageTextboxSchema from './schema/language-textbox.schema.json'; +import { schemaResolver } from './schema/schema-resolver'; + +export const languageTextBoxProps = { + id: { type: String, required: true }, + languages: { type: Array as PropType, default: [] }, + disabled: { type: Boolean, default: false }, + editable: { type: Boolean, default: false }, + readonly: { type: Boolean, default: false }, + placeholder: { type: String, default: '' }, + maxWords: { type: Object as PropType, default: null }, + modelValue: { type: Object as PropType, default: null }, + dropDownIcon: { type: String, default: '' }, + tabIndex: { type: Number, default: -1 }, + enableTitle: {type: Boolean, default: true } +}; + +export type LanguageTextBoxProps = ExtractPropTypes; + +export const propsResolver = createPropsResolver(languageTextBoxProps, languageTextboxSchema, schemaMapper, schemaResolver); diff --git a/packages/ui-vue/components/language-textbox/src/schema/language-textbox.schema.json b/packages/ui-vue/components/language-textbox/src/schema/language-textbox.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..d9a5dc6e4c2cecfd03b8eeb3c91e927f27542a80 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/schema/language-textbox.schema.json @@ -0,0 +1,129 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://farris-design.gitee.io/input-group.schema.json", + "title": "language-textbox", + "description": "A Farris Input Component", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a Input Group", + "type": "string" + }, + "type": { + "description": "The type string of Input Group component", + "type": "string", + "default": "language-textbox" + }, + "appearance": { + "description": "", + "type": "object", + "properties": { + "class": { + "type": "string" + }, + "style": { + "type": "string" + } + }, + "default": {} + }, + "binding": { + "description": "", + "type": "object", + "default": {} + }, + "formatValidation": { + "description": "", + "type": "object", + "default": {} + }, + "editable": { + "description": "", + "type": "boolean", + "default": true + }, + "enableLinkLabel": { + "description": "", + "type": "boolean", + "default": false + }, + "label": { + "description": "", + "type": "string", + "default": "" + }, + "labelWidth": { + "description": "", + "type": "number" + }, + "placeholder": { + "description": "", + "type": "string", + "default": "" + }, + "readonly": { + "description": "", + "type": "boolean", + "default": false + }, + "disabled": { + "description": "", + "type": "boolean", + "default": false + }, + "required": { + "description": "", + "type": "boolean", + "default": false + }, + "tabIndex": { + "description": "", + "type": "number", + "default": -1 + }, + "maxLength": { + "description": "", + "type": "number", + "default": "" + }, + "visible": { + "description": "", + "type": "boolean", + "default": true + }, + "onBlur": { + "description": "", + "type": "string", + "default": "" + }, + "onClickLinkLabel": { + "description": "", + "type": "sting", + "default": "" + }, + "languages": { + "description": "语种列表", + "type": "array", + "default": [] + }, + "modelValue": { + "description": "绑定值", + "type": "object", + "default": {} + }, + "maxWords": { + "description": "各语种最大字符数", + "type": "object", + "default": null + } + }, + "required": [ + "type" + ], + "ignore": [ + "id", + "appearance", + "binding", + "visible" + ] +} \ No newline at end of file diff --git a/packages/ui-vue/components/language-textbox/src/schema/schema-mapper.ts b/packages/ui-vue/components/language-textbox/src/schema/schema-mapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..97964aee23bbb8b523c7692723ea02db00d4f4c0 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/schema/schema-mapper.ts @@ -0,0 +1,5 @@ +import { MapperFunction, resolveAppearance } from '../../../dynamic-resolver'; + +export const schemaMapper = new Map([ + ['appearance', resolveAppearance] +]); diff --git a/packages/ui-vue/components/language-textbox/src/schema/schema-resolver.ts b/packages/ui-vue/components/language-textbox/src/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..b02bdf93eec9060948f579c53aa81e3963a7d706 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/schema/schema-resolver.ts @@ -0,0 +1,5 @@ +import { DynamicResolver } from "../../../dynamic-resolver"; + +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record): Record { + return schema; +} diff --git a/packages/ui-vue/components/language-textbox/src/types/index.ts b/packages/ui-vue/components/language-textbox/src/types/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..540535b6addf66917edb5327a3b1f75c9d743090 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/types/index.ts @@ -0,0 +1,18 @@ +export interface LanguageItem { + /** 地区或国家代码 */ + code: string; + /** 名称 */ + name: string; + + default?: boolean; +} + +/** 字段数据;格式: { 'zh-cn': '姓名', 'en': 'name'... } */ +export interface LanguageData { + /** 地区或国家代码;值为当前字段的数据 */ + [code: string]: string; +} + +export interface LanguageTextMaxLength { + [langCode: string]: number; +} diff --git a/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx b/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx index 9c5031095c652b4c3412dae63ca2b33f17602ac0..ddd9a76c6bb4b0a272c7a2c6a17e4cf658f5fa64 100644 --- a/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, ref } from "vue"; +import { defineComponent, ref, watch } from "vue"; import { FComboList } from "@farris/ui-vue/components/combo-list"; import { cascadeItems } from "../../composition/types"; @@ -6,12 +6,19 @@ import { cascadeItems } from "../../composition/types"; export default defineComponent({ name: 'FLookupCascadeSelector', props: ['modelValue'], - emits: [], + emits: ['update:modelValue'], setup(props, context) { const cascadeRef = ref(); const modelValue = ref(props.modelValue); + watch(() => modelValue.value, (newValue, oldValue) => { + if (newValue === oldValue) { + return; + } + context.emit('update:modelValue', newValue); + }); + return () => { return
{ if (isTreeList()) { - const { renderTreeGrid } = useTreegrid(props, context as any); return renderTreeGrid; } - - const { renderDataGrid } = useDatagrid(props, context as any, { navSelectedItems, getNavigationIdField, getRelationFilter }); return renderDataGrid; - } - - const renderDataComponent = getRenderDataComponent(); + }); const leftPanelWidth = getNavigationSize(); @@ -50,12 +49,38 @@ export default defineComponent({ context.emit('changeDialogOptions', {title}); } + function initLookupStates(result: LookupHttpResult, action: string) { + lookupState['action'] = action; + if (isTreeList()) { + unWatchDatagrid(); + } else { + unWatchTreegrid(); + } + + if (result.navigation) { + Object.keys(result.navigation).forEach(key => { + navigationState[key] = result.navigation?.[key]; + }); + + if (result.navigation.treeInfo) { + useHttpComposition?.setTreeInfo(result.navigation.treeInfo, true); + } + + delete result.navigation; + } + + if (result.treeInfo) { + useHttpComposition?.setTreeInfo(result.treeInfo); + } + Object.keys(result).forEach(key => { + lookupState[key] = result[key]; + }); + } + function initData() { const params: Record = {}; if (props.enableToSelect) { - params.selectedInfo = getIdQueryParams(); - if (queryState.value != null) { params.search = {'field': '*', value: queryState.value, type: 'like'}; params.action = 'search'; @@ -63,30 +88,13 @@ export default defineComponent({ } useHttpComposition?.loadData(params, (result: LookupHttpResult) => { - if (result.navigation) { - Object.keys(result.navigation).forEach(key => { - navigationState[key] = result.navigation?.[key]; - }); - - if (result.navigation.treeInfo) { - useHttpComposition?.setTreeInfo(result.navigation.treeInfo, true); - } - - delete result.navigation; - } - - if (result.treeInfo) { - useHttpComposition?.setTreeInfo(result.treeInfo); - } - Object.keys(result).forEach(key => { - lookupState[key] = result[key]; + nextTick(() => { + initLookupStates(result, params?.action || ''); + setDialogTitle(result.title || ''); }); - - setDialogTitle(result.title || ''); }); } - onMounted(() => { initData(); document.body.classList.add('lookup-modal-open'); @@ -107,7 +115,7 @@ export default defineComponent({
{renderSearchBar()}
- {renderDataComponent()} + {renderDataComponent.value()}
diff --git a/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx b/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx index d9f1cf6776f3c93ef8eccd588c917edbbc588bf2..08ba7f7c9f8d4868b5447a627f3e6217c5fd962d 100644 --- a/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx @@ -35,6 +35,7 @@ export default defineComponent({ provide(LOOKUP_TREEROW_OPTIONS, treeRowOptions); const isMaximized = ref(props.isMaximized); + watch(() => props.isMaximized, (newValue, oldValue) => { isMaximized.value = newValue; @@ -178,7 +179,7 @@ export default defineComponent({ {showIncludeChildNodes.value && } {props.showSelectedList && } - {props.showCascadeControl && } + {props.showCascadeControl && }
diff --git a/packages/ui-vue/components/lookup/src/components/modal-container.props.ts b/packages/ui-vue/components/lookup/src/components/modal-container.props.ts index 85a6f5def0d2773f7c40dd6ce4df42c185fd340e..75fc9d183b044991d271c1e2958b9dfc23a2fe8d 100644 --- a/packages/ui-vue/components/lookup/src/components/modal-container.props.ts +++ b/packages/ui-vue/components/lookup/src/components/modal-container.props.ts @@ -14,7 +14,6 @@ export const lookupModalContainerProps = { showSelectedList: { type: Boolean, default: false}, showIncludeChildNodes: { type: Object, default: { show: true, value: false}}, showCascadeControl: { type: Boolean, default: false }, - cascadeValue: { type: String, default: 'both' }, isMaximized: { type: Boolean, default: false }, }; export type LookupModalContainerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/lookup/src/components/popup-container.component.tsx b/packages/ui-vue/components/lookup/src/components/popup-container.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9c9d58076366c04564085b142bfd66e9d7c4fd72 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/components/popup-container.component.tsx @@ -0,0 +1,92 @@ +import { computed, defineComponent, inject, nextTick, onMounted, onUnmounted, provide, watch } from "vue"; +import { LookupPopupContainerProps, lookupPopupContainerProps } from "./popup-container.props"; +import { useCheckProps } from "../composition/use-check-props"; +import { useTreegrid } from "../composition/use-treegrid"; +import { useDatagrid } from "../composition/use-datagrid"; +import { LookupHttpResult } from "../composition/types"; +import { LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "../composition/use-treegrid-row-options"; + +export default defineComponent({ + name: 'FLookupPopupContainer', + props: lookupPopupContainerProps, + emits: [], + setup(props: LookupPopupContainerProps, context) { + const { lookupStates, getIdQueryParams } = props.useHttpComposition; + + const { lookupState, navigationState, queryState, lookupOptions} = lookupStates; + const { isTreeList } = useCheckProps(props, lookupStates); + + const popupContainerStyle = computed(() => { + return { + width: `${props.width}px`, + height: `${props.height}px`, + padding: '5px' + }; + }); + + const { renderDataGrid } = useDatagrid(props, context as any, {}); + const { renderTreeGrid } = useTreegrid(props, context as any); + + const renderDataComponent = computed(() => { + if (isTreeList()) { + return renderTreeGrid; + } + + return renderDataGrid; + }); + + function initData() { + const params: Record = {}; + if (props.enableToSelect) { + + params.selectedInfo = getIdQueryParams(); + + if (queryState.value != null) { + params.search = {'field': '*', value: queryState.value, type: 'like'}; + params.action = 'search'; + } + } + + const http = props.useHttpComposition; + http?.loadData(params, (result: LookupHttpResult) => { + nextTick(() => { + lookupState['action'] = params.action; + if (result.navigation) { + Object.keys(result.navigation).forEach(key => { + navigationState[key] = result.navigation?.[key]; + }); + + if (result.navigation.treeInfo) { + http?.setTreeInfo(result.navigation.treeInfo, true); + } + + delete result.navigation; + } + + if (result.treeInfo) { + http?.setTreeInfo(result.treeInfo); + } + Object.keys(result).forEach(key => { + lookupState[key] = result[key]; + }); + }); + }); + } + + onMounted(() => { + initData(); + }); + + onUnmounted(() => { + props.hidePopup?.(); + }); + + + return () => { + return
+ {renderDataComponent.value()} + {/*
*/} +
; + }; + } +}); diff --git a/packages/ui-vue/components/lookup/src/components/popup-container.props.ts b/packages/ui-vue/components/lookup/src/components/popup-container.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..965c60f3be22cc5e8b9b3974bdde416bfed45050 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/components/popup-container.props.ts @@ -0,0 +1,44 @@ +import { ExtractPropTypes, PropType } from "vue"; +import { UseHttpComposition } from "../composition/use-http"; +import { LoadTreeDataType, LookupDisplayType, LookupPagination } from "../composition/types"; + +export const lookupPopupContainerProps = { + height: { type: Number, default: 500 }, + width: { type: Number, default: 500 }, + useHttpComposition: { type: Object as PropType, default: {} }, + selectionsManager: { type: Object, default: {} }, + showSelectedList: { type: Boolean, default: false }, + enableToSelect: { type: Boolean, default: true }, + uri: { type: String, default: '' }, + hidePopup: { type: Function, default: null }, + fitColumns: { type: Boolean, default: true }, + openType: { type: String, default: 'Popup' }, + pagination: { + type: Object as PropType, default: { + enable: false, + showLimits: true, + sizeLimits: [10, 20, 30, 50, 100], + size: 20, + index: 1, + total: 0, + mode: 'server', + showGoto: false + } + }, + displayType: { type: String as PropType, default: LookupDisplayType.List }, + loadTreeDataType: { type: String as PropType, default: LoadTreeDataType.all }, + /** 0: 不展开; -1: 全部展开;>0: 展开到指定级数 */ + expandLevel: { type: Number, default: 0 }, + enableCascade: { type: Boolean, default: false }, + showCascadeControl: { type: Boolean, default: false }, + cascadeItems: { + type: Object, default: { + both: true, + up: true, + down: true, + disable: true + } + }, + cascadeStatus: { type: String, default: 'both' } +}; +export type LookupPopupContainerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx b/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx index 95b356d8a11f40232cc593ee7eb27298e22df82d..0307fd5269daf3a4d978ab3e9a62f05d91f2333f 100644 --- a/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx @@ -1,8 +1,9 @@ import { computed, defineComponent, inject, onMounted, Ref, ref, watch } from "vue"; import { FButtonEdit } from "@farris/ui-vue/components/button-edit"; import { FComboList } from "@farris/ui-vue/components/combo-list"; +// import { FSwitch } from "@farris/ui-vue/components/switch"; import { searchBarProps, SearchBarProps } from "./search-bar.props"; -import { SearchField } from "../../composition/types"; +import { SearchField, SearchInfo } from "../../composition/types"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "../../composition/use-http"; export default defineComponent({ @@ -21,7 +22,7 @@ export default defineComponent({ const isNavigation = ref(props.isNavigation); const searchFieldRef = ref(); - const eventParams = computed(() => { + const eventParams = computed(() => { return { field: searchField.value, value: searchValue.value, @@ -101,7 +102,10 @@ export default defineComponent({ onClear={onClearSearchValue} onKeyup={onEnterHandler} popupOnClick={true} - >
+ > +
+ + {/* */}
; }; diff --git a/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx b/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx index d5ba4e76933a092c116f35918c28fe73db642156..b97a4d58ca9810f93d4ad005177f7006ee685dbf 100644 --- a/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx @@ -166,7 +166,7 @@ export default defineComponent({ return (<>
已选{selectionState.value.length}
- {showClearButton.value && } + {showClearButton.value && }
; + + flattenTreeNodes?: any[]; } export interface LookupHttpService { @@ -239,6 +267,7 @@ export interface SearchInfo { field?: string; value?: any; type?: 'like' | 'equal' | ''; + isNavigation?: boolean; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type diff --git a/packages/ui-vue/components/lookup/src/composition/use-check-props.ts b/packages/ui-vue/components/lookup/src/composition/use-check-props.ts index 0ba29c87062576229cab54a287c9b88f071297d3..cef4bb4b7a7b1ff768691ba50df37a4376c00fb1 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-check-props.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-check-props.ts @@ -1,20 +1,10 @@ import { LookupProps } from "../lookup.props"; -import { LoadTreeDataType, LookupDisplayType, LookupPagination, TreeInfo } from "./types"; +import { DEFAULT_PAGINATION_OPTIONS, LoadTreeDataType, LookupDisplayType, LookupPagination, TreeInfo } from "./types"; import { LookupStates } from "./use-state"; export function useCheckProps(props: LookupProps, lookupStates: LookupStates) { const { lookupOptions } = lookupStates; - // 定义默认分页选项常量,避免重复创建对象 - const DEFAULT_PAGINATION_OPTIONS: LookupPagination = { - enable: true, - sizeLimits: [10, 20, 30, 50, 100], - size: 20, - index: 1, - total: 0, - mode: 'server' - }; - function checkPaination(paginationOptions?: Partial) { return { ...DEFAULT_PAGINATION_OPTIONS, ...paginationOptions }; } @@ -89,7 +79,12 @@ export function useCheckProps(props: LookupProps, lookupStates: LookupStates) { return treeInfo?.layerType === "pathcode"; } + function isEnableCascadeCheck() { + return props.enableCascade && lookupOptions.multiSelect; + } + + return { checkPaination, checkMultiSelect, checkColumnOptions, isDoubleList, getNavigationSize, isTreeList, navIsTreeList, navIsList, - isLoadAll, isPathCodeTree + isLoadAll, isPathCodeTree, isEnableCascadeCheck }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx b/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx index 5ec3c1d99dad60af5d3c6e93532cefaf4995c1a8..3e0a09938c726c7a30bfffef49579f94f26cc22f 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx @@ -18,14 +18,14 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; const lookupSelectionsManager = inject(LOOKUP_SELECTIONS_MANAGER) as LookupSelectionsManager; - const {lookupStates, loadData, includeChilds, getPathCode, getChildNodes } = useHttpComposition; - const { lookupState, navigationState, pageInfoState, searchState, lookupOptions} = lookupStates; + const { lookupStates, loadData, includeChilds, getPathCode, getChildNodes } = useHttpComposition; + const { lookupState, navigationState, pageInfoState, searchState, lookupOptions } = lookupStates; const { checkPaination, checkMultiSelect, checkColumnOptions, navIsTreeList, isLoadAll, isPathCodeTree } = useCheckProps(props, lookupStates); const { updatePageInfo } = usePageInfo(props, pageInfoState); - const { updateSelections } = lookupSelectionsManager; + const { updateSelections, isSelected, getPrimaryKey } = lookupSelectionsManager; const { loadAndSelect } = useLoadData(props, lookupStates); @@ -52,41 +52,49 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio useSyncSelect(props, datagridRef, useHttpComposition.lookupStates, currentTab); function dataGridLoadData(items: any, total: number, pageInfo: Record | undefined) { - const value: any = {total}; + const value: any = { total }; if (pageInfo) { const {pageIndex: index, pageSize: size, enablePager: enable, pageList: sizeLimits } = pageInfo; Object.assign(value, {index, size, enable, sizeLimits}); + } else { + value.enable = false; } updatePageInfo(value); loadAndSelect(datagridRef.value, items || []); } const relation = computed(() => { - if(navSelectedItems.value && navSelectedItems.value.length) { - let nodes = navSelectedItems.value; - if (navIsTreeList() && includeChilds.value) { - const treeInfo = navigationState.treeInfo as any; - if (isLoadAll(treeInfo)) { - nodes = getChildNodes(nodes[0]); - return {relationFilter: getRelationFilter(nodes)}; - } else if (isPathCodeTree(treeInfo)) { - return { navNodePathCode: getPathCode(nodes[0].data, treeInfo) }; + if (!navSelectedItems.value?.length) { return null; } + if (!navIsTreeList()) { return null; } + + const nodes = navSelectedItems.value; + if (!includeChilds.value) { return { relationFilter: getRelationFilter(nodes) }; } + + const { treeInfo } = navigationState; + const childNodes = getChildNodes(nodes[0]); + + return isLoadAll(treeInfo) + ? { relationFilter: getRelationFilter(childNodes) } + : isPathCodeTree(treeInfo) + ? { + navNodePathCode: getPathCode(nodes[0].data, treeInfo), + action: 'navAllChildren', + relationFilter: getRelationFilter(nodes) } - } - return {relationFilter: getRelationFilter(nodes)}; - } - return null; + : { relationFilter: getRelationFilter(childNodes) }; }); - watch([() => lookupState?.columns,() => lookupState?.items, () => lookupState?.pageInfo, () => lookupState?.total, - () => lookupState?.selectedData + const unWatchLookupState = watch([() => lookupState?.columns, () => lookupState?.items, () => lookupState?.pageInfo, () => lookupState?.total, + () => lookupState?.selectedData ], ([newColumns, newItems, newPageInfo, newTotal, selectedData]) => { - newColumns && setColumns(datagridRef.value, newColumns); - if (selectedData && selectedData.length) { - updateSelections(selectedData); + if (datagridRef.value) { + newColumns && setColumns(datagridRef.value, newColumns); + if (selectedData && selectedData.length) { + updateSelections(selectedData); + } + newItems && dataGridLoadData(newItems, newTotal || 0, newPageInfo); } - dataGridLoadData(newItems, newTotal || 0, newPageInfo); }); watch(() => props.idValue, (newValue: string) => { @@ -94,6 +102,15 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio }); function onSelectionChange(items: any[]) { + if (items && items.length) { + if (!lookupOptions.multiSelect) { + const itemId = getPrimaryKey(items[0]); + if (isSelected(itemId)) { + return; + } + } + } + updateSelections(items); } @@ -103,7 +120,7 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio function httpRequest() { const queryParams= { search: searchState.default, action: 'list'}; - if (relation.value) { + if (props.openType === 'Modal' && relation.value) { Object.assign(queryParams, relation.value); } loadData(queryParams, (result: LookupHttpResult) => { @@ -113,12 +130,12 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio // 优化 throttle 函数的使用,同时移除未使用的变量 const throttledRequest = throttle(() => { - updatePageInfo({index: 1}); + updatePageInfo({ index: 1 }); httpRequest(); }, 200); - watch(() => navSelectedItems.value, (navSelectedRows) => { - if (props.uri) { + const unWatchNavSelectedItems = watch(() => navSelectedItems?.value, (navSelectedRows) => { + if (lookupOptions.uri) { throttledRequest(); return; } @@ -126,29 +143,44 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio context.emit("navSelectionsChanged", { items: navSelectedRows, ids: navSelectIds }); }); - watch(() => searchState.default, (newSearchInfo) => { - if (props.uri) { + const unWatchSearchState = watch(() => searchState.default, (newSearchInfo) => { + if (lookupOptions.uri) { throttledRequest(); return; } context.emit('search', newSearchInfo); }); - watch(() => includeChilds.value, () => { - if (props.uri && navSelectedItems.value && navSelectedItems.value.length) { + const unWatchIncludeChilds = watch(() => includeChilds.value, () => { + if (lookupOptions.uri && navSelectedItems.value && navSelectedItems.value.length) { throttledRequest(); return; } }); - function onPageInfoChanged({pageSize, pageIndex}) { + function unwatches() { + unWatchLookupState(); + unWatchNavSelectedItems(); + unWatchSearchState(); + unWatchIncludeChilds(); + } + + + watch(() => lookupOptions.displayType, (newDisplayType) => { + if (newDisplayType.toLowerCase() === 'treelist') { + unwatches(); + } + }); + + + function onPageInfoChanged({ pageSize, pageIndex }) { let index = pageIndex; if (currentPaginationOptions.value?.size !== pageSize) { index = 1; } - const params = {size: pageSize, index, isNavigation: false}; + const params = { size: pageSize, index, isNavigation: false }; updatePageInfo(params); - if (props.uri) { + if (lookupOptions.uri) { httpRequest(); return; } @@ -157,7 +189,7 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio } function renderDataGrid() { - return void) => void; getData: (params: any) => Promise; updateSearchFieldTitle: (searchFields: any[], columns: any[]) => any[]; - getPathCode: (data: any, treeInfo: TreeInfo) => string; + getPathCode: (data: any, treeInfo?: TreeInfo) => string; getChildNodes: (node: any) => Array; lookupStates: LookupStates; httpService?: LookupHttpService; @@ -43,10 +43,9 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { const lookupStates = useLookupState(props); const { pageInfoState, lookupState, navigationState, lookupOptions } = lookupStates; - lookupOptions.loadTreeDataType = 'all'; + // lookupOptions.loadTreeDataType = 'all'; const { searchFieldItems, navSearchFieldItems } = useSearchFields(props, lookupState, navigationState); - const uri = ref(props.uri); const idValues = ref(props.idValue); const includeChilds = ref(false); @@ -231,7 +230,11 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { queryParams.treeToList = props.treeToList; queryParams.navTreeToList = props.navTreeToList; - queryParams.loadTreeDataType = lookupOptions.loadTreeDataType; + if (lookupOptions.loadTreeDataType === LoadTreeDataType.default) { + queryParams.loadTreeDataType = 'default'; + } else { + queryParams.loadTreeDataType = lookupOptions.loadTreeDataType === LoadTreeDataType.all ? 'loadall' : 'layerload'; + } if (event?.relationFilter) { queryParams.relationFilter = [...event.relationFilter]; @@ -290,8 +293,8 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { function getData(params: LookupRequestParams): Promise { const loader = lookupRequest(); - if (uri.value && loader) { - return loader(uri.value, params).then(res => { + if (loader) { + return loader(lookupOptions.uri, params).then(res => { initColumnsInfo(res); return res; }); @@ -328,7 +331,9 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { : lookupOptions.loadTreeDataType === 'all' ? !shoudExpand(getFieldValue(item.data, layerField)) : !(item.expanded ?? true); - + + item.hasChildren = !item.leaf; + if (item.children?.length) { setTreeNodeExpandStatus(item.children, expandAll, layerField); } @@ -341,9 +346,15 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { try { let httpResult = await getData(params); - + if (lookupOptions.displayType !== httpResult.displayType) { + lookupOptions.displayType = httpResult.displayType; + } + if (isTree()) { const { items: treeItems, treeInfo: treeInfoConfig } = httpResult.navigation || httpResult; + if (lookupOptions.loadDataType === LoadTreeDataType.default) { + lookupOptions.loadTreeDataType = treeInfoConfig?.loadDataType || LoadTreeDataType.all; + } if (treeItems?.length && treeInfoConfig) { const { dataField: treeInfoDataField, layerField } = treeInfoConfig; @@ -365,7 +376,10 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { includeChilds.value = checked; } - function getPathCode(data: any, treeInfo: TreeInfo) { + function getPathCode(data: any, treeInfo?: TreeInfo) { + if (!treeInfo) { + return ''; + } const { dataField, pathField } = treeInfo; if (pathField) { if (dataField) { diff --git a/packages/ui-vue/components/lookup/src/composition/use-input-change.ts b/packages/ui-vue/components/lookup/src/composition/use-input-change.ts index 97977dce07347976a2f5d76e4733cb78dcde061b..14801f702ae09172411fa7d553a51a5d2a5e4f86 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-input-change.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-input-change.ts @@ -13,62 +13,78 @@ export type LookupInputChangeOptions = { beforeOpenDialog: BeforeOpenDialogFunction; modelValue: Ref; useHttpComposition: UseHttpComposition; - lookupOptions: any + lookupOptions: any, + usePopupComposition: any; + isPopuped: Ref; }; export function useInputChange(props: LookupProps, context: any, options: LookupInputChangeOptions) { + const searchValueChanged = ref(true); const changeOnBlur = computed(() => props.textChangeType === 'blur' || props.textChangeType === 'any'); const changeOnEnter = computed(() => props.textChangeType === 'enter' || props.textChangeType === 'any'); - const {beforeOpenDialog, updateModelValue, selectedItems, openDialog, lookupOptions, useHttpComposition} = options; + const {beforeOpenDialog, updateModelValue, selectedItems, openDialog, lookupOptions, useHttpComposition, isPopuped} = options; const {updateSearchFieldTitle} = useHttpComposition; const { lookupState, queryState } = useHttpComposition.lookupStates; const isClear = ref(false); function isTextChange(text: string) { - const isChange = isEmpty(props.modelValue) && isEmpty(text) ? false: props.modelValue !== text; - if (isEmpty(text) && isChange) { + if (isEmpty(text) && searchValueChanged.value && !isPopuped.value) { isClear.value = true; + searchValueChanged.value = false; return false; } - if (isChange) { + if (searchValueChanged.value) { queryState.value = text; - } else { - queryState.value = ''; } - return isChange; + + return searchValueChanged.value; + } + + function getLeafNode(items: any[]) { + if (!items || items.length !== 1) {return;} + const [firstItem] = items; + return !firstItem.children?.length ? firstItem.data: getLeafNode(firstItem.children); + } + + function isOnlyOneItem(isTree: boolean, items?: any[]) { + if (!items || items.length !== 1) {return false;} + const [firstItem] = items; + if (!isTree) { + return firstItem; + } + return !firstItem.children?.length? firstItem.data: getLeafNode(firstItem.children); } async function queryDataBySearchKeys(searchText: string) { const searchParams = { - search: { field: '*', value: searchText, type: 'equal' }, + search: { field: '*', value: searchText, type: props.openType === 'Modal' ?'equal': 'like' }, action: 'search' }; - if (beforeOpenDialog) { - const shouldContinue = await beforeOpenDialog(searchParams); - if (shouldContinue === false) { - return; - } - + if (searchValueChanged.value) { document.body.classList.add("lookup-modal-open"); - useHttpComposition.loadData(searchParams, (data: LookupHttpResult) => { - const onlyOne = data.items && data.items.length === 1 && (!data.items[0].children || !data.items[0].children.length); - if (onlyOne) { - selectedItems.value = (data.items || []).map(item => item.data ? item.data: item); - updateModelValue(); - queryState.value = ''; - document.body.classList.remove('lookup-modal-open'); - return; - } - + useHttpComposition.loadData(searchParams, async (data: LookupHttpResult) => { if (data.searchFields && data.columns) { const searchFields = updateSearchFieldTitle(data.searchFields, data.columns); lookupState.searchFields = searchFields; } - - openDialog(); + searchValueChanged.value = false; + if (props.openType === 'Modal') { + const onlyOne = isOnlyOneItem(data.displayType?.toLowerCase() === 'treelist', data.items); + if (onlyOne) { + selectedItems.value = [onlyOne]; + updateModelValue(); + queryState.value = ''; + document.body.classList.remove('lookup-modal-open'); + return; + } + openDialog(); + } else { + lookupState['action'] = searchText ? 'search': 'list'; + lookupState.items = data.items; + } }); } } @@ -86,7 +102,13 @@ export function useInputChange(props: LookupProps, context: any, options: Lookup return; } - if (props.uri) { + if (props.openType !== 'Modal' && !isPopuped.value) { + openDialog(); + searchValueChanged.value = false; + return; + } + + if (lookupOptions.uri) { queryDataBySearchKeys(searchText); } else { context.emit('textChanged', {value: searchText, type: isBlur ? 'blur': 'enter'}); @@ -111,6 +133,7 @@ export function useInputChange(props: LookupProps, context: any, options: Lookup changeOnEnter, onInputBlur, onEnterKeyDown, - isClear + isClear, + searchValueChanged }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-load-data.ts b/packages/ui-vue/components/lookup/src/composition/use-load-data.ts index 0d995031a3e612ed326c6873796e795f03248fc4..d2d74ed71fe2822189cf451bac0838136a7d95fb 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-load-data.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-load-data.ts @@ -5,6 +5,7 @@ import { LookupStates } from "./use-state"; export function useLoadData(props: LookupProps, lookupStates: LookupStates) { const { getSelectionIds } = useSelections(props, lookupStates); + const { lookupOptions } = lookupStates; function loadData(gridRef: any, data: any) { gridRef?.updateDataSource(data || []); @@ -13,7 +14,11 @@ export function useLoadData(props: LookupProps, lookupStates: LookupStates) { function selectItemByIds(gridRef: any) { const itemIds = getSelectionIds(); if (itemIds && itemIds.length) { - gridRef?.selectItemByIds(itemIds); + if(lookupOptions.multiSelect) { + gridRef?.selectItemByIds(itemIds); + return; + } + gridRef?.activeRowById(itemIds[0]); } } diff --git a/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx b/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx index bc7ebdd7dcc25557c6aa5e5834a680c7699d9af4..7cade3c74757ec558341eea876183393875433ff 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx @@ -9,19 +9,25 @@ import { useSearchbar } from "./use-search-bar"; import { usePageInfo } from "./use-pageinfo"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "./use-http"; -import { LOOKUP_TREEROW_OPTIONS } from "./use-treegrid-row-options"; +import { LOOKUP_TREE_HIERARCHY, LOOKUP_TREEROW_OPTIONS } from "./use-treegrid-row-options"; +import { LookupHttpResult } from "./types"; +import { useTreeNode } from "./use-treenode"; export function useNavigation(props: LookupProps, context) { const treeRowOptions = inject(LOOKUP_TREEROW_OPTIONS); const useHttpComposition: UseHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; - const { loadData, lookupStates, expandAllNodes, getFieldValue } = useHttpComposition; - const { navigationState, pageInfoState, searchState } = lookupStates; + const { loadData, lookupStates, expandAllNodes, getFieldValue, getData } = useHttpComposition; + const { navigationState, pageInfoState, searchState, lookupOptions } = lookupStates; const { renderSearchBar } = useSearchbar(props, true, lookupStates); - const { checkPaination, navIsList, navIsTreeList } = useCheckProps(props, lookupStates); + const useCheckPropsComposition = useCheckProps(props, lookupStates); + const { checkPaination, navIsList, navIsTreeList } = useCheckPropsComposition; const { updatePageInfo } = usePageInfo(props, pageInfoState); const selectedItems = ref([]); + const { buildGetChildrenQueryParams, loadChildNodes } = useTreeNode(lookupStates, useCheckPropsComposition); + + if (navIsList()) { const paginationOptions = checkPaination(props.navigation?.pagination); updatePageInfo(paginationOptions, true); @@ -102,7 +108,7 @@ export function useNavigation(props: LookupProps, context) { } watch(() => searchState.navigation, (newSearchInfo) => { - if (props.uri) { + if (lookupOptions.uri) { updatePageInfo({ index: 1 }, true); httpRequest(); return; @@ -117,7 +123,7 @@ export function useNavigation(props: LookupProps, context) { } const params = { size: pageSize, index }; updatePageInfo(params, true); - if (props.uri) { + if (lookupOptions.uri) { httpRequest(); return; } @@ -125,7 +131,18 @@ export function useNavigation(props: LookupProps, context) { context.emit("pageSizeChanged", params); } - const hierarchy = { collapseField: 'collapse' }; + function loadChildren(treeNode: any) { + if (leftTreegridRef.value.hasChildren(treeNode)) { + return Promise.resolve(); + } + + // GET CHILDREN DATA + const parentId = treeNode.raw.id; + const params = buildGetChildrenQueryParams(treeNode, searchState.navigation as any, true); + return getData(params).then((result: LookupHttpResult) => { + loadChildNodes(result?.items ?? [], parentId, leftTreegridRef.value); + }); + } function renderLeftTreeGrid() { return ; + hierarchy={LOOKUP_TREE_HIERARCHY} + onSelectionChange={onTreegridSelectionChange} + loadData={loadChildren}>; } function renderLeftDataGrid() { diff --git a/packages/ui-vue/components/lookup/src/composition/use-popup.tsx b/packages/ui-vue/components/lookup/src/composition/use-popup.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eab556efd1c96e23bfcf3b66d2689734d6974509 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/composition/use-popup.tsx @@ -0,0 +1,87 @@ +import { LookupProps } from "../lookup.props"; +import PopupContainer from '../components/popup-container.component'; +import { computed, provide, Ref, watch } from "vue"; +import { LOOKUP_SELECTIONS_MANAGER } from "./use-selections"; +import { LOOKUP_HTTP_COMPOSITION } from "./use-http"; +import { LOOKUP_USER_DATA_SERVICE } from "./use-user-data"; +import { LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "./use-treegrid-row-options"; + +export function usePopup(props: LookupProps, context, elementRef: Ref, { + useHttpComposition, selectionsManager, userDataService, updateModelValue +}) { + + provide(LOOKUP_USER_DATA_SERVICE, userDataService); + provide(LOOKUP_HTTP_COMPOSITION, useHttpComposition); + provide(LOOKUP_SELECTIONS_MANAGER, selectionsManager); + + const { selectionState: selectedItems, queryState, lookupOptions } = useHttpComposition.lookupStates; + const treeRowOptions = useTreeRowOptions(lookupOptions, false); + provide(LOOKUP_TREEROW_OPTIONS, treeRowOptions); + + + const isPopuped = computed(() => { + const popoverInstance = elementRef.value?.popoverRef; + if (popoverInstance) { + return popoverInstance.shown; + } + return false; + }); + + function hidePopup() { + elementRef.value?.hidePopup(); + } + + if (props.openType === 'Popup') { + watch(() => selectedItems.value, (newValue) => { + if (isPopuped.value) { + if (!props.multiSelect){ + hidePopup(); + } else { + updateModelValue(); + } + } + }); + } + + const getPopupSize = () => { + const { width, height } = props.dialog; + return { + width: width || 460, + height: height || 380 + }; + }; + + const popoverInstance = computed(() => { + return elementRef.value?.popoverRef; + }); + + function onHidePopup() { + updateModelValue(); + isPopuped.value && hidePopup(); + queryState.value = ''; + } + + function showPopup() { + if (!isPopuped.value) { + elementRef.value?.showPopup(); + } + } + + + function renderPopup() { + const { width, height } = getPopupSize(); + return ; + } + + return { + popoverInstance, + isPopuped, + renderPopup, + showPopup + }; +} diff --git a/packages/ui-vue/components/lookup/src/composition/use-selections.ts b/packages/ui-vue/components/lookup/src/composition/use-selections.ts index 4525deaaf1db2b7552584aa2805050410700fdc5..96a9bb2be7d92dccdb5d7b9641d79f511d894cd3 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-selections.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-selections.ts @@ -11,6 +11,9 @@ export interface LookupSelectionsManager { unSelectionsByIds: (itemIds: any) => void; selectTreeNodes: (treeNodes: any[]) => void; unSelectTreeNode: (treeNode: VisualData) => void; + unSelectTreeNodes: (treeNodeIds: any[]) => void; + isSelected: (id: any) => boolean; + getPrimaryKey: (data: any) => any; } export const LOOKUP_SELECTIONS_MANAGER = Symbol('LOOKUP_SELECTIONS_MANAGER'); @@ -22,6 +25,10 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : return lookupOptions.idField; }); + function getPrimaryKey(data: any) { + return data[idField.value]; + } + function getSelectionsMap() { let selectionMap = new Map(); if (selectionState.value && selectionState.value.length) { @@ -63,6 +70,11 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : unSelectDataId.value = items; } + function isSelected(id: any) { + const selectionMap = getSelectionsMap(); + return selectionMap.has(id); + } + function updateSelections(data: any, checked = true) { if (!data) { return; @@ -85,14 +97,16 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : return; } + const itemIds = items.map(item => item[idField.value]); + if (!lookupOptions.multiSelect) { + if (itemIds.length && isSelected(itemIds[0])){ + return; + } clearSelections(); } - const itemIds = items.map(item => item[idField.value]); - const selectionMap = getSelectionsMap(); - if (checked) { const itemsMap = new Map(items.map<[string, any]>(n => [n[idField.value], n])); // Add new items and deduplicate @@ -129,6 +143,13 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : } } + function unSelectTreeNodes(treeNodeIds: any[]) { + if (!treeNodeIds || !treeNodeIds.length) { + return; + } + unSelectionsByIds(treeNodeIds); + } + return { updateSelections, clearSelections, @@ -136,6 +157,9 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : getSelections, unSelectionsByIds, selectTreeNodes, - unSelectTreeNode + unSelectTreeNode, + unSelectTreeNodes, + isSelected, + getPrimaryKey }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-state.ts b/packages/ui-vue/components/lookup/src/composition/use-state.ts index 34e5b7c87bdcfa8ecf898f9a2e3619bf126d2c93..4eac6e640346162af12a76919c3f8710556d4aa9 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-state.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-state.ts @@ -41,7 +41,10 @@ export function useLookupState(props: LookupProps): LookupStates { 'treeInfo', 'navTreeInfo', 'onlySelectLeaf', - 'loadTreeDataType' + 'loadTreeDataType', + 'uri', + 'enableFullTree', + 'cascadeValue' ]; function initAllowSetOptions() { diff --git a/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts b/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts index 328c3877d94bf660a1c07fdf6529323006a446ee..fdf3f605c0769a2547ce5f543f9cce8a7a1b3180 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts @@ -2,6 +2,11 @@ import { VisualData } from "@farris/ui-vue/components/data-view"; export const LOOKUP_TREEROW_OPTIONS = Symbol('LOOKUP_TREEROW_OPTIONS'); +export const LOOKUP_TREE_HIERARCHY = { + cascadeOption: { autoCheckChildren: false, autoCheckParent: false }, + collapseField: 'collapse', + hasChildrenField: 'hasChildren' +}; export function useTreeRowOptions(lookupOptions: any, isNavigation = false) { @@ -12,7 +17,7 @@ export function useTreeRowOptions(lookupOptions: any, isNavigation = false) { const treeinfo = isNavigation ? lookupOptions.navTreeInfo : lookupOptions.treeInfo; - if (treeinfo.onlySelectLeaf) { + if (treeinfo?.onlySelectLeaf) { visualData.disabled = !visualData.raw.leaf; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx b/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx index d90982560276a3373d4a3f25e726bb15efdf6746..c22892e838ec3e64b0ccc9db1cfafe6490313c1c 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx @@ -1,31 +1,38 @@ -import { inject, Ref, ref, SetupContext, watch } from "vue"; +import { computed, inject, Ref, ref, SetupContext, watch } from "vue"; import { VisualData } from "@farris/ui-vue/components/data-view"; import { FTreeGrid } from '@farris/ui-vue/components/tree-grid'; import { LookupProps } from "../lookup.props"; import { useCheckProps } from "./use-check-props"; -import { LOOKUP_ACTIVE_TAB, LookupHttpResult, LookupTabs } from "./types"; +import { CascadeEnum, LOOKUP_ACTIVE_TAB, LookupHttpResult, LookupTabs } from "./types"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "./use-http"; -import { LOOKUP_TREEROW_OPTIONS } from "./use-treegrid-row-options"; +import { LOOKUP_TREE_HIERARCHY, LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "./use-treegrid-row-options"; import { useFavorite } from "./use-favorite"; import { useSyncSelect } from "./use-sync-select"; import { LOOKUP_SELECTIONS_MANAGER, LookupSelectionsManager } from "./use-selections"; import { useLoadData } from "./use-load-data"; +import { useTreeNode } from "./use-treenode"; +import { get } from "lodash-es"; +import { customData } from "./use-customdata"; + export function useTreegrid(props: LookupProps, context: SetupContext) { const currentTab = inject(LOOKUP_ACTIVE_TAB, ref(LookupTabs.dataList)); const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; - const treeRowOptions = inject(LOOKUP_TREEROW_OPTIONS); - + const { lookupStates } = useHttpComposition; const { lookupState, searchState, lookupOptions } = lookupStates; + // const treeRowOptions = useTreeRowOptions(lookupOptions, false); + const treeRowOptions = inject(LOOKUP_TREEROW_OPTIONS) as any; + const lookupSelectionsManager = inject(LOOKUP_SELECTIONS_MANAGER) as LookupSelectionsManager; - const { checkMultiSelect, checkColumnOptions } = useCheckProps(props, lookupStates); + const useCheckPropsComposition = useCheckProps(props, lookupStates); + const { checkMultiSelect, checkColumnOptions, isEnableCascadeCheck } = useCheckPropsComposition; const { setColumns } = useFavorite(props, lookupOptions.idField); - const { selectTreeNodes, unSelectTreeNode, updateSelections } = lookupSelectionsManager; + const { selectTreeNodes, unSelectTreeNode, unSelectTreeNodes, updateSelections, getSelectionIds } = lookupSelectionsManager; const selectionOptions = checkMultiSelect(); const columnOptions = checkColumnOptions(); @@ -34,66 +41,118 @@ export function useTreegrid(props: LookupProps, context: SetupContext) { const selectionTreeValues = ref([]); const { loadAndSelect } = useLoadData(props, lookupStates); - const { loadData, expandAllNodes } = useHttpComposition; + const { loadData, expandAllNodes, getData } = useHttpComposition; - function treeGridLoadData(items: any) { + const { flattenTreeNodes, getTreeNodeWithCascadeValue, buildGetChildrenQueryParams, + needGetAllChildNodes, buildGetAllChildrenQueryParams, loadChildNodes + } = useTreeNode(lookupStates, useCheckPropsComposition); - if (searchState.default?.value) { + function treeGridLoadData(items: any, isSearched = false) { + if (isSearched) { items = expandAllNodes(items); } - + lookupState.flattenTreeNodes = flattenTreeNodes(items || []); loadAndSelect(treegridRef.value, items || []); } useSyncSelect(props, treegridRef, lookupStates, currentTab); - watch([() => lookupState?.columns, () => lookupState?.items, + const unWatchLookupState = watch([() => lookupState?.columns, () => lookupState?.items, () => lookupState?.pageInfo, () => lookupState?.total, () => lookupState?.selectedData], ([newColumns, newItems, newPageInfo, newTotal, selectedData]) => { + if (treegridRef.value) { + if (selectedData && selectedData.length) { + updateSelections(selectedData); + } - if (selectedData && selectedData.length) { - updateSelections(selectedData); + newColumns && setColumns(treegridRef.value, newColumns, true); + newItems && treeGridLoadData(newItems, lookupState['action'] === 'search'); } - - newColumns && setColumns(treegridRef.value, newColumns, true); - treeGridLoadData(newItems); }); + function getTreeNodeIdsWithCascadeValue(nodeId: string, isChecked = true) { + const { cascadeValue } = lookupOptions; + if (isEnableCascadeCheck() && cascadeValue !== CascadeEnum.Disable) { + const selectedNodes = getTreeNodeWithCascadeValue(nodeId, cascadeValue, isChecked); + return selectedNodes.map(node => node.id); + } + return []; + } + function onSelectionChange(items: any[]) { selectTreeNodes(items); } function onUnSelectItem(item: VisualData) { unSelectTreeNode(item); + const nodeId = get(item.raw, lookupOptions.idField); + const selectedNodeIds = getTreeNodeIdsWithCascadeValue(nodeId, false); + if (selectedNodeIds.length) { + unSelectTreeNodes([nodeId, ...selectedNodeIds]); + } + } + + function onSelectedItem(item: VisualData) { + const nodeId = get(item.raw, lookupOptions.idField); + const selectedNodeIds = getTreeNodeIdsWithCascadeValue(nodeId); + if (selectedNodeIds.length) { + treegridRef.value.selectItemByIds([nodeId, ...selectedNodeIds]); + } + + if (needGetAllChildNodes(item.raw)) { + const params = buildGetAllChildrenQueryParams(item.raw); + getData(params).then((result: LookupHttpResult) => { + const children: any[] = result.items || []; + const items = children.filter(n => !n.addtional && (n.selectable || n.selectable === undefined)); + if (items && items.length) { + selectTreeNodes(items); + } + }); + } } function httpRequest(action = 'list') { loadData({ search: searchState.default, action }, (result: LookupHttpResult) => { - treeGridLoadData(result.items); + treeGridLoadData(result.items, action === 'search'); }); } - watch(() => searchState.default, (newSearchInfo) => { - if (props.uri) { + const unWatchSearchState = watch(() => searchState.default, (newSearchInfo) => { + if (lookupOptions.uri) { httpRequest('search'); return; } context.emit('search', newSearchInfo); }); + function unwatches() { + unWatchLookupState(); + unWatchSearchState(); + } - const hierarchy = { - cascadeOption: { autoCheckChildren: false, autoCheckParent: false }, - collapseField: 'collapse' - }; + watch(() => lookupOptions.displayType, (newDisplayType) => { + if (newDisplayType.toLowerCase() !== 'treelist') { + unwatches(); + } + }); - function onExpandNode(node: any) { - // return console.log(node); + function loadChildren(treeNode: VisualData) { + if (treegridRef.value.hasChildren(treeNode)) { + return Promise.resolve(); + } + + // GET CHILDREN DATA + const parentId = treeNode.raw.id; + const params = buildGetChildrenQueryParams(treeNode, searchState.default as any); + return getData(params).then((result: LookupHttpResult) => { + loadChildNodes(result?.items ?? [], parentId, treegridRef.value); + }); } function renderTreeGrid() { return ; + onSelectItem={onSelectedItem} + loadData={loadChildren}>; }; return { treegridRef, selectionTreeValues, - renderTreeGrid + renderTreeGrid, + unwatches }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-treenode.ts b/packages/ui-vue/components/lookup/src/composition/use-treenode.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd11b484aa5e048b80adf7ff46a1bad5c9c77fe6 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/composition/use-treenode.ts @@ -0,0 +1,194 @@ +import { get } from "lodash-es"; +import { LookupStates } from "./use-state"; +import { CascadeEnum, SearchInfo } from "./types"; +import { customData } from "./use-customdata"; +import { nextTick } from "vue"; + +export interface TreeNode { + id: string; + parent?: TreeNode; + children?: TreeNode[]; + data: any; + parents?: any[], + leaf?: boolean +} + + +export function useTreeNode(lookupStates: LookupStates, useCheckPropsComposition: any) { + + const { lookupState, lookupOptions } = lookupStates; + + function flattenTreeNodes(treeNodes: any[], parentNode?: TreeNode, parentIds?: any[]): any[] { + return treeNodes.reduce((result, item) => { + item.parents = []; + if (parentNode) { + const parentID = get(parentNode.data, lookupOptions.idField); + item.parent = parentNode; + item.parents = [...(parentIds || []), parentID]; + } + item.id = get(item, lookupOptions.idField); + result.push(item); + if (item.children?.length) { + result.push(...flattenTreeNodes(item.children, item, item.parents)); + } + return result; + }, [] as any[]); + } + + function findTreeNode(nodeId: string) { + return lookupState?.flattenTreeNodes?.find(n => n.id === nodeId); + } + + function getChildren(parentNodeId: string) { + return lookupState?.flattenTreeNodes?.filter(n => n.parents?.includes(parentNodeId)) ?? []; + } + + + function getParents(nodeId: string) { + if (!nodeId) { return []; } + + const treeNode = findTreeNode(nodeId); + return treeNode?.parents?.map(parentId => findTreeNode(parentId)) ?? []; + } + + function getParentsAndChildren(nodeId: string) { + if (!nodeId) { return []; } + + const treeNode = findTreeNode(nodeId); + const parents = treeNode?.parents?.map(parentId => findTreeNode(parentId)) ?? []; + const children = getChildren(nodeId); + return [...parents, ...children]; + } + + function getUnCheckedNodes(nodeId: string, onlyParents = true) { + const parents = getParents(nodeId).filter(parent => + !parent.children?.some(child => + lookupStates.selectionState.value?.some(item => + item[lookupOptions.idField] === child.id + ) + ) + ); + + const children = onlyParents ? [] : getChildren(nodeId); + return parents.concat(children); + } + + /** 依据级联方式获取指定节点的父节点或子节点 */ + function getTreeNodeWithCascadeValue(nodeId: string, cascadeValue: CascadeEnum, isChecked = true) { + const getter = { + down: () => getChildren(nodeId), + up: () => isChecked ? getParents(nodeId) : getUnCheckedNodes(nodeId), + both: () => isChecked ? getParentsAndChildren(nodeId) : getUnCheckedNodes(nodeId, false) + }; + return getter[cascadeValue]?.() ?? []; + } + + function buildGetChildrenQueryParams(treeNode: any, searchInfo: SearchInfo = {}, isNavigation = false) { + const { field = '*', value = ''} = searchInfo as any; + + const isSearchResult = !!value; + + const { layerType, dataField, pathField, layerField } = isNavigation ? lookupOptions.navTreeInfo: lookupOptions.treeInfo; + const isParentId = layerType === 'parentId'; + const search = { category: isNavigation? 'navchildren': 'children', searchField: field , searchValue: value, searchType: 'like'}; + if (isParentId) { + search['parentId'] = treeNode.raw.id; + } else { + const treeInfoData = treeNode.raw[dataField]; + search['parentPath'] = get(treeInfoData, pathField); + search['parentLayer'] = get(treeInfoData, layerField); + } + + const queryParam: any = { + searchValue: '', + customData: customData.value, + enableFullTree: lookupOptions.enableFullTree, + loadTreeDataType: lookupOptions.loadTreeDataType === 'all'? 'loadall': 'layerload', + }; + + if (isSearchResult) { + queryParam.enableFullTree = false; + queryParam.loadTreeDataType = 'layerload'; + if (isParentId) { + search.searchValue = ''; + search.searchField = '*'; + } + } + queryParam.searchValue = JSON.stringify(search); + + return queryParam; + }; + + function needGetAllChildNodes(node?: TreeNode, isNavigation = false) { + const { isEnableCascadeCheck } = useCheckPropsComposition; + const { layerType } = isNavigation ? lookupOptions.navTreeInfo: lookupOptions.treeInfo; + const isNeed = lookupOptions.loadTreeDataType !== 'all' && layerType === "pathcode" && + isEnableCascadeCheck() && (lookupOptions.cascadeValue === "both" || lookupOptions.cascadeValue === "down"); + return node ? node.data && !node.leaf && isNeed : isNeed; + } + + function buildGetAllChildrenQueryParams(node: TreeNode) { + const { dataField, pathField } = lookupOptions.treeInfo; + + const treeInfoData = node.data[dataField]; + const pathcode = get(treeInfoData, pathField); + + + const params = { + searchValue: JSON.stringify({ category: 'allChildren' }), + parentsIds: [pathcode], + customData: customData.value + }; + return params; + } + + function findDirectParent(nodeList: TreeNode[], parentIds: any[]): any { + let currentLevel: TreeNode[] = nodeList; + let parentNode: TreeNode | null = null; + + for (const id of parentIds) { + const foundNode = currentLevel.find(node => node.data.id === id); + if (!foundNode) { + return null; // 路径不存在,返回null + } + parentNode = foundNode; + currentLevel = foundNode.children || []; + } + + return parentNode; + } + + function loadChildNodes(childItems: TreeNode[], parentId: string, gridRef: any) { + if (!gridRef) {return;} + + const parentNode = findTreeNode(parentId); + const parents = [...(parentNode?.parents ?? []), parentId]; + const children = (childItems ?? []).map(item => ({ + ...item, + hasChildren: !item.leaf, + parents: [...parents] + })); + + const treeNode = findDirectParent(lookupState.flattenTreeNodes || [], parents); + if (treeNode) { + treeNode.children = [...children]; + lookupState.flattenTreeNodes?.push(...children); + } + + gridRef.addChildrenToNode(children, parentId); + + nextTick(() => { + const itemIds = lookupStates.selectionState.value?.map(item => item[lookupOptions.idField]) ?? []; + const selectedIds = children + .filter(child => itemIds.includes(child.id)) + .map(child => child.id); + + selectedIds.length && gridRef.selectItemByIds(selectedIds); + }); + } + + + return { flattenTreeNodes, getParents, getChildren, getParentsAndChildren, getTreeNodeWithCascadeValue, buildGetChildrenQueryParams, + needGetAllChildNodes, buildGetAllChildrenQueryParams, loadChildNodes + }; +} diff --git a/packages/ui-vue/components/lookup/src/composition/use-user-data.ts b/packages/ui-vue/components/lookup/src/composition/use-user-data.ts index d0eb50ecb07f1533f1eecfec371b54d957ec6ba4..77908960a11cf9f17a4a740e81475ff82ab33640 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-user-data.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-user-data.ts @@ -8,7 +8,7 @@ import { UseHttpComposition } from "./use-http"; export interface LookupUserData { pageSize?: number; tabIndex?: string; - cascadeStatus?: boolean; + cascadeValue?: boolean; favorites?: Array; size?: Record; } @@ -98,7 +98,7 @@ export function useUserData(props: LookupProps, useHttpComposition: UseHttpCompo const loadingInstance = loadingService?.show(); const loader = lookupRequest(); if (loader) { - return loader(props.uri, params).then((res: Partial) => { + return loader(lookupOptions.uri, params).then((res: Partial) => { loadingInstance.value?.close(); userDataState.favoriteItems = res && res.items || []; return res; diff --git a/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx b/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx index 87220d0c39fcc304881d861e50390ec23e1cb8fb..31b602eb4414f3d0605b00fbef8d9019129b923f 100644 --- a/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx +++ b/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { defineComponent, inject, onMounted, Ref, ref, SetupContext } from 'vue'; +import { computed, defineComponent, inject, onMounted, Ref, ref, SetupContext } from 'vue'; import FButtonEditDesign from '@farris/ui-vue/components/button-edit/designer'; import { useDesignerComponent, DesignerItemContext } from '@farris/ui-vue/components/designer-canvas'; import { lookupProps, LookupProps } from '../lookup.props'; @@ -30,6 +30,15 @@ export default defineComponent({ const designerRulesComposition = useLookupDesignerRules(designItemContext, designerHostService); const componentInstance = useDesignerComponent(elementRef, designItemContext, designerRulesComposition); + const modalIcon = ''; + const popupIcon = ''; + const openType = computed(() => { + return props.openType === 'Popup' ? 'Popup' : 'Modal'; + }); + const buttonIcon = computed(() => { + return openType.value === 'Popup' ? popupIcon : modalIcon; + }); + onMounted(() => { elementRef.value.componentInstance = componentInstance; }); @@ -41,6 +50,7 @@ export default defineComponent({
{ + return props.openType === 'Popup' ? 'Popup' : 'Modal'; + }); + + const buttonIcon = computed(() => { + return openType.value === 'Popup' ? popupIcon : modalIcon; + }); + const useHttpComposition = useHttp(props, context); - const { lookupOptions, selectionState } = useHttpComposition.lookupStates; + const { lookupOptions, selectionState } = useHttpComposition.lookupStates; const userDataService = useUserData(props, useHttpComposition); const showSelections = computed(() => { return props.showSelections && lookupOptions.multiSelect; }); - + const showIncludeChildNodes = computed(() => { return props.showIncludeChildNodes && props.showNavigation && !lookupOptions.navTreeToList; }); const selectionsManager = useSelections(props, useHttpComposition.lookupStates); + const { dictPicked } = useLookupCallBack(props); + const { modalOptions, beforeOpenDialog, dialogSize, customData: customQueryData, updateModelValue, cancelDialog, submitDialog, destroyed } = useDialog(props, context as any, selectionState, { + dictPicked, modelValue, + buttonEditInstance: elementRef, + userDataService, + useHttp: useHttpComposition + }); + const usePopupComposition = usePopup(props, context, elementRef, { + useHttpComposition, selectionsManager, userDataService, updateModelValue + }); + const { renderPopup, showPopup, isPopuped } = usePopupComposition; + function openDialog() { + if (props.openType === 'Popup') { + showPopup(); + return; + } elementRef.value && !elementRef.value.getModal() && elementRef.value.openDialog(); } - const { dictPicked } = useLookupCallBack(props); - const { modalOptions, beforeOpenDialog, dialogSize, customData: customQueryData, updateModelValue, - cancelDialog, submitDialog - } = - useDialog(props, context as any, selectionState, { - dictPicked, modelValue, - buttonEditInstance: elementRef, - userDataService, - useHttp: useHttpComposition + const { onInputBlur, onEnterKeyDown, isClear, searchValueChanged } = useInputChange(props, context, + { + beforeOpenDialog, updateModelValue, selectedItems: selectionState, openDialog, + modelValue, useHttpComposition, lookupOptions, usePopupComposition, isPopuped }); - const { onInputBlur, onEnterKeyDown, isClear } = useInputChange(props, context, - { beforeOpenDialog, updateModelValue, selectedItems: selectionState, openDialog, modelValue, useHttpComposition, lookupOptions }); const modalConfigs = reactive(modalOptions); @@ -86,7 +110,7 @@ export default defineComponent({ customData.value = newData; }); - watch(() => props.modelValue, (newValue) =>{ + watch(() => props.modelValue, (newValue) => { modelValue.value = newValue; }); @@ -108,7 +132,7 @@ export default defineComponent({ onUnmounted(() => { useContext(props); }); - + watch(() => dialogSize.value, (newSizeValue) => { context.emit('resize', newSizeValue); }); @@ -117,7 +141,7 @@ export default defineComponent({ context.emit('update:modelValue', ''); updateIdValue(''); - const clearParams = {items: null, mappingFields: lookupOptions.mappingFields}; + const clearParams = { items: null, mappingFields: lookupOptions.mappingFields }; context.emit('clear', clearParams); useContext(props, clearParams); } @@ -172,7 +196,7 @@ export default defineComponent({ const isMaximized = computed(() => { const modalInstance = modalService?.getCurrentModal(); - return modalInstance?.isMaximized; + return modalInstance?.isMaximized; }); const showCascadeControl = computed(() => { @@ -188,13 +212,28 @@ export default defineComponent({ }); function updateLookupOptions(options: Record) { - for(const key in lookupOptions) { + for (const key in lookupOptions) { if (options[key] !== undefined) { lookupOptions[key] = options[key]; } } } + const handleChangeDebounce = debounce(($event) => { + onInputBlur($event); + }, 200); + + function onValueChange($event: any) { + let searchText = ($event.target as HTMLInputElement)?.value; + if (searchText !== '') { + searchText = searchText.trim(); + } + searchValueChanged.value = $event.target['_value'] !== searchText; + if (openType.value === 'Popup' && searchValueChanged.value) { + handleChangeDebounce($event); + } + } + context.expose({ openDialog, updateIdValue, @@ -215,21 +254,25 @@ export default defineComponent({ editable={props.editable} inputType={"text"} enableClear={props.enableClear} - buttonContent={buttonIcon} - buttonBehavior={"Modal"} + buttonContent={buttonIcon.value} + buttonBehavior={openType.value} modalOptions={modalConfigs} onClear={onClear} beforeOpen={beforeOpenDialog} onBlur={onInputBlur} onKeydown={onEnterKeyDown} placeholder={props.placeholder} + keepWidthWithReference={false} + popupOnClick={!props.editable && openType.value === 'Popup'} + onInput={onValueChange} enableTitle={props.enableTitle} + placement={'auto'} > - onMaximizeModal($event)} - useHttpComposition= {useHttpComposition} userDataService={userDataService} - useDialog={{cancelDialog, submitDialog}} showSelectedList={showSelections.value} - selectionsManager={ selectionsManager } showIncludeChildNodes={{show: showIncludeChildNodes.value, value: props.includeChildNodesValue}} + {openType.value === 'Modal' && onMaximizeModal($event)} + useHttpComposition={useHttpComposition} userDataService={userDataService} + useDialog={{ cancelDialog, submitDialog }} showSelectedList={showSelections.value} + selectionsManager={selectionsManager} showIncludeChildNodes={{ show: showIncludeChildNodes.value, value: props.includeChildNodesValue }} showCascadeControl={showCascadeControl.value} isMaximized={isMaximized.value}> {{ default: () => ( @@ -243,13 +286,14 @@ export default defineComponent({ > ), fav: () => ( - ) }} - + } + {openType.value === 'Popup' && renderPopup()} ); }; diff --git a/packages/ui-vue/components/lookup/src/lookup.props.ts b/packages/ui-vue/components/lookup/src/lookup.props.ts index 7f7f943281fdd2db04f168ab050af0082ee1b463..bf3f7aa5175ce134225622b1e21a8e93dee16b6e 100644 --- a/packages/ui-vue/components/lookup/src/lookup.props.ts +++ b/packages/ui-vue/components/lookup/src/lookup.props.ts @@ -10,7 +10,8 @@ import { BeforeOpenDialogFunction, BeforeSubmitFunction, CallBackFunction, LoadT NavigationOptions, SearchField, TextChangedType, BeforeLoadData, CascadeOptions, - LookupDialogOptions} from "./composition/types"; + LookupDialogOptions, + DEFAULT_PAGINATION_OPTIONS} from "./composition/types"; import { createLookupCallbackResolver } from "./schema/callback-resolvers"; export const lookupProps = { @@ -36,16 +37,7 @@ export const lookupProps = { multiSelect: {type: Boolean, default: false}, showSelections: {type: Boolean, default: true}, separator: {type: String, default: ','}, - pagination: {type: Object as PropType, default: { - enable: false, - showLimits: true, - sizeLimits: [10, 20, 30,50, 100], - size: 20, - index: 1, - total: 0, - mode: 'server', - showGoto: false - }}, + pagination: {type: Object as PropType, default: DEFAULT_PAGINATION_OPTIONS}, enableClear: {type: Boolean, default: true}, inputType: {type: String, default: 'text'}, idValue: {type: String as PropType, default: ''}, @@ -64,7 +56,7 @@ export const lookupProps = { enableMultiFieldSearch: { type: Boolean, default: false }, treeToList: { type: Boolean, default: false }, navTreeToList: { type: Boolean, default: false }, - loadTreeDataType:{ type: String as PropType, default: LoadTreeDataType.all}, + loadTreeDataType:{ type: String as PropType, default: LoadTreeDataType.default }, enableToSelect: { type: Boolean, default: true }, customData: { type: Object, default: null }, modelValue: { type: String, default: '' }, @@ -85,9 +77,10 @@ export const lookupProps = { down: true, disable: true } }, - cascadeStatus: { type: String, default: 'both' }, + cascadeValue: { type: String, default: 'both' }, /** 0: 不展开; -1: 全部展开;>0: 展开到指定级数 */ expandLevel: { type: Number, default: 0 }, + openType: { type: String as PropType<'Modal' | 'Popup'>, default: 'Modal' }, enableTitle: { type: Boolean, default: false } } as Record; diff --git a/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts b/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts index aa394bfc1d78ca997758dd27f7991222955c9213..8cfa9d3da7308ad8fa38ff7f8803178d6514c853 100644 --- a/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts +++ b/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts @@ -152,19 +152,29 @@ export const lookupDialogOptionsConverter = { return options[propertyKey] || DefaultDialogTitle; } - if (propertyKey === 'width') { - if (schema.editor.displayType?.toUpperCase().startsWith('NAV')) { - return options[propertyKey] || 960; + if (schema.editor.openType === 'Modal') { + if (propertyKey === 'width') { + if (schema.editor.displayType?.toUpperCase().startsWith('NAV')) { + return options[propertyKey] || 960; + } + return options[propertyKey] || 590; } - return options[propertyKey] || 590; - } - if (propertyKey === 'height') { - return options[propertyKey] || 620; - } + if (propertyKey === 'height') { + return options[propertyKey] || 620; + } - if (propertyKey === 'navigatorWidth') { - return options[propertyKey] || 320; + if (propertyKey === 'navigatorWidth') { + return options[propertyKey] || 320; + } + } else { + if (propertyKey === 'width') { + return options[propertyKey] || 460; + } + + if (propertyKey === 'height') { + return options[propertyKey] || 380; + } } if (propertyKey === 'draggable') { diff --git a/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts b/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts index d34cad44aad9c2cdc7a01d8219963228f8e7b454..8e69a8629c5aba74a95d990370c93ce3ef2e093a 100644 --- a/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts +++ b/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts @@ -12,6 +12,7 @@ import { DesignerComponentInstance } from "@farris/ui-vue/components/designer-ca import { LookupEvents } from "./lookup-events"; import { cascadeItems } from "../composition/types"; import { ExpressionProperty } from "@farris/ui-vue/components/property-panel"; +import { isUndefined } from "util"; export const LookupSchemaRepositoryToken = Symbol('schema_repository_token'); @@ -371,6 +372,7 @@ export class LookupPropertyConfig extends InputBaseProperty { type: "field-selector", textField: 'bindingPath', idField: 'bindingPath', + disabled: true, editorParams: { propertyData: editorOptions, formBasicInfo: this.formSchemaUtils.getFormMetadataBasicInfo() @@ -383,7 +385,6 @@ export class LookupPropertyConfig extends InputBaseProperty { repositoryToken: FieldSelectorRepositoryToken }, $converter: lookupIdFieldConverter, - visible: false }, textField: { description: "显示文本字段", @@ -426,13 +427,13 @@ export class LookupPropertyConfig extends InputBaseProperty { return `${data.raw['name']} [${data.raw['bindingPath']}]`; }, idField: 'bindingPath', - textField: 'bindingPath', + textField: 'name', valueField: 'bindingPath', repositoryToken: FieldSelectorRepositoryToken }, toData: { idField: 'bindingPath', - textField: 'bindingPath', + textField: 'name', valueField: 'bindingPath', dataSource: () => { const fields = this.designViewModelUtils.getAllFields2TreeByVMId(this.viewModelId); @@ -505,13 +506,14 @@ export class LookupPropertyConfig extends InputBaseProperty { $converter: lookupDefaultConverter, title: "允许查询所有列", type: "boolean", - visible: editorOptions.enableSearchBar == null ? true : !!editorOptions.enableSearchBar + visible: editorOptions.openType !== 'Popup' && editorOptions.enableSearchBar == null ? true : !!editorOptions.enableSearchBar }, enableFavorite: { description: "启用收藏夹", $converter: lookupDefaultConverter, title: "启用收藏夹", type: "boolean", + visible: editorOptions.openType !== 'Popup' }, enableUserData: { description: "保存界面状态", @@ -519,7 +521,7 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "保存界面状态", type: "boolean", - visible: true + visible: editorOptions.openType !== 'Popup' } } }; @@ -539,11 +541,29 @@ export class LookupPropertyConfig extends InputBaseProperty { title: "帮助窗口", parentPropertyID: 'dialog', properties: { + openType: { + description: "窗口展示方式:弹出窗口、下拉面板", + title: "打开方式", + type: "string", + $converter: lookupDefaultConverter, + refreshPanelAfterChanged: true, + editor: { + ...this.comboListEditor, + data: [ + { text: '弹出窗口', value: 'Modal'}, + { text: '下拉面板', value: 'Popup'}, + ], + valueField: 'value', + textField: 'text', + idField: 'value' + } + }, title: { description: "帮助标题", - title: "帮助标题", + title: "标题", type: "string", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' }, width: { description: "窗口宽度,最小值:300px", @@ -572,14 +592,14 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "显示导航栏", type: "boolean", - visible: this.getDisplayType(propertyData.editor).includes('NAV') + visible: this.getDisplayType(propertyData.editor).includes('NAV') && propertyData.editor.openType !== 'Popup' }, navigatorWidth: { description: "导航栏宽度,最小200px, 最大为窗口宽度减去200px", title: "导航栏宽度", type: "number", $converter: lookupDialogOptionsConverter, - visible: this.showNavigatiorWidth(propertyData.editor), + visible: this.showNavigatiorWidth(propertyData.editor) && propertyData.editor.openType !== 'Popup', editor: { ...this.numberEditor, min: 200, @@ -592,31 +612,35 @@ export class LookupPropertyConfig extends InputBaseProperty { type: "boolean", $converter: lookupDialogOptionsConverter, refreshPanelAfterChanged: true, + visible: propertyData.editor.openType !== 'Popup' }, rememberSize: { description: "记录窗口尺寸", title: "记录窗口尺寸", type: "boolean", $converter: lookupDialogOptionsConverter, - visible: !!propertyData.editor.enableUserData && (propertyData.editor?.dialog?.resizeable ?? true) + visible: !!propertyData.editor.enableUserData && (propertyData.editor?.dialog?.resizeable ?? true) && propertyData.editor.openType !== 'Popup' }, enableEsc: { description: "允许ESC关闭", title: "允许ESC关闭", type: "boolean", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' }, showMaxButton: { description: "显示最大化按钮", title: "显示最大化按钮", type: "boolean", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' }, showCloseButton: { description: "显示关闭按钮", title: "显示关闭按钮", type: "boolean", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' } } }; @@ -718,7 +742,7 @@ export class LookupPropertyConfig extends InputBaseProperty { !editorOptions.treeToList && !editorOptions.navTreeToList; } - private showOnlySelectLeaf(editorOptions: any) { + private isTree(editorOptions: any) { return this.getDisplayType(editorOptions) === 'TREELIST' && !editorOptions.treeToList; } @@ -759,7 +783,7 @@ export class LookupPropertyConfig extends InputBaseProperty { textField: 'text', valueField: 'value' }, - visible: false // this.showLoadType(editorOptions) + visible: this.showLoadType(editorOptions) }, enableFullTree: { description: "启用构造完整树", @@ -773,7 +797,7 @@ export class LookupPropertyConfig extends InputBaseProperty { $converter: lookupDefaultConverter, title: "仅选择叶子节点", type: "boolean", - visible: this.showOnlySelectLeaf(editorOptions), + visible: this.isTree(editorOptions), }, enableCascade: { description: "启用级联选择", @@ -781,21 +805,21 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "启用级联选择", type: "boolean", - visible: false // this.showOnlySelectLeaf(editorOptions) && !!editorOptions.multiSelect + visible: this.isTree(editorOptions) && !!editorOptions.multiSelect }, showCascadeControl: { description: "显示级联选择控件", $converter: lookupDefaultConverter, title: "显示级联选择控件", type: "boolean", - visible: false // !!editorOptions.enableCascade && this.showOnlySelectLeaf(editorOptions) + visible: !!editorOptions.enableCascade && this.isTree(editorOptions) }, - cascadeStatus: { + cascadeValue: { description: "级联选择默认状态", $converter: lookupDefaultConverter, title: "级联状态", type: "string", - visible: false, // !!editorOptions.enableCascade && this.showOnlySelectLeaf(editorOptions), + visible: !!editorOptions.enableCascade && this.isTree(editorOptions), editor: { ...this.comboListEditor, data: cascadeItems, diff --git a/packages/ui-vue/components/lookup/src/schema/lookup.schema.json b/packages/ui-vue/components/lookup/src/schema/lookup.schema.json index de21d419e708fb31d9887fb42bfc94bbaca7fe24..dd0812629c17c84b685dfe74015c60de351909e5 100644 --- a/packages/ui-vue/components/lookup/src/schema/lookup.schema.json +++ b/packages/ui-vue/components/lookup/src/schema/lookup.schema.json @@ -198,7 +198,7 @@ }, "loadTreeDataType": { "type": "string", - "default": "all" + "default": "default" }, "onlySelectLeaf": { "type": "boolean", @@ -233,7 +233,7 @@ "disable": true } }, - "cascadeStatus": { + "cascadeValue": { "type": "string", "default": "both" }, @@ -245,6 +245,10 @@ "type": "boolean", "default": true }, + "openType": { + "type": "string", + "default": "Modal" + }, "enableTitle": { "type": "boolean", "default": false diff --git a/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx b/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx index 345a932923265c0233e0a674d292dcab52a1f5e6..8d78e9aeb1af55ce5445d1b733a555c529391bf8 100644 --- a/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx +++ b/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx @@ -10,6 +10,17 @@ export interface ComboTreeRepository { getData(params?: any): Promise; } +function flattenTreeNodes(treeNodes: any[]): any[] { + return treeNodes.reduce((result, item) => { + result.push(item.data); + if (item.children?.length) { + result.push(...flattenTreeNodes(item.children)); + } + return result; + }, [] as any[]); +} + + export default defineComponent({ name: 'FMappingEditor', props: mappingEditorProps, @@ -34,6 +45,14 @@ export default defineComponent({ return visualData; }; + const flattenFromDataSource = computed(() => { + return flattenTreeNodes(fromDataSource.value); + }); + + const flattenToDataSource = computed(() => { + return flattenTreeNodes(toDataSource.value); + }); + const gridColumns = [ { field: 'sourceField', title: '数据源字段', dataType: 'string', editor: { @@ -46,6 +65,8 @@ export default defineComponent({ editorParams: props.fromData.editorParams, editable: false, customRowStatus: treeNodeStatus + },formatter: (cell, row) => { + return flattenFromDataSource.value.find(item => item[props.fromData.valueField] === row.raw.sourceField)?.[props.fromData.textField || 'name'] || ''; } }, { @@ -57,8 +78,18 @@ export default defineComponent({ valueField: props.toData.valueField || 'id', formatter: props.toData.formatter, editorParams: props.toData.editorParams, + multiSelect: true, editable: false, customRowStatus: treeNodeStatus + }, formatter: (cell, row) => { + if (row.raw.targetField) { + const fields = row.raw.targetField.indexOf(',') > -1 ? row.raw.targetField.split(','): [row.raw.targetField]; + return fields.map(id => { + return flattenToDataSource.value.find(item => item[props.toData.valueField] === id)?.[props.toData.textField || 'name'] || ''; + }).join(','); + } else { + return ''; + } } } ]; diff --git a/packages/ui-vue/components/modal/index.ts b/packages/ui-vue/components/modal/index.ts index a884c987dba86d258f54e08c4b1b7e7dd66de524..1630f0368b118b709338e035ab2647121b714aaf 100644 --- a/packages/ui-vue/components/modal/index.ts +++ b/packages/ui-vue/components/modal/index.ts @@ -19,6 +19,8 @@ import FModalService from './src/composition/modal.service'; export * from './src/composition/type'; export * from './src/modal.props'; +export * from './src/composition/use-resizeable'; +export * from './src/composition/use-draggable'; export const F_MODAL_SERVICE_TOKEN = Symbol('FModalService'); diff --git a/packages/ui-vue/components/query-solution/src/components/manager/solution-manager.component.tsx b/packages/ui-vue/components/query-solution/src/components/manager/solution-manager.component.tsx index 0b862f3a0e52290e8df03537a67f51cdc03978a9..32b24a9c53784893287992e819ffbf77f2be0cc7 100644 --- a/packages/ui-vue/components/query-solution/src/components/manager/solution-manager.component.tsx +++ b/packages/ui-vue/components/query-solution/src/components/manager/solution-manager.component.tsx @@ -16,7 +16,7 @@ import { defineComponent, ref, onMounted } from 'vue'; import { solutionManagerProps, SolutionManagerProps } from './solution-manager.props'; import { FDataGrid } from '@farris/ui-vue/components/data-grid'; -import FRadioGroup from '@farris/ui-vue/components/radio-group'; +import { FRadioGroup } from '@farris/ui-vue/components/radio-group'; export default defineComponent({ name: 'FSolutionManager', @@ -93,14 +93,7 @@ export default defineComponent({ {{ cellTemplate: (item) => { if(item.cell.field === 'isDefault') { - return handleDefault(item.row.raw.id)}> - - ; + return ; } } }} diff --git a/packages/ui-vue/components/time-picker/src/components/time.component.tsx b/packages/ui-vue/components/time-picker/src/components/time.component.tsx index e7253902a472a8b1ecb339ca36162bb94a88281c..251a7117eccf761e2d6c20581d70a820c5096dbe 100644 --- a/packages/ui-vue/components/time-picker/src/components/time.component.tsx +++ b/packages/ui-vue/components/time-picker/src/components/time.component.tsx @@ -13,7 +13,7 @@ export default defineComponent({ props: timeProps, emits: ['valueChange', 'formatedValueChange'], setup(props: TimeProps, context) { - const prefixClass = 'time-picker-panel'; + const prefixClass = props.inDatePicker? 'calendar-time-picker': 'time-picker-panel'; const panelWidth = ref(0); // 记录可用时间的范围 const hourRange = ref>([]); @@ -464,15 +464,17 @@ export default defineComponent({ } + const selectItemsListClass = prefixClass + '-select ' + (props.inDatePicker ? 'h-100': ''); + return () => { return ( -
-
-
+
+
+
{getHeaderHtml()} -
+
{showHour.value && ( -
+
    {hourRange.value.map((hour: any) => { return ( @@ -487,7 +489,7 @@ export default defineComponent({
)} {showMinute.value && ( -
+
    {minuteRange.value.map((minute: any) => { return ( @@ -502,7 +504,7 @@ export default defineComponent({
)} {showSecond.value && ( -
+
    {secondRange.value.map((second: any) => { return ( @@ -517,8 +519,8 @@ export default defineComponent({
)} {use12Hours.value && ( -
-
    +
    +
      {use12HoursRange.value.map((hour: any) => { return (
    • ; diff --git a/packages/ui-vue/demos/combo-tree/basic.vue b/packages/ui-vue/demos/combo-tree/basic.vue index 96337f7428d450301f3ad354cf2fcd1faa6a7397..9ae61cec49bbd8d1a092c99b663cbf02f84a94aa 100644 --- a/packages/ui-vue/demos/combo-tree/basic.vue +++ b/packages/ui-vue/demos/combo-tree/basic.vue @@ -16,7 +16,7 @@ const selectedValue = ref(''); :text-field="'name'" :value-field="'id'" :id-field="'id'" - :multi-select="false" + :multi-select="true" /> diff --git a/packages/ui-vue/demos/date-picker/date-range.vue b/packages/ui-vue/demos/date-picker/date-range.vue index ba9046fb44895fd8b5c5531d6cbe437bb8e3fb58..97995d4aba7d006d2e538539f52f8c2e937b4153 100644 --- a/packages/ui-vue/demos/date-picker/date-range.vue +++ b/packages/ui-vue/demos/date-picker/date-range.vue @@ -2,6 +2,10 @@ import { ref } from 'vue'; const currentDate = ref(''); +const currentDate1 = ref(''); +const currentDate2 = ref(''); +const currentDate3 = ref(''); +const currentDate4 = ref(''); const disableUntil = ref({ year: 2023, month: 4, day: 13 }); function updateValue() { @@ -10,9 +14,60 @@ function updateValue() { diff --git a/packages/ui-vue/demos/date-picker/date_format.vue b/packages/ui-vue/demos/date-picker/date_format.vue index d1eb0e3609f373167d6ebb771aea0171d7f6c97f..6ed87f5057316e935114bf68d45ea837bf40fa83 100644 --- a/packages/ui-vue/demos/date-picker/date_format.vue +++ b/packages/ui-vue/demos/date-picker/date_format.vue @@ -14,6 +14,7 @@ const currentDate10 = ref('2023-4-21'); const currentDate11 = ref('2023-04-22 00:00:00'); // const currentDate12 = ref('2023-04'); const currentDate13 = ref('2023'); +const currentDate14 = ref('2025-05'); // const currentDate = ref('2024-6-5');
{weekTitle.value}
{week.numberInTheYear}