Files
jindengchen-ai-report/cool-unix/uni_modules/cool-ui/components/cl-calendar/picker.uvue

274 lines
5.5 KiB
Plaintext
Raw Normal View History

2025-11-13 10:36:23 +08:00
<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 rounded-2xl;
}
}
</style>