# 小程序 · CRM 产品分类(缩进展开树)设计 > 日期:2026-07-18 > 状态:已确认并实现 > 视觉 Demo:`mobile-ui/shanxia-mp-crm-product-category-demo.html`(方案 A) > 功能对照:`web-admin/src/views/crm/product-category/index.vue` ## 1. 背景 - 小程序工作台 CRM 模块中「产品分类」为灰卡(`enabled: false`),页面未实现。 - Web 管理端已具备完整能力:分类树列表、名称/状态筛选、新增/修改/新增子分类/删除、全部展开/折叠。 - 后端接口已存在:`/web/crm/product-categories/{tree,add,update,delete}`,小程序可复用(与其它 CRM 页相同,走 admin JWT)。 ## 2. 目标 1. 小程序工作台可进入「产品分类」管理页,功能对齐 Web 端核心 CRUD。 2. 树形数据采用 **方案 A:缩进展开树**(一屏总览 + 行内操作),视觉对齐 Demo 方案 A 与三墙 Token。 3. 复用现有后端,不新增接口;不改表结构。 ## 3. 非目标 - 不做 Excel 导入/导出、列显隐、多选批量删除(Web 端工具栏扩展能力本期不做)。 - 不做产品管理页(「产品管理」仍为灰卡)。 - 不改 Web 端产品分类页。 ## 4. 信息架构与页面 | 页面 | 路径 | 职责 | |------|------|------| | 分类树列表 | `pages/crm/product-category/list` | 搜索、状态筛选、展开树、行操作入口 | | 新增/编辑表单 | `pages/crm/product-category/form` | 新增顶级 / 新增子分类 / 编辑;选择上级分类 | 入口:工作台 `产品分类` → `enabled: true`,`code: 'crm-product-category'` → 路由到 list。 ## 5. UI 设计(方案 A) ### 5.1 列表页骨架 顺序对齐现有 CRM 子页与 Demo: 1. Header:返回工作台 · 标题「产品分类」·「全部展开/折叠」·「+ 新增」 2. 搜索框:分类名称(确认搜索,调用 tree 接口 `categoryName`) 3. 状态 chips:全部 / 正常 / 停用(对应 `status` 空 / `0` / `1`,与 Web 字典 `sys_normal_disable` 一致) 4. 树列表:白底圆角容器内的缩进行 ### 5.2 树行 每行展示: - 左侧展开箭头(无子节点隐藏);缩进按层级(约 18px/级) - 分类名称(主文案) - 元信息:状态 Tag(正常=success / 停用=muted)· 排序号 · 子类数量(有子时) - 右侧:`+`(新增子分类)· `⋯`(打开 ActionSheet:编辑 / 新增子分类 / 删除) 交互: - 点箭头:仅展开/折叠该节点(本地 `expandedIds`,默认展开一级有子节点) - 点「全部展开/折叠」:切换整树展开态(对齐 Web) - 点「+ 新增」:进入 form,上级为空(顶级) - 点行内 `+` 或 Sheet「新增子分类」:进入 form,预填 `parentId` - 点 Sheet「编辑」:进入 form,带 `id` - 点 Sheet「删除」:`uni.showModal` 确认后调 delete,成功刷新树 树为全量接口(不分页),不做触底加载。 ### 5.3 表单页字段 对齐 Web 弹窗字段(精简交互适配移动端): | 字段 | 必填 | 说明 | |------|------|------| | 上级分类 | 否 | 选择器:树扁平为带缩进/全路径选项;留空或选「顶级分类」→ `parentId=0`;新增子分类时预填且可改 | | 分类名称 | 是 | | | 排序号 | 否 | 数字,默认 0 | | 状态 | 是 | 正常 `0` / 停用 `1`,默认 `0`(与 Web 表单默认一致) | | 备注 | 否 | | 编辑时禁止把节点设为自己或自己的子孙为上级(前端用当前树校验;后端若已有校验则透传错误 Toast)。 提交成功后返回列表并刷新。 ## 6. 数据与 API 在 `miniprogram/api/crm.js` 增加(与 Web 同路径): | 方法 | 路径 | 说明 | |------|------|------| | `listProductCategoryTree` | `POST /web/crm/product-categories/tree` | body: `{ categoryName?, status? }` → 嵌套 `children` | | `addProductCategory` | `POST /web/crm/product-categories/add` | | | `updateProductCategory` | `POST /web/crm/product-categories/update` | | | `deleteProductCategory` | `POST /web/crm/product-categories/delete` | `{ id }` | 列表页直接消费树结构;表单上级选择器可对同一 tree 做扁平遍历(展示全路径,分隔符 ` / `)。 删除权限:若接口返回创建人限制类错误,Toast 展示后端文案(与 Web `canDeleteByCreator` 语义一致,小程序侧不做复杂权限 UI,依赖接口)。 ## 7. 接入改动清单 1. `pages/crm/product-category/list.vue`、`form.vue`(新建) 2. `pages.json` 注册两页 3. `config/api.js`:`PAGE_PATHS` + `WORKBENCH_ROUTE_MAP` 4. `config/workbench-menus.js`:产品分类 `enabled: true`,补 `code` 5. `api/crm.js`:四个分类接口 6. 样式:优先复用 `mp-*` 全局类;树行专用样式可写在 list 页 scoped(Token 用三墙绿,勿用 Web `#10b981`) 7. Demo:保留 `mobile-ui/shanxia-mp-crm-product-category-demo.html` 作为视觉权威;实现后可把方案切换条标注「已采用 A」 ## 8. 错误与空态 - 加载失败:Toast + 空列表 - 树为空:空态文案「暂无产品分类」+ 引导新增 - 表单校验失败:字段级 Toast(名称必填) - 删除确认取消:无副作用 ## 9. 验收标准 1. 工作台「产品分类」可点进列表,不再 Toast「本期暂未开放」。 2. 树层级、展开折叠、搜索、状态筛选与 Web 数据一致(同租户同账号)。 3. 可完成:新增顶级 → 新增子分类 → 编辑 → 删除(有子节点时以后端规则为准,失败有提示)。 4. 视觉与 Demo 方案 A 一致:绿渐变头、缩进树、状态 Tag、底部/Sheet 操作。 ## 10. 测试建议 - 手工:三级树展开/折叠、搜索命中后祖先仍可见(后端 tree 已带祖先)、停用筛选、表单上级选择、删除确认。 - 若维护 `miniprogram-e2e` 按钮清单:为本页补 INV 行(列表头按钮、行内 +/⋯、Sheet 三项、删除 Modal、表单提交)。