Picker.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <template>
  2. <view class="nana-form-picker">
  3. <slot v-if="loaded && (columns.length === 0 || (columns.length === 1 && columns[0].length === 0))" name="empty">
  4. <Empty description="暂无可选数据" :innerStyle="{ position: 'absolute', width: '100%' }" />
  5. </slot>
  6. <picker-view
  7. v-else-if="loaded"
  8. :value="pickerSelectIndex"
  9. class="picker-view"
  10. :style="{
  11. width: themeContext.resolveThemeSize(pickerWidth),
  12. height: themeContext.resolveThemeSize(pickerHeight),
  13. }"
  14. immediate-change
  15. @change="bindChange"
  16. >
  17. <picker-view-column v-for="(column,c) in columns" :key="c">
  18. <view class="item" v-for="(item,k) in column" :key="k">{{item.text}}</view>
  19. </picker-view-column>
  20. </picker-view>
  21. </view>
  22. </template>
  23. <script setup lang="ts">
  24. import { nextTick, onMounted, ref, watch } from 'vue';
  25. import { useTheme } from '../theme/ThemeDefine';
  26. import Empty from '../feedback/Empty.vue';
  27. const themeContext = useTheme();
  28. export interface PickerItem {
  29. text: string;
  30. value: string|number;
  31. }
  32. export interface PickerProps {
  33. /**
  34. * 选择器列
  35. */
  36. columns: PickerItem[][];
  37. /**
  38. * 选中Value
  39. */
  40. value: (number|string)[],
  41. /**
  42. * picker高度
  43. * @default 300
  44. */
  45. pickerHeight?: string|number,
  46. /**
  47. * picker宽度
  48. * @default 750
  49. */
  50. pickerWidth?: string|number,
  51. /**
  52. * 是否在columns只有一列情况下value返回一个值
  53. * @default false
  54. */
  55. singleValue?: boolean,
  56. }
  57. const emit = defineEmits([ 'update:value', 'selectTextChange' ]);
  58. const props = withDefaults(defineProps<PickerProps>(), {
  59. pickerHeight: 300,
  60. pickerWidth: 750,
  61. });
  62. const loaded = ref(false);
  63. const pickerSelectIndex = ref<number[]>([]);
  64. function bindChange(e: any) {
  65. const val = e.detail.value as number[];
  66. for (let i = 0; i < props.columns.length; i++)
  67. if (val[i] === undefined)
  68. val[i] = 0;
  69. const value = val.map((p, i) => {
  70. const cols = props.columns[i];
  71. if (!cols || cols.length === 0)
  72. return null;
  73. return cols[p].value ?? cols[0].value ?? null;
  74. });
  75. pickerSelectIndex.value = val;
  76. if (props.singleValue && props.columns.length === 1)
  77. emit('update:value', value[0]);
  78. else
  79. emit('update:value', value);
  80. emit('selectTextChange', val.map((p, i) => {
  81. const cols = props.columns[i];
  82. if (!cols || cols.length === 0)
  83. return null;
  84. return cols[p].text ?? cols[0].text ?? null;
  85. }).join(' '));
  86. }
  87. function loadValues() {
  88. const value = typeof props.value === 'number' || typeof props.value === 'string' ? [props.value] : props.value ?? [];
  89. value.forEach((v,i) => {
  90. const index = props.columns[i]?.findIndex((item) => item.value === v);
  91. pickerSelectIndex.value[i] = index < 0 ? 0 : index;
  92. });
  93. emit('selectTextChange', pickerSelectIndex.value.map((p, i) => {
  94. const cols = props.columns[i];
  95. if (!cols || cols.length === 0)
  96. return null;
  97. return cols[p]?.text ?? cols[0]?.text ?? null;
  98. }).join(' '), true);
  99. }
  100. watch(() => props.value, (v) => {
  101. loadValues();
  102. })
  103. onMounted(() => {
  104. nextTick(() => {
  105. loadValues();
  106. loaded.value = true;
  107. })
  108. })
  109. const themeStyles = themeContext.useThemeStyles({
  110. });
  111. defineOptions({
  112. options: {
  113. styleIsolation: "shared",
  114. virtualHost: true,
  115. }
  116. })
  117. </script>
  118. <style lang="scss">
  119. .nana-form-picker {
  120. position: relative;
  121. display: flex;
  122. flex-direction: row;
  123. align-items: center;
  124. .picker-view {
  125. .item {
  126. text-align: center;
  127. }
  128. }
  129. }
  130. </style>