HRM 考勤管理补充功能 - 前端修改文档
概述
本次补充了考勤管理的核心功能:考勤规则管理、节假日配置、员工打卡、考勤统计汇总。
一、考勤规则管理
涉及页面
- 考勤规则列表页面(新增)
- 考勤规则新增/编辑弹窗(新增)
API
| 方法 |
路径 |
说明 |
| POST |
/hrm/attendance/rule/create |
新增考勤规则 |
| PUT |
/hrm/attendance/rule/update |
更新考勤规则 |
| DELETE |
/hrm/attendance/rule/delete |
删除考勤规则 |
| GET |
/hrm/attendance/rule/get |
获得考勤规则详情 |
| GET |
/hrm/attendance/rule/page |
获得考勤规则分页列表 |
| GET |
/hrm/attendance/rule/list |
获得考勤规则列表(启用状态) |
| GET |
/hrm/attendance/rule/export-excel |
导出考勤规则 Excel |
请求参数(create/update):
| 参数 |
类型 |
必填 |
说明 |
| id |
Long |
否 |
规则ID(修改时必填) |
| name |
String |
是 |
规则名称 |
| deptId |
Long |
否 |
适用部门ID(空表示全局) |
| workStartTime |
String |
是 |
上班时间,格式:HH:mm:ss |
| workEndTime |
String |
是 |
下班时间,格式:HH:mm:ss |
| lateAllowMinutes |
Integer |
否 |
迟到允许分钟数,默认 0 |
| earlyAllowMinutes |
Integer |
否 |
早退允许分钟数,默认 0 |
| overtimeThreshold |
BigDecimal |
否 |
加班起算时长(小时),默认 1 |
| status |
Integer |
否 |
状态:0-启用,1-禁用 |
| remark |
String |
否 |
备注 |
响应: { "code": 200, "data": 1, "msg": "操作成功" }
前端修改点
1. 路由配置
// src/router/modules/hrm.js
{
path: 'rule',
name: 'HrmAttendanceRule',
component: () => import('@/views/hrm/attendance/rule/index'),
meta: { title: '考勤规则', icon: 'setting' }
}
2. 列表页面表格
<el-table :data="list">
<el-table-column label="规则名称" prop="name" />
<el-table-column label="适用部门" prop="deptName">
<template #default="{ row }">
{{ row.deptName || '全局' }}
</template>
</el-table-column>
<el-table-column label="上班时间" prop="workStartTime" />
<el-table-column label="下班时间" prop="workEndTime" />
<el-table-column label="迟到允许(分钟)" prop="lateAllowMinutes" />
<el-table-column label="早退允许(分钟)" prop="earlyAllowMinutes" />
<el-table-column label="加班起算(小时)" prop="overtimeThreshold" />
<el-table-column label="状态" prop="status">
<template #default="{ row }">
<el-tag :type="row.status === 0 ? 'success' : 'danger'">
{{ row.status === 0 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="handleUpdate(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
3. 新增/编辑表单
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="规则名称" prop="name">
<el-input v-model="form.name" placeholder="请输入规则名称" />
</el-form-item>
<el-form-item label="适用部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
:data="deptList"
:props="{ label: 'name', value: 'id' }"
placeholder="请选择部门(不选表示全局)"
clearable
check-strictly
/>
</el-form-item>
<el-form-item label="上班时间" prop="workStartTime">
<el-time-picker v-model="form.workStartTime" value-format="HH:mm:ss" placeholder="选择上班时间" />
</el-form-item>
<el-form-item label="下班时间" prop="workEndTime">
<el-time-picker v-model="form.workEndTime" value-format="HH:mm:ss" placeholder="选择下班时间" />
</el-form-item>
<el-form-item label="迟到允许" prop="lateAllowMinutes">
<el-input-number v-model="form.lateAllowMinutes" :min="0" :max="60" placeholder="分钟" />
</el-form-item>
<el-form-item label="早退允许" prop="earlyAllowMinutes">
<el-input-number v-model="form.earlyAllowMinutes" :min="0" :max="60" placeholder="分钟" />
</el-form-item>
<el-form-item label="加班起算" prop="overtimeThreshold">
<el-input-number v-model="form.overtimeThreshold" :min="0" :precision="1" placeholder="小时" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio :value="0">启用</el-radio>
<el-radio :value="1">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
4. API 方法
// src/api/hrm/attendance.js
export function getAttendanceRulePage(params) {
return request({ url: '/hrm/attendance/rule/page', method: 'get', params })
}
export function getAttendanceRuleList() {
return request({ url: '/hrm/attendance/rule/list', method: 'get' })
}
export function createAttendanceRule(data) {
return request({ url: '/hrm/attendance/rule/create', method: 'post', data })
}
export function updateAttendanceRule(data) {
return request({ url: '/hrm/attendance/rule/update', method: 'put', data })
}
export function deleteAttendanceRule(id) {
return request({ url: '/hrm/attendance/rule/delete', method: 'delete', params: { id } })
}
二、节假日配置
涉及页面
- 节假日配置列表页面(新增)
- 节假日新增/编辑弹窗(新增)
API
| 方法 |
路径 |
说明 |
| POST |
/hrm/attendance/holiday/create |
新增节假日 |
| PUT |
/hrm/attendance/holiday/update |
更新节假日 |
| DELETE |
/hrm/attendance/holiday/delete |
删除节假日 |
| GET |
/hrm/attendance/holiday/get |
获得节假日详情 |
| GET |
/hrm/attendance/holiday/page |
获得节假日分页列表 |
| GET |
/hrm/attendance/holiday/export-excel |
导出节假日 Excel |
请求参数(create/update):
| 参数 |
类型 |
必填 |
说明 |
| id |
Long |
否 |
节假日ID(修改时必填) |
| name |
String |
是 |
节假日名称 |
| date |
String |
是 |
日期,格式:yyyy-MM-dd |
| type |
Integer |
是 |
类型:1-休息日,2-工作日 |
| isLegal |
Integer |
否 |
是否法定节假日:0-否,1-是 |
| remark |
String |
否 |
备注 |
前端修改点
1. 列表页面
<el-table :data="list">
<el-table-column label="节假日名称" prop="name" />
<el-table-column label="日期" prop="date" />
<el-table-column label="类型" prop="type">
<template #default="{ row }">
<el-tag :type="row.type === 1 ? 'warning' : 'success'">
{{ row.type === 1 ? '休息日' : '工作日' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="法定节假日" prop="isLegal">
<template #default="{ row }">
<el-tag v-if="row.isLegal === 1" type="danger">法定</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" />
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="handleUpdate(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
2. 表单
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="节假日名称" prop="name">
<el-input v-model="form.name" placeholder="如:春节、国庆节" />
</el-form-item>
<el-form-item label="日期" prop="date">
<el-date-picker v-model="form.date" value-format="YYYY-MM-DD" placeholder="选择日期" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-radio-group v-model="form.type">
<el-radio :value="1">休息日</el-radio>
<el-radio :value="2">工作日(调休)</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="法定节假日" prop="isLegal">
<el-checkbox v-model="form.isLegal" :true-value="1" :false-value="0">是法定节假日</el-checkbox>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" />
</el-form-item>
</el-form>
3. API 方法
export function getAttendanceHolidayPage(params) {
return request({ url: '/hrm/attendance/holiday/page', method: 'get', params })
}
export function createAttendanceHoliday(data) {
return request({ url: '/hrm/attendance/holiday/create', method: 'post', data })
}
export function updateAttendanceHoliday(data) {
return request({ url: '/hrm/attendance/holiday/update', method: 'put', data })
}
export function deleteAttendanceHoliday(id) {
return request({ url: '/hrm/attendance/holiday/delete', method: 'delete', params: { id } })
}
三、员工打卡
涉及页面
- 考勤记录列表页面(已有,扩展功能)
- 打卡按钮/组件(新增)
API
| 方法 |
路径 |
说明 |
| POST |
/hrm/attendance/record/clock-in |
上班打卡 |
| POST |
/hrm/attendance/record/clock-out |
下班打卡 |
请求参数:
| 参数 |
类型 |
必填 |
说明 |
| location |
String |
是 |
打卡地点 |
响应(打卡成功):
{
"code": 200,
"data": {
"id": 1,
"userId": 100,
"userName": "张三",
"deptId": 1,
"deptName": "研发部",
"date": "2024-01-15",
"clockInTime": "2024-01-15T09:05:00",
"clockInType": 1,
"clockOutTime": null,
"clockOutType": null,
"workHours": null,
"location": "公司前台"
}
}
前端修改点
1. 打卡按钮组件
<template>
<div class="clock-container">
<el-button type="primary" size="large" @click="handleClockIn">
<el-icon><Clock /></el-icon>
上班打卡
</el-button>
<el-button type="success" size="large" @click="handleClockOut">
<el-icon><Clock /></el-icon>
下班打卡
</el-button>
<div v-if="clockStatus" class="clock-status">
<p>今日打卡状态:</p>
<p>上班:{{ clockStatus.clockInTime || '未打卡' }}
<el-tag v-if="clockStatus.clockInType === 2" type="warning">迟到</el-tag>
</p>
<p>下班:{{ clockStatus.clockOutTime || '未打卡' }}
<el-tag v-if="clockStatus.clockOutType === 3" type="warning">早退</el-tag>
</p>
</div>
</div>
</template>
<script setup>
import { clockIn, clockOut } from '@/api/hrm/attendance'
import { ElMessage } from 'element-plus'
const location = ref('公司前台')
const handleClockIn = async () => {
try {
const res = await clockIn({ location: location.value })
ElMessage.success('上班打卡成功')
// 更新打卡状态显示
} catch (e) {
ElMessage.error(e.message || '打卡失败')
}
}
const handleClockOut = async () => {
try {
const res = await clockOut({ location: location.value })
ElMessage.success('下班打卡成功')
} catch (e) {
ElMessage.error(e.message || '打卡失败')
}
}
</script>
2. API 方法
export function clockIn(params) {
return request({ url: '/hrm/attendance/record/clock-in', method: 'post', params })
}
export function clockOut(params) {
return request({ url: '/hrm/attendance/record/clock-out', method: 'post', params })
}
四、考勤统计汇总
涉及页面
API
| 方法 |
路径 |
说明 |
| GET |
/hrm/attendance/summary/page |
获得考勤统计分页列表 |
| GET |
/hrm/attendance/summary/get |
获得员工月度汇总 |
| POST |
/hrm/attendance/summary/generate |
生成月度考勤汇总 |
| GET |
/hrm/attendance/summary/export-excel |
导出考勤统计 Excel |
请求参数(generate):
| 参数 |
类型 |
必填 |
说明 |
| yearMonth |
String |
是 |
年月,格式:yyyy-MM |
| deptId |
Long |
否 |
部门ID(指定部门时填写) |
| userId |
Long |
否 |
员工ID(指定员工时填写) |
前端修改点
1. 列表页面
<el-form :inline="true">
<el-form-item label="年月">
<el-date-picker v-model="queryParams.yearMonth" type="month" value-format="YYYY-MM" placeholder="选择年月" />
</el-form-item>
<el-form-item label="部门">
<el-tree-select v-model="queryParams.deptId" :data="deptList" clearable check-strictly placeholder="选择部门" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button type="success" @click="handleGenerate">生成统计</el-button>
<el-button type="warning" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
<el-table :data="list">
<el-table-column label="员工姓名" prop="userName" />
<el-table-column label="部门" prop="deptName" />
<el-table-column label="年月" prop="yearMonth" />
<el-table-column label="应出勤天数" prop="workDays" />
<el-table-column label="实际出勤天数" prop="actualDays" />
<el-table-column label="迟到次数" prop="lateCount">
<template #default="{ row }">
<el-tag v-if="row.lateCount > 0" type="warning">{{ row.lateCount }}</el-tag>
<span v-else>{{ row.lateCount }}</span>
</template>
</el-table-column>
<el-table-column label="早退次数" prop="earlyCount">
<template #default="{ row }">
<el-tag v-if="row.earlyCount > 0" type="warning">{{ row.earlyCount }}</el-tag>
<span v-else>{{ row.earlyCount }}</span>
</template>
</el-table-column>
<el-table-column label="缺卡次数" prop="absentCount">
<template #default="{ row }">
<el-tag v-if="row.absentCount > 0" type="danger">{{ row.absentCount }}</el-tag>
<span v-else>{{ row.absentCount }}</span>
</template>
</el-table-column>
<el-table-column label="加班时长(h)" prop="overtimeHours" />
<el-table-column label="请假时长(h)" prop="leaveHours" />
</el-table>
2. 生成统计弹窗
<el-dialog title="生成考勤统计" v-model="generateVisible">
<el-form :model="generateForm" label-width="100px">
<el-form-item label="年月">
<el-date-picker v-model="generateForm.yearMonth" type="month" value-format="YYYY-MM" placeholder="选择年月" />
</el-form-item>
<el-form-item label="生成范围">
<el-radio-group v-model="generateForm.rangeType">
<el-radio value="all">全公司</el-radio>
<el-radio value="dept">指定部门</el-radio>
<el-radio value="user">指定员工</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="generateForm.rangeType === 'dept'" label="部门">
<el-tree-select v-model="generateForm.deptId" :data="deptList" check-strictly placeholder="选择部门" />
</el-form-item>
<el-form-item v-if="generateForm.rangeType === 'user'" label="员工">
<el-select v-model="generateForm.userId" filterable placeholder="选择员工">
<el-option v-for="item in employeeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="generateVisible = false">取消</el-button>
<el-button type="primary" @click="handleGenerateSubmit">生成</el-button>
</template>
</el-dialog>
3. API 方法
export function getAttendanceSummaryPage(params) {
return request({ url: '/hrm/attendance/summary/page', method: 'get', params })
}
export function getMonthlySummary(params) {
return request({ url: '/hrm/attendance/summary/get', method: 'get', params })
}
export function generateSummary(params) {
return request({ url: '/hrm/attendance/summary/generate', method: 'post', params })
}
五、考勤异常申请提交
修改点
原提交接口 /hrm/attendance/exception/submit 需增加 processDefinitionKey 参数。
原接口:
js POST /hrm/attendance/exception/submit?id=xxx
新接口:
js POST /hrm/attendance/exception/submit?id=xxx&processDefinitionKey=xxx
前端修改
<!-- 提交弹窗增加流程选择 -->
<el-dialog title="提交补卡申请" v-model="submitVisible">
<el-form :model="submitForm" label-width="100px">
<el-form-item label="审批流程">
<el-select v-model="submitForm.processDefinitionKey" placeholder="选择审批流程">
<el-option v-for="item in processList" :key="item.key" :label="item.name" :value="item.key" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
// 提交方法
const handleSubmit = async () => {
await submitException({
id: currentId,
processDefinitionKey: submitForm.processDefinitionKey
})
ElMessage.success('提交成功')
submitVisible = false
getList()
}
六、字典数据配置
在系统字典管理中添加以下字典:
1. hrm_clock_type - 打卡类型
| 字典值 |
字典标签 |
| 1 |
正常 |
| 2 |
迟到 |
| 3 |
早退 |
| 4 |
缺卡 |
2. hrm_holiday_type - 节假日类型
3. hrm_is_legal_holiday - 是否法定节假日
4. hrm_data_source - 数据来源
| 字典值 |
字典标签 |
| 1 |
打卡 |
| 2 |
导入 |
| 3 |
补卡审批 |
七、菜单配置
在系统菜单中确认以下菜单已配置:
| 菜单ID |
菜单名称 |
路由路径 |
组件路径 |
| 6013 |
考勤规则 |
rule |
hrm/attendance/rule/index |
| 6014 |
节假日配置 |
holiday |
hrm/attendance/holiday/index |
| 6015 |
考勤统计 |
summary |
hrm/attendance/summary/index |
注意事项
- 打卡逻辑:打卡会自动判断是否工作日、迟到、早退,前端只需传入打卡地点即可
- 考勤规则优先级:部门专属规则优先,其次查找全局规则(不选部门)
- 节假日判断:有配置则按配置,无配置则按周末判断(周六周日为休息日)
- 补卡审批:审批通过后会自动更新考勤记录,无需手动操作
- 统计生成:建议每月结束后手动触发生成统计,或配置定时任务自动生成