大数跨境

SpringBoot3+Vue3+UniApp 假勤配置:部门班次、WiFi/GPS 与请假方案怎么选

SpringBoot3+Vue3+UniApp 假勤配置:部门班次、WiFi/GPS 与请假方案怎么选 企业软件源码
2026-09-10
8
导读:各公司部门考勤规则不同,不要拆成三张配置页。RuoYi Office 把班次、WiFi 白名单、GPS 围栏、节假日和补卡收在假勤配置一页:本级有行就覆盖,没有就沿部门树继承。UniApp 打卡只消费

SpringBoot3+Vue3+UniApp 假勤配置:部门班次、WiFi/GPS 与请假方案怎么选

🌐 文档地址:https://ruoyioffice.com
 📦 源码1·GitHub:https://github.com/yuqing2026/ruoyi-office
 📦 源码2·GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office
 📦 源码3·Gitee:https://gitee.com/yqzy1688/ruoyi-office
 💬 微信:17156169080(备注「RuoYi Office」)

总部 9 点,门店 8 点半;办公室要连指定 WiFi,工地只认围栏。规则若拆成「班次页 / 定位页 / 假期页」,实施对不上,手机打卡各吃各的。RuoYi Office 把假勤收成一页:按部门保存,没有本级就继承父级。UniApp 打卡不读第二套配置。

▲ 部门班次继承之后,GPS 围栏和 WiFi 白名单一起拦打卡;手机只显示解析结果


引言:假勤难在「谁用哪套规则」

打卡、请假、节假日经常被做成三个模块三个默认值。员工在手机上迟到,HR 在 PC 上说「我们配的是 9:30」。对不上的原因通常不是公式,而是 配置入口散了,继承关系没人解释

现场
配错之后
只给公司配一份班次
门店早班全员迟到
WiFi 和围栏各做一页
内网模式仍去要定位权限
假期方案不挂部门
子公司法定假和总部错一天
手机自己写死 9:00
PC 改了班次 App 不跟

本文只讲配置中枢:在哪一页选、本级还是继承、手机吃哪些字段。 围栏怎么画圆见天地图专文;补卡回写见补卡专文;请假额度见余额账户文。不重复那些算法。

技术栈锚点:Spring Boot 3.5 + Vue3 Vben + UniApp。PC 配,App 打。


一、一页中枢:模块表先看清

菜单:人力资源 → 假勤管理 → 假勤配置,路由 /hrm/attendance/attendance-config。左部门树,右一张大表。保存写 hrm_attendance_config,按 deptId 一行。

分区
字段
给谁用
班次
timeRules、大小周、工作日
迟到早退、是否工作日
打卡方式
punchMode、wifiList、经纬半径、外勤
UniApp / PC 打卡门禁
节假日
holidayPlanId
和班次一起决定今天打不打
考核与扣款
迟到/早退/缺卡次数与金额
算薪取数,不是打卡按钮
补卡
允许与否、月次数、截止类型
补卡申请提交前校验

不要把「请假类型」理解成另一套部门继承。假期类型是全局字典;部门挂的是节假日方案 + 班次。配置页下半截也会提示「假期规则继承自上级」,和考勤规则同一套 inherited 黄条。


▲ /hrm/attendance/attendance-config。选中有本级配置的部门,班次和时间规则可改。这是主效果图


二、继承怎么选:有行覆盖,没行往上走

AttendanceConfigServiceImpl.getAttendanceConfigByDeptId 先查本级。命中则 inherited=false。没有行就取部门 parentId 递归。到顶级还没有,返回 null,打卡页 hasConfig=false

