最近公司做自己的OA系统,有一个需求是针对客服部门不固定班制,要去实现类似钉钉的排班表格,如下是钉钉排班样式和功能概况
看到这里我感觉需要解决的就是这个表格怎么来尽可能的还原了,因为整体的项目其实已经完成差不多了,用的是vue3+element Plus,所以首先就是怎么用el-table来实现,下面是我最后实现的样式(没有做到特别还原和美化,以及细节功能如除本月其他月的其他月排班表格,搜索等等,这些公司没有这个需求,也不难,有需要后面也不费事能加,暂时只放这个的实现思路,也不会被过多的代码影响思路)
一步一步来解决,用抽屉组件包裹表格,从表格头开始,要先实现本月数据如何能像钉钉中这样先将表头完成,首先需要处理一个数据数组将日期,完整年月日(方便与后端获取的排班数据做进一步处理),星期计入到其中
javascript
//获取本月日期,即渲染考勤表表头数据
getDatesForCurrentMonth() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth();
let daysInMonth = new Date(year, month + 1, 0).getDate();
for (let i = 1; i <= daysInMonth; i++) {
let date = new Date(year, month, i);
let dateString = date.toISOString().split("T")[0];
let dayOfWeek = date.getDay();
let weekDays = ["日", "一", "二", "三", "四", "五", "六"];
this.dates.push({
date: i,//日期号
fullDate: dateString,//完整年月日
dayOfWeek: weekDays[dayOfWeek],//星期
});
}
},
循环遍历渲染出表格头来
html
<!-- 编辑排班 -->
<el-drawer v-model="table" title="编辑排班" direction="rtl" size="100%">
<el-table :data="gridData" @cell-click="selectCell">
<el-table-column property="employeeName" label="姓名" width="100" />
<el-table-column
:prop="item.fullDate"
v-for="item in dates"
:key="item"
:label="item.date + '\n' + item.dayOfWeek"
width="115"
>
<template #default="props">
//自定义的内容
</template>
</el-table-column>
</el-table>
</el-drawer>
这样应该空数据表格就好了,再用弹出框组件el-popover来配合按钮(按钮我用着方便,要求美观也能自己写或者其他样式),先要将后端给我的数据做个处理,要所在列有对应行的属性名,所以日期全称fullDate就派上用场,我们在表格数据的每个元素加入循环对象,对象键是对应日期,对象值就是对应班次的name,列如:{2023-12-12:早班}这样 那不管想怎么渲染上表格都可以很轻松,数据的处理根据后端返回给我们的数据来处理,下面是我的处理代码
javascript
// 点击编辑获取考勤组排班详情
update(id) {
this.table = true;
getScheduleDetailList(id).then((res) => {
if (res.code == 200) {
let data = res.data.result;
data.map((item) => {
item.schedules.map((element) => {
let className = this.options_class.filter(
(item1) => item1.id == element.classId
)[0].className;
item[element.date] = className;
return item;
});
return item;
});
this.gridData = data;
this.update_schedules.groupId = id;
// console.log(this.gridData);
}
});
},
html
<el-table-column
:prop="item.fullDate"
v-for="item in dates"
:key="item"
:label="item.date + '\n' + item.dayOfWeek"
width="115"
>
<template #default="props">
<el-popover
popper-class="popper"
:width="150"
placement="bottom"
trigger="click"
>
<template #reference>
//有当日排班数据则显示排班名称
<el-button type="primary" plain v-if="props.row[item.fullDate]">{{
props.row[item.fullDate]
}}</el-button>
//没有当日排班数据则未排班
<el-button type="info" v-else plain>未排班</el-button>
</template>
<el-button
style="width: 100px; margin: 2px 0"
v-for="item in options_class"
:key="item"
:loading="loading"
@click="selectClass(item.id)"
>{{ item.className }}</el-button
>
</el-popover>
</template>
</el-table-column>
对于修改后需要做网络请求来修改数据库可以通过cell-click函数能够返回用户点击某单元row,和cell,来获取到当前点击的员工id,日期,等。下面是完整的页面代码
html
<template>
<el-table ref="table" :data="TableData" border style="width: 100%">
<el-table-column align="center" label="班次名称" prop="groupName" />
<el-table-column align="center" label="人数" prop="personCount" />
<el-table-column align="center" label="考勤时间" prop="classVOList" />
<el-table-column align="center" label="操作" width="200">
<template #header> 操作 </template>
<template #default="scope">
<el-button type="primary" @click="update(scope.row.groupId)"
>编辑</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination
hide-on-single-page
v-model:current-page="serachData.pageNo"
v-model:page-size="serachData.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
background
class="pagination"
layout="total, sizes, prev, pager, next"
@current-change="handlePageChange"
@size-change="onSizeChange"
>
</el-pagination>
<!-- 编辑排班 -->
<el-drawer v-model="table" title="编辑排班" direction="rtl" size="100%">
<el-table :data="gridData" @cell-click="selectCell">
<el-table-column property="employeeName" label="姓名" width="100" />
<el-table-column
:prop="item.fullDate"
v-for="item in dates"
:key="item"
:label="item.date + '\n' + item.dayOfWeek"
width="115"
>
<template #default="props">
<el-popover
popper-class="popper"
:width="150"
placement="bottom"
trigger="click"
>
<template #reference>
<el-button type="primary" plain v-if="props.row[item.fullDate]">{{
props.row[item.fullDate]
}}</el-button>
<el-button type="info" v-else plain>未排班</el-button>
</template>
<el-button
style="width: 100px; margin: 2px 0"
v-for="item in options_class"
:key="item"
:loading="loading"
@click="selectClass(item.id)"
>{{ item.className }}</el-button
>
</el-popover>
</template>
</el-table-column>
</el-table>
</el-drawer>
</template>
<script>
import {
getScheduleList,
getScheduleDetailList,
getClass,
ScheduleEdit,
} from "@/utils/api";
import {ElMessage } from "element-plus";
export default {
name: "list",
data() {
return {
total: 0,
serachData: {
pageSize: 10,
pageNo: 1,
},
dept: {},
table: false,
TableData: [],
gridData: [],
dates: [],
options_class: [],
loading: false,
update_schedules: {//编辑完成网络请求传参数据
groupId: 0,
schedules: [
{
data: "",
employeeId: 78,
classId: 0,
},
],
},
loading: false,
};
},
created() {
this.getScheduleList();
this.getClass();
this.getDatesForCurrentMonth();
},
methods: {
getScheduleList() {
getScheduleList(this.serachData).then((result) => {
if (result.code === 200) {
this.TableData = result.data.result.records.map((item) => {
item.classVOList = item.classVOList
.map((item2) => {
item2 = `班次${item2.className}:${item2.startTime}-${item2.overTime}`;
return item2;
})
.join("\n");
return item;
});
this.total = result.data.result.total;
// this.$refs.table.doLayout();
} else {
ElMessage.error(result.message);
}
});
},
getClass() {
getClass().then((result) => {
if (result.code === 200) {
this.options_class = result.data.result;
}
});
},
//获取本月日期,即渲染考勤表表头数据
getDatesForCurrentMonth() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth();
let daysInMonth = new Date(year, month + 1, 0).getDate();
for (let i = 1; i <= daysInMonth; i++) {
let date = new Date(year, month, i);
let dateString = date.toISOString().split("T")[0];
let dayOfWeek = date.getDay();
let weekDays = ["日", "一", "二", "三", "四", "五", "六"];
this.dates.push({
date: i,
fullDate: dateString,
dayOfWeek: weekDays[dayOfWeek],
});
}
},
// 点击编辑获取考勤组排班详情
update(id) {
this.table = true;
getScheduleDetailList(id).then((res) => {
if (res.code == 200) {
let data = res.data.result;
data.map((item) => {
item.schedules.map((element) => {
let className = this.options_class.filter(
(item1) => item1.id == element.classId
)[0].className;
item[element.date] = className;
return item;
});
return item;
});
this.gridData = data;
this.update_schedules.groupId = id;
// console.log(this.gridData);
}
});
},
// 编辑修改考勤组处理操作
// 点击表格事件,获取点击的当天日期(行数据处理),获取所选人员id(列数据处理)
selectCell(row, column) {
console.log(row, column.property);
this.update_schedules.schedules[0].data = column.property;
},
// 选择排班班次,上传数据
selectClass(classId) {
this.update_schedules.schedules[0].classId = classId;
this.loading = true;
ScheduleEdit(this.update_schedules).then((res) => {
if (res.code == 200) {
ElMessage.success(res.message);
this.update(this.update_schedules.groupId);
this.loading = false;
} else {
ElMessage.error(res.message);
this.loading = false;
}
});
},
handlePageChange(val) {
this.serachData.pageNo = val;
this.getDeptList();
},
onSizeChange(val) {
this.serachData.pageSize = val;
this.getDeptList();
},
},
};
</script>
<style>
.el-table .cell {
white-space: pre-wrap;
}
.popper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>