zhangwencui
15 小时以前 a4d0446d7c1c1e56641fd4e887ad4d0ecd0534ca
排班管理页面完成70%
已添加2个文件
已修改4个文件
2367 ■■■■■ 文件已修改
src/api/personnelManagement/attendanceRules.js 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/class.js 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/classsSheduling/index.vue 2209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/attendanceRules.js
@@ -1,6 +1,6 @@
import request from "@/utils/request";
// èŽ·å–æ‰“å¡è§„åˆ™åˆ—è¡¨
// èŽ·å–ç­æ¬¡åˆ—è¡¨
export function getAttendanceRules(query) {
  return request({
    url: "/personalAttendanceLocationConfig/listPage",
@@ -9,7 +9,7 @@
  });
}
// æ–°å¢žæ‰“卡规则
// æ–°å¢žç­æ¬¡
export function addAttendanceRule(data) {
  return request({
    url: "/personalAttendanceLocationConfig/add",
@@ -18,7 +18,7 @@
  });
}
// æ›´æ–°æ‰“卡规则
// æ›´æ–°ç­æ¬¡
export function updateAttendanceRule(data) {
  return request({
    url: "/attendanceRules/update",
@@ -27,7 +27,7 @@
  });
}
// åˆ é™¤æ‰“卡规则
// åˆ é™¤ç­æ¬¡
export function deleteAttendanceRule(ids) {
  return request({
    url: `/personalAttendanceLocationConfig/del`,
@@ -36,7 +36,7 @@
  });
}
// èŽ·å–å•ä¸ªæ‰“å¡è§„åˆ™è¯¦æƒ…
// èŽ·å–å•ä¸ªç­æ¬¡è¯¦æƒ…
export function getAttendanceRuleDetail(id) {
  return request({
    url: `/attendanceRules/detail/${id}`,
src/api/personnelManagement/class.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
// ç­æ¬¡ç›¸å…³æŽ¥å£
import request from "@/utils/request";
// ç»©æ•ˆç®¡ç†-班次-分页查询
export function page(query) {
  return request({
    url: "/performanceShift/page",
    method: "get",
    params: query,
  });
}
// ç»©æ•ˆç®¡ç†-班次-年份分页查询
export function pageYear(query) {
  return request({
    url: "/performanceShift/pageYear",
    method: "get",
    params: query,
  });
}
// ç»©æ•ˆç®¡ç†-班次-排班
export function add(data) {
  return request({
    url: "/performanceShift/add",
    method: "post",
    data: data,
  });
}
// ç»©æ•ˆç®¡ç†-班次-时间配置-查询时间配置信息
export function list(query) {
  return request({
    url: "/shiftTime/list",
    method: "get",
    params: query,
  });
}
// ç»©æ•ˆç®¡ç†-班次-时间配置-新增
export function shiftAdd(data) {
  return request({
    url: "/shiftTime/add",
    method: "post",
    data: data,
  });
}
// ç»©æ•ˆç®¡ç†-班次-时间配置-修改
export function shiftUpdate(data) {
  return request({
    url: "/shiftTime/update",
    method: "post",
    data: data,
  });
}
// ç»©æ•ˆç®¡ç†-班次-时间配置-删除
export function shiftRemove(query) {
  return request({
    url: "/shiftTime/remove",
    method: "delete",
    params: query,
  });
}
// ç»©æ•ˆç®¡ç†-班次-导出
export function exportFile(query) {
  return request({
    url: "/performanceShift/export",
    method: "get",
    params: query,
  });
}
// ç»©æ•ˆç®¡ç†-班次-导出
export function obtainItemParameterList(query) {
  return request({
    url: "/laboratoryScope/obtainItemParameterList",
    method: "get",
    params: query,
  });
}
// ç»©æ•ˆç®¡ç†-班次-班次状态修改
export function update(data) {
  return request({
    url: "/performanceShift/update",
    method: "post",
    data: data,
  });
}
// èŽ·å–ç”¨æˆ·åˆ—è¡¨
// export function selectUserCondition(query) {
//   return request({
//     url: "/system/newUser/selectUserCondition",
//     method: "get",
//     params: query,
//   });
// }
export function selectUserCondition() {
  return request({
    url: '/system/user/userListNoPage',
    method: 'get'
  })
}
src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
@@ -140,9 +140,9 @@
  });
  const dialogTitle = computed(() => {
    if (props.operationType === "add") return "新增打卡规则";
    if (props.operationType === "edit") return "编辑打卡规则";
    return "查看打卡规则";
    if (props.operationType === "add") return "新增班次";
    if (props.operationType === "edit") return "编辑班次";
    return "查看班次";
  });
  // è¡¨å•数据
src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
@@ -2,14 +2,14 @@
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜å’Œæ“ä½œæŒ‰é’® -->
    <div class="page-header">
      <div class="title">打卡规则配置</div>
      <div class="title">班次配置</div>
      <div class="actions">
        <el-button type="primary"
                   @click="openForm('add')">
          <el-icon>
            <Plus />
          </el-icon>
          æ–°å¢žè§„则
          æ–°å¢žç­æ¬¡
        </el-button>
      </div>
    </div>
@@ -50,7 +50,7 @@
        </el-button>
      </el-form-item>
    </el-form> -->
    <!-- è§„则列表 -->
    <!-- ç­æ¬¡åˆ—表 -->
    <el-card shadow="never"
             class="mb16">
      <el-table :data="tableData"
@@ -110,7 +110,7 @@
                  @pagination="paginationChange"
                  class="mt10" />
    </el-card>
    <!-- æ–°å¢ž/编辑规则弹窗 -->
    <!-- æ–°å¢ž/编辑班次弹窗 -->
    <rule-form ref="ruleFormRef"
               v-model="dialogVisible"
               :operation-type="operationType"
@@ -206,7 +206,7 @@
    return "";
  };
  // æŸ¥è¯¢è§„则列表
  // æŸ¥è¯¢ç­æ¬¡åˆ—表
  const fetchData = () => {
    tableLoading.value = true;
    getAttendanceRules({ ...page, ...searchForm })
@@ -240,9 +240,9 @@
    dialogVisible.value = true;
  };
  // åˆ é™¤è§„则
  // åˆ é™¤ç­æ¬¡
  const handleDelete = id => {
    ElMessageBox.confirm("确定要删除这条规则吗?", "删除确认", {
    ElMessageBox.confirm("确定要删除这条班次吗?", "删除确认", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
src/views/personnelManagement/attendanceCheckin/index.vue
@@ -60,6 +60,15 @@
      </el-descriptions>
    </el-card> -->
    <div class="attendance-operation">
      <el-button @click="handleBack"
                 type="default"
                 size="small"
                 style="margin-right: 16px">
        <el-icon>
          <ArrowLeft />
        </el-icon>
        è¿”回排班管理
      </el-button>
      <!-- æŸ¥è¯¢æ¡ä»¶ï¼ˆç®¡ç†å‘˜è€ƒå‹¤æ—¥æŠ¥ï¼‰ -->
      <el-form :model="searchForm"
               :inline="true"
@@ -170,6 +179,7 @@
<script setup>
  import { ref, reactive, computed, onMounted, onBeforeUnmount } from "vue";
  import { useRouter } from "vue-router";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    createPersonalAttendanceRecord,
@@ -178,9 +188,10 @@
  } from "@/api/personnelManagement/personalAttendanceRecords.js";
  import Pagination from "@/components/Pagination/index.vue";
  import { deptTreeSelect } from "@/api/system/user.js";
  import { Refresh, Search } from "@element-plus/icons-vue";
  import { Refresh, Search, ArrowLeft } from "@element-plus/icons-vue";
  const { proxy } = getCurrentInstance();
  const router = useRouter();
  const tableLoading = ref(false);
  // åˆ†é¡µå‚æ•°
  const page = reactive({
@@ -445,6 +456,13 @@
    fetchDeptOptions();
  });
  // è¿”回排班管理页面
  const handleBack = () => {
    router.push({
      path: "/personnelManagement/classsSheduling/index",
    });
  };
  onBeforeUnmount(() => {
    if (timer) {
      clearInterval(timer);
src/views/personnelManagement/classsSheduling/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2209 @@
<template>
  <div class="class-page">
    <div class="search-container">
      <div class="search-form">
        <div class="search-row">
          <div class="search-item">
            <label class="search-label">选择时间:</label>
            <div class="search-input-group">
              <el-date-picker v-model="query.year"
                              type="year"
                              size="small"
                              format="YYYY"
                              placeholder="选择年"
                              @change="refreshTable()"
                              style="width: 90px"
                              :clearable="false" />
              <el-select v-model="query.month"
                         clearable
                         placeholder="选择月"
                         style="width: 70px; margin-left: 8px"
                         size="small"
                         @change="refreshTable()">
                <el-option v-for="item in monthOptions"
                           :key="item.value"
                           :label="item.label"
                           :value="item.value" />
              </el-select>
            </div>
          </div>
          <div class="search-item">
            <el-input v-model="query.userName"
                      placeholder="请输入人员名称"
                      size="small"
                      style="width: 120px"
                      clearable
                      @keyup.enter="refreshTable()" />
          </div>
          <div class="search-item">
            <el-tree-select v-model="query.deptId"
                            :data="deptOptions"
                            :props="{ value: 'id', label: 'label', children: 'children' }"
                            value-key="id"
                            placeholder="请选择部门"
                            size="small"
                            clearable
                            @change="refreshTable()"
                            style="width: 140px" />
          </div>
          <div class="search-actions">
            <el-button size="small"
                       type="primary"
                       @click="refreshTable()"
                       :icon="Search">
              æŸ¥è¯¢
            </el-button>
            <el-button size="small"
                       @click="refresh()"
                       :icon="Refresh"
                       style="margin-left: 8px">
              é‡ç½®
            </el-button>
          </div>
          <div class="search-buttons">
            <el-button size="small"
                       type="primary"
                       @click="configTime"
                       :icon="Setting">
              ç­æ¬¡é…ç½®
            </el-button>
            <el-button size="small"
                       type="success"
                       @click="handleDown"
                       :loading="downLoading"
                       :icon="Download"
                       style="margin-left: 8px">
              å¯¼å‡º
            </el-button>
            <el-button size="small"
                       type="warning"
                       @click="schedulingVisible = true"
                       :icon="Calendar"
                       style="margin-left: 8px">
              æŽ’班
            </el-button>
          </div>
        </div>
      </div>
    </div>
    <div class="scheduling-container"
         v-loading="pageLoading">
      <!-- æœˆåº¦æŽ’班 -->
      <div class="scheduling-table"
           v-show="query.month">
        <div class="scheduling-left">
          <div class="scheduling-header">
            äººå‘˜åç§°
          </div>
          <div class="scheduling-user"
               :class="{ 'scheduling-user-hover': currentUserIndex == index }"
               v-for="(item, index) in listForm"
               :key="'e' + index"
               @mouseenter="onMouseEnter(index)"
               @mouseleave="currentUserIndex = null">
            <div class="user-avatar">
              {{ item.name ? item.name.charAt(0) : "" }}
            </div>
            <div class="user-details">
              <h4 class="user-name">{{ item.name }}</h4>
              <!-- <div class="user-stats">
                <span class="stat-item">早:{{ item.day0 }}</span>
                <span class="stat-item">中:{{ item.day1 }}</span>
                <span class="stat-item">夜:{{ item.day2 }}</span>
                <span class="stat-item">休:{{ item.day3 }}</span>
                <span class="stat-item">假:{{ item.day4 }}</span>
                <span class="stat-item">å·®:{{ item.day6 }}</span>
              </div> -->
              <div class="user-total">
                <span class="total-label">合计出勤:</span>
                <span class="total-value">{{ item.monthlyAttendance.totalAttendance }}天</span>
              </div>
            </div>
          </div>
        </div>
        <div class="scheduling-right">
          <div class="scheduling-calendar">
            <div class="calendar-header">
              <div class="calendar-header-item"
                   v-for="(item, index) in weeks"
                   :key="'b' + index">
                <span class="week-number"
                      v-if="item.week == '周日'">{{ item.weekNum }}周</span>
                <div class="day-info">
                  <span class="day-number">{{ item.day }}</span>
                  <span class="day-week">{{ item.week.charAt(1) }}</span>
                </div>
              </div>
            </div>
            <div class="calendar-body">
              <div class="calendar-row"
                   v-for="(item, index) in listForm"
                   :key="'c' + index"
                   :class="{ 'calendar-row-hover': currentUserIndex == index }"
                   @mouseenter="onMouseEnter(index)"
                   @mouseleave="currentUserIndex = null">
                <div class="calendar-cell"
                     v-for="(m, i) in item.list"
                     :key="'d' + i">
                  <el-dropdown trigger="click"
                               placement="bottom"
                               @command="(e) => handleCommand(e, m)"
                               class="shift-dropdown">
                    <div class="shift-box"
                         :class="{
                      'shift-box-early': m.shift === '0',
                      'shift-box-mid': m.shift === '1',
                      'shift-box-night': m.shift === '2',
                      'shift-box-rest': m.shift === '3',
                      'shift-box-leave': m.shift === '4',
                      'shift-box-other': m.shift === '5',
                      'shift-box-business': m.shift === '6',
                    }">
                      <span class="shift-text">{{ getShiftByDic(m.shift) || '—' }}</span>
                    </div>
                    <template #dropdown>
                      <el-dropdown-menu>
                        <el-dropdown-item v-for="(n, j) in classType"
                                          :key="'h' + j"
                                          :command="n.id">{{ n.locationName
                          }}</el-dropdown-item>
                      </el-dropdown-menu>
                    </template>
                  </el-dropdown>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- å¹´åº¦æŽ’班 -->
      <div class="yearly-table"
           v-show="!query.month">
        <div class="scheduling-left">
          <div class="scheduling-header">
            äººå‘˜åç§°
          </div>
          <div class="scheduling-user"
               :class="{ 'scheduling-user-hover': currentUserIndex == index }"
               v-for="(item, index) in yearList"
               :key="'e' + index"
               @mouseenter="onMouseEnter(index)"
               @mouseleave="currentUserIndex = null">
            <div class="user-avatar">
              {{ item.name ? item.name.charAt(0) : "" }}
            </div>
            <div class="user-details">
              <h4 class="user-name">{{ item.name }}</h4>
              <!-- <div class="user-stats">
                <span class="stat-item">早:{{ item.day0 }}</span>
                <span class="stat-item">中:{{ item.day1 }}</span>
                <span class="stat-item">夜:{{ item.day2 }}</span>
                <span class="stat-item">休:{{ item.day3 }}</span>
                <span class="stat-item">假:{{ item.day4 }}</span>
                <span class="stat-item">å·®:{{ item.day6 }}</span>
              </div> -->
              <div class="user-total">
                <span class="total-label">合计出勤:</span>
                <span class="total-value">{{ item.work_time }}天</span>
              </div>
            </div>
          </div>
        </div>
        <div class="scheduling-right">
          <div class="yearly-calendar">
            <div class="yearly-header">
              <div class="yearly-header-item"
                   v-for="(item, index) in monthList"
                   :key="'b' + index">
                <span class="month-name">{{ item }}月</span>
              </div>
            </div>
            <div class="yearly-body">
              <div class="yearly-row"
                   v-for="(item, index) in yearList"
                   :key="'c' + index"
                   :class="{ 'calendar-row-hover': currentUserIndex == index }"
                   @mouseenter="onMouseEnter(index)"
                   @mouseleave="currentUserIndex = null">
                <div class="yearly-cell"
                     v-for="(m, i) in item.monthList"
                     :key="'d' + i">
                  <div class="monthly-attendance">
                    <span class="attendance-label">合计出勤:</span>
                    <span class="attendance-value">{{ m.totalMonthAttendance }}</span>
                  </div>
                  <!-- <div class="monthly-stats">
                    <span class="stat-item">早:{{ m.day0 }}</span>
                    <span class="stat-item">中:{{ m.day1 }}</span>
                    <span class="stat-item">夜:{{ m.day2 }}</span>
                    <span class="stat-item">休:{{ m.day3 }}</span>
                    <span class="stat-item">假:{{ m.day4 }}</span>
                    <span class="stat-item">å·®:{{ m.day6 }}</span>
                  </div> -->
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div style="display: flex; justify-content: flex-end; margin-top: 10px; margin-right: 30px">
      <el-pagination background
                     @current-change="currentChange"
                     :page-size="pageSize"
                     :current-page="currentPage"
                     layout="total, prev, pager, next, jumper"
                     :total="total">
      </el-pagination>
    </div>
    <el-dialog title="排班"
               v-model="schedulingVisible"
               width="400px">
      <div class="search_thing">
        <div class="search_label"
             style="width: 90px">
          <span style="color: red; margin-right: 4px">*</span>周次:
        </div>
        <div class="search_input">
          <el-date-picker v-model="schedulingQuery.week"
                          type="week"
                          format="YYYY ç¬¬ ww å‘¨"
                          placeholder="选择周次"
                          style="width: 100%">
          </el-date-picker>
        </div>
      </div>
      <div class="search_thing">
        <div class="search_label"
             style="width: 90px">
          <span style="color: red; margin-right: 4px">*</span>人员名称:
        </div>
        <div class="search_input">
          <el-select v-model="schedulingQuery.userId"
                     placeholder="请选择"
                     style="width: 100%"
                     multiple
                     clearable
                     collapse-tags>
            <el-option v-for="item in personList"
                       :key="item.userId"
                       :label="item.nickName"
                       :value="item.userId">
            </el-option>
          </el-select>
        </div>
      </div>
      <div class="search_thing">
        <div class="search_label"
             style="width: 90px">
          <span style="color: red; margin-right: 4px">*</span>班次:
        </div>
        <div class="search_input">
          <el-select v-model="schedulingQuery.shift"
                     placeholder="请选择"
                     style="width: 100%">
            <el-option v-for="item in classType"
                       :key="item.id"
                       :label="item.locationName"
                       :value="item.id">
            </el-option>
          </el-select>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="schedulingVisible = false">取 æ¶ˆ</el-button>
          <el-button type="primary"
                     @click="confirmScheduling"
                     :loading="loading">ç¡® å®š</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { useRouter } from "vue-router";
  import {
    page,
    pageYear,
    add,
    exportFile,
    update,
    selectUserCondition,
  } from "@/api/personnelManagement/class";
  import { deptTreeSelect } from "@/api/system/user.js";
  import { getAttendanceRules } from "@/api/personnelManagement/attendanceRules.js";
  const { proxy } = getCurrentInstance();
  const router = useRouter();
  // æŸ¥è¯¢æ¡ä»¶
  const query = reactive({
    userName: "",
    deptId: "",
    year: new Date(),
    month: new Date().getMonth() + 1,
  });
  // æœˆä»½é€‰é¡¹
  const monthOptions = [
    { value: 1, label: "1月" },
    { value: 2, label: "2月" },
    { value: 3, label: "3月" },
    { value: 4, label: "4月" },
    { value: 5, label: "5月" },
    { value: 6, label: "6月" },
    { value: 7, label: "7月" },
    { value: 8, label: "8月" },
    { value: 9, label: "9月" },
    { value: 10, label: "10月" },
    { value: 11, label: "11月" },
    { value: 12, label: "12月" },
  ];
  // éƒ¨é—¨åˆ—表
  const deptOptions = ref([]);
  // å‘¨åˆ—表
  const weeks = ref([
    { weekNum: 1, week: "周一", day: "01" },
    { weekNum: 1, week: "周二", day: "02" },
    { weekNum: 1, week: "周三", day: "03" },
    { weekNum: 1, week: "周四", day: "04" },
    { weekNum: 1, week: "周五", day: "05" },
    { weekNum: 1, week: "周六", day: "06" },
    { weekNum: 2, week: "周日", day: "07" },
    { weekNum: 2, week: "周一", day: "08" },
    { weekNum: 2, week: "周二", day: "09" },
    { weekNum: 2, week: "周三", day: "10" },
    { weekNum: 2, week: "周四", day: "11" },
    { weekNum: 2, week: "周五", day: "12" },
    { weekNum: 2, week: "周六", day: "13" },
    { weekNum: 3, week: "周日", day: "14" },
    { weekNum: 3, week: "周一", day: "15" },
    { weekNum: 3, week: "周二", day: "16" },
    { weekNum: 3, week: "周三", day: "17" },
    { weekNum: 3, week: "周四", day: "18" },
    { weekNum: 3, week: "周五", day: "19" },
    { weekNum: 3, week: "周六", day: "20" },
    { weekNum: 4, week: "周日", day: "21" },
    { weekNum: 4, week: "周一", day: "22" },
    { weekNum: 4, week: "周二", day: "23" },
    { weekNum: 4, week: "周三", day: "24" },
    { weekNum: 4, week: "周四", day: "25" },
    { weekNum: 4, week: "周五", day: "26" },
    { weekNum: 4, week: "周六", day: "27" },
    { weekNum: 5, week: "周日", day: "28" },
    { weekNum: 5, week: "周一", day: "29" },
    { weekNum: 5, week: "周二", day: "30" },
  ]);
  // ç­æ¬¡ç±»åž‹
  const classType = ref([]);
  // å½“前用户索引
  const currentUserIndex = ref(null);
  // æŽ’班弹窗显示状态
  const schedulingVisible = ref(false);
  // äººå‘˜åˆ—表
  const personList = ref([]);
  // åŠ è½½çŠ¶æ€
  const loading = ref(false);
  // æŽ’班查询条件
  const schedulingQuery = reactive({
    week: "",
    userId: null,
    shift: "",
  });
  // åˆ—表数据
  const listForm = ref([
    {
      id: 1,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
    {
      id: 2,
      name: "李四",
      monthlyAttendance: {
        totalAttendance: 20,
        æ—©ç­: 8,
        ä¸­ç­: 6,
        å¤œç­: 6,
        ä¼‘息: 8,
        è¯·å‡: 2,
        å‡ºå·®: 0,
      },
      day0: 8,
      day1: 6,
      day2: 6,
      day3: 8,
      day4: 2,
      day6: 0,
      list: [
        { id: 31, shift: "1" },
        { id: 32, shift: "1" },
        { id: 33, shift: "2" },
        { id: 34, shift: "2" },
        { id: 35, shift: "3" },
        { id: 36, shift: "0" },
        { id: 37, shift: "0" },
        { id: 38, shift: "1" },
        { id: 39, shift: "1" },
        { id: 40, shift: "2" },
        { id: 41, shift: "2" },
        { id: 42, shift: "3" },
        { id: 43, shift: "0" },
        { id: 44, shift: "0" },
        { id: 45, shift: "1" },
        { id: 46, shift: "1" },
        { id: 47, shift: "2" },
        { id: 48, shift: "2" },
        { id: 49, shift: "3" },
        { id: 50, shift: "0" },
        { id: 51, shift: "0" },
        { id: 52, shift: "4" },
        { id: 53, shift: "4" },
        { id: 54, shift: "1" },
        { id: 55, shift: "1" },
        { id: 56, shift: "2" },
        { id: 57, shift: "2" },
        { id: 58, shift: "3" },
        { id: 59, shift: "0" },
        { id: 60, shift: "0" },
      ],
    },
    {
      id: 3,
      name: "王五",
      monthlyAttendance: {
        totalAttendance: 23,
        æ—©ç­: 9,
        ä¸­ç­: 9,
        å¤œç­: 5,
        ä¼‘息: 5,
        è¯·å‡: 0,
        å‡ºå·®: 2,
      },
      day0: 9,
      day1: 9,
      day2: 5,
      day3: 5,
      day4: 0,
      day6: 2,
      list: [
        { id: 61, shift: "2" },
        { id: 62, shift: "2" },
        { id: 63, shift: "3" },
        { id: 64, shift: "0" },
        { id: 65, shift: "0" },
        { id: 66, shift: "1" },
        { id: 67, shift: "1" },
        { id: 68, shift: "2" },
        { id: 69, shift: "2" },
        { id: 70, shift: "3" },
        { id: 71, shift: "0" },
        { id: 72, shift: "0" },
        { id: 73, shift: "1" },
        { id: 74, shift: "1" },
        { id: 75, shift: "2" },
        { id: 76, shift: "2" },
        { id: 77, shift: "3" },
        { id: 78, shift: "0" },
        { id: 79, shift: "0" },
        { id: 80, shift: "1" },
        { id: 81, shift: "1" },
        { id: 82, shift: "6" },
        { id: 83, shift: "6" },
        { id: 84, shift: "2" },
        { id: 85, shift: "2" },
        { id: 86, shift: "3" },
        { id: 87, shift: "0" },
        { id: 88, shift: "0" },
        { id: 89, shift: "1" },
        { id: 90, shift: "1" },
      ],
    },
    {
      id: 4,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
    {
      id: 5,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
    {
      id: 6,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
  ]);
  // å½“前页
  const currentPage = ref(1);
  // æ¯é¡µæ¡æ•°
  const pageSize = ref(6);
  // æ€»æ¡æ•°
  const total = ref(3);
  // é¡µé¢åŠ è½½çŠ¶æ€
  const pageLoading = ref(false);
  // æœˆä»½åˆ—表
  const monthList = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
  // å¹´åº¦åˆ—表
  const yearList = ref([
    {
      id: 1,
      name: "张三",
      work_time: 260,
      day0: 98,
      day1: 78,
      day2: 46,
      day3: 74,
      day4: 14,
      day6: 10,
      monthList: [
        {
          totalMonthAttendance: 22,
          day0: 10,
          day1: 8,
          day2: 4,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 19,
          day0: 7,
          day1: 6,
          day2: 6,
          day3: 9,
          day4: 3,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 9,
          day1: 9,
          day2: 5,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 0,
          day6: 1,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 8,
          day4: 4,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 0,
          day6: 1,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 19,
          day0: 7,
          day1: 7,
          day2: 5,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
      ],
    },
    {
      id: 2,
      name: "李四",
      work_time: 252,
      day0: 90,
      day1: 72,
      day2: 50,
      day3: 76,
      day4: 18,
      day6: 8,
      monthList: [
        {
          totalMonthAttendance: 21,
          day0: 9,
          day1: 7,
          day2: 5,
          day3: 7,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 10,
          day4: 4,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 8,
          day1: 8,
          day2: 6,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 6,
          day2: 7,
          day3: 8,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 17,
          day0: 5,
          day1: 5,
          day2: 7,
          day3: 9,
          day4: 5,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 0,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 6,
          day2: 7,
          day3: 8,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 9,
          day4: 3,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
      ],
    },
    {
      id: 3,
      name: "王五",
      work_time: 268,
      day0: 102,
      day1: 82,
      day2: 48,
      day3: 70,
      day4: 10,
      day6: 14,
      monthList: [
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 24,
          day0: 10,
          day1: 9,
          day2: 5,
          day3: 4,
          day4: 0,
          day6: 3,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 8,
          day2: 5,
          day3: 5,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 24,
          day0: 10,
          day1: 9,
          day2: 5,
          day3: 4,
          day4: 0,
          day6: 3,
        },
      ],
    },
    {
      id: 4,
      name: "赵六",
      work_time: 248,
      day0: 88,
      day1: 70,
      day2: 50,
      day3: 78,
      day4: 20,
      day6: 6,
      monthList: [
        {
          totalMonthAttendance: 20,
          day0: 8,
          day1: 6,
          day2: 6,
          day3: 8,
          day4: 3,
          day6: 0,
        },
        {
          totalMonthAttendance: 17,
          day0: 5,
          day1: 5,
          day2: 7,
          day3: 10,
          day4: 5,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 7,
          day1: 7,
          day2: 7,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 19,
          day0: 6,
          day1: 6,
          day2: 7,
          day3: 9,
          day4: 2,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 16,
          day0: 4,
          day1: 4,
          day2: 8,
          day3: 10,
          day4: 6,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 19,
          day0: 6,
          day1: 6,
          day2: 7,
          day3: 9,
          day4: 2,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 9,
          day4: 4,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
      ],
    },
    {
      id: 5,
      name: "钱七",
      work_time: 266,
      day0: 100,
      day1: 84,
      day2: 46,
      day3: 72,
      day4: 12,
      day6: 12,
      monthList: [
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 23,
          day0: 9,
          day1: 9,
          day2: 5,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 9,
          day1: 9,
          day2: 5,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
      ],
    },
  ]);
  // å¯¼å‡ºåŠ è½½çŠ¶æ€
  const downLoading = ref(false);
  // èŽ·å–éƒ¨é—¨åˆ—è¡¨
  const fetchDeptOptions = () => {
    deptTreeSelect().then(response => {
      deptOptions.value = filterDisabledDept(
        JSON.parse(JSON.stringify(response.data))
      );
    });
  };
  // è¿‡æ»¤ç¦ç”¨çš„部门
  const filterDisabledDept = deptList => {
    return deptList.filter(dept => {
      if (dept.disabled) {
        return false;
      }
      if (dept.children && dept.children.length) {
        dept.children = filterDisabledDept(dept.children);
      }
      return true;
    });
  };
  // åˆ·æ–°
  const refresh = () => {
    listForm.value = [];
    yearList.value = [];
    currentPage.value = 1;
    query.userName = "";
    query.deptId = "";
    query.year = new Date();
    query.month = new Date().getMonth() + 1;
    if (query.month) {
      init();
    } else {
      initYear();
    }
  };
  // åˆ·æ–°è¡¨æ ¼
  const refreshTable = () => {
    currentPage.value = 1;
    if (query.month) {
      listForm.value = [];
      init();
    } else {
      yearList.value = [];
      initYear();
    }
  };
  // é¡µç æ”¹å˜
  const currentChange = num => {
    currentPage.value = num;
    if (query.month) {
      init();
    } else {
      initYear();
    }
  };
  // æ•°å­—转中文
  const transFromNumber = num => {
    let changeNum = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
    let unit = ["", "十", "百", "千", "万"];
    num = parseInt(num);
    let getWan = temp => {
      let strArr = temp.toString().split("").reverse();
      let newNum = "";
      for (var i = 0; i < strArr.length; i++) {
        newNum =
          (i == 0 && strArr[i] == 0
            ? ""
            : i > 0 && strArr[i] == 0 && strArr[i - 1] == 0
            ? ""
            : changeNum[strArr[i]] + (strArr[i] == 0 ? unit[0] : unit[i])) +
          newNum;
      }
      return newNum;
    };
    let overWan = Math.floor(num / 10000);
    let noWan = num % 10000;
    if (noWan.toString().length < 4) noWan = "0" + noWan;
    return overWan ? getWan(overWan) + "万" + getWan(noWan) : getWan(num);
  };
  // åˆå§‹åŒ–月度数据
  const init = () => {
    pageLoading.value = true;
    console.log(query.year, "query.year");
    let year = query.year.getFullYear();
    let month0 = query.month ? query.month : new Date().getMonth() + 1;
    let month = month0 > 9 ? month0 : "0" + month0;
    page({
      size: pageSize.value,
      current: currentPage.value,
      time: year + "-" + month + "-01 00:00:00",
      userName: query.userName,
      deptId: query.deptId,
    })
      .then(res => {
        pageLoading.value = false;
        total.value = res.data.page.total;
        listForm.value = res.data.page.records.map(item => {
          for (let key in item.monthlyAttendance) {
            let type = getDayByDic(key);
            if (type != undefined || type != null) {
              item[`day${type}`] = item.monthlyAttendance[key];
            }
          }
          return item;
        });
        let headerList = res.data.headerList;
        weeks.value = [];
        headerList.forEach(item => {
          let obj = {
            weekNum: item.weekly,
            week: item.headerTime.split(" ")[1],
            day: item.headerTime.split(" ")[0],
          };
          weeks.value.push(obj);
        });
      })
      .catch(() => {
        pageLoading.value = false;
      });
  };
  // åˆå§‹åŒ–年度数据
  const initYear = () => {
    pageLoading.value = true;
    let year = query.year.getFullYear();
    pageYear({
      size: pageSize.value,
      current: currentPage.value,
      time: year + "-01-01 00:00:00",
      userName: query.userName,
      deptId: query.deptId,
    }).then(res => {
      pageLoading.value = false;
      total.value = res.data.total;
      yearList.value = res.data.records.map(item => {
        for (let key in item.year) {
          let type = getDayByDic(key);
          if (type != undefined || type != null) {
            item[`day${type}`] = item.year[key];
          }
        }
        item.monthList = [];
        for (let m in item.month) {
          let obj = {};
          for (let key in item.month[m]) {
            let type = getDayByDic(key);
            if (type != undefined || type != null) {
              obj[`day${type}`] = item.month[m][key];
            }
          }
          obj.totalMonthAttendance = item.month[m].totalMonthAttendance;
          item.monthList.push(obj);
        }
        return item;
      });
    });
  };
  // é¼ æ ‡è¿›å…¥
  const onMouseEnter = index => {
    currentUserIndex.value = index;
  };
  // ç¡®è®¤æŽ’班
  const confirmScheduling = () => {
    if (!schedulingQuery.week) {
      proxy.$modal.msgError("请选择周次");
      return;
    }
    let time = schedulingQuery.week.getTime();
    // æ ¼å¼åŒ–日期为 YYYY-MM-DD æ ¼å¼
    const formatDate = date => {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      return `${year}-${month}-${day}`;
    };
    let startWeek =
      formatDate(new Date(time - 24 * 60 * 60 * 1000)) + " 00:00:00";
    let endWeek =
      formatDate(new Date(time + 24 * 60 * 60 * 1000 * 5)) + " 00:00:00";
    if (!schedulingQuery.userId || schedulingQuery.userId.length == 0) {
      proxy.$modal.msgError("请选择人员");
      return;
    }
    if (!schedulingQuery.shift) {
      proxy.$modal.msgError("请选择班次");
      return;
    }
    loading.value = true;
    add({
      startWeek,
      endWeek,
      userId: schedulingQuery.userId.join(","),
      shift: schedulingQuery.shift,
    })
      .then(res => {
        loading.value = false;
        proxy.$modal.msgSuccess("操作成功");
        schedulingVisible.value = false;
        schedulingQuery.week = "";
        schedulingQuery.userId = null;
        schedulingQuery.shift = "";
        refresh();
      })
      .catch(err => {
        loading.value = false;
      });
  };
  // æ—¶é—´é…ç½®
  const configTime = () => {
    // è·³è½¬åˆ°è€ƒå‹¤æ‰“卡页面
    router.push({
      path: "/personnelManagement/checkinRules",
    });
  };
  // åˆ¤æ–­æ˜¯å¦ä¸ºç©ºå¯¹è±¡
  const isObjectEmpty = obj => {
    return Object.keys(obj).some(key => !obj[key]);
  };
  // å¯¼å‡º
  const handleDown = () => {
    let year = query.year.getFullYear();
    let time = "";
    if (query.month) {
      let month = query.month > 9 ? query.month : "0" + query.month;
      time = year + "-" + month + "-01 00:00:00";
    } else {
      time = year + "-01-01 00:00:00";
    }
    downLoading.value = true;
    exportFile({
      time,
      userName: query.userName,
      deptId: query.deptId,
      isMonth: query.month ? true : false,
    })
      .then(res => {
        proxy.$modal.msgSuccess("下载成功");
        downLoading.value = false;
        const blob = new Blob([res], {
          type: "application/force-download",
        });
        let fileName = "";
        if (query.month) {
          fileName = year + "-" + query.month + " ç­æ¬¡ä¿¡æ¯";
        } else {
          fileName = year + " ç­æ¬¡æ±‡æ€»";
        }
        proxy.$download.saveAs(blob, fileName + ".xlsx");
      })
      .catch(err => {
        downLoading.value = false;
      });
  };
  // å¤„理命令
  const handleCommand = (e, m) => {
    if (e != m.shift) {
      update({
        id: m.id,
        shift: e,
      }).then(res => {
        proxy.$modal.msgSuccess("操作成功");
        m.shift = e;
      });
    }
  };
  // æŸ¥è¯¢è§„则列表
  const fetchData = () => {
    getAttendanceRules({ current: -1, size: -1 }).then(res => {
      classType.value = res.data.records;
    });
  };
  // èŽ·å–ç”¨æˆ·
  const getUsers = () => {
    // selectUserCondition({ type: 1 }).then(res => {
    //   let arr = res.data;
    //   personList.value = arr;
    // });
    selectUserCondition().then(res => {
      let arr = res.data;
      personList.value = arr;
    });
  };
  // æ ¹æ®å­—典获取日期
  const getDayByDic = e => {
    let obj = classType.value.find(m => m.locationName == e);
    if (obj) {
      return obj.id;
    }
  };
  // æ ¹æ®å­—典获取班次
  const getShiftByDic = e => {
    let obj = classType.value.find(m => m.id == e);
    if (obj) {
      return obj.locationName;
    }
    return "无";
  };
  // åˆå§‹åŒ–
  onMounted(() => {
    fetchData();
    getUsers();
    fetchDeptOptions();
    if (query.month) {
      init();
    } else {
      initYear();
    }
    monthList.value = [];
    for (let i = 12; i > 0; i--) {
      monthList.value.push(i);
    }
    monthList.value.reverse();
  });
</script>
<style scoped>
  .class-page {
    padding: 16px;
  }
  .form_title {
    height: 36px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    font-weight: 800;
  }
  /* æœç´¢åŒºåŸŸæ ·å¼ */
  .search-container {
    background: #f9fafb;
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 20px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  }
  .search-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .search-row {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: nowrap;
    overflow-x: auto;
  }
  .search-item {
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .search-label {
    font-size: 14px;
    font-weight: 500;
    color: #333;
    min-width: 65px;
    text-align: right;
  }
  .search-input-group {
    display: flex;
    align-items: center;
  }
  .search-actions {
    display: flex;
    align-items: center;
    margin-left: 8px;
  }
  .search-buttons {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex: 1;
  }
  /* å“åº”式调整 */
  @media (max-width: 1200px) {
    .search-row {
      gap: 12px;
    }
    .search-item {
      gap: 4px;
    }
    .search-label {
      min-width: 60px;
      font-size: 13px;
    }
    .search-actions {
      margin-left: 4px;
    }
    .search-buttons {
      margin-left: 12px;
    }
  }
  @media (max-width: 992px) {
    .search-row {
      flex-wrap: wrap;
      justify-content: flex-start;
    }
    .search-buttons {
      margin-left: 0;
      margin-top: 12px;
      width: 100%;
      justify-content: flex-start;
    }
  }
  /* æŽ’班容器 */
  .scheduling-container {
    width: 100%;
    min-height: calc(100vh - 280px);
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    overflow: hidden;
    margin-bottom: 20px;
  }
  /* æŽ’班表格 */
  .scheduling-table {
    display: flex;
    width: 100%;
    height: calc(100vh - 280px);
  }
  /* å·¦ä¾§äººå‘˜ä¿¡æ¯ */
  .scheduling-left {
    width: 240px;
    min-width: 240px;
    background-color: #f9fafb;
    border-right: 1px solid #e5e7eb;
  }
  /* å³ä¾§æŽ’班内容 */
  .scheduling-right {
    flex: 1;
    overflow-x: auto;
  }
  /* è¡¨å¤´ */
  .scheduling-header {
    height: 48px;
    line-height: 48px;
    padding: 0 20px;
    font-size: 14px;
    font-weight: 600;
    color: #333;
    background-color: #f3f4f6;
    border-bottom: 1px solid #e5e7eb;
  }
  /* äººå‘˜ä¿¡æ¯è¡Œ */
  .scheduling-user {
    display: flex;
    align-items: center;
    padding: 10px 10px;
    border-bottom: 1px solid #e5e7eb;
    transition: all 0.3s ease;
    height: 65px;
    box-sizing: border-box;
  }
  .scheduling-user:hover,
  .scheduling-user-hover {
    background-color: rgba(59, 130, 246, 0.05);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  }
  /* ç”¨æˆ·å¤´åƒ */
  .user-avatar {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: linear-gradient(135deg, #3b82f6, #60a5fa);
    color: #fff;
    font-size: 18px;
    font-weight: 600;
    text-align: center;
    line-height: 42px;
    margin-right: 16px;
    box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
    transition: all 0.3s ease;
  }
  .scheduling-user:hover .user-avatar {
    transform: scale(1.05);
    box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
  }
  /* ç”¨æˆ·è¯¦æƒ… */
  .user-details {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 100%;
  }
  /* ç”¨æˆ·å */
  .user-name {
    font-size: 14px;
    font-weight: 600;
    color: #333;
    margin: 0 0 6px 0;
    line-height: 1.2;
  }
  /* ç”¨æˆ·ç»Ÿè®¡ */
  .user-stats {
    /* display: flex; */
    /* flex-wrap: wrap;
                                                                                                                                                                        gap: 10px; */
    margin-bottom: 4px;
  }
  .stat-item {
    font-size: 12px;
    color: #666;
    /* background-color: #f9fafb; */
    /* padding: 2px 8px; */
    padding-right: 4px;
    /* border-radius: 10px; */
    /* border: 1px solid #e5e7eb; */
    /* transition: all 0.3s ease; */
  }
  .scheduling-user:hover .stat-item {
    background-color: rgba(59, 130, 246, 0.1);
    border-color: rgba(59, 130, 246, 0.3);
  }
  /* åˆè®¡å‡ºå‹¤ */
  .user-total {
    display: flex;
    align-items: center;
  }
  .total-label {
    font-size: 12px;
    color: #666;
    margin-right: 6px;
    font-weight: 500;
  }
  .total-value {
    font-size: 14px;
    font-weight: 600;
    color: #3b82f6;
    background-color: rgba(59, 130, 246, 0.1);
    padding: 2px 10px;
    border-radius: 10px;
    border: 1px solid rgba(59, 130, 246, 0.2);
  }
  /* æ—¥åŽ†å¤´éƒ¨ */
  .calendar-header {
    display: flex;
    border-bottom: 1px solid #e5e7eb;
  }
  .calendar-header-item {
    width: 50px;
    min-width: 50px;
    height: 48px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-right: 1px solid #e5e7eb;
    background-color: #f3f4f6;
    position: relative;
  }
  .week-number {
    position: absolute;
    top: 6px;
    font-size: 10px;
    font-weight: 600;
    color: #3b82f6;
    background-color: #dbeafe;
    padding: 3px 6px;
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(59, 130, 246, 0.2);
    transition: all 0.3s ease;
  }
  .week-number:hover {
    background-color: #3b82f6;
    color: #fff;
    transform: translateY(-1px);
  }
  .day-info {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .day-number {
    font-size: 14px;
    font-weight: 500;
    color: #333;
  }
  .day-week {
    font-size: 12px;
    color: #666;
  }
  /* æ—¥åކ䏻体 */
  .calendar-body {
    display: flex;
    flex-direction: column;
  }
  /* æ—¥åŽ†è¡Œ */
  .calendar-row {
    display: flex;
    border-bottom: 1px solid #e5e7eb;
    transition: all 0.3s ease;
  }
  .calendar-row:hover,
  .calendar-row-hover {
    background-color: rgba(59, 130, 246, 0.03);
  }
  /* æ—¥åŽ†å•å…ƒæ ¼ */
  .calendar-cell {
    width: 50px;
    min-width: 50px;
    height: 65px;
    border-right: 1px solid #e5e7eb;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  /* ç­æ¬¡ä¸‹æ‹‰æ¡† */
  .shift-dropdown {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  /* ç­æ¬¡æ¡† */
  .shift-box {
    width: 90%;
    height: 80%;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 500;
    transition: all 0.3s ease;
  }
  .shift-box:hover {
    transform: scale(1.05);
  }
  /* ç­æ¬¡ç±»åž‹æ ·å¼ */
  .shift-box-early {
    background: rgba(59, 130, 246, 0.15);
    color: #3b82f6;
  }
  .shift-box-mid {
    background: rgba(139, 92, 246, 0.15);
    color: #8b5cf6;
  }
  .shift-box-night {
    background: rgba(245, 158, 11, 0.15);
    color: #f59e0b;
  }
  .shift-box-rest {
    background: rgba(16, 185, 129, 0.15);
    color: #10b981;
  }
  .shift-box-leave {
    background: rgba(239, 68, 68, 0.15);
    color: #ef4444;
  }
  .shift-box-other {
    background: rgba(236, 72, 153, 0.15);
    color: #ec4899;
  }
  .shift-box-business {
    background: rgba(17, 24, 39, 0.15);
    color: #111827;
  }
  /* ç­æ¬¡æ–‡æœ¬ */
  .shift-text {
    text-align: center;
  }
  /* å¹´åº¦è¡¨æ ¼ */
  .yearly-table {
    display: flex;
    width: 100%;
    height: calc(100vh - 280px);
  }
  /* å¹´åº¦æ—¥åކ */
  .yearly-calendar {
    width: 100%;
  }
  /* å¹´åº¦è¡¨å¤´ */
  .yearly-header {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    background-color: #f3f4f6;
    border-bottom: 1px solid #e5e7eb;
  }
  .yearly-header-item {
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-right: 1px solid #e5e7eb;
  }
  .month-name {
    font-size: 14px;
    font-weight: 500;
    color: #333;
  }
  /* å¹´åº¦ä¸»ä½“ */
  .yearly-body {
    display: flex;
    flex-direction: column;
  }
  /* å¹´åº¦è¡Œ */
  .yearly-row {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    border-bottom: 1px solid #e5e7eb;
    transition: all 0.3s ease;
  }
  /* å¹´åº¦å•元格 */
  .yearly-cell {
    padding: 12px;
    border-right: 1px solid #e5e7eb;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  /* æœˆåº¦å‡ºå‹¤ */
  .monthly-attendance {
    margin-bottom: 8px;
  }
  .attendance-label {
    font-size: 12px;
    color: #666;
    margin-right: 4px;
  }
  .attendance-value {
    font-size: 14px;
    font-weight: 600;
    color: #333;
  }
  /* æœˆåº¦ç»Ÿè®¡ */
  .monthly-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    justify-content: center;
  }
  .monthly-stats .stat-item {
    font-size: 11px;
  }
  /* æ»šåŠ¨æ¡æ ·å¼ */
  .scheduling-right::-webkit-scrollbar {
    height: 8px;
  }
  .scheduling-right::-webkit-scrollbar-track {
    background: #f1f1f1;
  }
  .scheduling-right::-webkit-scrollbar-thumb {
    background: #c1c1c1;
    border-radius: 4px;
  }
  .scheduling-right::-webkit-scrollbar-thumb:hover {
    background: #a8a8a8;
  }
  .search_label {
    font-size: 14px;
    font-weight: 500;
    color: #333;
    margin-bottom: 8px;
    margin-top: 12px;
  }
</style>