87ece5a971
前端此前只有本地实现、从未入库,本次首次纳入版本管理。
框架切换
· 以 Gi Admin Pro(Vue3 + Vite + TS + Arco Design Vue + Pinia)为底
· 剥离 14 个演示页面目录 / 121 个文件(demo 28、file 20、form 19、system 18…)
· 依赖 dependencies 35 → 19,devDependencies 31 → 27
· 保留框架核心:layout、Gi* 组件库、hooks、权限指令、错误页、登录页
主题:腾讯云 / TDesign 官方色板
· 主色 #0052D9(腾讯蓝)、成功 #00A870、警告 #ED7B2F、危险 #E34D59
· 页面底色 #f3f3f3、文字 rgba(0,0,0,90/60/40/26%)
· 覆盖 Arco 底层色板 + 显式语义别名(primary/success/warning/danger/link/neutral)
· 三个改了不生效也不报错的坑已记入注释:
1) Arco 色值是逗号分隔 RGB 三元组,不是 #RRGGBB
2) Arco 把色板定义在 body 而非 :root,必须写 `:root, body`
3) setting.json 的 themeColor 会以内联样式写进 body,优先级高于样式表
对接后端(三处契约差异在 @/utils/http 统一适配)
· 响应信封 {code,msg,data} → {code,success,message,data}
· Token 头 token → Authorization: Bearer
· 分页 {list,total} → 补一份 records,list 原样保留
· 40101 → 登录页;登录密码 RSA 加密(公钥走 /auth/public-key,轮换无需重建前端)
· 路由:后端只下发菜单键,前端在本地路由表上过滤(有哪些页面归代码,谁能看归后端)
已实现页面(浏览器实测通过)
· 登录:RSA 加密 + 自适应验证码/租户码 + 账号锁定提示
· 工作台:真实数据(组织 6 个 / 角色 1 个 / 权限 25 项)
· 组织架构:三级树 + CRUD + 占用校验
· 租户管理:开通租户 + 初始密码只显示一次的强确认
· 待建页:未实现菜单显示所属里程碑
· 权限菜单:学校管理员看不到「租户管理」
视觉资产按腾讯云主题重做
· logo.svg(渐变圆角方 + 填)、favicon.svg(小尺寸做减法)、login-hero.svg(材料归集)
· 登录页点阵纹理走主题变量,换主色自动跟随
· 文案改为产品实际能力:自动改名 / 强制压缩 / 隐私保护
清理模板遗留(约 17MB + 三处外部依赖)
· 百度统计脚本(上报到 gi-demo 作者的统计账号)
· 用户菜单「项目地址」(跳转模板作者的 Gitee 仓库)
· 两份 8.8MB mp3、388KB favicon.ico、57KB 模板插画、未引用字体与 loading.gif
· 「个人中心」指向已删除的 /system/account(点了 404)
· 头部头像在无 avatarUrl 时渲染成裂图 → 改为姓名首字兜底
后端修复
· 新增 GET /auth/public-key(前端加密登录密码用)
· DataSeeder.ancestor_path 生成错误:子节点本该是 /16/17/ 却写成 /17/
该字段是「按院系统计」的索引,错了会漏人/多人且不报错 —— 改为从库里读父节点路径
· seedDemoOrgs 曾放在租户作用域外 + admin 早退分支之后,导致启动失败 / 永不执行
· 示范租户补一套三级组织树(1 学校 / 2 院系 / 3 班级),供界面演示
7.6 KiB
7.6 KiB
name, description
| name | description |
|---|---|
| project-guide | 后台管理模板的项目总览与前端强制规范。涵盖 Vue 3 + Arco Design + Gi 组件库、目录结构、自动导入、CRUD/API 约定与代码风格。在当前仓库内开发、新增页面、查阅架构或不确定项目规范时使用。 |
项目开发指南
适用范围:本仓库
src/**、mock/**规范来源:AGENTS.md常驻行为准则 + 本 skill(项目细节);编码规范按需读code-vue/code-css/code-tsskill
本项目 Agent 约定
- 优先复用
Gi*、hooks、getBaseApi、现有 mock 工具;新增页面/接口对齐参考页与getBaseApi约定。 - API / Mock 以
src/apis、mock与类型定义为准,不要猜测路径。 - 涉及 CRUD、API、Mock 时按需读取对应 skill;写
.vue/ 样式 / TS 前读取 code-vue、code-css、code-ts。 - 改动后视情况运行
pnpm typecheck、pnpm lint、pnpm lint:style。
技术栈
| 类别 | 选型 |
|---|---|
| 框架 | Vue 3.5 + TypeScript + <script setup lang="ts"> |
| 构建 | Vite 7 |
| UI | Arco Design Vue(按需自动导入)+ 本地 Gi* 组件 |
| 状态 | Pinia + pinia-plugin-persistedstate |
| 路由 | Vue Router 4(Hash 模式) |
| HTTP | @/utils/http,统一响应类型 ApiRes<T>、PageRes<T> |
| 工具 | @vueuse/core、lodash-es、dayjs、xe-utils |
| Mock | vite-plugin-mock + mockjs(开发默认启用) |
每个 SFC 必须 defineOptions({ name: 'XxxYyy' });通用 Vue 规范见 code-vue,本项目补充见下文「本项目 Vue 约定」。
路径别名
@→src/~→ 项目根目录
目录约定
src/
├── apis/ # 接口(按业务模块分文件,标准 CRUD 用 getBaseApi)
├── components/ # Gi* 业务组件 + 部分通用组件
├── hooks/ # 组合式函数(useTable、useDict 等)
├── hooks/app/ # 业务域 hooks(useDept、useRole)
├── stores/ # Pinia
├── views/ # 页面(按模块分子目录)
├── router/ # 路由与守卫
├── utils/ # http、auth、regexp、deepClone 等
├── types/ # 全局类型(global.d.ts、api.d.ts)
mock/ # vite-plugin-mock 模拟数据
页面文件命名:
src/views/{module}/index.vue # 列表页
src/views/{module}/XxxFormModal.vue # 新增/编辑弹窗(有表单时必建)
src/apis/{module}/{name}.ts # 类型定义 + CRUD API(或 apis/system/user.ts)
mock/{module}/{name}.ts # 与 API 路径一致的 Mock
全局类型(src/types/)
Status:'0' | '1'(禁用/启用,与字典STATUS一致)Gender:'1' | '2' | '3'Pagination:{ page, size }ApiRes<T>、PageRes<T>:统一接口响应(分页data为{ records, total })
CRUD 列表页(强制遵循)
新建/修改表格页时,必须参考并遵循:
- 参考实现:
src/views/system/user/index.vue、UserFormModal.vue - Hook:
@/hooks/useTable - API 模板:
src/apis/system/user.ts+getBaseApi - 详细步骤:读取并遵循 project-crud
核心结构:
GiPageLayout布局(可选#left树/分类)queryParams(reactive)+ 搜索区 +GiButton(search/reset)tableColumns(TableColumnData[],复杂列用lang="tsx"+render)useTable绑定listAPI与deleteAPIXxxFormModalref,defineExpose({ add, edit }),@save-success="search"
分页类型:PageRes<T>(records + total)。Mock 与接口详见 project-api。
API 层模板
import { getBaseApi } from '@/apis/base'
import http from '@/utils/http'
export interface ListItem {
id: string
name: string
status: Status
}
/** 标准 CRUD */
export const baseAPI = getBaseApi<ListItem>({ baseUrl: '/system/xxx' })
/** 非标准接口 */
export function customAction(params: Pagination) {
return http.get<PageRes<Item[]>>('/system/xxx/customAction', params)
}
- 开发环境
VITE_API_PREFIX = '/mock'(.env.development) - 禁止绕过
@/utils/http裸用 axios
代码风格
- 遵循
@antfu/eslint-config(eslint.config.js):箭头参数括号、1TBS、无尾随逗号;Vue block 顺序[['script', 'template'], 'style'];宏顺序defineOptions/defineModel/defineProps/defineEmits/defineSlots,defineExpose最后。 - 布局放
src/layout/,Gi 组件放src/components/Gi*,业务 Hook 放hooks/或hooks/app/。 - 空字符串查询条件传 API 前建议转为
undefined。 - 通用编码规范见 code-ts、code-vue、code-css;本项目补充见下文。
本项目编码约定
- 路径别名:
@→src/,~→ 项目根;禁止../../../穿越。 - 组件全局前缀
Gi仅用于src/components/Gi*全局组件。 - 目录:
src/views页面、src/components组件、src/apis/<domain>/+type.ts接口类型、src/types公共类型、src/hookscomposables。 - API 统一
@/utils/http;状态 Pinia +pinia-plugin-persistedstate。 - 图标
@iconify/vue;样式从 scss/less 导入,避免内联。 - 业务注释中文,标识符英文。
- 检查:
pnpm lint、pnpm lint:style、pnpm typecheck。
本项目 Vue 约定
- 每个 SFC 必须
defineOptions({ name: 'XxxYyy' }),名称与路由/模块对应,并与路由表name一致以支持缓存。 - 自动导入(
vite.config.ts,勿重复 import):- Vue API:
ref、reactive、computed、watch、useTemplateRef等 - Gi 组件:
src/components/Gi*(如GiPageLayout、GiButton、GiForm) - Arco 组件:
a-table、a-modal、a-select等 - 图标:
icon-*(Iconifyicon-park-outline)
- Vue API:
- Gi 组件模板用 PascalCase;Arco 用
a-*前缀;禁止手动 import Gi/Arco(类型除外)。 - 表格列复杂时用
lang="tsx"+TableColumnData[]的render;字典字段用useDict(['STATUS'])等。 dayjs默认导入变量名必须为Dayjs(大写),禁止小写dayjs。- 深拷贝使用
@/utils的deepClone,禁止JSON.parse(JSON.stringify())。 - Vue 参考页:
src/views/system/user/index.vue、UserFormModal.vue、src/views/table/custom3/index.vue
本项目样式
- 全局样式:
src/styles/index.scss(变量var.scss、混入mixin.scss、Arco 主题arco-ui/) - Stylelint:
pnpm lint:style - 列表页常用工具类:
g-row-tool(工具栏行)、g-table(表格)、g-full-column(左栏撑满)、g-mb(下边距) - BEM 示例:
src/views/login/LoginLeft.vue
子技能(按需阅读)
| 场景 | 技能 |
|---|---|
| Vue SFC、Emits、Ref | code-vue |
| 样式、BEM | code-css |
| TS 命名、导入 | code-ts |
| 新建 CRUD 列表页、表格、弹窗表单 | project-crud |
| 新增接口、Mock、baseAPI | project-api |
参考页面
- 标准 CRUD + 左树:
src/views/system/user/index.vue - 左右分栏 + 字典:
src/views/system/dict/index.vue - GiTableSetting + a-table:
src/views/table/custom3/index.vue - 表单弹窗:
src/views/system/user/UserFormModal.vue
禁止事项
- ❌ 绕过
@/utils/http直接裸用 axios - ❌ 表格页自行实现分页/删除逻辑,而不使用
useTable - ❌ 新建列表页不参考
system/user与 project-crud 约定