# 商品列表 — 前端技术方案

> **依据：** 《商品列表功能需求.md》v1.4、《店铺商品列表技术方案.md》v1.6  
> **前端规范：** `doc/前端设计/前端设计.md`  
> **范围：** 仅 **ruoyi-ui 商家端（店铺经营管理端）** 当前店铺商品列表、检索、发品（v1.0 单规格）、编辑、提交上架、下架、删除；平台审核 **不在本页**。  
> **实现状态：** 列表、发品（含属性模版/属性规格、副图、配送运费）、详情已落地，待联调。

---

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

| 项 | 说明 |
|----|------|
| 框架 | Vue 2 + Element UI（与 RuoYi-Vue 一致） |
| 请求 | `@/utils/request`；商家端接口携带 **`X-Shop-Id`** |
| 参考页面 | `ruoyi-ui/src/views/agri/goods/audit/index.vue`（列表 Tab、批量操作） |
| 商家端参考 | `ruoyi-ui/src/views/agri/seller/employee/index.vue`（`loadShopContext`） |
| 布局 | 检索区 `el-card` + `<br/>` + 列表区 `el-card` + 表格 `border` |
| 店铺切换 | **仅 Navbar**；业务页禁止展示店铺选择器（§5 商家端店铺上下文） |

---

## 2. 文件清单

| 类型 | 路径 | 说明 |
|------|------|------|
| 列表页 | `ruoyi-ui/src/views/agri/seller/goods/index.vue` | 检索、Tab、列表、批量操作、发品/编辑弹窗 |
| 详情抽屉 | `ruoyi-ui/src/views/agri/seller/goods/detail.vue` | 详情展示、行内操作 |
| 商品 API | `ruoyi-ui/src/api/agri/seller/goods.js` | 列表、下拉、CRUD、submit、offShelf、delete |
| 店铺上下文 | `ruoyi-ui/src/api/agri/seller/context.js` + `utils/sellerShop.js` | X-Shop-Id |

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

---

## 3. 菜单与路由

| 菜单名称 | 组件路径 | 路由 path（建议） | 权限标识 |
|----------|----------|-------------------|----------|
| 商品列表 | `agri/seller/goods/index` | `seller/goods` | `agri:seller:goods:list` |

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

| 按钮权限 | 标识 |
|----------|------|
| 详情 | `agri:seller:goods:query` |
| 添加 / 编辑 / 提交上架 | `agri:seller:goods:edit` |
| 添加 | `agri:seller:goods:add` |
| 下架 | `agri:seller:goods:offshelf` |
| 删除 | `agri:seller:goods:remove` |

---

## 4. 页面结构

```text
商品列表
├── 检索区（el-card）
│   ├── 商品编号 goodsSn（模糊）
│   ├── 商品名称 goodsName（模糊）
│   └── 商品分类 categoryId（平台二级下拉）
├── 列表区（el-card + border 表格）
│   ├── 状态 Tab：全部 / 未上架 / 待审核 / 出售中 / 审核失败 / 已下架
│   ├── 工具栏：添加商品、批量提交上架、批量下架、批量删除
│   ├── 列：编号、主图、名称、售价、库存、销量、商品分类、店铺分类、状态
│   └── 行操作（按状态）：详情、编辑、提交上架、下架、删除
├── 详情抽屉 detail.vue（72% 宽）
│   ├── 基础 / 分类 / 属性·规格 / **配送运费** / 状态 / 服务快照 / 详情 HTML
│   └── 操作：编辑、提交上架、下架、删除（can* 或状态驱动）
└── 添加/编辑弹窗（780px · 可滚动）
    ├── 商品分类 categoryId（必填，平台二级）
    ├── 店铺商品分类 shopCategoryId（选填，本店二级）
    ├── 属性模版 attrTemplateId（选填；切换时二次确认覆盖）
    ├── 属性 attributes[] / 规格 specs[]（itemName + values[]，可增删改）
    ├── 商品名称、主图、**商品副图 subPics**（选填，最多 10 张，image-upload 多图）、销售价、库存
    ├── **配送运费** freightType（必填：1 固定 / 2 模版）；fixedFreightAmount 或 freightTemplateId
    ├── 商品详情 detailContent（Editor 富文本）
    └── 服务说明 serviceIds（多选；新建默认勾选 defaultShow）
```

**v1.0 未实现（后续扩展）：** 多规格 SKU 明细（市场价/库存矩阵）、独立 form 路由页。

---

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

