cl-calendar 支持添加上下文案

This commit is contained in:
icssoa
2025-09-10 10:58:31 +08:00
parent 91e79a4df7
commit 21dd6a5eed
5 changed files with 410 additions and 161 deletions

View File

@@ -5,37 +5,188 @@
<cl-calendar-select v-model="date"></cl-calendar-select> <cl-calendar-select v-model="date"></cl-calendar-select>
</demo-item> </demo-item>
<demo-item :label="t('范围选择器')"> <demo-item :label="t('多选')">
<cl-calendar-select v-model:date="dateArr" mode="range"></cl-calendar-select> <cl-calendar-select v-model:date="dateArr" mode="multiple"></cl-calendar-select>
</demo-item>
<demo-item :label="t('范围选')">
<cl-calendar-select v-model:date="dateRange" mode="range"></cl-calendar-select>
</demo-item> </demo-item>
<demo-item :label="t('禁用部分日期')"> <demo-item :label="t('禁用部分日期')">
<cl-calendar-select <cl-calendar-select v-model="date" :date-config="dateConfig"></cl-calendar-select>
v-model="date"
:disabled-date="disabledDate"
></cl-calendar-select>
</demo-item> </demo-item>
<!-- <demo-item :label="t('日历长列表')">
<cl-button>{{ t("打开日历长列表") }}</cl-button>
</demo-item> -->
<demo-item :label="t('日历面板')"> <demo-item :label="t('日历面板')">
<cl-calendar v-model="date"></cl-calendar> <cl-calendar
v-model:date="dateRange2"
mode="range"
:month="10"
:show-header="isShowHeader"
:show-weeks="isShowWeeks"
:show-other-month="isShowOtherMonth"
:date-config="dateConfig2"
@change="onChange"
></cl-calendar>
<cl-list
border
:pt="{
className: 'mt-5'
}"
>
<cl-list-item :label="t('自定义文案和颜色')">
<cl-switch v-model="isCustomDateConfig"></cl-switch>
</cl-list-item>
<cl-list-item :label="t('显示头')">
<cl-switch v-model="isShowHeader"></cl-switch>
</cl-list-item>
<cl-list-item :label="t('显示星期')">
<cl-switch v-model="isShowWeeks"></cl-switch>
</cl-list-item>
<cl-list-item :label="t('显示其他月份')">
<cl-switch v-model="isShowOtherMonth"></cl-switch>
</cl-list-item>
</cl-list>
</demo-item> </demo-item>
</view> </view>
</cl-page> </cl-page>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { computed, ref } from "vue";
import { t } from "@/locale"; import { t } from "@/locale";
import DemoItem from "../components/item.uvue"; import DemoItem from "../components/item.uvue";
import { dayUts } from "@/cool"; import { dayUts, first, last } from "@/cool";
import type { ClCalendarDateConfig } from "@/uni_modules/cool-ui";
const date = ref<string | null>(dayUts().format("YYYY-MM-DD")); const date = ref<string | null>(dayUts().format("YYYY-MM-DD"));
const dateArr = ref<string[]>(["2025-09-02", "2025-09-09"]); const dateArr = ref<string[]>([
dayUts().format("YYYY-MM-DD"),
const disabledDate = ref<string[]>([ dayUts().add(1, "day").format("YYYY-MM-DD")
dayUts().add(2, "day").format("YYYY-MM-DD"),
dayUts().add(4, "day").format("YYYY-MM-DD"),
dayUts().add(6, "day").format("YYYY-MM-DD")
]); ]);
const dateRange = ref<string[]>([
dayUts().format("YYYY-MM-DD"),
dayUts().add(10, "day").format("YYYY-MM-DD")
]);
const dateConfig = ref<ClCalendarDateConfig[]>([
{
date: dayUts().add(1, "day").format("YYYY-MM-DD"),
disabled: true
},
{
date: dayUts().add(2, "day").format("YYYY-MM-DD"),
disabled: true
},
{
date: dayUts().add(3, "day").format("YYYY-MM-DD"),
disabled: true
}
]);
const isShowHeader = ref(true);
const isShowWeeks = ref(true);
const isShowOtherMonth = ref(true);
const isCustomDateConfig = ref(true);
const dateRange2 = ref<string[]>([]);
const dateConfig2 = computed(() => {
const dates = (
isCustomDateConfig.value
? [
{
date: "2025-10-01",
topText: "国庆节",
bottomText: "¥958",
color: "red"
},
{
date: "2025-10-02",
topText: "休",
bottomText: "¥613",
color: "red"
},
{
date: "2025-10-03",
topText: "休",
bottomText: "¥613",
color: "red"
},
{
date: "2025-10-04",
topText: "休",
bottomText: "¥613",
color: "red"
},
{
date: "2025-10-05",
topText: "休",
bottomText: "¥613",
color: "red"
},
{
date: "2025-10-06",
topText: "休",
bottomText: "¥613",
color: "red"
},
{
date: "2025-10-07",
topText: "休",
bottomText: "¥613",
color: "red"
},
{
date: "2025-10-08",
topText: "休",
bottomText: "¥613",
color: "red"
}
]
: []
) as ClCalendarDateConfig[];
const startDate = first(dateRange2.value);
const endDate = last(dateRange2.value);
if (startDate != null) {
const item = dates.find((e) => e.date == startDate);
if (item == null) {
dates.push({
date: startDate,
bottomText: "入住"
} as ClCalendarDateConfig);
} else {
item.bottomText = "入住";
}
}
if (endDate != null && dateRange2.value.length > 1) {
const item = dates.find((e) => e.date == endDate);
if (item == null) {
dates.push({
date: endDate,
bottomText: "离店"
} as ClCalendarDateConfig);
} else {
item.bottomText = "离店";
}
}
return dates;
});
function onChange(date: string[]) {
console.log("日期变化:", date);
}
</script> </script>

