优化日期选择体验

This commit is contained in:
icssoa
2025-08-07 18:18:18 +08:00
parent b9804c26db
commit 57809c7a37
4 changed files with 117 additions and 54 deletions

View File

@@ -89,15 +89,10 @@ const props = defineProps({
height: {
type: Number,
default: 600
},
// 选择后子集是否回到0
resetOnChange: {
type: Boolean,
default: true
}
});
const emit = defineEmits(["change"]);
const emit = defineEmits(["change-value", "change-index"]);
// 获取窗口宽度,用于计算选择器列宽
const { windowWidth } = uni.getWindowInfo();
@@ -151,33 +146,19 @@ const indicatorStyle = computed(() => {
return str;
});
// 选择器值改变事件
// 监听选择器值改变事件
function onChange(e: UniPickerViewChangeEvent) {
// 获取选择器当前选中值数组
const arr = e.detail.value;
const indexs = e.detail.value;
// 如果选择后子集是否回到0
if (props.resetOnChange) {
// 记录第一个发生变化的列的索引,初始为-1表示未发生变化
let start = -1;
// 获取所有列的值
const values = props.columns.map((c, i) => {
return c[indexs[i]].value;
});
// 遍历原选中值数组
props.value.forEach((e, i) => {
if (start >= 0) {
// 如果之前的列发生过变化,后续列都重置为0
arr[i] = 0;
} else {
// 比较当前列的值是否发生变化
if (e != arr[i]) {
// 记录第一个变化的列的索引
start = i;
}
}
});
}
// 触发change事件,传递新的选中值数组
emit("change", arr);
// 返回所有列的值或下标
emit("change-value", values);
emit("change-index", indexs);
}
</script>

View File

@@ -101,7 +101,8 @@
:headers="headers"
:value="indexes"
:columns="columns"
@change="onChange"
:reset-on-change="false"
@change-value="onChange"
></cl-picker-view>
</view>
@@ -514,12 +515,19 @@ const indexes = computed(() => {
if (isEmpty(value.value)) {
return [];
}
// 遍历每一列,查找当前值在选项中的下标
return value.value.map((e, i) => {
const index = list.value[i].findIndex((a) => a.value == e);
// 如果未找到返回0
let index = list.value[i].findIndex((a) => a.value == e) as number;
// 如果未找到,返回最后一个
if (index == -1) {
return 0;
index = list.value[i].length - 1;
}
// 如果小于0返回0
if (index < 0) {
index = 0;
}
return index;
@@ -548,6 +556,58 @@ function toDate() {
return parts.join("");
}
// 检查边界值
function checkDate(values: number[]): number[] {
if (values.length == 0) {
return values;
}
// 确保至少有6个元素缺失的用默认值填充
const checkedValues = [...values];
const defaultValues = [2000, 1, 1, 0, 0, 0];
for (let i = checkedValues.length; i < 6; i++) {
checkedValues.push(defaultValues[i]);
}
let [year, month, date, hour, minute, second] = checkedValues;
// 检查日期边界(根据年份和月份确定最大天数)
// 判断是否为闰年
const isLeapYear = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
// 每月天数数组2月根据闰年判断
const daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const maxDay = daysInMonth[month - 1];
if (date < 1) {
date = 1;
} else if (date > maxDay) {
date = maxDay;
}
// 检查小时边界 (0-23)
if (hour < 0) {
hour = 0;
} else if (hour > 23) {
hour = 23;
}
// 检查分钟边界 (0-59)
if (minute < 0) {
minute = 0;
} else if (minute > 59) {
minute = 59;
}
// 检查秒钟边界 (0-59)
if (second < 0) {
second = 0;
} else if (second > 59) {
second = 59;
}
return [year, month, date, hour, minute, second];
}
// 显示文本,展示当前选中的日期字符串
const text = ref("");
@@ -563,19 +623,19 @@ function getText() {
}
// 选择器值改变事件更新value
async function onChange(arr: number[]) {
const data = [] as number[];
async function onChange(data: number[]) {
// 更新value
value.value = checkDate(data);
for (let i = 0; i < arr.length; i++) {
const item = list.value[i][arr[i]];
if (!isNull(item)) {
data.push(item.value as number);
}
// 不能大于结束日期
if (dayUts(toDate()).isAfter(dayUts(props.end))) {
value.value = dayUts(props.end).toArray();
}
// 更新value
value.value = data;
// 不能小于开始日期
if (dayUts(toDate()).isBefore(dayUts(props.start))) {
value.value = dayUts(props.start).toArray();
}
// 设置范围值
if (props.rangeable) {
@@ -597,10 +657,10 @@ function setValue(val: string) {
// 如果值为空,使用当前时间
if (isNull(val) || isEmpty(val)) {
value.value = dayUts().toArray();
value.value = checkDate(dayUts().toArray());
} else {
// 否则解析为数组
value.value = dayUts(val).toArray();
value.value = checkDate(dayUts(val).toArray());
getText();
}
}
@@ -669,8 +729,7 @@ function close() {
// 选择器关闭后
function onClosed() {
value.value = [];
values.value = [];
values.value = ["", ""];
}
// 清空选择器,重置显示文本并触发事件
@@ -761,6 +820,14 @@ watch(
}
);
// 更新显示文本
watch(
computed(() => props.labelFormat),
() => {
getText();
}
);
defineExpose({
open,
close,

View File

@@ -33,7 +33,7 @@
:headers="headers"
:columns="columns"
:reset-on-change="false"
@change="onChange"
@change-index="onChange"
></cl-picker-view>
</view>