CalendarField.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <template>
  2. <Popup
  3. :show="popupShow"
  4. @close="onCancel"
  5. :closeIcon="false"
  6. position="bottom"
  7. closeable
  8. >
  9. <ActionSheetTitle
  10. v-bind="titleProps"
  11. :title="title"
  12. :confirmDisabled="confirmDisabled"
  13. border
  14. @cancel="onCancel"
  15. @confirm="onConfirm"
  16. />
  17. <Height :size="20" />
  18. <Calendar
  19. v-if="popupShow"
  20. v-bind="props"
  21. v-model="tempValue"
  22. @selectTextChange="onSelectTextChange"
  23. />
  24. <slot name="footer">
  25. <Height :size="20" />
  26. </slot>
  27. </Popup>
  28. <Text
  29. v-if="showSelectText"
  30. :size="30"
  31. :color="selectText ? 'text.content' : 'text.second'"
  32. :text="selectText || placeholder"
  33. :maxWidth="300"
  34. v-bind="textProps"
  35. />
  36. </template>
  37. <script setup lang="ts">
  38. import { computed, ref, toRef, watch } from 'vue';
  39. import { useFieldChildValueInjector } from './FormContext';
  40. import { usePickerFieldTempStorageData } from './PickerUtils';
  41. import type { CalendarProps } from './Calendar.vue';
  42. import Popup from '../dialog/Popup.vue';
  43. import ActionSheetTitle, { type ActionSheetTitleProps } from '../dialog/ActionSheetTitle.vue';
  44. import Calendar from './Calendar.vue';
  45. import Height from '../layout/space/Height.vue';
  46. import Text, { type TextProps } from '../basic/Text.vue';
  47. export interface CalendarFieldProps extends Omit<CalendarProps, 'modelValue'> {
  48. modelValue?: string|string[];
  49. /**
  50. * 标题
  51. */
  52. title?: string,
  53. /**
  54. * 标题属性
  55. */
  56. titleProps?: Omit<ActionSheetTitleProps, 'title'>,
  57. /**
  58. * 是否显示选择的文本。
  59. * @default true
  60. */
  61. showSelectText?: boolean,
  62. /**
  63. * 占位符
  64. */
  65. placeholder?: string,
  66. /**
  67. * 初始值
  68. */
  69. initalValue?: string[],
  70. /**
  71. * 是否在选择完成后立即更新值。
  72. * @default false
  73. */
  74. shouldUpdateValueImmediately?: boolean,
  75. /**
  76. * 显示的文本属性
  77. */
  78. textProps?: TextProps,
  79. /**
  80. * 是否在单选或者范围模式下,选择完成后直接关闭。
  81. * @default false
  82. */
  83. autoConfirm?: boolean,
  84. /**
  85. * 确认前的回调
  86. * @param value 选中的值
  87. * @returns 返回true可以阻止关闭弹窗
  88. */
  89. beforeConfirm?: (value: string|string[]|undefined) => Promise<boolean>,
  90. }
  91. const emit = defineEmits([ 'update:modelValue', 'cancel', 'confirm', 'selectTextChange', 'tempValueChange' ]);
  92. const props = withDefaults(defineProps<CalendarFieldProps>(), {
  93. title: '请选择日期',
  94. placeholder: '请选择日期',
  95. titleProps: () => ({
  96. cancelText: '取消',
  97. confirmText: '确定',
  98. }),
  99. showSelectText: true,
  100. showFestival: true,
  101. });
  102. const confirmDisabled = computed(() => {
  103. switch (props.pickType) {
  104. default:
  105. case 'days':
  106. case 'day':
  107. return (tempValue.value?.length || 0) < 1;
  108. case 'range':
  109. return tempValue.value?.length !== 2;
  110. }
  111. })
  112. const popupShow = ref(false);
  113. const {
  114. value,
  115. updateValue,
  116. } = useFieldChildValueInjector(
  117. toRef(props, 'modelValue'),
  118. (v) => emit('update:modelValue', v),
  119. undefined,
  120. () => {
  121. popupShow.value = true;
  122. },
  123. props.initalValue,
  124. );
  125. const {
  126. onSelectTextChange,
  127. onCancel,
  128. onConfirm,
  129. selectText,
  130. tempValue,
  131. } = usePickerFieldTempStorageData(
  132. value,
  133. updateValue,
  134. () => popupShow.value = false,
  135. emit as any,
  136. [],
  137. props.shouldUpdateValueImmediately,
  138. props.beforeConfirm,
  139. popupShow,
  140. );
  141. watch(tempValue, (v) => {
  142. if (props.autoConfirm) {
  143. switch (props.pickType) {
  144. default:
  145. case 'day':
  146. if (v?.length)
  147. onConfirm();
  148. break;
  149. case 'range':
  150. if (v?.length === 2)
  151. onConfirm();
  152. break;
  153. case 'days':
  154. if ((v?.length || 0) >= (props.maxPickRangeDays || 100))
  155. onConfirm();
  156. break;
  157. }
  158. }
  159. })
  160. defineOptions({
  161. options: {
  162. styleIsolation: "shared",
  163. virtualHost: true,
  164. }
  165. })
  166. </script>