Files
WAI_Project_UNIX/uni_modules/cool-ui/components/cl-calendar/picker.uvue
2025-09-09 22:08:21 +08:00

274 lines
5.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view
class="cl-calendar-picker"
:class="{
'is-dark': isDark
}"
v-if="visible"
>
<view class="cl-calendar-picker__header">
<view
class="cl-calendar-picker__prev"
:class="{
'is-dark': isDark
}"
@tap="prev"
>
<cl-icon name="arrow-left-double-line"></cl-icon>
</view>
<view class="cl-calendar-picker__date" @tap="toMode('year')">
<cl-text
:pt="{
className: 'text-lg'
}"
>
{{ title }}
</cl-text>
</view>
<view
class="cl-calendar-picker__next"
:class="{
'is-dark': isDark
}"
@tap="next"
>
<cl-icon name="arrow-right-double-line"></cl-icon>
</view>
</view>
<view class="cl-calendar-picker__list">
<view
class="cl-calendar-picker__item"
v-for="item in list"
:key="item.value"
@tap="select(item.value)"
>
<cl-text
:pt="{
className: parseClass([[item.value == value, 'text-primary-500']])
}"
>{{ item.label }}</cl-text
>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { first, isDark, last, parseClass } from "@/cool";
import { t } from "@/locale";
import { computed, ref } from "vue";
defineOptions({
name: "cl-calendar-picker"
});
// 定义日历选择器的条目类型
type Item = {
label: string; // 显示的标签,如"1月"、"2024"
value: number; // 对应的数值如1、2024
};
// 定义组件接收的属性年份和月份均为数字类型默认值为0
const props = defineProps({
year: {
type: Number,
default: 0
},
month: {
type: Number,
default: 0
}
});
// 定义组件可触发的事件,这里只定义了"change"事件
const emit = defineEmits(["change"]);
// 当前选择的模式,"year"表示选择年份,"month"表示选择月份,默认是"month"
const mode = ref<"year" | "month">("month");
// 当前选中的年份
const year = ref(0);
// 当前选中的月份
const month = ref(0);
// 当前年份选择面板的起始年份如2020-2029则startYear为2020
const startYear = ref(0);
// 当前选中的值,若为月份模式则为月份,否则为年份
const value = computed(() => {
return mode.value == "month" ? month.value : year.value;
});
// 计算可供选择的列表:
// - 若为月份模式返回1-12月
// - 若为年份模式返回以startYear为起点的连续10年
const list = computed(() => {
if (mode.value == "month") {
return [
{ label: t("1月"), value: 1 },
{ label: t("2月"), value: 2 },
{ label: t("3月"), value: 3 },
{ label: t("4月"), value: 4 },
{ label: t("5月"), value: 5 },
{ label: t("6月"), value: 6 },
{ label: t("7月"), value: 7 },
{ label: t("8月"), value: 8 },
{ label: t("9月"), value: 9 },
{ label: t("10月"), value: 10 },
{ label: t("11月"), value: 11 },
{ label: t("12月"), value: 12 }
] as Item[];
} else {
const years: Item[] = [];
// 生成10个连续年份
for (let i = 0; i < 10; i++) {
years.push({
label: `${startYear.value + i}`,
value: startYear.value + i
});
}
return years;
}
});
// 计算标题内容:
// - 月份模式下显示“xxxx年”
// - 年份模式下显示“起始年 - 结束年”
const title = computed(() => {
return mode.value == "month"
? `${year.value}年`
: `${first(list.value)?.label} - ${last(list.value)?.label}`;
});
// 控制选择器弹窗的显示与隐藏
const visible = ref(false);
/**
* 打开选择器,并初始化年份、月份、起始年份
*/
function open() {
visible.value = true;
// 初始化当前年份和月份为传入的props
year.value = props.year;
month.value = props.month;
// 计算当前年份所在的十年区间的起始年份
startYear.value = Math.floor(year.value / 10) * 10;
}
/**
* 关闭选择器
*/
function close() {
visible.value = false;
}
/**
* 切换选择模式(年份/月份)
* @param val "year" 或 "month"
*/
function toMode(val: "year" | "month") {
mode.value = val;
}
/**
* 选择某个值(年份或月份)
* @param val 选中的值
*/
function select(val: number) {
if (mode.value == "month") {
// 选择月份后关闭弹窗并触发change事件
month.value = val;
close();
emit("change", [year.value, month.value]);
} else {
// 选择年份后,切换到月份选择模式
year.value = val;
toMode("month");
}
}
/**
* 切换到上一个区间
* - 月份模式下年份减1
* - 年份模式下起始年份减10
*/
function prev() {
if (mode.value == "month") {
year.value -= 1;
} else {
startYear.value -= 10;
}
}
/**
* 切换到下一个区间
* - 月份模式下年份加1
* - 年份模式下起始年份加10
*/
function next() {
if (mode.value == "month") {
year.value += 1;
} else {
startYear.value += 10;
}
}
defineExpose({
open,
close
});
</script>
<style lang="scss" scoped>
.cl-calendar-picker {
@apply flex flex-col absolute left-0 top-0 w-full h-full bg-white z-10;
&__header {
@apply flex flex-row items-center justify-between w-full p-3;
}
&__prev,
&__next {
@apply flex flex-row items-center justify-center rounded-full bg-surface-100;
width: 60rpx;
height: 60rpx;
&.is-dark {
@apply bg-surface-700;
}
}
&__date {
@apply flex flex-row items-center justify-center;
}
&__list {
@apply flex flex-row flex-wrap;
}
&__item {
@apply flex flex-row items-center justify-center;
height: 100rpx;
width: 25%;
&-bg {
@apply px-4 py-2;
&.is-active {
@apply bg-primary-500 rounded-xl;
}
}
}
&.is-dark {
@apply bg-surface-800;
}
}
</style>