新增操作记录功能

This commit is contained in:
andy 2025-09-19 15:56:35 +08:00
parent 4a09fa18e4
commit 8aed1536bd
6 changed files with 200 additions and 7 deletions

View File

@ -35,3 +35,11 @@ export function updateStatistical(data) {
data: data
})
}
export function getAttendanceOper(id){
return request({
url: '/attendance/statistical/oper/' + id,
method: 'get'
})
}

View File

@ -11,5 +11,47 @@ export default{
lastDay.setMonth(lastDay.getMonth() + 1, 0); // 设置日期为下个月的第一天的前一天,即当前月的最后一天
// 格式化日期为YYYY-MM-DD格式
return lastDay.toISOString().slice(0, 10);
}
},
formatDate(date, pattern){
// 如果是时间戳,先转换为 Date 对象
if (typeof date === 'number') {
date = new Date(date);
}
// 如果是字符串,尝试转换为 Date 对象
if (typeof date === 'string') {
date = new Date(date.replace(/-/g, '/')); // 处理 Safari 兼容性
}
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false // 24小时制
};
// 简单格式直接返回
if (pattern === 'yyyy-MM-dd HH:mm:ss') {
return date.toLocaleString('zh-CN', options).replace(/\//g, '-');
}
// 自定义格式解析(示例支持 yyyy、MM、dd、HH、mm、ss
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const second = String(date.getSeconds()).padStart(2, '0');
return pattern.replace('yyyy', year)
.replace('MM', month)
.replace('dd', day)
.replace('HH', hour)
.replace('mm', minute)
.replace('ss', second);
}
}

View File

