@@ -0,0 +1,272 @@
|
||||
# 备货单 / 终版列表「全部 / 我的」视图切换 实现计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 在 `stocking_list.vue` 与 `final_list.vue` 给 `userId === 73` 的特权用户增加「全部 / 我的」视图切换,切换时给列表接口传 `view_scope=all|self`。
|
||||
|
||||
**Architecture:** 纯前端 UI + 传参改动。新增本地状态 `viewScope`,工具栏渲染条件 `canViewAll = userStore.getUserInfo.userId === 73`;`getList` 把 `viewScope.value` 作为 `view_scope` 随请求发出;切换时重置页码并重新请求。是否返回全量数据由后端按 token 鉴权决定(后端改造见 spec 第 3.4 节,不在本计划内)。
|
||||
|
||||
**Tech Stack:** Vue 3 `<script setup>` + TypeScript、element-plus 2.8、Pinia(`useUserStore`)、`unplugin-auto-import`(`ref/reactive/computed/onMounted/watch` 自动导入,无需手写 import)。
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- **无前端单元测试框架**:本项目 `package.json` 无 `test` 脚本。验收方式 = `lint` 通过 + 本地 `dev` 手动核对,不写自动化测试。
|
||||
- **后端是前置依赖**:前端「全部」真正生效需后端 `GET /crm/stock/order/list` 支持 `view_scope` 参数(见 spec 3.4)。前端可先行独立完成 UI 与传参;完整功能联调待后端就绪。
|
||||
- **安全边界在后端**:前端 `userId === 73` 仅控制按钮显隐,非授权依据。
|
||||
- **代码风格**:遵循现有写法——`<script setup lang="ts">`、element-plus 组件、snake_case 请求参数。
|
||||
- **commit 规范**:每任务一次 commit,信息以 `feat:` 开头。
|
||||
|
||||
## 文件结构
|
||||
|
||||
| 文件 | 责任 | 改动类型 |
|
||||
|---|---|---|
|
||||
| `src/views/crmpc/stocking/stocking_list.vue` | 备货单列表,加视图切换 + `view_scope` 传参 | Modify |
|
||||
| `src/views/crmpc/stocking/final_list.vue` | 终版列表,同构改动 | Modify |
|
||||
|
||||
两文件改动完全同构,仅 `is_final`(0 vs 1)不同。不新建文件。
|
||||
|
||||
---
|
||||
|
||||
## Task 1: stocking_list.vue 增加视图切换
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/views/crmpc/stocking/stocking_list.vue`
|
||||
- 模板工具栏(约 `line 9-22` 的 `toolbar-right`)
|
||||
- `getList` 参数(约 `line 339-344`)
|
||||
- script 状态(约 `line 280-281` `useAuth` 之后)
|
||||
- script import(约 `line 264-274`)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `useUserStore`(来自 `@/store/modules/user`),其 `getUserInfo: computed => Partial<{ userId: number }>`;`CrmService.getStockList(params)`(已存在)。
|
||||
- Produces: 列表请求新增参数字段 `view_scope: 'all' | 'self'`。
|
||||
|
||||
- [ ] **Step 1: 添加 import**
|
||||
|
||||
在 `<script setup lang="ts">` 顶部 import 区(`import { useAuth } from '@/composables/useAuth'` 同处)增加一行:
|
||||
|
||||
```ts
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加状态与切换处理**
|
||||
|
||||
在 `const { hasAuth } = useAuth()`(约 line 281)之后插入:
|
||||
|
||||
```ts
|
||||
const userStore = useUserStore()
|
||||
// 仅 userId=73 的特权用户可切换「全部/我的」
|
||||
const canViewAll = computed(() => userStore.getUserInfo.userId === 73)
|
||||
const viewScope = ref<'all' | 'self'>('self')
|
||||
const onScopeChange = () => {
|
||||
pagination.currentPage = 1
|
||||
getList()
|
||||
}
|
||||
```
|
||||
|
||||
说明:`computed` / `ref` 由 `unplugin-auto-import` 提供,无需 import。`getUserInfo.userId` 类型为 `number | undefined`,`undefined === 73` 为 `false`,判空安全。
|
||||
|
||||
- [ ] **Step 3: 修改 `getList` 请求参数**
|
||||
|
||||
定位 `getList` 内(约 line 339-344):
|
||||
|
||||
```ts
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
is_final: 0, // 只获取备货单,非终版
|
||||
keyword: keyword.value
|
||||
}
|
||||
```
|
||||
|
||||
改为(增加 `view_scope`):
|
||||
|
||||
```ts
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
is_final: 0, // 只获取备货单,非终版
|
||||
keyword: keyword.value,
|
||||
view_scope: viewScope.value
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 工具栏添加切换控件**
|
||||
|
||||
定位模板 `toolbar-right`(约 line 9-21),在 `<el-input>` 之前插入 `el-radio-group`:
|
||||
|
||||
原:
|
||||
|
||||
```html
|
||||
<div class="toolbar-right">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```html
|
||||
<div class="toolbar-right">
|
||||
<el-radio-group
|
||||
v-if="canViewAll"
|
||||
v-model="viewScope"
|
||||
@change="onScopeChange"
|
||||
style="margin-right: 12px"
|
||||
>
|
||||
<el-radio-button value="self">我的</el-radio-button>
|
||||
<el-radio-button value="all">全部</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
```
|
||||
|
||||
说明:用 `value`(element-plus 2.6+ 推荐)而非 `label` 作值,避免 2.8 的 label 弃用告警。文字走默认插槽。
|
||||
|
||||
- [ ] **Step 5: Lint 校验**
|
||||
|
||||
Run: `npm run fix -- src/views/crmpc/stocking/stocking_list.vue`
|
||||
Expected: 无 error。若有,按提示修复(常见:prettier 格式)。
|
||||
|
||||
- [ ] **Step 6: 手动核对(前端独立部分)**
|
||||
|
||||
Run: `npm run dev`,浏览器打开备货单列表页。
|
||||
|
||||
核对项:
|
||||
1. 用**非 73 账号**登录:工具栏**不出现**「全部/我的」控件,列表行为同改动前。
|
||||
2. 打开 DevTools Network,看 `/crm/stock/order/list` 请求 Payload:无论是否 73,都应带上 `view_scope=self`(默认值)。
|
||||
3. (需 73 登录态)用 73 账号登录:出现「全部/我的」,默认选中「我的」;点「全部」后 Network 请求带 `view_scope=all`,点回「我的」带 `view_scope=self`,且每次切换分页回到第 1 页。
|
||||
4. 「全部」是否真返回全量数据,取决于后端是否已支持——后端未就绪时,切到「全部」数据可能不变,属预期。
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add src/views/crmpc/stocking/stocking_list.vue
|
||||
git commit -m "feat(stocking): 备货单列表为特权用户增加「全部/我的」视图切换
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: final_list.vue 增加视图切换(同构)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/views/crmpc/stocking/final_list.vue`
|
||||
- 模板工具栏(约 `line 7-19` 的 `toolbar-right`)
|
||||
- `getList` 参数(约 `line 278-283`)
|
||||
- script 状态(约 `line 229` `useAuth` 之后)
|
||||
- script import(约 `line 206-216`)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: 同 Task 1(`useUserStore`、`CrmService.getStockList`)。
|
||||
- Produces: 同 Task 1(`view_scope` 参数)。
|
||||
|
||||
- [ ] **Step 1: 添加 import**
|
||||
|
||||
在 `<script setup lang="ts">` 顶部 import 区增加:
|
||||
|
||||
```ts
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加状态与切换处理**
|
||||
|
||||
在 `const { hasAuth } = useAuth()`(约 line 229)之后插入:
|
||||
|
||||
```ts
|
||||
const userStore = useUserStore()
|
||||
// 仅 userId=73 的特权用户可切换「全部/我的」
|
||||
const canViewAll = computed(() => userStore.getUserInfo.userId === 73)
|
||||
const viewScope = ref<'all' | 'self'>('self')
|
||||
const onScopeChange = () => {
|
||||
pagination.currentPage = 1
|
||||
getList()
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 修改 `getList` 请求参数**
|
||||
|
||||
定位 `getList` 内(约 line 278-283):
|
||||
|
||||
```ts
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
is_final: 1, // 只获取备货单,非终版
|
||||
keyword: keyword.value
|
||||
}
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
is_final: 1, // 终版
|
||||
keyword: keyword.value,
|
||||
view_scope: viewScope.value
|
||||
}
|
||||
```
|
||||
|
||||
(注:原代码注释「非终版」是笔误,此处顺手修正为「终版」。)
|
||||
|
||||
- [ ] **Step 4: 工具栏添加切换控件**
|
||||
|
||||
定位模板 `toolbar-right`(约 line 7-19),在 `<el-input>` 之前插入:
|
||||
|
||||
原:
|
||||
|
||||
```html
|
||||
<div class="toolbar-right">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```html
|
||||
<div class="toolbar-right">
|
||||
<el-radio-group
|
||||
v-if="canViewAll"
|
||||
v-model="viewScope"
|
||||
@change="onScopeChange"
|
||||
style="margin-right: 12px"
|
||||
>
|
||||
<el-radio-button value="self">我的</el-radio-button>
|
||||
<el-radio-button value="all">全部</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Lint 校验**
|
||||
|
||||
Run: `npm run fix -- src/views/crmpc/stocking/final_list.vue`
|
||||
Expected: 无 error。
|
||||
|
||||
- [ ] **Step 6: 手动核对(前端独立部分)**
|
||||
|
||||
Run: `npm run dev`,浏览器打开终版列表页。核对项同 Task 1 Step 6(注意此处是 `is_final=1` 的终版页)。
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add src/views/crmpc/stocking/final_list.vue
|
||||
git commit -m "feat(stocking): 终版列表为特权用户增加「全部/我的」视图切换
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后端联调清单(交付后端,不在本计划执行范围)
|
||||
|
||||
接口 `GET /crm/stock/order/list` 增加可选参数 `view_scope`:
|
||||
|
||||
| 请求 | 后端行为 |
|
||||
|---|---|
|
||||
| 不传 / `view_scope=self` | 按现状:依 token 用户过滤 |
|
||||
| `view_scope=all` 且 token 用户 id ∈ {73} | 返回全量 |
|
||||
| `view_scope=all` 且 token 用户 id ∉ {73} | 降级为 `self`(返回该用户自己的) |
|
||||
|
||||
要点:**只信 token,忽略前端任何用户标识**,防 IDOR。
|
||||
Reference in New Issue
Block a user