Skip to content

4.4 前端分层架构 ​

概述

前端采用 Vue 3 + TypeScript 组件化架构,分为 View(视图)→ API(接口)→ Store(状态)→ HTTP(请求)四层。辅以 Router(路由)、Directives(指令)、Hooks(组合式函数)、Utils(工具)等支撑模块。

架构全景 ​

text
┌─────────────────────────────────────────────────────────────────────┐
│                         支撑模块                                     │
│                                                                     │
│  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                                  │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘

各层职责详解 ​

View 层(视图) ​

文件:src/views/**/*.vue职责:页面渲染、用户交互、调用 API 获取数据

text
src/views/
├── system/             系统管理
│   ├── user/           用户管理
│   │   ├── index.vue   列表页
│   │   └── edit.vue    编辑弹窗
│   ├── role/           角色管理
│   └── menu/           菜单管理
├── content/            内容管理
│   ├── article/        文章管理
│   └── category/       分类管理
├── login.vue           登录页
├── dashboard/          仪表盘
└── ...

典型页面结构:

vue
<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>

API 层(接口) ​

文件:src/api/**/*.ts职责:封装后端 API 调用,一个后端模块对应一个 API 文件

命名规范:

操作函数名HTTP 方法参数传递
分页查询get{Module}PageGETparams
全量列表get{Module}ListGETparams
详情get{Module}DetailGETURL 参数
新增{module}AddPOSTdata
修改{module}UpdatePUTdata
删除{module}DeleteDELETEURL 参数
批量删除{module}BatchDeleteDELETEdata
导入{module}ImportPOSTFormData
导出{module}ExportGETparams

完整示例:

typescript
// 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 });
}

HTTP 层(请求) ​

文件:src/utils/http/axios/职责:Axios 封装,自动处理 Token、响应码、错误提示

text
src/utils/http/axios/
├── index.ts            Axios 实例配置和拦截器
├── Axios.ts            VAxios 类封装
├── axiosTransform.ts   数据转换接口
├── checkStatus.ts      HTTP 状态码处理
├── helper.ts           辅助函数
└── types.ts            类型定义

核心拦截器:

text
请求拦截器:
  ┌──────────────────────────────────────────┐
  │ 1. 从 Store 获取 Token                    │
  │ 2. 自动添加 Authorization: Bearer <token> │
  └──────────────────────────────────────────┘

响应拦截器:
  ┌──────────────────────────────────────────┐
  │ code === 0   → 成功,返回 data            │
  │ code === 401 → Token 过期,跳转登录页      │
  │ code === 403 → 权限不足,显示错误提示      │
  │ 其它 code    → 业务失败,显示 msg          │
  └──────────────────────────────────────────┘

Store 层(状态) ​

文件:src/store/modules/*.ts职责:全局状态管理(Pinia)

Store文件管理的状态
userStoreuser.tsToken、用户信息、权限列表、登录/登出
asyncRouteStoreasyncRoute.ts动态路由、keep-alive 缓存列表
projectSettingStoreprojectSetting.ts项目配置(主题、布局等)
tabsViewStoretabsView.ts标签页导航状态
designSettingStoredesignSetting.ts主题设计配置
ossConfigStoreossConfig.ts云存储配置
lockscreenStorelockscreen.ts锁屏状态

userStore 核心流程:

text
登录:
  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、用户信息、权限列表
    → 跳转登录页

路由与动态菜单 ​

路由架构 ​

text
静态路由(固定,src/router/)
├── /login              登录页
├── /                   根路径,重定向到首页
├── /redirect/:path     重定向路由
└── /:path(.*)*         404 错误页

动态路由(从后端菜单生成)
├── /dashboard          仪表盘
├── /system/user        用户管理
├── /system/role        角色管理
├── /content/article    文章管理
└── ...

动态路由生成流程 ​

text
1. 用户登录 → 获取 Token → 存入 Store
2. 路由守卫(router.beforeEach)检测到 Token
3. 调用 GET /index/getMenus 获取菜单树
4. generator-routers.ts 将菜单树转换为路由表
5. router.addRoute() 动态注入路由
6. 渲染侧边栏菜单

布局系统 ​

text
┌─────────────────────────────────────────────────────────────┐
│                        顶部导航栏                            │
│  [Logo]  [面包屑]                    [搜索] [通知] [头像]    │
├──────────┬──────────────────────────────────────────────────┤
│          │                                                  │
│  侧边栏   │              内容区域                            │
│  菜单     │                                                  │
│          │  ┌──────────────────────────────────────────┐   │
│ ──────── │  │ 标签页导航                                │   │
│ 系统管理  │  ├──────────────────────────────────────────┤   │
│  ├ 用户   │  │                                          │   │
│  ├ 角色   │  │          页面内容(keep-alive 缓存)       │   │
│  └ 菜单   │  │                                          │   │
│          │  └──────────────────────────────────────────┘   │
└──────────┴──────────────────────────────────────────────────┘

权限控制 ​

按钮级权限指令 ​

vue
<!-- 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 指令始终放行。

支撑模块 ​

Hooks(组合式函数) ​

Hook文件说明
usePermissionhooks/web/usePermission.ts权限判断(hasPermission、hasSomePermission)
useTablehooks/web/useTable.ts表格通用逻辑(分页、搜索、刷新)
useFormhooks/web/useForm.ts表单通用逻辑(提交、重置、校验)

Directives(自定义指令) ​

指令说明
v-perm单权限控制
v-perms多权限控制(满足任一)

Utils(工具函数) ​

工具文件说明
HTTPutils/http/axios/Axios 封装
Storageutils/Storage.tslocalStorage 封装
Dictutils/dict.ts字典工具(从 Store 获取字典值)

数据流向 ​

完整请求链路 ​

text
用户操作(点击搜索按钮)
    │
    ▼
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 响应式 → 表格自动刷新

Store 数据流 ​

text
登录成功
    │
    ▼
userStore.setToken(token)     → 响应式更新
userStore.setUserInfo(info)   → 响应式更新
userStore.setPermissions(perm) → 响应式更新
    │
    ▼
其他组件通过 userStore.username / userStore.permissions 读取
    │
    ▼
v-perm 指令读取 permissions 列表 → 控制元素显隐

前后端对应关系 ​

后端前端说明
app/controller/UserController.phpsrc/api/system/user.tsAPI 函数映射
app/logic/UserLogic.phpsrc/views/system/user/业务页面
app/model/User.php—前端不直接操作模型
app/validate/UserValidate.php—前端校验由表单组件处理
app/validate/*Validate.php 的场景表单 rules前端表单校验规则
route/app.phpsrc/api/**/*.ts路由 ↔ API 函数
后端字典 DictServicesrc/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

小蚂蚁云团队 · 提供技术支持