|
|
@@ -0,0 +1,159 @@
|
|
|
+# 小程序活动模块遗留项核验报告
|
|
|
+
|
|
|
+> 日期:2026-10-24
|
|
|
+> 核验范围:`cfc-frontend/pages/activity/index.vue` + 活动模块相关前后端代码
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 任务 A:`loadData()` 修复核验
|
|
|
+
|
|
|
+### 修复内容(用户描述)
|
|
|
+
|
|
|
+将 `loadData()`(约 462-488 行)改为:先重置当前 tab 的页码为 1、清空对应数组、再调用 `loadXxxActivities()` 加载第一页。
|
|
|
+
|
|
|
+### 实际代码(index.vue 第 462-488 行)
|
|
|
+
|
|
|
+```js
|
|
|
+loadData: function() {
|
|
|
+ if (this.currentTab === 'mine') {
|
|
|
+ this.minePage = 1
|
|
|
+ this.myActivities = []
|
|
|
+ this.hasMore = true
|
|
|
+ this.loadMyActivities()
|
|
|
+ } else if (this.currentTab === 'discover') {
|
|
|
+ this.discoverPage = 1
|
|
|
+ this.discoverActivities = []
|
|
|
+ this.hasMore = true
|
|
|
+ this.loadDiscoverActivities()
|
|
|
+ } else if (this.currentTab === 'history') {
|
|
|
+ this.historyPage = 1
|
|
|
+ this.historyActivities = []
|
|
|
+ this.hasMore = true
|
|
|
+ this.loadHistoryActivities()
|
|
|
+ } else if (this.currentTab === 'manage') {
|
|
|
+ if (this.canManageActivity) {
|
|
|
+ this.loadManageData()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 问题 1:从详情页返回走的确实是 onShow → loadData 路径
|
|
|
+
|
|
|
+**证据(index.vue 第 391-398 行):**
|
|
|
+
|
|
|
+```js
|
|
|
+onShow: function() {
|
|
|
+ if (this._onLoadFired) {
|
|
|
+ this._onLoadFired = false // 首次进入:onLoad 已加载,跳过避免双发
|
|
|
+ } else {
|
|
|
+ this.resolveManagePermission()
|
|
|
+ this.loadData() // ← 切 Tab/从子页返回时走这里
|
|
|
+ }
|
|
|
+},
|
|
|
+```
|
|
|
+
|
|
|
+详情页跳转用 `uni.navigateTo`(非 tab 页面),返回时触发 `onShow`;首次进入时 `onLoad` 已设 `_onLoadFired = true`,`onShow` 跳过。后续返回时标志已被重置为 `false`,会真正调用 `loadData()`。✅
|
|
|
+
|
|
|
+**关键:loadXxxActivities 在非 refresh 分支会 concat,但 loadData 先清空了数组,所以 concat 后不会叠加。**
|
|
|
+
|
|
|
+`loadDiscoverActivities` 非 refresh 分支(第 754-755 行):
|
|
|
+```js
|
|
|
+self.discoverActivities = self.discoverActivities.concat(list)
|
|
|
+```
|
|
|
+此时 `discoverActivities` 已被 `loadData()` 初始化为 `[]`,concat 结果 = `list`,不会翻倍。✅
|
|
|
+
|
|
|
+### 问题 2:各路径行为核验
|
|
|
+
|
|
|
+| 路径 | 实际行为 | 是否正确 |
|
|
|
+|------|---------|---------|
|
|
|
+| **下拉刷新** `onRefresh()` (659-677 行) | 调用 `loadXxxActivities(true)` → refresh=true 分支清空数组 + page=1 | ✅ 正确 |
|
|
|
+| **加载更多** `onLoadMore()` (646-658 行) | 递增 page++ → 调用 `loadXxxActivities()` 无 refresh → concat 追加 | ✅ 正确 |
|
|
|
+| **Tab 切换** `onTabChange()` (447-460 行) | 先清空数组+page=1,再调 `loadData()` → loadData 内部不再重复清空(不影响) | ✅ 正确(轻微冗余但无害) |
|
|
|
+| **维度筛选切换** `onDimensionChange()` (639-644 行) | 先清空 discover 数组+page=1,再调 `loadDiscoverActivities()` 无 refresh,concat 到空数组 | ✅ 正确 |
|
|
|
+| **mine 筛选切换** `onMineFilterChange()` (628-638 行) | 同维度筛选逻辑,直接调 `loadXxxActivities()` | ✅ 正确 |
|
|
|
+| **发布成功后 switchToDiscover** (831-837 行) | 先清空数组+page=1,再调 `loadDiscoverActivities()` 无 refresh | ✅ 正确 |
|
|
|
+| **manage 权限解析后回落** `onPermissionResolved()` (434-438 行) | 切换 tab 后调 `loadData()`,mine 分支会清空并加载 | ✅ 正确 |
|
|
|
+
|
|
|
+### 问题 3:语法检查
|
|
|
+
|
|
|
+运行 `node scripts/verify-vue-compile.js pages/activity/index.vue`:
|
|
|
+
|
|
|
+```
|
|
|
+校验文件数: 1
|
|
|
+ 模板通过: 1 样式通过: 1 脚本通过: 1
|
|
|
+PASS
|
|
|
+```
|
|
|
+
|
|
|
+无语法错误、无未定义变量、this 指向正确(全用 `var self = this` 或箭头函数)。✅
|
|
|
+
|
|
|
+### 任务 A 结论:通过 ✅
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 任务 B:第 3 项需求完成度核验
|
|
|
+
|
|
|
+原始需求三点:
|
|
|
+1. ① 活动支持添加「活动简介」
|
|
|
+2. ② 活动详情页的「海报预览」要重新设计
|
|
|
+3. ③ 后台可以对海报模板进行定义
|
|
|
+
|
|
|
+### ① 活动简介字段
|
|
|
+
|
|
|
+| 位置 | 状态 | 证据 |
|
|
|
+|------|------|------|
|
|
|
+| 后端 Entity | ✅ 已有 | `Activity.java` 第 23 行:`private String intro;` |
|
|
|
+| 后端 DTO | ✅ 已有 | `ActivityDTO.java` 第 13 行:`private String intro;` |
|
|
|
+| 数据库 Schema | ✅ 已有 | `schema.sql` 第 2134 行:`intro VARCHAR(200) DEFAULT NULL COMMENT '活动简介(列表卡片展示)'` |
|
|
|
+| Web 管理端表单 | ✅ 已有 | `ActivityEdit.vue` 第 84-86 行:`<el-form-item label="活动简介">` + `v-model="form.intro"` + maxlength=200 |
|
|
|
+| Web 管理端保存 | ✅ 已有 | `ActivityEdit.vue` 第 496 行:payload 含 `intro: this.form.intro` |
|
|
|
+| 小程序活动列表 | ✅ 展示 | `index.vue` 第 140 行:`<text class="card-desc" v-if="act.intro || act.description">{{ act.intro || act.description }}</text>` |
|
|
|
+| 小程序创建活动页 | ❌ **没有** | `create.vue` 无 `intro` 输入框,仅有 `description`(第 108-112 行) |
|
|
|
+| 小程序详情页海报 | ⚠️ 传的是 description 而非 intro | `activity-detail.vue` 第 300 行:`:intro="(activity && activity.description) || ''"` — 把 description 赋给了 poster 的 intro prop |
|
|
|
+
|
|
|
+**小结**:后端字段、Web 管理端录入、列表展示均已实现。**小程序创建页缺录入入口,详情页海报传了 description 而非 intro**。
|
|
|
+
|
|
|
+### ② 详情页「海报预览」重新设计
|
|
|
+
|
|
|
+| 位置 | 状态 | 证据 |
|
|
|
+|------|------|------|
|
|
|
+| 小程序详情页「生成海报」按钮 | ✅ 有 | `activity-detail.vue` 第 132 行:`<button class="share-btn" @click="generatePoster">生成海报</button>` |
|
|
|
+| 海报组件 | ✅ 已重设计 | `ActivityPoster.vue`(755 行):Canvas 绘制,三套模板引擎(gradient/minimal/card),支持模板选择、背景色切换、二维码 CTA |
|
|
|
+| 模板选择行 | ✅ 有 | `ActivityPoster.vue` 第 18-32 行:`tplOptions` 渲染管理端模板列表 |
|
|
|
+| 背景色板 | ✅ 有 | `ActivityPoster.vue` 第 34-46 行:渐变模板下可切换背景色 |
|
|
|
+| 保存功能 | ✅ 有 | `ActivityPoster.vue` 第 49 行:「保存到相册」按钮 |
|
|
|
+
|
|
|
+**小结**:海报预览已重设计为独立的 `ActivityPoster.vue` Canvas 组件,支持模板选择与配色定制。**已完成** ✅。
|
|
|
+
|
|
|
+### ③ 后台可以对海报模板进行定义
|
|
|
+
|
|
|
+| 位置 | 状态 | 证据 |
|
|
|
+|------|------|------|
|
|
|
+| 后端 Entity | ✅ 已有 | `ActivityPosterTemplate.java`(53 行):name/templateKey/mainColor/darkColor/lightColor/bgColors/qrSize/sortOrder/status |
|
|
|
+| 后端 Service | ✅ 已有 | `ActivityPosterTemplateService.java`:listAll/listEnabled/save/delete |
|
|
|
+| 后端 Admin Controller | ✅ 已有 | `AdminPosterTemplateController.java`:`POST /api/admin/poster-template/list|save|delete` |
|
|
|
+| 后端公开 Controller | ✅ 已有 | `PosterTemplateController.java`:`POST /api/poster-template/list`(小程序端调用) |
|
|
|
+| 数据库表 | ✅ 已有 | `schema.sql` 第 5409 行:`CREATE TABLE activity_poster_templates` |
|
|
|
+| 迁移脚本 | ✅ 已有 | `DatabaseInitializer.java` 第 3106-3107 行:迁移 381 建表 + 初始化 3 套种子模板;第 3139-3141 行:迁移 388 给 activities 加 `poster_template_id` 列 |
|
|
|
+| Web 管理端页面 | ✅ 已有 | `PosterTemplateManage.vue`(360 行):列表+新增/编辑/删除弹窗,支持颜色选择器、QR 尺寸、排序、启用/停用 |
|
|
|
+| 路由注册 | ✅ 已有 | `router/index.js` 第 723 行:`path: 'poster-template-manage'`,菜单入口在 `Layout.vue` 第 246 行 |
|
|
|
+| 小程序端接入 | ✅ 已有 | `activity-detail.vue` 第 808 行:`getPosterTemplates()` 拉取模板列表传给 `<ActivityPoster :templates="posterTemplates">` |
|
|
|
+
|
|
|
+**小结**:后台海报模板定义功能(CRUD + 启用/停用 + 颜色/尺寸配置)**已完整实现** ✅。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 最终结论
|
|
|
+
|
|
|
+### 任务 A:✅ 通过
|
|
|
+- 路径核验通过:onShow → loadData 路径正确,fix 不会再 concat 叠加
|
|
|
+- 6 条路径行为均正确(下拉刷新/加载更多/Tab 切换/维度筛选/mine 筛选/switchToDiscover)
|
|
|
+- 语法编译通过
|
|
|
+
|
|
|
+### 任务 B 三项子需求完成度
|
|
|
+
|
|
|
+| 子需求 | 完成度 | 说明 |
|
|
|
+|--------|--------|------|
|
|
|
+| ① 活动支持添加「活动简介」 | **部分完成** ⚠️ | 后端字段/DB/Web 管理端/列表展示均已完成;**小程序创建页缺少 intro 输入框**;**详情页海报传的是 description 而非 intro**(`activity-detail.vue:300`) |
|
|
|
+| ② 详情页海报预览重新设计 | **已完成** ✅ | `ActivityPoster.vue` 是全新 Canvas 组件,支持三套模板引擎、模板选择、背景色切换 |
|
|
|
+| ③ 后台自定义海报模板 | **已完成** ✅ | 后端 CRUD + 管理端 `PosterTemplateManage.vue` + 小程序端接入均已到位 |
|