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

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

---

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

| 项 | 说明 |
|----|------|
| 框架 | Vue 2 + Element UI |
| 请求 | `@/utils/request` + `sellerShopHeaders()` 携带 **`X-Shop-Id`** |
| 参考页面 | `agri/seller/stock/inbound/*`（对称实现） |
| 布局 | 检索 `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` 过期出库 / `4` 其他出库 |
| 手工表单 | **仅 2～4**；默认 **过期出库（3）**（对齐 UI 稿） |
| 命名 | **「下单扣减」≠「订单出库」**；列表/详情统一后端 `outboundTypeText` |
| 自动出库 | 支付扣减、删规格 **仅留痕**；详情展示提示文案 |
| 手工出库 | 确认后 **减少可用库存**；单据 **不可编辑/删除** |
| 库存校验 | 选品 **stock>0**；出库数量 **≤ 当前可用库存** |
| 关联订单 | 类型 `0` 详情展示 `refOrderNo`（只读文本） |

---

## 3. 文件清单

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

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

| 组件 | name |
|------|------|
| 列表页 | `AgriSellerStockOutbound` |
| 详情抽屉 | `SellerStockOutboundDetail` |
| 出库抽屉 | `SellerStockOutboundForm` |

---

## 4. 菜单与路由

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

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

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

---

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

```text
商品出库 index.vue
├── 检索区 search-card
│   ├── 出库搜索 keyword（单号/商品名/关联订单号模糊）
│   ├── 出库时间 dateRange → beginTime/endTime
│   ├── 出库类型 outboundType（0～4 下拉）
│   ├── 经办人 operatorId
│   └── 搜索 / 重置
├── 列表区 table-card
│   ├── 商品出库按钮（add 权限）+ right-toolbar
│   ├── el-table border
│   │   ├── 出库单号 outboundNo
│   │   ├── 出库类型 outboundTypeText
│   │   ├── 出库时间 outboundTime
│   │   ├── 备注 remark
│   │   ├── 经办人 operatorName
│   │   └── 操作：详情
│   └── pagination
├── 详情抽屉 SellerStockOutboundDetail（见 §6）
└── 出库抽屉 SellerStockOutboundForm（见 §7）
```

**不提供：** 页内店铺选择、出库单编辑/作废、打印/导出、跳转订单详情（非本期）。

---

## 6. 详情抽屉 detail.vue

### 6.1 对外接口

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

### 6.2 分区结构

| 区块 | 内容 |
|------|------|
| 基本信息 | 出库单号、类型、关联订单号（有则展示）、时间、经办人、备注 |
| 系统提示 | type=`0` 支付留痕；type=`1` 删规格留痕（变化量可能为 0） |
| 出库明细 | 商品信息、规格、出库数量、变化前/后库存 |

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

---

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

### 7.1 基本信息

| 字段 | 组件 | 校验 |
|------|------|------|
| 出库类型 | `el-radio-group`：过期(3)/订单(2)/其他(4)，默认 **3** | 必选 |
| 备注 | `textarea`，maxlength=200 | ≤200 字 |

### 7.2 出库商品区

| 能力 | 实现 |
|------|------|
| 选择商品 | 分页多选 → `GET /goods/options`（**仅 stock>0**） |
| 导入商品 | 按钮占位，提示暂未开放 |
| 条码录入 | Enter/确定 → `GET /goods/byBarcode`；无库存提示 |
| 明细表格 | 商品信息、规格、当前可用库存、出库数量（`:max=stock`）、删除 |

### 7.3 提交 payload

```json
{
  "outboundType": "3",
  "remark": "optional",
  "items": [{ "goodsId": 1001, "skuId": null, "quantity": 5 }]
}
```

| 前端校验 | 说明 |
|----------|------|
| 明细至少一行 | OUT2 |
| quantity 为正整数 | OUT3 |
| quantity ≤ stock | 库存不足提示 |
| 重复商品合并数量 | 合并时校验不超 stock |

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

---

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

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

---

## 9. API 封装

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

| 方法 | HTTP | 路径 | 权限 |
|------|------|------|------|
| `listSellerStockOutbound` | GET | `/list` | list |
| `getSellerStockOutbound` | GET | `/{outboundId}` | query |
| `createSellerStockOutbound` | POST | `/` | add |
| `listStockOutboundOperators` | GET | `/operators` | list |
| `listStockOutboundGoodsOptions` | GET | `/goods/options` | add |
| `getStockOutboundGoodsByBarcode` | GET | `/goods/byBarcode?barcode=` | add |

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

**排序：** 后端固定 `outbound_time DESC`。

---

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

| 场景 | 前端表现 |
|------|----------|
| 无数据 | 「暂无出库记录」 |
| 有筛选无结果 | 「未找到符合条件的出库单」 |
| 未选商品 | 「请添加出库商品」 |
| 数量非法 | 「请输入正确的出库数量」 |
| 超库存 | 「出库数量不能大于可用库存」 |
| 条码无库存 | 「该商品无可用库存」 |
| 提交成功 | 「出库成功」 |

---

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

| 模块 | 关系 |
|------|------|
| **商品入库** | 对称模块；退货加库存走入库 |
| **库存日志** | 出库成功后写入流水；日志页只读展示 |
| **全部订单** | 支付成功自动出库单在本列表可见；**不在** 订单页手工出库 |
| **商品列表** | 删规格自动出库单（type=1）在本列表可见 |

---

## 12. 联调检查清单

- [ ] 菜单挂载 `agri/seller/stock/outbound/index`
- [ ] 列表含自动单（下单扣减/删规格）与手工单
- [ ] 关键词可搜出库单号、商品名、关联订单号
- [ ] 手工出库：过期/订单/其他类型提交后库存减少
- [ ] 出库数量大于可用库存时前后端均拦截
- [ ] 详情 type=0 展示 refOrderNo 与留痕提示
- [ ] 详情 type=1 明细 quantity 可能为 0
- [ ] Navbar 切店后列表仅当前店数据
- [ ] 权限：无 `add` 不显示「商品出库」

---

## 13. 版本记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版：列表/检索/详情抽屉/手工出库抽屉/API 封装；对齐需求 v1.0.1、入库对称实现与 UI 稿 |

---

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