@ -0,0 +1,69 @@
<template>
<div>
<el-row>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>原数据</span>
</div>
<div class="text item">
<el-descriptions class="margin-top" :column="3" size="medium" border>
<el-descriptions-item label="应出勤"><el-tag v-if="oldInfo.shouldAttendance != newInfo.shouldAttendance" type="danger">{{oldInfo.shouldAttendance}}</el-tag><span v-else>{{oldInfo.shouldAttendance}}</span></el-descriptions-item>
<el-descriptions-item label="实出勤"><el-tag v-if="oldInfo.realAttendance != newInfo.realAttendance" type="danger">{{oldInfo.realAttendance}}</el-tag><span v-else>{{oldInfo.realAttendance}}</span></el-descriptions-item>
<el-descriptions-item label="打卡时长"><el-tag v-if="oldInfo.essentialAttendance != newInfo.essentialAttendance" type="danger">{{oldInfo.essentialAttendance}}</el-tag><span v-else>{{oldInfo.essentialAttendance}}</span></el-descriptions-item>
<el-descriptions-item label="加班时长"><el-tag v-if="oldInfo.workOvertimeNumber != newInfo.workOvertimeNumber" type="danger">{{oldInfo.workOvertimeNumber}}</el-tag><span v-else>{{oldInfo.workOvertimeNumber}}</span></el-descriptions-item>
<el-descriptions-item label="特殊加班"><el-tag v-if="oldInfo.overTimeHours != newInfo.overTimeHours" type="danger">{{oldInfo.overTimeHours}}</el-tag><span v-else>{{oldInfo.overTimeHours}}</span></el-descriptions-item>
<el-descriptions-item label="请假时长"><el-tag v-if="oldInfo.absenteeism != newInfo.absenteeism" type="danger">{{oldInfo.absenteeism}}</el-tag><span v-else>{{oldInfo.absenteeism}}</span></el-descriptions-item>
<el-descriptions-item label="异常次数"><el-tag v-if="oldInfo.lateNumber != newInfo.lateNumber" type="danger">{{oldInfo.lateNumber}}</el-tag><span v-else>{{oldInfo.lateNumber}}</span></el-descriptions-item>
<el-descriptions-item label="中班次数"><el-tag v-if="oldInfo.middleShiftNumber != newInfo.middleShiftNumber" type="danger">{{oldInfo.middleShiftNumber}}</el-tag><span v-else>{{oldInfo.middleShiftNumber}}</span></el-descriptions-item>
<el-descriptions-item label="夜班次数"><el-tag v-if="oldInfo.nightNumber != newInfo.nightNumber" type="danger">{{oldInfo.nightNumber}}</el-tag><span v-else>{{oldInfo.nightNumber}}</span></el-descriptions-item>
</el-descriptions>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>新数据</span>
</div>
<div class="text item">
<el-descriptions class="margin-top" :column="3" size="medium" border>
<el-descriptions-item label="应出勤"><el-tag v-if="oldInfo.shouldAttendance != newInfo.shouldAttendance" type="danger">{{newInfo.shouldAttendance}}</el-tag><span v-else>{{newInfo.shouldAttendance}}</span></el-descriptions-item>
<el-descriptions-item label="实出勤"><el-tag v-if="oldInfo.realAttendance != newInfo.realAttendance" type="danger">{{newInfo.realAttendance}}</el-tag><span v-else>{{newInfo.realAttendance}}</span></el-descriptions-item>
<el-descriptions-item label="打卡时长"><el-tag v-if="oldInfo.essentialAttendance != newInfo.essentialAttendance" type="danger">{{newInfo.essentialAttendance}}</el-tag><span v-else>{{newInfo.essentialAttendance}}</span></el-descriptions-item>
<el-descriptions-item label="加班时长"><el-tag v-if="oldInfo.workOvertimeNumber != newInfo.workOvertimeNumber" type="danger">{{newInfo.workOvertimeNumber}}</el-tag><span v-else>{{newInfo.workOvertimeNumber}}</span></el-descriptions-item>
<el-descriptions-item label="特殊加班"><el-tag v-if="oldInfo.overTimeHours != newInfo.overTimeHours" type="danger">{{newInfo.overTimeHours}}</el-tag><span v-else>{{newInfo.overTimeHours}}</span></el-descriptions-item>
<el-descriptions-item label="请假时长"><el-tag v-if="oldInfo.absenteeism != newInfo.absenteeism" type="danger">{{newInfo.absenteeism}}</el-tag><span v-else>{{newInfo.absenteeism}}</span></el-descriptions-item>
<el-descriptions-item label="异常次数"><el-tag v-if="oldInfo.lateNumber != newInfo.lateNumber" type="danger">{{newInfo.lateNumber}}</el-tag><span v-else>{{newInfo.lateNumber}}</span></el-descriptions-item>
<el-descriptions-item label="中班次数"><el-tag v-if="oldInfo.middleShiftNumber != newInfo.middleShiftNumber" type="danger">{{newInfo.middleShiftNumber}}</el-tag><span v-else>{{newInfo.middleShiftNumber}}</span></el-descriptions-item>
<el-descriptions-item label="夜班次数"><el-tag v-if="oldInfo.nightNumber != newInfo.nightNumber" type="danger">{{newInfo.nightNumber}}</el-tag><span v-else>{{newInfo.nightNumber}}</span></el-descriptions-item>
</el-descriptions>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "DataComparison",
props: {
oldInfo: {
},
newInfo: {
},
},
data() {
return {
}
},
created() {
},
methods: {
}
};
</script>

View File

