|
@@ -2,6 +2,7 @@
|
|
|
|
|
|
|
|
> Vue3 管理端,承载**教务域重写** + **教学 admin 复用**两块页面,统一走 nginx 入口 + 统一 Go 后端 SSO。
|
|
> Vue3 管理端,承载**教务域重写** + **教学 admin 复用**两块页面,统一走 nginx 入口 + 统一 Go 后端 SSO。
|
|
|
> 本文档覆盖四块:**技术栈 / 目录结构与用途 / 编码规范 / 构建**。接口契约与角色权限见工作区文档库 `unified/`、`academic/05-architecture/`、`teaching/05-architecture/`(本仓库不复制契约原文)。
|
|
> 本文档覆盖四块:**技术栈 / 目录结构与用途 / 编码规范 / 构建**。接口契约与角色权限见工作区文档库 `unified/`、`academic/05-architecture/`、`teaching/05-architecture/`(本仓库不复制契约原文)。
|
|
|
|
|
+> 框架层选型(2026-11-06 定稿):**UMD 全局脚本 + 自托管 OSS + 业务层本地编译 + 默认简体中文**。
|
|
|
|
|
|
|
|
## 定位
|
|
## 定位
|
|
|
|
|
|
|
@@ -9,108 +10,209 @@
|
|
|
|
|
|
|
|
## 1. 技术栈
|
|
## 1. 技术栈
|
|
|
|
|
|
|
|
-| 关注点 | 选型 | 说明 / 依据 |
|
|
|
|
|
|
|
+### 1.1 资源双层结构(核心选型)
|
|
|
|
|
+
|
|
|
|
|
+| 层 | 内容 | 引入方式 | 版本管理 |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| **框架层** | Vue 3 / Vue Router 4 / Pinia / Element Plus(JS + CSS + zh-cn locale)/ axios,共 7 项 UMD 资源 | `index.html` 内 `<link>`/`<script>` 按**固定顺序**引入,源文件**自托管到自有 OSS**(自定义域名 + SSL,不用第三方 CDN) | 版本事实 = `index.html` 头部"vendor 版本表"(钉死次版本)+ 仓库 `vendor/` 源文件(git 入库) |
|
|
|
|
|
+| **业务层** | 本项目全部业务代码(TS 编写) | 本地 `pnpm` 工具链编译(Vite/vue-tsc/ESLint/Prettier 为 dev 依赖),产物 `dist/` 经 nginx 部署 | `pnpm-lock.yaml` 入库(只锁工具链,不含框架) |
|
|
|
|
|
+
|
|
|
|
|
+- **开发测试**:`pnpm dev` 起 Vite 本地服务(HMR 只热更业务代码,框架层从自托管 OSS 加载);`/api` 代理到后端 dev 地址。
|
|
|
|
|
+- **构建**:`pnpm build` 只打包业务代码 → `dist/`;**产物禁止包含框架运行时**(质量门禁 grep 抽查,防 external 配错)。
|
|
|
|
|
+
|
|
|
|
|
+### 1.2 框架层 UMD 资源清单(加载顺序固定,版本以 `index.html` 版本表为准)
|
|
|
|
|
+
|
|
|
|
|
+| # | 资源 | OSS 路径模板 | 全局变量 | 用途 |
|
|
|
|
|
+|---|---|---|---|---|
|
|
|
|
|
+| 1 | Vue 3(prod UMD) | `vue/{v}/vue.global.prod.js` | `Vue` | 框架本体 |
|
|
|
|
|
+| 2 | Vue Router 4 | `vue-router/{v}/vue-router.global.js` | `VueRouter` | 路由(按端组织 + 守卫链) |
|
|
|
|
|
+| 3 | Pinia | `pinia/{v}/pinia.iife.prod.js` | `Pinia` | 状态(login / scope 两 store) |
|
|
|
|
|
+| 4 | Element Plus CSS | `element-plus/{v}/index.css` | — | UI 样式(走自托管,不打包) |
|
|
|
|
|
+| 5 | Element Plus(full UMD) | `element-plus/{v}/index.full.min.js` | `ElementPlus` | UI 组件(表格/表单/日期等全量) |
|
|
|
|
|
+| 6 | Element Plus 中文 locale | `element-plus/{v}/locale/zh-cn.js` | `ElementPlusLocaleZhCn` | 默认简体中文 |
|
|
|
|
|
+| 7 | axios | `axios/{v}/axios.min.js` | `axios` | HTTP(统一封装于 `src/api/`) |
|
|
|
|
|
+
|
|
|
|
|
+> OSS 根路径固定:`https://<自有OSS域名>/static/vendor/<库名>/<版本号>/<文件名>/`,**只增不删**(旧版本路径保留,回滚 = 改回 index.html 版本号)。
|
|
|
|
|
+
|
|
|
|
|
+### 1.3 业务层工具链(本地编译)
|
|
|
|
|
+
|
|
|
|
|
+| 关注点 | 选型 | 理由 / 依据 |
|
|
|
|---|---|---|
|
|
|---|---|---|
|
|
|
-| 框架 | **Vue 3**(`<script setup>` + Composition API) | 与教学 admin 现有栈一致,复用成本低 |
|
|
|
|
|
-| 语言 | **TypeScript**(strict) | 接口契约类型化;统一信封 `R<T>` 泛型 |
|
|
|
|
|
-| 构建 | **Vite** | dev HMR + 产物构建;主 chunk < 1024KB(质量门禁) |
|
|
|
|
|
-| UI | **Element Plus**(含 `el-config-provider` 中文 locale) | 管理端组件库;locale 双注入(见编码规范 §3.4) |
|
|
|
|
|
-| 状态 | **Pinia** | `login`(token/角色)+ `scope`(institution/campus 作用域) |
|
|
|
|
|
-| 路由 | **Vue Router 4** | 按端组织 + 角色/权限码守卫 |
|
|
|
|
|
-| HTTP | **axios**(统一封装于 `src/api/`) | 统一 `{code, data, message}` 信封处理;拦截器只注入 token/trace,**不** reject 业务错误 |
|
|
|
|
|
-| 鉴权 | SSO(最小可行)token:access 30min + refresh 30 天 | 统一 identity 签发(ADR-014);菜单/按钮按权限码显隐 |
|
|
|
|
|
-| 包管理 | **pnpm**(lockfile 入库) | 与移动端同栈,依赖可对齐 |
|
|
|
|
|
-| 代码风格 | ESLint + Prettier(vue3 官方推荐配置) | 提交前 lint |
|
|
|
|
|
|
|
+| 构建 | **Vite 6.x** | 业务代码 HMR + 产物构建;框架 external 后主 chunk 只剩业务代码 |
|
|
|
|
|
+| 语言 | **TypeScript strict** | 契约类型化:`R<T>` / `PageData<T>` / 各域 DTO;UMD 全局经 `src/types/globals.d.ts` 声明类型 |
|
|
|
|
|
+| UI 组件库 | **Element Plus 2.x**(UMD 自托管) | 教学 admin 现有栈,复用成本最低;zhCn locale 双注入 |
|
|
|
|
|
+| 状态 | **Pinia** | `login`(token/角色/权限码)+ `scope`(institution/campus 作用域) |
|
|
|
|
|
+| 路由 | **Vue Router 4** | 按端组织 + 守卫链(登录态 → 静默刷新 → 权限码 → 作用域就绪) |
|
|
|
|
|
+| HTTP | **axios 1.x**(UMD 自托管,封装于 `src/api/`) | 统一信封处理 + 401 refresh 单例重放;业务错误**不** reject |
|
|
|
|
|
+| 鉴权 | SSO token(access 30min + refresh 30 天,ADR-014) | 统一 identity 签发;菜单/按钮按**权限码**显隐,禁 `role == N` 硬编码 |
|
|
|
|
|
+| 包管理 | **pnpm 10.12.1**(lockfile 入库) | 本机已装;与移动端仓同栈 |
|
|
|
|
|
+| 代码风格 | ESLint 9 + Prettier 3 | 提交前 lint;`no-explicit-any` 强制 |
|
|
|
|
|
+| 类型检查 | vue-tsc `--noEmit` | 质量门禁 0 错误 |
|
|
|
|
|
+
|
|
|
|
|
+### 1.4 默认简体中文
|
|
|
|
|
+
|
|
|
|
|
+- `<html lang="zh-CN">`;页面文案、placeholder、提示一律中文,**默认且唯一**语言。
|
|
|
|
|
+- Element Plus **locale 双注入**(教务实战铁律):`app.use(ElementPlus, { locale: ElementPlusLocaleZhCn })`(命令式 ElMessageBox/Notification)+ `<el-config-provider :locale="ElementPlusLocaleZhCn">`(声明式),单用其一必有遗漏。
|
|
|
|
|
+- **不引入 vue-i18n**(7 角色全中文、无多语言需求);将来真需多语言再上 i18n 框架,属另立决策。
|
|
|
|
|
+
|
|
|
|
|
+### 1.5 框架升级规则(自托管五步,缺一不可)
|
|
|
|
|
+
|
|
|
|
|
+1. 下载新版本 UMD 文件 → 2. 覆盖仓库 `vendor/` 对应目录并 commit → 3. 上传 OSS 对应路径 → 4. 改 `index.html` 版本表 → 5. 全量回归(vue-tsc + build + 端点联调)。
|
|
|
|
|
+> 版本升级不再受 lockfile 保护,靠仓库 diff + 回归兜底;发布前 `vendor/` 与 OSS 内容 md5 对账。
|
|
|
|
|
|
|
|
## 2. 目录结构与用途
|
|
## 2. 目录结构与用途
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
.
|
|
.
|
|
|
-├── README.md ← 本文件
|
|
|
|
|
-├── package.json / pnpm-lock.yaml
|
|
|
|
|
-├── vite.config.ts ← Vite 配置(proxy /api → 后端;chunk 拆分)
|
|
|
|
|
-├── tsconfig.json
|
|
|
|
|
-├── .env.example ← 环境变量模板(VITE_API_BASE 等;实际 .env.* 走 .gitignore)
|
|
|
|
|
-└── src/
|
|
|
|
|
- ├── main.ts ← 装配:Element Plus 双 locale 注入 + Pinia + Router + 作用域/权限守卫挂载
|
|
|
|
|
- ├── views/ ← 页面:按域分子目录(见用途表)
|
|
|
|
|
- │ ├── academic/ ← 教务域(排班/费用/打卡/充值/报课)——**新写**(旧 jQuery 视图全部重写)
|
|
|
|
|
- │ ├── teaching/ ← 教学 admin(备课/题库/组卷/练习分析)——**按现有 Vue3 逐步迁入**
|
|
|
|
|
- │ ├── org/ ← 机构/校区/班级/成员管理
|
|
|
|
|
- │ └── system/ ← 系统(用户/角色/字典/任务监控/日志)
|
|
|
|
|
- ├── components/ ← 复用组件:表格/表单/作用域选择器(institution·campus 联动)/Badge 色板
|
|
|
|
|
- ├── api/ ← 接口封装:统一信封 + 错误码处理;按域分子目录(academic/teaching/org/…)
|
|
|
|
|
- ├── router/ ← 路由:按端组织 + 角色/权限码守卫
|
|
|
|
|
- ├── store/ ← Pinia:login(token/角色)、scope(institution/campus)
|
|
|
|
|
- ├── composables/ ← 可组合逻辑:useListPage(列表页规范)等
|
|
|
|
|
- └── types/ ← 契约类型:R<T> 信封、角色/权限码、各域 DTO
|
|
|
|
|
|
|
+├── index.html ← 【框架层挂载点】lang="zh-CN";7 项 vendor 资源按固定顺序引入;
|
|
|
|
|
+│ 头部注释含"vendor 版本表"(库 → OSS 路径 → 版本,一处维护);
|
|
|
|
|
+│ <div id="app"> + 业务入口 <script type="module" src="/src/main.ts">
|
|
|
|
|
+├── vendor/ ← 【框架层自托管源文件】git 入库的版本事实(升级五步流程落点)
|
|
|
|
|
+│ ├── vue@x.y/ vue.global.prod.js
|
|
|
|
|
+│ ├── vue-router@x.y/ vue-router.global.js
|
|
|
|
|
+│ ├── pinia@x.y/ pinia.iife.prod.js
|
|
|
|
|
+│ ├── element-plus@x.y/ index.full.min.js + index.css + locale/zh-cn.js
|
|
|
|
|
+│ └── axios@x.y/ axios.min.js
|
|
|
|
|
+│ (上传 OSS 经 /static/vendor/<库名>/<版本号>/ 暴露;只增不删)
|
|
|
|
|
+├── package.json ← 仅【业务层工具链】:vite / @vitejs/plugin-vue / vue-tsc / eslint / prettier / typescript
|
|
|
|
|
+├── pnpm-lock.yaml ← 工具链版本锁定(入库)
|
|
|
|
|
+├── vite.config.ts ← ① rollup external:vue/vue-router/pinia/axios → 全局变量映射(运行时只认 UMD 全局)
|
|
|
|
|
+│ ② dev server:/api 代理 → 后端 dev 地址 ③ 产物 chunk 拆分(业务代码)
|
|
|
|
|
+├── tsconfig.json ← strict 基线 + paths 别名(@ → src/)
|
|
|
|
|
+├── .env.example ← 环境变量模板(VITE_API_BASE 等;实际 .env.* 走 .gitignore)
|
|
|
|
|
+└── src/ ← 【业务层】全部业务代码(本地编译进 dist/)
|
|
|
|
|
+ ├── main.ts ← 业务装配入口:取 UMD 全局 Vue.createApp + Element Plus locale 双注入
|
|
|
|
|
+ │ + Pinia + Router 挂载;不 import 框架运行时
|
|
|
|
|
+ ├── types/
|
|
|
|
|
+ │ ├── globals.d.ts ← UMD 全局变量类型声明(declare global:Vue/VueRouter/Pinia/ElementPlus/axios)
|
|
|
|
|
+ │ └── api.d.ts ← 契约类型:R<T> 信封 / PageData<T>(list,total,page,pageSize,pages)/ 角色与权限码
|
|
|
|
|
+ ├── views/ ← 页面层(只放视图与页面级状态;禁散写 axios)
|
|
|
|
|
+ │ ├── academic/ ← 教务域:排班/费用/打卡/充值/报课 ——【新写】(旧 jQuery 视图全部重写)
|
|
|
|
|
+ │ ├── teaching/ ← 教学 admin:备课/题库/组卷/练习分析 ——【按现有 Vue3 逐步迁入】
|
|
|
|
|
+ │ ├── org/ ← 机构/校区/班级/成员管理
|
|
|
|
|
+ │ └── system/ ← 系统:用户/角色/字典/任务监控/日志
|
|
|
|
|
+ ├── components/ ← 跨页面复用组件:ListPage(表格+分页+筛选+批量收口)、作用域选择器
|
|
|
|
|
+ │ (institution·campus 联动)、GradeBadge/SubjectBadge(色板纯数据零 Vue 依赖)
|
|
|
|
|
+ ├── api/ ← 接口封装(一域一目录、一资源一文件):axios 实例 + 统一信封处理 +
|
|
|
|
|
+ │ 401 refresh 单例重放 + 分页统一解构 res.data.{list,total,page,pageSize,pages}
|
|
|
|
|
+ ├── router/ ← 路由表按端组织 + 守卫链:登录态 → 静默刷新 → 权限码 → 作用域就绪
|
|
|
|
|
+ ├── store/ ← Pinia:login(token/角色/权限码)、scope(institution/campus)——唯一 token 存取点
|
|
|
|
|
+ ├── composables/ ← 可组合逻辑:useListPage<T>(列表页唯一规范入口)等
|
|
|
|
|
+ ├── utils/ ← 纯函数工具集(无 Vue 依赖):日期/金额格式化、脱敏回显、下载/导出辅助
|
|
|
|
|
+ └── styles/ ← 全局样式:variables.scss(色板/间距 token)+ reset + Element Plus 主题覆盖
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**目录用途约定**:
|
|
**目录用途约定**:
|
|
|
|
|
|
|
|
| 目录 | 用途 | 约束 |
|
|
| 目录 | 用途 | 约束 |
|
|
|
|---|---|---|
|
|
|---|---|---|
|
|
|
-| `src/views/{域}/` | 页面层,只放视图与页面级状态 | 禁止散写 fetch/axios;只经 `src/api/` |
|
|
|
|
|
|
|
+| `index.html` | 框架层挂载 + 版本表 | 7 项 vendor 引用**顺序固定**;版本号只改这里,禁止散落 src |
|
|
|
|
|
+| `vendor/` | 框架层源文件版本事实 | 与 OSS 内容一致(提交前 diff 核对);只增不删,禁止覆盖已发布旧版本路径 |
|
|
|
|
|
+| `src/views/{域}/` | 页面层(四域分包:academic/teaching/org/system) | 只经 `src/api/` 发请求;views 之间互引禁止(复用 → 上提 components/) |
|
|
|
| `src/components/` | 跨页面复用组件 | 零业务耦合(作用域感知组件除外,从 store 读) |
|
|
| `src/components/` | 跨页面复用组件 | 零业务耦合(作用域感知组件除外,从 store 读) |
|
|
|
-| `src/api/{域}/` | 接口封装(一域一目录,一资源一文件) | 统一信封处理收口在此;返回类型化 DTO |
|
|
|
|
|
|
|
+| `src/api/{域}/` | 接口封装(一域一目录,一资源一文件) | 信封处理与分页解构**只在此**;返回类型化 DTO |
|
|
|
| `src/router/` | 路由与守卫 | 权限码驱动;不写 `role == N` 硬编码 |
|
|
| `src/router/` | 路由与守卫 | 权限码驱动;不写 `role == N` 硬编码 |
|
|
|
-| `src/store/` | 全局状态(登录态/作用域) | 唯一 token 存取点(禁 localStorage 散写) |
|
|
|
|
|
|
|
+| `src/store/` | 全局状态(登录态/作用域) | 唯一 token 存取点(禁 localStorage 散写);scope 未就绪时守卫拦截 |
|
|
|
| `src/composables/` | 可组合逻辑 | 列表页一律复用 `useListPage<T>` |
|
|
| `src/composables/` | 可组合逻辑 | 列表页一律复用 `useListPage<T>` |
|
|
|
-| `src/types/` | 契约类型 | 与后端 DTO camelCase 对齐;`R<T>` 泛型 |
|
|
|
|
|
|
|
+| `src/utils/` | 纯函数工具集 | 文件内零组件、零 store 依赖;可被 composables/api/views 任意引用,反向禁止 |
|
|
|
|
|
+| `src/styles/` | 全局样式与 token | 业务组件 `<style>` 禁硬编码色值/尺寸,必须引 variables token;页面级局部样式留组件内 `<style scoped>` |
|
|
|
|
|
+| `src/types/` | 契约类型 | `R<T>`/`PageData<T>` 只在此定义一次;UMD 全局声明全覆盖(新增框架资源先补声明后写业务) |
|
|
|
|
|
|
|
|
## 3. 编码规范
|
|
## 3. 编码规范
|
|
|
|
|
|
|
|
-### 3.1 结构铁律
|
|
|
|
|
|
|
+### 3.1 命名规则
|
|
|
|
|
|
|
|
-1. **作用域(institution/campus)是全局状态**:所有业务页面查询必须带作用域,由 `store/scope` 统一注入,禁止页面各自拼 `schoolid/campusid`(对齐清单 §3:消灭控制器隐式参数)。
|
|
|
|
|
-2. **权限码驱动显隐**:菜单/按钮/路由守卫读统一 RBAC 权限码,不写 `role == N` 硬编码(对齐清单 §3 超管判据收敛)。
|
|
|
|
|
-3. **教务域页面为新写**(旧 DataTables/视图 jQuery 全部重写);**教学 admin 页面按现有 Vue3 逐步迁入**,契约不变。
|
|
|
|
|
-4. 接口调用只经 `src/api/`,禁止页面里散写 fetch/axios。
|
|
|
|
|
|
|
+| 对象 | 规则 | 示例 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| 文件 | 组件 `PascalCase.vue`;其余 `kebab-case.ts` | `ListPage.vue`、`use-list-page.ts`、`grade-badge.ts` |
|
|
|
|
|
+| 组件 | 注册名 PascalCase,模板里 kebab-case | `<list-page>` / `ListPage` |
|
|
|
|
|
+| 组合式函数 | `useXxx` | `useListPage<T>()`、`useScope()` |
|
|
|
|
|
+| 变量/函数 | `camelCase` | `institutionId`、`handleSubmitRefund` |
|
|
|
|
|
+| 常量 | 模块内 `UPPER_SNAKE`(仅真常量:错误码段、权限码枚举) | `PERM_CODE` |
|
|
|
|
|
+| 接口/类型 | 名词 PascalCase,DTO 带域前缀 | `PageData<T>`、`AcademicFeeDTO`、`TeachingQuestionDTO` |
|
|
|
|
|
+| **API 字段** | **一律 `camelCase`**,与后端 DTO 严格对齐(`institutionId` 非 `institution_id`);禁止前端自造映射层 | — |
|
|
|
|
|
+| 路由 path | `kebab-case`,与后端端点一致(`/{端}/{模块}/{资源}`) | `/admin/academic/fees` |
|
|
|
|
|
+| 路由 name | PascalCase 页名 | `AcademicFeeList` |
|
|
|
|
|
+| 权限码 | **`域.资源.动作` 三段式**(点分) | `academic.fee.refund` |
|
|
|
|
|
+| 事件/方法 | 内部回调 `onXxx`;用户触发的提交类 `handleXxx` | `onScopeChange`、`handleDelete` |
|
|
|
|
|
+| 禁词 | 禁用 `userid/schoolid/campusid` 类旧系统无下划线混用命名 | — |
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 文件组织
|
|
|
|
|
+
|
|
|
|
|
+**组件 SFC 骨架(顺序固定)**:
|
|
|
|
|
|
|
|
-### 3.2 接口与响应契约
|
|
|
|
|
|
|
+```
|
|
|
|
|
+<script setup lang="ts"> ← ① 类型 import(仅类型与本地 DTO,禁 import 框架运行时)
|
|
|
|
|
+ ← ② defineProps/defineEmits 显式类型(禁 any,unknown 收窄)
|
|
|
|
|
+ ← ③ store 注入(useLogin/useScope)
|
|
|
|
|
+ ← ④ 响应式状态(ref/computed,单一数据源)
|
|
|
|
|
+ ← ⑤ 生命周期(onMounted 拉数据)
|
|
|
|
|
+ ← ⑥ 方法(handleXxx 在前,onXxx 在后)
|
|
|
|
|
+</script>
|
|
|
|
|
+<template> ← ⑦ 结构;列表页一律 <list-page> 收口
|
|
|
|
|
+</template>
|
|
|
|
|
+<style scoped> ← ⑧ 局部样式;色值/尺寸引 @/styles/variables token
|
|
|
|
|
+```
|
|
|
|
|
|
|
|
-- 统一「**HTTP 恒 200 + body `code`**」信封 `{code, data, message, success}`;判成功读 `code === 200`,错误文案读 `message`,**不靠**拦截器 reject、**不靠** HTTP status(源自教务 Go 重构在产验证)。
|
|
|
|
|
-- 错误码分段(与后端一致):`1xxx` 认证/权限 · `2xxx` 参数 · `3xxx` 数据 · `4xxx` 依赖 · `5xxx` 系统;`1001/1002` 统一跳登录(refresh 失败才跳,access 过期先静默 refresh)。
|
|
|
|
|
-- **统一分页**:请求 `page`/`pageSize`(默认 1/10,上限 200);响应 `data` 恒为 `PageData` 对象 `{list, total, page, pageSize, pages}`,前端统一读 `res.data.list` / `res.data.total`(`useListPage<T>` 收口,不再区分分页/非分页两套读法)。
|
|
|
|
|
-- 命名:入参/出参 `camelCase`;组件 `PascalCase`;组合式函数 `useXxx`。
|
|
|
|
|
|
|
+**域目录内约定**:
|
|
|
|
|
+- `views/{域}/{模块}/`:`list.vue`(列表)+ `detail.vue`(详情)+ 页内私有子组件 `{模块}-xxx.vue`(私有组件**不放** `components/`);
|
|
|
|
|
+- `api/{域}/{资源}.ts`:一资源一文件,只导出该资源的请求函数 + 对应 DTO 类型;
|
|
|
|
|
+- 跨域引用只允许经 `api/` 与 `components/`。
|
|
|
|
|
|
|
|
-### 3.3 路由与鉴权
|
|
|
|
|
|
|
+### 3.3 组件编写
|
|
|
|
|
|
|
|
-- 路由守卫顺序:登录态(access 有效?)→ 刷新(过期且有 refresh 则静默换)→ 角色/权限码 → 作用域就绪(scope 已注入)。
|
|
|
|
|
-- 刷新单例:并发 401/过期只触发一次 refresh(Promise 复用),成功后重放原请求。
|
|
|
|
|
-- 敏感操作(充值/退款/改密)二次确认 + 操作留痕(后端审计)。
|
|
|
|
|
|
|
+1. **框架运行时全走 UMD 全局**:`src/` 内**禁止** `import Vue/VueRouter/Pinia/ElementPlus/axios`;组合 API 标识符经 vite `define` 注入映射到 UMD 全局(或统一 `const { ref } = Vue` 解构,两种写法脚手架定稿时二选一),规范条文恒为"禁 import 框架"。
|
|
|
|
|
+2. **Props**:显式 `interface` 类型;可选项给默认值;禁内联 `any`。
|
|
|
|
|
+3. **状态最小化**:能用 store 的(token/scope/权限码)不本地存;列表状态收口 `useListPage<T>`(page/pageSize/total/loading 全在里面,组件只传 fetcher)。
|
|
|
|
|
+4. **作用域纪律**:业务查询参数里的 `institutionId/campusId` **只从 `useScope()` 取**,禁止页面自拼(对齐后端"作用域强制注入",消灭旧系统隐式 `schoolid` 坑)。
|
|
|
|
|
+5. **表单**:`el-form` + 显式 `rules`(提交前 `validate()`);服务端 2001 错误 message 直接 toast,前端规则只拦格式。
|
|
|
|
|
+6. **表格**:列定义数组化(`prop/label/width/formatter` 数据驱动),新增列不改模板结构。
|
|
|
|
|
+7. **权限**:按钮级 `v-perm="'域.资源.动作'"` 自定义指令(读 login store 权限码集);**禁止 `role === N` 硬编码**。
|
|
|
|
|
+8. **三态**:所有异步区域必须有 loading + 空态 + 错误态;错误态展示后端 message(已脱敏),不二次包装文案。
|
|
|
|
|
+9. **敏感操作**(充值/退款/改密/删除):`ElMessageBox.confirm` 二次确认 + 操作后刷新 + 后端留痕。
|
|
|
|
|
+10. **脱敏**:手机号/身份证直接展示后端已脱敏值,前端不存明文、不二次处理。
|
|
|
|
|
|
|
|
-### 3.4 可复用资产与惯例(源自教务 Go 重构实战)
|
|
|
|
|
|
|
+### 3.4 接口与响应契约(前后端统一,2026-11-05 拍板)
|
|
|
|
|
|
|
|
-- **列表页规范**:`useListPage<T>` + `ListPage.vue`(分页/筛选/批量操作收口);年级 `<GradeBadge>`(数字=年级 id + 学段色系)、科目 `<SubjectBadge>`(首字简称 + 独立色系)——色板纯数据零 Vue 依赖,新增列表页一律复用。
|
|
|
|
|
-- **locale 双注入**:Element Plus 默认英文,需 `app.use(ElementPlus, {locale: zhCn})`(命令式 ElMessageBox/Notification)+ `<el-config-provider :locale>`(声明式)**双注入**,单用其一必有遗漏;locale 引 `element-plus/es/locale/lang/zh-cn`。
|
|
|
|
|
-- **脱敏展示**:手机号/身份证按后端已脱敏值展示,前端不再二次处理(源头脱敏)。
|
|
|
|
|
|
|
+- 统一「**HTTP 恒 200 + body `code`**」信封 `{code, data, message, success}`;判成功读 `code === 200`,错误文案读 `message`,**不靠**拦截器 reject、**不靠** HTTP status(源自教务 Go 重构在产验证)。
|
|
|
|
|
+- 错误码分段(与后端一致):`1xxx` 认证/权限 · `2xxx` 参数 · `3xxx` 数据 · `4xxx` 依赖 · `5xxx` 系统;`1001/1002` 统一走 refresh 单例(并发只刷一次,Promise 复用),成功后重放原请求;refresh 失败才清 login store 跳登录。错误 toast 去重:同一 message 1s 内不重复弹。
|
|
|
|
|
+- **统一分页**:请求 `page`/`pageSize`(默认 1/10,上限 200);响应 `data` 恒为 `PageData` 对象 `{list, total, page, pageSize, pages}`,`useListPage<T>` 统一解构 `res.data.{list, total, page, pageSize, pages}`,组件层只见 `rows/total` 两个绑定(不再区分分页/非分页两套读法)。
|
|
|
|
|
+
|
|
|
|
|
+### 3.5 类型纪律(TS strict)
|
|
|
|
|
|
|
|
-### 3.5 质量门禁
|
|
|
|
|
|
|
+- `any` 全面禁用(vue-tsc + ESLint `no-explicit-any` 双卡);未知结构用 `unknown` + 类型收窄。
|
|
|
|
|
+- DTO 与后端字段**逐字段**对齐:新增接口先补 `api/{域}/{资源}.ts` 类型定义再写调用;禁止"先 any 后补类型"。
|
|
|
|
|
+- `R<T>`/`PageData<T>` 泛型只在 `types/api.d.ts` 定义一次,全仓引用。
|
|
|
|
|
|
|
|
-| 门禁 | 命令 | 基线 |
|
|
|
|
|
|
|
+### 3.6 质量门禁
|
|
|
|
|
+
|
|
|
|
|
+| 门禁 | 命令 / 做法 | 基线 |
|
|
|
|---|---|---|
|
|
|---|---|---|
|
|
|
| 类型检查 | `vue-tsc --noEmit` | 0 错误 |
|
|
| 类型检查 | `vue-tsc --noEmit` | 0 错误 |
|
|
|
-| 构建 | `pnpm build`(vite) | 主 chunk < 1024KB |
|
|
|
|
|
|
|
+| 构建 | `pnpm build`(vite) | 0 错误;主 chunk < 1024KB;**dist/ 产物 grep 不得含 vue/element-plus 运行时**(防 external 配错回退成本地打包) |
|
|
|
| Lint | `pnpm lint`(eslint + prettier) | 0 错误 |
|
|
| Lint | `pnpm lint`(eslint + prettier) | 0 错误 |
|
|
|
|
|
+| vendor 一致性 | 发布前 `vendor/` 与 OSS 路径内容 md5 对账 | 一致方可发布 |
|
|
|
|
|
+| 端点联调 | 真实登录 token → 页面操作 → 断言 body code + 结构 | 结构不符先查后端路由重复注册 |
|
|
|
|
|
|
|
|
-### 3.6 Git 与提交规范
|
|
|
|
|
|
|
+### 3.7 Git 与提交规范
|
|
|
|
|
|
|
|
- **工程纪律**:本地 commit 可执行;**脚手架搭建与 `git push` 前必须经用户确认**(逐次确认)。
|
|
- **工程纪律**:本地 commit 可执行;**脚手架搭建与 `git push` 前必须经用户确认**(逐次确认)。
|
|
|
-- 提交信息:conventional commits(`feat:` / `fix:` / `chore:` / `docs:` / `refactor:`)。
|
|
|
|
|
-- 提交前自检:`vue-tsc` + `pnpm build` 通过;敏感值未入库(`.env` 走 .gitignore,仅 `.env.example` 入库)。
|
|
|
|
|
|
|
+- 提交信息:conventional commits(`feat:` / `fix:` / `chore:` / `docs:` / `refactor:`),正文写清影响域。
|
|
|
|
|
+- 提交前自检:`vue-tsc` + `pnpm build` 通过;敏感值未入库(`.env` 走 .gitignore,仅 `.env.example` 入库);**vendor 变更必须与 index.html 版本表同步提交**。
|
|
|
|
|
|
|
|
## 4. 构建与本地运行
|
|
## 4. 构建与本地运行
|
|
|
|
|
|
|
|
```bash
|
|
```bash
|
|
|
-pnpm install # 依赖(pnpm 10.x;lockfile 入库)
|
|
|
|
|
-pnpm dev # 本地开发(vite dev,/api 代理到后端 dev 地址)
|
|
|
|
|
-pnpm build # 产物 → dist/(部署经 nginx)
|
|
|
|
|
|
|
+pnpm install # 工具链依赖(pnpm 10.12.1;lockfile 入库)
|
|
|
|
|
+pnpm dev # 本地开发(vite dev,业务代码 HMR;框架层自 OSS 加载;/api 代理到后端 dev 地址)
|
|
|
|
|
+pnpm build # 产物 → dist/(仅业务代码;部署经 nginx 按 location 分发,ADR-010)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**环境变量**(`.env.example` 入库,实际 `.env.*` 不入库):`VITE_API_BASE`(后端 base,dev 走 vite proxy 时可省)。
|
|
**环境变量**(`.env.example` 入库,实际 `.env.*` 不入库):`VITE_API_BASE`(后端 base,dev 走 vite proxy 时可省)。
|
|
|
|
|
|
|
|
|
|
+**框架层升级**:执行 §1.5 五步流程(下载 → 覆盖 vendor/ 并 commit → 上传 OSS → 改 index.html 版本表 → 全量回归)。
|
|
|
|
|
+
|
|
|
## 5. 与文档目录的关系
|
|
## 5. 与文档目录的关系
|
|
|
|
|
|
|
|
- 本仓库**只放前端代码**;接口契约、端点清单、错误码见工作区文档库 `academic/05-architecture/` 与 `teaching/05-architecture/`;前端策略(重写 vs 复用)见 `unified/00-planning/cross-system-reconciliation.md` §5。
|
|
- 本仓库**只放前端代码**;接口契约、端点清单、错误码见工作区文档库 `academic/05-architecture/` 与 `teaching/05-architecture/`;前端策略(重写 vs 复用)见 `unified/00-planning/cross-system-reconciliation.md` §5。
|
|
|
-- 与移动端仓库共享编码惯例(统一信封/脱敏/权限码/commit 规范),差异仅在 UI 库与路由形态。
|
|
|
|
|
|
|
+- 与移动端仓库共享编码惯例(统一信封/统一分页 PageData/脱敏/权限码三段式/commit 规范),差异:PC 端为 UMD 自托管 + 单项目,移动端为 pnpm monorepo + Vant 4。
|