# 店铺商品分类 — 前端技术方案

> **依据：** 《店铺商品分类功能需求.md》v1.0、《店铺商品分类技术方案.md》v1.0  
> **前端规范：** `doc/前端设计/前端设计.md`  
> **范围：** 仅 **ruoyi-ui 商家端（店铺经营管理端）** 当前店铺商品分类 CRUD；平台端商品分类 **不在本页维护**。  
> **实现状态：** 页面与 API 封装已落地，待菜单配置及 `/agri/seller/shopCategory` 联调。

---

## 1. 技术栈与写法约定

| 项 | 说明 |
|----|------|
| 框架 | Vue 2 + Element UI（与 RuoYi-Vue 一致） |
| 请求 | `@/utils/request`；商家端接口携带 **`X-Shop-Id`** |
| 参考页面 | `ruoyi-ui/src/views/agri/goods/category/index.vue`（两级分类树交互） |
| 商家端参考 | `ruoyi-ui/src/views/agri/seller/role/index.vue`（`loadShopContext` 写 X-Shop-Id） |
| 布局 | 检索区 `el-card` + `<br/>` + 列表区 `el-card` + 表格 `border` |

---

## 2. 与需求文档的差异说明

| 项 | 功能需求 v1.0 | 当前后端实现 | 前端对齐 |
|----|---------------|--------------|----------|
| 数据结构 | 单层 flat 分类 | `biz_goods_category`，**shop_id 非空**，最多 **两级** | 对齐 **后端** |
| 表单字段 | 名称、排序、是否显示 | 另含 **分类图片、是否热门** | 对齐 **后端** |
| 删除 | 单条删除 | 支持 **批量**；失败时返回 `data.reasons` | 支持单条与批量 |
| 主键字段 | `shopCategoryId` | `categoryId` | 使用 `categoryId` |

> 发品侧「店铺商品分类」下拉可后续对接 `GET /level2Options` 或 `GET /tree`；本页以列表 CRUD 为主。

---

## 3. 文件清单

| 类型 | 路径 | 说明 |
|------|------|------|
| 列表页 | `ruoyi-ui/src/views/agri/seller/shopCategory/index.vue` | 检索、列表、添加/编辑弹窗、批量删除 |
| 分类 API | `ruoyi-ui/src/api/agri/seller/shopCategory.js` | scope、list、tree、level1/2Options、CRUD |
| 店铺上下文 API | `ruoyi-ui/src/api/agri/seller/context.js` | 登录后默认店铺、切换店铺 |
| 店铺上下文工具 | `ruoyi-ui/src/utils/sellerShop.js` | 缓存 shopId、组装 `X-Shop-Id` 请求头 |

**组件 name（keep-alive）：** `AgriSellerShopCategory`

---

## 4. 菜单与路由

若依路由由 **后端菜单** 动态加载，前端需配置：

| 菜单名称 | 组件路径 | 路由 path（建议） | 权限标识 |
|----------|----------|-------------------|----------|
| 店铺商品分类 | `agri/seller/shopCategory/index` | `seller/shopCategory` | `agri:seller:shopCategory:list` |

**上级菜单：** 店铺经营管理端 → **商品管理**

| 按钮权限 | 标识 |
|----------|------|
| 详情回显 | `agri:seller:shopCategory:query` |
| 添加分类 | `agri:seller:shopCategory:add` |
| 编辑分类 | `agri:seller:shopCategory:edit` |
| 删除分类 | `agri:seller:shopCategory:remove` |

---

## 5. 页面结构

```text
店铺商品分类
├── 检索区（el-card）
│   └── 分类名称 keyword（模糊，选填）
├── 列表区（el-card + border 表格）
│   ├── 提示：本店分类与平台分类数据隔离
│   ├── 工具栏：添加一级分类、批量删除
│   ├── 列：分类名称（层级标识）、上级分类、分类图片、是否显示、是否热门、排序
│   └── 操作：添加子分类（一级且 canAddChild）、编辑、删除
└── 添加/编辑弹窗（560px）
    ├── 分类层级（只读）
    ├── 上级分类 parentId（二级必填；编辑时不可改上级）
    ├── 分类名称 categoryName（必填）
    ├── 分类图片 categoryPic（必填，image-upload）
    ├── 排序 sortNo（≥0，默认 0）
    ├── 是否显示 showFlag（默认 1）
    └── 是否热门 hotFlag（默认 0）
```

**列表排序：** 由后端按 `sort_no` 升序、`create_time` 升序；前端不分页内二次排序。

---

## 6. 店铺上下文（X-Shop-Id）

