# vue3-admin-template **Repository Path**: template-warehouse/vue3-admin-template ## Basic Information - **Project Name**: vue3-admin-template - **Description**: Vue3+Pinia+TS+Axios+Yarn+Mock+Element-Plus 管理系统模板(Vite版) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-07 - **Last Updated**: 2025-12-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Vue3+Pinia+TS+Axios+Yarn+Mock+Element-Plus 管理系统模板(Vite版) 本文基于 **Vite** 脚手架搭建一套可直接用于工作的后台管理系统基础模板,包含完整的配置、核心功能实现、代码规范和Git提交规范,所有步骤均附带详细备注,便于后期扩展修改。 # 一、环境准备 ## 1. 必备工具(版本要求) - Node.js:`v16.0.0+`(Vite 官方最低要求) - Yarn:`v1.22.0+`(替代npm,包管理更高效) - Git:`v2.0.0+`(版本控制) - VSCode:推荐插件(Volar、ESLint、Prettier、TypeScript Vue Plugin (Volar)) ## 2. 环境检查/安装 ```bash # 检查Node版本 node -v # 检查npm版本 npm -v # 全局安装Yarn(若未安装) npm install -g yarn # 检查Yarn版本 yarn -v # 检查Vite版本(后续创建项目会自动安装,也可全局装) yarn global add vite && vite -v ``` # 二、项目初始化(Vite创建项目) ## 1. 创建Vite+Vue3+TS项目 ```bash # 创建项目(项目名:vue3-admin-template) yarn create vite vue3-admin-template --template vue-ts # 进入项目目录 cd vue3-admin-template # 安装基础依赖(Vite默认依赖) yarn install ``` ## 2. 安装核心依赖 |依赖分类|依赖名称|用途说明| |---|---|---| |核心UI|element-plus|Vue3 适配的Element UI组件库| |自动导入|unplugin-vue-components + unplugin-auto-import|自动导入Vue/Element Plus API和组件(无需手动import)| |状态管理|pinia|Vue3 官方推荐状态管理(替代Vuex)| |路由|vue-router@4|Vue3 路由管理| |网络请求|axios|封装HTTP请求| |Cookie操作|js-cookie + @types/js-cookie|Cookie操作及TS类型声明| |Mock数据|vite-plugin-mock + mockjs|Vite 专用Mock插件,模拟后端接口| |代码规范|eslint + eslint-plugin-vue + @typescript-eslint/* + prettier + eslint-config-prettier + eslint-plugin-prettier|ESLint代码检查、Prettier格式化、解决二者冲突| |Git规范|husky + lint-staged + @commitlint/cli + @commitlint/config-conventional|Git钩子(提交前检查)、提交信息规范校验| 安装命令(分批次,避免依赖冲突): ```bash # 1. 核心业务依赖 yarn add element-plus pinia vue-router@4 axios js-cookie # 2. TS类型依赖(开发环境) yarn add -D @types/js-cookie @types/mockjs # 3. Vite插件(自动导入/Mock) yarn add -D unplugin-vue-components unplugin-auto-import vite-plugin-mock mockjs # 4. 代码规范依赖(开发环境) yarn add -D eslint eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier eslint-config-prettier eslint-plugin-prettier # 5. Git规范依赖(开发环境) yarn add -D husky lint-staged @commitlint/cli @commitlint/config-conventional ``` # 三、Vite核心配置(vite.config.ts) 修改项目根目录的 `vite.config.ts`,整合别名、自动导入、Mock、服务器等配置: ```typescript import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path' // 自动导入Vue/Element Plus API import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // Vite Mock插件 import { viteMockServe } from 'vite-plugin-mock' // https://vitejs.dev/config/ export default defineConfig({ // 插件配置 plugins: [ vue(), // 自动导入配置 AutoImport({ resolvers: [ElementPlusResolver()], // 自动导入Vue核心API、Pinia、VueRouter imports: ['vue', 'pinia', 'vue-router'], // 生成的声明文件路径(解决TS类型提示) dts: resolve(__dirname, 'src/auto-imports.d.ts'), // 忽略eslint报错(自动导入的变量未声明) eslintrc: { enabled: true // 生成.eslintrc-auto-import.json } }), // 自动导入组件(Element Plus无需手动import) Components({ resolvers: [ElementPlusResolver()], // 生成组件声明文件 dts: resolve(__dirname, 'src/components.d.ts') }), // Mock配置 viteMockServe({ // Mock文件根目录 mockPath: 'mock', // 开发环境启用Mock localEnabled: true, // 生产环境禁用Mock(可根据需求调整) prodEnabled: false, // 自动注入Mock请求(无需手动引入) injectCode: ` import { setupProdMockServer } from './mockProdServer'; setupProdMockServer(); ` }) ], // 路径别名(简化导入路径) resolve: { alias: { '@': resolve(__dirname, 'src'), // @指向src目录 '@assets': resolve(__dirname, 'src/assets'), '@components': resolve(__dirname, 'src/components'), '@views': resolve(__dirname, 'src/views') } }, // 开发服务器配置 server: { port: 3000, // 启动端口 open: true, // 启动后自动打开浏览器 cors: true, // 允许跨域 // 代理配置(后期对接真实后端用) proxy: { '/api': { target: 'http://localhost:8080', // 后端接口地址 changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } }, // 构建配置 build: { outDir: 'dist', // 打包输出目录 chunkSizeWarningLimit: 1500, // 解决打包体积过大警告 minify: 'terser', // 压缩方式 terserOptions: { // 打包时移除console和debugger compress: { drop_console: true, drop_debugger: true } } } }) ``` # 四、TS配置(tsconfig.json) 适配Vite别名和自动导入,修改 `tsconfig.json`: ```json { "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* 路径别名配置 */ "baseUrl": ".", "paths": { "@/*": ["src/*"], "@assets/*": ["src/assets/*"], "@components/*": ["src/components/*"], "@views/*": ["src/views/*"] }, /* 模块解析配置 */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", /* 严格类型检查 */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": [ "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts", // 自动导入声明文件 "components.d.ts" // 组件自动导入声明文件 ], "references": [{ "path": "./tsconfig.node.json" }] } ``` # 五、代码规范配置 ## 1. ESLint配置(.eslintrc.cjs) 项目根目录创建 `.eslintrc.cjs`(Vite项目默认是ES模块,ESLint需用CommonJS): ```javascript module.exports = { root: true, env: { browser: true, es2021: true, node: true, 'vue/setup-compiler-macros': true // 支持Vue3 setup语法糖 }, extends: [ 'plugin:vue/vue3-essential', // Vue3基础规则 'eslint:recommended', // ESLint推荐规则 '@vue/typescript/recommended', // TS推荐规则 'plugin:prettier/recommended', // 整合Prettier(最后扩展,覆盖冲突规则) './.eslintrc-auto-import.json' // 自动导入的ESLint声明(AutoImport生成) ], parser: 'vue-eslint-parser', // Vue解析器 parserOptions: { ecmaVersion: 'latest', parser: '@typescript-eslint/parser', // TS解析器 sourceType: 'module' }, plugins: ['vue', '@typescript-eslint'], rules: { // 自定义规则(根据团队需求调整) 'vue/multi-word-component-names': 'off', // 关闭组件名必须多单词(方便测试) '@typescript-eslint/no-explicit-any': 'warn', // any类型警告(不强制禁止) 'vue/no-unused-vars': 'error', // 未使用变量报错 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn', // 生产环境禁止console 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn' // 生产环境禁止debugger } } ``` ## 2. ESLint忽略文件(.eslintignore) ```plaintext /node_modules/ /dist/ /auto-imports.d.ts /components.d.ts /mock/ /public/ ``` ## 3. Prettier配置(.prettierrc) 项目根目录创建 `.prettierrc`: ```json { "semi": false, // 语句末尾不加分号 "singleQuote": true, // 使用单引号 "tabWidth": 2, // 缩进2个空格 "trailingComma": "es5", // 尾逗号(ES5规范) "printWidth": 100, // 每行最大字符数 "bracketSpacing": true, // 对象字面量括号间加空格 "arrowParens": "avoid" // 箭头函数参数单个时省略括号 } ``` ## 4. Prettier忽略文件(.prettierignore) ```plaintext /node_modules/ /dist/ /package.json /.eslintrc.cjs ``` # 六、Git提交规范配置 ## 1. 初始化Husky ```bash # 初始化husky(生成.husky目录) npx husky install # 设置开机自动启用husky(Windows/mac通用) npm set-script prepare "husky install" yarn prepare ``` ## 2. 添加commitlint校验规则 项目根目录创建 `commitlint.config.cjs`: ```javascript module.exports = { extends: ['@commitlint/config-conventional'], // 自定义提交信息规则 rules: { 'type-enum': [ 2, 'always', [ 'feat', // 新功能 'fix', // 修复bug 'docs', // 文档修改 'style', // 代码格式(不影响逻辑) 'refactor', // 重构(既不是feat也不是fix) 'perf', // 性能优化 'test', // 测试相关 'build', // 构建/打包相关 'ci', // CI配置 'chore', // 其他杂项 'revert' // 回滚代码 ] ], 'type-case': [0], // 类型大小写不限制 'type-empty': [2, 'never'], // 类型不能为空 'subject-empty': [2, 'never'], // 描述不能为空 'subject-full-stop': [0, 'never'], // 描述末尾不加句号 'header-max-length': [2, 'always', 72] // 提交信息头部最大长度72 } } ``` ## 3. 添加Git钩子 ```bash # 1. 添加commit-msg钩子(校验提交信息) npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1' # 2. 添加pre-commit钩子(提交前执行lint检查) npx husky add .husky/pre-commit 'npx lint-staged' ``` ## 4. 配置lint-staged(只检查暂存文件) 项目根目录的 `package.json` 中添加: ```json "lint-staged": { "src/**/*.{ts,vue,js}": [ "eslint --fix", "prettier --write" ] } ``` # 七、项目核心目录结构 删除Vite默认的 `src/components/HelloWorld.vue`,创建标准化目录: ```plaintext vue3-admin-template/ ├── .husky/ # Git钩子配置 ├── mock/ # Mock数据目录 │ ├── index.ts # Mock服务入口 │ └── user.ts # 用户相关Mock接口 ├── src/ │ ├── api/ # 接口请求封装 │ │ ├── index.ts # axios实例封装 │ │ └── user.ts # 用户相关接口 │ ├── assets/ # 静态资源(样式/图片) │ │ └── style/ │ │ └── reset.css # 全局样式重置 │ ├── components/ # 公共组件 │ │ └── Layout/ # 布局组件 │ │ ├── index.vue # 核心布局(侧边栏+头部+内容区) │ │ ├── Sidebar.vue # 侧边栏 │ │ └── Header.vue # 头部 │ ├── pinia/ # Pinia状态管理 │ │ ├── index.ts # Pinia实例创建 │ │ └── modules/ │ │ └── user.ts # 用户状态模块 │ ├── router/ # 路由配置 │ │ ├── index.ts # 路由入口 │ │ └── routes.ts # 路由规则 │ ├── types/ # TS类型声明 │ │ └── index.ts # 全局类型 │ ├── utils/ # 工具函数 │ │ ├── cookie.ts # Cookie操作封装 │ │ └── request.ts # axios请求封装(可选,抽离更细) │ ├── views/ # 页面视图 │ │ ├── Login/ # 登录页 │ │ │ └── index.vue │ │ └── Dashboard/ # 首页 │ │ └── index.vue │ ├── App.vue # 根组件 │ ├── main.ts # 入口文件 │ ├── auto-imports.d.ts # 自动导入声明(AutoImport生成) │ └── components.d.ts # 组件自动导入声明(Components生成) ├── .eslintrc.cjs # ESLint配置 ├── .eslintignore # ESLint忽略 ├── .prettierrc # Prettier配置 ├── .prettierignore # Prettier忽略 ├── commitlint.config.cjs # Commitlint配置 ├── package.json # 项目配置 ├── tsconfig.json # TS配置 └── vite.config.ts # Vite配置 ``` # 八、核心功能实现 ## 1. 入口文件(src/main.ts) ```typescript import { createApp } from 'vue' import { createPinia } from 'pinia' import router from '@/router' import App from './App.vue' // 全局样式重置(可选) import '@assets/style/reset.css' // 创建Vue实例 const app = createApp(App) // 注册Pinia app.use(createPinia()) // 注册路由 app.use(router) // 挂载到DOM app.mount('#app') ``` ## 2. 全局样式重置(src/assets/style/reset.css) ```css /* 重置默认样式 */ * { margin: 0; padding: 0; box-sizing: border-box; } html, body, #app { height: 100%; font-size: 14px; font-family: "Microsoft Yahei", "PingFang SC", sans-serif; } a { text-decoration: none; color: inherit; } ul, li { list-style: none; } /* Element Plus 全局样式适配 */ :root { --el-color-primary: #1890ff; /* 主题色可自定义 */ } ``` ## 3. Axios封装(src/api/index.ts) ```typescript import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' import { ElMessage } from 'element-plus' import { getToken } from '@/utils/cookie' // 创建axios实例 const service = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 环境变量(后期可配置) timeout: 10000, // 请求超时 headers: { 'Content-Type': 'application/json;charset=utf-8' } }) // 请求拦截器 service.interceptors.request.use( (config: AxiosRequestConfig) => { // 添加token到请求头 const token = getToken() if (token && config.headers) { config.headers.Authorization = `Bearer ${token}` } return config }, (error: AxiosError) => { ElMessage.error('请求异常,请稍后重试') return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( (response: AxiosResponse) => { const { code, msg, data } = response.data // 自定义业务码(示例:200为成功) if (code === 200) { return data } else { ElMessage.error(msg || '请求失败') return Promise.reject(new Error(msg || '请求失败')) } }, (error: AxiosError) => { // 处理HTTP状态码 const status = error.response?.status switch (status) { case 401: ElMessage.error('登录失效,请重新登录') // 跳转到登录页(需结合路由) window.location.href = '/login' break case 403: ElMessage.error('暂无权限访问') break case 404: ElMessage.error('接口不存在') break case 500: ElMessage.error('服务器内部错误') break default: ElMessage.error('请求异常,请稍后重试') } return Promise.reject(error) } ) // 封装请求方法(可选,简化调用) export const request = { get(url: string, params?: object): Promise { return service.get(url, { params }) }, post(url: string, data?: object): Promise { return service.post(url, data) }, put(url: string, data?: object): Promise { return service.put(url, data) }, delete(url: string, params?: object): Promise { return service.delete(url, { params }) } } export default service ``` ## 4. Cookie工具封装(src/utils/cookie.ts) ```typescript import Cookies from 'js-cookie' // Token键名 const TOKEN_KEY = 'admin_token' /** * 获取Token */ export const getToken = (): string | undefined => { return Cookies.get(TOKEN_KEY) } /** * 设置Token * @param token Token值 * @param expires 过期时间(天) */ export const setToken = (token: string, expires = 7): void => { Cookies.set(TOKEN_KEY, token, { expires }) } /** * 移除Token */ export const removeToken = (): void => { Cookies.remove(TOKEN_KEY) } ``` ## 5. Pinia状态管理(用户模块) ### 5.1 Pinia实例入口(src/pinia/index.ts) ```typescript import { createPinia } from 'pinia' const pinia = createPinia() export default pinia ``` ### 5.2 用户状态模块(src/pinia/modules/user.ts) ```typescript import { defineStore } from 'pinia' import { loginApi } from '@/api/user' import { setToken, getToken, removeToken } from '@/utils/cookie' import { UserInfo, LoginParams } from '@/types' // 定义用户Store export const useUserStore = defineStore('user', { state: () => ({ token: getToken() || '', // Token(优先从Cookie读取) userInfo: {} as UserInfo // 用户信息 }), getters: { // 是否登录 isLogin: (state) => !!state.token }, actions: { /** * 登录 * @param params 登录参数 */ async login(params: LoginParams) { const res = await loginApi(params) const { token } = res // 存储Token this.token = token setToken(token) // 可后续补充获取用户信息逻辑 // await this.getUserInfo() }, /** * 退出登录 */ logout() { this.token = '' this.userInfo = {} as UserInfo removeToken() // 跳转到登录页 window.location.href = '/login' }, /** * 获取用户信息(示例) */ async getUserInfo() { // 后续对接真实接口补充 // const res = await getUserInfoApi() // this.userInfo = res } } }) ``` ## 6. TS类型声明(src/types/index.ts) ```typescript // 登录参数类型 export interface LoginParams { username: string password: string } // 用户信息类型 export interface UserInfo { id?: number username?: string avatar?: string roles?: string[] } // 接口响应通用类型 export interface ApiResponse { code: number msg: string data: T } ``` ## 7. 用户接口封装(src/api/user.ts) ```typescript import { request } from './index' import { LoginParams, ApiResponse, UserInfo } from '@/types' /** * 登录接口 * @param params 登录参数 */ export const loginApi = (params: LoginParams): Promise> => { return request.post('/user/login', params) } /** * 获取用户信息接口(示例) */ export const getUserInfoApi = (): Promise> => { return request.get('/user/info') } ``` ## 8. Mock数据配置 ### 8.1 Mock服务入口(mock/index.ts) ```typescript import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer' import userMock from './user' // 生产环境Mock服务(vite.config中prodEnabled为true时生效) export function setupProdMockServer() { createProdMockServer([...userMock]) } ``` ### 8.2 用户Mock接口(mock/user.ts) ```typescript import { MockMethod } from 'vite-plugin-mock' const userMock: MockMethod[] = [ // 登录接口 { url: '/api/user/login', method: 'post', response: ({ body }) => { const { username, password } = body // 模拟账号密码校验(admin/123456) if (username === 'admin' && password === '123456') { return { code: 200, msg: '登录成功', data: { token: 'admin_token_123456789' } } } else { return { code: 400, msg: '账号或密码错误', data: null } } } }, // 用户信息接口 { url: '/api/user/info', method: 'get', response: () => { return { code: 200, msg: '获取成功', data: { id: 1, username: 'admin', avatar: 'https://avatars.githubusercontent.com/u/10624465?v=4', roles: ['admin'] } } } } ] export default userMock ``` ## 9. 路由配置 ### 9.1 路由规则(src/router/routes.ts) ```typescript import { RouteRecordRaw } from 'vue-router' import Layout from '@/components/Layout/index.vue' // 静态路由 export const constantRoutes: RouteRecordRaw[] = [ { path: '/login', name: 'Login', component: () => import('@/views/Login/index.vue'), meta: { title: '登录', hidden: true // 侧边栏隐藏 } }, { path: '/', name: 'Layout', component: Layout, redirect: '/dashboard', children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/Dashboard/index.vue'), meta: { title: '首页', icon: 'el-icon-s-home' // Element Plus图标 } } ] }, // 404页面(需放在最后) { path: '/:pathMatch(.*)*', name: 'NotFound', component: () => import('@/views/NotFound/index.vue'), meta: { title: '404', hidden: true } } ] // 动态路由(后期权限控制用) export const asyncRoutes: RouteRecordRaw[] = [] ``` ### 9.2 路由入口(src/router/index.ts) ```typescript import { createRouter, createWebHistory, Router } from 'vue-router' import { constantRoutes } from './routes' import { useUserStore } from '@/pinia/modules/user' // 创建路由实例 const router: Router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: constantRoutes, scrollBehavior: () => ({ top: 0 }) // 路由切换时回到顶部 }) // 路由守卫(登录校验) router.beforeEach((to, from, next) => { const userStore = useUserStore() const token = userStore.token // 未登录且访问非登录页,跳转到登录页 if (!token && to.name !== 'Login') { next({ name: 'Login' }) } else if (token && to.name === 'Login') { // 已登录且访问登录页,跳转到首页 next({ name: 'Dashboard' }) } else { next() } }) export default router ``` ## 10. 布局组件(src/components/Layout/index.vue) ```html ``` ### 侧边栏组件(src/components/Layout/Sidebar.vue) ```html ``` ### 头部组件(src/components/Layout/Header.vue) ```html ``` ## 11. 登录页面(src/views/Login/index.vue) ```html ``` ## 12. 首页(src/views/Dashboard/index.vue) ```html ``` # 九、package.json脚本配置 修改 `package.json` 的 `scripts` 部分: ```json "scripts": { "dev": "vite", // 开发环境启动 "build": "vue-tsc && vite build", // 打包(先TS校验,再构建) "preview": "vite preview", // 预览打包后的文件 "lint": "eslint src/**/*.{ts,vue,js} --fix", // ESLint检查并修复 "format": "prettier --write src/**/*.{ts,vue,js,css,md}", // Prettier格式化 "prepare": "husky install" // Husky自动启用 } ``` # 十、启动测试 ```bash # 安装所有依赖(确保前面步骤无遗漏) yarn install # 启动开发服务器 yarn dev ``` 浏览器会自动打开 `http://localhost:3000`,输入账号 `admin`、密码 `123456` 即可登录,进入首页。 # 十一、后期扩展说明 ## 1. 环境变量配置 创建 `.env.development` 和 `.env.production` 文件,配置不同环境的接口地址: ```js # .env.development VITE_API_BASE_URL = '/api' # .env.production VITE_API_BASE_URL = 'https://xxx.com/api' ``` ## 2. 权限控制扩展 - 在 `src/router/routes.ts` 中扩展动态路由 - 在路由守卫中根据用户角色加载不同路由 - 在Pinia中存储用户角色,控制侧边栏显示 ## 3. 更多功能扩展 - 封装表格/分页/搜索等通用组件 - 添加国际化(vue-i18n) - 集成图表(ECharts) - 实现文件上传/下载 - 完善错误日志收集 ## 4. 代码规范调整 - 根据团队需求修改 `.eslintrc.cjs` 和 `.prettierrc` 中的规则 - 扩展commitlint的类型或规则 # 十二、Git提交示例 ```bash # 正确的提交格式 git commit -m "feat: 新增登录页面" git commit -m "fix: 修复登录表单校验bug" git commit -m "docs: 更新README.md说明" ``` # 总结 本模板基于Vite搭建,包含了后台管理系统的核心基础功能:登录、路由守卫、状态管理、接口封装、Mock数据、布局组件,同时配置了标准化的ESLint、Prettier和Git提交规范,可直接作为工作模板使用。后期只需基于此模板扩展业务页面、接口和权限逻辑即可。 > (注:文档部分内容可能由 AI 生成)