仿钉钉排班管理,基于Element Plus的排班表格可编辑实现思路

最近公司做自己的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>
相关推荐
王哲晓11 分钟前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
理想不理想v16 分钟前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云26 分钟前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
GIS程序媛—椰子2 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
我血条子呢3 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
半开半落3 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt
麦麦大数据3 小时前
基于vue+neo4j 的中药方剂知识图谱可视化系统
vue.js·知识图谱·neo4j
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS医院管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源·intellij-idea
理想不理想v3 小时前
vue经典前端面试题
前端·javascript·vue.js