| 步骤 | 说明 |
|------|------|
| 页面 `created` | `GET /agri/seller/context` → `setSellerShopContext(shopId, shopName)` |
| 每次 API | `sellerShopHeaders()` 注入 `X-Shop-Id` |
| 切换店铺 | **仅 Navbar**（`merchant` 角色）→ `PUT /agri/seller/context/shop` → `location.reload()` |

**业务页禁止** 展示店铺名称或选择器（见 `doc/前端设计/前端设计.md` §5）。

---

## 7. 接口封装

**Base URL：** `/agri/seller/shopCategory`  
**公共请求头：** `{ 'X-Shop-Id': '<当前店铺ID>' }`

| 方法 | 路径 | 函数名 | 用途 |
|------|------|--------|------|
| GET | `/scope` | `getShopCategoryScope` | 数据范围（shopId、shopName） |
| GET | `/list` | `listShopCategory` | 分页列表；query：`pageNum`、`pageSize`、`keyword` |
| GET | `/tree` | `treeShopCategory` | 分类树（本页未用，供发品等复用） |
| GET | `/level1Options` | `level1ShopCategoryOptions` | 一级下拉（添加二级、编辑回显） |
| GET | `/level2Options` | `level2ShopCategoryOptions` | 二级下拉（供其他模块） |
| GET | `/{categoryId}` | `getShopCategory` | 编辑详情回显 |
| POST | `/` | `addShopCategory` | 新增 |
| PUT | `/` | `updateShopCategory` | 修改 |
| DELETE | `/{categoryIds}` | `delShopCategory` | 删除；多 ID 逗号分隔 |

### 7.1 列表行字段（CategoryRowVO）

| 字段 | 说明 |
|------|------|
| categoryId | 主键 |
| categoryName | 分类名称 |
| categoryLevel | `1` 一级 / `2` 二级 |
| parentId / parentName | 上级（一级为空） |
| categoryPic | 图片 URL |
| showFlag | `1` 显示 / `0` 隐藏 |
| hotFlag | `1` 热门 / `0` 否 |
| sortNo | 排序 |
| canAddChild | 是否可添加子分类 |

### 7.2 提交体（BizGoodsCategory）

| 字段 | 新增 | 编辑 | 说明 |
|------|------|------|------|
| categoryLevel | 必填 | 不可改 | `1` / `2` |
| parentId | 二级必填 | 不可改 | 一级传 `0` |
| categoryName | 必填 | 必填 | 同店唯一 |
| categoryPic | 必填 | 必填 | 上传组件 |
| sortNo | 必填 | 必填 | 整数 ≥0 |
| showFlag | 必填 | 必填 | 默认 `1` |
| hotFlag | 必填 | 必填 | 默认 `0` |
| categoryId | — | 必填 | 编辑时 |

### 7.3 删除交互

| 场景 | 前端提示 |
|------|----------|
| 删除一级 | 「删除一级分类将同时删除其下全部二级分类，是否继续？」 |
| 删除二级 / 批量 | 「确认删除所选分类？分类下存在商品时无法删除」 |
| 后端拒绝 | 拦截器展示 `msg`；若含 `data.reasons` 数组，以服务端文案为准 |

---

## 8. 权限与按钮

| 操作 | v-hasPermi |
|------|------------|
| 添加一级 / 子分类 | `agri:seller:shopCategory:add` |
| 编辑 | `agri:seller:shopCategory:edit` |
| 删除 / 批量删除 | `agri:seller:shopCategory:remove` |

列表查询依赖菜单权限 `agri:seller:shopCategory:list`；编辑回显需后端 `query` 权限。

---

## 9. 联调检查清单

- [ ] 后端菜单配置组件路径 `agri/seller/shopCategory/index` 及按钮权限
- [ ] 商户经营账号登录，Navbar 显示当前店铺并可切换
- [ ] 多店账号 Navbar 切换店铺后列表仅展示该店分类
- [ ] 添加一级、添加二级、编辑、单删、批删
- [ ] 分类下有商品时删除失败，错误提示可读
- [ ] 子管理员无权限时按钮隐藏、接口 403

---

## 10. 后续扩展（非本期）

| 项 | 说明 |
|----|------|
| 发品选分类 | 商品列表页调用 `level2ShopCategoryOptions` 或 `treeShopCategory` |
| 单层简化版 | 若产品改回 flat 模型，需后端与前端同步裁剪层级与图片字段 |
| 删除原因明细 | 可在 `catch` 中解析 `data.reasons` 用 `MessageBox` 逐条展示 |
