CDesign Pro 项目
项目概述
CDesign Pro 是一个基于 Vue 3 + TypeScript + Vite 构建的现代化后台管理系统。项目采用最新的前端技术栈,提供了完整的权限管理、动态路由、主题配置等功能,适合作为企业级后台管理系统的脚手架。项目地址:https://gitee.com/WilliamHao/cdesign-pro。dev 分支删除了一些组件的演示页面,只保留了必要的组件。main 分支是项目的主分支,包含了完整的代码和文档。后续会持续更新。
核心特性
核心特性
- 🚀 现代化技术栈 - Vue 3.5 + TypeScript 5.6 + Vite 7.1
- 📦 开箱即用 - 完整的后台管理系统解决方案
- 🎨 丰富的主题配置 - 支持亮色/暗色/自动主题切换
- 🔐 完善的权限系统 - 动态路由、权限验证、角色管理
- 🌐 国际化支持 - 内置中英文切换
- 📊 丰富的组件库 - 图表、表单、表格等业务组件
- 🛠️ 开发工具齐全 - ESLint、Prettier、Stylelint、Husky 等
- 🎯 Mock 数据支持 - 基于 MSW 的 Mock 服务
技术栈
核心框架
| 技术 | 版本 | 说明 | 状态 |
|---|---|---|---|
| Vue | 3.5.21 | 渐进式 JavaScript 框架 | 核心 |
| TypeScript | 5.6.3 | JavaScript 的超集,提供类型支持 | 核心 |
| Vite | 7.1.5 | 下一代前端构建工具 | 构建工具 |
UI 框架
| 技术 | 版本 | 说明 | 状态 |
|---|---|---|---|
| Element Plus | 2.11.2 | 基于 Vue 3 的组件库 | UI 组件 |
| Tailwind CSS | 4.1.14 | 实用优先的 CSS 框架 | 样式 |
| @element-plus/icons-vue | 2.3.2 | Element Plus 图标库 | 图标 |
状态管理
| 技术 | 版本 | 说明 |
|---|---|---|
| Pinia | 3.0.3 | Vue 官方状态管理库 |
| pinia-plugin-persistedstate | 4.3.0 | Pinia 持久化插件 |
路由
| 技术 | 版本 | 说明 |
|---|---|---|
| Vue Router | 4.5.1 | Vue.js 官方路由 |
工具库
| 技术 | 版本 | 说明 |
|---|---|---|
| Axios | 1.12.2 | HTTP 客户端 |
| Vue i18n | 9.14.0 | 国际化解决方案 |
| ECharts | 6.0.0 | 数据可视化图表库 |
| @vueuse/core | 13.9.0 | Vue Composition API 工具集 |
| @wangeditor/editor | 5.1.23 | 富文本编辑器 |
| xlsx | 0.18.5 | Excel 文件处理 |
| xgplayer | 3.0.20 | 视频播放器 |
| mitt | 3.0.1 | 事件总线 |
| nprogress | 0.2.0 | 进度条 |
| crypto-js | 4.2.0 | 加密库 |
| file-saver | 2.0.5 | 文件保存 |
| highlight.js | 11.10.0 | 代码高亮 |
开发工具
| 技术 | 版本 | 说明 |
|---|---|---|
| ESLint | 9.9.1 | JavaScript 代码检查工具 |
| Prettier | 3.5.3 | 代码格式化工具 |
| Stylelint | 16.20.0 | CSS/SCSS 代码检查工具 |
| Husky | 9.1.5 | Git hooks 工具 |
| lint-staged | 15.5.2 | 只检查暂存区代码 |
| commitizen | 4.3.0 | 规范化提交信息 |
| MSW | 2.13.2 | Mock Service Worker |
| Vue DevTools | 7.7.6 | Vue 开发者工具 |
项目结构
项目目录树
点击展开查看完整的项目结构。
cdesign-pro/
├── .husky/ # Git hooks 配置
│ ├── commit-msg # 提交信息验证
│ └── pre-commit # 提交前检查
├── .vscode/ # VS Code 配置
│ ├── extensions.json # 推荐扩展
│ └── settings.json # 编辑器设置
├── public/ # 静态资源
│ ├── favicon.ico # 网站图标
│ └── mockServiceWorker.js # MSW Service Worker
├── scripts/ # 脚本文件
│ └── clean-dev.ts # 清理开发环境脚本
├── src/ # 源代码目录
│ ├── api/ # API 接口定义
│ │ ├── auth.ts # 认证相关接口
│ │ └── system-manage.ts # 系统管理接口
│ ├── assets/ # 静态资源
│ │ ├── images/ # 图片资源
│ │ │ ├── avatar/ # 头像
│ │ │ ├── ceremony/ # 节日相关
│ │ │ ├── common/ # 公共图片
│ │ │ ├── draw/ # 绘画相关
│ │ │ ├── lock/ # 锁屏背景
│ │ │ ├── login/ # 登录相关
│ │ │ ├── settings/ # 设置相关
│ │ │ ├── svg/ # SVG 图片
│ │ │ └── user/ # 用户相关
│ │ ├── styles/ # 样式文件
│ │ │ ├── core/ # 核心样式
│ │ │ │ ├── app.scss
│ │ │ │ ├── dark.scss
│ │ │ │ ├── el-dark.scss
│ │ │ │ ├── el-light.scss
│ │ │ │ ├── el-ui.scss
│ │ │ │ ├── md.scss
│ │ │ │ ├── mixin.scss
│ │ │ │ ├── reset.scss
│ │ │ │ ├── router-transition.scss
│ │ │ │ ├── tailwind.css
│ │ │ │ ├── theme-animation.scss
│ │ │ │ └── theme-change.scss
│ │ │ ├── custom/ # 自定义样式
│ │ │ └── index.scss # 样式入口
│ │ └── svg/ # SVG 资源
│ │ └── loading.ts
│ ├── components/ # 组件目录
│ │ └── core/ # 核心组件
│ │ ├── banners/ # 横幅组件
│ │ ├── base/ # 基础组件
│ │ ├── cards/ # 卡片组件
│ │ ├── charts/ # 图表组件
│ │ ├── forms/ # 表单组件
│ │ ├── layouts/ # 布局组件
│ │ ├── media/ # 媒体组件
│ │ ├── others/ # 其他组件
│ │ ├── tables/ # 表格组件
│ │ ├── text-effect/ # 文本特效
│ │ ├── theme/ # 主题组件
│ │ ├── views/ # 页面组件
│ │ └── widget/ # 小部件
│ ├── config/ # 配置文件
│ │ ├── assets/ # 资源配置
│ │ ├── modules/ # 配置模块
│ │ ├── fastEnter.ts # 快速入口配置
│ │ ├── index.ts # 主配置
│ │ └── setting.ts # 设置配置
│ ├── directives/ # 自定义指令
│ │ ├── business/ # 业务指令
│ │ ├── core/ # 核心指令
│ │ └── index.ts # 指令入口
│ ├── enums/ # 枚举定义
│ │ ├── appEnum.ts # 应用枚举
│ │ └── formEnum.ts # 表单枚举
│ ├── hooks/ # 组合式函数
│ │ ├── core/ # 核心 hooks
│ │ └── index.ts # hooks 入口
│ ├── locales/ # 国际化
│ │ ├── langs/ # 语言文件
│ │ │ ├── en.json # 英文
│ │ │ └── zh.json # 中文
│ │ └── index.ts # 国际化配置
│ ├── mock/ # Mock 数据
│ │ ├── browser/ # 浏览器 Mock
│ │ │ ├── handlers/ # 处理器
│ │ │ └── index.ts # Mock 入口
│ │ ├── temp/ # 临时数据
│ │ └── upgrade/ # 升级相关
│ ├── plugins/ # 插件
│ │ ├── echarts.ts # ECharts 插件
│ │ └── index.ts # 插件入口
│ ├── router/ # 路由配置
│ │ ├── core/ # 路由核心
│ │ │ ├── ComponentLoader.ts # 组件加载器
│ │ │ ├── IframeRouteManager.ts # Iframe 路由管理
│ │ │ ├── MenuProcessor.ts # 菜单处理器
│ │ │ ├── RoutePermissionValidator.ts # 路由权限验证
│ │ │ ├── RouteRegistry.ts # 路由注册器
│ │ │ ├── RouteTransformer.ts # 路由转换器
│ │ │ ├── RouteValidator.ts # 路由验证器
│ │ │ └── index.ts
│ │ ├── guards/ # 路由守卫
│ │ │ ├── afterEach.ts # 后置守卫
│ │ │ └── beforeEach.ts # 前置守卫
│ │ ├── modules/ # 路由模块
│ │ ├── routes/ # 路由定义
│ │ │ ├── asyncRoutes.ts # 异步路由
│ │ │ └── staticRoutes.ts # 静态路由
│ │ ├── index.ts # 路由入口
│ │ └── routesAlias.ts # 路由别名
│ ├── store/ # 状态管理
│ │ ├── modules/ # Store 模块
│ │ │ ├── menu.ts # 菜单状态
│ │ │ ├── setting.ts # 设置状态
│ │ │ ├── table.ts # 表格状态
│ │ │ ├── user.ts # 用户状态
│ │ │ └── worktab.ts # 工作标签状态
│ │ └── index.ts # Store 入口
│ ├── types/ # 类型定义
│ │ ├── api/ # API 类型
│ │ ├── common/ # 公共类型
│ │ ├── component/ # 组件类型
│ │ ├── config/ # 配置类型
│ │ ├── directive/ # 指令类型
│ │ ├── router/ # 路由类型
│ │ ├── store/ # Store 类型
│ │ └── index.ts # 类型入口
│ ├── utils/ # 工具函数
│ │ ├── constants/ # 常量
│ │ ├── form/ # 表单工具
│ │ ├── http/ # HTTP 工具
│ │ ├── navigation/ # 导航工具
│ │ ├── socket/ # Socket 工具
│ │ ├── storage/ # 存储工具
│ │ ├── sys/ # 系统工具
│ │ ├── table/ # 表格工具
│ │ ├── ui/ # UI 工具
│ │ ├── index.ts # 工具入口
│ │ └── router.ts # 路由工具
│ ├── views/ # 页面视图
│ │ ├── auth/ # 认证页面
│ │ │ ├── forget-password/ # 忘记密码
│ │ │ ├── login/ # 登录
│ │ │ └── register/ # 注册
│ │ ├── dashboard/ # 仪表盘
│ │ │ └── console/ # 控制台
│ │ ├── exception/ # 异常页面
│ │ │ ├── 403/ # 403 页面
│ │ │ ├── 404/ # 404 页面
│ │ │ └── 500/ # 500 页面
│ │ ├── index/ # 首页
│ │ └── outside/ # 外部页面
│ ├── App.vue # 根组件
│ ├── env.d.ts # 环境变量类型
│ └── main.ts # 应用入口
├── .env # 环境变量
├── .env.development # 开发环境变量
├── .env.production # 生产环境变量
├── .gitattributes # Git 属性
├── .gitignore # Git 忽略文件
├── .prettierignore # Prettier 忽略文件
├── .prettierrc # Prettier 配置
├── .stylelintignore # Stylelint 忽略文件
├── .stylelintrc.cjs # Stylelint 配置
├── commitlint.config.cjs # Commitlint 配置
├── eslint.config.mjs # ESLint 配置
├── index.html # HTML 模板
├── package.json # 项目配置
├── pnpm-lock.yaml # 依赖锁定文件
├── tsconfig.json # TypeScript 配置
└── vite.config.ts # Vite 配置核心功能实现
1. 路由系统
路由架构
项目采用 静态路由 + 动态路由 的混合路由架构:
- 静态路由:不需要权限验证的页面(登录、注册、异常页面等)
- 动态路由:根据用户权限动态加载的路由
核心模块
路由注册器 (RouteRegistry)
负责动态路由的注册和卸载:
class RouteRegistry {
// 注册动态路由
register(menuList: MenuItem[]): void;
// 卸载动态路由
unregister(): void;
// 检查是否已注册
isRegistered(): boolean;
}菜单处理器 (MenuProcessor)
负责菜单数据的获取和处理:
class MenuProcessor {
// 获取菜单列表(支持前端模式和后端模式)
async getMenuList(): Promise<MenuItem[]>;
// 验证菜单数据
validateMenuList(menuList: MenuItem[]): boolean;
}路由权限验证器 (RoutePermissionValidator)
验证用户是否有权限访问目标路径:
class RoutePermissionValidator {
// 验证路径权限
static validatePath(
targetPath: string,
menuList: MenuItem[],
homePath: string,
): { path: string; hasPermission: boolean };
}路由守卫
前置守卫 (beforeEach)
完整的路由导航守卫流程:
- 启动进度条 - 根据
showNprogress配置显示进度条 - 检查登录状态 - 未登录用户跳转到登录页
- 路由初始化检查 - 防止路由初始化失败后死循环
- 动态路由注册 - 首次访问时获取菜单并注册路由
- 根路径重定向 - 将根路径重定向到首页
- 权限验证 - 验证用户是否有权限访问目标路径
- 页面标题设置 - 设置页面标题
- 工作标签页管理 - 添加工作标签页
后置守卫 (afterEach)
- 关闭进度条
- 关闭加载动画
路由配置
// vite.config.ts
export default defineConfig({
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
"@views": resolvePath("src/views"),
"@imgs": resolvePath("src/assets/images"),
"@icons": resolvePath("src/assets/icons"),
"@utils": resolvePath("src/utils"),
"@stores": resolvePath("src/store"),
"@styles": resolvePath("src/assets/styles"),
},
},
});2. HTTP 请求封装
核心特性
- 请求拦截器:自动添加 Token、设置请求头
- 响应拦截器:统一处理响应数据、错误处理
- 401 防抖机制:防止多次弹出未授权提示
- 自动重试:请求失败自动重试(可配置)
- 统一消息提示:成功/失败消息提示
实现细节
Axios 实例配置
const axiosInstance = axios.create({
timeout: 15000, // 请求超时时间
baseURL: VITE_API_URL, // API 基础路径
withCredentials: true, // 跨域携带 cookie
validateStatus: (status) => status >= 200 && status < 300,
});请求拦截器
axiosInstance.interceptors.request.use(
(request: InternalAxiosRequestConfig) => {
// 自动添加 Token
const { accessToken } = useUserStore();
if (accessToken) {
request.headers.set("Authorization", accessToken);
}
// 自动设置 Content-Type
if (request.data && !(request.data instanceof FormData)) {
request.headers.set("Content-Type", "application/json");
request.data = JSON.stringify(request.data);
}
return request;
},
);响应拦截器
axiosInstance.interceptors.response.use(
(response: AxiosResponse<BaseResponse>) => {
const { code, msg } = response.data;
// 成功响应
if (code === ApiStatus.success) {
return response;
}
// 401 未授权
if (code === ApiStatus.unauthorized) {
handleUnauthorizedError(msg);
}
// 其他错误
throw createHttpError(msg || "请求失败", code);
},
(error) => {
// 网络错误处理
if (error.response?.status === ApiStatus.unauthorized) {
handleUnauthorizedError();
}
return Promise.reject(handleError(error));
},
);401 防抖机制
let isUnauthorizedErrorShown = false;
let unauthorizedTimer: NodeJS.Timeout | null = null;
function handleUnauthorizedError(message?: string): never {
const error = createHttpError(message || "未授权", ApiStatus.unauthorized);
if (!isUnauthorizedErrorShown) {
isUnauthorizedErrorShown = true;
logOut(); // 退出登录
// 3秒后重置状态
unauthorizedTimer = setTimeout(resetUnauthorizedError, 3000);
showError(error, true);
throw error;
}
throw error;
}自动重试机制
async function retryRequest<T>(
config: ExtendedAxiosRequestConfig,
retries: number = 0,
): Promise<T> {
try {
return await request<T>(config);
} catch (error) {
// 判断是否需要重试
if (retries > 0 && error instanceof HttpError && shouldRetry(error.code)) {
await delay(1000); // 延迟1秒
return retryRequest<T>(config, retries - 1);
}
throw error;
}
}
function shouldRetry(statusCode: number) {
return [
ApiStatus.requestTimeout, // 408
ApiStatus.internalServerError, // 500
ApiStatus.badGateway, // 502
ApiStatus.serviceUnavailable, // 503
ApiStatus.gatewayTimeout, // 504
].includes(statusCode);
}API 方法
const api = {
get<T>(config: ExtendedAxiosRequestConfig) {
return retryRequest<T>({ ...config, method: "GET" });
},
post<T>(config: ExtendedAxiosRequestConfig) {
return retryRequest<T>({ ...config, method: "POST" });
},
put<T>(config: ExtendedAxiosRequestConfig) {
return retryRequest<T>({ ...config, method: "PUT" });
},
del<T>(config: ExtendedAxiosRequestConfig) {
return retryRequest<T>({ ...config, method: "DELETE" });
},
};3. 状态管理
Pinia Store 架构
项目采用 Pinia 作为状态管理工具,支持模块化和持久化。
核心模块
- User Store - 用户状态管理
- Setting Store - 系统设置状态管理
- Menu Store - 菜单状态管理
- Worktab Store - 工作标签页状态管理
- Table Store - 表格状态管理
持久化机制
版本化存储键管理
class StorageKeyManager {
private version: string;
constructor() {
this.version = __APP_VERSION__; // 从环境变量获取版本号
}
// 生成版本化的存储键
getStorageKey(storeId: string): string {
return `sys-v${this.version}-${storeId}`;
}
}持久化配置
import { createPersistedState } from "pinia-plugin-persistedstate";
store.use(
createPersistedState({
key: (storeId: string) => storageKeyManager.getStorageKey(storeId),
storage: localStorage,
serializer: {
serialize: JSON.stringify,
deserialize: JSON.parse,
},
}),
);自动数据迁移
当版本更新时,自动迁移旧版本数据到当前版本:
// 存储键示例
// v1.0.0: sys-v1.0.0-user
// v1.0.1: sys-v1.0.1-user
// 自动从 sys-v1.0.0-user 迁移到 sys-v1.0.1-userUser Store
用户状态管理模块,管理用户登录状态、个人信息、语言设置等。
状态定义
{
language: LanguageEnum, // 语言设置
isLogin: boolean, // 登录状态
isLock: boolean, // 锁屏状态
lockPassword: string, // 锁屏密码
info: Partial<Api.Auth.UserInfo>, // 用户信息
searchHistory: AppRouteRecord[], // 搜索历史
accessToken: string, // 访问令牌
refreshToken: string // 刷新令牌
}核心方法
// 设置用户信息
setUserInfo(newInfo: Api.Auth.UserInfo): void
// 设置登录状态
setLoginStatus(status: boolean): void
// 设置语言
setLanguage(lang: LanguageEnum): void
// 设置令牌
setToken(newAccessToken: string, newRefreshToken?: string): void
// 退出登录
logOut(): void
// 检查并清理工作台标签页(不同用户登录时)
checkAndClearWorktabs(): void退出登录流程
function logOut() {
// 1. 保存当前用户 ID
const currentUserId = info.value.userId;
if (currentUserId) {
localStorage.setItem(StorageConfig.LAST_USER_ID_KEY, String(currentUserId));
}
// 2. 清空用户信息
info.value = {};
isLogin.value = false;
isLock.value = false;
lockPassword.value = "";
accessToken.value = "";
refreshToken.value = "";
// 3. 清理路由和菜单
sessionStorage.removeItem("iframeRoutes");
useMenuStore().setHomePath("");
resetRouterState(500);
// 4. 跳转到登录页
router.push({
name: "Login",
query: { redirect: currentRoute.fullPath },
});
}Setting Store
系统设置状态管理,管理主题、菜单、界面显示等配置。
状态定义
{
// 菜单相关
menuType: MenuTypeEnum, // 菜单类型
menuOpenWidth: number, // 菜单展开宽度
menuOpen: boolean, // 菜单是否展开
dualMenuShowText: boolean, // 双菜单是否显示文本
// 主题相关
systemThemeType: SystemThemeEnum, // 系统主题类型
systemThemeMode: SystemThemeEnum, // 系统主题模式
menuThemeType: MenuThemeEnum, // 菜单主题类型
systemThemeColor: string, // 系统主题颜色
// 界面显示
showMenuButton: boolean, // 显示菜单按钮
showFastEnter: boolean, // 显示快速入口
showRefreshButton: boolean, // 显示刷新按钮
showCrumbs: boolean, // 显示面包屑
showWorkTab: boolean, // 显示工作标签
showLanguage: boolean, // 显示语言切换
showNprogress: boolean, // 显示进度条
showSettingGuide: boolean, // 显示设置引导
showFestivalText: boolean, // 显示节日文本
watermarkVisible: boolean, // 显示水印
// 功能设置
autoClose: boolean, // 自动关闭
uniqueOpened: boolean, // 唯一展开
colorWeak: boolean, // 色弱模式
refresh: boolean, // 刷新标记
// 样式设置
boxBorderMode: boolean, // 边框模式
pageTransition: string, // 页面过渡效果
tabStyle: string, // 标签页样式
customRadius: string, // 自定义圆角
containerWidth: ContainerWidthEnum // 容器宽度
}核心方法
// 切换菜单布局
switchMenuLayouts(type: MenuTypeEnum): void
// 设置全局主题
setGlopTheme(theme: SystemThemeEnum, themeMode: SystemThemeEnum): void
// 切换菜单样式
switchMenuStyles(theme: MenuThemeEnum): void
// 设置 Element Plus 主题颜色
setElementTheme(theme: string): void
// 刷新页面
reload(): void
// 设置水印显示
setWatermarkVisible(visible: boolean): void计算属性
// 获取菜单主题
const getMenuTheme = computed((): MenuThemeType => {
const list = AppConfig.themeList.filter(
(item) => item.theme === menuThemeType.value,
);
return isDark.value ? AppConfig.darkMenuStyles[0] : list[0];
});
// 判断是否为暗色模式
const isDark = computed((): boolean => {
return systemThemeType.value === SystemThemeEnum.DARK;
});
// 获取菜单展开宽度
const getMenuOpenWidth = computed((): string => {
return menuOpenWidth.value + "px";
});
// 获取自定义圆角
const getCustomRadius = computed((): string => {
return customRadius.value + "rem";
});4. 主题系统
主题类型
支持三种主题类型:
- 亮色主题 (Light) - 浅色背景
- 暗色主题 (Dark) - 深色背景
- 自动主题 (Auto) - 跟随系统主题
主题配置
// src/config/index.ts
{
// 系统主题列表
settingThemeList: [
{
name: 'Light',
theme: SystemThemeEnum.LIGHT,
color: ['#fff', '#fff'],
img: configImages.themeStyles.light
},
{
name: 'Dark',
theme: SystemThemeEnum.DARK,
color: ['#22252A'],
img: configImages.themeStyles.dark
},
{
name: 'System',
theme: SystemThemeEnum.AUTO,
color: ['#fff', '#22252A'],
img: configImages.themeStyles.system
}
],
// 菜单主题列表
themeList: [
{
theme: MenuThemeEnum.DESIGN,
background: '#FFFFFF',
systemNameColor: 'var(--art-gray-800)',
iconColor: '#6B6B6B',
textColor: '#29343D',
img: configImages.menuStyles.design
},
{
theme: MenuThemeEnum.DARK,
background: '#191A23',
systemNameColor: '#D9DADB',
iconColor: '#BABBBD',
textColor: '#BABBBD',
img: configImages.menuStyles.dark
},
{
theme: MenuThemeEnum.LIGHT,
background: '#ffffff',
systemNameColor: 'var(--art-gray-800)',
iconColor: '#6B6B6B',
textColor: '#29343D',
img: configImages.menuStyles.light
}
],
// 系统主色
systemMainColor: [
'#5D87FF',
'#B48DF3',
'#1D84FF',
'#60C041',
'#38C0FC',
'#F9901F',
'#FF80C8'
]
}主题初始化
// src/hooks/core/useTheme.ts
export function initializeTheme() {
const settingStore = useSettingStore();
const { systemThemeType, systemThemeColor } = storeToRefs(settingStore);
// 初始化主题类型
const theme = localStorage.getItem(StorageConfig.THEME_KEY);
if (theme) {
settingStore.setGlopTheme(theme as SystemThemeEnum, systemThemeType.value);
}
// 初始化主题颜色
setElementThemeColor(systemThemeColor.value);
// 监听系统主题变化
if (systemThemeType.value === SystemThemeEnum.AUTO) {
const darkModeMediaQuery = window.matchMedia(
"(prefers-color-scheme: dark)",
);
darkModeMediaQuery.addEventListener("change", (e) => {
document.documentElement.classList.toggle("dark", e.matches);
});
}
}Element Plus 主题色动态设置
// src/utils/ui/colors.ts
export function setElementThemeColor(color: string) {
const el = document.documentElement;
el.style.setProperty("--el-color-primary", color);
// 生成不同深度的颜色
for (let i = 1; i <= 9; i++) {
const lightColor = lighten(color, i / 10);
el.style.setProperty(`--el-color-primary-light-${i}`, lightColor);
}
// 生成深色
const darkColor = darken(color, 0.1);
el.style.setProperty("--el-color-primary-dark-2", darkColor);
}5. 国际化
配置
// src/locales/index.ts
import { createI18n } from "vue-i18n";
import enMessages from "./langs/en.json";
import zhMessages from "./langs/zh.json";
const messages = {
[LanguageEnum.EN]: enMessages,
[LanguageEnum.ZH]: zhMessages,
};
const i18n = createI18n({
locale: getDefaultLanguage(), // 从存储中获取语言设置
legacy: false, // 使用 Composition API 模式
globalInjection: true, // 全局注入 $t 函数
fallbackLocale: LanguageEnum.ZH, // 回退语言
messages,
});
// 全局翻译函数
export const $t = i18n.global.t as Translation;语言选项
export const languageOptions = [
{ value: LanguageEnum.ZH, label: "简体中文" },
{ value: LanguageEnum.EN, label: "English" },
];使用方式
在模板中使用
<template>
<div>{{ $t("common.submit") }}</div>
</template>在脚本中使用
import { $t } from "@/locales";
const message = $t("common.submit");切换语言
import { useUserStore } from "@/store/modules/user";
const userStore = useUserStore();
userStore.setLanguage(LanguageEnum.EN);6. Mock 服务
MSW 配置
// src/mock/browser/index.ts
import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);
export async function setupMockServiceWorker() {
const VITE_ENABLE_MSW = import.meta.env.VITE_ENABLE_MSW === "true";
if (!VITE_ENABLE_MSW) {
console.log("[MSW] 未启用,使用真实 API");
return false;
}
try {
await worker.start({
onUnhandledRequest: "bypass", // 未处理的请求直接通过
});
console.log("[MSW] Mock Service Worker 已启动");
return true;
} catch (error) {
console.error("[MSW] 启动失败:", error);
return false;
}
}Handler 示例
// src/mock/browser/handlers/auth.ts
import { http, HttpResponse } from "msw";
export const authHandlers = [
// 登录
http.post("/api/login", () => {
return HttpResponse.json({
code: 200,
msg: "登录成功",
data: {
accessToken: "mock-access-token",
refreshToken: "mock-refresh-token",
},
});
}),
// 获取用户信息
http.get("/api/user/info", () => {
return HttpResponse.json({
code: 200,
msg: "获取成功",
data: {
userId: 1,
username: "admin",
nickname: "管理员",
avatar: "https://example.com/avatar.jpg",
roles: ["admin"],
},
});
}),
];环境变量控制
# .env.development
VITE_ENABLE_MSW = true # 启用 Mock
# .env.production
# 不设置或设置为 false,使用真实 APIMSW 方案优势
1. 真实的网络环境
MSW 在 Service Worker 层面拦截请求,而不是在应用层面 Mock。这意味着:
- 请求仍然通过真实的网络层发送
- 可以在 DevTools 的 Network 面板中看到完整的请求和响应
- 可以测试请求超时、网络错误等真实场景
- 更接近生产环境的实际表现
2. 无需修改代码
- 不需要在代码中引入 Mock 库或修改业务逻辑
- 只需在入口文件启动 MSW,业务代码完全无感知
- 切换 Mock 和真实 API 只需修改环境变量,无需改动代码
3. 支持浏览器和 Node.js 环境
- 浏览器环境:使用 Service Worker 拦截请求
- Node.js 环境:使用 Node.js 原生模块拦截请求
- 可以在单元测试、集成测试中使用同一套 Mock 数据
4. 与真实 API 无缝切换
- 未定义 Mock 的请求会自动转发到真实服务器
- 可以部分 Mock,部分使用真实 API
- 方便渐进式开发和测试
5. 类型安全
- 支持 TypeScript,提供完整的类型定义
- 可以与 OpenAPI 规范集成,自动生成 Mock Handler
6. 开发体验优秀
- 支持热更新,修改 Mock 数据无需重启服务
- 提供丰富的调试工具和日志
- 社区活跃,文档完善
Mock 方案对比
| 特性 | MSW | Mock.js | Apifox | 传统 Mock Server |
|---|---|---|---|---|
| 实现方式 | Service Worker 拦截 | XHR/Fetch 劫持 | 独立 Mock 服务器 | 独立服务器 |
| 网络请求 | ✅ 真实网络请求 | ❌ 劫持请求,不发送 | ✅ 真实网络请求 | ✅ 真实网络请求 |
| DevTools 支持 | ✅ 可在 Network 查看 | ❌ 无法查看 | ✅ 可在 Network 查看 | ✅ 可在 Network 查看 |
| 代码侵入性 | ✅ 无侵入 | ⚠️ 需要引入库 | ✅ 无侵入 | ✅ 无侵入 |
| 环境支持 | ✅ 浏览器 + Node.js | ✅ 浏览器 + Node.js | ❌ 仅浏览器 | ✅ 浏览器 + Node.js |
| 部分 Mock | ✅ 支持 | ⚠️ 需要配置 | ✅ 支持 | ✅ 支持 |
| 热更新 | ✅ 支持 | ⚠️ 需要配置 | ✅ 支持 | ❌ 需要重启 |
| 类型安全 | ✅ TypeScript 支持 | ⚠️ 社区类型定义 | ✅ 自动生成 | ⚠️ 需手动维护 |
| 学习成本 | ⚠️ 中等 | ✅ 低 | ⚠️ 中等 | ⚠️ 高 |
| 部署复杂度 | ✅ 零部署 | ✅ 零部署 | ⚠️ 需要部署服务 | ⚠️ 需要部署服务 |
| 团队协作 | ✅ Mock 数据在代码中 | ✅ Mock 数据在代码中 | ✅ 云端同步 | ⚠️ 需要共享服务器 |
| OpenAPI 集成 | ✅ 支持 | ❌ 不支持 | ✅ 深度集成 | ⚠️ 需手动配置 |
| 成本 | ✅ 开源免费 | ✅ 开源免费 | ⚠️ 免费版有限制 | ⚠️ 需要服务器成本 |
详细对比分析
1. Mock.js
优点:
- 学习成本低,API 简单直观
- 内置数据生成器,可以快速生成随机数据
- 社区成熟,使用广泛
缺点:
- 劫持 XHR/Fetch,不是真实的网络请求
- 无法在 DevTools 中查看请求和响应
- 对 Fetch API 的支持不够完善
- 难以模拟网络错误、超时等场景
- 需要在业务代码中引入 Mock 数据,有侵入性
适用场景:
- 快速原型开发
- 简单的 Mock 需求
- 不需要真实网络请求的场景
2. Apifox
优点:
- 提供 Web 界面,可视化配置 Mock
- 支持 OpenAPI 规范,自动生成 Mock 数据
- 团队协作方便,云端同步
- 集成 API 文档、调试、Mock、测试等功能
缺点:
- 需要启动独立的 Mock 服务器
- 免费版功能有限制
- 依赖外部服务,离线开发受限
- 需要额外的学习和配置成本
适用场景:
- 团队协作项目
- 需要完整的 API 管理平台
- 有 OpenAPI 规范的项目
3. MSW (Mock Service Worker)
优点:
- 真实的网络请求,开发体验最佳
- 无代码侵入,业务代码无感知
- 支持浏览器和 Node.js 环境
- 可以部分 Mock,部分使用真实 API
- 支持 TypeScript,类型安全
- 开源免费,社区活跃
缺点:
- 学习成本略高,需要理解 Service Worker
- 配置相对复杂
- 需要单独定义 Handler
适用场景:
- 追求真实开发体验的项目
- 需要在测试中使用 Mock 的项目
- 前后端分离开发,前端独立开发
为什么选择 MSW?
本项目选择 MSW 作为 Mock 方案,主要基于以下考虑:
真实的开发体验 - MSW 在 Service Worker 层拦截请求,保留了真实的网络请求流程,开发者可以在 DevTools 中查看完整的请求和响应,这与生产环境完全一致。
无侵入性 - 业务代码不需要任何修改,只需在入口文件启动 MSW。切换 Mock 和真实 API 只需修改环境变量,极大地降低了开发和测试的成本。
灵活性 - 支持部分 Mock,未定义 Mock 的请求会自动转发到真实服务器。这在后端接口部分完成时特别有用,前端可以 Mock 未完成的接口,使用真实的已完成接口。
测试友好 - 同一套 Mock 数据可以在浏览器和 Node.js 环境中使用,方便单元测试和集成测试。
类型安全 - 支持 TypeScript,可以与项目的类型系统完美集成,避免 Mock 数据与实际 API 不一致的问题。
零部署成本 - 不需要启动额外的 Mock 服务器,Mock 数据直接在代码仓库中管理,方便版本控制和团队协作。
最佳实践建议
1. Mock 数据管理
// 推荐:将 Mock 数据单独管理
// src/mock/data/user.ts
export const userData = {
userId: 1,
username: "admin",
nickname: "管理员",
};
// src/mock/browser/handlers/user.ts
import { userData } from "../data/user";
export const userHandlers = [
http.get("/api/user/info", () => {
return HttpResponse.json({
code: 200,
data: userData,
});
}),
];2. 环境隔离
// 开发环境启用 Mock,生产环境禁用
const VITE_ENABLE_MSW = import.meta.env.VITE_ENABLE_MSW === "true";
// 只在开发环境启动 MSW
if (import.meta.env.DEV && VITE_ENABLE_MSW) {
setupMockServiceWorker();
}3. 错误场景模拟
// 模拟网络错误
http.get("/api/user/info", () => {
return new Response(null, { status: 500 });
});
// 模拟网络超时
http.get("/api/user/info", async () => {
await delay(30000); // 延迟 30 秒
return HttpResponse.json({ code: 200 });
});4. 动态 Mock 数据
// 使用内存数据库模拟真实场景
let users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
http.get("/api/users", () => {
return HttpResponse.json({ code: 200, data: users });
});
http.post("/api/users", async ({ request }) => {
const newUser = await request.json();
users.push(newUser);
return HttpResponse.json({ code: 200, data: newUser });
});7. 组件库
图表组件
- ArtBarChart - 柱状图
- ArtLineChart - 折线图
- ArtRingChart - 环形图
- ArtRadarChart - 雷达图
- ArtScatterChart - 散点图
- ArtKLineChart - K线图
- ArtHBarChart - 横向柱状图
- ArtDualBarCompareChart - 双柱对比图
表单组件
- ArtForm - 表单组件
- ArtSearchBar - 搜索栏
- ArtWangEditor - 富文本编辑器
- ArtDragVerify - 拖动验证
- ArtExcelImport - Excel 导入
- ArtExcelExport - Excel 导出
- ArtButtonMore - 更多按钮
- ArtButtonTable - 表格按钮
布局组件
- ArtHeaderBar - 顶部栏
- ArtSidebarMenu - 侧边栏菜单
- ArtHorizontalMenu - 水平菜单
- ArtMixedMenu - 混合菜单
- ArtBreadcrumb - 面包屑
- ArtWorkTab - 工作标签页
- ArtPageContent - 页面内容
- ArtSettingsPanel - 设置面板
- ArtScreenLock - 锁屏
- ArtGlobalSearch - 全局搜索
- ArtFastEnter - 快速入口
- ArtNotification - 通知
- ArtWatermark - 水印
卡片组件
- ArtStatsCard - 统计卡片
- ArtDataListCard - 数据列表卡片
- ArtTimelineListCard - 时间线列表卡片
- ArtProgressCard - 进度卡片
- ArtImageCard - 图片卡片
- ArtBarChartCard - 柱状图卡片
- ArtLineChartCard - 折线图卡片
- ArtDonutChartCard - 环形图卡片
表格组件
- ArtTable - 表格组件
- ArtTableHeader - 表格头部
媒体组件
- ArtVideoPlayer - 视频播放器
- ArtCutterImg - 图片裁剪
文本特效组件
- ArtCountTo - 数字滚动
- ArtTextScroll - 文本滚动
- ArtFestivalTextScroll - 节日文本滚动
开发指南
环境要求
- Node.js >= 20.19.0
- pnpm >= 8.8.0
安装依赖
pnpm installpnpm devpnpm build代码检查
pnpm lintpnpm fixpnpm lint:prettierpnpm lint:stylelintGit 提交规范
项目使用 Commitizen 规范化提交信息:
pnpm commit提交类型:
feat: 新功能fix: 修复 bugdocs: 文档更新style: 代码格式调整refactor: 重构代码perf: 性能优化test: 测试相关chore: 构建/工具相关revert: 回退提交
目录别名
// vite.config.ts
{
resolve: {
alias: {
'@': 'src',
'@views': 'src/views',
'@imgs': 'src/assets/images',
'@icons': 'src/assets/icons',
'@utils': 'src/utils',
'@stores': 'src/store',
'@styles': 'src/assets/styles'
}
}
}自动导入
项目配置了自动导入功能:
API 自动导入
// vite.config.ts
AutoImport({
imports: ["vue", "vue-router", "pinia", "@vueuse/core"],
dts: "src/types/import/auto-imports.d.ts",
resolvers: [ElementPlusResolver()],
});自动导入的 API:
vue: ref, reactive, computed, watch, onMounted 等vue-router: useRouter, useRoute 等pinia: defineStore, storeToRefs 等@vueuse/core: useMouse, useStorage 等
组件自动导入
// vite.config.ts
Components({
dts: "src/types/import/components.d.ts",
resolvers: [ElementPlusResolver()],
});Element Plus 组件自动按需导入,无需手动 import。
部署指南
环境变量配置
# 应用部署基础路径
VITE_BASE_URL = /
# API 请求基础路径
VITE_API_URL = /
# 是否启用 MSW Mock
VITE_ENABLE_MSW = true
# 是否删除 console
VITE_DROP_CONSOLE = false# 应用部署基础路径
VITE_BASE_URL = /
# API 地址前缀
VITE_API_URL = https://api.example.com
# 是否删除 console
VITE_DROP_CONSOLE = true构建优化
代码分割
// vite.config.ts
{
build: {
target: 'es2015',
outDir: 'dist',
chunkSizeWarningLimit: 2000,
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // 删除 console
drop_debugger: true // 删除 debugger
}
}
}
}Gzip 压缩
// vite.config.ts
viteCompression({
verbose: false, // 不输出压缩结果
disable: false, // 启用压缩
algorithm: "gzip", // 压缩算法
ext: ".gz", // 文件扩展名
threshold: 10240, // 大于 10KB 才压缩
deleteOriginFile: false, // 不删除原文件
});依赖预构建
// vite.config.ts
optimizeDeps: {
include: [
"echarts/core",
"echarts/charts",
"echarts/components",
"echarts/renderers",
"xlsx",
"xgplayer",
"crypto-js",
"file-saver",
"vue-img-cutter",
"element-plus/es",
"element-plus/es/components/*/style/css",
"element-plus/es/components/*/style/index",
];
}部署方式
server {
listen 80;
server_name example.com;
root /var/www/art-design-pro/dist;
index index.html;
# 开启 gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1000;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA 路由支持
location / {
try_files $uri $uri/ /index.html;
}
# API 代理
location /api/ {
proxy_pass http://backend-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}# 构建阶段
FROM node:20-alpine as build-stage
WORKDIR /app
# 安装 pnpm
RUN npm install -g pnpm
# 复制依赖文件
COPY package.json pnpm-lock.yaml ./
# 安装依赖
RUN pnpm install --frozen-lockfile
# 复制源代码
COPY . .
# 构建应用
RUN pnpm build
# 生产阶段
FROM nginx:alpine as production-stage
# 复制构建产物
COPY --from=build-stage /app/dist /usr/share/nginx/html
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]# 构建镜像
docker build -t art-design-pro .
# 运行容器
docker run -d -p 80:80 art-design-pro总结
CDesign Pro 是一个功能完善、架构清晰的后台管理系统脚手架。它采用了最新的前端技术栈,提供了完整的权限管理、动态路由、主题配置等功能,适合作为企业级后台管理系统的起点。
核心优势
- 现代化技术栈 - 使用 Vue 3 + TypeScript + Vite,开发体验优秀
- 完善的架构设计 - 模块化、可扩展、易维护
- 丰富的功能特性 - 权限管理、主题配置、国际化等开箱即用
- 优秀的开发体验 - 自动导入、热更新、代码规范等工具齐全
- 生产就绪 - 构建优化、部署方案完善
适用场景
- 企业级后台管理系统
- 管理后台脚手架
- 中型项目快速启动
- 学习 Vue 3 + TypeScript 最佳实践
文档版本: 1.0.0
最后更新: 2026-04-11
维护团队: CDesign Pro Team