优化日期选择体验

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

@@ -2,14 +2,18 @@
<cl-page> <cl-page>
<view class="p-3"> <view class="p-3">
<demo-item :label="t('基础用法')"> <demo-item :label="t('基础用法')">
<cl-select-date v-model="form.date1" type="date"></cl-select-date> <cl-select-date
v-model="form.date1"
type="date"
@change="onDateChange"
></cl-select-date>
</demo-item> </demo-item>
<demo-item :label="t('固定开始、结束日期')"> <demo-item :label="t('固定开始、结束日期')">
<cl-select-date <cl-select-date
v-model="form.date3" v-model="form.date3"
start="2025-06-01" start="2025-06-01"
end="2026-06-01" end="2027-08-01"
type="date" type="date"
></cl-select-date> ></cl-select-date>
</demo-item> </demo-item>
@@ -34,7 +38,7 @@
<demo-item :label="t('自定义快捷选项')"> <demo-item :label="t('自定义快捷选项')">
<cl-select-date <cl-select-date
v-model:values="form.date5" v-model:values="form.date6"
type="date" type="date"
rangeable rangeable
:shortcuts="shortcuts" :shortcuts="shortcuts"
@@ -43,7 +47,7 @@
<demo-item :label="t('自定义')"> <demo-item :label="t('自定义')">
<cl-select-date <cl-select-date
v-model="form.date6" v-model="form.date7"
:type="type" :type="type"
:label-format="labelFormat" :label-format="labelFormat"
:disabled="isDisabled" :disabled="isDisabled"
@@ -129,13 +133,23 @@ import { dayUts } from "@/cool";
const ui = useUi(); const ui = useUi();
// 测试日期选择变化事件
function onDateChange(value: string) {
console.log("日期选择变化:", value);
ui.showToast({
message: `选择了日期: ${value}`,
type: "success"
});
}
type Form = { type Form = {
date1: string; date1: string;
date2: string; date2: string;
date3: string; date3: string;
date4: string; date4: string;
date5: string[]; date5: string[];
date6: string; date6: string[];
date7: string;
}; };
const form = reactive<Form>({ const form = reactive<Form>({
@@ -144,7 +158,8 @@ const form = reactive<Form>({
date3: "", date3: "",
date4: "", date4: "",
date5: [], date5: [],
date6: "" date6: [],
date7: ""
}); });
const isDisabled = ref(false); const isDisabled = ref(false);

View File

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

View File

@@ -101,7 +101,8 @@
:headers="headers" :headers="headers"
:value="indexes" :value="indexes"
:columns="columns" :columns="columns"
@change="onChange" :reset-on-change="false"
@change-value="onChange"
></cl-picker-view> ></cl-picker-view>
</view> </view>
@@ -514,12 +515,19 @@ const indexes = computed(() => {
if (isEmpty(value.value)) { if (isEmpty(value.value)) {
return []; return [];
} }
// 遍历每一列,查找当前值在选项中的下标 // 遍历每一列,查找当前值在选项中的下标
return value.value.map((e, i) => { return value.value.map((e, i) => {
const index = list.value[i].findIndex((a) => a.value == e); let index = list.value[i].findIndex((a) => a.value == e) as number;
// 如果未找到返回0
// 如果未找到,返回最后一个
if (index == -1) { if (index == -1) {
return 0; index = list.value[i].length - 1;
}
// 如果小于0返回0
if (index < 0) {
index = 0;
} }
return index; return index;
@@ -548,6 +556,58 @@ function toDate() {
return parts.join(""); 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(""); const text = ref("");
@@ -563,19 +623,19 @@ function getText() {
} }
// 选择器值改变事件更新value // 选择器值改变事件更新value
async function onChange(arr: number[]) { async function onChange(data: number[]) {
const data = [] as number[]; // 更新value
value.value = checkDate(data);
for (let i = 0; i < arr.length; i++) { // 不能大于结束日期
const item = list.value[i][arr[i]]; if (dayUts(toDate()).isAfter(dayUts(props.end))) {
value.value = dayUts(props.end).toArray();
if (!isNull(item)) {
data.push(item.value as number);
}
} }
// 更新value // 不能小于开始日期
value.value = data; if (dayUts(toDate()).isBefore(dayUts(props.start))) {
value.value = dayUts(props.start).toArray();
}
// 设置范围值 // 设置范围值
if (props.rangeable) { if (props.rangeable) {
@@ -597,10 +657,10 @@ function setValue(val: string) {
// 如果值为空,使用当前时间 // 如果值为空,使用当前时间
if (isNull(val) || isEmpty(val)) { if (isNull(val) || isEmpty(val)) {
value.value = dayUts().toArray(); value.value = checkDate(dayUts().toArray());
} else { } else {
// 否则解析为数组 // 否则解析为数组
value.value = dayUts(val).toArray(); value.value = checkDate(dayUts(val).toArray());
getText(); getText();
} }
} }
@@ -669,8 +729,7 @@ function close() {
// 选择器关闭后 // 选择器关闭后
function onClosed() { function onClosed() {
value.value = []; values.value = ["", ""];
values.value = [];
} }
// 清空选择器,重置显示文本并触发事件 // 清空选择器,重置显示文本并触发事件
@@ -761,6 +820,14 @@ watch(
} }
); );
// 更新显示文本
watch(
computed(() => props.labelFormat),
() => {
getText();
}
);
defineExpose({ defineExpose({
open, open,
close, close,

View File

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