CascaderField.vue 3.6 KB

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