feat(crm): 合同含税总金额自动填入订单总金额 & 移除冗余页面
- feat(crm/contract): 含税总额变化后自动填写订单总金额(可配置开关) - fix(crm/receive): 修复金额单位错误(万元→元);清理未使用导入 - fix(contract/product): 产品备注设置默认值 - chore: 移除已废弃的 OrderDashboard 组件和 finance/order 页面 - feat(wms/hrm): 新增考勤异常管理页面(attendanceAbnormal.vue) - chore: 移除 trae git 提交规则配置
This commit is contained in:
877
klp-ui/src/views/wms/hrm/attendance/attendanceAbnormal.vue
Normal file
877
klp-ui/src/views/wms/hrm/attendance/attendanceAbnormal.vue
Normal file
@@ -0,0 +1,877 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="date-range-section">
|
||||
<TimeRangePicker v-model="dateRangeParams" startKey="startDate" endKey="endDate"
|
||||
:defaultStartTime="defaultStartTime" :defaultEndTime="defaultEndTime" format="yyyy-MM-dd" @change="handleDateRangeChange"
|
||||
@quick-select="getList" />
|
||||
</div>
|
||||
|
||||
<div class="operation-bar">
|
||||
<el-button type="primary" plain icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="warning" plain icon="el-icon-download" @click="handleExport">导出</el-button>
|
||||
</div>
|
||||
|
||||
<div class="filter-row">
|
||||
<el-input v-model="queryParams.employeeName" placeholder="请输入员工姓名" clearable class="filter-input" @keyup.enter.native="handleQuery" />
|
||||
<!-- <el-select v-model="queryParams.overallStatus" placeholder="请选择状态" clearable @change="handleQuery" class="filter-select">
|
||||
<el-option label="迟到/早退/缺卡" value="abnormal" />
|
||||
<el-option label="半天旷工" value="absent_half" />
|
||||
<el-option label="全天旷工" value="absent_full" />
|
||||
</el-select> -->
|
||||
</div>
|
||||
|
||||
<div class="dept-filter-section" v-if="departmentList.length > 0">
|
||||
<el-radio-group v-model="selectedDept" @change="handleDeptChange">
|
||||
<el-radio-button label="">全部({{ totalEmployeeCount }}人)</el-radio-button>
|
||||
<el-radio-button v-for="item in departmentList" :key="item.deptName" :label="item.deptName">
|
||||
{{ item.deptName }}({{ item.count }}人)
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<el-alert type="info" title="提示:点击修改按钮可查看考勤详情并调整考勤结果"></el-alert>
|
||||
|
||||
<el-table v-loading="loading" :data="abnormalList" border stripe>
|
||||
<el-table-column label="员工姓名" align="center" prop="employeeName" width="100" />
|
||||
<el-table-column label="日期" align="center" prop="workDate" width="100" />
|
||||
<el-table-column label="总体状态" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span :class="getStatusClass(scope.row.overallStatus)">{{ getStatusText(scope.row.overallStatus) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时段一状态" align="center" prop="p1Status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.p1Status" :class="getStatusClass(scope.row.p1Status)">{{ getStatusText(scope.row.p1Status) }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="迟到(分)" align="center" prop="p1LateMinutes" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.p1LateMinutes > 0" class="late-info">{{ scope.row.p1LateMinutes }}</span>
|
||||
<span v-else>0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="早退(分)" align="center" prop="p1EarlyMinutes" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.p1EarlyMinutes > 0" class="early-info">{{ scope.row.p1EarlyMinutes }}</span>
|
||||
<span v-else>0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="扣款" align="center" width="80">
|
||||
<template slot-scope="scope">¥{{ scope.row.p1Deduct || '0.00' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时段二状态" align="center" prop="p2Status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.p2Status" :class="getStatusClass(scope.row.p2Status)">{{ getStatusText(scope.row.p2Status) }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="迟到(分)" align="center" prop="p2LateMinutes" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.p2LateMinutes > 0" class="late-info">{{ scope.row.p2LateMinutes }}</span>
|
||||
<span v-else>0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="早退(分)" align="center" prop="p2EarlyMinutes" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.p2EarlyMinutes > 0" class="early-info">{{ scope.row.p2EarlyMinutes }}</span>
|
||||
<span v-else>0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="扣款" align="center" width="80">
|
||||
<template slot-scope="scope">¥{{ scope.row.p2Deduct || '0.00' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="旷工类型" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.absentType === 'half_day'">半天旷工</span>
|
||||
<span v-else-if="scope.row.absentType === 'full_day'">全天旷工</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="连续旷工天数" align="center" prop="continuousAbsentDays" width="110" />
|
||||
<el-table-column label="总扣款" align="center" width="90">
|
||||
<template slot-scope="scope">
|
||||
<span class="deduct-amount">¥{{ scope.row.totalDeduct || '0.00' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="80" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="考勤详情" :visible.sync="detailDialogVisible" width="700px">
|
||||
<el-form v-if="currentDetail" :model="editForm" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="员工姓名">
|
||||
<span>{{ currentDetail.employeeName }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="日期">
|
||||
<span>{{ currentDetail.workDate || currentDetail.startDate }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="班次名称">
|
||||
<el-input v-if="isEdit" v-model="editForm.shiftName" placeholder="请输入班次名称" />
|
||||
<span v-else>{{ currentDetail.shiftName || '-' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="班次类型">
|
||||
<el-select v-if="isEdit" v-model="editForm.shiftType" placeholder="请选择班次类型">
|
||||
<el-option label="白班" value="白班" />
|
||||
<el-option label="夜班" value="夜班" />
|
||||
</el-select>
|
||||
<span v-else>{{ currentDetail.shiftType || '-' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="总体状态">
|
||||
<el-select v-if="isEdit" v-model="editForm.overallStatus" placeholder="请选择状态">
|
||||
<el-option label="正常" value="normal" />
|
||||
<el-option label="迟到/早退/缺卡" value="abnormal" />
|
||||
<el-option label="半天旷工" value="absent_half" />
|
||||
<el-option label="全天旷工" value="absent_full" />
|
||||
</el-select>
|
||||
<span v-else :class="getStatusClass(currentDetail.overallStatus)">{{ getStatusText(currentDetail.overallStatus) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="旷工类型">
|
||||
<el-select v-if="isEdit" v-model="editForm.absentType" placeholder="请选择旷工类型">
|
||||
<el-option label="半天旷工" value="half_day" />
|
||||
<el-option label="全天旷工" value="full_day" />
|
||||
</el-select>
|
||||
<span v-else>{{ currentDetail.absentType ? (currentDetail.absentType === 'half_day' ? '半天旷工' : '全天旷工') : '-' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="连续旷工天数">
|
||||
<el-input v-if="isEdit" v-model="editForm.continuousAbsentDays" type="number" placeholder="请输入天数" />
|
||||
<span v-else>{{ currentDetail.continuousAbsentDays || '0' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="总扣款金额">
|
||||
<el-input v-if="isEdit" v-model="editForm.totalDeduct" type="number" placeholder="请输入金额" />
|
||||
<span v-else class="deduct-amount">¥{{ currentDetail.totalDeduct || '0.00' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-if="isEdit" v-model="editForm.remark" placeholder="请输入备注" />
|
||||
<span v-else>{{ currentDetail.remark || '-' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left">时段一</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="理论上班">
|
||||
<el-time-picker style="width: 100%" v-if="isEdit" v-model="editForm.p1StartTime" value-format="HH:mm:ss" placeholder="选择时间" />
|
||||
<span v-else>{{ formatTime(currentDetail.p1StartTime) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="理论下班">
|
||||
<el-time-picker style="width: 100%" v-if="isEdit" v-model="editForm.p1EndTime" value-format="HH:mm:ss" placeholder="选择时间" />
|
||||
<span v-else>{{ formatTime(currentDetail.p1EndTime) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实际上班">
|
||||
<span>{{ formatTime(currentDetail.p1FirstCheck) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实际下班">
|
||||
<span>{{ formatTime(currentDetail.p1LastCheck) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-if="isEdit" v-model="editForm.p1Status" placeholder="请选择状态">
|
||||
<el-option label="正常" value="normal" />
|
||||
<el-option label="迟到预警" value="late_warn" />
|
||||
<el-option label="迟到I" value="late_one" />
|
||||
<el-option label="迟到II" value="late_two" />
|
||||
<el-option label="早退预警" value="early_warn" />
|
||||
<el-option label="早退I" value="early_one" />
|
||||
<el-option label="早退II" value="early_two" />
|
||||
<el-option label="半天旷工" value="absent_half" />
|
||||
<el-option label="未打卡" value="missed" />
|
||||
</el-select>
|
||||
<span v-else :class="getStatusClass(currentDetail.p1Status)">{{ getStatusText(currentDetail.p1Status) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="迟到分钟">
|
||||
<el-input v-if="isEdit" v-model="editForm.p1LateMinutes" type="number" placeholder="请输入分钟数" />
|
||||
<span v-else>{{ currentDetail.p1LateMinutes || '0' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="早退分钟">
|
||||
<el-input v-if="isEdit" v-model="editForm.p1EarlyMinutes" type="number" placeholder="请输入分钟数" />
|
||||
<span v-else>{{ currentDetail.p1EarlyMinutes || '0' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="扣款金额">
|
||||
<el-input v-if="isEdit" v-model="editForm.p1Deduct" type="number" placeholder="请输入金额" />
|
||||
<span v-else>¥{{ currentDetail.p1Deduct || '0.00' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left" v-if="currentDetail.p2StartTime || isEdit">时段二</el-divider>
|
||||
<el-row v-if="currentDetail.p2StartTime || isEdit">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="理论上班">
|
||||
<el-time-picker style="width: 100%" v-if="isEdit" v-model="editForm.p2StartTime" value-format="HH:mm:ss" placeholder="选择时间" />
|
||||
<span v-else>{{ formatTime(currentDetail.p2StartTime) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="理论下班">
|
||||
<el-time-picker style="width: 100%" v-if="isEdit" v-model="editForm.p2EndTime" value-format="HH:mm:ss" placeholder="选择时间" />
|
||||
<span v-else>{{ formatTime(currentDetail.p2EndTime) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实际上班">
|
||||
<span>{{ formatTime(currentDetail.p2FirstCheck) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实际下班">
|
||||
<span>{{ formatTime(currentDetail.p2LastCheck) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="currentDetail.p2StartTime || isEdit">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-if="isEdit" v-model="editForm.p2Status" placeholder="请选择状态">
|
||||
<el-option label="正常" value="normal" />
|
||||
<el-option label="迟到预警" value="late_warn" />
|
||||
<el-option label="迟到I" value="late_one" />
|
||||
<el-option label="迟到II" value="late_two" />
|
||||
<el-option label="早退预警" value="early_warn" />
|
||||
<el-option label="早退I" value="early_one" />
|
||||
<el-option label="早退II" value="early_two" />
|
||||
<el-option label="半天旷工" value="absent_half" />
|
||||
<el-option label="未打卡" value="missed" />
|
||||
</el-select>
|
||||
<span v-else :class="getStatusClass(currentDetail.p2Status)">{{ getStatusText(currentDetail.p2Status) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="迟到分钟">
|
||||
<el-input v-if="isEdit" v-model="editForm.p2LateMinutes" type="number" placeholder="请输入分钟数" />
|
||||
<span v-else>{{ currentDetail.p2LateMinutes || '0' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="早退分钟">
|
||||
<el-input v-if="isEdit" v-model="editForm.p2EarlyMinutes" type="number" placeholder="请输入分钟数" />
|
||||
<span v-else>{{ currentDetail.p2EarlyMinutes || '0' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="扣款金额">
|
||||
<el-input v-if="isEdit" v-model="editForm.p2Deduct" type="number" placeholder="请输入金额" />
|
||||
<span v-else>¥{{ currentDetail.p2Deduct || '0.00' }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">请假记录</el-divider>
|
||||
<div class="dialog-filter-bar">
|
||||
<el-input v-model="detailLeaveQuery.applicantName" placeholder="请假人姓名" class="dialog-filter-input" />
|
||||
<el-button type="primary" plain size="small" icon="el-icon-search" @click="getDetailLeaveList">搜索</el-button>
|
||||
</div>
|
||||
<el-table v-loading="detailLeaveLoading" :data="detailLeaveList" border stripe size="small">
|
||||
<el-table-column prop="leaveType" label="请假类型" width="100" />
|
||||
<el-table-column prop="startTime" label="开始时间" width="150" />
|
||||
<el-table-column prop="endTime" label="结束时间" width="150" />
|
||||
<el-table-column prop="leaveDays" label="小时" width="80" />
|
||||
<el-table-column prop="leaveReason" label="原因" />
|
||||
<el-table-column prop="approvalStatus" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span :class="getApprovalStatusClass(scope.row.approvalStatus)">{{ getApprovalStatusText(scope.row.approvalStatus) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-divider content-position="left">外出记录</el-divider>
|
||||
<div class="dialog-filter-bar">
|
||||
<el-input v-model="detailOutQuery.applicantName" placeholder="外出人姓名" class="dialog-filter-input" />
|
||||
<el-button type="primary" plain size="small" icon="el-icon-search" @click="getDetailOutList">搜索</el-button>
|
||||
</div>
|
||||
<el-table v-loading="detailOutLoading" :data="detailOutList" border stripe size="small">
|
||||
<el-table-column prop="outType" label="外出类型" width="100" />
|
||||
<el-table-column prop="startTime" label="开始时间" width="150" />
|
||||
<el-table-column prop="endTime" label="结束时间" width="150" />
|
||||
<el-table-column prop="outHours" label="时长(小时)" width="100" />
|
||||
<el-table-column prop="outPlace" label="地点" />
|
||||
<el-table-column prop="approvalStatus" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span :class="getApprovalStatusClass(scope.row.approvalStatus)">{{ getApprovalStatusText(scope.row.approvalStatus) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-divider content-position="left">打卡记录</el-divider>
|
||||
<el-table v-loading="detailRecordsLoading" :data="detailRecordsList" border stripe size="small">
|
||||
<el-table-column prop="ename" label="姓名" width="100" />
|
||||
<el-table-column prop="deptname" label="部门" />
|
||||
<el-table-column prop="checktime" label="打卡时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.checktime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button v-if="!isEdit" type="primary" @click="handleEdit">编辑</el-button>
|
||||
<el-button v-if="isEdit" @click="cancelEdit">取消</el-button>
|
||||
<el-button v-if="isEdit" type="primary" @click="submitEdit">保存</el-button>
|
||||
<el-button @click="closeDetail">关闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listAttendanceCheck, getAttendanceCheck, updateAttendanceCheck } from "@/api/wms/attendanceCheck";
|
||||
import { listOutRequest } from "@/api/wms/outRequest";
|
||||
import { listLeaveRequest } from "@/api/wms/leaveRequest";
|
||||
import { listRecords } from "@/api/wms/attendance";
|
||||
import { listEmployeeInfo } from "@/api/wms/employeeInfo";
|
||||
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker";
|
||||
|
||||
export default {
|
||||
name: "AttendanceAbnormal",
|
||||
components: {
|
||||
TimeRangePicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
abnormalList: [],
|
||||
dateRangeParams: {},
|
||||
defaultStartTime: '',
|
||||
defaultEndTime: '',
|
||||
queryParams: {
|
||||
employeeName: undefined,
|
||||
startDate: undefined,
|
||||
endDate: undefined,
|
||||
overallStatus: undefined,
|
||||
abnormal: true
|
||||
},
|
||||
allEmployees: [],
|
||||
departmentList: [],
|
||||
selectedDept: '',
|
||||
currentDeptEmployeeIds: '',
|
||||
detailDialogVisible: false,
|
||||
currentDetail: null,
|
||||
isEdit: false,
|
||||
editForm: {},
|
||||
detailLeaveLoading: false,
|
||||
detailLeaveList: [],
|
||||
detailLeaveQuery: {
|
||||
applicantName: ''
|
||||
},
|
||||
detailOutLoading: false,
|
||||
detailOutList: [],
|
||||
detailOutQuery: {
|
||||
applicantName: ''
|
||||
},
|
||||
detailRecordsLoading: false,
|
||||
detailRecordsList: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getAllEmployees().then(() => {
|
||||
this.initDateRange();
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
totalEmployeeCount() {
|
||||
return this.allEmployees.length;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDateRange() {
|
||||
const now = new Date()
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0)
|
||||
|
||||
this.defaultStartTime = this.formatDate(firstDay)
|
||||
this.defaultEndTime = this.formatDate(lastDay)
|
||||
|
||||
this.dateRangeParams = {
|
||||
startDate: this.defaultStartTime,
|
||||
endDate: this.defaultEndTime
|
||||
}
|
||||
|
||||
this.queryParams.startDate = this.defaultStartTime
|
||||
this.queryParams.endDate = this.defaultEndTime
|
||||
this.getList()
|
||||
},
|
||||
getAllEmployees() {
|
||||
return listEmployeeInfo({
|
||||
pageNum: 1,
|
||||
pageSize: 10000
|
||||
}).then(res => {
|
||||
this.allEmployees = res.rows || []
|
||||
const deptMap = {}
|
||||
this.allEmployees.forEach(emp => {
|
||||
const deptName = emp.dept || '未分配部门'
|
||||
if (!deptMap[deptName]) {
|
||||
deptMap[deptName] = new Set()
|
||||
}
|
||||
deptMap[deptName].add(String(emp.infoId))
|
||||
})
|
||||
this.departmentList = Object.keys(deptMap).map(deptName => {
|
||||
const ids = [...deptMap[deptName]]
|
||||
return {
|
||||
deptName,
|
||||
count: ids.length,
|
||||
empIds: ids.join(',')
|
||||
}
|
||||
})
|
||||
if (this.departmentList.length > 0) {
|
||||
this.selectedDept = ''
|
||||
this.currentDeptEmployeeIds = ''
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDeptChange(deptName) {
|
||||
if (!deptName) {
|
||||
this.currentDeptEmployeeIds = ''
|
||||
} else {
|
||||
const dept = this.departmentList.find(d => d.deptName === deptName)
|
||||
this.currentDeptEmployeeIds = dept ? dept.empIds : ''
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
formatDate(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
},
|
||||
handleDateRangeChange() {
|
||||
if (this.dateRangeParams.startDate && this.dateRangeParams.endDate) {
|
||||
this.queryParams.startDate = this.dateRangeParams.startDate
|
||||
this.queryParams.endDate = this.dateRangeParams.endDate
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
getList() {
|
||||
this.loading = true;
|
||||
const params = { ...this.queryParams };
|
||||
if (this.currentDeptEmployeeIds) {
|
||||
params.userIds = this.currentDeptEmployeeIds.split(',');
|
||||
}
|
||||
listAttendanceCheck(params).then(response => {
|
||||
this.abnormalList = response.rows || [];
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.getList();
|
||||
},
|
||||
handleExport() {
|
||||
const params = { ...this.queryParams, abnormal: true };
|
||||
if (this.currentDeptEmployeeIds) {
|
||||
params.userIds = this.currentDeptEmployeeIds.split(',');
|
||||
}
|
||||
this.download('wms/attendanceCheck/export', params, `attendanceAbnormal_${new Date().getTime()}.xlsx`);
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.loading = true;
|
||||
getAttendanceCheck(row.checkId).then(response => {
|
||||
this.currentDetail = response.data;
|
||||
this.detailLeaveQuery.applicantName = row.employeeName;
|
||||
this.detailOutQuery.applicantName = row.employeeName;
|
||||
this.getDetailLeaveList();
|
||||
this.getDetailOutList();
|
||||
this.getDetailRecords(row.employeeName, row.startDate);
|
||||
this.detailDialogVisible = true;
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
closeDetail() {
|
||||
this.detailDialogVisible = false;
|
||||
this.currentDetail = null;
|
||||
this.isEdit = false;
|
||||
},
|
||||
handleEdit() {
|
||||
this.isEdit = true;
|
||||
this.editForm = { ...this.currentDetail };
|
||||
this.initEditForm();
|
||||
},
|
||||
initEditForm() {
|
||||
if (this.currentDetail.p1StartTime) {
|
||||
this.editForm.p1StartTime = this.convertToTimeString(this.currentDetail.p1StartTime);
|
||||
}
|
||||
if (this.currentDetail.p1EndTime) {
|
||||
this.editForm.p1EndTime = this.convertToTimeString(this.currentDetail.p1EndTime);
|
||||
}
|
||||
if (this.currentDetail.p1FirstCheck) {
|
||||
this.editForm.p1FirstCheck = this.convertToDateTimeString(this.currentDetail.p1FirstCheck);
|
||||
}
|
||||
if (this.currentDetail.p1LastCheck) {
|
||||
this.editForm.p1LastCheck = this.convertToDateTimeString(this.currentDetail.p1LastCheck);
|
||||
}
|
||||
if (this.currentDetail.p2StartTime) {
|
||||
this.editForm.p2StartTime = this.convertToTimeString(this.currentDetail.p2StartTime);
|
||||
}
|
||||
if (this.currentDetail.p2EndTime) {
|
||||
this.editForm.p2EndTime = this.convertToTimeString(this.currentDetail.p2EndTime);
|
||||
}
|
||||
if (this.currentDetail.p2FirstCheck) {
|
||||
this.editForm.p2FirstCheck = this.convertToDateTimeString(this.currentDetail.p2FirstCheck);
|
||||
}
|
||||
if (this.currentDetail.p2LastCheck) {
|
||||
this.editForm.p2LastCheck = this.convertToDateTimeString(this.currentDetail.p2LastCheck);
|
||||
}
|
||||
},
|
||||
convertToTimeString(dateStr) {
|
||||
if (!dateStr) return ''
|
||||
if (dateStr instanceof Date) {
|
||||
return dateStr.toTimeString().slice(0, 8)
|
||||
}
|
||||
const str = String(dateStr)
|
||||
if (str.length >= 8) {
|
||||
const parts = str.split(/[-T:\s]/)
|
||||
if (parts.length >= 6) {
|
||||
return `${parts[3]}:${parts[4]}:${parts[5]}`
|
||||
} else if (str.includes(':')) {
|
||||
const timePart = str.split(' ').pop()
|
||||
return timePart.length >= 8 ? timePart : timePart.padEnd(8, ':00')
|
||||
}
|
||||
}
|
||||
return str
|
||||
},
|
||||
convertToDateTimeString(dateStr) {
|
||||
if (!dateStr) return ''
|
||||
if (dateStr instanceof Date) {
|
||||
const d = dateStr
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hours = String(d.getHours()).padStart(2, '0')
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
const str = String(dateStr)
|
||||
if (str.includes('T')) {
|
||||
return str.replace('T', ' ')
|
||||
}
|
||||
return str
|
||||
},
|
||||
cancelEdit() {
|
||||
this.isEdit = false;
|
||||
this.editForm = {};
|
||||
},
|
||||
submitEdit() {
|
||||
this.loading = true;
|
||||
updateAttendanceCheck(this.editForm).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.isEdit = false;
|
||||
this.editForm = {};
|
||||
this.getList();
|
||||
this.closeDetail();
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
formatTime(time) {
|
||||
if (!time) return '-'
|
||||
return time
|
||||
},
|
||||
getDetailLeaveList() {
|
||||
this.detailLeaveLoading = true;
|
||||
const query = {
|
||||
applicantName: this.detailLeaveQuery.applicantName,
|
||||
startTime: this.queryParams.startDate,
|
||||
endTime: this.queryParams.endDate
|
||||
};
|
||||
listLeaveRequest(query).then(response => {
|
||||
this.detailLeaveList = response.rows || response.data || [];
|
||||
this.detailLeaveLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLeaveLoading = false;
|
||||
});
|
||||
},
|
||||
getDetailOutList() {
|
||||
this.detailOutLoading = true;
|
||||
const query = {
|
||||
applicantName: this.detailOutQuery.applicantName,
|
||||
startTime: this.queryParams.startDate,
|
||||
endTime: this.queryParams.endDate
|
||||
};
|
||||
listOutRequest(query).then(response => {
|
||||
this.detailOutList = response.rows || response.data || [];
|
||||
this.detailOutLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailOutLoading = false;
|
||||
});
|
||||
},
|
||||
getDetailRecords(ename, date) {
|
||||
this.detailRecordsLoading = true;
|
||||
let dateStr = '';
|
||||
if (date) {
|
||||
if (date instanceof Date) {
|
||||
const y = date.getFullYear();
|
||||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(date.getDate()).padStart(2, '0');
|
||||
dateStr = `${y}-${m}-${d}`;
|
||||
} else if (typeof date === 'string') {
|
||||
dateStr = date.split(' ')[0];
|
||||
}
|
||||
}
|
||||
if (!dateStr) {
|
||||
dateStr = this.queryParams.startDate || '';
|
||||
}
|
||||
if (!dateStr) {
|
||||
this.detailRecordsLoading = false;
|
||||
return;
|
||||
}
|
||||
listRecords({
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
ename,
|
||||
checktimeStart: dateStr + ' 00:00:00',
|
||||
checktimeEnd: dateStr + ' 23:59:59'
|
||||
}).then(response => {
|
||||
this.detailRecordsList = response.rows || [];
|
||||
this.detailRecordsLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailRecordsLoading = false;
|
||||
});
|
||||
},
|
||||
getApprovalStatusClass(status) {
|
||||
switch (status) {
|
||||
case '待审批': return 'approval-pending'
|
||||
case '已同意': return 'approval-approved'
|
||||
case '已驳回': return 'approval-rejected'
|
||||
case '已撤销': return 'approval-canceled'
|
||||
default: return 'approval-default'
|
||||
}
|
||||
},
|
||||
getApprovalStatusText(status) {
|
||||
switch (status) {
|
||||
case '待审批': return '待审批'
|
||||
case '已同意': return '已同意'
|
||||
case '已驳回': return '已驳回'
|
||||
case '已撤销': return '已撤销'
|
||||
default: return status || '-'
|
||||
}
|
||||
},
|
||||
getStatusClass(status) {
|
||||
switch (status) {
|
||||
case 'normal': return 'status-normal'
|
||||
case 'late_warn':
|
||||
case 'late_one':
|
||||
case 'late_two': return 'status-late'
|
||||
case 'early_warn':
|
||||
case 'early_one':
|
||||
case 'early_two': return 'status-early'
|
||||
case 'absent_half': return 'status-absent-half'
|
||||
case 'absent_full': return 'status-absent-full'
|
||||
case 'abnormal': return 'status-abnormal'
|
||||
case 'missed': return 'status-missed'
|
||||
case 'missed_start': return 'status-missed'
|
||||
case 'missed_end': return 'status-missed'
|
||||
default: return 'status-default'
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
switch (status) {
|
||||
case 'normal': return '正常'
|
||||
case 'late_warn': return '迟到预警'
|
||||
case 'late_one': return '迟到I'
|
||||
case 'late_two': return '迟到II'
|
||||
case 'early_warn': return '早退预警'
|
||||
case 'early_one': return '早退I'
|
||||
case 'early_two': return '早退II'
|
||||
case 'absent_half': return '半天旷工'
|
||||
case 'absent_full': return '全天旷工'
|
||||
case 'abnormal': return '迟到/早退/缺卡'
|
||||
case 'missed': return '未打卡'
|
||||
case 'missed_start': return '上班漏打卡'
|
||||
case 'missed_end': return '下班漏打卡'
|
||||
default: return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.date-range-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.operation-bar {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.dept-filter-section {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.late-info {
|
||||
color: #e6a23c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.early-info {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.deduct-amount {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-normal {
|
||||
background-color: #67c23a;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-late {
|
||||
background-color: #e6a23c;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-early {
|
||||
background-color: #f56c6c;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-absent-half {
|
||||
background-color: #909399;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-absent-full {
|
||||
background-color: #606266;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-abnormal {
|
||||
background-color: #f56c6c;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-missed {
|
||||
background-color: #e6a23c;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-default {
|
||||
background-color: #c0c4cc;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dialog-filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dialog-filter-input {
|
||||
width: 150px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.approval-pending {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.approval-approved {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.approval-rejected {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.approval-canceled {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.approval-default {
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user