# 我的店铺关注 — 前端技术方案（C 端 · shop-app）

> **依据：** 《我的店铺关注功能需求.md》v1.0、《我的店铺关注技术方案.md》v1.0（仅作接口对照，**本文档独立维护**）  
> **关联：** 《店铺主页前端技术方案》、《我的服务前端技术方案》  
> **范围：** C 端 **已关注店铺列表、进店、列表内取消关注**；**不** 改后端、**不** 在本模块新增关注（在店铺主页完成）。  
> **实现状态：** 页面与 API 封装已落地，待与 `GET /api/member/shop-follows` 联调。

---

## 1. 技术栈与约定

| 项 | 说明 |
|----|------|
| 框架 | uni-app **Vue 3** + **uview-plus** |
| 请求 | `@/utils/request`；列表为 **`TableDataInfo`**（`rows` + `total`） |
| 鉴权 | **须登录**；`ensureApiToken` / `navigateMinePage` 与我的服务 **MS0** 一致 |
| 写操作 | 取消关注 **复用** `DELETE /api/shop/{shopId}/follow`（`api/shop.js`） |
| 展示映射 | `mapShopFollowList`（`utils/shopDisplay.js`），字段口径与店铺主页一致 |
| 导航返回 | `SafeNavBar` + `smartNavigateBack`（从 Tab「我的」进入分包） |

---

## 2. 页面与路由

| 页面 | 需求代号 | 路径 | 入口 |
|------|----------|------|------|
| 我的店铺关注列表 | **A** | `subpackage/account/shop-follow-list` | 我的 Tab →「我的店铺关注」 |

**`pages.json`：** 分包 `pkg-account`，`navigationStyle: custom`（与入驻列表一致）

**路径常量：** `PAGE_SHOP_FOLLOW_LIST`（`utils/pageRoute.js`）

**无 Query 参数**；未登录由 `ensureApiToken` 拦截并引导登录。

---

## 3. 文件清单

| 类型 | 路径 | 说明 |
|------|------|------|
| 列表页 | `shop-app/subpackage/account/shop-follow-list.vue` | 下拉刷新、上拉分页、空态、取关 |
| 列表 UI | `shop-app/components/shop/ShopFollowList.vue` | 店铺卡片 +「取消关注」 |
| 会员 API | `shop-app/api/member.js` | `getShopFollowList` |
| 店铺 API | `shop-app/api/shop.js` | `unfollowShop`（复用） |
| 常量 | `shop-app/constants/shopFollow.js` | 分页大小、空态文案 |
| 我的 Tab | `shop-app/pages/mine/index.vue` | 菜单「我的店铺关注」 |

---

## 4. 接口封装

### 4.1 列表（本模块）

| 方法 | HTTP | 路径 | 鉴权 |
|------|------|------|------|
| `getShopFollowList(params)` | GET | `/api/member/shop-follows` | **须 Token** |

**Query：** `pageNum`、`pageSize`（默认 10，`SHOP_FOLLOW_PAGE_SIZE`）

**`rows` 元素映射为：**

| 字段 | 前端用途 |
|------|----------|
| shopId | 进店、取关 |
| shopName | 卡片标题 |
| shopAvatar | `displayAvatar` |
| shopStatus | `isClosed` →「休息中」标签 |
| rating | `showRating` / `ratingText` |
| fansCount | 粉丝展示 |
| followTime | 仅排序依据，**UI 不展示** |

### 4.2 取消关注（复用店铺主页）

| 方法 | HTTP | 路径 |
|------|------|------|
| `unfollowShop(shopId)` | DELETE | `/api/shop/{shopId}/follow` |

成功后 **本地移除** 该项（可选再调列表刷新；当前实现为本地 filter）。

### 4.3 进店

| 方法 | 说明 |
|------|------|
| `goShopHome(shopId)` | `utils/shopNav.js` → 店铺主页 |

---

## 5. 页面逻辑（`shop-follow-list.vue`）

```text
onShow
  → ensureApiToken(false) 未登录则 return
  → reloadList(true) 拉第一页

下拉刷新
  → refresherrefresh → reloadList(true)

上拉触底
  → scrolltolower → fetchPage(false)

点击卡片
  → goShopHome(shopId)

点击「取消关注」
  → showModal 确认
  → unfollowShop
  → 成功：列表移除该项 + Toast

空列表
  → 「暂无关注的店铺」+ 按钮「去逛逛」→ switchTab 首页
```

| 规则编号 | 前端实现 |
|----------|----------|
| SF0 / SF2 | `ensureApiToken`、`navigateMinePage` |
| SF3 | 后端 `ORDER BY create_time DESC` |
| SF5 | 整卡点击进店 |
| SF6 | 本页 **无** 关注按钮 |
| SF7 | 已删店由后端 JOIN 过滤 |
| SF8 | 取关后项消失 |
| 停业展示 | `shopStatus=1` 显示「休息中」，仍可进店 |

---

## 6. 组件说明（`ShopFollowList.vue`）

- 布局对齐 `components/search/ShopList.vue`，增加 **取消关注**（`@click.stop` 防冒泡）
- 停业标签文案：`SHOP_CLOSED_LABEL`（`constants/shop.js`）

---

## 7. 异常与空态

| 情形 | 行为 |
|------|------|
| 从未关注 | `u-empty` +「去逛逛」 |
| 首屏加载失败 | 错误区 +「点击重试」 |
| 取关失败 | request Toast；**不** 移除列表项 |
| Token 失效 | request 401 → 登录引导（全局拦截） |

---

## 8. 联调检查清单

- [ ] 登录后 `GET /api/member/shop-follows` 返回分页数据
- [ ] 关注时间倒序与店主页关注后刷新可见
- [ ] 点击进店跳转 `subpackage/shop/index?shopId=`
- [ ] 列表取关后粉丝数在店主页同步（刷新店主页）
- [ ] 已删店不出现在列表
- [ ] 停业店显示「休息中」且可进店
- [ ] 未登录从「我的」点菜单 → 引导登录

---

## 9. 非本期（前端不实现）

| 项 | 说明 |
|------|------|
| 列表内搜索/筛选 | 需求未要求 |
| 本页新增关注 | 仅店铺主页 |
| 批量取消关注 | 未要求 |
| 展示关注时间 | UI 可不展示 |

---

## 10. 修订记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版：关注列表页、分页、下拉刷新、取关、我的 Tab 入口 |

---

*文档版本：v1.0 · 不修改《我的店铺关注技术方案.md》（后端方案）。*
