开发背景:Vue+element组件开发
业务需求:用户提交请假申请单,请假申请的业务逻辑处理
实现:用户选择开始时间需要大于本地时间,不得大于请假结束时间,请假时长根据每日工作时间实现累加计算
页面布局
在前端页面选择的是el-date-picker组件
<!-- 请假开始时间表单项 -->
<el-form-item
prop="start_time" <!-- 绑定表单验证规则的属性名 -->
label="请假开始时间" <!-- 表单项标签文本 -->
style="height: 55px" <!-- 设置表单项高度 -->
label-width="120px" <!-- 设置标签宽度 -->
:required="true" <!-- 标记为必填项 -->
>
<el-col :span="11"> <!-- 使用 Element UI 的 Col 布局,设置列宽 -->
<el-date-picker <!-- 日期时间选择器组件 -->
type="datetime" <!-- 设置类型为日期和时间选择 -->
placeholder="选择日期" <!-- 提示信息 -->
v-model="form.start_time" <!-- 绑定到表单对象的 start_time 属性 -->
style="width: 100%" <!-- 设置组件宽度为100% -->
value-format="yyyy-MM-dd HH:mm:ss" <!-- 设置选中值的时间格式 -->
:picker-options="pickerOptionsStart" <!-- 传入开始时间的选择器选项 -->
@change="calculateLeaveHours" <!-- 当选择日期发生改变时触发的方法 -->
></el-date-picker>
</el-col>
</el-form-item>
<!-- 请假结束时间表单项 -->
<el-form-item
label="请假结束时间"
style="height: 55px"
prop="end_time"
label-width="120px"
:required="true"
>
<el-col :span="11">
<el-date-picker
type="datetime"
placeholder="选择日期"
v-model="form.end_time" <!-- 绑定到表单对象的 end_time 属性 -->
style="width: 100%"
value-format="yyyy-MM-dd HH:mm:ss"
:picker-options="pickerOptionsEnd" <!-- 传入结束时间的选择器选项 -->
@change="calculateLeaveHours" <!-- 同样在结束时间改变时触发计算方法 -->
></el-date-picker>
</el-col>
</el-form-item>
<!-- 请假时长(小时)展示表单项 -->
<el-form-item label="请假时长(小时)" prop="hours" label-width="120px">
<el-input <!-- 输入框组件 -->
v-model="form.hours" <!-- 绑定到表单对象的 hours 属性 -->
disabled <!-- 设置输入框为禁用状态,仅用于显示计算出的请假时长 -->
></el-input>
</el-form-item>
用户选择开始时间的范围校验
:picker-options="pickerOptionsStart"
@change="calculateLeaveHours"
通过picker-options vue动态绑定属性设定了选择器的自定义配置,例如开始时间需大于本地时间且需要小于结束时间
开始时间选择代码如下:
// pickerOptionsStart 定义开始时间选择器的自定义选项
pickerOptionsStart: {
// disabledDate 是一个函数,用于决定日期选择器中哪些日期应被禁用(即不可选)
disabledDate: (time) => {
// 获取表单中结束时间的值
let endDateVal = this.form.end_time;
// 创建一个新的 Date 对象表示当前本地时间,并将其时间部分设置为0,确保包含今天整天的时间范围
let now = new Date();
now.setHours(0, 0, 0, 0);
// 如果结束时间未设置,则只允许用户选择从当前时间(包含今天)到未来的所有时间
if (!endDateVal) {
return time.getTime() < now.getTime();
}
// 同时满足以下条件时,该日期将被禁用:
// 1. 开始时间需大于等于当前本地时间
// 2. 开始时间需小于结束时间
return (
// 时间戳比较:如果开始时间早于当前时间 或者 开始时间晚于已设置的结束时间,则禁用该日期
time.getTime() < now.getTime() ||
time.getTime() > new Date(endDateVal).getTime()
);
},
},
获取结束时间的值,如果有则从本地时间到结束时间,如果没有就从本地时间到未来的时间,创建Date对象来获取当前本地的时间,至于为什么需要包含今天整体的时间是因为如果只判断开始时间大于本地时间的话,在时间选择器里今天的日期也是被禁止掉了的,这对于用户的体验是不好的,同理,结束时间与开始时间类似,但是这个方法的缺陷便是用户可以在下午选择今天早上的时间,这对于业务是不满足的,后面会有解决方案
结束时间范围选择
如果用户先选择结束时间时,如:2-29,开始时间就只能从26-29进行选择
代码如下:
// pickerOptionsEnd 定义结束时间选择器的自定义选项
pickerOptionsEnd: {
// disabledDate 是一个函数,用于决定日期选择器中哪些日期应被禁用(即不可选)
disabledDate: (time) => {
// 获取表单中开始时间的值
let beginDateVal = this.form.start_time;
// 创建一个新的 Date 对象表示当前本地时间
const currentDate = new Date();
// 确保结束时间大于开始时间(如果开始时间已设置)
if (beginDateVal) {
return time.getTime() <= new Date(beginDateVal).getTime();
}
// 同时允许用户选择今天的日期作为结束时间,因此仅当结束时间早于当前时间才禁用
return time.getTime() < currentDate.setHours(0, 0, 0, 0);
},
},
对于开始时间的校验
为了解决用户在当天可以选择任意的时间段以及计算请假时长,在开始时间和结束时间发生变化时便执行方法
methods: {
//计算请假时长
calculateLeaveHours() {}
}
当用户选择的开始时间小于本地时间的5分钟之前时(因为涉及到秒数,选择的开始时间会一直小于本地时间,为用户操作保留缓冲区间),提示用户并重置开始时间为本地时间,当结束时间小于本地时间时,则重置结束时间为选择的开始时间后一个小时
const startTime = new Date(this.form.start_time);
const endTime = new Date(this.form.end_time);
let now = new Date();
const fiveMinutesAgo = new Date();
fiveMinutesAgo.setMinutes(now.getMinutes() - 5);
fiveMinutesAgo.setSeconds(0);
fiveMinutesAgo.setMilliseconds(0);
if (startTime < fiveMinutesAgo) {
this.$message({
message: "开始时间需在当前时间附近,已为您选择当前时间!",
type: "warning",
});
this.form.start_time = new Date();
}
if (endTime < startTime) {
this.$message({
message:
"结束时间小于开始时间,已为您选择结束时间为开始时间后一个小时!",
type: "warning",
});
this.form.end_time = new Date(startTime.getTime() + 60 * 60 * 1000);
}
请假时长的计算
// 初始化请假时长
let leaveHours = 0;
// 计算每天的工作时间
let currentDate = new Date(startTime);
while (currentDate < endTime) {
const currentHour = currentDate.getHours(); // 获取当前小时数
const currentMinute = currentDate.getMinutes(); // 获取当前分钟数
// 判断当前时间是否在工作时间段内,若在则加入工作时间
if (
// 上午工作时间段:8:30 - 11:20
(((currentHour === 8 && currentMinute >= 30) || currentHour > 8) &&
((currentHour === 11 && currentMinute < 20) || currentHour < 11)) ||
// 下午工作时间段:14:00 - 18:00
((currentHour === 14 || currentHour === 18) && currentMinute >= 0) ||
(currentHour > 14 && currentHour < 18) ||
// 晚上工作时间段:19:00 - 20:30
(((currentHour === 19 && currentMinute >= 0) || currentHour > 19) &&
((currentHour === 20 && currentMinute <= 30) || currentHour < 20))
) {
// 在工作时间段内,加入工作时间
leaveHours += 0.1;
}
// 将日期增加一小时
currentDate.setMinutes(currentMinute + 6); // 以6分钟为间隔加入工作时间
}
// 更新请假时长
this.form.hours = leaveHours.toFixed(1);
这样的基本模块便完善得七七八八了,但在其中依然还是有些小问题,例如如果用户先选择时间结束后,再重置便会导致请假时长会一直计算的问题
最初想的解决方案是如果开始时间为空的话,就重置时间跳出方法,
// 首先检查开始时间和结束时间是否为空,如果任一为空则跳出此方法
if (!this.form.start_time || !this.form.end_time) {
return this.form.hours=0;
}
但是如果用户选择的开始时间小于本地时间的话就不能再进行判断了,所以进行了优化
if (this.form.hours !=0 &&(!this.form.start_time || !this.form.end_time)) {
return this.form.hours=0;
}
这样便都解决了