Files
xiaotutongxue/frontend-pc/.agents/skills/project-guide/SKILL.md
T
lovife 87ece5a971 feat(frontend): 管理端改用 Gi Admin Pro 框架 + 腾讯云主题
前端此前只有本地实现、从未入库,本次首次纳入版本管理。

框架切换
  · 以 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 班级),供界面演示
2026-09-28 20:49:55 +08:00

172 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: project-guide
description: 后台管理模板的项目总览与前端强制规范。涵盖 Vue 3 + Arco Design + Gi 组件库、目录结构、自动导入、CRUD/API 约定与代码风格。在当前仓库内开发、新增页面、查阅架构或不确定项目规范时使用。
---
# 项目开发指南
> **适用范围**:本仓库 `src/**`、`mock/**`
> **规范来源**:`AGENTS.md` 常驻行为准则 + 本 skill(项目细节);编码规范按需读 `code-vue` / `code-css` / `code-ts` skill
## 本项目 Agent 约定
- 优先复用 `Gi*`、`hooks`、`getBaseApi`、现有 mock 工具;新增页面/接口对齐参考页与 `getBaseApi` 约定。
- API / Mock 以 `src/apis`、`mock` 与类型定义为准,不要猜测路径。
- 涉及 CRUD、API、Mock 时按需读取对应 skill;写 `.vue` / 样式 / TS 前读取 [code-vue](../code-vue/SKILL.md)、[code-css](../code-css/SKILL.md)、[code-ts](../code-ts/SKILL.md)。
- 改动后视情况运行 `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](../code-vue/SKILL.md),本项目补充见下文「本项目 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](../project-crud/SKILL.md)
核心结构:
1. `GiPageLayout` 布局(可选 `#left` 树/分类)
2. `queryParams`(reactive)+ 搜索区 + `GiButton`(search/reset)
3. `tableColumns`(`TableColumnData[]`,复杂列用 `lang="tsx"` + `render`)
4. `useTable` 绑定 `listAPI` 与 `deleteAPI`
5. `XxxFormModal` ref,`defineExpose({ add, edit })`,`@save-success="search"`
分页类型:`PageRes<T>`(`records` + `total`)。Mock 与接口详见 [project-api](../project-api/SKILL.md)。
## API 层模板
```typescript
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-ts/SKILL.md)、[code-vue](../code-vue/SKILL.md)、[code-css](../code-css/SKILL.md);本项目补充见下文。
## 本项目编码约定
- 路径别名:`@` → `src/`,`~` → 项目根;禁止 `../../../` 穿越。
- 组件全局前缀 **`Gi`** 仅用于 `src/components/Gi*` 全局组件。
- 目录:`src/views` 页面、`src/components` 组件、`src/apis/<domain>/` + `type.ts` 接口类型、`src/types` 公共类型、`src/hooks` composables。
- 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-*`(Iconify `icon-park-outline`)
- 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](../code-vue/SKILL.md) |
| 样式、BEM | [code-css](../code-css/SKILL.md) |
| TS 命名、导入 | [code-ts](../code-ts/SKILL.md) |
| 新建 CRUD 列表页、表格、弹窗表单 | [project-crud](../project-crud/SKILL.md) |
| 新增接口、Mock、baseAPI | [project-api](../project-api/SKILL.md) |
## 参考页面
- 标准 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](../project-crud/SKILL.md) 约定