在uni-app开发中,uni-datetime-picker是一个非常实用的日期选择组件,但官方版本的功能相对基础。很多开发者在使用过程中都会遇到这样的需求:需要限制用户只能选择某个特定范围内的日期,或者禁用某些特定日期。比如在预约系统中,我们通常不希望用户选择过去的日期;在排班系统中,可能需要禁用周末或节假日。
原生组件虽然提供了基本的日期选择功能,但缺少类似Element UI中picker-options这样的高级配置选项。这就导致开发者不得不自己动手改造组件源码。我最近在一个医疗预约项目中就遇到了这个问题,需要实现只能预约未来7天内的日期,并且要屏蔽周末。经过一番摸索,终于找到了一套完整的解决方案。
uni-datetime-picker组件实际上由三个主要部分组成:
要实现禁用日期的功能,我们需要从最外层的父组件传入禁用规则,然后通过层层传递,最终在calendar-item组件中应用这些规则。
根据项目使用的包管理工具不同,修改方式也有所区别:
我推荐使用pnpm的patch功能来管理这些修改,这样可以确保团队成员安装依赖时自动应用这些修改,而不需要每个人都手动改一遍代码。
首先,在业务组件中使用uni-datetime-picker时,我们需要定义一个disabledDate函数,并通过props传递给子组件:
javascript复制<uni-datetime-picker
type="date"
v-model="selectedDate"
:disabledDate="disabledDate"
/>
const disabledDate = (time) => {
// 禁用今天之前的日期
return time.getTime() < Date.now() - 8.64e7;
};
这个函数接收一个日期对象作为参数,返回true表示禁用该日期,false表示可用。上面的例子实现了禁用过去日期的功能。
接下来需要修改三个关键文件:
具体修改点包括:
javascript复制// 在uni-datetime-picker.vue中添加
props: {
disabledDate: {
type: Function,
default: null
}
}
// 在Calendar.vue中同样添加props定义
// 在calendar-item.vue中修改日期渲染逻辑
computed: {
isDisabled() {
return this.disabledDate && this.disabledDate(new Date(this.weeks.fullDate).setHours(0, 0, 0, 0));
}
}
有时候我们需要根据业务逻辑动态调整可选日期范围。比如在酒店预订系统中,入住日期选择后,离店日期必须至少比入住日期晚一天:
javascript复制const disabledCheckoutDate = (time) => {
return time.getTime() < checkinDate.value.getTime() + 8.64e7;
};
这种动态限制需要在父组件中维护状态,并在状态变化时更新禁用规则。
直接修改node_modules中的代码不是个好主意,因为这些修改会在重新安装依赖时丢失。pnpm的patch功能可以完美解决这个问题。
首先执行以下命令创建补丁:
bash复制pnpm patch @dcloudio/uni-ui@1.4.28
这会创建一个临时目录,里面是uni-ui组件的源代码。我们可以直接修改这个目录中的文件,或者把之前修改好的文件复制过来。
修改完成后,执行以下命令提交补丁:
bash复制pnpm patch-commit /path/to/temp/dir
这会在项目根目录下创建patches文件夹,里面保存着我们的修改。同时package.json中会自动添加补丁配置:
json复制"pnpm": {
"patchedDependencies": {
"@dcloudio/uni-ui@1.4.28": "patches/@dcloudio__uni-ui@1.4.28.patch"
}
}
这样其他开发者在安装依赖时,会自动应用这些修改。
除了禁用日期范围,我们还可以实现禁用特定日期列表的功能:
javascript复制const disabledDates = ['2023-10-01', '2023-10-02']; // 要禁用的日期列表
const disabledDate = (time) => {
const dateStr = formatDate(time, 'yyyy-MM-dd');
return disabledDates.includes(dateStr);
};
在某些场景下,我们可能只需要允许选择工作日:
javascript复制const disabledDate = (time) => {
const day = time.getDay();
return day === 0 || day === 6; // 禁用周末
};
还可以实现更复杂的时间段限制,比如只允许选择上午9点到下午6点之间的时间:
javascript复制const disabledTime = (time) => {
const hours = time.getHours();
return hours < 9 || hours >= 18;
};
在实际项目中,我遇到过几个典型问题:
一个优化后的禁用函数示例:
javascript复制const disabledDate = (time) => {
// 统一使用UTC时间避免时区问题
const utcTime = Date.UTC(
time.getFullYear(),
time.getMonth(),
time.getDate()
);
const today = new Date();
const utcToday = Date.UTC(
today.getFullYear(),
today.getMonth(),
today.getDate()
);
return utcTime < utcToday;
};
经过多个项目的实践,我总结出以下几点经验:
这套方案已经在多个生产环境中验证过,包括电商限时活动、医疗预约、酒店预订等场景,效果非常稳定。虽然需要修改源码,但通过pnpm patch可以很好地管理这些定制化需求。