web-feature-development.md 7.3 KB

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/postsystem/usersystem/rolesystem/noticelogin 还是其他模块,再按相似模块的页面结构、API 类型、hook、表单、字典、表格、分页、排序、删除、导出、权限和组件命名风格实现。

目录结构

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

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

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

components/rule.tstypes.ts 按需创建,不要求每个模块都存在。

页面私有组件

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

页面私有组件适合承载:

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

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

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

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

各文件职责

index.vue 负责页面结构:

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

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

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

form.vuexxx-form-modal.vue 负责新增/编辑:

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

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

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

API 规范

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

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

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

文件下载使用:

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() 传给后端。

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

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

字典和状态

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

下拉选项使用:

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

表格状态渲染使用:

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

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

交互和组件

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

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

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

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

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

权限、路由和菜单

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

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

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

验证清单

前端改动后优先运行:

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

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

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

业务页面至少检查:

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