# 售后管理 — 前端技术方案

> **依据：** 《售后管理功能需求.md》v1.0、《售后管理技术方案.md》v1.0  
> **前端规范：** `doc/前端设计/前端设计.md`  
> **范围：** 仅 **ruoyi-ui 商家端** 当前店铺售后列表、检索、详情查看、**处理完结**；**不含** C 端提交售后、退款原路退回、平台仲裁。  
> **实现状态：** `index.vue`、`detail.vue`、`api/agri/seller/aftersale.js` **已按 v1.0 落地**；待菜单配置及 `/agri/seller/aftersale` 联调。

---

## 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-dialog` + **二次确认**（`$modal.confirm`） |
| 图片 | 全局 `image-preview`（凭证图可预览大图） |
| 店铺切换 | **仅 Navbar**；业务页禁止展示店铺选择器 |

---

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

| 项 | 说明 |
|----|------|
| 数据来源 | C 端买家 **已提交** 的售后；商家 **不可代客发起** |
| 售后状态 | `1` 进行中 / `2` 已完结；**单向** 完结，不可回退 |
| 申请类型 | `1` 仅退款-未发货 / `2` 仅退款-已发货 / `3` 退货退款；**只读展示** |
| 退货数量 | 类型 `1` 列表/详情展示 **—**；其他类型展示数值 |
| 快照 | 商品/订单/会员/收货信息均为提交快照；列表手机 **后端脱敏** |
| 处理完结 | 填写 **处理结果**（≤500 字）；**不** 自动退款/关单/回库存 |
| 关联订单 | 详情订单编号点击 → **订单详情抽屉**（只读，复用 `SellerOrderDetail`） |

---

## 3. 文件清单

| 类型 | 路径 | 说明 |
|------|------|------|
| 列表页 | `ruoyi-ui/src/views/agri/seller/aftersale/index.vue` | 检索、状态页签、表格、处理弹窗、详情/订单抽屉宿主 |
| 详情抽屉 | `ruoyi-ui/src/views/agri/seller/aftersale/detail.vue` | 进度条、分区展示、处理按钮（事件上抛） |
| 售后 API | `ruoyi-ui/src/api/agri/seller/aftersale.js` | list、detail、finish |
| 订单详情 | `agri/seller/order/detail.vue` | 嵌套只读抽屉（`:show-actions="false"`） |
| 店铺上下文 | `api/agri/seller/context.js` + `utils/sellerShop.js` | X-Shop-Id |

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

| 组件 | name |
|------|------|
| 列表页 | `AgriSellerAftersale` |
| 详情抽屉 | `SellerAftersaleDetail` |

---

## 4. 菜单与路由

| 菜单名称 | 组件路径 | 路由 path（建议） | 权限标识 |
|----------|----------|-------------------|----------|
| 售后管理 | `agri/seller/aftersale/index` | `seller/aftersale` | `agri:seller:aftersale:list` |

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

| 按钮权限 | 标识 | 页面落点 |
|----------|------|----------|
| 列表 | `agri:seller:aftersale:list` | 进入列表页 |
| 详情 | `agri:seller:aftersale:query` | 操作列「查看详情」 |
| 处理完结 | `agri:seller:aftersale:finish` | 操作列/详情「处理售后」、确认完结 |

---

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

```text
售后管理 index.vue
├── 检索区 search-card
│   ├── 订单编号 orderNo（模糊）
│   ├── 售后编号 aftersaleNo（模糊）
│   ├── 申请类型 applyType（1/2/3，可清空）
│   └── 搜索 / 重置
├── 列表区 table-card
│   ├── 售后状态页签 statusTab：全部(all) | 进行中(1) | 已完结(2)
│   ├── right-toolbar
│   ├── el-table border（empty-text 动态）
│   │   ├── 售后编号 aftersaleNo
│   │   ├── 申请类型 applyTypeText
│   │   ├── 售后订单信息 min-width=300（订单号+商品快照+会员/收货/地址）
│   │   ├── 售后原因 applyReason
│   │   ├── 退货数量（类型1为 —）
│   │   ├── 申请金额 applyAmount
│   │   ├── 申请时间 createTime
│   │   ├── 售后状态 Tag
│   │   ├── 完结时间 finishTime
│   │   └── 操作：查看详情 / 处理售后（仅 status=1）
│   └── pagination
├── 详情抽屉 SellerAftersaleDetail（见 §6）
├── 订单详情抽屉 SellerOrderDetail（只读嵌套）
└── 处理售后弹窗 el-dialog width=560px
    ├── 警告提示（不可回退、不自动退款）
    └── processResult textarea maxlength=500
```