public AttendanceConfigRespVO getAttendanceConfigByDeptId(Long deptId) {    AttendanceConfigDO config = attendanceConfigMapper.selectByDeptId(deptId);    if (config != null) {        AttendanceConfigRespVO respVO = BeanUtils.toBean(config, AttendanceConfigRespVO.class);        respVO.setTimeRules(BeanUtils.toBean(config.getTimeRules(), AttendanceTimeRuleVO.class));        respVO.setWifiList(BeanUtils.toBean(config.getWifiList(), AttendanceWifiItemVO.class));        respVO.setInherited(false);        return respVO;    }    DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData();    if (dept == null || dept.getParentId() == null || dept.getParentId() == 0) {        return null;    }    AttendanceConfigRespVO parentConfig = getAttendanceConfigByDeptId(dept.getParentId());    if (parentConfig != null) {        parentConfig.setInherited(true);    }    return parentConfig;}

前端 loadConfiginherited=true 时黄条「当前部门未设置,以下继承自上级。改完点保存即变成本级」。保存后 hasConfig=true,黄条消失。删除本级行,下次加载重新继承——确认框写的就是这句。

你想要的结果
怎么配
全公司同一套
只在公司节点保存,子部门不要保存
门店早半小时
只给门店部门保存一份,其余继承
试点部门先试弹性
试点部门单独 timeRules,删行即可回退
某组不考核
该组 needCheck=0 的本级行,不要改公司行

保存是整行覆盖,没有「只改 WiFi、班次继续继承」的字段级继承。需要半套不同,就复制父级后再改。这是有意做简单:实施讲得清,排障只查有没有本级行。


三、班次:timeRules 比两个字符串更靠谱

表上仍留着 workStartTime / workEndTime 当回退。真正生效的是 JSON timeRules:季节(全年/夏/冬)、精确或弹性、弹性窗、最少工时。前端保存前 ensureTimeRules,精确班次还会把当前规则写回那两个回退字段。

weekMode=1 才走大小周:bigWeekParity 奇数周或偶数周,bigWeekDays / smallWeekDays 各写一份。固定工作日只用 workDays

打卡时不把整份 timeRules 丢给手机算。AttendancePunchController.getPunchInfo 调 attendanceRuleService.resolveDayRule(config, today),只下发 今天 的工作日、上下班、弹性窗。手机只展示和判断,迟到早退以后端 calcCheckInStatus / calcCheckOutStatus 为准,不采信前端 statusFlag


四、打卡方式:位置、内网、外勤怎么选

punchMode1 位置打卡,2 内网打卡。不要理解成「PC 一种、App 一种」——两端读同一字段。

位置打卡。 配经纬度和 punchRadius。UniApp 用当前距离比半径,isInRange 为假则不能普通打卡。外勤另开 allowFieldPunch,状态记 2,不走围栏。

内网打卡。wifiList 是 {ssid, bssid} 数组。SSID 给人看,匹配只认 BSSID。名单为空:兼容旧逻辑,能进系统就能打。名单非空:必须提交 BSSID,且归一化后命中任一项。PC 浏览器、H5 常常拿不到 BSSID,会被 ATTENDANCE_PUNCH_WIFI_REQUIRED 拦住——这是门禁,不是 bug。外勤打卡跳过 WiFi 校验。

private void validatePunchWifi(AttendanceConfigRespVO config, String bssid) {    if (config == null || config.getPunchMode() == null || config.getPunchMode() != 2) {        return;    }    if (CollUtil.isEmpty(config.getWifiList())) {        return;    }    if (StrUtil.isBlank(bssid)) {        throw exception(ATTENDANCE_PUNCH_WIFI_REQUIRED);    }    String current = normalizeBssid(bssid);    boolean matched = config.getWifiList().stream()            .map(item -> normalizeBssid(item.getBssid()))            .anyMatch(white -> StrUtil.isNotBlank(white) && white.equals(current));    if (!matched) {        throw exception(ATTENDANCE_PUNCH_WIFI_NOT_MATCH);    }}

归一化:去空格、小写、- 改 :。路由器后台抄 MAC 时格式不统一,不要让实施手工对齐冒号。

前端保存会丢掉没填 BSSID 的行。只写「办公室 WiFi」不写物理地址,等于没配白名单。

▲ 研发部门本级行:定位打卡、500 米半径、大小周、2026 法定假、08:00–17:00。演示数据是位置模式,所以露出经纬而不是 WiFi 表

