8.4 KiB
8.4 KiB
家校活动信息发布(family-activity)
武校家校信息发布模块 —— 活动信息发布 子模块。用于学校(德育处 / 武术训练部 / 教务处 / 校医务室等)向家长发布活动通知, 并回收家长的阅读已读与报名回执。
以
D:\项目管理\CourseHomework(课程作业)为模板工程改造:保留其 Vue3 + Vite + view-ui-plus + alova + vite-plugin-mock 技术栈、布局壳层、权限菜单契约、表格/分页/选人/富文本/附件等基础设施,业务域整体替换为「家校活动」。
快速开始
# 纯前端演示(内置 mock 数据,无需后端)
npm run dev:mock
# 联调本地后端
npm run dev
# 构建(产物直出 dist/)
npm run build
mock 模式访问:http://localhost:8078/
功能范围
发布端(教师 / 教练 / 处室)
| 菜单 | 路由 | 说明 |
|---|---|---|
| 我发布的 | /list |
自己发布的活动列表,按流程状态分标签(全部/报名中/已截止/已结束/草稿) |
| 全部活动 | /activitylist |
管理端全量列表(同一组件,去掉 scope=user) |
| 发布活动 | /list/new |
新建 / 编辑 / 复用再发;支持存草稿与定时发布 |
| 活动详情 | /list/detail、/activitylist/detail |
type=0 管理视角(阅读进度 + 回执进度 + 编辑/打印/复用);type=1 查看视角 |
| 报名情况 | /signups |
家长回执名单:按班级分组的汇总卡 + 回执状态筛选 + 回执详情 + 代为确认/取消 + 催报名 + 导出 CSV |
| 活动统计 | /stats |
总量卡、活动分类分布、月度发布趋势、按发布人统计、我发布活动的回执与名额 |
| 应用设置 | /setting |
主办部门(字典,可拖拽排序/启停)+ 自定义组(联系组与成员) |
家长端(家长 / 学生,演示用)
| 菜单 | 路由 | 说明 |
|---|---|---|
| 活动广场 | /par/activities |
左列表右详情,按「全部 / 待回执 / 已报名 / 已结束」筛选,阅读即标读 |
| 我的报名 | /par/mine |
同组件,默认落「已报名」标签 |
| 发布端(教师) | /list |
从家长端切回发布端的入口(mock 演示便利项) |
业务模型要点
- 发布对象到班级一级:
receiveList = "ALL"或班级 id 逗号串;应收人数、已读人数按班级学生数累加(家长即挂在学生档案上的联系人)。 - 回执方式
receiptMode:none仅通知 —— 只统计已读,不产生报名记录(如放假通知、安全演练告知);signup需报名 —— 家长填报参加人数(可含随行家长),可设名额上限与随行开关;confirm需确认 —— 家长端打开活动详情即自动确认,无需任何点击(如家长会、考核反馈)。
- 流程状态
actStatus:draft草稿 →signup报名中 →closed已截止 →finished已结束。 - 回执状态
status:none未回执 /signed已报名 /confirmed已确认 /canceled已取消(请假)。 - 名额:
quota为 0 表示不限;按家长填报的参加人数累计占用,满额后拒绝新报名。
目录结构
src/
├── api/module/activity.ts # 活动域接口封装(get/postJSON/postForm 三件套,统一 {code,msg,data} 解包)
├── assets/
│ ├── config/menu.ts # 左侧菜单(对接后端 GetUserMod 前的兜底)
│ ├── mock/activityapi.ts # 本地 mock:种子数据 + 全部接口实现
│ └── styles/ # 平台主题 / 布局 / iconfont(沿用模板)
├── components/ # 布局壳层、表格、选人、上传、富文本附件等(沿用模板)
├── router/routes.ts # 扁平短路由 + modId 契约(菜单 ↔ 路由)
├── types/activity.ts # 活动域 TS 契约(对齐后端 VO/DTO)
└── views/activity/ # 业务页面(列表 / 发布 / 详情 / 报名 / 统计 / 设置 / 家长端)
mock 演示数据
src/assets/mock/activityapi.ts 内置一套武校家校场景数据:
- 学校:少林文武学校;班级:小学部(三/四年级)、初中部(初一)、武术部(套路表演班、散打竞技班),共 7 个班 259 名学生;
- 8 条活动:金秋武术套路比赛、秋季家长会、国庆离校告知、体质健康测试、中秋文艺汇演、新生基本功考核、嵩山研学(草稿)、消防疏散演练;
- 每条活动按班级花名册生成完整回执记录(含家长姓名、手机号、参加人数、备注),并派生回执率与名额占用;
- 当前登录身份:德育处 王建山(发布端);家长端演示学生:三年级(1)班 第 1 位学生。
与模板工程的差异
| 方面 | 模板(课程作业) | 本工程(家校活动) |
|---|---|---|
| 域模型 | 作业 / 提交 / 批改 | 活动 / 回执报名 / 名额 |
| 回执闭环 | 学生提交作业 → 教师批改 | 家长阅读 + 报名回执 → 学校核验/催办/导出 |
| 菜单 modId | 1/2/3/4/21/25/26/31/32 | 2/3/4/21/25/31/32(+22/23/24 深层子页) |
| 家长端前缀 | /stu/*(学生端) |
/par/*(家长端) |
| 端口 | 8077 | 8078 |
| 已下线 | 审批流、批改中心、学生端提交 | —— |
交互实现要点(易踩坑,改动时别再踩回去)
下列几处是 view-ui-plus 的 API 契约(不是业务代码)导致的坑,已在工程内固化做法。
排查同类问题时先读 node_modules/view-ui-plus/src/components/<组件>/*.vue,再怀疑业务代码。
| 现象 | 根因 | 现行做法 |
|---|---|---|
| 必填项「选完/填完值,红字还挂着」 | FormItem.getFilteredRule(trigger) 只保留 !rule.trigger || rule.trigger 命中 的规则。控件只发它自己那种事件(Select/DatePicker/RadioGroup/Number 发 change,Input 发 change+blur):规则写了 blur 就漏掉 change,写了 change 就漏掉 blur,校验根本不执行,上一次的红字原地不动 |
规则一律不写 trigger(NewActivity、ParentActivities、ActivitySetting、DictCardManager 均已去掉)。数字字段必须显式 type: "number" |
| 同上,但值来自程序化赋值 | 勾「全部班级」把数组填满、编辑/复用回显、弹窗回填都不产生控件事件,trigger 修法也救不了 | Utils.clearFilledValidateState(formRef, props) 按「值已填但仍标 error」兜底清状态;发布页 watch 字段值 + nextTick 调用 |
| 点左边栏「右边不刷新,点两次才刷」/ 家长端点菜单没反应 | /list ↔ /activitylist、/par/activities ↔ /par/mine 各自共用同一组件,Vue 复用实例 → setup()/onMounted 不重跑;点当前项又走 router.go(0)(整页 reload、白屏一下) |
App.vue 给 <router-view> 加 :key="viewKey"(route.fullPath + tick)并提供 refreshView;layout.vue 的 linkMenu 点当前页时调 refreshView 而非 router.go(0) |
| Tabs 高亮不跟随变量/路由 | Tabs 的 props 里没有 value,只有 modelValue。:value 会被当成普通 HTML 属性 fallthrough 到根元素,高亮永远退回第一个 tab(点击有反应是因为组件内部自己改 activeKey,所以只表现为「点进来高亮不对」) |
统一 v-model / :model-value |
| 表格压住分页 | 表格高度用 Utils.useTableHeight({...}) 常量估算,漏算 Tabs 栏与容器内边距(约 78px),而 .page-panel 是 absolute 吸底 |
列表页改用真实 DOM 实测:分页顶边 − 表格顶边 − 12 − (列宽合计超出容器 ? 15 : 0);onMounted + resize 重算 |
| 表头挤成两行 | 为了「收进一屏」合并列 / 把列宽压太窄 | 让表格横向滚动(列宽给足),操作列 fixed: "right";序号列宽 ≥ 76px(单元格左右各 18px padding,「序号」两字约需 36px)。横向滚动条样式在 App.vue 里覆写 .ivu-table-overflowX::-webkit-scrollbar(8px 圆角) |
| 附件名挤在「选择文件」按钮右边 | Upload 根节点内部是 .ivu-upload-select + ul.ivu-upload-list 两个兄弟节点;根节点一旦是 flex/inline-flex,ul 作为 flex item 会被排到按钮同排 |
.ivu-upload 保持 display: block(关键),只让 .ivu-upload-select 内部 inline-flex 对齐;.ivu-upload-list 另起一行、每项独占一行(见 components/AttachmentUpload.vue) |