234 lines
7.9 KiB
Plaintext
234 lines
7.9 KiB
Plaintext
<template>
|
||
<picker-view
|
||
class="l-picker-item__group"
|
||
:style="{opacity: options.length > 0 ? 1 : 0}"
|
||
:indicator-style="indicatorStyles"
|
||
:value="innerIndex"
|
||
@change="handlePick"
|
||
indicator-class="l-picker-item__indicator">
|
||
<picker-view-column class="l-picker-item__wrapper">
|
||
<!-- #ifdef APP-ANDROID -->
|
||
<view ref="itemRef"></view>
|
||
<!-- #endif -->
|
||
<!-- #ifndef APP-ANDROID -->
|
||
<text class="l-picker-item__group-item" v-for="(item, i) in options"
|
||
:style="[itemStyles, curIndex == i ? itemActiveStyles: {}]"
|
||
:class="{'l-picker-item__group-item--active': curIndex == i}" :key="item.value">{{item.label}}</text>
|
||
<!-- #endif -->
|
||
</picker-view-column>
|
||
</picker-view>
|
||
</template>
|
||
<script lang="uts" setup>
|
||
/**
|
||
* PickerItem 选择器子项组件
|
||
* @description 用于构建多列选择器的单个列项,通常作为 Picker 组件的子组件使用
|
||
* <br>插件类型:LPickerItemComponentPublicInstance
|
||
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-picker
|
||
*
|
||
* @property {PickerColumnItem[]} options 当前列的选项列表(必填)
|
||
* @property {PickerValue} value 当前选中值
|
||
* @property {number} column 列索引标识(从0开始计数)
|
||
* @property {string | number} name 列名称标识符
|
||
*/
|
||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||
import { clamp } from '@/uni_modules/lime-shared/clamp'
|
||
import { PickerItemProps, ManageChildInList, OnPick, UpdateItems } from './type';
|
||
import { PickerColumnItem, PickerValue } from '../l-picker/type';
|
||
const instance = getCurrentInstance()!;
|
||
const props = withDefaults(defineProps<PickerItemProps>(), {
|
||
options: [] as PickerColumnItem[],
|
||
column: -1,
|
||
resetIndex: false
|
||
})
|
||
const picker = inject<LPickerComponentPublicInstance | null>('limePicker', null);
|
||
const pickerItemInstanceArray = inject<LPickerItemComponentPublicInstance[] | null>('limePickerItems', null);
|
||
const manageChildInList = inject<ManageChildInList | null>('limePickerManageChildInList', null);
|
||
manageChildInList?.(instance.proxy! as LPickerItemComponentPublicInstance, true)
|
||
const onPick = inject<OnPick | null>('limePickerOnPick', null);
|
||
const updateItems = inject<UpdateItems | null>('limePickerUpdateItems', null);
|
||
// web 如果初始是0 当数据加载后 无法指向0
|
||
// #ifdef WEB
|
||
const curIndex = ref(-1)
|
||
// #endif
|
||
// #ifndef WEB
|
||
const curIndex = ref(0)
|
||
// #endif
|
||
const curValue = ref<PickerValue | null>(props.value);
|
||
const column = computed(() : number => props.column != -1 ? props.column : pickerItemInstanceArray?.indexOf(instance.proxy! as LPickerItemComponentPublicInstance) ?? props.column);
|
||
|
||
const elementPosition = computed(() : boolean[] => {
|
||
const totalElements = pickerItemInstanceArray?.length ?? 0;
|
||
return [column.value == 0, column.value == totalElements - 1]
|
||
});
|
||
const innerIndex = computed(() : number[] => [curIndex.value])
|
||
const indicatorStyles = computed(() : string => {
|
||
const [isFirst, isLast] = elementPosition.value
|
||
let style = ``
|
||
|
||
if(isFirst) {
|
||
style+= 'border-top-left-radius:12rpx; border-bottom-left-radius:12rpx;'
|
||
}
|
||
if(isLast) {
|
||
style+= 'border-top-right-radius:12rpx; border-bottom-right-radius:12rpx;'
|
||
}
|
||
return `
|
||
${style}
|
||
height: ${picker?.itemHeight ?? '50px'};
|
||
background-color: rgba(0, 0, 0, 0.04); ${picker?.indicatorStyle}`
|
||
})
|
||
const itemStyles = computed(() : Map<string, any> => {
|
||
const style = new Map<string, any>();
|
||
if (picker?.itemColor != null) {
|
||
style.set('color', picker.itemColor!)
|
||
}
|
||
if (picker?.itemFontSize != null) {
|
||
style.set('font-size', picker.itemFontSize!)
|
||
}
|
||
if(picker?.itemHeight != null) {
|
||
style.set('line-height', picker.itemHeight!)
|
||
style.set('height', picker.itemHeight!)
|
||
}
|
||
return style
|
||
})
|
||
const itemActiveStyles = computed(() : Map<string, any> => {
|
||
const style = new Map<string, any>();
|
||
if (picker?.itemActiveColor != null) {
|
||
style.set('color', picker.itemActiveColor!)
|
||
}
|
||
if (picker?.itemActiveFontWeight != null) {
|
||
style.set('font-weight', picker.itemActiveFontWeight!)
|
||
}
|
||
return style
|
||
})
|
||
|
||
|
||
const getIndexByValue = (val : PickerValue | null) => {
|
||
let defaultIndex = 0;
|
||
if (val != null) {
|
||
defaultIndex = props.options.findIndex((item) => item.value == val);
|
||
}
|
||
return defaultIndex < 0 ? 0 : defaultIndex;
|
||
};
|
||
|
||
const setIndex = (index : number) => {
|
||
let lastIndex = curIndex.value
|
||
let _index = clamp(index, 0, props.options.length - 1)
|
||
if(props.options.length > _index) {
|
||
curIndex.value = _index;
|
||
curValue.value = props.options[_index].value
|
||
}
|
||
// #ifdef WEB
|
||
if(lastIndex == _index || lastIndex == -1) return
|
||
if (instance.proxy!.$el) {
|
||
const childs = Array.from(instance.proxy!.$el.parentElement.children).slice(column.value + 1);
|
||
childs.forEach((child) => {
|
||
(child as HTMLElement).style.setProperty('--picker-duration', '300ms');
|
||
setTimeout(function () {
|
||
(child as HTMLElement).style.setProperty('--picker-duration', '0ms');
|
||
}, 299);
|
||
})
|
||
}
|
||
// #endif
|
||
|
||
}
|
||
const setValue = (value : PickerValue | null) => {
|
||
if (value == curValue.value) return
|
||
curValue.value = value
|
||
const index = getIndexByValue(value)
|
||
setIndex(index)
|
||
}
|
||
const setOptions = () => { }
|
||
const setUpdateItems = () => {
|
||
const index = clamp(curIndex.value, 0, props.options.length - 1)
|
||
const curItem = props.options.length > index ? props.options[index] : null;
|
||
if (curItem == null) return
|
||
updateItems?.(curItem, index, column.value);
|
||
}
|
||
|
||
const handlePick = (e : UniPickerViewChangeEvent) => {
|
||
if(props.options.length == 0) return
|
||
const index = clamp(e.detail.value[0], 0, props.options.length - 1);
|
||
const curItem = props.options[index];
|
||
if(index == curIndex.value) return
|
||
setIndex(index)
|
||
onPick?.(curItem, index, column.value);
|
||
}
|
||
|
||
const stopValue = watch(() : PickerValue | null => props.value, (v : PickerValue | null) => {
|
||
setValue(v);
|
||
setUpdateItems();
|
||
}, { immediate: true })
|
||
|
||
// #ifdef APP-ANDROID
|
||
const itemRef = ref<UniElement | null>(null)
|
||
const updateItemStyle = () => {
|
||
if (itemRef.value != null) {
|
||
const ctx = itemRef.value!.getDrawableContext()!
|
||
const height = unitConvert(picker?.itemHeight ?? 50);
|
||
const fontSize = unitConvert(picker?.itemFontSize ?? '32rpx');
|
||
const rect = itemRef.value!.getBoundingClientRect()
|
||
const x = itemRef.value!.offsetWidth / 2;
|
||
const itemActiveFontWeight = picker?.itemActiveFontWeight ?? 700
|
||
itemRef.value!.style.setProperty('height', height * props.options.length);
|
||
ctx.reset()
|
||
ctx.fillStyle = picker?.itemActiveColor ?? 'rgba(0,0,0,0.88)';
|
||
ctx.font = `${fontSize}px`;
|
||
ctx.textAlign = 'center'
|
||
ctx.lineWidth = 0.5
|
||
|
||
props.options.forEach((item, index) => {
|
||
const y = height * index + fontSize + (height - fontSize) * 0.4; //(height - fontSize * 1.1)
|
||
ctx.fillText(item.label, x, y)
|
||
if (index == curIndex.value && itemActiveFontWeight > 600) {
|
||
ctx.strokeText(item.label, x, y)
|
||
}
|
||
})
|
||
ctx.update()
|
||
}
|
||
}
|
||
// 防止更新后尺寸不对
|
||
watchEffect(()=> {
|
||
if(curIndex.value > 0){}
|
||
nextTick(updateItemStyle)
|
||
})
|
||
const stop = watch(() : PickerColumnItem[] => props.options, (v : PickerColumnItem[], o : PickerColumnItem[]) => {
|
||
nextTick(updateItemStyle)
|
||
})
|
||
|
||
const resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
|
||
updateItemStyle()
|
||
})
|
||
const stopWatch = watch(():UniElement|null => itemRef.value, (el:UniElement|null) => {
|
||
if(el== null) return
|
||
resizeObserver.observe(el)
|
||
})
|
||
// onMounted(() => {
|
||
// nextTick(updateItemStyle)
|
||
// })
|
||
|
||
// #endif
|
||
onBeforeUnmount(() => {
|
||
manageChildInList?.(instance.proxy! as LPickerItemComponentPublicInstance, false)
|
||
|
||
// #ifdef APP-ANDROID
|
||
stop()
|
||
stopWatch()
|
||
resizeObserver.disconnect()
|
||
// #endif
|
||
})
|
||
// onMounted(()=>{
|
||
// if(props.options.length > 0 && curIndex.value == -1) {
|
||
// curIndex.value = 0
|
||
// }
|
||
// })
|
||
defineExpose({
|
||
setIndex,
|
||
setValue,
|
||
// setOptions,
|
||
// setUpdateItems,
|
||
getIndexByValue
|
||
})
|
||
</script>
|
||
<style lang="scss">
|
||
@import './index';
|
||
</style> |