| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- <template>
- <view class="multi-select">
- <wd-cell
- title-width="200rpx"
- :placeholder="'请选择' + taskFieldConfig.taskFiledValue"
- :title="taskFieldConfig.taskFiledValue"
- :required="taskFieldConfig.isMustfill === '1'"
- :value="cellValue"
- is-link
- @click="pickerClick"
- />
- <wd-select-picker
- v-model="pickerValue"
- v-model:visible="pickerShow"
- :columns="pickerOptions"
- @confirm="handleSelect"
- />
- </view>
- </template>
- <script setup lang="ts">
- import { computed, ref, watch } from 'vue'
- import type { TaskFieldConfigViewItem, TaskFieldOption } from '../composables/useTaskForm'
- type RawOptionValue = string | number
- type FieldValue = string
- interface SelectPickerConfirmEvent {
- value: RawOptionValue[]
- selectedItems: TaskFieldOption[]
- }
- interface Props {
- taskFieldConfig: TaskFieldConfigViewItem
- disabled?: boolean
- /**
- * 打开选择器前执行。
- *
- * 特殊字段可在页面回调中请求接口,
- * 然后重新赋值当前字段的 options。
- */
- beforeOpen?: () => boolean | Promise<boolean>
- }
- const props = withDefaults(defineProps<Props>(), {
- disabled: false,
- })
- const label = defineModel<string>('label', { default: '' })
- const value = defineModel<FieldValue>('value', { default: '' })
- const pickerShow = ref(false)
- const opening = ref(false)
- const pickerValue = ref<string[]>([])
- /**
- * 选项统一从字段配置中读取。
- *
- * 普通字段由 useTaskForm 初始化字典;
- * 特殊字段由页面 beforeOpen 重新赋值。
- */
- const pickerOptions = computed<TaskFieldOption[]>(() => {
- return props.taskFieldConfig.options ?? []
- })
- const cellValue = computed(() => label.value ?? '')
- const normalizeValue = (rawValue: unknown): string[] => {
- if (Array.isArray(rawValue)) {
- return rawValue.filter((item) => item !== undefined && item !== null && item !== '').map(String)
- }
- if (rawValue === undefined || rawValue === null || rawValue === '') {
- return []
- }
- if (typeof rawValue === 'string' && rawValue.includes(',')) {
- return rawValue
- .split(',')
- .map((item) => item.trim())
- .filter(Boolean)
- }
- return [String(rawValue)]
- }
- const getSelectedItemsByValues = (selectedValues: string[]): TaskFieldOption[] => {
- return selectedValues
- .map((selectedValue) => pickerOptions.value.find((item) => item.value === selectedValue))
- .filter((item): item is TaskFieldOption => Boolean(item))
- }
- const syncLabelByValue = (selectedValues: string[]) => {
- const selectedItems = getSelectedItemsByValues(selectedValues)
- label.value = selectedItems.map((item) => item.label).join(',')
- }
- const syncPickerValueByModelValue = () => {
- const normalizedValues = normalizeValue(value.value)
- const matchedValues = normalizedValues.filter((selectedValue) =>
- pickerOptions.value.some((item) => item.value === selectedValue)
- )
- pickerValue.value = matchedValues
- syncLabelByValue(matchedValues)
- }
- /**
- * 表单值或选项变化时,同步选择器状态。
- *
- * beforeOpen 中重新赋值 field.options 后,
- * 此处也会自动重新匹配已有值。
- */
- watch(
- [() => value.value, () => props.taskFieldConfig.options],
- () => {
- syncPickerValueByModelValue()
- },
- {
- immediate: true,
- deep: true,
- }
- )
- const pickerClick = async () => {
- if (props.disabled || opening.value) {
- return
- }
- opening.value = true
- try {
- /**
- * 页面可在 beforeOpen 中请求特殊接口,
- * 并重新赋值 item.options。
- */
- const canOpen = await props.beforeOpen?.()
- if (canOpen === false) {
- return
- }
- /**
- * beforeOpen 完成后读取最新 options。
- */
- if (!pickerOptions.value.length) {
- uni.showToast({
- title: '暂无可选内容',
- icon: 'none',
- })
- return
- }
- syncPickerValueByModelValue()
- pickerShow.value = true
- } catch (error) {
- console.error(`打开多选字段失败:${props.taskFieldConfig.taskFiledValue}`, error)
- uni.showToast({
- title: '选项加载失败,请稍后重试',
- icon: 'none',
- })
- } finally {
- opening.value = false
- }
- }
- const handleSelect = (event: SelectPickerConfirmEvent) => {
- const selectedValues = event.value.map(String)
- pickerValue.value = selectedValues
- /**
- * 保持后端原有提交格式:
- * 多选 value 存储为逗号分隔字符串。
- */
- value.value = selectedValues.join(',')
- label.value = event.selectedItems.map((item) => item.label).join(',')
- }
- </script>
|