# Web 前端业务功能开发规范

本文是 `frontend/web` 新增或修改前端业务功能时的权威规范。当前 Web 项目基于 Vue 3、Vite、Element Plus、Pure Admin、Pinia、Vue Router 和项目自有 `@/utils/http` 封装。开发时必须优先复用现有目录结构、组件和工具链。

本文只覆盖 `frontend/web`，不覆盖 `frontend/app`。

## 架构原则

前端业务模块按“页面结构 + API 类型封装 + 业务 hook + 表单/弹窗 + 页面私有组件”组织。

优先参考这些现有模块：

- `frontend/web/src/views/system/post`
- `frontend/web/src/views/system/user`
- `frontend/web/src/views/system/role`
- `frontend/web/src/views/system/notice`
- `frontend/web/src/views/login`
- `frontend/web/src/api/system/post.ts`

不要绕过 `@/utils/http` 直接使用 Axios。不要在 `index.vue` 中堆积复杂列表逻辑。不要硬编码字典状态文本、值或标签样式。

## 开发准备

开发 `frontend/web` 业务功能前，应先确认页面所属业务域、后端接口、菜单路由、权限标识、字典依赖和相似页面。本文是 Web 前端业务功能开发的统一规范。

开始编码前必须先做一次相似模块调研，确认目标功能最接近 `system/post`、`system/user`、`system/role`、`system/notice`、`login` 还是其他模块，再按相似模块的页面结构、API 类型、hook、表单、字典、表格、分页、排序、删除、导出、权限和组件命名风格实现。

## 目录结构

业务页面放在 `frontend/web/src/views/<area>/<module>/`：

```text
frontend/web/src/views/<area>/<module>/
├── index.vue
├── form.vue 或 xxx-form-modal.vue
├── components/
│   ├── private-panel.vue
│   └── private-fragment.vue
└── utils/
    ├── hook.tsx
    ├── rule.ts
    └── types.ts
```

API 放在 `frontend/web/src/api/<area>/<module>.ts`：

```text
frontend/web/src/api/<area>/<module>.ts
```

`components/`、`rule.ts`、`types.ts` 按需创建，不要求每个模块都存在。

## 页面私有组件

只被某个页面或模块使用的组件，放在该模块目录下的 `components/`。现有示例是 `frontend/web/src/views/login/components`。

页面私有组件适合承载：

- 页面内局部展示组件。
- 局部表单片段。
- 局部抽屉、局部面板、局部弹窗。
- 只服务当前页面流程的交互单元。

页面私有组件不应承载整个业务模块的主流程。主列表、主表单、主弹窗仍按现有模块风格放在 `index.vue`、`form.vue` 或 `xxx-form-modal.vue`。

如果组件开始被第二个业务模块复用，应提升到 `frontend/web/src/components`，不要继续放在某个页面私有目录中。只有在明确属于某个业务域且多个同域模块复用时，才考虑创建 `views/<area>/components`；当前项目还没有这个模式，新增前应谨慎。

页面私有组件的判断标准是“是否只服务当前页面或当前模块”。如果组件包含通用交互、通用展示、通用上传、通用选择器等能力，应优先设计为公共组件，而不是藏在某个业务页面目录下。

## 各文件职责

`index.vue` 负责页面结构：

- 搜索表单。
- 表格和表格插槽。
- 工具栏按钮。
- 弹窗、抽屉、页面私有组件的挂载。
- `defineOptions({ name })`，组件 name 应与菜单表中的 `router_name` 保持一致。

`utils/hook.tsx` 负责列表业务状态和行为：

- 查询参数、分页、排序、loading。
- 表格列定义。
- 列表查询、搜索、重置。
- 删除、批量删除、导出。
- 字典值渲染，例如 `el-tag`。

`form.vue` 或 `xxx-form-modal.vue` 负责新增/编辑：

- 接收 `v-model` 控制显示。
- 接收 `type`、`row` 等必要 props。
- 维护表单数据和校验状态。
- 调用新增/修改 API。
- 成功后关闭并向父组件发出 `success` 事件。

`utils/rule.ts` 负责复杂或可复用的表单校验规则。简单模块也可以把规则放在表单组件内，保持与现有代码一致。

`utils/types.ts` 只放页面私有类型。跨 API 复用的请求/响应类型优先放在对应 `src/api/<area>/<module>.ts`。

## API 规范

每个业务模块在 `src/api/<area>/<module>.ts` 中封装后端接口和 TypeScript 类型。

接口调用使用项目 HTTP 封装：

```ts
http.request<ResponseData<T>>("get", "/path", { params });
http.request<ResponseData<void>>("post", "/path", { data });
```

文件下载使用：

```ts
http.download("/path/excel", fileName, { params });
```

类型命名优先遵循现有风格：

- `XxxListCommand`：列表查询参数。
- `XxxPageResponse`：列表响应行。
- `AddXxxCommand`：新增请求。
- `UpdateXxxCommand`：修改请求。

删除接口如果后端接收 `ids` 查询参数，按现有写法把数组转成字符串，避免 Axios 序列化为 `ids[0]`、`ids[1]`。

## 列表、分页和排序

列表页优先使用 `PureTableBar + pure-table`。

分页使用 `PaginationProps`，并通过 `CommonUtils.fillPaginationParams()` 填充查询参数。

排序使用 Element Plus `Sort`，并通过 `CommonUtils.fillSortParams()` 传给后端。

时间范围查询使用 `beginTime`、`endTime`，优先复用现有 computed 写法处理 `el-date-picker` 的双向绑定。

批量操作必须维护 `multipleSelection`，并在空选择时给出提示。

## 字典和状态

字典来自登录配置并缓存在用户 store 中。

下拉选项使用：

```ts
useUserStoreHook().dictionaryList["common.status"]
```

表格状态渲染使用：

```ts
useUserStoreHook().dictionaryMap["common.status"]
```

不要在页面中硬编码状态 label、value、`el-tag` 类型。字典不存在或可能为空时，新增代码应避免直接访问导致运行时报错。

## 交互和组件

按钮图标优先使用 `useRenderIcon()` 和现有 iconify 图标。

新增/编辑弹窗优先复用 `VDialog` 或相似模块的弹窗写法。

单条删除使用 `el-popconfirm` 或模块现有风格。批量删除使用 `ElMessageBox.confirm` 时，必须处理取消分支并清理选择状态。

操作成功后刷新列表。弹窗通过 `v-model` 和 `success` 事件与父组件通信。

跨业务通用能力优先放在 `src/components` 或 `src/utils`；页面私有能力保持在页面目录内。

## 权限、路由和菜单

后台业务菜单主要依赖后端菜单和动态路由。新页面的组件 name 必须与菜单表中的 `router_name` 保持一致。

不要随意新增静态路由。只有登录页、重定向、个人中心等固定页面才参考 `src/router/modules` 中的静态路由模式。

如需要按钮权限，优先复用项目已有的 `ReAuth` 或 auth directive 模式，不要自行实现一套权限判断。

## 验证清单

前端改动后优先运行：

```bash
pnpm --dir frontend/web typecheck
pnpm --dir frontend/web lint
```

如果依赖未安装或环境不允许运行，说明原因。

如果只修改文档，不需要运行前端 typecheck 或 lint；如果修改了 TypeScript、Vue、样式或路由/API 文件，应优先运行上述检查。

业务页面至少检查：

- 列表加载。
- 搜索和重置。
- 分页和排序。
- 新增和编辑。
- 单条删除和批量删除。
- 导出。
- 字典下拉和表格状态展示。
- 弹窗关闭、成功回调和列表刷新。
