refactor(attendance): 重构考勤统计页面,优化数据计算与表格展示
1. 重构attendanceSummary页面:重写数据统计逻辑,替换旧的统计字段为更贴合业务的指标,移除冗余的汇总footer布局 2. 修复attendanceCheck页面:新增dayjs依赖,处理生产倒班夜班转白班的下班时间清空逻辑 3. 统一代码风格与API调用逻辑,简化重复代码块
This commit is contained in:
@@ -397,6 +397,7 @@ import { listAttendanceCheck, getAttendanceCheck, delAttendanceCheck, generateAt
|
|||||||
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker";
|
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker";
|
||||||
import { listOutRequest } from "@/api/wms/outRequest";
|
import { listOutRequest } from "@/api/wms/outRequest";
|
||||||
import { listLeaveRequest } from "@/api/wms/leaveRequest";
|
import { listLeaveRequest } from "@/api/wms/leaveRequest";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AttendanceCheck",
|
name: "AttendanceCheck",
|
||||||
@@ -629,6 +630,24 @@ export default {
|
|||||||
dataMap[record.userId][dateKey] = record
|
dataMap[record.userId][dateKey] = record
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Object.values(dataMap).forEach(employeeData => {
|
||||||
|
const dates = Object.keys(employeeData).filter(key => key !== 'employeeName' && key !== 'employeeId').sort()
|
||||||
|
|
||||||
|
dates.forEach((date, index) => {
|
||||||
|
const currentRecord = employeeData[date]
|
||||||
|
if (currentRecord && currentRecord.shiftName === '生产倒班夜班') {
|
||||||
|
const nextDate = dayjs(date).add(1, 'day').format('YYYY-MM-DD')
|
||||||
|
const nextRecord = employeeData[nextDate]
|
||||||
|
|
||||||
|
if (nextRecord && nextRecord.shiftName === '夜转白') {
|
||||||
|
currentRecord.p1EndTime = ''
|
||||||
|
currentRecord.p1LastCheck = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return Object.values(dataMap)
|
return Object.values(dataMap)
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -783,6 +802,8 @@ export default {
|
|||||||
initEditForm() {
|
initEditForm() {
|
||||||
if (this.currentDetail.p1StartTime) {
|
if (this.currentDetail.p1StartTime) {
|
||||||
this.editForm.p1StartTime = this.convertToTimeString(this.currentDetail.p1StartTime)
|
this.editForm.p1StartTime = this.convertToTimeString(this.currentDetail.p1StartTime)
|
||||||
|
// this.editForm.p1StartTime = this.currentDetail.p1StartTime
|
||||||
|
|
||||||
}
|
}
|
||||||
if (this.currentDetail.p1EndTime) {
|
if (this.currentDetail.p1EndTime) {
|
||||||
this.editForm.p1EndTime = this.convertToTimeString(this.currentDetail.p1EndTime)
|
this.editForm.p1EndTime = this.convertToTimeString(this.currentDetail.p1EndTime)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<div class="date-range-section">
|
<div class="date-range-section">
|
||||||
<TimeRangePicker v-model="dateRangeParams" startKey="startDate" endKey="endDate"
|
<TimeRangePicker v-model="dateRangeParams" startKey="startDate" endKey="endDate"
|
||||||
:defaultStartTime="defaultStartTime" :defaultEndTime="defaultEndTime" format="yyyy-MM-dd" @change="handleDateRangeChange"
|
:defaultStartTime="defaultStartTime" :defaultEndTime="defaultEndTime" format="yyyy-MM-dd"
|
||||||
@quick-select="getSummaryList" />
|
@change="handleDateRangeChange" @quick-select="getList" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="operation-bar">
|
<div class="operation-bar">
|
||||||
@@ -11,98 +11,93 @@
|
|||||||
<el-button type="warning" plain icon="el-icon-download" @click="handleExport">导出</el-button>
|
<el-button type="warning" plain icon="el-icon-download" @click="handleExport">导出</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="summary-table-wrapper">
|
|
||||||
<el-table v-loading="loading" :data="summaryData" border stripe>
|
<el-table v-loading="loading" :data="summaryData" border stripe>
|
||||||
<el-table-column prop="employeeId" label="工号" width="100" />
|
<el-table-column prop="employeeName" label="姓名" fixed="left" />
|
||||||
<el-table-column prop="employeeName" label="姓名" width="100" />
|
<el-table-column label="理论工作小时数" width="140" align="center">
|
||||||
<el-table-column prop="departmentName" label="所属部门" width="120" />
|
<template slot-scope="scope">{{ scope.row.theoreticalHours.toFixed(1) }}</template>
|
||||||
|
|
||||||
<el-table-column label="工作时数" width="160">
|
|
||||||
<el-table-column prop="standardWorkHours" label="标准" width="80" />
|
|
||||||
<el-table-column prop="actualWorkHours" label="实际" width="80" />
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="实际工作小时数" width="140" align="center">
|
||||||
<el-table-column label="迟到" width="120">
|
<template slot-scope="scope">{{ scope.row.actualHours.toFixed(1) }}</template>
|
||||||
<el-table-column prop="lateCount" label="次数" width="60" />
|
|
||||||
<el-table-column prop="lateScore" label="分数" width="60" />
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="lateCount" label="迟到次数" width="100" align="center" />
|
||||||
<el-table-column label="早退" width="120">
|
<el-table-column prop="lateMinutes" label="迟到分钟数" width="100" align="center" />
|
||||||
<el-table-column prop="earlyCount" label="次数" width="60" />
|
<el-table-column prop="earlyCount" label="早退次数" width="100" align="center" />
|
||||||
<el-table-column prop="earlyScore" label="分数" width="60" />
|
<el-table-column prop="earlyMinutes" label="早退分钟数" width="100" align="center" />
|
||||||
|
<el-table-column label="加班小时数" width="110" align="center">
|
||||||
|
<template slot-scope="scope">{{ scope.row.overtimeHours.toFixed(1) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="theoreticalDays" label="理论出勤天数" width="120" align="center" />
|
||||||
<el-table-column label="加班时数" width="160">
|
<el-table-column prop="actualDays" label="实际出勤天数" width="120" align="center" />
|
||||||
<el-table-column prop="normalOvertime" label="正常" width="80" />
|
<el-table-column prop="businessTripDays" label="出差天数" width="100" align="center" />
|
||||||
<el-table-column prop="specialOvertime" label="特殊" width="80" />
|
<el-table-column prop="absentDays" label="旷工天数" width="100" align="center" />
|
||||||
</el-table-column>
|
<el-table-column prop="leaveDays" label="请假天数" width="100" align="center" />
|
||||||
|
|
||||||
<el-table-column prop="attendanceDays" label="出勤天数(标准/实际)" width="140" />
|
|
||||||
<el-table-column prop="businessTripDays" label="出差(天)" width="80" />
|
|
||||||
<el-table-column prop="absentDays" label="旷工(天)" width="80" />
|
|
||||||
<el-table-column prop="leaveDays" label="请假(天)" width="80" />
|
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="summary-footer">
|
|
||||||
<div class="summary-totals">
|
|
||||||
<span>总人数:{{ summaryData.length }}人</span>
|
|
||||||
<span>平均出勤:{{ averageAttendance }}天</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listAttendanceCheck } from "@/api/wms/attendanceCheck";
|
import { listAttendanceCheck } from "@/api/wms/attendanceCheck"
|
||||||
import { listLeaveRequest } from "@/api/wms/leaveRequest";
|
import { listLeaveRequest } from "@/api/wms/leaveRequest"
|
||||||
import { listOutRequest } from "@/api/wms/outRequest";
|
import { listOutRequest } from "@/api/wms/outRequest"
|
||||||
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker";
|
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker"
|
||||||
|
|
||||||
|
function timeToMinutes(timeStr) {
|
||||||
|
if (!timeStr) return 0
|
||||||
|
const parts = String(timeStr).split(':')
|
||||||
|
return parseInt(parts[0]) * 60 + parseInt(parts[1]) + (parseInt(parts[2] || 0) / 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcPeriodHours(start, end) {
|
||||||
|
if (!start || !end) return 0
|
||||||
|
const startMin = timeToMinutes(start)
|
||||||
|
const endMin = timeToMinutes(end)
|
||||||
|
if (endMin <= startMin) return 0
|
||||||
|
return (endMin - startMin) / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcActualHours(start, end) {
|
||||||
|
if (!start || !end) return 0
|
||||||
|
const s = new Date(String(start).replace(/-/g, '/'))
|
||||||
|
const e = new Date(String(end).replace(/-/g, '/'))
|
||||||
|
const diffMs = e - s
|
||||||
|
if (diffMs <= 0) return 0
|
||||||
|
return diffMs / (1000 * 60 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
function countTripDays(startTime, endTime) {
|
||||||
|
if (!startTime || !endTime) return 0
|
||||||
|
const s = new Date(String(startTime).split(' ')[0].replace(/-/g, '/'))
|
||||||
|
const e = new Date(String(endTime).split(' ')[0].replace(/-/g, '/'))
|
||||||
|
return Math.max(1, Math.floor((e - s) / (1000 * 60 * 60 * 24)) + 1)
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AttendanceSummary",
|
name: "AttendanceSummary",
|
||||||
components: {
|
components: { TimeRangePicker },
|
||||||
TimeRangePicker
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
summaryData: [],
|
summaryData: [],
|
||||||
dateRangeParams: {},
|
dateRangeParams: {},
|
||||||
defaultStartTime: '',
|
defaultStartTime: '',
|
||||||
defaultEndTime: '',
|
defaultEndTime: ''
|
||||||
leaveList: [],
|
|
||||||
outList: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
averageAttendance() {
|
|
||||||
if (this.summaryData.length === 0) return '0.00';
|
|
||||||
const total = this.summaryData.reduce((sum, item) => {
|
|
||||||
const days = item.attendanceDays?.split('/')[1] || '0';
|
|
||||||
return sum + parseFloat(days);
|
|
||||||
}, 0);
|
|
||||||
return (total / this.summaryData.length).toFixed(2);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initDateRange();
|
this.initDateRange()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initDateRange() {
|
initDateRange() {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1)
|
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0)
|
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0)
|
||||||
|
|
||||||
this.defaultStartTime = this.formatDate(firstDay)
|
this.defaultStartTime = this.formatDate(firstDay)
|
||||||
this.defaultEndTime = this.formatDate(lastDay)
|
this.defaultEndTime = this.formatDate(lastDay)
|
||||||
|
|
||||||
this.dateRangeParams = {
|
this.dateRangeParams = {
|
||||||
startDate: this.defaultStartTime,
|
startDate: this.defaultStartTime,
|
||||||
endDate: this.defaultEndTime
|
endDate: this.defaultEndTime
|
||||||
}
|
}
|
||||||
|
this.getList()
|
||||||
this.getSummaryList()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
formatDate(date) {
|
formatDate(date) {
|
||||||
@@ -114,206 +109,120 @@ export default {
|
|||||||
|
|
||||||
handleDateRangeChange() {
|
handleDateRangeChange() {
|
||||||
if (this.dateRangeParams.startDate && this.dateRangeParams.endDate) {
|
if (this.dateRangeParams.startDate && this.dateRangeParams.endDate) {
|
||||||
this.getSummaryList()
|
this.getList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getSummaryList() {
|
|
||||||
this.loading = true
|
|
||||||
Promise.all([
|
|
||||||
this.fetchAttendanceData(),
|
|
||||||
this.fetchLeaveData(),
|
|
||||||
this.fetchOutData()
|
|
||||||
]).then(([attendanceRows, leaveRows, outRows]) => {
|
|
||||||
this.summaryData = this.calculateSummary(attendanceRows, leaveRows, outRows)
|
|
||||||
this.loading = false
|
|
||||||
}).catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchAttendanceData() {
|
|
||||||
return listAttendanceCheck(this.dateRangeParams).then(response => {
|
|
||||||
return response.rows || []
|
|
||||||
}).catch(() => [])
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchLeaveData() {
|
|
||||||
const query = {
|
|
||||||
startTime: this.dateRangeParams.startDate,
|
|
||||||
endTime: this.dateRangeParams.endDate
|
|
||||||
}
|
|
||||||
return listLeaveRequest(query).then(response => {
|
|
||||||
return response.rows || response.data || []
|
|
||||||
}).catch(() => [])
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchOutData() {
|
|
||||||
const query = {
|
|
||||||
startTime: this.dateRangeParams.startDate,
|
|
||||||
endTime: this.dateRangeParams.endDate
|
|
||||||
}
|
|
||||||
return listOutRequest(query).then(response => {
|
|
||||||
return response.rows || response.data || []
|
|
||||||
}).catch(() => [])
|
|
||||||
},
|
|
||||||
|
|
||||||
calculateSummary(attendanceRows, leaveRows, outRows) {
|
|
||||||
const summaryMap = {}
|
|
||||||
const totalDays = this.calculateDaysInRange()
|
|
||||||
|
|
||||||
attendanceRows.forEach(record => {
|
|
||||||
const employeeId = record.userId
|
|
||||||
if (!summaryMap[employeeId]) {
|
|
||||||
summaryMap[employeeId] = {
|
|
||||||
employeeId: employeeId,
|
|
||||||
employeeName: record.employeeName,
|
|
||||||
departmentName: record.departmentName || '',
|
|
||||||
standardWorkHours: '0.00',
|
|
||||||
actualWorkHours: '0',
|
|
||||||
lateCount: 0,
|
|
||||||
lateScore: 0,
|
|
||||||
earlyCount: 0,
|
|
||||||
earlyScore: 0,
|
|
||||||
normalOvertime: '0',
|
|
||||||
specialOvertime: '0',
|
|
||||||
attendanceDays: 0,
|
|
||||||
businessTripDays: 0,
|
|
||||||
absentDays: 0,
|
|
||||||
leaveDays: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const summary = summaryMap[employeeId]
|
|
||||||
|
|
||||||
if (record.overallStatus === 'absent_full') {
|
|
||||||
summary.absentDays += 1
|
|
||||||
} else if (record.overallStatus === 'absent_half') {
|
|
||||||
summary.absentDays += 0.5
|
|
||||||
} else if (record.overallStatus !== 'absent_full' && record.overallStatus !== 'absent_half') {
|
|
||||||
summary.attendanceDays += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record.p1LateMinutes > 0 || record.p2LateMinutes > 0) {
|
|
||||||
summary.lateCount += 1
|
|
||||||
summary.lateScore += (record.p1LateMinutes || 0) + (record.p2LateMinutes || 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record.p1EarlyMinutes > 0 || record.p2EarlyMinutes > 0) {
|
|
||||||
summary.earlyCount += 1
|
|
||||||
summary.earlyScore += (record.p1EarlyMinutes || 0) + (record.p2EarlyMinutes || 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
let actualHours = 0
|
|
||||||
if (record.p1FirstCheck && record.p1LastCheck) {
|
|
||||||
const p1Hours = this.calculateHours(record.p1FirstCheck, record.p1LastCheck)
|
|
||||||
actualHours += p1Hours
|
|
||||||
}
|
|
||||||
if (record.p2FirstCheck && record.p2LastCheck) {
|
|
||||||
const p2Hours = this.calculateHours(record.p2FirstCheck, record.p2LastCheck)
|
|
||||||
actualHours += p2Hours
|
|
||||||
}
|
|
||||||
summary.actualWorkHours = this.formatHours(actualHours)
|
|
||||||
})
|
|
||||||
|
|
||||||
leaveRows.forEach(leave => {
|
|
||||||
const employeeId = leave.applicantId || leave.applicantName
|
|
||||||
if (!summaryMap[employeeId]) {
|
|
||||||
summaryMap[employeeId] = {
|
|
||||||
employeeId: employeeId,
|
|
||||||
employeeName: leave.applicantName,
|
|
||||||
departmentName: leave.applicantDeptName || '',
|
|
||||||
standardWorkHours: '0.00',
|
|
||||||
actualWorkHours: '0',
|
|
||||||
lateCount: 0,
|
|
||||||
lateScore: 0,
|
|
||||||
earlyCount: 0,
|
|
||||||
earlyScore: 0,
|
|
||||||
normalOvertime: '0',
|
|
||||||
specialOvertime: '0',
|
|
||||||
attendanceDays: 0,
|
|
||||||
businessTripDays: 0,
|
|
||||||
absentDays: 0,
|
|
||||||
leaveDays: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
summaryMap[employeeId].leaveDays += parseFloat(leave.leaveDays || 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
outRows.forEach(out => {
|
|
||||||
const employeeId = out.applicantId || out.applicantName
|
|
||||||
if (!summaryMap[employeeId]) {
|
|
||||||
summaryMap[employeeId] = {
|
|
||||||
employeeId: employeeId,
|
|
||||||
employeeName: out.applicantName,
|
|
||||||
departmentName: out.applicantDeptName || '',
|
|
||||||
standardWorkHours: '0.00',
|
|
||||||
actualWorkHours: '0',
|
|
||||||
lateCount: 0,
|
|
||||||
lateScore: 0,
|
|
||||||
earlyCount: 0,
|
|
||||||
earlyScore: 0,
|
|
||||||
normalOvertime: '0',
|
|
||||||
specialOvertime: '0',
|
|
||||||
attendanceDays: 0,
|
|
||||||
businessTripDays: 0,
|
|
||||||
absentDays: 0,
|
|
||||||
leaveDays: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
summaryMap[employeeId].businessTripDays += parseFloat(out.outHours || 0) / 8
|
|
||||||
})
|
|
||||||
|
|
||||||
const standardHours = totalDays * 9
|
|
||||||
Object.values(summaryMap).forEach(summary => {
|
|
||||||
summary.standardWorkHours = standardHours.toFixed(2)
|
|
||||||
summary.attendanceDays = `${totalDays}/${summary.attendanceDays.toFixed(1)}`
|
|
||||||
summary.businessTripDays = summary.businessTripDays.toFixed(1)
|
|
||||||
summary.absentDays = summary.absentDays.toFixed(1)
|
|
||||||
summary.leaveDays = summary.leaveDays.toFixed(1)
|
|
||||||
summary.lateCount = String(summary.lateCount)
|
|
||||||
summary.lateScore = String(summary.lateScore)
|
|
||||||
summary.earlyCount = String(summary.earlyCount)
|
|
||||||
summary.earlyScore = String(summary.earlyScore)
|
|
||||||
})
|
|
||||||
|
|
||||||
return Object.values(summaryMap)
|
|
||||||
},
|
|
||||||
|
|
||||||
calculateDaysInRange() {
|
|
||||||
const start = new Date(this.dateRangeParams.startDate)
|
|
||||||
const end = new Date(this.dateRangeParams.endDate)
|
|
||||||
const diffTime = Math.abs(end - start)
|
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1
|
|
||||||
return diffDays
|
|
||||||
},
|
|
||||||
|
|
||||||
calculateHours(startTime, endTime) {
|
|
||||||
try {
|
|
||||||
const start = new Date(`2000-01-01 ${startTime}`)
|
|
||||||
const end = new Date(`2000-01-01 ${endTime}`)
|
|
||||||
const diffMs = end - start
|
|
||||||
return diffMs / (1000 * 60 * 60)
|
|
||||||
} catch {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHours(hours) {
|
|
||||||
const h = Math.floor(hours)
|
|
||||||
const m = Math.round((hours - h) * 60)
|
|
||||||
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
|
||||||
},
|
|
||||||
|
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.getSummaryList()
|
this.getList()
|
||||||
|
},
|
||||||
|
|
||||||
|
getList() {
|
||||||
|
this.loading = true
|
||||||
|
const leaveQuery = {
|
||||||
|
startTime: this.dateRangeParams.startDate,
|
||||||
|
endTime: this.dateRangeParams.endDate,
|
||||||
|
pageSize: 9999
|
||||||
|
}
|
||||||
|
const outQuery = {
|
||||||
|
startTime: this.dateRangeParams.startDate,
|
||||||
|
endTime: this.dateRangeParams.endDate,
|
||||||
|
pageSize: 9999
|
||||||
|
}
|
||||||
|
Promise.all([
|
||||||
|
listAttendanceCheck(this.dateRangeParams),
|
||||||
|
listLeaveRequest(leaveQuery),
|
||||||
|
listOutRequest(outQuery)
|
||||||
|
]).then(([attendanceRes, leaveRes, outRes]) => {
|
||||||
|
const attendanceRows = attendanceRes.rows || []
|
||||||
|
const leaveRows = leaveRes.rows || []
|
||||||
|
const outRows = outRes.rows || []
|
||||||
|
this.calculateSummary(attendanceRows, leaveRows, outRows)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateSummary(attendanceRows, leaveRows, outRows) {
|
||||||
|
const empMap = {}
|
||||||
|
|
||||||
|
attendanceRows.forEach(record => {
|
||||||
|
const uid = record.userId
|
||||||
|
if (!empMap[uid]) {
|
||||||
|
empMap[uid] = {
|
||||||
|
employeeName: record.employeeName,
|
||||||
|
employeeId: uid,
|
||||||
|
theoreticalHours: 0,
|
||||||
|
actualHours: 0,
|
||||||
|
lateCount: 0,
|
||||||
|
lateMinutes: 0,
|
||||||
|
earlyCount: 0,
|
||||||
|
earlyMinutes: 0,
|
||||||
|
overtimeHours: 0,
|
||||||
|
theoreticalDays: 0,
|
||||||
|
actualDays: 0,
|
||||||
|
absentDays: 0,
|
||||||
|
businessTripDays: 0,
|
||||||
|
leaveDays: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const emp = empMap[uid]
|
||||||
|
|
||||||
|
emp.theoreticalDays++
|
||||||
|
|
||||||
|
const dayTheoretical = calcPeriodHours(record.p1StartTime, record.p1EndTime) + calcPeriodHours(record.p2StartTime, record.p2EndTime)
|
||||||
|
emp.theoreticalHours += dayTheoretical
|
||||||
|
|
||||||
|
const dayActual = calcActualHours(record.p1FirstCheck, record.p1LastCheck) + calcActualHours(record.p2FirstCheck, record.p2LastCheck)
|
||||||
|
emp.actualHours += dayActual
|
||||||
|
|
||||||
|
if (dayActual > 0) emp.actualDays++
|
||||||
|
|
||||||
|
const p1Late = parseInt(record.p1LateMinutes) || 0
|
||||||
|
const p2Late = parseInt(record.p2LateMinutes) || 0
|
||||||
|
if (p1Late > 0) { emp.lateCount++; emp.lateMinutes += p1Late }
|
||||||
|
if (p2Late > 0) { emp.lateCount++; emp.lateMinutes += p2Late }
|
||||||
|
|
||||||
|
const p1Early = parseInt(record.p1EarlyMinutes) || 0
|
||||||
|
const p2Early = parseInt(record.p2EarlyMinutes) || 0
|
||||||
|
if (p1Early > 0) { emp.earlyCount++; emp.earlyMinutes += p1Early }
|
||||||
|
if (p2Early > 0) { emp.earlyCount++; emp.earlyMinutes += p2Early }
|
||||||
|
|
||||||
|
if (dayActual > dayTheoretical) {
|
||||||
|
emp.overtimeHours += (dayActual - dayTheoretical)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.overallStatus === 'absent_full') {
|
||||||
|
emp.absentDays += 1
|
||||||
|
} else if (record.overallStatus === 'absent_half') {
|
||||||
|
emp.absentDays += 0.5
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
leaveRows.forEach(record => {
|
||||||
|
if (record.approvalStatus !== '已同意') return
|
||||||
|
const emp = Object.values(empMap).find(e => e.employeeName === record.applicantName)
|
||||||
|
if (emp) {
|
||||||
|
emp.leaveDays += parseFloat(record.leaveDays) || 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
outRows.forEach(record => {
|
||||||
|
if (record.approvalStatus !== '已同意' || record.outType !== '出差') return
|
||||||
|
const emp = Object.values(empMap).find(e => e.employeeName === record.applicantName)
|
||||||
|
if (emp) {
|
||||||
|
emp.businessTripDays += countTripDays(record.startTime, record.endTime)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.summaryData = Object.values(empMap)
|
||||||
},
|
},
|
||||||
|
|
||||||
handleExport() {
|
handleExport() {
|
||||||
this.download('wms/attendanceSummary/export', { ...this.dateRangeParams }, `attendanceSummary_${new Date().getTime()}.xlsx`)
|
this.download('wms/attendanceCheck/export', { ...this.dateRangeParams }, `attendanceSummary_${new Date().getTime()}.xlsx`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -328,27 +237,4 @@ export default {
|
|||||||
.operation-bar {
|
.operation-bar {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-table-wrapper {
|
|
||||||
overflow-x: auto;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary-footer {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 15px;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary-totals {
|
|
||||||
display: flex;
|
|
||||||
gap: 30px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary-totals span {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user