209 lines
4.2 KiB
Plaintext
209 lines
4.2 KiB
Plaintext
<template>
|
|
<view class="cl-picker-view">
|
|
<view class="cl-picker-view__header" v-if="headers.length > 0">
|
|
<cl-text
|
|
:pt="{
|
|
className: 'flex-1 !text-sm text-center'
|
|
}"
|
|
v-for="(label, index) in headers"
|
|
:key="index"
|
|
>{{ label }}</cl-text
|
|
>
|
|
</view>
|
|
|
|
<view
|
|
class="px-[10rpx]"
|
|
:style="{
|
|
height: parseRpx(height)
|
|
}"
|
|
>
|
|
<picker-view
|
|
class="h-full"
|
|
:value="value"
|
|
:mask-style="maskStyle"
|
|
:mask-top-style="maskStyle"
|
|
:mask-bottom-style="maskStyle"
|
|
:indicator-style="indicatorStyle"
|
|
@change="onChange"
|
|
>
|
|
<picker-view-column
|
|
class="cl-select-popup__column"
|
|
v-for="(column, columnIndex) in columns"
|
|
:key="columnIndex"
|
|
>
|
|
<view
|
|
class="cl-picker-view__item"
|
|
:style="{
|
|
height: `${itemHeight}px`
|
|
}"
|
|
v-for="(item, index) in column"
|
|
:key="index"
|
|
>
|
|
<cl-text
|
|
:pt="{
|
|
className: parseClass([
|
|
[isDark, '!text-surface-500'],
|
|
[isDark && index == value[columnIndex], '!text-white']
|
|
])
|
|
}"
|
|
>{{ item.label }}</cl-text
|
|
>
|
|
</view>
|
|
</picker-view-column>
|
|
</picker-view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { forInObject, isAppIOS, isDark, isEqual, isNull, parseClass, rpx2px } from "@/cool";
|
|
import type { ClSelectOption } from "../../types";
|
|
import { parseRpx } from "@/cool";
|
|
import { computed } from "vue";
|
|
import type { PropType } from "vue";
|
|
|
|
defineOptions({
|
|
name: "cl-select-picker-view"
|
|
});
|
|
|
|
const props = defineProps({
|
|
// 选择器表头
|
|
headers: {
|
|
type: Array as PropType<string[]>,
|
|
default: () => []
|
|
},
|
|
// 选择器值
|
|
value: {
|
|
type: Array as PropType<number[]>,
|
|
default: () => []
|
|
},
|
|
// 选择器选项
|
|
columns: {
|
|
type: Array as PropType<ClSelectOption[][]>,
|
|
default: () => []
|
|
},
|
|
// 选择器选项高度
|
|
itemHeight: {
|
|
type: Number,
|
|
default: 42
|
|
},
|
|
// 选择器高度
|
|
height: {
|
|
type: Number,
|
|
default: 600
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(["change-value", "change-index"]);
|
|
|
|
// 获取窗口宽度,用于计算选择器列宽
|
|
const { windowWidth } = uni.getWindowInfo();
|
|
|
|
// 顶部显示表头
|
|
const headers = computed(() => {
|
|
return props.headers.slice(0, props.columns.length);
|
|
});
|
|
|
|
// 遮罩层样式
|
|
const maskStyle = computed(() => {
|
|
if (isDark.value) {
|
|
if(isAppIOS()) {
|
|
return `background-color: rgba(0, 0, 0, 0);`
|
|
}
|
|
|
|
return `background-image: linear-gradient(
|
|
180deg,
|
|
rgba(0, 0, 0, 0),
|
|
rgba(0, 0, 0, 0)
|
|
)`;
|
|
}
|
|
|
|
return "";
|
|
});
|
|
|
|
// 计算选择器列样式
|
|
const indicatorStyle = computed(() => {
|
|
// 根据窗口宽度和列数计算每列宽度
|
|
const width = ((windowWidth - rpx2px(20)) / props.columns.length - rpx2px(2) - 8).toFixed(0);
|
|
|
|
let str = "";
|
|
|
|
// 选择器列样式配置
|
|
const style = {
|
|
height: `${props.itemHeight}px`,
|
|
width: `${width}px`,
|
|
left: "4px",
|
|
backgroundColor: "rgba(10, 10, 10, 0.04)",
|
|
borderRadius: "10px",
|
|
border: "1rpx solid rgba(10, 10, 10, 0.2)"
|
|
};
|
|
|
|
// 深色模式
|
|
if (isDark.value) {
|
|
style.backgroundColor = "rgba(0, 0, 0, 0.1)";
|
|
style.border = "1rpx solid rgba(255, 255, 255, 0.3)";
|
|
}
|
|
|
|
// 构建样式字符串
|
|
forInObject(style, (value, key) => {
|
|
str += `${key}: ${value};`;
|
|
});
|
|
|
|
return str;
|
|
});
|
|
|
|
// 监听选择器值改变事件
|
|
function onChange(e: UniPickerViewChangeEvent) {
|
|
// 获取选择器当前选中值数组
|
|
const indexs = e.detail.value;
|
|
|
|
// 处理因快速滑动导致下级数据未及时渲染而产生的索引越界问题
|
|
indexs.forEach((v, i, arr) => {
|
|
if (i < props.columns.length) {
|
|
const n = props.columns[i].length;
|
|
|
|
if (v >= n) {
|
|
arr[i] = n - 1;
|
|
}
|
|
}
|
|
});
|
|
|
|
// 相同值不触发事件
|
|
if (isEqual(indexs, props.value)) {
|
|
return;
|
|
}
|
|
|
|
// 获取所有列的值
|
|
const values = props.columns.map((c, i) => {
|
|
return isNull(c[indexs[i]]) ? 0 : c[indexs[i]].value;
|
|
});
|
|
|
|
// 返回所有列的值或下标
|
|
emit("change-value", values);
|
|
emit("change-index", indexs);
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.cl-picker-view {
|
|
@apply w-full h-full;
|
|
|
|
&__header {
|
|
@apply flex flex-row items-center py-4;
|
|
}
|
|
|
|
&__item {
|
|
@apply flex flex-row items-center justify-center;
|
|
}
|
|
|
|
.uni-picker-view-indicator {
|
|
// #ifdef H5
|
|
&::after,
|
|
&::before {
|
|
display: none;
|
|
}
|
|
// #endif
|
|
}
|
|
}
|
|
</style>
|