# 商品入库 — 前端技术方案

> **依据：** 《商品入库功能需求.md》v1.0.1、《商品入库技术方案.md》v1.0  
> **前端规范：** `doc/前端设计/前端设计.md`  
> **范围：** 仅 **ruoyi-ui 商家端** 当前店铺入库单列表、高级检索、**手工入库**、详情查看；**不含** 出库/调整/库存查询/库存日志页面、平台端、C 端。  
> **实现状态：** `index.vue`、`detail.vue`、`form.vue`、`api/agri/seller/stock/inbound.js` **已按需求与 UI 稿落地**；待菜单配置及 `/agri/seller/stock/inbound` 联调。

---

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

| 项 | 说明 |
|----|------|
| 框架 | Vue 2 + Element UI |
| 请求 | `@/utils/request` + `sellerShopHeaders()` 携带 **`X-Shop-Id`** |
| 参考页面 | `agri/seller/review/index.vue`（检索+列表+抽屉）、`agri/seller/ship/index.vue`（店铺上下文） |
| 布局 | 检索 `el-card` + `<br/>` + 列表 `el-card` + `border` 表格 |
| 详情 | 右侧 `el-drawer`（`size="72%"`，`append-to-body`） |
| 手工入库 | 右侧 `el-drawer`（`size="85%"`，底部固定「取消/确认」） |
| 图片 | 全局 `image-preview`（列表缩略、明细主图） |
| 店铺切换 | **仅 Navbar**；业务页禁止展示店铺选择器 |

---

## 2. 业务要点（前端需体现）

| 项 | 说明 |
|----|------|
| 入库类型 | `0` 新增商品规格（系统自动）、`1` 采购、`2` 退货、`3` 其他；**手工表单仅 1～3** |
| 自动入库 | 商品保存触发，**仅留痕**；详情页对 type=`0` 展示提示文案 |
| 手工入库 | 确认后 **增加可用库存**；单据 **不可编辑/删除** |
| 明细粒度 | 首期 **单规格**（`skuId` 传 `null`）；规格空展示 **—** |
| 条码 | 按 **`goods_sn`** 精确匹配；0 条报错、1 条追加、多条弹窗选择 |
| 重复商品 | 同 `goodsId`（+ `skuId`）再次添加 **合并数量 +1** 并提示 |
| 导入 | UI 保留按钮；后端 **`POST /import/preview` 未实现**，点击提示改用选品/条码 |

---

## 3. 文件清单

| 类型 | 路径 | 说明 |
|------|------|------|
| 列表页 | `ruoyi-ui/src/views/agri/seller/stock/inbound/index.vue` | 检索、表格、分页、打开入库/详情抽屉 |
| 详情抽屉 | `ruoyi-ui/src/views/agri/seller/stock/inbound/detail.vue` | 基本信息 + 入库明细（只读） |
| 入库抽屉 | `ruoyi-ui/src/views/agri/seller/stock/inbound/form.vue` | 手工入库表单、选品、条码、明细编辑 |
| 入库 API | `ruoyi-ui/src/api/agri/seller/stock/inbound.js` | list、detail、create、operators、goods/options、goods/byBarcode |
| 店铺上下文 | `api/agri/seller/context.js` + `utils/sellerShop.js` | X-Shop-Id |

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

| 组件 | name |
|------|------|
| 列表页 | `AgriSellerStockInbound` |
| 详情抽屉 | `SellerStockInboundDetail` |
| 入库抽屉 | `SellerStockInboundForm` |

---

## 4. 菜单与路由

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

**上级菜单：** 店铺经营管理端 → **库存管理**

| 按钮权限 | 标识 | 页面落点 |
|----------|------|----------|
| 列表 | `agri:seller:stock:inbound:list` | 进入列表页、经办人下拉 |
| 详情 | `agri:seller:stock:inbound:query` | 操作列「详情」 |
| 手工入库 | `agri:seller:stock:inbound:add` | 「商品入库」、选品、条码 |

---

## 5. 页面结构（与代码一致）

```text
商品入库 index.vue
├── 检索区 search-card（el-card，v-show 受 right-toolbar 控制）
│   ├── 入库搜索 keyword（单号/商品名模糊）
│   ├── 入库时间 dateRange（daterange → beginTime/endTime）
│   ├── 入库类型 inboundType（0～3 下拉，可清空=全部）
│   ├── 经办人 operatorId（GET /operators 动态下拉）
│   └── 搜索 / 重置
├── 列表区 table-card（el-card）
│   ├── 工具栏：商品入库（add 权限）+ right-toolbar
│   ├── el-table border（empty-text 动态）
│   │   ├── 入库单号 inboundNo
│   │   ├── 入库类型 inboundTypeText
│   │   ├── 入库时间 inboundTime
│   │   ├── 备注 remark（空则 —）
│   │   ├── 经办人 operatorName
│   │   └── 操作：详情
│   └── pagination
├── 详情抽屉 SellerStockInboundDetail（见 §6）
└── 入库抽屉 SellerStockInboundForm（见 §7）
```

