Files
2026-09-01 14:50:50 +08:00

5.0 KiB
Raw Permalink Blame History

clinical-web 开发规范

技术栈与边界

  • 使用 Vue 3、TypeScript、Vite、Vue Router、Pinia、Element Plus 和 Axios。
  • 所有接口请求统一经过 src/utils/request.ts 的单例实例。
  • 开发环境默认使用 /api,由 vite.config.ts 代理到后端;不要在页面中直接拼接后端域名。
  • 当前除登录外的业务接口可以使用 Mock但 Mock 数据必须遵守真实接口类型。

目录约定

src/
├─ api/
│  ├─ auth.ts
│  ├─ workbench/
│  │  ├─ home.ts
│  │  ├─ signing.ts
│  │  └─ types.ts
│  └─ management/
│     ├─ documents.ts
│     ├─ reports.ts
│     ├─ users.ts
│     ├─ permissions.ts
│     ├─ document-permissions.ts
│     ├─ settings.ts
│     └─ types.ts
├─ components/
│  ├─ layout/
│  └─ signing/
├─ composables/
├─ stores/
├─ types/
├─ utils/
└─ views/
   ├─ auth/
   ├─ workbench/
   └─ management/
  • API 文件按页面业务域对应;管理端直接使用 documents.tsreports.ts 等文件,不再额外套同名目录。
  • 每个具体 View 使用 index.vue 作为入口;当前页面专用组件放在同级 components
  • 跨页面的领域组件放在 src/components,布局组件统一放在 src/components/layout
  • API DTO 类型放在对应 API 业务域的 types.ts;页面展示和交互类型放在 View 的 types.ts;全局类型放在 src/types/common.ts

API 规范

  • API 文件只负责请求、响应类型和必要的数据标准化。
  • 不在 API 文件中创建新的 Axios 实例,不在 API 层操作组件、路由或 ElMessage
  • 所有请求通过 request 单例发送;接口函数必须提供明确的返回类型。
  • 接口地址使用相对路径,不重复写基础地址。
  • 业务接口的列表、详情、创建、更新和删除函数按资源命名,例如:
import { request } from '@/utils/request'
import type { ReportOverviewResponse, ReportQuery } from './types'

export function getReportOverview(params: ReportQuery) {
  return request.get<ReportOverviewResponse>('/management/reports/overview', { params })
}
  • 响应包装、错误码和后端 DTO 与页面展示模型不一致时,在 API 边界或 composable 中转换,组件不负责猜测接口结构。
  • 登录 Token、用户信息和本地持久化只由 stores/login.tsutils/auth-storage.ts 管理,页面不直接操作 localStorage

页面与组件拆分

  • views/**/index.vue 负责页面编排、接口调用、加载/错误状态、筛选条件和路由联动。
  • 当前页面专用组件负责展示和交互,通过类型化 props 接收数据,通过 emit 通知父组件。
  • 展示型组件不直接调用 API、不直接修改全局 Store确有跨页面业务复用时放入领域组件并明确其边界。
  • 组件只承担一个清晰职责。不要为了减少文件长度机械拆分小片段,也不要让页面入口堆积复杂表单和弹窗逻辑。
  • 复杂表单、弹窗流程、校验、提交和异步状态抽到 composables/useXxx.ts;弹窗组件负责表单渲染和事件呈现。
  • 签署任务新增流程由 components/signing/NewSigningTaskDialog.vuecomposables/useSigningTaskForm.ts 共同维护,供签署工作台和文档库复用。

状态管理

  • 页面只使用一次的表格数据、筛选条件、弹窗显隐和 loading优先使用页面内的 refreactive 和 composable。
  • 只有跨页面共享、需要持久化或代表全局上下文的状态才进入 Pinia。
  • Store 按业务域拆分,并统一使用 Composition API 写法:
export const useExampleStore = defineStore('example', () => {
  const value = ref('')

  const isReady = computed(() => Boolean(value.value))

  function setValue(nextValue: string) {
    value.value = nextValue
  }

  return { value, isReady, setValue }
})
  • 不使用 Pinia 的 stategettersactions 配置式写法。
  • stores/app.ts 只管理院区、环境等应用上下文。
  • stores/login.ts 只管理登录态、用户信息、Token 和登录/退出流程。
  • 不把所有业务数据集中到 app Store签署任务列表等页面数据默认留在页面或 composable 中。
  • 避免 Store 之间互相依赖,尤其不要让 request.ts 静态依赖业务 Store防止循环依赖。

类型与命名

  • 不使用无理由的 any;后端字段不确定时先定义 unknown,在边界处完成校验或标准化。
  • Vue 组件使用 PascalCaseAPI、composable、Store 和工具文件使用 kebab-case 或项目已有命名方式。
  • useXxx 表示 composableuseXxxStore 表示 Pinia Store。
  • 类型名描述业务含义,避免使用 DataInfo 等无语义名称。

提交前检查

npm run lint
npm run format:check
npx vue-tsc --noEmit
npm run build

如果构建被本机 node_modules 或已有 dist 的权限问题阻断,应保留错误信息并使用临时输出目录验证,不要删除或覆盖已有构建产物。