# 积分模块新增页面 — Coding Plan > 版本:v1.0 > 日期:2026-03-30 > 范围:管理后台(backend-adminend)新增积分订单、用户积分、用户积分明细三个独立页面 --- ## 1. 需求概述 在管理后台中新增三个独立页面,用于积分业务的外部查看与运营。所有页面需跳过用户登录验证,按后端 API 最小修改原则,尽量复用现有后端接口。 | 序号 | 页面 | 参考原页面 | 说明 | |------|------|-----------|------| | 1 | 积分订单 | `/order/index` | 新建独立页面,展示积分相关订单 | | 2 | 用户积分 | `/user/index` | 新建独立页面,增加 `wa_users` 相关字段 | | 3 | 用户积分明细 | 用户管理 → 账户详情 → 积分明细 | 子页面,复用 `/admin/user/integral/list` 接口 | --- ## 2. 技术架构分析 ### 2.1 技术栈 管理后台前端基于 Vue 2 + Vue CLI + Element UI + Vue Router (history mode) + Vuex + Axios。 ### 2.2 现有认证机制 认证逻辑位于 `src/permission.js`,通过 `router.beforeEach` 全局守卫实现。未登录时除白名单路由外,一律重定向至 `/login`。 白名单当前值:`['/login', '/auth-redirect']` 请求拦截器(`src/utils/request.js`)会在 header 中附加 `Authori-zation` token,后端返回 401 时自动跳转登录页。 ### 2.3 关键参考文件 | 文件 | 说明 | |------|------| | `src/views/order/index.vue` | 订单列表页,约 40k 行,含筛选/表格/分页/操作 | | `src/views/user/list/index.vue` | 用户管理页,含多条件筛选、用户详情弹窗 | | `src/views/user/integral/index.vue` | 积分日志页(242 行),表格 + 搜索 + 分页 | | `src/api/integral.js` | 积分接口:`integralListApi` → POST `/admin/user/integral/list` | | `src/api/user.js` | 用户接口:`userListApi` → GET `/admin/user/list` | | `src/router/modules/order.js` | 订单路由定义 | | `src/router/modules/user.js` | 用户路由定义 | | `src/router/index.js` | 主路由,含 `constantRoutes` 和白名单 | | `src/permission.js` | 全局路由守卫(登录校验) | | `src/utils/request.js` | Axios 封装,token 注入 & 401 拦截 | ### 2.4 wa_users 表字段(需要在用户积分页展示) | 字段 | 类型 | 说明 | |------|------|------| | `id` | int | 主键 | | `username` | string | 用户名 | | `nickname` | string | 昵称 | | `mobile` | string | 手机号 | | `money` | BigDecimal | 账户余额 | | `selfBonus` | BigDecimal | 个人奖金 | | `shareBonus` | BigDecimal | 分享奖金 | | `score` | BigDecimal | 积分 | | `level` | int | 等级 | | `status` | int | 状态(0=禁用, 1=启用) | | `isVip` | int | VIP(0=否, 1=是) | | `isResell` | int | 可转卖(0=否, 1=是) | | `joinTime` | timestamp | 注册时间 | | `lastTime` | timestamp | 最后登录 | --- ## 3. 整体方案设计 ### 3.1 目录结构规划 ``` src/ ├── views/ │ └── integral-external/ # 新增:积分外部页面目录 │ ├── order/ │ │ └── index.vue # 积分订单页面 │ ├── user/ │ │ └── index.vue # 用户积分页面 │ └── user-integral-detail/ │ └── index.vue # 用户积分明细子页面 ├── api/ │ └── integralExternal.js # 新增:积分外部页面 API 集合 ├── router/ │ └── modules/ │ └── integralExternal.js # 新增:积分外部路由模块 └── layout/ └── EmptyLayout.vue # 新增:空白布局(无侧边栏/顶栏) ``` ### 3.2 跳过登录验证方案 采用**多层级免登录**策略,确保页面完全绕过认证: **第一层:路由白名单** 在 `src/permission.js` 的 `whiteList` 中添加新页面路径前缀: ```js const whiteList = ['/login', '/auth-redirect', '/integral-external']; ``` 同时修改白名单匹配逻辑,从精确匹配改为前缀匹配: ```js // 修改前 if (whiteList.indexOf(to.path) !== -1) // 修改后 if (whiteList.some(path => to.path.startsWith(path))) ``` **第二层:无 token 请求支持** 新建一个不注入 token、不拦截 401 的 Axios 实例 `requestNoAuth`,供外部页面 API 使用: ```js // src/utils/requestNoAuth.js import axios from 'axios'; import { Message } from 'element-ui'; import SettingMer from '@/utils/settingMer'; const service = axios.create({ baseURL: SettingMer.apiBaseURL, timeout: 60000, }); // 不注入 token,不拦截 401 跳转 service.interceptors.response.use( (response) => { const res = response.data; if (res.code !== 0 && res.code !== 200) { Message({ message: res.msg || '请求失败', type: 'error' }); return Promise.reject(new Error(res.msg || '请求失败')); } return res.data; }, (error) => { Message({ message: '网络请求失败', type: 'error' }); return Promise.reject(error); }, ); export default service; ``` **第三层:空白布局** 新建 `EmptyLayout.vue`,不包含侧边栏、顶栏和权限组件,作为外部页面的容器: ```vue ``` --- ## 4. 各页面详细设计 ### 4.1 积分订单页面 **路由**:`/integral-external/order` **参考**:`src/views/order/index.vue` #### 功能要点 从原订单页面中提取积分订单相关的核心功能,去除权限校验(`v-hasPermi`)和管理操作(编辑、发货、退款等),保留只读展示。 #### 筛选条件 | 筛选项 | 类型 | 说明 | |--------|------|------| | 订单状态 | RadioGroup | 全部/未支付/未发货/待收货/交易完成 等 | | 时间选择 | DateRangePicker | 快捷选项 + 自定义范围 | | 订单号 | Input | 精确搜索 | #### 表格列 | 列 | 字段 | 宽度 | |----|------|------| | 订单号 | `orderId` | 210 | | 订单类型 | `orderType` | 110 | | 收货人 | `realName` | 100 | | 商品信息 | `productList` | 400 | | 支付金额 | `payPrice` | 100 | | 支付方式 | `payType` | 100 | | 订单状态 | `status` | 100 | | 创建时间 | `createTime` | 150 | #### API 复用 直接复用现有订单列表接口。需确认后端是否允许无 token 调用,若不允许,需后端新增一个免认证的订单查询接口(或在已有接口上增加免认证标注)。 ```js // src/api/integralExternal.js export function getIntegralOrderList(params) { return requestNoAuth({ url: '/admin/order/list', // 复用原接口或后端新增免认证接口 method: 'get', params, }); } ``` #### 实现步骤 1. 复制 `order/index.vue` 为基础模板 2. 删除所有 `v-hasPermi` 权限指令 3. 删除操作列(编辑价格、发货、退款等按钮) 4. 删除导出功能 5. 将 API 调用替换为 `requestNoAuth` 版本 6. 简化订单类型筛选,只保留积分相关类型 7. 去除 Vuex store 依赖 --- ### 4.2 用户积分页面 **路由**:`/integral-external/user` **参考**:`src/views/user/list/index.vue` #### 功能要点 基于用户列表页精简,增加 `wa_users` 表的积分/奖金相关字段展示,提供积分明细跳转入口。 #### 筛选条件 | 筛选项 | 类型 | 说明 | |--------|------|------| | 用户搜索 | Input | 姓名/手机号/用户名 | | 时间选择 | DateRangePicker | 注册时间范围 | #### 表格列 | 列 | 字段 | 来源 | 说明 | |----|------|------|------| | 用户ID | `uid` | CRMEB | 系统用户ID | | 用户昵称 | `nickname` | CRMEB | — | | 手机号 | `phone` | CRMEB | — | | 系统积分 | `integral` | CRMEB | CRMEB 系统积分 | | WA用户名 | `wa_username` | wa_users | WA系统用户名 | | 账户余额 | `wa_money` | wa_users | WA账户余额 | | 个人奖金 | `wa_selfBonus` | wa_users | 可提现奖金 | | 分享奖金 | `wa_shareBonus` | wa_users | 推荐奖金 | | WA积分 | `wa_score` | wa_users | WA系统积分 | | 用户等级 | `wa_level` | wa_users | — | | 状态 | `wa_status` | wa_users | 启用/禁用 | | 注册时间 | `createTime` | CRMEB | — | | 操作 | — | — | 查看积分明细 | #### API 方案 **方案 A(推荐 — 最小后端修改)**:前端分别调用用户列表接口和 WA 用户信息接口,在前端做数据合并。 ```js // 复用原用户列表 export function getUserListNoAuth(params) { return requestNoAuth({ url: '/admin/user/list', method: 'get', params, }); } // 复用前端 WA 用户信息接口(需确认是否免认证) export function getWaUserInfo(userId) { return requestNoAuth({ url: '/api/front/wa/user/info', method: 'post', data: { userId }, }); } ``` **方案 B(若后端配合)**:后端新增一个聚合接口,一次性返回 CRMEB 用户 + wa_users 的合并数据。 #### 操作列 "查看积分明细" 按钮,点击后跳转至用户积分明细子页面,携带 `uid` 参数: ```js this.$router.push({ path: '/integral-external/user/integral-detail', query: { uid: row.uid }, }); ``` #### 实现步骤 1. 以 `user/list/index.vue` 为参考创建精简版页面 2. 删除所有权限指令、分组/标签/等级筛选、操作按钮(编辑、设为分销员等) 3. 删除 Tab 切换(全部/有效/无效用户) 4. 在表格中增加 wa_users 字段列 5. 实现前端数据合并逻辑(逐行匹配或批量查询) 6. 添加"查看积分明细"操作按钮 7. 将所有 API 替换为 `requestNoAuth` 版本 --- ### 4.3 用户积分明细子页面 **路由**:`/integral-external/user/integral-detail` **参考**:`src/views/user/integral/index.vue`(242 行) **后端 API**:POST `/admin/user/integral/list`(复用) #### 功能要点 该页面完整复用原积分日志页的展示逻辑,通过 URL query 参数 `uid` 锁定指定用户,隐藏"用户搜索"字段。 #### 筛选条件 | 筛选项 | 类型 | 说明 | |--------|------|------| | 用户ID | 隐藏字段 | 从 URL query `uid` 自动获取 | | 时间选择 | DateRangePicker | 日期范围 | #### 表格列(完全复用原页面) | 列 | 字段 | 说明 | |----|------|------| | ID | `id` | 记录ID | | 用户ID | `uid` | — | | 用户昵称 | `nickName` | — | | 标题 | `title` | 积分变动标题 | | 积分变动 | `integral` | +/- 显示 | | 剩余积分 | `balance` | 变动后余额 | | 类型 | `type` | 增加(1)/扣减(2) | | 关联类型 | `linkType` | 订单/签到/系统 | | 状态 | `status` | 订单创建/冻结期/完成/失效 | | 备注 | `mark` | — | | 创建时间 | `createTime` | — | #### API 复用 ```js export function getIntegralLogNoAuth(data) { return requestNoAuth({ url: '/admin/user/integral/list', // 直接复用原接口 method: 'post', data, }); } ``` #### 页面头部信息 在表格上方展示当前用户的积分概览卡片: ``` ┌──────────────────────────────────────┐ │ 用户:张三 (UID: 1001) │ │ 积分:1,200 个人奖金:350 │ │ [← 返回用户积分列表] │ └──────────────────────────────────────┘ ``` > **字段说明**:积分取自 `eb_user` 表的 `integral` 字段(`BigDecimal`,用户剩余积分);个人奖金取自 `wa_users` 表的 `selfBonus` 字段。 #### 实现步骤 1. 复制 `user/integral/index.vue` 作为基础 2. 从 URL query 中读取 `uid`,自动注入搜索参数 3. 隐藏"用户搜索"和"用户ID"输入框(已通过 query 锁定) 4. 添加顶部用户信息概览卡片 5. 添加"返回"按钮 6. 替换 API 为 `requestNoAuth` 版本 --- ## 5. 路由配置 ### 5.1 新增路由模块 ```js // src/router/modules/integralExternal.js const EmptyLayout = () => import('@/layout/EmptyLayout'); const integralExternalRouter = { path: '/integral-external', component: EmptyLayout, redirect: '/integral-external/order', hidden: true, // 不在侧边栏显示 children: [ { path: 'order', component: () => import('@/views/integral-external/order/index'), name: 'IntegralExternalOrder', meta: { title: '积分订单' }, }, { path: 'user', component: () => import('@/views/integral-external/user/index'), name: 'IntegralExternalUser', meta: { title: '用户积分' }, }, { path: 'user/integral-detail', component: () => import('@/views/integral-external/user-integral-detail/index'), name: 'IntegralExternalUserDetail', meta: { title: '用户积分明细' }, }, ], }; export default integralExternalRouter; ``` ### 5.2 注册路由 在 `src/router/index.js` 中将新模块加入 `constantRoutes`: ```js import integralExternalRouter from './modules/integralExternal'; export const constantRoutes = [ integralExternalRouter, // 积分外部页面(免登录) storeRouter, orderRouter, // ...其余路由 ]; ``` ### 5.3 修改权限守卫 在 `src/permission.js` 中扩展白名单: ```js const whiteList = ['/login', '/auth-redirect', '/integral-external']; // 匹配逻辑改为前缀匹配 if (whiteList.some(path => to.path.startsWith(path))) { next(); } ``` --- ## 6. 后端 API 影响评估 ### 6.1 可直接复用的接口 | 接口 | Method | 免认证现状 | 所需改动 | |------|--------|-----------|---------| | `/admin/user/integral/list` | POST | 需认证 | 需后端为外部调用新增免认证入口,或前端伪造 token | | `/admin/user/list` | GET | 需认证 | 同上 | | `/admin/order/list` | GET | 需认证 | 同上 | ### 6.2 推荐的后端最小改动方案 按照"最小修改原则",建议后端在现有 Controller 基础上新增一套免认证的映射路径,内部直接调用相同的 Service 方法: ``` 新路径 → 复用的 Service 方法 /api/external/integral/order/list → OrderService.list() /api/external/integral/user/list → UserService.list()(补充 wa_users 字段) /api/external/integral/log/list → IntegralService.list() ``` 只需新建一个 `ExternalIntegralController`,加 `@RestController` 免认证注解,约 50-80 行代码。 ### 6.3 wa_users 字段集成 **方案 A**:后端在用户列表接口返回中直接 JOIN wa_users 表,新增字段返回。 **方案 B**:前端先拿用户列表,再批量查 wa_users 信息,前端做合并。 推荐方案 A(后端改动更少,前端实现更简单)。 --- ## 7. 开发任务清单 ### Phase 1:基础设施(预计 0.5 天) | # | 任务 | 文件 | |---|------|------| | 1.1 | 创建 `EmptyLayout.vue` 空白布局 | `src/layout/EmptyLayout.vue` | | 1.2 | 创建 `requestNoAuth.js` 免认证请求实例 | `src/utils/requestNoAuth.js` | | 1.3 | 创建 `integralExternal.js` 路由模块 | `src/router/modules/integralExternal.js` | | 1.4 | 注册路由到 `constantRoutes` | `src/router/index.js` | | 1.5 | 修改 `permission.js` 白名单 | `src/permission.js` | | 1.6 | 创建 `integralExternal.js` API 文件 | `src/api/integralExternal.js` | ### Phase 2:积分订单页面(预计 1 天) | # | 任务 | |---|------| | 2.1 | 基于 `order/index.vue` 创建精简版积分订单页 | | 2.2 | 去除权限校验、操作按钮、导出功能 | | 2.3 | 接入 `requestNoAuth` 请求 | | 2.4 | 测试筛选、分页、数据展示 | ### Phase 3:用户积分页面(预计 1.5 天) | # | 任务 | |---|------| | 3.1 | 基于 `user/list/index.vue` 创建精简版用户积分页 | | 3.2 | 去除高级筛选、权限、操作按钮 | | 3.3 | 增加 wa_users 字段列(奖金、积分、余额等) | | 3.4 | 实现数据合并逻辑(前端或后端) | | 3.5 | 添加"查看积分明细"跳转按钮 | | 3.6 | 测试数据展示与跳转 | ### Phase 4:用户积分明细子页面(预计 0.5 天) | # | 任务 | |---|------| | 4.1 | 基于 `user/integral/index.vue` 创建积分明细页 | | 4.2 | 通过 URL query 读取 uid 并锁定用户 | | 4.3 | 添加用户积分概览卡片 | | 4.4 | 添加返回按钮 | | 4.5 | 接入 `requestNoAuth` 请求 | | 4.6 | 测试分页、筛选、数据展示 | ### Phase 5:联调与验收(预计 0.5 天) | # | 任务 | |---|------| | 5.1 | 无 token 状态下完整流程测试 | | 5.2 | 页面间跳转逻辑验证 | | 5.3 | 后端免认证接口联调 | | 5.4 | 兼容性和响应式测试 | **总计预估工时:4 天** --- ## 8. 测试方案 ### 8.1 免登录访问测试 | 编号 | 测试场景 | 操作步骤 | 预期结果 | |------|---------|---------|---------| | A-01 | 无 token 直接访问积分订单页 | 清除浏览器所有 cookie/sessionStorage,直接访问 `/integral-external/order` | 页面正常加载,不跳转至 `/login` | | A-02 | 无 token 直接访问用户积分页 | 同上,访问 `/integral-external/user` | 页面正常加载,不跳转至 `/login` | | A-03 | 无 token 直接访问积分明细页 | 同上,访问 `/integral-external/user/integral-detail?uid=1` | 页面正常加载,不跳转至 `/login` | | A-04 | 免登录页面不影响原有认证 | 无 token 访问 `/order/index`(原页面) | 仍然正常跳转至 `/login` | | A-05 | 已登录用户访问免登录页面 | 管理员登录后访问 `/integral-external/order` | 页面正常加载,不受登录态影响 | ### 8.2 积分订单页面测试 | 编号 | 测试场景 | 操作步骤 | 预期结果 | |------|---------|---------|---------| | B-01 | 默认加载 | 进入页面 | 表格展示订单列表,分页信息正确 | | B-02 | 按订单状态筛选 | 依次点击"未支付""未发货""交易完成"等状态 | 表格数据按状态正确过滤,数量标签更新 | | B-03 | 按时间范围筛选 | 选择起止日期 | 仅显示时间范围内的订单 | | B-04 | 按订单号搜索 | 输入完整订单号,点击搜索 | 精确匹配到对应订单 | | B-05 | 重置筛选条件 | 设置筛选条件后点击重置 | 所有筛选项恢复默认,表格展示全部数据 | | B-06 | 分页切换 | 切换页码、修改每页显示数 | 数据正确刷新,分页器状态正确 | | B-07 | 空数据状态 | 搜索不存在的订单号 | 表格显示空状态提示,无 JS 报错 | | B-08 | 无操作列 | 检查表格列 | 不存在编辑、发货、退款等操作按钮 | ### 8.3 用户积分页面测试 | 编号 | 测试场景 | 操作步骤 | 预期结果 | |------|---------|---------|---------| | C-01 | 默认加载 | 进入页面 | 用户列表正常展示,含 CRMEB 和 wa_users 字段 | | C-02 | wa_users 字段展示 | 查看表格列 | 个人奖金(`selfBonus`)、账户余额(`money`)等 wa_users 字段正确显示 | | C-03 | 积分字段来源验证 | 对比数据库 `eb_user.integral` 值 | 页面显示的积分与 `eb_user` 表一致 | | C-04 | wa_users 无关联数据 | 查看无 wa_users 记录的 CRMEB 用户行 | wa_users 相关列显示 `-` 或 `0`,不报错 | | C-05 | 用户搜索 | 输入姓名/手机号搜索 | 正确过滤,支持模糊匹配 | | C-06 | 跳转积分明细 | 点击某用户行的"查看积分明细" | 正确跳转至 `/integral-external/user/integral-detail?uid=xxx` | | C-07 | 分页功能 | 切换页码和每页条数 | 数据正确刷新 | | C-08 | 无权限指令残留 | 审查页面 DOM | 不存在 `v-hasPermi` 相关的隐藏元素或报错 | ### 8.4 用户积分明细子页面测试 | 编号 | 测试场景 | 操作步骤 | 预期结果 | |------|---------|---------|---------| | D-01 | 带 uid 参数加载 | 访问 `?uid=1001` | 自动加载 uid=1001 的积分明细,顶部概览卡片显示用户信息 | | D-02 | 概览卡片数据验证 | 对比数据库值 | 积分值 = `eb_user.integral`,个人奖金 = `wa_users.selfBonus` | | D-03 | 无 uid 参数访问 | 访问不带 `?uid` 参数的页面 | 页面给出"缺少用户参数"提示,或重定向至用户积分列表 | | D-04 | 无效 uid 访问 | 访问 `?uid=999999`(不存在的用户) | 表格为空,概览卡片显示空状态,无 JS 报错 | | D-05 | 时间范围筛选 | 选择日期范围 | 积分明细按时间正确过滤 | | D-06 | 积分变动显示 | 查看积分变动列 | 增加显示绿色 `+`,扣减显示红色 `-` | | D-07 | 状态与关联类型 | 查看状态和关联类型列 | 订单创建/冻结期/完成/失效 正确渲染标签颜色;订单/签到/系统 正确显示 | | D-08 | 返回按钮 | 点击"返回用户积分列表" | 正确跳转回 `/integral-external/user` | | D-09 | 分页功能 | 切换页码(15/30/45/60) | 数据正确刷新 | ### 8.5 接口与数据测试 | 编号 | 测试场景 | 操作步骤 | 预期结果 | |------|---------|---------|---------| | E-01 | 免认证接口可达性 | 无 token 调用各外部接口 | 返回 200 及正确业务数据,不返回 401 | | E-02 | 原认证接口不受影响 | 无 token 调用原 `/admin/user/list` 等接口 | 仍返回 401 | | E-03 | 接口仅读不写 | 尝试对免认证接口发送写操作请求 | 返回 403 或方法不允许 | | E-04 | 大数据量分页 | 请求 limit=60,数据总量 > 1000 | 分页正确,响应时间 < 3s | | E-05 | 边界参数 | page=0、limit=-1、uid=null 等异常参数 | 接口返回友好错误信息,不产生 500 | | E-06 | 数据脱敏验证 | 检查返回的手机号字段 | 中间 4 位做掩码处理(如 `138****8888`) | ### 8.6 兼容性与 UI 测试 | 编号 | 测试场景 | 预期结果 | |------|---------|---------| | F-01 | Chrome 最新版 | 页面布局正常,功能正常 | | F-02 | Firefox 最新版 | 页面布局正常,功能正常 | | F-03 | Edge 最新版 | 页面布局正常,功能正常 | | F-04 | 1920×1080 分辨率 | 表格列宽合理,无横向滚动条溢出 | | F-05 | 1366×768 分辨率 | 表格可横向滚动,筛选栏自动换行 | | F-06 | EmptyLayout 布局验证 | 页面无侧边栏、无顶部导航栏、无面包屑 | | F-07 | 加载状态 | 数据加载中显示 loading 动画 | ### 8.7 测试执行时间规划 | 阶段 | 内容 | 预计时间 | |------|------|---------| | 冒烟测试 | Phase 1 基础设施完成后,验证 A-01 ~ A-05 免登录链路 | 0.5h | | 功能测试 | 每个页面开发完成后,执行对应 B/C/D 组用例 | 每页面 1~2h | | 接口联调测试 | 后端免认证接口就绪后,执行 E 组用例 | 1h | | 回归测试 | 全部开发完成后,执行全量用例 | 2h | | 兼容性测试 | 回归通过后,执行 F 组用例 | 1h | --- ## 9. 注意事项 1. **安全风险**:免登录页面直接暴露后台数据,建议后端对免认证接口做 IP 白名单或 API Key 鉴权。 2. **数据脱敏**:用户手机号等敏感字段建议做中间位掩码处理(如 `138****8888`)。 3. **接口幂等**:所有免认证接口仅开放读取权限(GET/查询),禁止写操作。 4. **路由隔离**:新页面使用 `EmptyLayout`,与管理后台主布局完全隔离,避免引入侧边栏/权限组件的副作用。 5. **组件依赖**:新页面可复用 Element UI 组件,但避免引入需要 Vuex store(如用户信息、权限)的业务组件。