最近有开发一个假期日历的需求,就类似于手机上的日历能显示某一天休息或者调休这种。大体上的框架引用了这篇文章,感谢前端设置工作日日历_前端日历-CSDN博客这篇文章的基础框架。
因为原始的页面所实现的功能不能很好的和业务契合 因此自己又做了修改。主要是增加了自定义某一天的类型以及一些其他的保存,删除的接口啥的,具体的效果可以看效果图。
废话少说 直接上源码,主体也是用了elementui。
<template>
<div>
<!-- 顶部选择栏 -->
<div class="calendar-top">
<select v-model="selectedYear" @change="changeCalendar">
<option v-for="year in years" :key="year" :value="year">{{ year }} 年</option>
</select>
<el-button type="info" size="mini" @click="remark" icon="el-icon-message" class="save-btn">
功能解释说明
</el-button>
<el-button type="danger" size="mini" @click="deleteAllDate" class="save-btn">
一键清空所有节假日
</el-button>
<el-button type="primary" size="mini" @click="saveSelectedDate" icon="el-icon-folder" class="save-btn">
保存选中日期
</el-button>
</div>
<!-- 日历容器 -->
<div class="calendar-container">
<div v-for="(monthData, index) in monthData" :key="index" class="calendar">
<div class="calendar-header">{{ selectedYear }} 年 {{ months[monthData.month - 1] }}</div>
<div class="calendar-grid">
<div class="calendar-day-header" v-for="day in dayHeaders" :key="day">
{{ day }}
</div>
<div
class="calendar-day"
v-for="(day, dayIndex) in monthData.days"
:key="`${monthData.month}-${dayIndex}`"
@click="openHolidaySetting(monthData.month, day)"
>
<span>{{ day.date }}</span>
<span v-if="day.status" :class="['status-label', day.status === '班' ? 'status-label-work' : 'status-label-holiday']">
{{ day.status }}
</span>
</div>
</div>
</div>
</div>
<!-- 假期设置弹出框 -->
<el-dialog :visible.sync="holidaySettingVisible" title="假期设置">
<el-form :model="holidaySettingForm" label-width="80px">
<el-form-item label="假期类型">
<el-select v-model="holidaySettingForm.holidayType" placeholder="请选择假期类型">
<!-- 固定选项“无”,value为空 -->
<el-option label="无" value=""></el-option>
<el-option
v-for="holidayType in holiday_typeList"
:key="holidayType.id"
:label="holidayType.name"
:value="holidayType.name"
></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="holidaySettingVisible = false">取消</el-button>
<el-button type="primary" @click="saveHolidaySetting">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import { addHoliday_calendar, deleteHoliday_calendar, holiday_calendar } from '@/api/attendance_manger/holiday_calendar/holiday_calendar.js';
import { listHoliday_type } from "@/api/attendance_manger/holiday_type/holiday_type";
export default {
data() {
return {
selectedYear: new Date().getFullYear(),
years: [],
months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
dayHeaders: ['一', '二', '三', '四', '五', '六', '日'],
// 后端已有数据(JSON字符串,不直接修改)
holidaySettingsJson: '{}',
// 新增数据:仅存储前端新增的假期日期(后端不存在的记录)
needAddHolidaySettingsJson: '{}',
// 删除数据:存储需要删除的记录(后端已有数据)
needRemoveHolidaySettingsJson: '{}',
markedDates: [],
monthData: [],
holidaySettingVisible: false,
holidaySettingForm: { holidayType: null },
currentSelectedDay: null, // 当前点击的日期对象
currentSelectedMonthData: null, // 当前点击的月份数据({ month: number })
holiday_typeList: [] // 假期类型数据
};
},
async mounted() {
await this.fetchHolidayCalendar();
this.generateYearOptions();
this.getList();
this.generateCalendarData();
},
methods: {
formatDate(year, month, day) {
return `${year}-${month}-${day}`;
},
generateYearOptions() {
for (let i = 2023; i <= 2060; i++) {
this.years.push(i);
}
},
changeCalendar() {
this.fetchHolidayCalendar();
this.generateCalendarData();
},
// 从后端获取假期数据
async fetchHolidayCalendar() {
const res = await holiday_calendar({ year: this.selectedYear });
if (res.code === 200) {
this.holidaySettingsJson = res.data.holidayCalendar || '{}';
} else {
this.holidaySettingsJson = '{}';
}
this.generateCalendarData();
},
// 获取假期类型列表
getList() {
listHoliday_type().then(response => {
if (response.code === 200) {
this.holiday_typeList = response.rows;
}
}).catch(error => console.error('获取假期类型失败:', error));
},
// 生成月数据以及每月的日期数据
generateCalendarData() {
// 解析后端、前端新增和前端删除数据
let backendData = {};
let addData = {};
let removeData = {};
try {
backendData = JSON.parse(this.holidaySettingsJson || '{}');
} catch (e) {
console.error('解析后端数据失败:', e);
}
try {
addData = JSON.parse(this.needAddHolidaySettingsJson || '{}');
} catch (e) {
console.error('解析新增数据失败:', e);
}
try {
removeData = JSON.parse(this.needRemoveHolidaySettingsJson || '{}');
} catch (e) {
console.error('解析删除数据失败:', e);
}
// 合并后端数据和新增数据
let mergedData = { ...backendData, ...addData };
// 对于需要删除的日期,从合并数据中剔除
Object.keys(removeData).forEach(date => {
delete mergedData[date];
});
// 清空 monthData 及 markedDates 重新生成
this.monthData = [];
this.markedDates = [];
for (let m = 1; m <= 12; m++) {
const days = this.generateMonthDays(this.selectedYear, m, mergedData);
this.monthData.push({ month: m, days });
}
},
generateMonthDays(year, month, mergedData) {
const firstDayOfMonth = new Date(year, month - 1, 1);
let startingDay = firstDayOfMonth.getDay() - 1;
if (startingDay === -1) {
startingDay = 6;
}
const daysInMonth = new Date(year, month, 0).getDate();
const days = [];
// 填充前导空白
for (let i = 0; i < startingDay; i++) {
days.push({ date: '', isCurrentMonth: false, status: '' });
}
// 填充当月日期
for (let i = 1; i <= daysInMonth; i++) {
const dateStr = this.formatDate(year, month, i);
let status = '';
if (mergedData[dateStr]) {
// 根据假期类型决定状态:如果包含“调休”显示“班”,否则显示“休”
const keywords = ['调休', '补班', '加班'];
status = keywords.some(key => mergedData[dateStr].includes(key)) ? '班' : '休';
if (!this.markedDates.includes(dateStr)) {
this.markedDates.push(dateStr);
}
}
days.push({ date: i, isCurrentMonth: true, status });
}
return days;
},
// 打开假期设置弹窗,传入月份数据和对应日期对象
openHolidaySetting(month, day) {
if (!day.date) return; // 前导空白不处理
this.currentSelectedDay = day;
this.currentSelectedMonthData = { month };
this.holidaySettingForm.holidayType = null;
this.holidaySettingVisible = true;
},
// 保存假期设置
saveHolidaySetting() {
const { holidayType } = this.holidaySettingForm;
if (holidayType !== null) {
const dateString = this.formatDate(this.selectedYear, this.currentSelectedMonthData.month, this.currentSelectedDay.date);
try {
const backendData = JSON.parse(this.holidaySettingsJson || '{}');
let addData = JSON.parse(this.needAddHolidaySettingsJson || '{}');
let removeData = JSON.parse(this.needRemoveHolidaySettingsJson || '{}');
if (holidayType === '') {
// 用户选择“无”:取消假期
// 如果该日期存在于后端数据中,则标记删除
if (backendData[dateString]) {
removeData[dateString] = true;
}
// 如果该日期存在于新增数据中,则直接删除
if (addData[dateString]) {
delete addData[dateString];
}
// 更新当前页面显示
this.currentSelectedDay.status = '';
const index = this.markedDates.indexOf(dateString);
if (index > -1) {
this.markedDates.splice(index, 1);
}
} else {
// 用户选择了某个假期类型
// 如果该日期在后端已有数据中,则不允许新增(你可以扩展为更新逻辑)
if (backendData[dateString]) {
// 如果之前标记了删除,则取消删除操作
if (removeData[dateString]) {
delete removeData[dateString];
}
// 提示用户该日期数据已存在,不做新增
this.$message.warning('该日期数据已存在后端,无法新增,请先删除后再修改!');
} else {
// 新增数据写入 needAddHolidaySettingsJson
addData[dateString] = holidayType;
// 更新显示状态,假设“调休”显示“班”,其它显示“休”
this.currentSelectedDay.status = holidayType.includes('调休') ? '班' : '休';
if (!this.markedDates.includes(dateString)) {
this.markedDates.push(dateString);
}
}
}
// 更新前端的新增和删除 JSON 字符串
this.needAddHolidaySettingsJson = JSON.stringify(addData);
this.needRemoveHolidaySettingsJson = JSON.stringify(removeData);
this.generateCalendarData(); // 重新生成日历数据以确保状态正确
} catch (error) {
console.error('JSON处理出错:', error);
}
this.holidaySettingVisible = false;
}
},
// 保存前端新增和删除的数据到后端
saveSelectedDate() {
let addData = {};
let removeData = {};
try {
addData = JSON.parse(this.needAddHolidaySettingsJson || '{}');
} catch (e) {
addData = {};
}
try {
removeData = JSON.parse(this.needRemoveHolidaySettingsJson || '{}');
} catch (e) {
removeData = {};
}
if (Object.keys(addData).length === 0 && Object.keys(removeData).length === 0) {
this.$message.warning('没有需要保存的假期数据');
return;
}
const params = {
// 根据实际接口,你可以将新增和删除数据一起传递,或者分别调用接口
holidaySettingsToAdd: JSON.stringify(addData),
holidaySettingsToRemove: JSON.stringify(removeData)
};
addHoliday_calendar(params).then((res) => {
if (res.code === 200) {
this.$message.success('假期日历保存成功!');
// 保存成功后,合并新增数据到后端数据中,并清空新增和删除数据
const merged = { ...JSON.parse(this.holidaySettingsJson || '{}'), ...addData };
// 删除标记的数据
Object.keys(removeData).forEach(date => {
delete merged[date];
});
this.holidaySettingsJson = JSON.stringify(merged);
this.needAddHolidaySettingsJson = '{}';
this.needRemoveHolidaySettingsJson = '{}';
this.generateCalendarData();
} else {
this.$message.error('假期日历保存失败!');
}
});
},
// 一键删除所有假期(仅调用删除接口,删除后刷新数据)
deleteAllDate() {
this.$confirm('确定要清空所有节假日吗?此操作将清空所有目前已有的假期数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteHoliday_calendar().then(res => {
if (res.code === 200) {
this.$message.success('已清空所有节假日!');
this.holidaySettingsJson = '{}';
this.needAddHolidaySettingsJson = '{}';
this.needRemoveHolidaySettingsJson = '{}';
this.generateCalendarData();
} else {
this.$message.error('清空失败!');
}
});
}).catch(() => {
this.$message.info('已取消清空操作');
});
},
// 解释说明
remark() {
this.$confirm('如果想在日期右上角显示"班",请在假期类型管理添加假期名称为调休, 补班, 加班这三种的任意一种,其他种类假期类型默认右上角显示"休"', '提示', {
type: 'warning'
})
}
}
};
</script>
<style scoped>
.calendar-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 10px;
overflow-y: auto;
max-height: 75vh;
}
.calendar {
width: calc(100% / 4 - 10px);
margin: 5px;
border: 1px solid #ccc;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.calendar-header {
height: 35px;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: #fff;
background-color: #3370ff;
padding: 5px;
}
.calendar-top {
width: 100%;
height: 50px;
display: flex;
justify-content: flex-start;
align-items: center;
padding-left: 10px;
}
select {
width: 120px;
height: 30px;
margin-left: 10px;
border: 1px solid #dcdfe6;
border-radius: 3px;
}
.save-btn {
margin-left: 10px;
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
padding: 10px;
}
.calendar-day-header {
text-align: center;
font-weight: bold;
}
.calendar-day {
text-align: center;
padding: 5px;
position: relative;
cursor: pointer;
}
.status-label {
position: absolute;
top: 0;
right: 0;
font-size: 10px;
}
.status-label-work {
color: red;
}
.status-label-holiday {
color: green;
}
</style>
其中holiday_calendar.js的代码如下所示,大家可以根据自己的实际业务自己修改
import request from '@/utils/request'
// 查询假期日历
export function holiday_calendar(query) {
return request({
url: '/attendance_manger/holiday_calendar/list',
method: 'get',
params: query
})
}
// 保存假期日历
export function addHoliday_calendar(data) {
return request({
url: '/attendance_manger/holiday_calendar',
method: 'post',
data: data
})
}
// 一键删除假期日历
export function deleteHoliday_calendar() {
return request({
url: '/attendance_manger/holiday_calendar',
method: 'delete',
})
}
其中holiday_type.js的代码如下所示,大家可以根据自己的实际业务自己修改
import request from '@/utils/request'
// 查询假期类型管理列表
export function listHoliday_type(query) {
return request({
url: '/attendance_manger/holiday_type/list',
method: 'get',
params: query
})
}
其余的后端接口的话 大家根据前端自主实现吧 前端通过两个json字符串来向后端发起新增以及删除某一天假日的请求,后端只需要接受解析相应的json然后存到数据库中就行了,本文主要分享一下前端的实现