选法对照:

场景
punchMode
还要开什么
不要开什么
写字楼固定工位
2 内网
路由器 BSSID 列表
不要指望 H5 过白名单
工地 / 门店
1 位置
半径 + 允许外勤(巡店)
不要再配一份 WiFi 充数
混合:办公室 + 出差
1 或 2
外勤=1
不要给每个出差地建部门配置

五、节假日和补卡为什么挂在同一页

holidayPlanId 指向节假日方案。方案本身在弹窗里维护(调休、法定假),配置页只选「本部门用哪一套」。班次说「周一到周五要打」,方案说「十月一日不用打」。resolveDayRule 两边都看。方案配错页,手机只看见「今天不是工作日」,HR 以为班次坏了。

补卡:allowCardReplacement、每月次数、截止类型(当天/本周/本月/自定义天数)。提交补卡单时读的是 员工部门解析后的那一行,不是请假类型上的开关。关补卡却在请假里开「事假抵缺卡」,对不上账。

考核开关 needCheck:不考核的部门仍可能要打卡记出勤,但不进扣款。和「没有配置」不同——没有配置是 hasConfig=false,按钮直接不可用。


六、UniApp:只消费解析结果

移动端 pages-hrm/attendance/punch.vuegetPunchInfo 回来后:

  • hasConfig===false
    :提示无考勤规则,不能打。
  • punchMode===2
    :不比距离,看 wifiRequired 和 wifiList 的 SSID 提示。
  • 否则比 punchRadius
  • 展示的上班时间来自当日 workStartTime,不是写死 9:00。
