芋道管理后台前端(yudao-ui-admin-vben)
基于 Vue Vben Admin v5(pnpm workspaces + Turborepo monorepo),使用 Vue 3 + TypeScript + Vite 构建的企业级管理后台前端。提供多套 UI 库变体(Ant Design Vue、Element Plus、Naive UI、TDesign),与 ruoyi-vue-pro 后端配套。
项目结构
→ 详见 yudao-ui-dir
开发指南
| 主题 | 说明 | 参考 |
|---|---|---|
| 新增 CRUD 页面 | 创建完整前端 CRUD:API + 表格 + 表单弹窗 + 路由 | yudao-ui-crud |
| 新增 API | 添加带类型的后端 API 调用定义 | yudao-ui-add-api |
| 组件参考 | VxeTable、Form、Modal 等核心组件用法 | yudao-ui-components |
关键约定
路径别名
#/*映射到各应用的./src/*(在package.json#imports中定义)- 应用内部导入始终使用
#/前缀:import { $t } from '#/locales' - 共享包使用
@vben/前缀:import { preferences } from '@vben/preferences'
依赖管理
- 内部包:
"workspace:*" - 第三方包:
"catalog:"(版本在pnpm-workspace.yaml#catalog中集中管理)
代码风格
- 所有 Vue SFC 使用
<script lang="ts" setup> - 仅使用 Composition API,禁止 Options API
- 使用 TailwindCSS 工具类进行样式开发
- 所有用户可见文本使用
$t('key')国际化 - 遵循 Conventional Commits 规范
API 请求
所有 API 通过 requestClient(基于 @vben/request 的 Axios 封装)调用:
import { requestClient } from '#/api/request';
// GET 请求
requestClient.get<ResultType>('/path', { params })
// POST 请求
requestClient.post<ResultType>('/path', data)
// PUT 请求
requestClient.put<ResultType>('/path', data)
// DELETE 请求
requestClient.delete('/path?id=1')
// 下载
requestClient.download('/path', { params })
// 上传
requestClient.upload('/path', { file, ...data })
后端返回格式 { code: 0, data: T, msg: '' },requestClient 自动解包为 data。
API 文件组织
src/api/
├── core/ # 核心 API(登录、菜单等)
├── system/ # 系统管理模块
│ ├── dept/
│ │ └── index.ts
│ ├── user/
│ │ └── index.ts
│ └── ...
├── infra/ # 基础设施模块
├── bpm/ # 工作流模块
├── request.ts # requestClient 配置
└── index.ts
API 定义模式
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace SystemXxxApi {
export interface Xxx {
id?: number;
name: string;
status: number;
createTime?: Date;
}
}
/** 查询分页 */
export function getXxxPage(params: PageParam) {
return requestClient.get<PageResult<SystemXxxApi.Xxx>>('/system/xxx/page', { params });
}
/** 查询详情 */
export function getXxx(id: number) {
return requestClient.get<SystemXxxApi.Xxx>(`/system/xxx/get?id=${id}`);
}
/** 新增 */
export function createXxx(data: SystemXxxApi.Xxx) {
return requestClient.post('/system/xxx/create', data);
}
/** 修改 */
export function updateXxx(data: SystemXxxApi.Xxx) {
return requestClient.put('/system/xxx/update', data);
}
/** 删除 */
export function deleteXxx(id: number) {
return requestClient.delete(`/system/xxx/delete?id=${id}`);
}
/** 批量删除 */
export function deleteXxxList(ids: number[]) {
return requestClient.delete(`/system/xxx/delete-list?ids=${ids.join(',')}`);
}
/** 精简列表(下拉选项) */
export function getSimpleXxxList() {
return requestClient.get<SystemXxxApi.Xxx[]>('/system/xxx/simple-list');
}
页面文件组织
src/views/system/xxx/
├── data.ts # 表单 Schema + 表格列定义
├── index.vue # 列表页面(VxeTable Grid)
├── modules/
│ └── form.vue # 新建/编辑弹窗(Modal + Form)
└── components/ # 页面特有组件(可选)
权限控制
TableAction 组件支持 auth 属性进行权限控制:
{
label: '新增',
auth: ['system:xxx:create'], // 权限编码数组
onClick: handleCreate,
}
指令方式:
<button v-access:code="['system:xxx:create']">新增</button>
组件方式:
<AccessControl :codes="['system:xxx:create']">
<button>新增</button>
</AccessControl>
字典使用
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
// 在表单中使用
{
fieldName: 'status',
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
},
}
// 在表格中使用
{
field: 'status',
title: '状态',
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.COMMON_STATUS },
},
}
国际化
import { $t } from '#/locales';
// 模板中
{{ $t('ui.actionTitle.create', ['用户']) }}
// 脚本中
$t('ui.actionMessage.deleteConfirm', [row.name])
常用 i18n key:
ui.actionTitle.create/edit/delete/deleteBatchui.actionMessage.deleteConfirm/deleteBatchConfirm/deleteSuccess/operationSuccesscommon.edit/common.delete/common.status
路由配置
前端路由仅用于不需要后端菜单管理的页面(如"我的站内信"):
// src/router/routes/modules/system.ts
const routes: RouteRecordRaw[] = [
{
path: '/system/xxx',
component: () => import('#/views/system/xxx/index.vue'),
name: 'SystemXxx',
meta: {
title: 'Xxx管理',
icon: 'ant-design:xxx-outlined',
hideInMenu: true, // 不显示在菜单(由后端菜单管理)
},
},
];
大部分页面通过后端菜单动态注册路由,无需手动配置前端路由。
应用变体
| 应用 | UI 库 | 路径 |
|---|---|---|
web-antd |
Ant Design Vue | apps/web-antd |
web-antdv-next |
Ant Design Vue (Next) | apps/web-antdv-next |
web-ele |
Element Plus | apps/web-ele |
web-naive |
Naive UI | apps/web-naive |
web-tdesign |
TDesign Vue | apps/web-tdesign |
各应用在 src/adapter/ 中桥接通用组件到具体 UI 库。
请求拦截器
src/api/request.ts 中的拦截器:
- 请求拦截:附加
Bearer Token+Accept-Language+tenant-id请求头 - 响应拦截:API 解密(可选) → 解包
{ code, data, msg }→ Token 刷新 → 错误提示
核心包参考
| 包名 | 路径 | 用途 |
|---|---|---|
@vben/request |
packages/effects/request |
基于 Axios 的 RequestClient |
@vben/common-ui |
packages/effects/common-ui |
共享 UI(Page、Modal、Drawer 等) |
@vben/hooks |
packages/effects/hooks |
useAppConfig、getDictOptions 等 |
@vben/stores |
packages/stores |
全局 Pinia Store |
@vben/constants |
packages/constants |
全局常量(DICT_TYPE 等) |
@vben/utils |
packages/utils |
工具函数(handleTree 等) |
@vben/preferences |
packages/preferences |
响应式偏好管理器 |
@vben/locales |
packages/locales |
vue-i18n 工具 |
@vben/access |
packages/effects/access |
路由/菜单生成,权限指令 |
常用命令
pnpm dev:antd # 启动 Ant Design Vue 变体
pnpm dev:ele # 启动 Element Plus 变体
pnpm build:antd # 构建 Ant Design Vue 变体
pnpm lint # ESLint 检查
pnpm format # 代码格式化
pnpm test:unit # 运行单元测试
pnpm check:type # TypeScript 类型检查