From fe167dd71a1300aeae07522db990d6b3fdb77a0e Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 12 三月 2026 13:26:02 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New' into dev_中兴实强
---
src/views/personnelManagement/classsSheduling/index.vue | 1283 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,283 insertions(+), 0 deletions(-)
diff --git a/src/views/personnelManagement/classsSheduling/index.vue b/src/views/personnelManagement/classsSheduling/index.vue
new file mode 100644
index 0000000..99c19bb
--- /dev/null
+++ b/src/views/personnelManagement/classsSheduling/index.vue
@@ -0,0 +1,1283 @@
+<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.sysDeptId"
+ :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 === '鏃╃彮',
+ 'shift-box-mid': m.shift === '涓彮',
+ 'shift-box-night': m.shift === '澶滅彮',
+ 'shift-box-rest': m.shift === '浼戞伅',
+ 'shift-box-leave': m.shift === '璇峰亣',
+ 'shift-box-other': m.shift === '澶�11',
+ 'shift-box-business': m.shift === '澶�12',
+ }">
+ <span class="shift-text">{{ getShiftNameByValue(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.shift || '鈥�'
+ }}</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.id"
+ :label="item.staffName"
+ :value="item.id">
+ </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="getShiftNameByValue(item.shift)"
+ :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,
+ staffOnJobListPage,
+ } from "@/api/personnelManagement/class";
+ import { deptTreeSelect } from "@/api/system/user.js";
+ import { getAttendanceRules } from "@/api/personnelManagement/attendanceRules.js";
+ import { useDict } from "@/utils/dict";
+ const { proxy } = getCurrentInstance();
+ const router = useRouter();
+
+ // 鏌ヨ鏉′欢
+ const query = reactive({
+ userName: "",
+ sysDeptId: "",
+ year: new Date(),
+ month: new Date().getMonth() + 1,
+ });
+ // 鑾峰彇鐝瀛楀吀鍊�
+ const { shifts_list } = useDict("shifts_list");
+ // 鏈堜唤閫夐」
+ 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([]);
+
+ // 鐝绫诲瀷
+ 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([]);
+
+ // 褰撳墠椤�
+ 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([]);
+
+ // 瀵煎嚭鍔犺浇鐘舵��
+ const downLoading = ref(false);
+
+ // 鑾峰彇閮ㄩ棬鍒楄〃
+ const fetchDeptOptions = () => {
+ deptTreeSelect().then(response => {
+ deptOptions.value = filterDisabledDept(
+ JSON.parse(JSON.stringify(response.data))
+ );
+ });
+ };
+
+ // 鏍规嵁鐝鍊艰幏鍙栫彮娆″悕绉�
+ const getShiftNameByValue = value => {
+ if (!value) return "";
+ const shift = shifts_list.value.find(item => item.value === value);
+ return shift ? shift.label : value;
+ };
+
+ // 杩囨护绂佺敤鐨勯儴闂�
+ 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.sysDeptId = "";
+ 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,
+ sysDeptId: query.sysDeptId,
+ })
+ .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,
+ sysDeptId: query.sysDeptId,
+ }).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,
+ staffOnJobId: schedulingQuery.userId.join(","),
+ personalAttendanceLocationConfigId: 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,
+ sysDeptId: query.sysDeptId,
+ 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,
+ personalAttendanceLocationConfigId: e,
+ }).then(res => {
+ proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+ // m.shift = e;
+ if (query.month) {
+ init();
+ } else {
+ initYear();
+ }
+ });
+ // }
+ };
+ // 鏌ヨ瑙勫垯鍒楄〃
+ 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;
+ // });
+ staffOnJobListPage({
+ current: -1,
+ size: -1,
+ staffState: 1,
+ }).then(res => {
+ let arr = res.data.records;
+ personList.value = arr;
+ });
+ };
+
+ // 鏍规嵁瀛楀吀鑾峰彇鏃ユ湡
+ const getDayByDic = e => {
+ let obj = classType.value.find(m => m.shift == e);
+ if (obj) {
+ return obj.id;
+ }
+ };
+
+ // 鏍规嵁瀛楀吀鑾峰彇鐝
+ const getShiftByDic = e => {
+ let obj = classType.value.find(m => m.shift == e);
+ if (obj) {
+ return obj.shift;
+ }
+ 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>
--
Gitblit v1.9.3