const isIntranetMode = computed(() => punchInfo.value.punchMode === 2)const wifiRequired = computed(() => punchInfo.value.wifiRequired === true)const isInRange = computed(() => {  if (!punchInfo.value.hasConfig) return false  if (isIntranetMode.value) return true  if (distance.value === nullreturn false  return distance.value <= (punchInfo.value.punchRadius || 200)})

executePunch 带 punchType、加班、外勤。迟到早退以后端规则引擎为准。PC 打卡页走同一套 /info 和 /do。改配置不用发 App 版本,只要员工重新进入打卡页。


▲ 390 宽竖屏。顶栏是班次时间,中部是范围或 WiFi 提示。规则来自 PC 假勤配置,不是小程序本地缓存


七、和相邻页怎么分工

路由
和本页的边界
假勤配置
/hrm/attendance/attendance-config
唯一写入 timeRules / punchMode / 方案 id
打卡记录
/hrm/attendance/punch-record
只读结果,最多留一张列表对照
假期类型
/hrm/attendance/leave-type
假种额度,不写班次
节假日方案弹窗
配置页内
日历例外,被 holidayPlanId 引用
UniApp 打卡
/pages-hrm/attendance/punch
只读解析结果

打卡记录用来印证「配完能打」,不要再截第二张表。围栏画圆去天地图文;本页只要半径和经纬字段看得到。


八、保存时的两个实施坑

1. 逻辑删除后主键冲突。 本级曾经配过又删掉,再保存必须 config.setId(null) 再 insert。代码已经清 id。二次开发不要把前端带回的旧 id 硬塞回去。

2. 继承黄条下直接保存。 黄条数据是父级内容。点保存等于 复制一份到本级。门店只想改半径、班次仍跟总部,当前模型做不到字段级继承——保存后班次也固定在门店行,总部再改班次门店不会跟。这是最常见的误配。解决:门店不要保存整页;或接受「门店从此独立」。

统计出勤时 PunchStatisticsServiceImpl 同样按员工部门 getAttendanceConfigByDeptId。报表和打卡页必须是同一套继承,禁止统计服务自己写一份默认 9 点。


九、技术亮点

要点
实现
价值
一页中枢
左树右表,一行一部门
实施只记一个菜单
整行继承
无本级则 parentId 递归
讲得清、查得快
inherited 黄条
前端不把父级当已保存
避免误改公司规则
删行回退
deleteByDeptId
试点可撤
当日规则下发
resolveDayRule
App 不算大小周
状态后端算
calcCheckIn/OutStatus
防改包变正常
WiFi 认 BSSID
归一化后匹配
SSID 改名不影响
空名单兼容
内网且无白名单不校验
旧租户不炸
外勤跳过 WiFi
isFieldPunch
出差仍能记一笔
PC/App 同源
同一 /info /do
改配置不用发版

十、快速体验

在线演示:https://ruoyioffice.com/web/(账号 admin / admin123)

  1. 打开 人力资源 → 假勤管理 → 假勤配置
  2. 点公司节点,看是否已有本级行;再点子部门,看黄条是否出现。
  3. 看打卡模式:1 则认半径,2 则认 WiFi 表是否有 BSSID。
  4. 打开节假日方案,确认 holidayPlanId 有值。
  5. 手机或 H5:pages-hrm/attendance/punch,核对上班时间和 PC 当日规则一致。
  6. 演示站不要随便给公共部门删配置。

源码仓库:GitHub:https://github.com/yuqing2026/ruoyi-office | GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office | Gitee:https://gitee.com/yqzy1688/ruoyi-office

本地:后端 48080,PC pnpm dev:antd,UniApp pnpm dev 后用 390 宽。两端验证码关闭后才能自动登录截图。


常见问题(FAQ)

子部门改了半径,总部改班次为什么带不动?

子部门一旦保存,就是完整本级行。班次也被固定。要跟总部,删掉子部门配置。

内网打卡在电脑浏览器失败?

白名单非空时必须有 BSSID。浏览器拿不到就失败。改用 App,或该部门改回位置打卡,或清空白名单(只保留内网模式、不校验 WiFi)。

没有配置为什么不能打卡?

hasConfig=false。给员工部门或任一祖先保存一行。不要在 UniApp 写本地默认班次。

节假日和班次哪个优先?

resolveDayRule 先看今天是不是工作日(含方案里的假/调休),再套 timeRules。方案说放假,弹性窗不会让你变成上班。

和请假规则配置那篇文章什么关系?

那篇讲假种额度、单位、限制。本篇讲部门用哪套班次和哪套日历。请假扣的是余额,打卡看的是当日规则,不要把假种开关当成班次。


结语

假勤配置要选的是「本级覆盖还是继承」,以及「位置、内网、外勤」怎么组合。班次、WiFi、围栏、节假日、补卡写在同一页,手机只吃解析后的当日字段。字段级继承故意不做,免得实施讲不清。

读者能带走的三句:只在需要例外的部门保存;WiFi 认 BSSID;App 不写第二套班次。你们现在门店迟到,是班次真的 9 点,还是继承断了?评论区贴一下有没有黄条。

相关阅读:天地图文讲围栏怎么画;补卡文讲审批回写打卡记录;假期规则文讲假种。那些文章不替代本页的部门继承。


💡 想要体验 RuoYi Office 的强大功能?

🌐 在线演示:https://ruoyioffice.com/web/(账号 admin / admin123)

📦 源码仓库:GitHub:https://github.com/yuqing2026/ruoyi-office | GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office | Gitee:https://gitee.com/yqzy1688/ruoyi-office

💬 技术咨询:添加微信 17156169080,备注「RuoYi Office」

⭐ 如果觉得不错,请给个 Star 支持一下!

【声明】内容源于网络
0
0
企业软件源码
RuoyiOffice 是一套基于 Spring Boot + Vue3 +Uniapp 的企业一体化管理平台,集 OA、CRM、ERP、工作流、HR、资产、合同、项目、AI应用等业务于一体,帮助企业用一个系统协同管理多类核心业务。
内容 120
粉丝 0
企业软件源码 RuoyiOffice 是一套基于 Spring Boot + Vue3 +Uniapp 的企业一体化管理平台,集 OA、CRM、ERP、工作流、HR、资产、合同、项目、AI应用等业务于一体,帮助企业用一个系统协同管理多类核心业务。
总阅读1.4k
粉丝0
内容120