becomer
48c3948c56
我的页面会员卡统一为 VIP 黑金色,普通用户提示改为引导开通
...
ci/woodpecker/tag/build Pipeline was successful
1. 文案
普通用户副标题由「免费用户」改为「开通 VIP 即可不限次数」,
直接传达价值;VIP 分支保持显示到期时间与剩余天数。
2. 卡片配色统一
原先普通用户是珍珠白卡片、VIP 才是黑金,两套样式(.is-vip/.is-free)。
现统一为 VIP 专属黑金色,两种身份同一套质感,区别只在文案与角标:
- .member-card / .is-vip / .is-free 共用同一段背景与边框
- 文字、图标、光晕规则去掉 .is-vip 前缀,直接作用于 .member-card
- 暗色主题同步合并,移除原先 is-free 的墨绿覆盖(会破坏金色统一)
3. 连带调整
- 头像占位文字统一为金棕 text-amber-800(原普通用户用品牌绿,
在黑金底上不协调)
- 「兑换 CDK」按钮加深阴影,与深色卡片底更好分离
已用无头 Chrome 截图确认实际渲染:黑金卡片、金色文字、金色按钮
层次清晰,文字无遮挡。
npm run build 与 vue-tsc 均通过。
2026-10-21 17:12:20 +08:00
becomer
0d3280fe7b
我的页面:精简普通用户提示文案 + 头图边界对齐按钮中线
...
ci/woodpecker/tag/build Pipeline was successful
1. 文案过长显示不全
普通用户的副标题原为「普通用户 · 开通后执行任务不限次数」,
在卡片的 flex-1 区域内被 truncate 截断。
改为「免费用户」,与上方姓名行组合读作「张三 / 免费用户」,
既完整显示又不丢信息(VIP 分支同步去掉冗余的 "VIP ·" 前缀,
因为 VIP 身份已由姓名旁的角标体现)。
2. 头图绿色渐变边界位置
原先 -mt-14 时,头图底色边界落在会员卡约 63.6% 高度处,
低于「兑换 CDK」按钮中线 12px,看起来切在卡片中上部。
改为 -mt-11 后边界正好对齐按钮中线。
该结论来自无头 Chrome 实测而非估算:构造等价 DOM 后测量
heroBottom / btnCenter 差值,-mt-14 → +12px(偏低),
-mt-11 → 0px(对齐)。页面上写死了这次实测数据作为注释。
另经实测确认头图的 pb 只影响上方留白、不影响对齐
(pb-14 与 pb-16 均对齐),故保留 pb-16。
卡片内边距同步由 p-4 收窄为 px-4 py-3,让三行文字更紧凑。
npm run build 与 vue-tsc 均通过。
2026-10-21 17:07:13 +08:00
becomer
0d639ea485
我的页面:头像姓名并入会员卡,去掉学校信息
...
ci/woodpecker/tag/build Pipeline was successful
原先顶部渐变区单独一块「大头像 + 姓名 + 学校 + 学院专业标签」,
对用户实际没什么用,还占掉一屏顶部空间。
改动:
- 删除顶部个人信息块(含学校名与学院/专业/班级标签)
- 头像缩为 44px 圆形,移到会员卡内 Crown 图标的位置(该图标去掉),
姓名与会员身份(VIP/普通用户)合并为同一行
- VIP 角标改为贴在头像右下角
- 顶部渐变条高度压缩:padding-top 28px→14px、pb-20→pb-16,
会员卡仍以 -mt-14 上浮叠压,视觉层次不变
顺带清理不再使用的导入与计算属性:Crown 图标、GraduationCap 图标、
profileTags(学院/专业/班级标签)。
修掉 img 的 :src 缺可选链导致的 null 类型报错。
npm run build 与 vue-tsc 均通过。
2026-10-21 16:58:24 +08:00
becomer
3b4a8df8fd
兑换弹窗购买按钮去掉图标并缩小文字
...
ci/woodpecker/tag/build Pipeline was successful
按钮在弹窗内与「立即兑换」等宽,加上图标后文字被挤得偏紧。
按需求去掉 ShoppingCart 图标(图标导入仍被页面其他两处使用,保留),
文字由 text-sm font-semibold 调为 text-xs font-medium,
并加 px-2 避免文字贴边,整体更舒展。
npm run build 与 vue-tsc 均通过。
2026-10-21 16:45:00 +08:00
becomer
5a33afa213
修复兑换弹窗购买按钮不可见,并调整左右顺序
...
ci/woodpecker/tag/build Pipeline was successful
两个问题:
1. 按钮完全看不见(严重)
上一版给「没有 CDK?立即购买」用了 cdk-goto-btn 类,但该类只在
NoRunsDialog.vue 的 scoped 样式里定义过——scoped 样式带 data-v 属性
选择器,不会作用于 ProfileView 内的元素。结果按钮没有任何背景,
而文字是 text-white,白字落在弹窗白底上等于隐形(点击仍生效)。
现在在 ProfileView 的样式块内补齐 .cdk-goto-btn 定义:
珊瑚渐变背景 + 白色文字 + 阴影 + nowrap,与外层购买入口视觉呼应。
2. 左右顺序反了
按需求改为「没有 CDK?立即购买」在左、「立即兑换」在右。
npm run build 与 vue-tsc 均通过。
2026-10-21 16:41:41 +08:00
becomer
eaafc50739
兑换 CDK 弹窗内新增「没有 CDK?立即购买」入口
...
ci/woodpecker/tag/build Pipeline was successful
原先兑换弹窗里只有「立即兑换」,没有 CDK 的用户只能关掉弹窗
再去找外层购买入口,路径绕。
改为并排两个动作:
- 立即兑换(原有,flex-1)
- 没有 CDK?立即购买(flex-1,沿用外层购买的珊瑚渐变样式)
新按钮等同于点击外层「购买 CDK 兑换码」入口:先关闭兑换弹窗,
再调用同一个 onCdkBuyClick()(弹流程说明 → 确认后跳购买平台),
避免两个弹窗叠加;购买入口未开放时提示「请联系管理员」。
npm run build 与 vue-tsc 均通过。
2026-10-21 16:37:32 +08:00
becomer
8926c0b109
邀请码改为 8 位 + 支持用户重置
...
1. 增至 8 位(backed/internal/handler/invite_code.go)
字符集 31 个不变(已去掉易混淆的 0/O/1/L),
空间从 31^6≈8.9 亿提升到 31^8≈8.5 万亿。
2. 新增重置接口 POST /api/auth/reset-invite
- resetInviteCode():复用 withUniqueInviteCode,唯一性由
invite_code 唯一索引 + 冲突重试保证(与首次生成同一套逻辑,
不做「先查后写」,并发下依然正确)
- 乐观锁:仅当库中旧码仍是本次读到的值时才替换,
避免两个并发重置互相覆盖导致其中一个返回的码实际已失效;
冲突时返回 409 提示刷新重试
- 用户当前无码时先补齐再重置,否则乐观锁条件会失效
- 记录 oldCode/newCode 便于排查
3. 前端(ProfileView)
- 邀请码行内新增重置按钮(环形箭头图标,重置中转圈)
- 点击先弹确认框,用 danger 样式并明确告知影响:
「原来的邀请码会立即失效,别人再用旧码填写将无法绑定;
已经通过旧码完成的邀请关系不受影响,积分也不会被收回」
- 成功后直接更新页面上的邀请码并提示
测试:8 位长度与字符集校验、5000 次生成无重复、
重置生成的新码格式合规、两个哨兵错误互不混淆。
2026-10-21 16:29:31 +08:00
becomer
320f21442c
填写邀请码按钮文案改为展示实际可得积分
...
ci/woodpecker/tag/build Pipeline was successful
原文案「有同学的邀请码?点此填写」只说明能填,没告诉用户填了能得什么。
改为「输入同学邀请码,立即获得 X 积分」,其中 X 复用
inviteeRewardPerInvite —— 与上方推广板块「TA 得 X 积分」同源,
保证两处展示的被邀请方积分始终一致;接口未返回该值时降级为
「输入同学邀请码,立即获得积分」,文案仍通顺。
npm run build 与 vue-tsc 均通过。
2026-10-21 16:23:55 +08:00
becomer
770b1754b4
我的页面:次数并入会员卡、兑换 CDK 统一、邀请积分并入推广板块
...
ci/woodpecker/tag/build Pipeline was successful
1. 可执行次数并入会员卡那一行
- 普通用户:显示「可执行次数 N」,右侧按钮「兑换 CDK」
- VIP 用户:仍显示「VIP 会员 + 有效期/剩余天数」,
下方额外显示「可执行次数 不限」
2. 兑换入口统一
- 原「开通 VIP / 续费」按钮改为「兑换 CDK」,既能兑 VIP 也能兑次数
- 后端本就是同一接口:handleRedeem 依据响应 data.type
('vip' / 次数)分别处理,故后端无需改动
- 兑换弹窗标题/标签统一为「兑换 CDK」「CDK 兑换码」,
移除 redeemKind 状态(已无分支)
3. 删除下方数据三宫格
- 不再单独显示「可执行次数」格子(已并入会员卡)
- 删除「已邀请 N 位同学」格子
4. 邀请积分并入推广板块
- 在邀请卡内展示「邀请积分 N」+「免费换次数」按钮
- 原「换次数」文案改为「免费换次数」
- 积分不足时按钮禁用并显示「满 1 可换」
- 兑换弹窗内的引导文案同步指向新位置
顺带清理:移除随三宫格一起不再使用的 Users 图标导入。
npm run build 与 vue-tsc 均通过。
2026-10-21 16:15:59 +08:00
becomer
651739af1b
次数不足弹窗统一为专用组件,底部改为「邀请好友 / 购买 CDK」
...
ci/woodpecker/tag/build Pipeline was successful
原先只有打卡页有专用的「签到次数不足」弹窗,请销假页只有一条
一闪而过的红色 Toast;而且提示让用户「购买 CDK」,但请销假页内
没有任何购买或兑换入口,用户看不懂也无处可去。
新增 components/NoRunsDialog.vue(打卡/请假/续假/销假共用):
- 标题按场景传入(签到/请假/续假/销假次数不足)
- 说明两条获取途径:邀请新用户、购买 CDK
- 底部两个动作按钮:
· 立即邀请好友 → 跳 /profile?intent=invite,进页后自动复制邀请码并提示
· 立即购买 CDK → 跳 /profile?intent=buy,进页后自动触发 CDK 购买入口
(弹出流程说明弹窗,确认后跳购买平台)
ProfileView 新增 intent 处理(onMounted 末尾):
- intent=invite:调用已有 copyInviteCode()
- intent=buy:CDK 入口可见时调用 onCdkBuyClick() 弹说明;
不可见时提示「购买入口暂未开放」
- 处理完用 router.replace 清掉 query,避免刷新/返回时重复触发
LeaveView 两处 onError 接入 handleNoRunsError():命中次数不足时
不再弹红色 Toast(避免误导为系统错误),改弹专用窗;请假/续假
按 submittingExtend 区分标题。
HomeView 移除内联弹窗,改用共用组件。
npm run build 与 vue-tsc 均通过(仅剩改动前已有的 Gift 未使用告警)。
2026-10-21 16:10:05 +08:00
becomer
608dee697f
置顶公告视觉增强 + 请假销假页接入公告条
...
ci/woodpecker/tag/build Pipeline was successful
1. 公告条美化(PinnedNotice.vue)
- 增加左侧琥珀色渐变强调条,提升顶部存在感
- 增加 hover 态阴影过渡,点击反馈更明确
- 卡片背景改为更通透的柠檬暖色玻璃
2. 请假/销假页接入置顶公告(LeaveView.vue)
- 引入 PinnedNotice 与 announceApi
- onMounted 中异步拉取置顶公告,失败静默降级为空列表
(不阻塞页面主体加载)
- 公告条置于页面最顶部,与打卡页位置和交互一致
(翻页、长文本横向滚动、点击查看全部)
npm run build 与 vue-tsc 均通过。
2026-10-21 13:26:22 +08:00
becomer
fab756a1ef
购买 CDK 入口更醒目 + 跳转前弹流程说明
...
ci/woodpecker/tag/build Pipeline was successful
入口视觉强化:
- 从 <a> 改为 <button>(点击先进说明弹窗,由弹窗确认后才跳转)
- 内边距 p-4→py-5、图标 11→12、标题 15px→base/extrabold、箭头 8→9
- 增加呼吸光环动画 cdk-pulse(prefers-reduced-motion 下自动关闭)
跳转前说明弹窗(新增 showCdkBuyConfirm):
按用户要求提示完整流程——
1. 在购买平台购买卡密,需填写并牢记「邮箱」与「查询密码」
2. 支付成功后,用同样的邮箱与查询密码在平台查询 CDK
3. 回到本站,在「兑换次数 / VIP」处粘贴 CDK 兑换
并提示购买平台为独立站点、兑换码勿泄露。
跳转方式:确认后在 DOM 中创建临时 <a target=_blank> 并 click(),
而非 window.open —— 手机浏览器会把程序化 window.open 当弹窗强制
当前页打开,原生链接行为更稳定。
2026-10-21 13:10:53 +08:00
becomer
b40e95d6c3
同步编辑表单图片块的过时注释(点击直接替换 → 缩略图+编辑按钮)
ci/woodpecker/tag/build Pipeline was successful
2026-10-18 22:53:46 +08:00
becomer
1ffe68eb72
打卡表单图片加「编辑」按钮,点击重新上传
...
改动前:整个缩略图区域是 @click="handleImageUpload" 的上传热区,
视觉上没有任何按钮,用户不知道点图片会发生什么(部分浏览器/WebView
点击 <img> 还会触发自身原生看图,进一步造成「点了是预览」的错觉)。
改为三态清晰渲染:
1. 上传中 → 显示 loading(优先于缩略图,替换时给出明确反馈)
2. 已有图片 → 缩略图 + 右下角「编辑」按钮(铅笔图标),点按钮才重新上传;
图片本身不再是热区,避免误触
3. 无图片 → 虚线「上传」框(按钮语义)
两套表单(新建打卡 / 编辑表单)同步修改。
调整了 v-if 链顺序:原先缩略图判断在前,导致上传中状态在有图时永不显示。
说明:本次未定位用户所见「点图片是全屏预览」的确切来源——
代码中不存在任何预览组件,HomeView 的图片仅有上述点击绑定,
推测为宿主浏览器/App 内核对 <img> 的原生行为,与本项目代码无关。
2026-10-18 22:53:03 +08:00
becomer
72dd2645ec
前端上传 OBS 显式附带 filename,避免部分环境 400
...
ci/woodpecker/tag/build Pipeline was successful
对真实 OBS 做了 5 组对照(真实用户 cookie + 真实策略 + 真实上传):
filename=file.jpg CT=image/jpeg → 204 成功
无 filename CT=image/jpeg → 400 失败
filename=blob CT=image/jpeg → 204 成功
无 filename CT=octet-stream → 400 失败
filename=image.jpg CT=octet-stream → 204 成功
结论:file 部分必须带 filename,否则 OBS 直接 400;与 Content-Type 无关。
原代码 `obsForm.append('file', file)` 只传两个参数。浏览器对
append(name, Blob) 会按规范自动补 filename="blob",所以多数桌面/移动
浏览器能通过;但自动补的名字无扩展名,且部分环境(老 WebView、
App 内置浏览器)不补,就会命中上表的 400 分支。
改动:
- 新增 utils/obsUpload.ts:resolveUploadFileName 优先用原始文件名
(无扩展名时按 MIME 补),无 name 时按 MIME + 时间戳生成;
非图片/未知 MIME 回退 jpg。
- HomeView.vue(任务表单上传)与 LeaveView.vue(请假上传)两处
append 均改为显式传文件名。
说明:本次未定位到用户 400 的确切链路(后端复现 return_register 得到的是
cookie_expired 而非 400),此项为消除已知的 filename 差异,
使前端行为在任何环境下都稳定。
2026-10-18 18:58:24 +08:00
becomer
4a064b53cc
sdau 销假接口实现:与请假共用 application_submit,靠 requestType 区分
...
静态分析定位到请假详情页组件链(views/leave/detail/):
ActionPopup-CRBsBafz.js 中 handleType=CANCELAPPLY 的销假申请逻辑,
其提交函数 re 从 fieldsConfig-BzLAymBp.js 导入,即与请假同一个
application_submit 接口。
销假 payload(requestType=CANCELLATION):
{ originalId: <原假单ID>, projectId, requestType:"CANCELLATION",
fields[], cancellationApplyInArea, inArea, address, longitude, latitude }
改动:
- service:新增 XsgzSubmitCancel / XsgzSubmitApplication(请假/销假/续假共用),
XsgzSubmitPayload 增加 originalId/inArea/cancellationApplyInArea
- service:XsgzLeaveRecord 增加 projectId 字段;ToLeaveItems 把 projectId
写入 TypeWid,供前端销假时回传
- handler:新增 executeXsgzLeaveCancel,SSE 事件流 + 落日志
- handler:ExecuteLeaveRequest 增加 projectId
- 前端:ExecuteLeaveParams 增加 projectId;销假提交时回传 typeWid
另:batch-cancel / batch-cancel-list 经确认是辅导员侧接口,学生销假不走它们。
2026-10-17 17:40:16 +08:00
becomer
cb3e0f8917
sdau 提交请假接口实现:按前端源码解析的契约接入 application_submit
...
从主 bundle 路由表 __vite__mapDeps 定位到请假页 chunk 链
(LeaveApply → index-C4CfkeDm.js → fieldsConfig-BzLAymBp.js),
完整解析出提交契约:
POST /api/leave/student_action/application_submit (multipart/form-data)
payload = { projectId, requestType:"LEAVE", startTime, endTime,
fields[], address, longitude, latitude }
改动:
- service:新增 XsgzSubmitLeave(multipart 提交)、XsgzFetchIsRequireLocation
- service:do() 支持 JSON body;新增 XsgzSubmitPayload
- handler:新增 executeXsgzLeaveApply,SSE 事件流与辅导猫链路一致,
并落 cp_leave_logs
- LeaveType / ExecuteLeaveApplyParams 新增 projectId:xsgz 用 UUID 作类型 ID
- 前端提交时带上 projectId
顺带修正 GetPendingLeaves:batch-cancel-list 经实测是**辅导员**接口
(学生调用报「没有辅导员销假权限」),学生端改回 leave_page 按状态标记 CanReport。
注:销假写接口(batch-cancel)学生侧契约仍未确认,暂未接入。
2026-10-17 17:34:01 +08:00
becomer
18ead961de
修复请假/销假提交偶发「连接失败,请检查网络」
...
ci/woodpecker/tag/build Pipeline was successful
现象:iOS 独立 QQ 浏览器 App 提交请假时偶发报「连接失败,请检查网络」,
重试十余次偶尔成功;同页其他接口(axios/XHR)与打卡(EventSource)均正常。
根因:请假/销假是唯一使用 fetch + res.body.getReader() 手动解析 SSE 的接口
(因需 POST + JSON body,无法用 EventSource)。这些浏览器对 fetch 响应流的
支持不稳定,偶发 res.body 为 null 或流读取失败,命中 !res.body 分支误报
「连接失败」;服务端实测始终返回 200 + 正确 SSE(生产日志 189 次全 200)。
修复:
- 前端 leaveSSE 改用 XMLHttpRequest 增量解析(onprogress 累积 responseText),
回调签名与事件语义不变;XHR 在这些浏览器上已被证明可靠,且能取到
状态码与错误响应体,错误提示更准确。
- 后端 gzip 排除列表由仅 /api/task/execute/ 扩大到全部 SSE 端点
(/api/task/execute/、/api/leave/apply、/api/leave/execute),SSE 流保持明文。
排查过程:曾怀疑 gzip 缓冲破坏 SSE,写了两个探针测试(事件到达时序、
压缩 vs 不压缩),证伪——压缩下事件仍逐块即时到达,gzip 非根因。
2026-10-17 00:32:58 +08:00
becomer
57903d9b34
工单状态语义对齐:处理中改蓝、已完成/关闭文案、用户删除自动关闭
...
ci/woodpecker/tag/build Pipeline was successful
- 状态颜色:处理中由品牌青绿改为蓝色(adm-badge-sky),即
待处理(黄)/处理中(蓝)/已完成(绿)/关闭(灰)。
- 状态文案:resolved「已解决」→「已完成」,closed「已关闭」→「关闭」;
管理端列表筛选与详情按钮(标记已完成)同步。
- 用户删除自动关闭:DeleteTicket 软删除前先将 status 置为 closed,
此后不可回复,管理端也不再计入未关闭待办。
- 回复权限维持:closed 阶段禁止回复,其余状态(待处理/处理中/已完成)
用户均可在客户端回复(用户回复已完成工单会自动重开为待处理)。
2026-10-17 00:03:30 +08:00
becomer
486e7ede04
工单与公告问题修复:回复强制弹窗、消息左右顺序、指定用户搜索、用户回复邮件通知
...
ci/woodpecker/tag/build Pipeline was successful
1. 工单新回复任意页面强制弹出:通知新增 kind 字段(reply/status),
MobileLayout 轮询检测本设备本账号新出现的未读 reply 通知并强制弹窗
(打卡/请假/我的等所有页面均生效);首次加载只记基线不弹历史,
基线按用户 id 分开存储避免多账号串扰;状态变更通知不弹窗。
2. 修复管理端工单对话左右顺序:此前 user/admin 位置对调(admin 在左、
user 在右),现 manager 侧 admin 居右、user 居左,与用户端一致。
3. 公告指定用户改为搜索选择:新增 /admin/announcements/targets,
按用户名/学号/姓名/学校(代理按用户名)模糊查询,前端改为
搜索候选列表点选,替代手动输入用户ID。
4. 用户回复工单也邮件通知管理员:新增 NotifyTicketUserReply,
AddTicketMessage 后异步发信。
2026-10-16 23:15:26 +08:00
becomer
44de36c47b
修复人脸识别签到「假成功」:提交后复核结果并按真实结果扣费
...
ci/woodpecker/tag/build Pipeline was successful
今日校园对开启人脸识别的查寝/签到任务,submitSign 返回 code=0 仅表示受理成功,
人脸核验在服务端异步进行,可能事后被系统自动驳回(如 FRS.0036 从url获取文件失败)。
此前仅凭 code=0 即判成功,导致日志记成功、照常扣费,而今日校园侧实为未签到。
- 后端:提交成功后若任务开启人脸识别,回查 detailSignInstance 的
isFaceIdentify/recognitionProgress/recognitionProgressCode/signStatus 判定真实结果;
立即查 → 无结果等 1s → 再无结果等 2s(累计≤3s,t≈0/1/3)。
驳回则经 SSE 返回错误并标记失败,不扣费;明确成功才扣费;
三轮后仍无明确结果按成功处理,避免误杀。
- 前端:上传/提交前校验图片非空且体积>0,压缩产物为空回退原文件,
上传失败提示重新选择照片,防止 0 字节空对象入 OBS。
- 新增 evalFaceIdentify/NeedsFaceIdentify/rawCodeString 单测。
2026-10-16 22:47:16 +08:00
becomer
0ca3528afd
请假/续假与销假弹窗新增操作提示
...
ci/woodpecker/tag/build Pipeline was successful
- 请假/续假申请弹窗表单顶部提示:定位经纬度请使用微信小程序「经纬度查询定位助手」
- 销假表单弹窗顶部提示:销假请使用请假设备,授权设备码,否则无法正常销假
2026-10-15 09:34:24 +08:00
becomer
2a989a15ef
购买 CDK 入口改为原生锚点链接:修复手机浏览器 window.open 被强制当前页打开
...
ci/woodpecker/tag/build Pipeline was successful
手机浏览器(UC/夸克等)把程序化 window.open 当弹窗强制降级为当前页跳转,
换成 <a target=_blank rel=noopener> 走浏览器原生新标签页处理。
微信 WebView 无 tab 概念,仍为当前页打开(平台限制)。
2026-10-14 11:05:05 +08:00
becomer
150cd86a43
「我的」页新增购买 CDK 显眼入口:后台可配跳转链接与显隐,新标签页打开
...
ci/woodpecker/tag/build Pipeline was successful
- 用户端:VIP 卡下方展示渐变购买按钮(含 HOT 角标与光泽动画),
点击新标签页打开后台配置的链接;开关关闭或链接为空时不渲染
- 后台:系统设置新增「CDK 购买入口」卡(滑动开关 + 链接输入),
链接校验 http(s) 协议,开关值归一化 true/false
- 配置经既有 /cdk/vip-info 接口下发,零新增请求
2026-10-14 10:50:00 +08:00
becomer
1724a37bcf
修复部分浏览器销假/请假上传图片「选完无任何反应」
...
ci/woodpecker/tag/build Pipeline was successful
用户反馈:销假时选完图片后无转圈、无预览、无报错,服务端也收不到
upload-policy 请求(访问日志可证)。
原实现用游离的 input(createElement 后未挂 DOM)且 accept 限定
image/png,image/jpeg,image/jpg;在部分第三方内核(如 iPhone 夸克)
下,游离 input 不触发 onchange,iOS 相册的 HEIC 等格式也会被判为
不匹配,导致 input.files 为空 → 命中 `if (!file) return` 静默返回。
抽出 pickImageFiles() 统一修复:
- accept 改 image/*(与首页上传一致);
- input 挂到 DOM,用完移除;
- 监听 cancel 区分「用户取消」(静默)与「选了但读不到文件」(可见提示);
- 销假图片、请假附件两处共用,并修正 uploading 标志在提前 return 之后才置位的问题。
2026-10-08 23:02:43 +08:00
becomer
7f39b39643
续假在后台执行日志中单独归类(新增 action=extend)
...
ci/woodpecker/tag/build Pipeline was successful
现象:后台用户管理「当天执行总次数」明细里,续假与请假同显示为「请假」。
根因:续假与新建请假都写 action='apply',后台 UNION SQL 的
CASE action WHEN 'apply' THEN 'leave_apply' 因此把两者归一。
改动:
- 模型新增 LeaveActionExtend="extend"
- 请假/续假写日志按 req.IsExtend 分流:续假记 extend,请假仍记 apply
- 后台 UNION SQL 映射 extend → leave_extend
- 前端执行日志类型标签新增「续假」(紫色徽标)
说明:存量历史的续假记录已写入 apply,无原始标记可回溯,不做数据迁移。
go build/vet/test 与前端 vue-tsc/build 通过。
2026-10-08 10:39:01 +08:00
becomer
13faba27b6
修复请假自定义选项字段值类型:提交 optionSeq 数字而非选项文本
...
ci/woodpecker/tag/build Pipeline was successful
现象:请假成功但查看该单详情报 For input string: "是"(服务端返回 code -1),
且列表接口随之整体报错。
根因:自定义附加字段的选项值应提交 optionSeq(数字),此前提交 optionContent("是")。
服务端把文本存进数字字段,导致该单详情(及含它的列表)解析崩溃。
证据:官方 ReApply 分包提交前把服务端的 optionContent 反查成 optionSeq 再提交;
提交体 fieldValue 为数字。
改动:前端选项字段提交 Number(optionSeq);文本字段仍提交字符串。
验证:用该校真实类型配置在浏览器核对,提交体为 {"fieldValue":1,...}。
注:已被坏数据写入的那张单需要单独处理(见后续说明)。
2026-10-07 19:01:13 +08:00
becomer
46a4bd3b80
请假附加信息:selfDefineAnswers 补 fieldType,与官方前端一致
...
ci/woodpecker/tag/build Pipeline was successful
官方 Apply 分包构造:selfDefineAnswers = selfDefineFields.map(c => ({fieldWid, fieldName, fieldValue, fieldType}))。
此前漏了 fieldType,补上以降低服务端校验拒绝风险。构建/vue-tsc 通过。
2026-10-07 18:42:02 +08:00
becomer
248e28858b
修复新套请假「请检查附加信息是否全部填写正确」
...
ci/woodpecker/tag/build Pipeline was successful
根因(用该校真实类型配置核对):typeWid=1873「离校请假」详情里
- isRequiredEvidenceFile=true(证明材料必填),但类型列表接口返回 false,
我们的映射只看了 isVisibledEvidenceFile,未透传必填开关 → 漏检附件
- selfDefineFields 有必填自定义字段「当天是否返校」(fieldWid=2422),
我们完全未采集,提交时 selfDefineAnswers 恒为 [] → 服务端拒绝
改动:
- LeaveType 增 requireEvidence/evidenceMin/selfDefineFields 并映射(取详情字段)
- LeaveNewFormDetail 暴露 selfDefineFields;提交体 selfDefineAnswers 从请求透传
- ExecuteLeaveApplyRequest 增 selfDefineAnswers
- 前端:请假表单渲染自定义字段(单选/文本),必填校验(附加字段 + 证明材料最少张数),
附件标签按 requireEvidence 显示「必传」
- api/leave.ts 补 SelfDefineField/SelfDefineAnswer 类型
验证:用该校真实类型配置在浏览器核对,表单出现「当天是否返校」、附件标注「必传」。
未做提交(按用户要求)。构建/go vet/test/vue-tsc 通过。
2026-10-07 18:40:37 +08:00
becomer
005ce14013
工作日志签到:改为选择校区,用该校区规定经纬度签到
...
ci/woodpecker/tag/build Pipeline was successful
按需求把签到项的「获取 GPS 定位」改为「选择校区」:用户选所在校区,直接使用该校区
规定的经纬度(学校按点位半径校验)。不再依赖设备定位。
- 后端透传签到范围点(地址/经纬度/半径)到 formItems;新增 WorklogScopePoints 复用
查寝的 SignPlace 解析与 coordsWithinSignRange 距离判定;执行时校验坐标是否在范围内,
不在则明确拒绝并提示(不伪造定位)
- 前端签到字段渲染校区下拉,选中即把该校区的 lat,lng 写入字段 content
- 移除 GPS 定位按钮与相关函数
验证:用真实范围数据(许东/奶槐等校区)在浏览器核对,下拉正确列出校区,选择后
显示"已选:某某校区"并写入坐标。构建/go vet/test/vue-tsc 均通过。
2026-10-06 19:44:24 +08:00
becomer
d2d4847171
工作日志签到项:编辑页提供「获取当前位置」,坐标取真实 GPS
...
ci/woodpecker/tag/build Pipeline was successful
实测确认真实字段定义:值班签到 fieldType=5、signCondition=true,
templateScopes 给出 4 个校区点位(含经纬度与半径 500/600m)——即学校按点位半径校验,
与查寝同理,需真实定位;signScopeWids 是允许签到的范围点位 id(非坐标)。
- 前端:isWorklogSign 字段渲染「获取当前位置」按钮,调用 navigator.geolocation
取真实经纬度写入该字段 content("lat,lng"),已定位显示回显与「重新定位」
- 后端:执行时从签到字段 content 解析坐标传 submitSign;缺定位时提示先获取定位
- FormItem 类型补 isWorklogSign/signScopeWids
验证:用任务 315 真实数据在浏览器核对,签到字段正确渲染为定位按钮,其余字段正常。
构建/go vet/test/vue-tsc 均通过。未做真实提交。
2026-10-06 19:37:00 +08:00
becomer
7fb15d17c8
修复工作日志字段在打卡页不渲染,导致必填校验报「缺少值班时间/记录/签到」
...
ci/woodpecker/tag/build Pipeline was successful
现象:教师账号执行「学管值班」报「签到信息未配置完整,缺少:值班时间、值班记录、值班签到」。
根因:打卡页表单渲染只覆盖 type 0/1(文本)、2/3(单选多选)、图片、二维码;
工作日志用到的 type 5(日期时间)、7(日期时间)、9(多行文本)没有任何 v-else-if 匹配,
字段根本不渲染,用户填不了 → 保存为空 → 执行前必填校验拦截。
修复:
- 前端加三类渲染分支(5/7 → datetime-local 输入;9 → textarea),并加兜底分支
(未识别类型按文本渲染),避免今后再出现「字段静默丢弃」;内置 toLocalInput 回显
- 模型枚举补 ItemTypeDate(5)/ItemTypeDateTime(7)/ItemTypeTextarea(9)
- 去掉误导性的 isWorklogSign 标记:实测「值班签到」signScopeWids 为空、是 type 5 普通
可填字段,并非定位签到项(此前照第三方实现误判)
验证:用任务 315 真实 formData 在浏览器核对,编辑弹窗正确渲染全部 6 字段
(2 个 datetime + 1 个 textarea + 单选)。构建/vue-tsc 通过。
未验证:该 type 5「值班签到」的真实填写要求(时间点还是留空由后端处理)待真实使用确认。
2026-10-06 19:14:57 +08:00
becomer
de700ae6c8
接入教师端「工作日志」任务类型(集成进现有打卡链路)
...
ci/woodpecker/tag/build Pipeline was successful
接口来自公开逆向实现(两个开源项目互相印证,见 spec://worklog_api.md),
未再扫描 H5;独立套件 /wec-counselor-worklog-apps,服务端按角色校验(学生 401)。
- service/worklog.go:模板列表/日志列表/详情/模板详情/建单/定位签到/提交,
响应解析含 code!=0 与空 message 回退;字段宽泛解析(isRequired/signScopeWids/templateScopes)
- FetchWorklogTasks 产出统一任务项,SignWid="formWid:worklogWid";学生账号被拒静默跳过
- handler:GetTaskForm 增 worklog 分支(字段转通用 formItems);新增 executeWorklogTask(SSE)
- 执行分流 ExecuteTask 增 TaskTypeWorklog;前端加「工作日志」标签与配色
- 单测:响应错误/空 message/非 JSON/成功解析/字段解析/提交体形状
未验证(无教师会话):真实字段名与流程、定位签到坐标来源(当前占位空值)。
2026-10-06 13:29:11 +08:00
becomer
b4357f746f
v1.2.125:打卡页骨架与界面一致;后台执行日志类型不再换行
...
ci/woodpecker/tag/build Pipeline was successful
- 打卡页加载骨架改为与主内容同结构:品牌渐变头卡(标题+四宫格+主按钮)、
带左侧色条的任务行、日志时间线;头卡占位用固定白色透明度,暗色下与真实卡片一致
- 后台「执行日志明细」类型列 64→104px,改用 adm-badge(nowrap)并按类型配色,
「返校登记」等长标签单行显示
2026-10-05 22:59:14 +08:00
becomer
5042c869b1
v1.2.124:各页底部导航滚动收窄行为统一
...
ci/woodpecker/tag/build Pipeline was successful
收窄门槛 COLLAPSE_MIN_RANGE 200→80px:此前可滚动距离不足 200px 的页面(如「我的」约 170px)
从不收窄,与打卡/请假页表现不一致。方向阈值 32→24px、顶部展开区 40→24px,
保留同向累计阈值与回弹夹取,底部回弹/小幅来回不抖动。
2026-10-05 18:05:51 +08:00
becomer
96e1c3d1f2
v1.2.123:修复登录页选择学校后再次搜索并重新弹出下拉框
...
ci/woodpecker/tag/build Pipeline was successful
原因:选中学校时把输入框写成学校全名,watch 把这次写入当成用户输入,
以全名重新请求并重新打开下拉框;且无防抖、无过期响应保护。
- 写回全名时跳过搜索;选中后再改输入则清空选择并重新搜索
- 300ms 防抖 + 请求序号,丢弃过期响应
- 已选中学校时聚焦输入框不再弹出列表
2026-10-05 18:01:58 +08:00
becomer
ad9b4f4ee3
v1.2.122:登录页文案调整;切换手机号登录时提示会踢下线 App
...
ci/woodpecker/tag/build Pipeline was successful
- 功能标签「自动打卡」改为「人脸签到」
- 去掉卡片副标题与底部密码说明
- 点「手机号登录」弹窗提示:会导致今日校园 App 被踢下线,重新登录 App 即可,不影响 App 功能
2026-10-05 17:55:09 +08:00
becomer
ae4ca4a00e
v1.2.121:请销假页新增「续假」入口;登录页重构美化
...
ci/woodpecker/tag/build Pipeline was successful
续假入口
- 头卡改为「请假 / 销假 / 续假」三按钮,续假打开只含可续假请假单的列表
- 列表按模式过滤:销假只看可销假单,续假只看可续假单;空态说明续假条件(休假中 + 学校允许)
- LeaveItem 增 canReport(列表含仅可续假的单,销假模式据此排除)
登录页
- 品牌渐变头图(问候语 + 功能标签 + 细网格),登录卡片上浮压住头图
- 滑块分段控件切换学工号/手机号;输入行改为图标 + 原生输入,整行聚焦高亮,已选学校带勾
- 验证码块统一尺寸并带刷新角标;短信按钮倒计时文案;主按钮固定品牌色
- 登录逻辑不变(已验证验证码加载、学校选择、/auth/login 提交体)
2026-10-05 17:41:01 +08:00
becomer
8fd132fc64
v1.2.119:新增续假功能(新套 wec-counselor-leave-new-apps)
...
ci/woodpecker/tag/build Pipeline was successful
调研:从真实新套 H5 JS(ReApply chunk)确认续假 = 复用 /stu/leave/apply,
body 带 reApply:true/isExtend:true/leaveId/extendId;预填用 /stu/leave/queryReplyLeaveDetail。
后端:
- BuildLeaveNewApplyForm 补 reApply/extendId(LeaveID/IsExtend 已有)
- FetchLeaveNewReplyDetail(queryReplyLeaveDetail) 续假预填
- ExecuteLeaveApply 续假分支:取 extendId 后按续假体提交
- 新增 POST /leave/extend-prefill
- 待销假列表放宽为「可销假 或 isAllowExtend」,LeaveItem 增 canExtend/typeWid/isLeaveOut
前端:
- 待销假列表每项拆「销假 / 续假」按钮,仅 canExtend 显示续假
- 点续假调 extend-prefill 预填(类型锁定、开始时间=原结束时间、原因/离校带入),复用请假弹窗,标题/按钮随续假态切换
- 提交带 isExtend/extendLeaveId
限制:旧套(leave-apps)续假未实现(其 H5 走 OAuth 中转拿不到 JS),仅新套学校显示入口。
2026-10-05 17:02:30 +08:00
becomer
f27603f724
v1.2.118:查寝二维码点击先选「拍照扫码 / 相册选图」
...
ci/woodpecker/tag/build Pipeline was successful
- 二维码字段改为单一入口「获取二维码」,点击弹出底部选择卡:
拍照扫码(打开摄像头)/ 相册选图(原图片解析),不再默认直接开摄像头
- 选择弹层 z-[120] 高于创建任务表单弹窗,避免被盖住
2026-10-05 16:18:04 +08:00
becomer
5bb39454b8
v1.2.117:查寝二维码支持摄像头实时扫码
...
ci/woodpecker/tag/build Pipeline was successful
- 新增 QrScanner 组件:getUserMedia 调后置摄像头 + jsQR 每 120ms 识别一帧,命中即写入二维码ID
- 识别用整帧下采样(480→720 两档),取景框仅作视觉引导:object-cover 会放大裁切画面,
只扫中心区时近距离对准反而扫不到(实测复现)
- 支持手电筒(设备支持时);非 HTTPS / 权限被拒 / 无设备分别给出可操作的提示
- 保留「选图」回退到原有图片解析;扫码页 z-[400] 且显式恢复 pointer-events
(reka modal 会把 body 设为 pointer-events:none,否则扫码页按钮点不动)
2026-10-05 16:07:38 +08:00
becomer
b928c7ceb0
v1.2.116:邀请码改为登录即自动生成,移除手动生成入口
...
ci/woodpecker/tag/build Pipeline was successful
后端
- 删除 POST /auth/generate-invite 及 GenerateMyInviteCode
- 新用户注册时生成邀请码;唯一索引冲突(仅 invite_code 索引)自动换码重试,最多 8 次
- 老用户登录(用户名 / 学工号匹配)缺邀请码时补发;GET /auth/my-invite 兜底补发
- 服务启动时批量补齐存量空邀请码(幂等;条件更新 invite_code 为空才写,并发安全)
- 邀请码改用 crypto/rand,字符集去掉 0/O/1/I/L;旧实现按纳秒取模,可预测且并发注册易撞码
- 测试:单元测试 + MySQL 集成测试(设置 INVITE_IT_DSN 时运行,已在 8.4.11 上通过)
前端
- 「我的」移除「生成我的邀请码」按钮与 getMyInvite API;新手引导去掉对应目标与文案
2026-10-04 11:09:47 +08:00
becomer
79d531f340
v1.2.115:修复后台页面无法向下滚动、公告弹窗底部按钮被截断
...
ci/woodpecker/tag/build Pipeline was successful
- 后台内容区 <main> 恢复为滚动容器(h-dvh + overflow-y-auto):html/body 为手机外壳设了 overflow:hidden,
v1.2.114 去掉 main 的 overflow-y-auto 后长页面(用户管理等)超出部分无法滚动
- 新建/编辑公告弹窗:标题栏与底部操作栏固定、表单区独立滚动,面板高度限制在视口内,并补充「取消」按钮
2026-10-02 23:07:58 +08:00
becomer
99e01dc338
v1.2.114:后台管理界面重构美化与样式修复
...
ci/woodpecker/tag/build Pipeline was successful
- 新增后台组件类 adm-*(页头/卡片/统计卡/表格/徽标/行内操作/分段筛选/分页/空状态),亮暗两套,各页统一套用
- 外壳:侧栏分组(概览/运营/系统)+ 品牌区 + 选中左侧色条;桌面顶栏面包屑 + 公告入口;手机端玻璃顶栏显示当前页名
- 仪表盘:欢迎横幅展示余额与费率,统计卡新增 CDK 使用率;CDK 页统计卡可点击按状态筛选
- 工单详情改为卡片内对话视图;系统设置改两列网格;代理列表加头像首字母
- 修复:暗色下 bg-blue-500/bg-green-500 白字看不清(改固定品牌/成功色)、text-gray-300 占位符对比不足、
残留的蓝色主题色、工单状态色映射统一
2026-10-02 22:54:01 +08:00
becomer
02dcd6fc32
v1.2.113:重构个人中心
...
ci/woodpecker/tag/build Pipeline was successful
- 头图改左对齐:头像 + 姓名 + 学校,补充学院/班级标签,细网格纹理
- 新增会员卡压在头图下沿:VIP 黑金(显示到期日与剩余天数)/ 普通用户珍珠白,主按钮「开通 VIP / 续费」
- 数据三宫格:可执行次数(点击兑换)/ 邀请积分(够 1 直接换次数,不够提示规则)/ 已邀请人数
- 邀请卡:薰衣草渐变,邀请码大字展示 + 复制按钮,奖励规则一句话说明,「点此填写」他人邀请码
- 功能菜单改 iOS 分组列表:设备码带「已授权/随机/未绑定」状态标签;工单显示新回复数
- 新手引导文案同步新布局;暗色下紫色按钮改固定色保证白字对比度
2026-10-02 19:58:12 +08:00
becomer
51f3641a0c
v1.2.112:重构首页置顶公告;打卡日志改时间线、任务行精修
...
ci/woodpecker/tag/build Pipeline was successful
- 置顶公告新组件 PinnedNotice:一次一条上下翻页轮播(N/M 计数),长文本先停顿再横向滚到末尾,
点击打开底部面板查看全部全文;切后台 / 减弱动态效果时暂停;柠檬暖色卡片,亮暗各一套配色
- 打卡日志:状态圆点 + 竖线时间线,相对时间(刚刚 / N 分钟前 / 今天 / 昨天),失败原因直接展示,空状态带说明
- 任务行:左侧状态色条,执行按钮改品牌渐变圆形主按钮,编辑/删除降级为图标按钮
- 贴边竖条(收藏本站 / 无法请销假 / 网课)位置统一上移避开引导入口;收藏本站改柠檬底深棕字
- 修复 scoped 样式中 `:global(.dark) .x` 被编译为仅 `.dark` 导致暗色覆盖失效(请销假头卡暗色此前未生效)
2026-10-02 19:38:36 +08:00
becomer
9f2f705457
v1.2.111:修复线上暗色主题全站灰底;重做外观切换控件
...
ci/woodpecker/tag/build Pipeline was successful
- 根因:postbuild-css 解包 :where() 时直接去壳,Tailwind 暗色变体 `.x:where(.dark, .dark *)` 被拆成
`.x.dark, .dark *`,`.dark *` 成为全局规则,暗色下所有元素被刷上同一背景/边框(开发服务器不经后处理,故本地截图正常)。
改为先展开逗号列表(`.dark .x`),并在产物中出现全局 `.dark *` 规则时直接构建失败
- 外观切换改为整宽三段分段控件 + 滑动高亮,窄屏不再挤压换行
- 我的页对比度:续费 VIP / 兑换按钮改固定深棕字(≥4.6:1),奖励数字改 amber-600
2026-10-02 15:21:16 +08:00
becomer
301a4fb971
v1.2.110:打卡/请销假页头像行上下间距统一为 16px(原上 12px、下 28px)
ci/woodpecker/tag/build Pipeline was successful
2026-10-02 14:31:36 +08:00
becomer
df40b2ae73
v1.2.109:顶部安全区仅在主屏幕全屏模式生效,修复浏览器/App 内顶栏下移
...
ci/woodpecker/tag/build Pipeline was successful
部分浏览器与 App 内置 WebView 已把网页放在状态栏下方,却仍上报 safe-area-inset-top,
叠加后头像行凭空下移约 47px。改为 --safe-top 变量,仅 display-mode: standalone/fullscreen 时取 env() 值。
2026-10-02 14:13:41 +08:00
becomer
317bf4b5a1
v1.2.108:修复新手引导末步页面左移;新版本提示更及时
...
ci/woodpecker/tag/build Pipeline was successful
- 引导定位 scrollIntoView 改为 inline:nearest,不再横向滚动 overflow:hidden 的头卡容器(装饰光斑超出边界导致整块左移 45px);头卡改用 overflow:clip 兜底
- UpdateChecker:检查间隔 5 分钟→2 分钟、首检 30s→10s;页面回到前台(切回标签/唤起 App)时补查;修正 prompting 在 finally 中被清零导致的并发防护失效
2026-10-02 13:59:40 +08:00