import { getCurrentInstance, nextTick, ref, watch } from 'vue' import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants' import { cloneDeep, isArray, isEmptyVariableInDefault, isObject, throwError, } from '../../../../utils' import type { PickerDataType, PickerMode, PickerProps, PickerValueType, } from '../picker' type PickerData = Array> interface PickerDataItem { label: string | number value: string | number originalData: any children?: Array } export const usePicker = (props: PickerProps) => { const { emit } = getCurrentInstance()! // 显示popup弹框 const openPopup = ref(false) const showPicker = ref(true) // #ifdef MP-ALIPAY showPicker.value = false // #endif watch( () => props.open, (value) => { openPopup.value = value // #ifdef MP-ALIPAY if (value) { setTimeout(() => { nextTick(() => { showPicker.value = value }) }, 350) } // #endif } ) const _closePopup = () => { // #ifdef MP-ALIPAY showPicker.value = false // #endif emit('update:open', false) } // 关闭popup弹框 const closePopupEvent = () => { _closePopup() _generatePickerViewData(props.modelValue) emit('close') } // picker选择器类型 let pickerMode: PickerMode = 'signle' // 生成指定格式的数据 const _generateData = ( data: PickerDataType ): Pick => { if (isObject(data)) { const originalData = cloneDeep(data) if ( Object.prototype.hasOwnProperty.call(originalData, props.childrenKey) ) { delete originalData[props.childrenKey] } return { label: data[props.labelKey], value: data[props.valueKey], originalData, } } else { return { label: data as string | number, value: data as string | number, originalData: data, } } } // 更新/生成级联选择器的数据 const _generateOrUpdateCascadeData = ( data: any[], generateIndex = 1, defaultValue: Array = [] ) => { // 判断生成的的级联数据是否已经有数据,如果有数据则更新,否则生成 if (pickerData.value.length < generateIndex) { pickerData.value.push( ...Array.from( { length: generateIndex - pickerData.value.length }, () => [] ) ) } pickerData.value[generateIndex - 1] = [ ...data.map((item) => _generateData(item)), ] // 判断从第几个子级开始生成级联数据 let childrenIndex = 0 if (defaultValue.length) { childrenIndex = pickerData.value[generateIndex - 1].findIndex( (item) => item.value === defaultValue[generateIndex - 1] ) childrenIndex = ~childrenIndex ? childrenIndex : 0 } if ( data[childrenIndex] && Object.prototype.hasOwnProperty.call( data[childrenIndex], props.childrenKey ) ) { _generateOrUpdateCascadeData( data[childrenIndex][props.childrenKey] as PickerDataItem[], generateIndex + 1, defaultValue ) } } // 记录picker是否正在滚动 const isPickerScrolling = ref(false) let isBeforeConfirm = false // picker选择器的数据 const pickerData = ref([]) // 当前选中picker-view的索引 const currentPickerIndex = ref>([]) // 初始化选中的默认Index const initDefaultPickerIndex = () => { let indexValue: number[] = [] // 如果没有设置默认值,则默认选中第一项 if ( props.modelValue === undefined || (!props.modelValue && ['multiple', 'cascade'].includes(pickerMode)) || (isArray(props.modelValue) && !props.modelValue.length) ) { indexValue = Array.from({ length: pickerData.value.length }, () => 0) } else { if (isArray(props.modelValue)) { indexValue = pickerData.value.map((item, index) => { let pickerIndex = 0 if (!(props.modelValue as (string | number)[])[index]) pickerIndex = 0 else { pickerIndex = item.findIndex((childItem) => { return ( childItem.value === (props.modelValue as (string | number)[])[index] ) }) } return ~pickerIndex ? pickerIndex : 0 }) } else { indexValue = pickerData.value.map((_, k: number) => { const index = pickerData.value[k].findIndex( (item) => item.value === props.modelValue ) return index === -1 ? 0 : index }) } } currentPickerIndex.value = indexValue } // 处理用户传递的数据 const splitUserPickerData = () => { const { data } = props if (!data) return // 判断用户是否有传递数据,并且数据格式是否正确 if (!isArray(data)) { throwError('TnPicker', 'picker选择器数据不正确,请传递数组格式的数据') } if (data.length === 0) return // 根据用户传递的数据来判断是什么类型的选择器 if (isArray(data[0])) { // 多选选择器 pickerMode = 'multiple' pickerData.value = (data as PickerDataType[][]).reduce( (prev: PickerData, cur: Array) => { prev.push(cur.map((item) => _generateData(item))) return prev }, [] ) } else if ( !isArray(data[0]) && isObject(data[0]) && Object.prototype.hasOwnProperty.call(data[0], props.childrenKey) ) { // 级联选择器 pickerMode = 'cascade' _generateOrUpdateCascadeData( data as PickerDataItem[], 1, props.modelValue as Array ) } else { // 单列选择器 pickerMode = 'signle' pickerData.value = [data.map((item) => _generateData(item))] } // console.log(JSON.stringify(pickerData.value)) nextTick(() => { initDefaultPickerIndex() }) } watch( () => props.data, () => { splitUserPickerData() }, { immediate: true, deep: true, } ) // 获取当前选中的值 const _getCurrentPickerValue = (): PickerValueType => { if (pickerMode === 'signle' && !isArray((props.data as any[])[0])) { return pickerData.value[0][currentPickerIndex.value[0]].value } else { // currentPickerIndex.value.splice(pickerData.value.length) const pickerIndex = cloneDeep(currentPickerIndex.value) pickerIndex.splice(pickerData.value.length) return pickerIndex.map((item, index) => isEmptyVariableInDefault(pickerData.value[index][item]?.value, 0) ) } } // 根据用户选中的索引获取当前用户传入的数据 const _getCurrentPickerOriginData = (): any => { if (pickerMode === 'signle' && !isArray((props.data as any[])[0])) { return pickerData.value[0][currentPickerIndex.value[0]].originalData } else { // currentPickerIndex.value.splice(pickerData.value.length) const pickerIndex = cloneDeep(currentPickerIndex.value) pickerIndex.splice(pickerData.value.length) return pickerIndex.map((item, index) => isEmptyVariableInDefault( pickerData.value[index][item]?.originalData, undefined ) ) } } // 生成picker-view的数据 const _generatePickerViewData = (val: any) => { // 如果是级联选择器,对应的级联数据也要更新 if (pickerMode === 'cascade') { _generateOrUpdateCascadeData( props.data as PickerDataItem[], 1, val as Array ) } nextTick(() => { initDefaultPickerIndex() }) } // 标记是否内部更新 let isInnerUpdate = false watch( () => props.modelValue, (val) => { if (isInnerUpdate) { isInnerUpdate = false return } _generatePickerViewData(val) }, { deep: true, } ) // picker-view滑动事件 const pickerViewPickStartEvent = () => { isPickerScrolling.value = true emit('pickstart') } const pickerViewPickerEndEvent = () => { isPickerScrolling.value = false emit('pickend') } // picker-view选择发生改变事件 let changeTimer: ReturnType | null = null let continuousChangeStatus = false const pickerViewChangeEvent = (e: any) => { if (continuousChangeStatus) { return } changeTimer = setTimeout(() => { continuousChangeStatus = false changeTimer && clearTimeout(changeTimer) changeTimer = null }, 300) continuousChangeStatus = true // 比较上一次的值,判断是那一列发生了改变 let changePickerColumnIndex = currentPickerIndex.value.findIndex( (item, index) => item !== e.detail.value[index] ) changePickerColumnIndex = ~changePickerColumnIndex ? changePickerColumnIndex : 0 currentPickerIndex.value = e.detail.value // 如果是级联选择器,对应的列有children的值,则需要更新后面的数据,并且重置后面选中的索引 if (pickerMode === 'cascade') { let data: any[] = props.data as any[] for (let i = 0; i < changePickerColumnIndex; i++) { data = data[currentPickerIndex.value[i]][props.childrenKey] } const pickerIndex = currentPickerIndex.value[changePickerColumnIndex] pickerData.value.splice(changePickerColumnIndex + 1) if ( data[pickerIndex] && Object.prototype.hasOwnProperty.call( data[pickerIndex], props.childrenKey ) ) { _generateOrUpdateCascadeData( data[pickerIndex][props.childrenKey] as PickerDataItem[], changePickerColumnIndex + 2 ) currentPickerIndex.value = pickerData.value.map((item, index) => { return index <= changePickerColumnIndex ? currentPickerIndex.value[index] : 0 }) } } isInnerUpdate = true const value = _getCurrentPickerValue() const originData = _getCurrentPickerOriginData() emit(CHANGE_EVENT, value, changePickerColumnIndex, originData) if (isBeforeConfirm) { isBeforeConfirm = false emit(UPDATE_MODEL_EVENT, value) } } // 重置指定位置的数据 const resetPickerIndexWithPosition = (start: number, end?: number) => { currentPickerIndex.value = currentPickerIndex.value.map((item, index) => { return index >= start && (!end || index <= end) ? 0 : item }) } // 点击确认按钮 const confirmEvent = () => { if (isPickerScrolling.value) { if (props.allowConfirmBeforeScrollEnd) { isBeforeConfirm = true } else { return } } const value = _getCurrentPickerValue() const originData = _getCurrentPickerOriginData() isInnerUpdate = true emit(UPDATE_MODEL_EVENT, value) nextTick(() => { emit('confirm', value, originData) }) _closePopup() } // 点击取消按钮 const cancelEvent = () => { _generatePickerViewData(props.modelValue) emit('cancel') _closePopup() } return { openPopup, showPicker, pickerData, currentPickerIndex, isPickerScrolling, closePopupEvent, pickerViewPickStartEvent, pickerViewPickerEndEvent, pickerViewChangeEvent, confirmEvent, cancelEvent, initDefaultPickerIndex, resetPickerIndexWithPosition, } }