| 步骤 | 说明 |
|------|------|
| 页面 `created` | `getSellerContext()` → `setSellerShopContext` |
| 每次 API | `sellerShopHeaders()` 注入 `X-Shop-Id` |
| 切换店铺 | **仅 Navbar** → `location.reload()` |

---

## 6. 接口封装

**Base URL：** `/agri/seller/goods`

| 前端方法 | HTTP | 路径 | 用途 |
|----------|------|------|------|
| `listSellerGoods` | GET | `/list` | 分页列表 |
| `sellerGoodsCategoryOptions` | GET | `/categoryOptions` | 平台二级分类 |
| `sellerGoodsShopCategoryOptions` | GET | `/shopCategoryOptions` | 本店二级店铺分类 |
| `sellerAttrTemplateOptions` | GET | `/attrTemplateOptions` | 本店属性模版下拉 |
| `getSellerAttrTemplateDetail` | GET | `/attrTemplate/{templateId}` | 选用模版预填 attributes/specs |
| `sellerFreightTemplateOptions` | GET | `/freightTemplateOptions` | 本店运费模版下拉 |
| `getSellerFreightDefault` | GET | `/freightDefault` | 默认运费模版预填 |
| `getSellerFreightTemplateDetail` | GET | `/freightTemplate/{templateId}` | 选用运费模版回显 |
| `sellerGoodsServiceOptions` | GET | `/serviceOptions` | `{ all, defaultShow }` |
| `getSellerGoods` | GET | `/{goodsId}` | 详情 |
| `addSellerGoods` | POST | `/` | 新增 → status=0 |
| `updateSellerGoods` | PUT | `/` | 编辑（不改状态） |
| `submitSellerGoods` | PUT | `/{goodsId}/submit` | 单条提交上架 |
| `submitSellerGoodsBatch` | PUT | `/submit` | 批量提交 |
| `offShelfSellerGoods` | PUT | `/{goodsId}/offShelf` | 单条下架 |
| `offShelfSellerGoodsBatch` | PUT | `/offShelf` | 批量下架 |
| `delSellerGoods` | DELETE | `/{goodsIds}` | 删除（逗号分隔批量） |

### 6.1 列表 Query

| 字段 | 说明 |
|------|------|
| pageNum, pageSize | 分页 |
| goodsSn, goodsName | 模糊 |
| categoryId | 平台二级分类 ID |
| goodsStatusQuery | `0`～`4`；Tab 切换写入；不传查全部 |

> 注意：商家列表用 **`goodsStatusQuery`**，与平台 `/agri/goods/list` 的 `goodsStatus` 字段名不同。

### 6.2 商品状态（goods_status）

| 值 | 标签 | 行操作 |
|----|------|--------|
| 0 | 未上架 | 详情、编辑、提交上架、删除 |
| 1 | 待审核 | 详情、编辑 |
| 2 | 出售中 | 详情、编辑、下架 |
| 3 | 审核失败 | 详情、编辑、提交上架、删除 |
| 4 | 已下架 | 详情、编辑、提交上架、删除 |

### 6.3 保存体（GoodsSaveDTO · v1.0）

| 字段 | 新增 | 编辑 | 说明 |
|------|------|------|------|
| categoryId | 必填 | 必填 | 平台二级 |
| shopCategoryId | 选填 | 选填 | 本店二级 |
| attrTemplateId | 选填 | 选填 | 本店有效属性模版 |
| attributes | 选填 | 选填 | `[{ itemName, values[] }]` 属性项 |
| specs | 选填 | 选填 | `[{ itemName, values[] }]` 规格项 |
| goodsName | 必填 | 必填 | max 200 |
| mainPic | 必填 | 必填 | image-upload **主图** |
| subPics | 选填 | 选填 | string[] **副图**；最多 10；不含 mainPic |
| detailContent | 必填 | 必填 | 富文本 |
| salePrice | 必填 | 必填 | ≥0 |
| stock | 必填 | 必填 | 整数 ≥0 |
| freightType | 必填 | 必填 | **1** 固定运费 **2** 运费模版 |
| fixedFreightAmount | type=1 必填 | type=1 必填 | ≥0 |
| freightTemplateId | type=2 必填 | type=2 必填 | 本店有效运费模版 |
| serviceIds | 选填 | 选填 | 数组 |
| goodsId | — | 必填 | 编辑 |
| goodsStatus | **禁止** | **禁止** | 后端拒收 |

保存后状态 **不变**（新建为 0）；须单独调用 submit 接口上架。