@ -6,6 +6,7 @@
</el-form-item>
<el-form-item label="员工姓名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入员工姓名" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="所属月份" prop="month">
<el-date-picker clearable
@ -132,6 +133,13 @@
@click="handleUpdate(scope.row)"
v-hasPermi="['attendance:statistical:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-view"
v-hasPermi="['attendance:statistical:oper']"
@click="viewInfo(scope.row)"
>操作记录</el-button>
</template>
</el-table-column>
</el-table>
@ -362,11 +370,30 @@
</div>
</el-dialog>
<el-drawer
:title="info.title"
:visible.sync="info.infoOpen"
:size="'60%'"
direction="rtl">
<div class="block">
<el-timeline >
<el-timeline-item v-for="(activity, index) in info.activities" :key="index" placement="top" :timestamp="activity.operationTime">
<el-card>
<div slot="header" class="clearfix">
<span>数据修改人: {{activity.operationAccount}}</span>
</div>
<data-comparison :oldInfo="activity.oldInfo" :newInfo="activity.newInfo"/>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-drawer>
</div>
</template>
<script>
import { listStatistical,correctStatistics,updateStatistical,getStatistical} from "@/api/attendance/statistical";
import { listStatistical,correctStatistics,updateStatistical,getStatistical, getAttendanceOper} from "@/api/attendance/statistical";
import { listAllDepts } from '@/api/system/dept';
import {listAttendanceByParams} from "@/api/attendance/attendance";
import {listDetailByStaffIdAndMonth} from "@/api/personnelMatters/overTime";
@ -375,10 +402,13 @@ import { listAbnormalDetails } from '@/api/attendance/abnormal'
import { getToken } from "@/utils/auth";
import Treeselect from '@riophae/vue-treeselect'
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import DateUtils from "../../../plugins/DateUtils";
import DataComparison from '@/views/attendance/statistical/DataComparison/index.vue';
export default {
name: "Statistical",
components: { Treeselect },
components: { Treeselect,DataComparison },
data() {
return {
//
@ -454,6 +484,12 @@ export default {
//
openDetail: false,
form:{},
info:{
infoOpen: false,
title: "",
activities: [
]
},
//
rules: {
handleDate: [
@ -468,6 +504,17 @@ export default {
this.getList();
},
methods: {
viewInfo(row){
this.info.title=row.name+" ["+DateUtils.formatDate(row.month, 'yyyy-MM') +"] 操作记录";
getAttendanceOper(row.id).then(response => {
this.info.activities = response.data;
this.info.infoOpen=true;
});
},
/** 获取部门 */
getDeptList(){
listAllDepts().then(response => {
@ -639,6 +686,7 @@ export default {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
console.log(this.form)
updateStatistical(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.openStatic = false;

View File

@ -153,6 +153,13 @@
@click="handleUpdateDetail(scope.row)"
v-hasPermi="['personnelMatters:leaveDetail:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDeleteDetail(scope.row)"
v-hasPermi="['personnelMatters:leaveDetail:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
@ -197,7 +204,7 @@
<script>
import { listLeave, getLeave, delLeave, addLeave, updateLeave,autoTypeList,getNewDicts } from "@/api/personnelMatters/leave";
import { listDetail, getDetail, addDetail, updateDetail, calculationLeaveHour } from "@/api/personnelMatters/leaveDetail";
import { listDetail, getDetail, addDetail, updateDetail, calculationLeaveHour,delDetail } from "@/api/personnelMatters/leaveDetail";
import { listAllDepts } from '@/api/system/dept';
import {listStaffAll, listStaffByDept} from '@/api/system/staff';
import Treeselect from "@riophae/vue-treeselect";
@ -316,6 +323,15 @@ export default {
this.title = "修改请假详情";
});
},
handleDeleteDetail(row){
delDetail(row.id).then(response => {
this.$modal.msgSuccess("删除成功");
this.detail.detailOpen = false;
this.detailOpen(this.detail.row);
});
},
cancelDetail() {
this.detail.detailOpen = false;
},

View File

@ -341,8 +341,8 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="打卡位置:" prop="timeClock">
<el-select v-model="form.timeClock" placeholder="选择打卡机" clearable >
<el-form-item label="打卡位置:" prop="timeClockList">
<el-select v-model="form.timeClockList" multiple placeholder="选择打卡机" style="width: 100%;">
<el-option
v-for="dict in dict.type.time_clock"
:key="dict.value"
@ -350,6 +350,15 @@
:value="dict.value"
/>
</el-select>
<!-- <el-select v-model="form.timeClock" placeholder="选择打卡机" clearable >
<el-option
v-for="dict in dict.type.time_clock"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select> -->
</el-form-item>
</el-col>
</el-row>
@ -1051,7 +1060,7 @@ export default {
imageUrl:[
{ required: true, message: "请上传员工头像", trigger: "blur" },
],
timeClock: [
timeClockList: [
{ required: true, message: "打卡机不能为空", trigger: "blur" }
],
jobCode: [
@ -1159,6 +1168,7 @@ export default {
imageUrl:null,
fileId:null,
timeClock:null,
timeClockList:null,
subsidyList:null
};
this.resetForm("form");