# 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///`: ```text frontend/web/src/views/// ├── 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//.ts`: ```text frontend/web/src/api//.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//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//.ts`。 ## API 规范 每个业务模块在 `src/api//.ts` 中封装后端接口和 TypeScript 类型。 接口调用使用项目 HTTP 封装: ```ts http.request>("get", "/path", { params }); http.request>("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 文件,应优先运行上述检查。 业务页面至少检查: - 列表加载。 - 搜索和重置。 - 分页和排序。 - 新增和编辑。 - 单条删除和批量删除。 - 导出。 - 字典下拉和表格状态展示。 - 弹窗关闭、成功回调和列表刷新。