index.taro.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <template>
  2. <view :class="classes">
  3. <nut-popup
  4. position="bottom"
  5. v-model:visible="show"
  6. :teleport="teleport"
  7. :lock-scroll="lockScroll"
  8. :close-on-click-overlay="closeOnClickOverlay"
  9. @close="close"
  10. :round="true"
  11. >
  12. <view class="nut-picker__bar">
  13. <view class="nut-picker__cancel nut-picker__left nut-picker__button" @click="close">{{
  14. cancelText || translate('cancel')
  15. }}</view>
  16. <view class="nut-picker__title"> {{ title }}</view>
  17. <view class="nut-picker__confirm nut-picker__right nut-picker__button" @click="confirmHandler()">{{
  18. okText || translate('confirm')
  19. }}</view>
  20. </view>
  21. <slot name="top"></slot>
  22. <view class="nut-picker__column">
  23. <view class="nut-picker__hairline"></view>
  24. <view class="nut-picker__columnitem" v-for="(column, columnIndex) in columnsList" :key="columnIndex">
  25. <nut-picker-column
  26. :itemShow="show"
  27. :column="column"
  28. :readonly="readonly"
  29. :columnsType="columnsType"
  30. :value="defaultValues[columnIndex]"
  31. :threeDimensional="threeDimensional"
  32. @change="
  33. (option) => {
  34. changeHandler(columnIndex, option);
  35. }
  36. "
  37. ></nut-picker-column>
  38. </view>
  39. </view>
  40. <slot name="default"></slot>
  41. </nut-popup>
  42. </view>
  43. </template>
  44. <script lang="ts">
  45. import { ref, onMounted, onBeforeUnmount, reactive, watch, computed, toRaw, toRefs, PropType } from 'vue';
  46. import { createComponent } from '@/packages/utils/create';
  47. import { popupProps } from '../popup/index.taro.vue';
  48. import column from './ColumnTaro.vue';
  49. const { componentName, create, translate } = createComponent('picker');
  50. export default create({
  51. components: {
  52. [column.name]: column
  53. },
  54. props: {
  55. ...popupProps,
  56. modelValue: {
  57. type: Array as PropType<(string | number)[]>,
  58. default: () => []
  59. },
  60. title: {
  61. type: String,
  62. default: ''
  63. },
  64. cancelText: {
  65. type: String,
  66. default: ''
  67. },
  68. okText: {
  69. type: String,
  70. default: ''
  71. },
  72. columns: {
  73. type: Array,
  74. default: () => {
  75. return [];
  76. }
  77. },
  78. readonly: {
  79. type: Boolean,
  80. default: false
  81. },
  82. // 是否开启3D效果
  83. threeDimensional: {
  84. type: Boolean,
  85. default: true
  86. }
  87. },
  88. emits: ['close', 'change', 'confirm', 'update:visible', 'update:modelValue'],
  89. setup(props, { emit }) {
  90. const state = reactive({
  91. show: false,
  92. formattedColumns: props.columns as import('./types').PickerOption[]
  93. });
  94. // 选中项
  95. let defaultValues = ref<(number | string)[]>(props.modelValue);
  96. const classes = computed(() => {
  97. const prefixCls = componentName;
  98. return {
  99. [prefixCls]: true
  100. };
  101. });
  102. const selectedOptions = computed(() => {
  103. let optins: import('./types').PickerOption[] = [];
  104. (columnsList.value as import('./types').PickerOption[][]).map(
  105. (column: import('./types').PickerOption[], index: number) => {
  106. let currOptions = [];
  107. currOptions = column.filter((item) => item.value == defaultValues.value[index]);
  108. optins.push(currOptions[0]);
  109. }
  110. );
  111. return optins;
  112. });
  113. // 当前类型
  114. const columnsType = computed(() => {
  115. const firstColumn: import('./types').PickerOption = state.formattedColumns[0];
  116. if (firstColumn) {
  117. if (Array.isArray(firstColumn)) {
  118. return 'multiple';
  119. }
  120. if ('children' in firstColumn) {
  121. return 'cascade';
  122. }
  123. }
  124. return 'single';
  125. });
  126. // 将传入的 columns 格式化
  127. const columnsList = computed(() => {
  128. switch (columnsType.value) {
  129. case 'multiple':
  130. return state.formattedColumns;
  131. case 'cascade':
  132. // 级联数据处理
  133. return formatCascade(state.formattedColumns, defaultValues.value);
  134. default:
  135. return [state.formattedColumns];
  136. }
  137. });
  138. const formatCascade = (columns: import('./types').PickerOption[], defaultValues: (number | string)[]) => {
  139. const formatted: import('./types').PickerOption[][] = [];
  140. let cursor: import('./types').PickerOption = {
  141. text: '',
  142. value: '',
  143. children: columns
  144. };
  145. let columnIndex = 0;
  146. while (cursor && cursor.children) {
  147. const options: import('./types').PickerOption[] = cursor.children;
  148. const value = defaultValues[columnIndex];
  149. let index = options.findIndex((columnItem) => columnItem.value == value);
  150. if (index == -1) index = 0;
  151. cursor = cursor.children[index];
  152. columnIndex++;
  153. formatted.push(options);
  154. }
  155. return formatted;
  156. };
  157. const close = () => {
  158. emit('close', {
  159. selectedValue: defaultValues.value,
  160. selectedOptions: selectedOptions.value
  161. });
  162. emit('update:visible', false);
  163. };
  164. const changeHandler = (columnIndex: number, option: import('./types').PickerOption) => {
  165. if (option && Object.keys(option).length) {
  166. if (columnsType.value === 'cascade') {
  167. defaultValues.value[columnIndex] = option.value ? option.value : '';
  168. let index = columnIndex;
  169. let cursor = option;
  170. while (cursor && cursor.children && cursor.children[0]) {
  171. defaultValues.value[index + 1] = cursor.children[0].value;
  172. index++;
  173. cursor = cursor.children[0];
  174. }
  175. // 当前改变列 的 下一列 children 值为空
  176. if (cursor && cursor.children && cursor.children.length == 0) {
  177. defaultValues.value = defaultValues.value.slice(0, index + 1);
  178. }
  179. } else {
  180. defaultValues.value[columnIndex] = option.hasOwnProperty('value') ? option.value : '';
  181. }
  182. emit('change', {
  183. columnIndex: columnIndex,
  184. selectedValue: defaultValues.value,
  185. selectedOptions: selectedOptions.value
  186. });
  187. }
  188. };
  189. const confirmHandler = () => {
  190. emit('confirm', {
  191. selectedValue: defaultValues.value,
  192. selectedOptions: selectedOptions.value
  193. });
  194. emit('update:visible', false);
  195. };
  196. onMounted(() => {
  197. console.log('更新');
  198. if (props.visible) state.show = props.visible;
  199. });
  200. onBeforeUnmount(() => {
  201. if (props.visible) state.show = false;
  202. });
  203. watch(
  204. () => props.modelValue,
  205. (newValues) => {
  206. const isSameValue = JSON.stringify(newValues) === JSON.stringify(defaultValues.value);
  207. if (!isSameValue) {
  208. defaultValues.value = newValues;
  209. }
  210. },
  211. { deep: true }
  212. );
  213. watch(
  214. defaultValues,
  215. (newValues) => {
  216. const isSameValue = JSON.stringify(newValues) === JSON.stringify(props.modelValue);
  217. if (!isSameValue) {
  218. emit('update:modelValue', newValues);
  219. }
  220. },
  221. { immediate: true }
  222. );
  223. watch(
  224. () => props.visible,
  225. (val) => {
  226. state.show = val;
  227. }
  228. );
  229. watch(
  230. () => props.columns,
  231. (val) => {
  232. if (val.length) state.formattedColumns = val as import('./types').PickerOption[];
  233. }
  234. );
  235. return {
  236. classes,
  237. ...toRefs(state),
  238. column,
  239. columnsType,
  240. columnsList,
  241. close,
  242. changeHandler,
  243. confirmHandler,
  244. defaultValues,
  245. translate
  246. };
  247. }
  248. });
  249. </script>