Become a sponsor

概述
前端采用 Vue 3 + TypeScript 组件化架构,分为 View(视图)→ API(接口)→ Store(状态)→ HTTP(请求)四层。辅以 Router(路由)、Directives(指令)、Hooks(组合式函数)、Utils(工具)等支撑模块。
┌─────────────────────────────────────────────────────────────────────┐
│ 支撑模块 │
│ │
│ Router(路由) Directives(指令) Hooks(组合式函数) │
│ src/router/ src/directives/ src/hooks/ │
│ │
│ Utils(工具) Enums(枚举) Styles(样式) │
│ src/utils/ src/enums/ src/styles/ │
└──────────────────────────────┬──────────────────────────────────────┘
│ 被主干层引用
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 主干链路 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ View(视图层) │ │
│ │ 页面渲染、用户交互、调用 API 和 Store │ │
│ │ 文件:src/views/**/*.vue │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ 调用 API 函数 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ API(接口层) │ │
│ │ 封装后端 API 调用,定义请求参数和响应类型 │ │
│ │ 文件:src/api/**/*.ts │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ 调用 http.request() │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ HTTP(请求层) │ │
│ │ Axios 封装:拦截器、Token 携带、错误处理 │ │
│ │ 文件:src/utils/http/axios/ │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 后端 API(RESTful JSON) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Store(状态层) │ │
│ │ 全局状态管理:Token、用户信息、权限列表、动态路由 │ │
│ │ 文件:src/store/modules/*.ts │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘文件:src/views/**/*.vue职责:页面渲染、用户交互、调用 API 获取数据
src/views/
├── system/ 系统管理
│ ├── user/ 用户管理
│ │ ├── index.vue 列表页
│ │ └── edit.vue 编辑弹窗
│ ├── role/ 角色管理
│ └── menu/ 菜单管理
├── content/ 内容管理
│ ├── article/ 文章管理
│ └── category/ 分类管理
├── login.vue 登录页
├── dashboard/ 仪表盘
└── ...典型页面结构:
<template>
<!-- 搜索区 -->
<el-form :model="searchForm" inline>
<el-form-item label="名称">
<el-input v-model="searchForm.name" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchData">搜索</el-button>
</el-form-item>
</el-form>
<!-- 操作栏 -->
<el-button v-perm="'sys:example:add'" type="primary" @click="handleAdd">新增</el-button>
<!-- 数据表格 -->
<el-table :data="tableData">
<el-table-column prop="id" label="ID" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="statusText" label="状态" />
<el-table-column label="操作">
<template #default="{ row }">
<el-button v-perm="'sys:example:update'" @click="handleEdit(row)">编辑</el-button>
<el-button v-perm="'sys:example:delete'" type="danger" @click="handleDelete(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination v-model:current-page="pageNo" v-model:page-size="pageSize"
:total="total" @change="fetchData" />
</template>
<script setup lang="ts">
import { getExamplePage, exampleDelete } from '@/api/tool/example';
const tableData = ref([]);
const pageNo = ref(1);
const pageSize = ref(20);
const total = ref(0);
const fetchData = async () => {
const res = await getExamplePage({ pageNo: pageNo.value, pageSize: pageSize.value });
tableData.value = res.records;
total.value = res.total;
};
onMounted(() => fetchData());
</script>文件:src/api/**/*.ts职责:封装后端 API 调用,一个后端模块对应一个 API 文件
命名规范:
| 操作 | 函数名 | HTTP 方法 | 参数传递 |
|---|---|---|---|
| 分页查询 | get{Module}Page | GET | params |
| 全量列表 | get{Module}List | GET | params |
| 详情 | get{Module}Detail | GET | URL 参数 |
| 新增 | {module}Add | POST | data |
| 修改 | {module}Update | PUT | data |
| 删除 | {module}Delete | DELETE | URL 参数 |
| 批量删除 | {module}BatchDelete | DELETE | data |
| 导入 | {module}Import | POST | FormData |
| 导出 | {module}Export | GET | params |
完整示例:
// src/api/system/user.ts
import { http } from '@/utils/http/axios';
// 分页查询
export function getUserPage(params?) {
return http.request({ url: '/user/page', method: 'GET', params });
}
// 详情
export function getUserDetail(id) {
return http.request({ url: '/user/detail/' + id, method: 'GET' });
}
// 新增
export function userAdd(data: any) {
return http.request({ url: '/user/add', method: 'POST', data });
}
// 修改
export function userUpdate(data: any) {
return http.request({ url: '/user/update', method: 'PUT', data });
}
// 删除
export function userDelete(id) {
return http.request({ url: '/user/delete/' + id, method: 'DELETE' });
}
// 批量删除
export function userBatchDelete(data: any) {
return http.request({ url: '/user/batchDelete', method: 'DELETE', data });
}文件:src/utils/http/axios/职责:Axios 封装,自动处理 Token、响应码、错误提示
src/utils/http/axios/
├── index.ts Axios 实例配置和拦截器
├── Axios.ts VAxios 类封装
├── axiosTransform.ts 数据转换接口
├── checkStatus.ts HTTP 状态码处理
├── helper.ts 辅助函数
└── types.ts 类型定义核心拦截器:
请求拦截器:
┌──────────────────────────────────────────┐
│ 1. 从 Store 获取 Token │
│ 2. 自动添加 Authorization: Bearer <token> │
└──────────────────────────────────────────┘
响应拦截器:
┌──────────────────────────────────────────┐
│ code === 0 → 成功,返回 data │
│ code === 401 → Token 过期,跳转登录页 │
│ code === 403 → 权限不足,显示错误提示 │
│ 其它 code → 业务失败,显示 msg │
└──────────────────────────────────────────┘文件:src/store/modules/*.ts职责:全局状态管理(Pinia)
| Store | 文件 | 管理的状态 |
|---|---|---|
| userStore | user.ts | Token、用户信息、权限列表、登录/登出 |
| asyncRouteStore | asyncRoute.ts | 动态路由、keep-alive 缓存列表 |
| projectSettingStore | projectSetting.ts | 项目配置(主题、布局等) |
| tabsViewStore | tabsView.ts | 标签页导航状态 |
| designSettingStore | designSetting.ts | 主题设计配置 |
| ossConfigStore | ossConfig.ts | 云存储配置 |
| lockscreenStore | lockscreen.ts | 锁屏状态 |
userStore 核心流程:
登录:
userStore.login(userInfo)
→ POST /login → 获取 access_token
→ Storage.set(ACCESS_TOKEN, token)
→ this.setToken(token)
获取用户信息:
userStore.GetInfo()
→ GET /index/getUserInfo
→ this.setPermissions(res.permissions)
→ this.setUserInfo(res)
登出:
userStore.logout()
→ POST /logout
→ 清除 Token、用户信息、权限列表
→ 跳转登录页静态路由(固定,src/router/)
├── /login 登录页
├── / 根路径,重定向到首页
├── /redirect/:path 重定向路由
└── /:path(.*)* 404 错误页
动态路由(从后端菜单生成)
├── /dashboard 仪表盘
├── /system/user 用户管理
├── /system/role 角色管理
├── /content/article 文章管理
└── ...1. 用户登录 → 获取 Token → 存入 Store
2. 路由守卫(router.beforeEach)检测到 Token
3. 调用 GET /index/getMenus 获取菜单树
4. generator-routers.ts 将菜单树转换为路由表
5. router.addRoute() 动态注入路由
6. 渲染侧边栏菜单┌─────────────────────────────────────────────────────────────┐
│ 顶部导航栏 │
│ [Logo] [面包屑] [搜索] [通知] [头像] │
├──────────┬──────────────────────────────────────────────────┤
│ │ │
│ 侧边栏 │ 内容区域 │
│ 菜单 │ │
│ │ ┌──────────────────────────────────────────┐ │
│ ──────── │ │ 标签页导航 │ │
│ 系统管理 │ ├──────────────────────────────────────────┤ │
│ ├ 用户 │ │ │ │
│ ├ 角色 │ │ 页面内容(keep-alive 缓存) │ │
│ └ 菜单 │ │ │ │
│ │ └──────────────────────────────────────────┘ │
└──────────┴──────────────────────────────────────────────────┘<!-- v-perm:单权限 -->
<el-button v-perm="'sys:user:add'" type="primary">新增</el-button>
<!-- v-perms:多权限(满足任一即可) -->
<el-button v-perms="['sys:user:add', 'sys:user:update']">操作</el-button>无权限时元素直接从 DOM 移除,而非仅隐藏。
超级管理员
用户权限列表包含 *:*:* 时,表示超级管理员,拥有所有权限,v-perm/v-perms 指令始终放行。
| Hook | 文件 | 说明 |
|---|---|---|
usePermission | hooks/web/usePermission.ts | 权限判断(hasPermission、hasSomePermission) |
useTable | hooks/web/useTable.ts | 表格通用逻辑(分页、搜索、刷新) |
useForm | hooks/web/useForm.ts | 表单通用逻辑(提交、重置、校验) |
| 指令 | 说明 |
|---|---|
v-perm | 单权限控制 |
v-perms | 多权限控制(满足任一) |
| 工具 | 文件 | 说明 |
|---|---|---|
| HTTP | utils/http/axios/ | Axios 封装 |
| Storage | utils/Storage.ts | localStorage 封装 |
| Dict | utils/dict.ts | 字典工具(从 Store 获取字典值) |
用户操作(点击搜索按钮)
│
▼
View: fetchData()
│ 调用 API 函数
▼
API: getExamplePage(params)
│ 调用 http.request()
▼
HTTP: 请求拦截器添加 Token
│ 发送 GET /example/page?pageNo=1&pageSize=20
▼
后端: Controller → Logic → Model → Database
│
▼
HTTP: 响应拦截器判断 code
│ code === 0 → 返回 data
▼
API: 返回 data
│
▼
View: tableData.value = res.records; total.value = res.total
│
▼
Vue 响应式 → 表格自动刷新登录成功
│
▼
userStore.setToken(token) → 响应式更新
userStore.setUserInfo(info) → 响应式更新
userStore.setPermissions(perm) → 响应式更新
│
▼
其他组件通过 userStore.username / userStore.permissions 读取
│
▼
v-perm 指令读取 permissions 列表 → 控制元素显隐| 后端 | 前端 | 说明 |
|---|---|---|
app/controller/UserController.php | src/api/system/user.ts | API 函数映射 |
app/logic/UserLogic.php | src/views/system/user/ | 业务页面 |
app/model/User.php | — | 前端不直接操作模型 |
app/validate/UserValidate.php | — | 前端校验由表单组件处理 |
app/validate/*Validate.php 的场景 | 表单 rules | 前端表单校验规则 |
route/app.php | src/api/**/*.ts | 路由 ↔ API 函数 |
后端字典 DictService | src/enums/ + 字典 Store | 枚举常量 |
后端 #[Permission] 注解 | v-perm 指令 | 权限码对应 |
| 调用方向 | 是否允许 | 说明 |
|---|---|---|
| View → API | ✅ | 标准调用 |
| View → Store | ✅ | 读取/修改全局状态 |
| View → Hooks | ✅ | 复用逻辑 |
| API → HTTP | ✅ | 调用 http.request() |
| API → Store | ❌ | API 层不应操作状态 |
| Store → API | ✅ | Store actions 中调用 API |
| HTTP → Store | ⚠️ | 仅在拦截器中读取 Token |
| Hooks → API | ✅ | Hooks 中可调用 API |