**不提供：** 页内店铺选择、修改买家申请内容、代客发起、批量处理、导出。

---

## 6. 详情抽屉 detail.vue

### 6.1 对外接口

| 类型 | 名称 | 说明 |
|------|------|------|
| props | `visible` | 抽屉显隐（`.sync`） |
| props | `aftersaleId` | 当前售后主键 |
| emit | `update:visible` | 关闭抽屉 |
| emit | `finish` | 点击「处理售后」，payload 为详情对象 |
| emit | `view-order` | 点击订单编号，payload 含 `orderId` |
| 方法 | `reload()` | 完结成功后父页调用重载详情 |

### 6.2 分区结构

| 区块 | 内容 |
|------|------|
| 流程进度 | `el-steps`：商家处理 → 售后完结（`active=0` 进行中 / `2` 已完结） |
| 售后信息 | 编号、状态、类型、原因、退货数量、申请金额、申请/完结时间 |
| 商品信息 | 主图+名称+规格+购买数量（快照） |
| 订单信息 | 订单编号（可点）、订单总金额 |
| 买家/收货 | 会员名称、收货人、手机、地址 |
| 补充说明 | 买家 `description`（有则展示） |
| 凭证图片 | `evidencePics[]` 网格预览 |
| 处理结果 | 已完结展示 `processResult`；进行中提示待处理 |
| 操作栏 | 进行中显示「处理售后」（finish 权限） |

---

## 7. 处理完结交互

```text
列表/详情 → 处理售后
    → 打开 finish 弹窗，填写 processResult
    → $modal.confirm 二次确认
    → PUT /{aftersaleId}/finish
    → msgSuccess → 刷新列表 → 若详情打开则 reload()
```

| 校验 | 说明 |
|------|------|
| processResult 必填 | 对应 ASM7 / 后端 MSG |
| maxlength=500 | 对齐需求 §8.2（后端上限 1000，前端按需求 500） |
| 仅 status=`1` | 已完结隐藏处理按钮 |

---

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

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

---

## 9. API 封装

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

| 方法 | HTTP | 路径 | 权限 |
|------|------|------|------|
| `listSellerAftersale` | GET | `/list` | list |
| `getSellerAftersale` | GET | `/{aftersaleId}` | query |
| `finishSellerAftersale` | PUT | `/{aftersaleId}/finish` | finish |

**列表 Query：** `pageNum`、`pageSize`、`aftersaleNo`、`orderNo`、`applyType`、`aftersaleStatus`。

**完结 Body：** `{ "processResult": "..." }`

**排序：** 后端固定 `create_time DESC`（申请时间最新在前）。

---

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

| 场景 | 前端表现 |
|------|----------|
| 无数据 | 「暂无售后申请」 |
| 有筛选无结果 | 「未找到符合条件的售后申请」 |
| 处理结果为空 | 表单校验「请填写处理结果」 |
| 二次确认取消 | 不提交 |
| 提交成功 | 「售后已完结」 |
| 已完结重复处理 | 展示后端「售后已完结，不可重复处理」 |

---

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

| 模块 | 关系 |
|------|------|
| **全部订单** | 详情内嵌订单抽屉只读查看；**不在** 订单页完结售后 |
| **发货管理** | 独立菜单；履约与售后并行 |
| **评价管理** | 独立；售后不影响评价入口 |
| **商品入库** | 退货加库存须商家手工「退货入库」，**不** 随售后自动 |

---

## 12. 联调检查清单

- [ ] 菜单挂载 `agri/seller/aftersale/index`，路由可访问
- [ ] 列表默认按申请时间倒序，仅当前店铺数据
- [ ] 页签：全部 / 进行中 / 已完结 与 `aftersaleStatus` 联动
- [ ] 检索：订单编号、售后编号、申请类型组合过滤
- [ ] 进行中显示「处理售后」；已完结隐藏
- [ ] 完结后 C 端详情可读 `processResult` 与进度「售后完结」
- [ ] 凭证图点击预览；列表手机为脱敏值
- [ ] 订单编号打开嵌套订单详情抽屉
- [ ] Navbar 切店后列表刷新
- [ ] 权限：无 `finish` 不显示处理按钮

---

## 13. 版本记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版：列表/检索/详情抽屉/处理完结弹窗/API 封装；对齐需求 v1.0 与 C 端售后协作口径 |

---

*文档版本：v1.0 · 依据《售后管理功能需求.md》v1.0、《售后管理技术方案.md》v1.0*