**不提供：** 页内店铺选择、入库单编辑/作废/反审、打印/导出、成本录入。

---

## 6. 详情抽屉 detail.vue

### 6.1 对外接口

| 类型 | 名称 | 说明 |
|------|------|------|
| props | `visible` | 抽屉显隐（`.sync`） |
| props | `inboundId` | 入库单主键 |
| emit | `update:visible` | 关闭抽屉 |
| 方法 | `reload()` | 可选重载（预留） |

### 6.2 分区结构

| 区块 | 内容 |
|------|------|
| 基本信息 | 入库单号、类型、时间、经办人、备注 |
| 系统提示 | `inboundType === '0'` 时说明「仅留痕」 |
| 入库明细 | 表格：商品信息（图+名）、规格、入库数量 |

**无底部操作栏**（确认后只读）。

---

## 7. 手工入库抽屉 form.vue

### 7.1 基本信息

| 字段 | 组件 | 校验 |
|------|------|------|
| 入库类型 | `el-radio-group`，默认 `1` 采购 | 必选（IN1） |
| 备注 | `textarea`，maxlength=200 | ≤200 字（IN4） |

### 7.2 入库商品区

| 能力 | 实现 |
|------|------|
| 选择商品 | `el-dialog` + 分页表格多选 → `GET /goods/options` |
| 导入商品 | 按钮占位，提示暂未开放 |
| 条码录入 | 输入框 + Enter/确定 → `GET /goods/byBarcode` |
| 明细表格 | 商品信息、规格、当前可用库存（只读）、入库数量 `el-input-number` min=1、删除 |

### 7.3 提交 payload

```json
{
  "inboundType": "1",
  "remark": "optional",
  "items": [{ "goodsId": 1001, "skuId": null, "quantity": 10 }]
}
```

| 前端校验 | 对应规则 |
|----------|----------|
| 明细至少一行 | IN2 |
| 每行 quantity 为正整数 | IN3 |
| 入库类型已选 | IN1 |

成功：`msgSuccess('入库成功')` → 关闭抽屉 → 父页刷新列表与经办人下拉。

---

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

| 步骤 | 说明 |
|------|------|
| `created` | `GET /agri/seller/context` → `setSellerShopContext` |
| 全部 API | `sellerShopHeaders()` |
| Navbar 切店 | `location.reload()` 整页刷新列表 |

---

## 9. API 封装

**模块：** `@/api/agri/seller/stock/inbound.js`

| 方法 | HTTP | 路径 | 权限 |
|------|------|------|------|
| `listSellerStockInbound` | GET | `/list` | list |
| `getSellerStockInbound` | GET | `/{inboundId}` | query |
| `createSellerStockInbound` | POST | `/` | add |
| `listStockInboundOperators` | GET | `/operators` | list |
| `listStockInboundGoodsOptions` | GET | `/goods/options` | add |
| `getStockInboundGoodsByBarcode` | GET | `/goods/byBarcode?barcode=` | add |

**列表 Query：** `pageNum`、`pageSize`、`keyword`、`inboundType`、`operatorId`、`beginTime`、`endTime`。

**排序：** 后端固定 `inbound_time DESC`（最新在前）。

---

## 10. 空状态与错误提示

| 场景 | 前端表现 |
|------|----------|
| 无数据 | 「暂无入库记录」 |
| 有筛选无结果 | 「未找到符合条件的入库单」 |
| 未选商品提交 | `请添加入库商品` |
| 数量非法 | `请输入正确的入库数量` |
| 条码未匹配 | 展示后端 `未找到该条码对应的商品` |
| 条码多匹配 | 弹窗选择规格 |
| 提交成功 | `入库成功` |

---

## 11. 与兄弟模块边界（前端）

| 模块 | 关系 |
|------|------|
| **商品列表** | 新增规格自动入库单在本列表展示；不在商品页重复入库入口 |
| **商品出库 / 库存调整** | 独立菜单，本方案不实现 |
| **库存查询 / 库存日志** | 可读本模块写入记录；页面待建 |

---

## 12. 联调检查清单

- [ ] 菜单挂载 `agri/seller/stock/inbound/index`，路由可访问
- [ ] 列表默认按入库时间倒序
- [ ] 检索：单号、商品名、类型、时间、经办人组合过滤
- [ ] 手工入库：采购/退货/其他类型提交后库存增加、列表刷新
- [ ] 商品保存触发的「新增商品规格」单可在列表查看，详情有留痕提示
- [ ] 条码：无匹配/单匹配/多匹配三种路径
- [ ] 同商品重复添加合并数量
- [ ] Navbar 切店后列表仅当前店数据
- [ ] 权限：无 `add` 不显示「商品入库」；无 `query` 不显示「详情」

---

## 13. 版本记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版：列表/检索/详情抽屉/手工入库抽屉/API 封装；对齐需求 v1.0.1 与 UI 稿；导入占位待后端 |

---

*文档版本：v1.0 · 依据《商品入库功能需求.md》v1.0.1、《商品入库技术方案.md》v1.0*
