完善考勤审批功能模块

This commit is contained in:
1147680382@qq.com
2025-05-23 09:11:43 +08:00
parent 4ad03c111f
commit df432367c6
9 changed files with 2078 additions and 57 deletions
@@ -0,0 +1,475 @@
<template>
<div class="attendance-finance-container">
<el-card>
<template #header>
<div class="card-header">
<span class="title">财务查看考勤列表</span>
<div class="header-right">
<el-date-picker
v-model="dateRange"
type="month"
placeholder="选择月份"
:default-time="defaultTime"
:shortcuts="dateShortcuts"
@change="handleDateChange"
style="width: 200px; margin-right: 8px;"
/>
<el-input
v-model="searchKeyword"
placeholder="请输入姓名搜索"
clearable
@input="handleSearch"
style="width: 200px; margin-right: 8px;"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
</div>
</template>
<el-table
:data="attendanceList"
style="width: 100%"
height="calc(100vh - 280px)"
border
stripe
:header-cell-style="{
background: '#f5f7fa',
color: '#606266',
fontWeight: 'bold',
textAlign: 'center',
padding: '12px 0'
}"
:cell-style="{
padding: '8px 0'
}"
>
<el-table-column label="头像" width="80" align="center" fixed="left">
<template #default="scope">
<el-avatar :size="40" :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" width="100" show-overflow-tooltip fixed="left" />
<el-table-column prop="workDays" label="工作天数" width="100" align="center" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 0 ? 'success' : 'warning'" size="small">
{{ scope.row.status === 0 ? '正式' : '试用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="personalLeave" label="事假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="sickLeave" label="病假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="maternityLeave" label="产假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="paternityLeave" label="陪产假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="marriageLeave" label="婚假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="bereavementLeave" label="丧假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="annualLeave" label="年休假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="managementAnnualLeave" label="年休假(管理岗)" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="compensatoryLeave" label="调休假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="early" label="早退次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="late" label="迟到次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="seriousLate" label="严重迟到" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="absenteeism" label="旷工次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="missedCount" label="缺卡次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="absenceCount" label="缺勤次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="missingLogCount" label="未提交日志" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="workdayOvertime" label="工作日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="restdayOvertime" label="休息日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="holidayOvertime" label="节假日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="annualPerformance" label="年度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
<el-table-column prop="monthlyPerformance" label="月度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
<el-table-column prop="penaltyAmount" label="违纪扣奖" width="100" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="120" fixed="right" align="center">
<template #default="scope">
<el-button
type="primary"
link
size="small"
@click="handleViewAbnormal(scope.row)"
>
异常考勤
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 异常考勤详情对话框 -->
<el-dialog
v-model="abnormalDialogVisible"
title="异常考勤详情"
width="800px"
>
<div v-for="(records, date) in abnormalList" :key="date" class="abnormal-date-group">
<div class="date-header">{{ date }}</div>
<el-table
:data="records"
style="width: 100%"
border
stripe
>
<el-table-column prop="type" label="异常类型" width="120">
<template #default="scope">
<el-tag :type="getAbnormalTypeTag(scope.row.type)">
{{ getAbnormalTypeLabel(scope.row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="异常详情" min-width="200">
<template #default="scope">
<template v-if="scope.row.type === 'abnormal'">
<div>考勤类型{{ scope.row.checkType === 'OnDuty' ? '上班' : '下班' }}</div>
<div>考勤时间{{ scope.row.baseCheckTime }}</div>
<div>考勤结果{{ getAbnormalResultLabel(scope.row.realResult) }}</div>
</template>
<template v-else-if="scope.row.type === 'checkin'">
<div>缺卡次数{{ scope.row.missedCount }}</div>
</template>
<template v-else-if="scope.row.type === 'report'">
<div>日志状态{{ scope.row.reportCount === 0 ? '未写日志' : '正常' }}</div>
</template>
</template>
</el-table-column>
<el-table-column label="修改记录" min-width="300">
<template #default="scope">
<div v-if="scope.row.modifications && scope.row.modifications.length > 0">
<div v-for="(mod, index) in scope.row.modifications" :key="index" class="modification-item">
<div>修改人{{ mod.modifiedBy }}</div>
<div>修改时间{{ mod.modifiedAt }}</div>
<div>修改前{{ getModificationValue(mod.originalValue, scope.row.type) }}</div>
<div>修改后{{ getModificationValue(mod.modifiedValue, scope.row.type) }}</div>
<div v-if="mod.isFinally" class="finally-tag">最终修改</div>
</div>
</div>
<span v-else>无修改记录</span>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ElMessage } from "element-plus";
import { Search } from '@element-plus/icons-vue';
import dayjs from 'dayjs';
import {
getFinanceAttendanceList,
getAbnormalAttendanceDetail,
} from "../../../../api/modules/index";
// 权限检查
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
// if (userInfo.role !== 3) {
// ElMessage.error("您没有权限访问此页面");
// window.history.back();
// }
// 列表数据
const attendanceList = ref([]);
const originalList = ref([]);
const searchKeyword = ref('');
// 日期范围
const dateRange = ref(null);
const defaultTime = new Date(2000, 1, 1, 0, 0, 0);
// 日期快捷选项
const dateShortcuts = [
{
text: '上个月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() - 1, 1);
},
},
{
text: '本月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), 1);
},
},
{
text: '下个月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() + 1, 1);
},
},
];
// 设置默认日期范围(上个月)
const setDefaultDateRange = () => {
const now = new Date();
dateRange.value = new Date(now.getFullYear(), now.getMonth() - 1, 1);
};
// 处理日期变化
const handleDateChange = (val) => {
if (val) {
fetchList();
}
};
// 获取列表数据
const fetchList = async () => {
try {
const params = {};
if (dateRange.value) {
params.year = dateRange.value.getFullYear();
params.month = dateRange.value.getMonth() + 1; // 月份从0开始,需要+1
}
const res = await getFinanceAttendanceList(params);
if (res.errcode === 0 && res.result && res.result.data) {
originalList.value = res.result.data;
attendanceList.value = res.result.data;
} else {
ElMessage.error(res.errmsg || "获取列表失败");
}
} catch (error) {
ElMessage.error("获取列表失败");
}
};
// 搜索
const handleSearch = () => {
if (!searchKeyword.value) {
attendanceList.value = originalList.value;
return;
}
attendanceList.value = originalList.value.filter(item =>
item.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
};
// 异常考勤对话框
const abnormalDialogVisible = ref(false);
const abnormalList = ref([]);
const currentAbnormalUser = ref({
id: '',
userid: ''
});
// 获取异常类型标签
const getAbnormalTypeLabel = (type) => {
const typeMap = {
'abnormal': '考勤异常',
'checkin': '签到签退',
'report': '日志异常'
};
return typeMap[type] || '未知';
};
// 获取异常类型标签样式
const getAbnormalTypeTag = (type) => {
const typeMap = {
'abnormal': 'danger',
'checkin': 'warning',
'report': 'info'
};
return typeMap[type] || '';
};
// 获取异常结果标签
const getAbnormalResultLabel = (result) => {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[result] || '未知';
};
// 获取修改记录的值显示
const getModificationValue = (value, type) => {
if (type === 'abnormal') {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[value] || value;
} else if (type === 'checkin') {
return `${value}`;
} else if (type === 'report') {
return value == 0 ? '未写日志' : '正常';
}
return value;
};
// 查看异常考勤
const handleViewAbnormal = async (row) => {
try {
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const res = await getAbnormalAttendanceDetail(row.userid, year, month);
if (res.errcode === 0) {
currentAbnormalUser.value = {
id: res.result.id,
userid: res.result.userid
};
abnormalList.value = res.result.data;
abnormalDialogVisible.value = true;
} else {
ElMessage.error(res.errmsg || "获取异常考勤数据失败");
}
} catch (error) {
ElMessage.error("获取异常考勤数据失败");
}
};
// 页面加载时设置默认日期范围并获取数据
onMounted(() => {
setDefaultDateRange();
fetchList();
});
</script>
<style lang="scss" scoped>
.attendance-finance-container {
height: calc(100vh - 120px);
padding: 20px;
background-color: var(--el-bg-color-page);
:deep(.el-card) {
height: 100%;
display: flex;
flex-direction: column;
border-radius: 8px;
box-shadow: var(--el-box-shadow-light);
.el-card__body {
flex: 1;
overflow: hidden;
padding: 0;
display: flex;
flex-direction: column;
}
}
:deep(.el-table) {
flex: 1;
.el-table__body-wrapper {
height: 100%;
}
.el-table__cell {
padding: 8px 0;
}
.el-table__header {
th {
background-color: #f5f7fa;
font-weight: bold;
color: #606266;
height: 48px;
padding: 12px 0;
}
}
.el-table__row {
height: 56px;
}
.el-table__fixed-right {
height: 100% !important;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
}
.el-table__fixed {
height: 100% !important;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
.title {
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
}
}
.abnormal-date-group {
margin-bottom: 20px;
.date-header {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
padding: 8px;
background-color: #f5f7fa;
border-radius: 4px;
}
.modification-item {
padding: 8px;
border-bottom: 1px solid #ebeef5;
position: relative;
&:last-child {
border-bottom: none;
}
.finally-tag {
position: absolute;
top: 8px;
right: 8px;
background-color: #67c23a;
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
}
}
}
:deep(.el-dialog__body) {
padding: 20px;
}
:deep(.el-dialog__header) {
margin: 0;
padding: 20px;
border-bottom: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
}
:deep(.el-dialog__footer) {
padding: 20px;
border-top: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
}
:deep(.el-dialog) {
border-radius: 8px;
overflow: hidden;
}
</style>
@@ -82,13 +82,6 @@
<el-table-column prop="annualPerformance" label="年度绩效" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="monthlyPerformance" label="月度绩效" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="penaltyAmount" label="违纪扣奖" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="flag" label="审批状态" width="100" align="center">
<template #default="scope">
<el-tag :type="getFlagTagType(scope.row.flag)" size="small">
{{ getFlagLabel(scope.row.flag) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="scope">
<el-button
@@ -104,7 +97,6 @@
link
size="small"
@click="handleApprove(scope.row)"
v-if="scope.row.flag === 0"
>
审批
</el-button>
@@ -221,16 +213,6 @@
<el-input-number v-model="editForm.holidayOvertime" :min="0" :precision="1" :step="0.5" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年度绩效" prop="annualPerformance">
<el-input-number v-model="editForm.annualPerformance" :min="0" :max="100" :precision="1" :step="0.5" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="月度绩效" prop="monthlyPerformance">
<el-input-number v-model="editForm.monthlyPerformance" :min="0" :max="100" :precision="1" :step="0.5" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="违纪扣奖" prop="penaltyAmount">
<el-input-number v-model="editForm.penaltyAmount" :min="0" :precision="2" :step="100" />
@@ -271,6 +253,7 @@
<template #default="scope">
<template v-if="scope.row.type === 'abnormal'">
<div>考勤类型{{ scope.row.checkType === 'OnDuty' ? '上班' : '下班' }}</div>
<div>考勤时间{{ scope.row.baseCheckTime }}</div>
<div class="result-row">
考勤结果
<el-select
@@ -289,10 +272,32 @@
</div>
</template>
<template v-else-if="scope.row.type === 'checkin'">
<div>缺卡次数{{ scope.row.missedCount }}</div>
<div class="result-row">
缺卡次数
<el-input-number
v-model="scope.row.missedCount"
size="small"
style="width: 100px; margin: 0 8px;"
:min="0"
:max="10"
@change="handleUpdateAbnormal(scope.row)"
/>
</div>
</template>
<template v-else-if="scope.row.type === 'report'">
<div>日志状态{{ scope.row.reportCount === 0 ? '未提交' : '已提交' }}</div>
<div class="result-row">
日志状态
<el-select
v-model="scope.row.reportCount"
size="small"
style="width: 100px; margin: 0 8px;"
@change="handleUpdateAbnormal(scope.row)"
>
<el-option label="未写日志" :value="0" />
<el-option label="正常" :value="1" />
</el-select>
</div>
</template>
</template>
</el-table-column>
@@ -302,8 +307,8 @@
<div v-for="(mod, index) in scope.row.modifications" :key="index" class="modification-item">
<div>修改人{{ mod.modifiedBy }}</div>
<div>修改时间{{ mod.modifiedAt }}</div>
<div>修改前{{ mod.originalValue }}</div>
<div>修改后{{ mod.modifiedValue }}</div>
<div>修改前{{ getModificationValue(mod.originalValue, scope.row.type) }}</div>
<div>修改后{{ getModificationValue(mod.modifiedValue, scope.row.type) }}</div>
<div v-if="mod.isFinally" class="finally-tag">最终修改</div>
</div>
</div>
@@ -407,8 +412,6 @@ const editForm = ref({
workdayOvertime: 0,
restdayOvertime: 0,
holidayOvertime: 0,
annualPerformance: 0,
monthlyPerformance: 0,
penaltyAmount: 0
});
@@ -433,8 +436,6 @@ const editRules = {
workdayOvertime: [{ type: 'number', message: '请输入数字' }],
restdayOvertime: [{ type: 'number', message: '请输入数字' }],
holidayOvertime: [{ type: 'number', message: '请输入数字' }],
annualPerformance: [{ type: 'number', message: '请输入数字' }],
monthlyPerformance: [{ type: 'number', message: '请输入数字' }],
penaltyAmount: [{ type: 'number', message: '请输入数字' }]
};
@@ -554,8 +555,6 @@ const handleEdit = (row) => {
workdayOvertime: row.workdayOvertime || 0,
restdayOvertime: row.restdayOvertime || 0,
holidayOvertime: row.holidayOvertime || 0,
annualPerformance: row.annualPerformance || 0,
monthlyPerformance: row.monthlyPerformance || 0,
penaltyAmount: row.penaltyAmount || 0
};
editDialogVisible.value = true;
@@ -610,6 +609,12 @@ const handleApprove = (row) => {
const abnormalDialogVisible = ref(false);
const abnormalList = ref([]);
// 在 data 中添加新的响应式变量
const currentAbnormalUser = ref({
id: '',
userid: ''
});
// 获取异常类型标签
const getAbnormalTypeLabel = (type) => {
const typeMap = {
@@ -656,53 +661,113 @@ const getAbnormalResultTag = (result) => {
return resultMap[result] || '';
};
// 查看异常考勤
// 修改查看异常考勤方法
const handleViewAbnormal = async (row) => {
try {
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const res = await getAbnormalAttendanceDetail(row.userid, year, month);
if (res.errcode === 0) {
abnormalList.value = res.result;
currentAbnormalUser.value = {
id: res.result.id,
userid: res.result.userid
};
abnormalList.value = res.result.data;
abnormalDialogVisible.value = true;
} else {
ElMessage.error(res.errmsg || '获取异常考勤失败');
ElMessage.error(res.errmsg || "获取异常考勤数据失败");
}
} catch (error) {
ElMessage.error('获取异常考勤失败');
ElMessage.error("获取异常考勤数据失败");
}
};
// 修改异常考勤
const handleUpdateAbnormal = async (record) => {
// 保存原始值
const originalValue = record.realResult;
try {
await ElMessageBox.confirm(
'确认修改该考勤记录?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
);
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const data = {
id: record.id,
userid: record.userid,
id: currentAbnormalUser.value.id,
userid: currentAbnormalUser.value.userid,
year,
month,
data: {
realResult: record.realResult
}
data: abnormalList.value
};
const res = await updateAbnormalAttendance(data);
if (res.errcode === 0) {
ElMessage.success("修改成功");
fetchList()
// 重新获取异常考勤数据
const detailRes = await getAbnormalAttendanceDetail(record.userid, year, month);
const detailRes = await getAbnormalAttendanceDetail(currentAbnormalUser.value.userid, year, month);
if (detailRes.errcode === 0) {
abnormalList.value = detailRes.result;
currentAbnormalUser.value = {
id: detailRes.result.id,
userid: detailRes.result.userid
};
abnormalList.value = detailRes.result.data;
}
} else {
// 如果修改失败,恢复原值
record.realResult = originalValue;
ElMessage.error(res.errmsg || "修改失败");
}
} catch (error) {
ElMessage.error("修改失败");
if (error === 'cancel') {
// 用户取消时恢复原值并刷新列表
record.realResult = originalValue;
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const detailRes = await getAbnormalAttendanceDetail(currentAbnormalUser.value.userid, year, month);
if (detailRes.errcode === 0) {
currentAbnormalUser.value = {
id: detailRes.result.id,
userid: detailRes.result.userid
};
abnormalList.value = detailRes.result.data;
}
ElMessage.info('已取消修改');
} else {
// 其他错误时也恢复原值
record.realResult = originalValue;
ElMessage.error("修改失败");
}
}
};
// 获取修改记录的值显示
const getModificationValue = (value, type) => {
if (type === 'abnormal') {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[value] || value;
} else if (type === 'checkin') {
return `${value}`;
} else if (type === 'report') {
return value == 0 ? '未写日志' : '正常';
}
return value;
};
// 页面加载时设置默认日期范围并获取数据
onMounted(() => {
setDefaultDateRange();
@@ -0,0 +1,656 @@
<template>
<div class="attendance-manager-container">
<el-card>
<template #header>
<div class="card-header">
<span class="title">主管审批列表</span>
<div class="header-right">
<el-date-picker
v-model="dateRange"
type="month"
placeholder="选择月份"
:default-time="defaultTime"
:shortcuts="dateShortcuts"
@change="handleDateChange"
style="width: 200px; margin-right: 8px;"
/>
<el-input
v-model="searchKeyword"
placeholder="请输入姓名搜索"
clearable
@input="handleSearch"
style="width: 200px; margin-right: 8px;"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
</div>
</template>
<el-table
:data="attendanceList"
style="width: 100%"
height="calc(100vh - 280px)"
border
stripe
:header-cell-style="{
background: '#f5f7fa',
color: '#606266',
fontWeight: 'bold',
textAlign: 'center',
padding: '12px 0'
}"
:cell-style="{
padding: '8px 0'
}"
>
<el-table-column label="头像" width="80" align="center" fixed="left">
<template #default="scope">
<el-avatar :size="40" :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" width="100" show-overflow-tooltip fixed="left" />
<el-table-column prop="workDays" label="工作天数" width="100" align="center" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 0 ? 'success' : 'warning'" size="small">
{{ scope.row.status === 0 ? '正式' : '试用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="personalLeave" label="事假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="sickLeave" label="病假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="maternityLeave" label="产假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="paternityLeave" label="陪产假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="marriageLeave" label="婚假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="bereavementLeave" label="丧假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="annualLeave" label="年休假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="managementAnnualLeave" label="年休假(管理岗)" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="compensatoryLeave" label="调休假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="early" label="早退次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="late" label="迟到次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="seriousLate" label="严重迟到" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="absenteeism" label="旷工次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="missedCount" label="缺卡次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="absenceCount" label="缺勤次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="missingLogCount" label="未提交日志" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="workdayOvertime" label="工作日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="restdayOvertime" label="休息日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="holidayOvertime" label="节假日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="annualPerformance" label="年度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
<el-table-column prop="monthlyPerformance" label="月度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
<el-table-column prop="penaltyAmount" label="违纪扣奖" width="100" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="280" fixed="right" align="center">
<template #default="scope">
<el-button
type="primary"
link
size="small"
@click="handleViewAbnormal(scope.row)"
>
异常考勤
</el-button>
<el-button
type="success"
link
size="small"
@click="handleApprove(scope.row)"
>
审批
</el-button>
<el-button
type="warning"
link
size="small"
@click="handleEditPerformance(scope.row)"
>
修改绩效
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 异常考勤详情对话框 -->
<el-dialog
v-model="abnormalDialogVisible"
title="异常考勤详情"
width="800px"
>
<div v-for="(records, date) in abnormalList" :key="date" class="abnormal-date-group">
<div class="date-header">{{ date }}</div>
<el-table
:data="records"
style="width: 100%"
border
stripe
>
<el-table-column prop="type" label="异常类型" width="120">
<template #default="scope">
<el-tag :type="getAbnormalTypeTag(scope.row.type)">
{{ getAbnormalTypeLabel(scope.row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="异常详情" min-width="200">
<template #default="scope">
<template v-if="scope.row.type === 'abnormal'">
<div>考勤类型{{ scope.row.checkType === 'OnDuty' ? '上班' : '下班' }}</div>
<div>考勤时间{{ scope.row.baseCheckTime }}</div>
<div>考勤结果{{ getAbnormalResultLabel(scope.row.realResult) }}</div>
</template>
<template v-else-if="scope.row.type === 'checkin'">
<div>缺卡次数{{ scope.row.missedCount }}</div>
</template>
<template v-else-if="scope.row.type === 'report'">
<div>日志状态{{ scope.row.reportCount === 0 ? '未写日志' : '正常' }}</div>
</template>
</template>
</el-table-column>
<el-table-column label="修改记录" min-width="300">
<template #default="scope">
<div v-if="scope.row.modifications && scope.row.modifications.length > 0">
<div v-for="(mod, index) in scope.row.modifications" :key="index" class="modification-item">
<div>修改人{{ mod.modifiedBy }}</div>
<div>修改时间{{ mod.modifiedAt }}</div>
<div>修改前{{ getModificationValue(mod.originalValue, scope.row.type) }}</div>
<div>修改后{{ getModificationValue(mod.modifiedValue, scope.row.type) }}</div>
<div v-if="mod.isFinally" class="finally-tag">最终修改</div>
</div>
</div>
<span v-else>无修改记录</span>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
<!-- 修改绩效对话框 -->
<el-dialog
v-model="performanceDialogVisible"
title="修改绩效"
width="500px"
>
<el-form
ref="performanceFormRef"
:model="performanceForm"
label-width="100px"
:rules="performanceRules"
>
<el-form-item label="年度绩效" prop="annualPerformance">
<el-input-number
v-model="performanceForm.annualPerformance"
:min="0"
:max="100"
:precision="1"
:step="0.5"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="月度绩效" prop="monthlyPerformance">
<el-input-number
v-model="performanceForm.monthlyPerformance"
:min="0"
:max="100"
:precision="1"
:step="0.5"
style="width: 200px"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="performanceDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitPerformanceForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Search } from '@element-plus/icons-vue';
import dayjs from 'dayjs';
import {
getManagerAttendanceList,
updateManagerAttendanceDetail,
approveManagerAttendance,
getAbnormalAttendanceDetail,
} from "../../../../api/modules/index";
// 权限检查
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
// if (userInfo.role !== 2) {
// ElMessage.error("您没有权限访问此页面");
// window.history.back();
// }
// 列表数据
const attendanceList = ref([]);
const originalList = ref([]);
const searchKeyword = ref('');
// 日期范围
const dateRange = ref(null);
const defaultTime = new Date(2000, 1, 1, 0, 0, 0);
// 日期快捷选项
const dateShortcuts = [
{
text: '上个月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() - 1, 1);
},
},
{
text: '本月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), 1);
},
},
{
text: '下个月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() + 1, 1);
},
},
];
// 设置默认日期范围(上个月)
const setDefaultDateRange = () => {
const now = new Date();
dateRange.value = new Date(now.getFullYear(), now.getMonth() - 1, 1);
};
// 处理日期变化
const handleDateChange = (val) => {
if (val) {
fetchList();
}
};
// 获取列表数据
const fetchList = async () => {
try {
const params = {};
if (dateRange.value) {
params.year = dateRange.value.getFullYear();
params.month = dateRange.value.getMonth() + 1; // 月份从0开始,需要+1
}
const res = await getManagerAttendanceList(params);
if (res.errcode === 0) {
originalList.value = res.result;
attendanceList.value = res.result;
}
} catch (error) {
ElMessage.error("获取列表失败");
}
};
// 搜索
const handleSearch = () => {
if (!searchKeyword.value) {
attendanceList.value = originalList.value;
return;
}
attendanceList.value = originalList.value.filter(item =>
item.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
};
// 获取审批状态标签
const getFlagLabel = (flag) => {
const flagMap = {
0: '待审批',
1: '已通过',
2: '已驳回'
};
return flagMap[flag] || '未知';
};
// 获取审批状态标签样式
const getFlagTagType = (flag) => {
const flagMap = {
0: 'warning',
1: 'success',
2: 'danger'
};
return flagMap[flag] || '';
};
// 审批
const handleApprove = (row) => {
ElMessageBox.confirm(
'确认审批通过该考勤记录?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(async () => {
try {
const res = await approveManagerAttendance([row.id]);
if (res.errcode === 0) {
ElMessage.success("审批成功");
fetchList();
}
} catch (error) {
ElMessage.error("审批失败");
}
}).catch(() => {
ElMessage.info('已取消审批');
});
};
// 异常考勤对话框
const abnormalDialogVisible = ref(false);
const abnormalList = ref([]);
// 在 data 中添加新的响应式变量
const currentAbnormalUser = ref({
id: '',
userid: ''
});
// 获取异常类型标签
const getAbnormalTypeLabel = (type) => {
const typeMap = {
'abnormal': '考勤异常',
'checkin': '签到签退',
'report': '日志异常'
};
return typeMap[type] || '未知';
};
// 获取异常类型标签样式
const getAbnormalTypeTag = (type) => {
const typeMap = {
'abnormal': 'danger',
'checkin': 'warning',
'report': 'info'
};
return typeMap[type] || '';
};
// 获取异常结果标签
const getAbnormalResultLabel = (result) => {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[result] || '未知';
};
// 获取异常结果标签样式
const getAbnormalResultTag = (result) => {
const resultMap = {
'Normal': 'success',
'Early': 'warning',
'Late': 'warning',
'SeriousLate': 'danger',
'Absenteeism': 'danger',
'NotSigned': 'danger'
};
return resultMap[result] || '';
};
// 获取修改记录的值显示
const getModificationValue = (value, type) => {
if (type === 'abnormal') {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[value] || value;
} else if (type === 'checkin') {
return `${value}`;
} else if (type === 'report') {
return value == 0 ? '未写日志' : '正常';
}
return value;
};
// 修改查看异常考勤方法
const handleViewAbnormal = async (row) => {
try {
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const res = await getAbnormalAttendanceDetail(row.userid, year, month);
if (res.errcode === 0) {
currentAbnormalUser.value = {
id: res.result.id,
userid: res.result.userid
};
abnormalList.value = res.result.data;
abnormalDialogVisible.value = true;
} else {
ElMessage.error(res.errmsg || "获取异常考勤数据失败");
}
} catch (error) {
ElMessage.error("获取异常考勤数据失败");
}
};
// 绩效修改相关
const performanceDialogVisible = ref(false);
const performanceFormRef = ref(null);
const performanceForm = ref({
id: '',
annualPerformance: 0,
monthlyPerformance: 0
});
const performanceRules = {
annualPerformance: [
{ required: true, message: '请输入年度绩效', trigger: 'blur' },
{ type: 'number', min: 0, max: 100, message: '年度绩效必须在0-100之间', trigger: 'blur' }
],
monthlyPerformance: [
{ required: true, message: '请输入月度绩效', trigger: 'blur' },
{ type: 'number', min: 0, max: 100, message: '月度绩效必须在0-100之间', trigger: 'blur' }
]
};
// 打开修改绩效对话框
const handleEditPerformance = (row) => {
performanceForm.value = {
id: row.id,
annualPerformance: row.annualPerformance || 0,
monthlyPerformance: row.monthlyPerformance || 0
};
performanceDialogVisible.value = true;
};
// 提交绩效表单
const submitPerformanceForm = async () => {
if (!performanceFormRef.value) return;
await performanceFormRef.value.validate(async (valid) => {
if (valid) {
try {
const res = await updateManagerAttendanceDetail(performanceForm.value);
if (res.errcode === 0) {
ElMessage.success("修改成功");
performanceDialogVisible.value = false;
fetchList();
} else {
ElMessage.error(res.errmsg || "修改失败");
}
} catch (error) {
ElMessage.error("修改失败");
}
}
});
};
// 页面加载时设置默认日期范围并获取数据
onMounted(() => {
setDefaultDateRange();
fetchList();
});
</script>
<style lang="scss" scoped>
.attendance-manager-container {
height: calc(100vh - 120px);
padding: 20px;
background-color: var(--el-bg-color-page);
:deep(.el-card) {
height: 100%;
display: flex;
flex-direction: column;
border-radius: 8px;
box-shadow: var(--el-box-shadow-light);
.el-card__body {
flex: 1;
overflow: hidden;
padding: 0;
display: flex;
flex-direction: column;
}
}
:deep(.el-table) {
flex: 1;
.el-table__body-wrapper {
height: 100%;
}
.el-table__cell {
padding: 8px 0;
}
.el-table__header {
th {
background-color: #f5f7fa;
font-weight: bold;
color: #606266;
height: 48px;
padding: 12px 0;
}
}
.el-table__row {
height: 56px;
}
.el-table__fixed-right {
height: 100% !important;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
}
.el-table__fixed {
height: 100% !important;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
.title {
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
}
}
:deep(.el-dialog__body) {
padding: 20px;
}
:deep(.el-dialog__header) {
margin: 0;
padding: 20px;
border-bottom: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
}
:deep(.el-dialog__footer) {
padding: 20px;
border-top: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
}
:deep(.el-dialog) {
border-radius: 8px;
overflow: hidden;
}
.abnormal-date-group {
margin-bottom: 20px;
.date-header {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
padding: 8px;
background-color: #f5f7fa;
border-radius: 4px;
}
.modification-item {
padding: 8px;
border-bottom: 1px solid #ebeef5;
position: relative;
&:last-child {
border-bottom: none;
}
.finally-tag {
position: absolute;
top: 8px;
right: 8px;
background-color: #67c23a;
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
}
}
}
.result-row {
display: flex;
align-items: center;
margin-top: 4px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
</style>
@@ -0,0 +1,653 @@
<template>
<div class="attendance-reviewed-container">
<el-card>
<template #header>
<div class="card-header">
<span class="title">综合部复审列表</span>
<div class="header-right">
<el-date-picker
v-model="dateRange"
type="month"
placeholder="选择月份"
:default-time="defaultTime"
:shortcuts="dateShortcuts"
@change="handleDateChange"
style="width: 200px; margin-right: 8px;"
/>
<el-input
v-model="searchKeyword"
placeholder="请输入姓名搜索"
clearable
@input="handleSearch"
style="width: 200px; margin-right: 8px;"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
</div>
</template>
<el-table
:data="attendanceList"
style="width: 100%"
height="calc(100vh - 280px)"
border
stripe
:header-cell-style="{
background: '#f5f7fa',
color: '#606266',
fontWeight: 'bold',
textAlign: 'center',
padding: '12px 0'
}"
:cell-style="{
padding: '8px 0'
}"
>
<el-table-column label="头像" width="80" align="center" fixed="left">
<template #default="scope">
<el-avatar :size="40" :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" width="100" show-overflow-tooltip fixed="left" />
<el-table-column prop="workDays" label="工作天数" width="100" align="center" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 0 ? 'success' : 'warning'" size="small">
{{ scope.row.status === 0 ? '正式' : '试用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="personalLeave" label="事假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="sickLeave" label="病假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="maternityLeave" label="产假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="paternityLeave" label="陪产假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="marriageLeave" label="婚假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="bereavementLeave" label="丧假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="annualLeave" label="年休假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="managementAnnualLeave" label="年休假(管理岗)" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="compensatoryLeave" label="调休假" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="early" label="早退次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="late" label="迟到次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="seriousLate" label="严重迟到" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="absenteeism" label="旷工次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="missedCount" label="缺卡次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="absenceCount" label="缺勤次数" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="missingLogCount" label="未提交日志" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="workdayOvertime" label="工作日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="restdayOvertime" label="休息日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="holidayOvertime" label="节假日加班" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="annualPerformance" label="年度绩效" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="monthlyPerformance" label="月度绩效" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="penaltyAmount" label="违纪扣奖" width="100" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="scope">
<el-button
type="primary"
link
size="small"
@click="handleViewAbnormal(scope.row)"
>
异常考勤
</el-button>
<el-button
type="success"
link
size="small"
@click="handleApprove(scope.row)"
>
审批
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 异常考勤详情对话框 -->
<el-dialog
v-model="abnormalDialogVisible"
title="异常考勤详情"
width="800px"
>
<div v-for="(records, date) in abnormalList" :key="date" class="abnormal-date-group">
<div class="date-header">{{ date }}</div>
<el-table
:data="records"
style="width: 100%"
border
stripe
>
<el-table-column prop="type" label="异常类型" width="120">
<template #default="scope">
<el-tag :type="getAbnormalTypeTag(scope.row.type)">
{{ getAbnormalTypeLabel(scope.row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="异常详情" min-width="200">
<template #default="scope">
<template v-if="scope.row.type === 'abnormal'">
<div>考勤类型{{ scope.row.checkType === 'OnDuty' ? '上班' : '下班' }}</div>
<div>考勤时间{{ scope.row.baseCheckTime }}</div>
<div class="result-row">
考勤结果
<el-select
v-model="scope.row.realResult"
size="small"
style="width: 100px; margin: 0 8px;"
@change="handleUpdateAbnormal(scope.row)"
>
<el-option label="正常" value="Normal" />
<el-option label="早退" value="Early" />
<el-option label="迟到" value="Late" />
<el-option label="严重迟到" value="SeriousLate" />
<el-option label="旷工迟到" value="Absenteeism" />
<el-option label="未打卡" value="NotSigned" />
</el-select>
</div>
</template>
<template v-else-if="scope.row.type === 'checkin'">
<div class="result-row">
缺卡次数
<el-input-number
v-model="scope.row.missedCount"
size="small"
style="width: 100px; margin: 0 8px;"
:min="0"
:max="10"
@change="handleUpdateAbnormal(scope.row)"
/>
</div>
</template>
<template v-else-if="scope.row.type === 'report'">
<div class="result-row">
日志状态
<el-select
v-model="scope.row.reportCount"
size="small"
style="width: 100px; margin: 0 8px;"
@change="handleUpdateAbnormal(scope.row)"
>
<el-option label="未写日志" :value="0" />
<el-option label="正常" :value="1" />
</el-select>
</div>
</template>
</template>
</el-table-column>
<el-table-column label="修改记录" min-width="300">
<template #default="scope">
<div v-if="scope.row.modifications && scope.row.modifications.length > 0">
<div v-for="(mod, index) in scope.row.modifications" :key="index" class="modification-item">
<div>修改人{{ mod.modifiedBy }}</div>
<div>修改时间{{ mod.modifiedAt }}</div>
<div>修改前{{ getModificationValue(mod.originalValue, scope.row.type) }}</div>
<div>修改后{{ getModificationValue(mod.modifiedValue, scope.row.type) }}</div>
<div v-if="mod.isFinally" class="finally-tag">最终修改</div>
</div>
</div>
<span v-else>无修改记录</span>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Search } from '@element-plus/icons-vue';
import dayjs from 'dayjs';
import {
getReviewedAttendanceList,
updateAttendanceDetail,
reviewAttendance,
getAbnormalAttendanceDetail,
updateAbnormalAttendance,
} from "../../../../api/modules/index";
// 权限检查
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
// if (userInfo.role !== 2) {
// ElMessage.error("您没有权限访问此页面");
// window.history.back();
// }
// 列表数据
const attendanceList = ref([]);
const originalList = ref([]);
const searchKeyword = ref('');
// 日期范围
const dateRange = ref(null);
const defaultTime = new Date(2000, 1, 1, 0, 0, 0);
// 日期快捷选项
const dateShortcuts = [
{
text: '上个月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() - 1, 1);
},
},
{
text: '本月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), 1);
},
},
{
text: '下个月',
value: () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() + 1, 1);
},
},
];
// 设置默认日期范围(上个月)
const setDefaultDateRange = () => {
const now = new Date();
dateRange.value = new Date(now.getFullYear(), now.getMonth() - 1, 1);
};
// 处理日期变化
const handleDateChange = (val) => {
if (val) {
fetchList();
}
};
// 获取列表数据
const fetchList = async () => {
try {
const params = {};
if (dateRange.value) {
params.year = dateRange.value.getFullYear();
params.month = dateRange.value.getMonth() + 1; // 月份从0开始,需要+1
}
const res = await getReviewedAttendanceList(params);
if (res.errcode === 0) {
originalList.value = res.result;
attendanceList.value = res.result;
}
} catch (error) {
ElMessage.error("获取列表失败");
}
};
// 搜索
const handleSearch = () => {
if (!searchKeyword.value) {
attendanceList.value = originalList.value;
return;
}
attendanceList.value = originalList.value.filter(item =>
item.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
};
// 获取审批状态标签
const getFlagLabel = (flag) => {
const flagMap = {
0: '待审批',
1: '已通过',
2: '已驳回'
};
return flagMap[flag] || '未知';
};
// 获取审批状态标签样式
const getFlagTagType = (flag) => {
const flagMap = {
0: 'warning',
1: 'success',
2: 'danger'
};
return flagMap[flag] || '';
};
// 审批
const handleApprove = (row) => {
ElMessageBox.confirm(
'确认审批通过该考勤记录?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(async () => {
try {
const res = await reviewAttendance([row.id]);
if (res.errcode === 0) {
ElMessage.success("审批成功");
fetchList();
}
} catch (error) {
ElMessage.error("审批失败");
}
}).catch(() => {
ElMessage.info('已取消审批');
});
};
// 异常考勤对话框
const abnormalDialogVisible = ref(false);
const abnormalList = ref([]);
// 在 data 中添加新的响应式变量
const currentAbnormalUser = ref({
id: '',
userid: ''
});
// 获取异常类型标签
const getAbnormalTypeLabel = (type) => {
const typeMap = {
'abnormal': '考勤异常',
'checkin': '签到签退',
'report': '日志异常'
};
return typeMap[type] || '未知';
};
// 获取异常类型标签样式
const getAbnormalTypeTag = (type) => {
const typeMap = {
'abnormal': 'danger',
'checkin': 'warning',
'report': 'info'
};
return typeMap[type] || '';
};
// 获取异常结果标签
const getAbnormalResultLabel = (result) => {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[result] || '未知';
};
// 获取异常结果标签样式
const getAbnormalResultTag = (result) => {
const resultMap = {
'Normal': 'success',
'Early': 'warning',
'Late': 'warning',
'SeriousLate': 'danger',
'Absenteeism': 'danger',
'NotSigned': 'danger'
};
return resultMap[result] || '';
};
// 获取修改记录的值显示
const getModificationValue = (value, type) => {
if (type === 'abnormal') {
const resultMap = {
'Normal': '正常',
'Early': '早退',
'Late': '迟到',
'SeriousLate': '严重迟到',
'Absenteeism': '旷工迟到',
'NotSigned': '未打卡'
};
return resultMap[value] || value;
} else if (type === 'checkin') {
return `${value}`;
} else if (type === 'report') {
return value == 0 ? '未写日志' : '正常';
}
return value;
};
// 修改查看异常考勤方法
const handleViewAbnormal = async (row) => {
try {
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const res = await getAbnormalAttendanceDetail(row.userid, year, month);
if (res.errcode === 0) {
currentAbnormalUser.value = {
id: res.result.id,
userid: res.result.userid
};
abnormalList.value = res.result.data;
abnormalDialogVisible.value = true;
} else {
ElMessage.error(res.errmsg || "获取异常考勤数据失败");
}
} catch (error) {
ElMessage.error("获取异常考勤数据失败");
}
};
// 修改异常考勤
const handleUpdateAbnormal = async (record) => {
// 保存原始值
const originalValue = record.realResult;
try {
await ElMessageBox.confirm(
'确认修改该考勤记录?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
);
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const data = {
id: currentAbnormalUser.value.id,
userid: currentAbnormalUser.value.userid,
year,
month,
data: abnormalList.value
};
const res = await updateAbnormalAttendance(data);
if (res.errcode === 0) {
ElMessage.success("修改成功");
fetchList()
// 重新获取异常考勤数据
const detailRes = await getAbnormalAttendanceDetail(currentAbnormalUser.value.userid, year, month);
if (detailRes.errcode === 0) {
currentAbnormalUser.value = {
id: detailRes.result.id,
userid: detailRes.result.userid
};
abnormalList.value = detailRes.result.data;
}
} else {
// 如果修改失败,恢复原值
record.realResult = originalValue;
ElMessage.error(res.errmsg || "修改失败");
}
} catch (error) {
if (error === 'cancel') {
// 用户取消时恢复原值并刷新列表
record.realResult = originalValue;
const year = dateRange.value.getFullYear();
const month = dateRange.value.getMonth() + 1;
const detailRes = await getAbnormalAttendanceDetail(currentAbnormalUser.value.userid, year, month);
if (detailRes.errcode === 0) {
currentAbnormalUser.value = {
id: detailRes.result.id,
userid: detailRes.result.userid
};
abnormalList.value = detailRes.result.data;
}
ElMessage.info('已取消修改');
} else {
// 其他错误时也恢复原值
record.realResult = originalValue;
ElMessage.error("修改失败");
}
}
};
// 页面加载时设置默认日期范围并获取数据
onMounted(() => {
setDefaultDateRange();
fetchList();
});
</script>
<style lang="scss" scoped>
.attendance-reviewed-container {
height: calc(100vh - 120px);
padding: 20px;
background-color: var(--el-bg-color-page);
:deep(.el-card) {
height: 100%;
display: flex;
flex-direction: column;
border-radius: 8px;
box-shadow: var(--el-box-shadow-light);
.el-card__body {
flex: 1;
overflow: hidden;
padding: 0;
display: flex;
flex-direction: column;
}
}
:deep(.el-table) {
flex: 1;
.el-table__body-wrapper {
height: 100%;
}
.el-table__cell {
padding: 8px 0;
}
.el-table__header {
th {
background-color: #f5f7fa;
font-weight: bold;
color: #606266;
height: 48px;
padding: 12px 0;
}
}
.el-table__row {
height: 56px;
}
.el-table__fixed-right {
height: 100% !important;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
}
.el-table__fixed {
height: 100% !important;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
.title {
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
}
}
:deep(.el-dialog__body) {
padding: 20px;
}
:deep(.el-dialog__header) {
margin: 0;
padding: 20px;
border-bottom: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
}
:deep(.el-dialog__footer) {
padding: 20px;
border-top: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
}
:deep(.el-dialog) {
border-radius: 8px;
overflow: hidden;
}
.abnormal-date-group {
margin-bottom: 20px;
.date-header {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
padding: 8px;
background-color: #f5f7fa;
border-radius: 4px;
}
.modification-item {
padding: 8px;
border-bottom: 1px solid #ebeef5;
position: relative;
&:last-child {
border-bottom: none;
}
.finally-tag {
position: absolute;
top: 8px;
right: 8px;
background-color: #67c23a;
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
}
}
}
.result-row {
display: flex;
align-items: center;
margin-top: 4px;
}
</style>