家长端回执弹窗的专属区动态规则此前只写 required + trigger、没给 type。
async-validator 的 getValidationMethod 仅在规则「除掉 message 后只剩 required」
时才走 required 校验器;带上 trigger 后会回退到默认 string 校验器,而 DatePicker
的 v-model 给出的是 Date 对象,被判类型不符。真正的报错又在 cb() 里被自定义
message 顶掉,界面上只剩「请填写XX」——表现为字段明明填了却不让提交。
- ParentNotices.vue: 新增 ruleTypeOf() 统一映射规则类型
(number -> type:"number",date/datetime -> type:"date",其余保持默认 string)。
注意 async-validator 没有 datetime 类型,写上去会导致 validate 抛异常。
- ParentNotices.vue: 日期字段回填统一转 Date(本地时间语义,避免 new Date("yyyy-MM-dd")
的 UTC 偏移),提交时序列化成 yyyy-MM-dd / yyyy-MM-dd HH:mm。
这同时修掉一处数据污染:此前 String(Date) 会把
"Tue Sep 22 2026 10:33:00 GMT+0800 (中国标准时间)" 这类 locale 脏串写进 fillData。
- NewNotice.vue: 定时发布时间弹窗的规则存在同类问题,补 type:"date"。
- 覆盖四类回执单:stay / visit / adjust 的日期字段曾坏,general 及公共区字段本就正常。
.gitignore 追加 .workbuddy/(本地 AI 工作目录,不入库)。
24 KiB
宿舍通知模块 · 功能与接口说明 v1
模板来源:
D:\项目管理\FamilyActivity(家校活动信息发布) 本模块:D:\项目管理\DormNotice(家校宿舍通知) 技术栈一致:Vue3 + Vite + view-ui-plus + alova + vite-plugin-mock,npm run dev:mock纯前端可跑。
一、为什么不能直接复用「活动」模块
活动与宿舍通知同为「发布 → 家长接收 → 回执」的家校闭环,但收件人粒度和回执形态两处根本差异,决定了它是独立模块而不是活动的换皮:
| 维度 | 活动信息发布 | 宿舍通知 |
|---|---|---|
| 收件人粒度 | 班级(receiveType=class,receiveList 为班级 id 串) |
宿舍房间(receiveType=room,receiveList 为房间 id 串) |
| 应收人数口径 | 班级学生数 | 覆盖房间的实际入住床位数 |
| 回执形态 | 单一:报名(填参加人数 + 随行 + 费用) | 三档:仅通知 / 需确认(打开即确认) / 需填报(统一回执单:公共区固定 + 专属区随单据类型) |
| 回执内容 | 固定字段(joinCount、guardianName、remark) | 动态字段:由发布方声明(留宿起止、探视时段、搬迁日期…) |
| 紧急程度 | 无 | 普通 / 重要 / 紧急(家长端置顶标红) |
| 宿管协同 | 无 | needSupervisor:需宿管到场配合执行 |
| 覆盖统计 | 按班 | 按楼栋覆盖房间数 / 床位数 |
关键业务点: 武校是寄宿制,同一班级的学生分住不同楼栋房间。若沿用「按班级下发」,一条「1号楼 3 层明日停水」会推送给全部家长(大量无关触达),而真正受影响的房间只占少数。因此本模块把发布对象下沉到房间一级,应收人数取房间实际入住床位,回执率才具备管理意义。
二、功能地图(7 个页面)
发布端(教师 / 宿管 / 德育处)
| # | 页面 | 路由 | modId | 说明 |
|---|---|---|---|---|
| 1 | 我发布的 | /list |
2 | 本人发布的通知列表,含草稿;行内可催办、置顶、撤回、删除、复用再发 |
| 2 | 全部通知 | /noticelist |
3 | 全校通知(去掉 scope=user),用于跨部门查询与统计核对 |
| 3 | 发布通知 | /list/new |
22(parentId=2) |
新建 / 编辑(?serid=)/ 复用(?fid=)三态同一组件 |
| 4 | 通知详情 | /list/detail、/noticelist/detail |
23 / 24 | 管理视角(type=0 不标读)与查看视角(type=1 标读) |
| 5 | 回执情况 | /receipts |
25 | 按「楼栋·房间」分组的回执/填报名单,支持代确认、取消、退回、催办、导出 CSV |
| 6 | 通知统计 | /stats |
4 | 总量/回执率/覆盖人次、类型分布、月度趋势、楼栋覆盖、发布人分布 |
| 7 | 宿舍设置 | /setting |
21 | 通知类型字典 + 宿舍楼栋房间台账 + 自定义组(宿管/生活老师) |
家长端(家长 / 学生账号)
| # | 页面 | 路由 | modId | 说明 |
|---|---|---|---|---|
| 8 | 宿舍通知箱 | /par/notices |
31 | 左侧分桶列表(全部/待回执/已回执/已结束)+ 右侧通知详情与回执操作 |
| 9 | 我的回执 | /par/mine |
32 | 同一组件,meta.initTab=done 默认落「已回执」 |
端分离机制(沿用模板)
App.vue 按路由前缀判定视角(/par → parent,其余 → teacher),先调 POST /api/base/SetRole 切 mock 当前用户,再拉 GetUserInfo / GetUserMod。这样同一份代码在家长端与发布端各拿一份身份与菜单,不需要两套工程。
三、发布通知页(模块的复杂度中心)
3.1 表单字段
| 字段 | 控件 | 必填 | 说明 |
|---|---|---|---|
| 通知标题 | Input(≤50 字) | ✅ | 家长端列表主标题 |
| 通知类型 | Select | ✅ | 取自「宿舍设置 → 通知类型」字典(停用项不下发) |
| 发布部门 | Select | ✅ | 校办 / 德育处 / 生活管理科 / 教务处 / 后勤保障部 / 校医务室 / 武术训练部 / 安全管理办公室 |
| 紧急程度 | Radio | — | 普通 / 重要 / 紧急(家长端置顶标红) |
| 生效时间 | DatePicker(datetime) | — | 留空 = 立即生效;用于「明早起执行新作息」提前下发 |
| 回执截止 | DatePicker(datetime) | — | 留空 = 不设截止;早于生效时间会被前端拦下 |
| 回执方式 | Radio | — | none / confirm / fill,见 3.3 |
| 回执单据 | Select(单据类型) | fill 时 ✅ | 只选类型,字段由系统预置模板带出,不可增删改;下方为家长端表单只读预览 |
| 接收宿舍 | 楼栋→楼层→房间勾选弹窗 | ✅ | 或勾「全部宿舍」(receiveList=ALL) |
| 宿管配合 | Switch | — | 需宿管到场配合执行时开启 |
| 置顶展示 | Switch | — | 家长端列表置顶并标「顶」 |
| 短信提醒 | Switch | — | 需学校已开通短信 |
| 附件 | AttachmentUpload | — | 图片/文档,走 /api/base/FileUpload |
| 通知内容 | wangEditor 富文本 | — | 图片走 /api/file/UploadImage |
3.2 接收宿舍选择器(本模块独有)
弹窗左侧楼栋列表(名称 / 房间数 / 入住 N/M),右侧按楼层分组的房间勾选网格,每间显示 房号(已住/总床位);支持「本层全选」「本楼全选」「清空」。底部实时汇总 N 间 · M 名住宿生。
表单区回显为可读摘要:6 间宿舍 · 42 名住宿生|1号楼 305、1号楼 306、2号楼 …。
校验要点:
receiveList挂type: "array", min: 1,「全部宿舍」勾选时清空手工选择并以ALL提交,保证校验能通过(模板里「全部班级」的同一套处理方式)。
3.3 三档回执方式(家校闭环的核心)
| 值 | 家长端行为 | 适用通知类型 |
|---|---|---|
none 仅通知 |
阅读即知悉,不产生回执记录,只统计已读 | 作息调整、停水停电、卫生检查、违规通报、假期安排 |
confirm 需确认 |
打开通知详情即自动确认,无需点击;仅在自动确认失败时给手动兜底按钮 | 安全排查、夜间查寝、宿舍调整(知悉类) |
fill 需填报 |
家长按统一回执单提交(公共区固定 + 专属区随单据类型),截止前可修改,也可取消 | 留宿申请、家长探视、物品清退、宿舍调整(需回执搬迁日期) |
为什么 confirm 要「打开即确认」:让家长为「已阅」多点一次按钮,实际结果是没人点、管理端拿不到真实触达数据。打开即确认把「已读」升级为「已确认知悉」,且顺手省掉一次点击。实现上在 open() 内触发一次 autoConfirmIfNeeded(),提交后只刷新左侧列表与分桶计数、不重新拉详情,避免自我循环。
3.4 统一回执单(公共区 + 专属区)
「需填报」不是自由表单,而是一张统一回执单,分两个区块:
| 区块 | 内容 | 是否随单据变化 |
|---|---|---|
| 公共区 | 学生(花名册带出) / 家长姓名 / 联系电话 / 回执结论(同意 / 不同意 / 需沟通) | ❌ 所有单据完全一致 |
| 专属区 | 单据登记项(留宿起止、到访时段、搬迁日期等) | ✅ 随单据类型 formType |
四类预置单据:
| 单据 | 专属区字段 | 适用 |
|---|---|---|
stay 留宿申请单 |
留宿开始日期* / 留宿结束日期* / 留宿人数*(人) / 留宿事由*(家远不便往返、赛前集训、伤病康复观察、学业补差、其他) | 周末 / 假期留宿申请 |
visit 探视预约单 |
到访日期* / 到访时段*(上午 / 下午 / 晚间) / 到访人数*(人) | 家长到校探视预约 |
adjust 调宿申请单 |
可配合搬迁日期* / 情况说明 | 床位重排、宿舍调换确认 |
general 通用回执单 |
无(仅公共区) | 只需家长给结论的场景 |
发布人只能选单据类型,不能增删字段:字段清单由后端按 formType 生成(buildFormFields()),发布页表单只上传 formType,不传字段定义。发布页下方为只读预览(公共区 chips + 专属区 chips),单据类型由 GET /api/notice/FormTemplates 下发(结构见 ReceiptFormTemplate)。
为什么不做自由表单:自由拼字段会让每条通知的字段名、字段数、表头各不相同 —— 回执落成
fillData的 JSON 后无法跨通知合并统计,导出的 CSV 表头也对不上。而本模块的回执只有confirmed / filled / canceled三态、没有审批下游,字段结构化的意义只剩「汇总」;既然只为汇总,口径就必须固定。
字段模型:
FillField = { key, label, type, required, placeholder?, options?, unit?, group },其中 group ∈ common|specific,type ∈ text|number|date|datetime|select|textarea。
家长端按 group 分两段渲染(先公共区、后专属区),按 type 渲染控件,select 回显时把 value 反查为 label。
提交时按后端口径统一转成字符串 Map(Map<String,String>),避免数字/日期序列化口径不一致。
3.5 通知流程状态
draft 草稿 → published 已发布 → closed 已截止 → finished 已结束。
草稿不产生回执记录、家长端不可见;撤回(UserPull)把状态置为 closed 并标 isPulled=1,家长端不再展示,但已提交的回执记录保留以便追溯。
四、回执情况页
- 顶部:通知选择(仅列出
receiptMode != none且非草稿的本部门通知)+ 状态过滤(全部/已确认/已填报/已取消/未回执)+ 催办 + 导出。 - 汇总条:应收(住宿生)/ 已回执 / 已确认 / 已填报 / 已取消 / 未回执 / 回执率 / 覆盖宿舍数 / 覆盖床位数。
- 名单:按
楼栋名 · 房间号分组,组内每名学生一行 —— 学生 / 床位 / 家长 / 电话 / 关系 / 回执结论(公共区,独立成 Tag 列)/ 单据登记项(专属区字段展开)/ 状态 / 回执时间 / 备注。 - 行操作:
- 回执详情 —— 弹窗展示家长信息 + 按「公共区 / 专属区」两段展开的填报内容 + 已读时间;
- 代为确认 —— 家长电话口头确认等系统外已告知的情形,管理员代录(需填报类会同时写入结论,避免产生「已填报但内容为空」的空单);
- 取消/撤回 —— 家长申请不参与(不留宿、不探视)时置为已取消并留痕;
- 退回未回执 —— 置回
none,家长端可重新提交。
- 导出 CSV:列顺序固定为
房间/床位/学生/班级/单据类型/家长/关系/电话+ 公共区(回执结论) + 专属区字段 +状态/时间/备注,带 BOM 供 Excel 直接打开;同类单据导出的列完全相同,可纵向合并。
五、宿舍设置页(3 个 Tab)
- 通知类型 ——
TypeSetBox卡片 +vuedraggable拖动排序;启停状态影响发布下拉,删除不影响历史通知。 - 宿舍楼栋房间 —— 左侧楼栋台账(房间数、入住率、宿管),右侧该楼房间表格(
房号 / 楼层 / 入住 N/M 床(满)/ 宿舍长 / 宿管)。- 新增楼栋按「楼层数 × 每层房间数 × 每间床位数」批量铺房间,新楼栋按空楼创建(
usedBeds=0)——真实流程是先建房间、再由床位安排流程写入住宿生; - 新增房间从该层已有房号往后追加,避免重号;
- 改床位数收紧时,装不下的学生退回待安排池(不再凭空裁掉);
- 删除房间/楼栋均有保护:房间内仍有学生、楼栋下仍有房间时后端拒绝并给出可读原因。
- 新增楼栋按「楼层数 × 每层房间数 × 每间床位数」批量铺房间,新楼栋按空楼创建(
- 自定义组 —— 宿管 / 生活老师联系组,成员从基础平台教师账号中选取(
UserCheckModal)。
5.1 房间人员(住宿生)与宿舍长
「人员怎么进房间」在本模块只有一条链路,全部收在房间行的「住宿生/床位」入口里:
| 动作 | 效果 | 约束 |
|---|---|---|
| 安排入住 | 从「待安排」名单(学籍在册未分配 / 退宿退回 / 转学插班)勾选学生调入本房 | 不得超过空床位数;按空床位顺序落位;性别与楼栋校验(女生楼不收男生,反之亦然) |
| 调整宿舍 | 房内学生转到其他房间(可跨楼栋) | 目标房需有空床位,满房选项置灰;原房为宿舍长时,身份顺延给同寝第一位 |
| 退宿 | 学生移出房间并退回待安排池(来源标注「退宿待重新安排」) | 退宿后床位号自动顺延补齐,不留空洞床位 |
关键口径:usedBeds 恒等于花名册长度(单一数据源)。早期实现是反过来的 —— 按 usedBeds 反向凭空生成学生,导致新建房间/调大床位会「自动变出不存在的人」。现已收口:房间人数只能由床位安排接口驱动。
宿舍长(isMonitor)显式落在花名册数据上,不再靠「数组第一位」隐式约定:
- 房间表格「宿舍长」列显示当前宿舍长,未指定显示灰字「未指定」;
- 每间房至多 1 人,在住宿生名单里点「设为宿舍长 / 取消宿舍长」改派(
SetMonitor,取消传studentId=0); - 宿舍长退宿或调出时,身份自动顺延给同寝第一位学生。
待安排学生池(PendingStudents)是安排入住的数据源:初始 46 人(姓名按性别分池,避免「周雨萱(男)」这类错配),配合「来源」列区分新生未分配 / 退宿待重新安排 / 楼栋调整待安置 / 转学插班待安排。
六、接口清单
统一响应 { code, msg, data },code==0 时 alova 拦截器解包为 data。GET 走 query,POST 分两种(postJSON 对齐 @RequestBody,postForm 对齐 @RequestParam)。
基础 / 身份
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/base/GetUserInfo |
当前登录用户(教师 / 家长) |
| GET | /api/base/GetUserMod |
已授权菜单(modId + page) |
| POST | /api/base/SetRole |
端分离切视角(as=teacher/parent) |
| GET | /api/base/GetSchoolAll |
教师组织树(联系组选人) |
| GET | /api/base/GetDormTree |
楼栋→楼层→房间树(叶子带 bedCount / usedBeds) |
宿舍台账
GET /api/dorm/BuildingList、GET /api/dorm/RoomList?buildingId=&keyword=、GET /api/dorm/RoomStudents?roomId=(含 isMonitor)、
POST /api/dorm/AddBuilding|EditBuilding|DelBuilding、POST /api/dorm/AddRoom|EditRoom|DelRoom
床位安排 / 宿舍长
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/dorm/PendingStudents?keyword=&gender= |
待安排学生池(安排入住的数据源) |
| POST | /api/dorm/AssignStudent |
安排入住(roomId + studentIds 逗号串),先整体校验性别/空床位再落位,避免半截数据 |
| POST | /api/dorm/RemoveStudent |
退宿(roomId + studentId),学生退回待安排池,床位号顺延 |
| POST | /api/dorm/TransferStudent |
调整宿舍(studentId + fromRoomId + toRoomId),返回 { wasMonitor, bedNo } |
| POST | /api/dorm/SetMonitor |
设 / 取消宿舍长(roomId + studentId,取消传 0) |
通知
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/notice/SendList |
我发布的(scope=user)/ 全部通知;支持 keyword、noticeType、level、buildingId、noticeStatus、时间区间、orderByTime |
| GET | /api/notice/List |
家长端收件箱(仅已发布、非未来定时、面向本生房间),额外返回 unreadCount |
| GET | /api/notice/Detail |
详情(type=1 标读并同步标记该房间学生的已读;type=0 管理视角不标读) |
| GET | /api/notice/GetDetail |
编辑回显(不标读) |
| GET | /api/notice/FormTemplates |
单据模板列表(统一回执单选型:公共区 + 各类单据的专属区字段,发布人只能选不能改) |
| GET | /api/notice/GetNextPrev |
上一篇 / 下一篇 |
| GET | /api/notice/ReadDetail |
阅读情况(按楼栋分组,叶子为房间名 + 人数) |
| POST | /api/notice/Create |
新建(JSON body,返回新 id) |
| POST | /api/notice/Edit |
编辑(JSON body,含 sourceId) |
| POST | /api/notice/Delete |
删除(JSON 数组 id,级联删回执) |
| POST | /api/notice/UserPull |
撤回 |
| POST | /api/notice/SetTopmost |
置顶 / 取消置顶 |
| POST | /api/notice/Expedite |
催办未读 |
| POST | /api/notice/ReceiveRooms |
追加接收房间(补发) |
| POST | /api/notice/InitializeRed |
一键全部已读 |
回执 / 填报
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/notice/Receipts?serid=&status= |
回执名单 + 汇总(按 楼栋 · 房间 分组),含 formType / formTypeDesc / formFields |
| GET | /api/notice/ReceiptDetail?receiptId= |
单条回执详情(含 fillData、formType 与 formFields) |
| POST | /api/notice/Review |
处理回执(JSON):action=confirm/cancel/reset;需填报类代确认时会写入公共区结论 |
| POST | /api/notice/Remind |
向未回执家长发送提醒,返回 { reminded } |
| GET | /api/notice/ExportReceipts |
导出 CSV 文本(固定列序:固定列 → 公共区 → 专属区 → 回执信息) |
| GET | /api/notice/MyNotices |
家长端通知箱(含 buckets: {todo, done, finished, all}) |
| GET | /api/notice/MyNotice?serid= |
家长端某通知详情(通知信息 + 我的回执 + formType + formFields),打开即标读 |
| POST | /api/notice/SubmitReceipt |
提交回执 / 填报(JSON:serid, fillData, guardianName, guardianPhone, relation, remark;fillData 为字段键 → 值) |
| POST | /api/notice/CancelReceipt |
取消回执 |
统计 / 字典 / 联系组 / 文件
GET /api/notice/Statis、GET /api/dict/List?type=dept|noticeType、GET /api/dict/Sync、
GET /api/dict/ManageList、POST /api/dict/Add|Update|Delete|Sort、
GET /api/contacts/GetGroup|GetContactsTea|GetGroupTree、POST /api/contacts/AddGroup|EditGroup|DelGroup|SetContactsTea、
POST /api/base/FileUpload、POST /api/file/UploadImage、GET /api/file/Preview|Down、GET /api/notice/AllFileDown
七、mock 数据说明
src/assets/mock/dormapi.ts(约 2200 行)覆盖上表全部接口,要点:
- 确定性伪随机:
seededRand(sin(seed)),同一房间/通知的入住学生、回执率刷新不跳变,适合演示与截图。 - 楼栋与房间:4 栋楼(1/2 号男生 6+5 层×8 间×8 床,3 号女生 4 层×6 间×6 床,4 号武术特长班 3 层×6 间×4 床)。
roomId = buildingId*10000 + floor*100 + index(如10305= 1 号楼 3 层 05 房),房间号即305。 - 住宿生花名册:1 床 1 生,含姓名(武校风格,男/女名字分池)、班级(文化班 + 套路表演班/散打竞技班/拳击班混合)、床位号、家长姓名/关系/电话、是否宿舍长
isMonitor(默认首位,可改派)。 - 待安排学生池:46 人(
studentId段900001+,与房内学生roomId*100+i不冲突),带来源标注;退宿 / 床位缩减的学生回流到此池。 - 床位一致性:
usedBeds由花名册长度派生,rebuildRoomIndex()不再按床位数反向生成学生。 - 通知数据集:覆盖 13 类通知 × 4 种流程状态 × 3 档回执方式 × 3 种紧急程度,含草稿、已撤回、定时未到、已截止等边界样本。
- 回执生成:按通知状态给不同回执率(已结束 ~95%、已发布 ~60%、已截止 ~85%),已结束类回执时间落在截止日前;填报内容按
formFields逐字段造合理值(留宿起止、探视时段、搬迁日期等),并派生progressOf()汇总(应收/已确认/已填报/已取消/未回执/回执率/房间数/床位数)。 - 家长端演示身份:
ME_ROOM = "10305"(1 号楼 305 室第一位学生),家长端只看该房间命中的通知;?serid=打开即标读。 - 写操作真实生效:Create/Edit/Delete/Review/SubmitReceipt/CancelReceipt/ReceiveRooms 以及宿舍台账的 Add/Edit/Del、床位安排的 Assign/Remove/Transfer/SetMonitor 都会改内存数据并同步重建回执,页面刷新后行为一致(重启 dev server 复位)。
八、沿用模板的既有约定(踩坑记录)
这些是模板项目已经踩过并写进注释的坑,本模块原样继承:
<FormItem required>必须配prop—— 否则字段名被解析成undefined,model 取到undefined,家长填了值也照样报错、表单永远提交不了。- 非 string 取值的控件,规则里必须显式写
type—— async-validator 只在规则「除掉 message 后只剩 required」时走 required 校验器;一旦带trigger,会回退到default validators[type || 'string'],再拿typeof value !== 'string'判类型不符,而真实报错会被自定义message覆盖,于是「明明填了值,红字仍显示『请填写XX』、表单交不上去」,极具误导性。InputNumber→type: "number";DatePicker→type: "date"(v-model 给的是Date对象;async-validator 没有datetime这个类型,写上去getType()直接抛Unknown rule type datetime,整个 validate 崩掉,被.catch(() => false)吞成「提交失败」);Select/Input/Textarea→ string,默认即可。- 配套:日期字段在模型里统一存
Date、提交时再格式化成yyyy-MM-dd/yyyy-MM-dd HH:mm(本地时间手工拼,别new Date("2026-09-23")—— 按 UTC 解析会跑日期),否则String(Date)会提交成"Tue Sep 22 2026 10:33:00 GMT+0800 (中国标准时间)",回执名单与导出 CSV 全废。 - 30 秒定位法:在 node 里用项目自己的
node_modules/async-validator复刻FormItem.validate()(descriptor[prop] = rules、model[prop] = fieldValue、{ firstFields: true }),逐个类型跑一遍,比读代码猜快得多。
- 表单模型是稳定的
reactive对象,不能写成computed(() => ({...}))—— 每次依赖变化生成新对象,而FormItem校验读的是Form.model,props 要等父组件重渲染才更新,同步 change 校验拿到的还是旧对象(「选了却红字不消失」)。 rules写成普通常量/computed要分清场景 —— Form 内有watch: { rules () { this.validate() } },引用一变就全量重校验。- 存草稿只校验标题,并用
clearFieldState()清掉其它字段的残留红字。 postForm同时把参数拼进 URL query —— 后端@RequestParam收得到,且vite-plugin-mock不解析 urlencoded body,靠 query 才能拿参。- 面包屑/菜单高亮靠
meta.parentId,发布通知/详情不在菜单树里也能正确高亮父菜单「我发布的」。 menuRoute.ts做模块配置兜底解析(平台模块 path 格式不可控),NAME_ALIASES已按宿舍口径改写,并兼容旧/notice、/dorm、/activity前缀。
九、后续接入后端的改造点
vite.config.ts的/api代理 target 换成本模块后端上下文(现仍指向模板的familyactivity)。src/assets/config/menu.ts是本地菜单兜底,接入后端GetUserMod后以接口返回为准(modId契约见路由表)。src/assets/mock/index.ts是vite build的 prod mock 入口,正式构建不启用 mock(localEnabled: mode === "mock")。GetDormTree已按 ztree 结构返回,若后续要用 ztree 选房间(而非当前的自绘楼栋+楼层勾选),可直接接入src/components/MyTree*.vue。