ElementPlus 的 el-date-picker 组件是 Vue3 项目中常用的日期选择器,它的 disabled-date 属性提供了强大的日期禁用能力。很多开发者刚开始接触这个功能时,可能只停留在"禁用今天之前日期"的基础用法上。但实际上,这个功能可以玩出很多花样。
我们先来看一个最基础的例子,禁用今天之前的日期:
javascript复制const pickerOptions = {
disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
return time.getTime() < today.getTime();
}
}
这段代码中,disabledDate 函数接收一个时间参数,返回 true 表示禁用该日期,false 表示允许选择。这是最基础的用法,但实际项目中,我们往往需要更复杂的逻辑。
比如在酒店预订系统中,我们不仅要考虑日期是否在今天之后,还要考虑房间的库存情况。假设我们有一个 API 返回了不可预订的日期列表,我们可以这样实现:
javascript复制const pickerOptions = {
async disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
// 获取不可预订日期
const unavailableDates = await fetchUnavailableDates();
// 检查是否在今天之前或不可预订
return time.getTime() < today.getTime() ||
unavailableDates.includes(time.toISOString().split('T')[0]);
}
}
这种进阶用法展示了 disabled-date 的真正威力 - 它可以根据业务需求动态决定哪些日期应该被禁用。
在很多业务场景中,我们需要考虑节假日或特殊营业日。比如一个任务管理系统,在节假日不允许创建新任务。这种情况下,我们可以这样实现:
javascript复制const pickerOptions = {
disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
// 节假日列表,可以从API获取
const holidays = ['2023-10-01', '2023-10-02', '2023-10-03'];
const dateStr = time.toISOString().split('T')[0];
// 禁用今天之前日期和节假日
return time.getTime() < today.getTime() ||
holidays.includes(dateStr);
}
}
更复杂的场景下,节假日可能是动态变化的,我们可以结合 API 调用来获取最新的节假日数据:
javascript复制const pickerOptions = {
async disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
// 从API获取节假日数据
const response = await fetch('/api/holidays');
const holidays = await response.json();
const dateStr = time.toISOString().split('T')[0];
return time.getTime() < today.getTime() ||
holidays.some(holiday => holiday.date === dateStr);
}
}
在酒店预订或会议室预约系统中,我们需要根据库存或资源可用性来禁用日期。这种情况下,disabled-date 函数需要处理更复杂的业务逻辑:
javascript复制const pickerOptions = {
async disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
// 获取房间库存数据
const inventory = await fetchRoomInventory();
const dateStr = time.toISOString().split('T')[0];
const dateInventory = inventory.find(item => item.date === dateStr);
// 如果日期在今天之前或库存为0,则禁用
return time.getTime() < today.getTime() ||
(dateInventory && dateInventory.available === 0);
}
}
这种实现方式确保了用户只能选择有可用库存的日期,大大提升了用户体验。
当 disabled-date 函数中包含异步操作时,我们需要考虑性能问题。频繁调用 API 会导致性能下降。解决方案之一是缓存数据:
javascript复制let cachedHolidays = null;
let lastFetchTime = 0;
const pickerOptions = {
async disabledDate(time) {
const now = Date.now();
// 如果缓存超过1小时,重新获取
if (!cachedHolidays || now - lastFetchTime > 3600000) {
const response = await fetch('/api/holidays');
cachedHolidays = await response.json();
lastFetchTime = now;
}
const today = new Date();
today.setHours(0, 0, 0, 0);
const dateStr = time.toISOString().split('T')[0];
return time.getTime() < today.getTime() ||
cachedHolidays.some(holiday => holiday.date === dateStr);
}
}
仅仅禁用日期还不够,好的用户体验应该让用户明白为什么某些日期被禁用。我们可以结合 tooltip 或其他UI元素来提供反馈:
javascript复制<template>
<el-date-picker
v-model="selectedDate"
type="date"
:picker-options="pickerOptions"
@change="handleDateChange"
>
<template #default="scope">
<div class="cell" :class="{ disabled: scope.disabled }">
{{ scope.text }}
<el-tooltip
v-if="scope.disabled && isHoliday(scope.date)"
content="这是节假日,不可选择"
placement="top"
>
<el-icon><question-filled /></el-icon>
</el-tooltip>
</div>
</template>
</el-date-picker>
</template>
在实际项目中,我们经常需要组合多个条件来决定是否禁用日期。比如一个任务管理系统可能需要考虑:
javascript复制const pickerOptions = {
async disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
// 获取各种规则数据
const [holidays, blackoutPeriods] = await Promise.all([
fetchHolidays(),
fetchBlackoutPeriods()
]);
const date = new Date(time);
const dateStr = date.toISOString().split('T')[0];
const dayOfWeek = date.getDay();
// 周末
const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
// 节假日
const isHoliday = holidays.some(h => h.date === dateStr);
// 特定禁用期
const isBlackout = blackoutPeriods.some(period => {
return date >= new Date(period.start) && date <= new Date(period.end);
});
// 组合所有条件
return time.getTime() < today.getTime() ||
isWeekend ||
isHoliday ||
isBlackout;
}
}
为了代码的可维护性,我们可以将复杂的禁用逻辑封装成可复用的策略:
javascript复制// dateRules.js
export const createDateRules = async () => {
const [holidays, blackoutPeriods] = await Promise.all([
fetchHolidays(),
fetchBlackoutPeriods()
]);
return {
isPastDate(date) {
const today = new Date();
today.setHours(0, 0, 0, 0);
return date.getTime() < today.getTime();
},
isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
},
isHoliday(date) {
const dateStr = date.toISOString().split('T')[0];
return holidays.some(h => h.date === dateStr);
},
isBlackout(date) {
return blackoutPeriods.some(period => {
return date >= new Date(period.start) && date <= new Date(period.end);
});
}
};
};
// 在组件中使用
import { createDateRules } from './dateRules';
const pickerOptions = {
async disabledDate(time) {
const date = new Date(time);
const rules = await createDateRules();
return rules.isPastDate(date) ||
rules.isWeekend(date) ||
rules.isHoliday(date) ||
rules.isBlackout(date);
}
}
这种封装方式使得业务规则更清晰,也更容易进行单元测试。