<template>
<view class="l-picker" :style="[styles]">
<view class="l-picker__toolbar" v-if="cancelBtn != null || title != null || confirmBtn != null">
<text class="l-picker__cancel" :style="cancelStyle??''" v-if="cancelBtn != null" @click="onCancel">{{cancelBtn}}</text>
<text class="l-picker__title" :style="titleStyle??''">{{title}}</text>
<text class="l-picker__confirm" :style="confirmStyle??''" v-if="confirmBtn != null" @click="onConfirm">{{confirmBtn}}</text>
</view>
<slot name="header"></slot>
<view class="l-picker__main" :style="[groupHeight != null ? { height: groupHeight}: {}]">
<slot>
<l-picker-item v-for="(options, i) in props.columns" :options="options" :key="i" :column="i" :value="pickerValue.length > i ? pickerValue[i]: null"></l-picker-item>
</slot>
<view class="l-picker__empty" v-if="isEmpty">
<slot name="empty"></slot>
</view>
</view>
<slot name="footer" />
<view class="l-picker__loading" ref="loadingRef" v-if="loading" :style="[loadingMaskColor != null ? {background: loadingMaskColor}: {}]">
<!-- #ifndef APP -->
<l-loading :size="loadingSize" :color="loadingColor"></l-loading>
<!-- #endif -->
</view>
</view>
</template>
<script lang="uts" setup>
import { PickerProps, PickerColumn, PickerValue, PickerColumnItem, PickerConfirmEvent, PickerPickEvent } from './type';
import { pushAt } from './utils';
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
// #ifdef APP
import { useLoading } from '@/uni_modules/lime-loading'
// #endif
const emit = defineEmits(['change', 'cancel', 'pick', 'confirm', 'update:modelValue', 'update:value']);
const props = withDefaults(defineProps<PickerProps>(), {
columns: [] as PickerColumn[],
loading: false,
resetIndex: false,
loadingSize: '64rpx'
})
const pickerItemInstanceArray = reactive<LPickerItemComponentPublicInstance[]>([]);
const modelValue = ref<PickerValue[]>(props.value ?? props.modelValue ?? props.defaultValue ?? [])
const pickerValue = computed({
set(value: PickerValue[]) {
if(value.join('') == modelValue.value.join('')) return
modelValue.value = value;
emit('update:modelValue', value)
emit('change', value)
},
get():PickerValue[] {
return props.value ?? props.modelValue ?? modelValue.value
}
} as WritableComputedOptions<PickerValue[]>)
const isEmpty = computed(():boolean => {
return props.columns.length == 0 && pickerItemInstanceArray.every(child => child.options.length == 0)
})
const styles = computed(():Map<string, any>=>{
const style = new Map<string, any>()
if(props.bgColor != null) {
style.set('background', props.bgColor!)
}
if(props.radius != null) {
style.set('border-top-left-radius', props.radius!)
style.set('border-top-right-radius', props.radius!)
}
return style
})
const curIndexArray = ref<number[]>([]);
const curValueArray = ref([...pickerValue.value]);
const curItemArray:PickerColumnItem[] = []
const realColumns = computed(():PickerColumn[] => {
const pickerColumns = pickerItemInstanceArray.map((child):PickerColumn => child.options)
if(pickerColumns.length > 0) {
return pickerColumns
}
return props.columns
})
const manageChildInList = (child: LPickerItemComponentPublicInstance, shouldAdd: boolean) => {
const index = pickerItemInstanceArray.indexOf(child);
if(shouldAdd) {
if(index != -1) return
pickerItemInstanceArray.push(child)
} else {
if(index == -1) return
pickerItemInstanceArray.splice(index, 1);
}
}
const updateItems = (item: PickerColumnItem, index:number, column: number) => {
pushAt(curIndexArray.value, column, index)
pushAt(curValueArray.value, column, item.value)
pushAt(curItemArray, column, item)
};
const updatePickerItems = () => {
pickerItemInstanceArray.forEach((child, column)=>{
if(child.options.length == 0) return
const value = curValueArray.value.length > column ? curValueArray.value[column] : null;
// #ifndef APP
const index = value == null ? 0 : child._.exposed.getIndexByValue(value);
child._.exposed.setIndex(index)
// #endif
// #ifdef APP
// const index = value == null ? 0 : child.getIndexByValue(value)
// child.setIndex(index)
const index:number = (value == null ? 0 : child.$callMethod('getIndexByValue', value)) as number
child.$callMethod('setIndex', index)
// #endif
const item = child.options[index]
pushAt(curIndexArray.value, column, index)
pushAt(curValueArray.value, column, item.value)
pushAt(curItemArray, column, item)
// 不能改变单向数据流, 只有值不存在时候才处理
if(pickerValue.value.length == 0) {
pickerValue.value = [...curValueArray.value]
}
// if(pickerValue.value.join('') == curValueArray.value.join('')) return
// pickerValue.value = [...curValueArray.value]
})
}
const onPick = (item: PickerColumnItem, index:number, column: number) => {
if(curIndexArray.value[column] == index) return
pushAt(curIndexArray.value, column, index)
pushAt(curValueArray.value, column, item.value)
pushAt(curItemArray, column, item)
const obj:PickerPickEvent = {
values: curValueArray.value,
column,
index
}
pickerValue.value = [...curValueArray.value]
emit('pick', obj)
};
const onCancel = (e: UniPointerEvent) => {
updatePickerItems()
emit('cancel', e)
}
const onConfirm = (e: UniPointerEvent) => {
const values = [...curValueArray.value];
const indexs = [...curIndexArray.value];
const items = curItemArray.map((item):PickerColumnItem => toRaw(item))
if(pickerValue.value.join('') != values.join('')) {
pickerValue.value = values;
}
const obj:PickerConfirmEvent = {
values,
indexs,
items
}
emit('confirm', obj)
}
const stopPickerValue = watch(pickerValue, () => {
if(pickerValue.value.join('') == curValueArray.value.join('')) return
curValueArray.value = pickerValue.value.map((item: PickerValue) => item);
updatePickerItems()
})
const stopColumns = watch(realColumns, ()=>{
nextTick(()=>{
updatePickerItems()
})
})
onMounted(()=>{
nextTick(()=>{
if(pickerValue.value.join('') != curValueArray.value.join('') && pickerValue.value.length > 0) {
curValueArray.value = [...pickerValue.value]
updatePickerItems()
}
})
})
// #ifdef APP
const loadingRef = ref<UniElement|null>(null);
// const {play, clear, failed} = useLoading(loadingRef, 'circular', props.loadingColor?? '#3283ff', unitConvert(props.loadingSize))
const loadingAni = useLoading(loadingRef)
loadingAni.type = 'circular'
loadingAni.color = props.loadingColor?? '#3283ff'
loadingAni.ratio = unitConvert(props.loadingSize)
watchEffect(()=>{
if(props.loading) {
loadingAni.play()
} else {
loadingAni.clear()
}
})
// #endif
onBeforeUnmount(()=> {
stopPickerValue()
stopColumns()
})
provide('limePicker', props)
provide('limePickerOnPick', onPick)
provide('limePickerUpdateItems', updateItems)
provide('limePickerItems', pickerItemInstanceArray)
provide('limePickerManageChildInList', manageChildInList)
</script>
<style lang="scss">
@import './index.scss';
</style>