View File

@@ -33,7 +33,8 @@
v-model="value" v-model="value"
v-model:date="date" v-model:date="date"
:mode="mode" :mode="mode"
:disabled-date="disabledDate" :date-config="dateConfig"
@change="onCalendarChange"
></cl-calendar> ></cl-calendar>
</view> </view>
@@ -68,7 +69,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, type PropType } from "vue"; import { ref, computed, type PropType } from "vue";
import type { ClCalendarMode } from "../../types"; import type { ClCalendarDateConfig, ClCalendarMode } from "../../types";
import { isEmpty, parsePt } from "@/cool"; import { isEmpty, parsePt } from "@/cool";
import type { ClSelectTriggerPassThrough } from "../cl-select-trigger/props"; import type { ClSelectTriggerPassThrough } from "../cl-select-trigger/props";
import type { ClPopupPassThrough } from "../cl-popup/props"; import type { ClPopupPassThrough } from "../cl-popup/props";
@@ -105,9 +106,9 @@ const props = defineProps({
type: String as PropType<ClCalendarMode>, type: String as PropType<ClCalendarMode>,
default: "single" default: "single"
}, },
// 禁用的日期 // 日期配置
disabledDate: { dateConfig: {
type: Array as PropType<string[]>, type: Array as PropType<ClCalendarDateConfig[]>,
default: () => [] default: () => []
}, },
// 选择器标题 // 选择器标题
@@ -132,6 +133,11 @@ const props = defineProps({
}, },
// 分隔符 // 分隔符
splitor: { splitor: {
type: String,
default: "、"
},
// 范围分隔符
rangeSplitor: {
type: String, type: String,
default: () => t(" 至 ") default: () => t(" 至 ")
}, },
@@ -158,7 +164,7 @@ const props = defineProps({
}); });
// 定义事件 // 定义事件
const emit = defineEmits(["update:modelValue", "update:date", "change"]); const emit = defineEmits(["update:modelValue", "update:date", "change", "select"]);
// 弹出层引用 // 弹出层引用
const popupRef = ref<ClPopupComponentPublicInstance | null>(null); const popupRef = ref<ClPopupComponentPublicInstance | null>(null);
@@ -180,7 +186,16 @@ const date = ref<string[]>([]);
// 显示文本 // 显示文本
const text = computed(() => { const text = computed(() => {
return props.mode == "single" ? (props.modelValue ?? "") : props.date.join(props.splitor); switch (props.mode) {
case "single":
return props.modelValue ?? "";
case "multiple":
return props.date.join(props.splitor);
case "range":
return props.date.join(props.rangeSplitor);
default:
return "";
}
}); });
// 选择器显示状态 // 选择器显示状态
@@ -249,6 +264,11 @@ function confirm() {
close(); close();
} }
// 日历变化
function onCalendarChange(date: string[]) {
emit("select", date);
}
defineExpose({ defineExpose({
open, open,
close close

View File

@@ -40,7 +40,7 @@
</view> </view>
<!-- 星期标题行 --> <!-- 星期标题行 -->
<view class="cl-calendar__weeks" :style="{ gap: `${cellGap}px` }"> <view class="cl-calendar__weeks" :style="{ gap: `${cellGap}px` }" v-if="showWeeks">
<view class="cl-calendar__weeks-item" v-for="weekName in weekLabels" :key="weekName"> <view class="cl-calendar__weeks-item" v-for="weekName in weekLabels" :key="weekName">
<cl-text>{{ weekName }}</cl-text> <cl-text>{{ weekName }}</cl-text>
</view> </view>
@@ -53,7 +53,6 @@
:style="{ height: `${viewHeight}px`, gap: `${cellGap}px` }" :style="{ height: `${viewHeight}px`, gap: `${cellGap}px` }"
@tap="onTap" @tap="onTap"
> >
<!-- Web端使用DOM渲染 -->
<!-- #ifndef APP --> <!-- #ifndef APP -->
<view <view
class="cl-calendar__view-row" class="cl-calendar__view-row"
@@ -73,12 +72,41 @@
'is-today': dateCell.isToday, 'is-today': dateCell.isToday,
'is-other-month': !dateCell.isCurrentMonth 'is-other-month': !dateCell.isCurrentMonth
}" }"
:style="{ height: cellHeight + 'px' }" :style="{
height: cellHeight + 'px',
backgroundColor: getCellBgColor(dateCell)
}"
@click.stop="selectDateCell(dateCell)" @click.stop="selectDateCell(dateCell)"
> >
<cl-text :color="getCellColor(dateCell)" :size="`${fontSize}px`">{{ <!-- 顶部文本 -->
dateCell.date <cl-text
}}</cl-text> :size="20"
:color="getCellTextColor(dateCell)"
:pt="{
className: 'absolute top-[2px]'
}"
>{{ dateCell.topText }}</cl-text
>
<!-- 主日期数字 -->
<cl-text
:color="getCellTextColor(dateCell)"
:size="`${fontSize}px`"
:pt="{
className: 'font-bold'
}"
>{{ dateCell.date }}</cl-text
>
<!-- 底部文本 -->
<cl-text
:size="20"
:color="getCellTextColor(dateCell)"
:pt="{
className: 'absolute bottom-[2px]'
}"
>{{ dateCell.bottomText }}</cl-text
>
</view> </view>
</view> </view>
<!-- #endif --> <!-- #endif -->
@@ -91,7 +119,7 @@ import { computed, nextTick, onMounted, ref, watch, type PropType } from "vue";
import { ctx, dayUts, first, isDark, parsePt, useRefs } from "@/cool"; import { ctx, dayUts, first, isDark, parsePt, useRefs } from "@/cool";
import CalendarPicker from "./picker.uvue"; import CalendarPicker from "./picker.uvue";
import { $t, t } from "@/locale"; import { $t, t } from "@/locale";
import type { ClCalendarMode } from "../../types"; import type { ClCalendarDateConfig, ClCalendarMode } from "../../types";
defineOptions({ defineOptions({
name: "cl-calendar" name: "cl-calendar"
@@ -107,6 +135,9 @@ type DateCell = {
fullDate: string; // 完整日期格式 YYYY-MM-DD fullDate: string; // 完整日期格式 YYYY-MM-DD
isDisabled: boolean; // 是否被禁用 isDisabled: boolean; // 是否被禁用
isHide: boolean; // 是否隐藏显示 isHide: boolean; // 是否隐藏显示
topText: string; // 顶部文案
bottomText: string; // 底部文案
color: string; // 颜色
}; };
// 组件属性定义 // 组件属性定义
@@ -116,16 +147,6 @@ const props = defineProps({
type: Object, type: Object,
default: () => ({}) default: () => ({})
}, },
// 是否显示头部导航栏
showHeader: {
type: Boolean,
default: true
},
// 日期选择模式:单选/多选/范围选择
mode: {
type: String as PropType<ClCalendarMode>,
default: "single"
},
// 当前选中的日期值(单选模式) // 当前选中的日期值(单选模式)
modelValue: { modelValue: {
type: String as PropType<string | null>, type: String as PropType<string | null>,
@@ -136,65 +157,38 @@ const props = defineProps({
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: () => [] default: () => []
}, },
// 日期选择模式:单选/多选/范围选择
mode: {
type: String as PropType<ClCalendarMode>,
default: "single"
},
// 日期配置
dateConfig: {
type: Array as PropType<ClCalendarDateConfig[]>,
default: () => []
},
// 设置年份
year: {
type: Number
},
// 设置月份
month: {
type: Number
},
// 是否显示其他月份的日期 // 是否显示其他月份的日期
showOtherMonth: { showOtherMonth: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 禁用的日期列表 // 是否显示头部导航栏
disabledDate: { showHeader: {
type: Array as PropType<string[]>, type: Boolean,
default: () => [] default: true
}, },
// 单元格高度 // 是否显示星期
cellHeight: { showWeeks: {
type: Number, type: Boolean,
default: 60 default: true
},
// 单元格间距
cellGap: {
type: Number,
default: 0
},
// 字体大小
fontSize: {
type: Number,
default: 14
},
// 当前月日期颜色
currentMonthColor: {
type: String,
default: () => ctx.color["surface-700"] as string
},
// 其他月日期颜色
otherMonthColor: {
type: String,
default: () => ctx.color["surface-300"] as string
},
// 今天日期颜色
todayColor: {
type: String,
default: "#ff6b6b"
},
// 选中日期文字颜色
selectedTextColor: {
type: String,
default: "#ffffff"
},
// 选中日期背景色
selectedBgColor: {
type: String,
default: () => ctx.color["primary-500"] as string
},
// 范围选择背景色
rangeBgColor: {
type: String,
default: () => ctx.color["primary-100"] as string
},
// 禁用的日期颜色
disabledColor: {
type: String,
default: () => ctx.color["surface-300"] as string
} }
}); });
@@ -209,6 +203,39 @@ type PassThrough = {
// 解析透传样式配置 // 解析透传样式配置
const pt = computed(() => parsePt<PassThrough>(props.pt)); const pt = computed(() => parsePt<PassThrough>(props.pt));
// 主色
const color = ref(ctx.color["primary-500"] as string);
// 单元格高度
const cellHeight = ref(66);
// 单元格间距
const cellGap = ref(0);
// 字体大小
const fontSize = ref(14);
// 当前月份日期颜色
const textColor = computed(() => {
return isDark.value ? "white" : (ctx.color["surface-700"] as string);
});
// 其他月份日期颜色
const textOtherMonthColor = computed(() => {
return isDark.value
? (ctx.color["surface-500"] as string)
: (ctx.color["surface-300"] as string);
});
// 禁用日期颜色
const textDisabledColor = computed(() => {
return isDark.value
? (ctx.color["surface-500"] as string)
: (ctx.color["surface-300"] as string);
});
// 今天日期颜色
const textTodayColor = ref("#ff6b6b");
// 选中日期颜色
const textSelectedColor = ref("#ffffff");
// 选中日期背景颜色
const bgSelectedColor = ref(color.value);
// 范围选择背景颜色
const bgRangeColor = ref(color.value + "11");
// 组件引用管理器 // 组件引用管理器
const refs = useRefs(); const refs = useRefs();
@@ -223,7 +250,7 @@ const currentMonth = ref(0);
// 视图高度 // 视图高度
const viewHeight = computed(() => { const viewHeight = computed(() => {
return props.cellHeight * 6 + "px"; return cellHeight.value * 6;
}); });
// 单元格宽度 // 单元格宽度
@@ -266,7 +293,7 @@ function isDateSelected(dateStr: string): boolean {
* @param dateStr 日期字符串 YYYY-MM-DD * @param dateStr 日期字符串 YYYY-MM-DD
*/ */
function isDateDisabled(dateStr: string): boolean { function isDateDisabled(dateStr: string): boolean {
return props.disabledDate.includes(dateStr); return props.dateConfig.some((config) => config.date == dateStr && config.disabled == true);
} }
/** /**
@@ -284,6 +311,63 @@ function isDateInRange(dateStr: string): boolean {
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate); return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
} }
/**
* 获取单元格字体颜色
* @param dateCell 日期单元格数据
* @returns 字体颜色
*/
function getCellTextColor(dateCell: DateCell): string {
// 选中的日期文字颜色
if (dateCell.isSelected) {
return textSelectedColor.value;
}
if (dateCell.color != "") {
return dateCell.color;
}
// 范围选择日期颜色
if (dateCell.isRange) {
return color.value;
}
// 禁用的日期颜色
if (dateCell.isDisabled) {
return textDisabledColor.value;
}
// 今天日期颜色
if (dateCell.isToday) {
return textTodayColor.value;
}
// 当前月份日期颜色
if (dateCell.isCurrentMonth) {
return textColor.value;
}
// 其他月份日期颜色
return textOtherMonthColor.value;
}
/**
* 获取单元格背景颜色
* @param dateCell 日期单元格数据
* @returns 背景颜色
*/
function getCellBgColor(dateCell: DateCell): string {
if (dateCell.isSelected) {
return bgSelectedColor.value;
}
if (dateCell.isRange) {
return bgRangeColor.value;
}
return "transparent";
}
/** /**
* 计算并生成日历矩阵数据 * 计算并生成日历矩阵数据
* 生成6行7列共42个日期包含上月末尾和下月开头的日期 * 生成6行7列共42个日期包含上月末尾和下月开头的日期
@@ -314,6 +398,9 @@ function calculateDateMatrix() {
nativeDate.getMonth() + 1 == currentMonth.value && nativeDate.getMonth() + 1 == currentMonth.value &&
nativeDate.getFullYear() == currentYear.value; nativeDate.getFullYear() == currentYear.value;
// 日期配置
const dateConfig = props.dateConfig.find((config) => config.date == fullDateStr);
// 构建日期单元格数据 // 构建日期单元格数据
const dateCell = { const dateCell = {
date: `${dayNumber}`, date: `${dayNumber}`,
@@ -323,7 +410,10 @@ function calculateDateMatrix() {
isRange: isDateInRange(fullDateStr), isRange: isDateInRange(fullDateStr),
fullDate: fullDateStr, fullDate: fullDateStr,
isDisabled: isDateDisabled(fullDateStr), isDisabled: isDateDisabled(fullDateStr),
isHide: false isHide: false,
topText: dateConfig?.topText ?? "",
bottomText: dateConfig?.bottomText ?? "",
color: dateConfig?.color ?? ""
} as DateCell; } as DateCell;
// 根据配置决定是否隐藏相邻月份的日期 // 根据配置决定是否隐藏相邻月份的日期
@@ -364,47 +454,54 @@ async function renderCalendarCanvas() {
function drawSingleCell(dateCell: DateCell, colIndex: number, rowIndex: number) { function drawSingleCell(dateCell: DateCell, colIndex: number, rowIndex: number) {
// 计算单元格位置 // 计算单元格位置
const cellX = colIndex * cellWidth.value; const cellX = colIndex * cellWidth.value;
const cellY = rowIndex * props.cellHeight; const cellY = rowIndex * cellHeight.value;
const centerX = cellX + cellWidth.value / 2; const centerX = cellX + cellWidth.value / 2;
const centerY = cellY + props.cellHeight / 2; const centerY = cellY + cellHeight.value / 2;
// 绘制背景(选中状态或范围状态) // 绘制背景(选中状态或范围状态)
if (dateCell.isSelected || dateCell.isRange) { if (dateCell.isSelected || dateCell.isRange) {
const padding = props.cellGap; // 使用间距作为内边距 const padding = cellGap.value; // 使用间距作为内边距
const bgX = cellX + padding; const bgX = cellX + padding;
const bgY = cellY + padding; const bgY = cellY + padding;
const bgWidth = cellWidth.value - padding * 2; const bgWidth = cellWidth.value - padding * 2;
const bgHeight = props.cellHeight - padding * 2; const bgHeight = cellHeight.value - padding * 2;
// 设置背景颜色 // 设置背景颜色
if (dateCell.isSelected) { if (dateCell.isSelected) {
canvasContext!.fillStyle = props.selectedBgColor; canvasContext!.fillStyle = bgSelectedColor.value;
} }
if (dateCell.isRange) { if (dateCell.isRange) {
canvasContext!.fillStyle = props.rangeBgColor; canvasContext!.fillStyle = bgRangeColor.value;
} }
canvasContext!.fillRect(bgX, bgY, bgWidth, bgHeight); // 绘制背景矩形 canvasContext!.fillRect(bgX, bgY, bgWidth, bgHeight); // 绘制背景矩形
} }
// 设置文字样式 // 获取单元格文字颜色
canvasContext!.font = `${props.fontSize}px sans-serif`; const cellTextColor = getCellTextColor(dateCell);
canvasContext!.textAlign = "center"; canvasContext!.textAlign = "center";
// 根据状态设置文字颜色 // 绘制顶部文本
if (dateCell.isSelected) { if (dateCell.topText != "") {
canvasContext!.fillStyle = props.selectedTextColor; canvasContext!.font = `${Math.floor(fontSize.value * 0.75)}px sans-serif`;
} else if (dateCell.isToday) { canvasContext!.fillStyle = cellTextColor;
canvasContext!.fillStyle = props.todayColor; const topY = cellY + 16; // 距离顶部
} else if (dateCell.isCurrentMonth) { canvasContext!.fillText(dateCell.topText, centerX, topY);
canvasContext!.fillStyle = props.currentMonthColor;
} else {
canvasContext!.fillStyle = props.otherMonthColor;
} }
// 绘制日期数字(垂直居中对齐) // 绘制日期数字
const textOffsetY = (props.fontSize / 2) * 0.7; canvasContext!.font = `${fontSize.value}px sans-serif`;
canvasContext!.fillStyle = cellTextColor;
const textOffsetY = (fontSize.value / 2) * 0.7;
canvasContext!.fillText(dateCell.date.toString(), centerX, centerY + textOffsetY); canvasContext!.fillText(dateCell.date.toString(), centerX, centerY + textOffsetY);
// 绘制底部文本
if (dateCell.bottomText != "") {
canvasContext!.font = `${Math.floor(fontSize.value * 0.75)}px sans-serif`;
canvasContext!.fillStyle = cellTextColor;
const bottomY = cellY + cellHeight.value - 8; // 距离底部
canvasContext!.fillText(dateCell.bottomText, centerX, bottomY);
}
} }
// 获取容器尺寸信息 // 获取容器尺寸信息
@@ -425,7 +522,10 @@ async function renderCalendarCanvas() {
const weekRow = dateMatrix.value[rowIndex]; const weekRow = dateMatrix.value[rowIndex];
for (let colIndex = 0; colIndex < weekRow.length; colIndex++) { for (let colIndex = 0; colIndex < weekRow.length; colIndex++) {
const dateCell = weekRow[colIndex]; const dateCell = weekRow[colIndex];
drawSingleCell(dateCell, colIndex, rowIndex);
if (!dateCell.isHide) {
drawSingleCell(dateCell, colIndex, rowIndex);
}
} }
} }
@@ -489,42 +589,11 @@ function selectDateCell(dateCell: DateCell) {
renderCalendarCanvas(); renderCalendarCanvas();
} }
/**
* 获取单元格字体颜色
* @param dateCell 日期单元格数据
* @returns 字体颜色
*/
function getCellColor(dateCell: DateCell): string {
// 禁用的日期颜色
if (dateCell.isDisabled) {
return props.disabledColor;
}
// 选中的日期文字颜色
if (dateCell.isSelected) {
return props.selectedTextColor;
}
// 今天日期颜色
if (dateCell.isToday) {
return props.todayColor;
}
// 当前月份日期颜色
if (dateCell.isCurrentMonth) {
return props.currentMonthColor;
}
// 其他月份日期颜色
return props.otherMonthColor;
}
/** /**
* 处理年月选择器的变化事件 * 处理年月选择器的变化事件
* @param yearMonthArray [年份, 月份] 数组 * @param yearMonthArray [年份, 月份] 数组
*/ */
function onYearMonthChange(yearMonthArray: number[]) { function onYearMonthChange(yearMonthArray: number[]) {
console.log("年月选择器变化:", yearMonthArray);
currentYear.value = yearMonthArray[0]; currentYear.value = yearMonthArray[0];
currentMonth.value = yearMonthArray[1]; currentMonth.value = yearMonthArray[1];
@@ -550,7 +619,7 @@ async function onTap(e: UniPointerEvent) {
// 根据坐标计算对应的行列索引 // 根据坐标计算对应的行列索引
const columnIndex = Math.floor(relativeX / cellWidth.value); const columnIndex = Math.floor(relativeX / cellWidth.value);
const rowIndex = Math.floor(relativeY / props.cellHeight); const rowIndex = Math.floor(relativeY / cellHeight.value);
// 边界检查:确保索引在有效范围内 // 边界检查:确保索引在有效范围内
if ( if (
@@ -613,8 +682,8 @@ function parseDate() {
const initialDate = first(selectedDates.value); const initialDate = first(selectedDates.value);
const [initialYear, initialMonth] = dayUts(initialDate).toArray(); const [initialYear, initialMonth] = dayUts(initialDate).toArray();
currentYear.value = initialYear; currentYear.value = props.year ?? initialYear;
currentMonth.value = initialMonth; currentMonth.value = props.month ?? initialMonth;
// 计算初始日历数据 // 计算初始日历数据
calculateDateMatrix(); calculateDateMatrix();
@@ -648,6 +717,18 @@ onMounted(() => {
immediate: true immediate: true
} }
); );
// 重新渲染
watch(
computed(() => [props.dateConfig, props.showOtherMonth]),
() => {
calculateDateMatrix();
renderCalendarCanvas();
},
{
deep: true
}
);
}); });
</script> </script>
@@ -694,7 +775,6 @@ onMounted(() => {
&__view { &__view {
@apply w-full; @apply w-full;
/* Web端DOM渲染样式 */
// #ifndef APP // #ifndef APP
/* 日期行样式 */ /* 日期行样式 */
&-row { &-row {
@@ -706,16 +786,6 @@ onMounted(() => {
@apply flex-1 flex flex-col items-center justify-center relative; @apply flex-1 flex flex-col items-center justify-center relative;
height: 80rpx; height: 80rpx;
/* 选中状态样式 */
&.is-selected {
background-color: v-bind("props.selectedBgColor");
}
/* 范围选择背景样式 */
&.is-range {
background-color: v-bind("props.rangeBgColor");
}
/* 隐藏状态(相邻月份日期) */ /* 隐藏状态(相邻月份日期) */
&.is-hide { &.is-hide {
opacity: 0; opacity: 0;

View File

@@ -140,7 +140,7 @@ const list = computed(() => {
// - 年份模式下显示“起始年 - 结束年” // - 年份模式下显示“起始年 - 结束年”
const title = computed(() => { const title = computed(() => {
return mode.value == "month" return mode.value == "month"
? `${year.value}` ? `${year.value}`
: `${first(list.value)?.label} - ${last(list.value)?.label}`; : `${first(list.value)?.label} - ${last(list.value)?.label}`;
}); });

View File

@@ -200,3 +200,11 @@ export type ClTreeNodeInfo = {
}; };
export type ClCalendarMode = "single" | "multiple" | "range"; export type ClCalendarMode = "single" | "multiple" | "range";
export type ClCalendarDateConfig = {
date: string;
topText?: string;
bottomText?: string;
disabled?: boolean;
color?: string;
};