### 6.4 批量操作规则

| 操作 | 允许状态 | 失败策略 |
|------|----------|----------|
| 批量提交 | 0、3、4 | 含其他状态 → 整批失败，`data.reasons` |
| 批量下架 | 2 | 同上 |
| 批量删除 | 0、3、4 | 同上 |

前端通过 `selection.every(...)` 预禁用按钮；最终以服务端校验为准。

### 6.5 详情 VO 扩展字段

| 字段 | 用途 |
|------|------|
| canSubmit / canOffShelf / canDelete / canEdit | 按钮显隐 |
| rejectReason | 审核失败展示 |
| services[] | 服务快照；编辑时回显 `serviceIds` |
| shopCategoryPath | 店铺分类路径 |
| attrTemplateId / attrTemplateName | 属性模版引用与名称 |
| attributes / specs | 属性/规格快照（同保存体结构） |
| subPics | 副图 URL 列表（详情回显；编辑表单绑定） |
| freightType / fixedFreightAmount / freightTemplateId | 配送运费保存字段 |
| freightTemplateName / freightDesc | 详情展示运费模版名称与摘要 |

---

## 7. 交互要点

| 场景 | 行为 |
|------|------|
| 添加成功 | 提示「未上架」；不自动提交 |
| 提交成功 | 展示返回的 `goodsStatusLabel`（待审核 / 出售中） |
| 下架 | 二次确认「C 端不可购买」 |
| 删除 | 二次确认；待审核/出售中不可删 |
| 审核失败 | 详情抽屉红色展示 `rejectReason` |
| 服务默认勾选 | `serviceOptions.defaultShow` → 新建表单 `serviceIds` |
| 选用属性模版 | 下拉变更 → 若已有属性/规格内容则二次确认 → 预填 `attributes`/`specs` |
| 清空属性模版 | 仅清除 `attrTemplateId`；已填属性/规格 **保留** |
| 属性/规格校验 | 有项时校验项名、值非空、段内/跨段不重名；可为空（不传快照） |
| 商品副图 | `image-upload` 多图 `:limit="10"`；保存前 `normalizeSubPics` 转为 string[] |
| 新建预填运费 | `handleAdd` → `getSellerFreightDefault()`；有默认模版则 `freightType=2` + `freightTemplateId`，否则 `freightType=1` + `fixedFreightAmount=0` |
| 切换运费方式 | `freightType=1` 清 `freightTemplateId`；`freightType=2` 清 `fixedFreightAmount`，并尝试选中默认模版 |
| 保存运费字段 | type=1 传 `fixedFreightAmount`，`freightTemplateId=null`；type=2 反之 |

---

## 8. 联调检查清单

- [ ] 菜单组件路径 `agri/seller/goods/index` 及按钮权限
- [ ] Navbar 切换店铺后列表数据隔离
- [ ] 五态 Tab 筛选正确
- [ ] 添加 → 未上架 → 提交上架（免审开/关两种策略）
- [ ] 出售中编辑保存后状态不变
- [ ] 批量操作含非法状态时后端报错
- [ ] 选模版 → 预填 attributes/specs → 修改后保存 → 详情回显一致
- [ ] 副图 subPics：0～10 张上传/保存/详情回显一致
- [ ] 配送运费：固定 / 模版两种模式保存与详情回显；新建预填默认模版
- [ ] 分类/店铺分类/属性模版/服务/运费模版下拉数据正确

---

## 9. 修订记录

| 版本 | 说明 |
|------|------|
| v1.0 | 首版：列表 + v1.0 单规格发品弹窗 + 详情抽屉 + 状态流转操作 |
| v1.1 | 对齐后端 `attrTemplateId`：保存体/详情字段；下拉走属性模版模块 |
| v1.2 | 对齐 `biz_goods_attr`：attributes/specs 保存体；goods 侧预填/下拉 API |
| v1.3 | 发品表单接入属性模版下拉、预填、属性/规格动态编辑；详情展示属性模版与快照 |
| v1.4 | 对齐后端 `subPics`：保存体/详情字段；副图最多 10 张、非必填 |
| v1.5 | 发品表单接入副图多图上传；详情展示副图画廊 |
| v1.6 | 接入配送运费：freightType 单选、固定金额/模版下拉；新建预填 `/freightDefault`；详情展示 freightDesc |

---

*文档版本：v1.6 · 关联《商品列表功能需求.md》v1.4、《店铺商品列表技术方案.md》v1.6*
