【vue3-element-admin 】

【vue3-element-admin 】基于 Vue3 + Vite4 + TypeScript + Element-Plus 从0到1搭建后台管理系统(前后端开源@有来开源组织)
vue3-element-admin 是基于 vue-element-admin 升级的 Vue3 + Element Plus 版本的后台管理前端解决方案,技术栈为 Vue3 + Vite4 + TypeScript + Element Plus + Pinia + Vue Router 等当前主流框架。
相较于其他管理前端框架,vue3-element-admin 的优势在于一有一无 (有配套后端、无复杂封装):
配套完整 Java 后端 权限管理接口,开箱即用,提供 OpenAPI 文档 搭配 Apifox 生成 Node、Python、Go等其他服务端代码;
完全基于 vue-element-admin 升级的 Vue3 版本,没有对框架(Element Plus)的组件再封装,上手成本低和扩展性高。
前言
本篇是 vue3-element-admin v2.x 版本从 0 到 1,相较于 v1.x 版本 主要增加了对原子CSS(UnoCSS)、按需自动导入、暗黑模式的支持。
阅读前的两条声明:
博客有时效性,源代码会一直更新,本篇源码
tag版本 vue3-element-admin v2.2.0 ;各章节会有先后顺序依赖关系,例如:安装 Element Plus 需要先安装自动导入等,建议按照顺序完成0到1,当然也可各取所需。
项目预览
在线预览
首页控制台
![]() |
|---|
![]() |
接口文档

权限管理系统
![]() | ![]() |
|---|---|
![]() | ![]() |
扩展生态
youlai-mall 有来开源商城:Spring Cloud微服务+ vue3-element-admin+uni-app
| youlai-mall 商品管理 | mall-app 移动端 |
|---|---|
![]() | ![]() |
项目指南
功能清单

技术栈&官网
| 技术栈 | 描述 | 官网 |
|---|---|---|
| Vue3 | 渐进式 JavaScript 框架 | https://cn.vuejs.org/ |
| Element Plus | 基于 Vue 3,面向设计师和开发者的组件库 | https://element-plus.gitee.io/zh-CN/ |
| Vite | 前端开发与构建工具 | https://cn.vitejs.dev/ |
| TypeScript | 微软新推出的一种语言,是 JavaScript 的超集 | https://www.tslang.cn/ |
| Pinia | 新一代状态管理工具 | https://pinia.vuejs.org/ |
| Vue Router | Vue.js 的官方路由 | https://router.vuejs.org/zh/ |
| wangEditor | Typescript 开发的 Web 富文本编辑器 | https://www.wangeditor.com/ |
| Echarts | 一个基于 JavaScript 的开源可视化图表库 | https://echarts.apache.org/zh/ |
| vue-i18n | Vue 国际化多语言插件 | https://vue-i18n.intlify.dev/ |
| VueUse | 基于Vue组合式API的实用工具集(类比HuTool工具) | http://www.vueusejs.com/ |
前/后端源码
| Gitee | Github | |
|---|---|---|
| 前端 | vue3-element-admin | vue3-element-admin |
| 后端 | youlai-boot | youlai-boot |
接口文档
接口调用地址:https://vapi.youlai.tech
接口文档地址:在线接口文档
OpenAPI 3.0 文档地址:http://vapi.youlai.tech/v3/api-docs
环境准备
| 名称 | 备注 | |
|---|---|---|
| 开发工具 | VSCode 下载 | - |
| 运行环境 | Node 16+ 下载 | ![]() |
| VSCode插件(必装) | 插件市场搜索 Vue Language Features (Volar) 和 TypeScript Vue Plugin (Volar) 安装,且禁用 Vetur | ![]() |
项目初始化
按照 【emoji】Vite 官方文档 - 搭建第一个 Vite 项目 说明,执行以下命令完成 vue 、typescirpt 模板项目的初始化
npm init vite@latest vue3-element-admin --template vue-ts
vue3-element-admin: 自定义的项目名称vue-ts:vue+typescript模板的标识,查看 create-vite 以获取每个模板的更多细节:vue,vue-ts,react,react-ts

初始化完成项目位于 D:\project\demo\vue3-element-admin , 使用 VSCode 导入,执行以下命令启动:
npm install npm run dev

浏览器访问 localhost:5173 预览

路径别名配置
相对路径别名配置,使用 @ 代替 src
Vite 配置

TypeScirpt 编译器配置
// tsconfig.json "compilerOptions": { ... "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录 "paths": { // 路径映射,相对于baseUrl "@/*": ["src/*"] } }路径别名使用
// src/App.vue import HelloWorld from '/src/components/HelloWorld.vue' ↓ import HelloWorld from '@/components/HelloWorld.vue'

安装自动导入
Element Plus 官方文档中推荐
按需自动导入的方式,而此需要使用额外的插件unplugin-auto-import和unplugin-vue-components来导入要使用的组件。所以在整合Element Plus之前先了解下自动导入的概念和作用
概念
为了避免在多个页面重复引入 API 或 组件,由此而产生的自动导入插件来节省重复代码和提高开发效率。
| 插件 | 概念 | 自动导入对象 |
|---|---|---|
| unplugin-auto-import | 按需自动导入API | ref,reactive,watch,computed 等API |
| unplugin-vue-components | 按需自动导入组件 | Element Plus 等三方库和指定目录下的自定义组件 |
看下自动导入插件未使用和使用的区别:
| 插件名 | 未使用自动导入 | 使用自动导入 |
|---|---|---|
| unplugin-auto-import | ![]() | ![]() |
| unplugin-vue-components | ![]() | ![]() |
安装插件依赖
npm install -D unplugin-auto-import unplugin-vue-components
vite.config.ts - 自动导入配置
新建 /src/types 目录用于存放自动导入函数和组件的TS类型声明文件
import AutoImport from "unplugin-auto-import/vite"; import Components from "unplugin-vue-components/vite"; plugins: [ AutoImport({ // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等 imports: ["vue"], eslintrc: { enabled: true, // 是否自动生成 eslint 规则,建议生成之后设置 false filepath: "./.eslintrc-auto-import.json", // 指定自动导入函数 eslint 规则的文件 }, dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 指定自动导入函数TS类型声明文件路径 }), Components({ dts: path.resolve(pathSrc, "types", "components.d.ts"), // 指定自动导入组件TS类型声明文件路径 }), ].eslintrc.cjs - 自动导入函数 eslint 规则引入
"extends": [ "./.eslintrc-auto-import.json" ],
tsconfig.json - 自动导入TS类型声明文件引入
{ "include": ["src/**/*.d.ts"] }自动导入效果
运行项目 npm run dev 自动

整合 Element Plus
需要完成上面一节的 自动导入 的安装和配置
安装 Element Plus
npm install element-plus
安装自动导入 Icon 依赖
npm i -D unplugin-icons
vite.config.ts 配置
参考: element-plus-best-practices - vite.config.ts
// vite.config.ts import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; import Icons from "unplugin-icons/vite"; import IconsResolver from "unplugin-icons/resolve export default ({ mode }: ConfigEnv): UserConfig => { return { plugins: [ // ... AutoImport({ // ... resolvers: [ // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式) ElementPlusResolver(), // 自动导入图标组件 IconsResolver({}), ] vueTemplate: true, // 是否在 vue 模板中自动导入 dts: path.resolve(pathSrc, 'types', 'auto-imports.d.ts') // 自动导入组件类型声明文件位置,默认根目录 }), Components({ resolvers: [ // 自动导入 Element Plus 组件 ElementPlusResolver(), // 自动注册图标组件 IconsResolver({ enabledCollections: ["ep"] // element-plus图标库,其他图标库 https://icon-sets.iconify.design/ }), ], dts: path.resolve(pathSrc, "types", "components.d.ts"), // 自动导入组件类型声明文件位置,默认根目录 }), Icons({ // 自动安装图标库 autoInstall: true, }), ], }; };示例代码
<!-- src/components/HelloWorld.vue --> <div> <el-button type="success"><i-ep-SuccessFilled />Success</el-button> <el-button type="info"><i-ep-InfoFilled />Info</el-button> <el-button type="warning"><i-ep-WarningFilled />Warning</el-button> <el-button type="danger"><i-ep-WarnTriangleFilled />Danger</el-button> </div>
效果预览

整合 SVG 图标
通过 vite-plugin-svg-icons 插件整合
Iconfont第三方图标库实现本地图标
参考: vite-plugin-svg-icons 安装文档
安装依赖
npm install -D fast-glob@3.2.11 npm install -D vite-plugin-svg-icons@2.0.1
创建 src/assets/icons 目录 , 放入从 Iconfont 复制的 svg 图标

main.ts 引入注册脚本
// src/main.ts import 'virtual:svg-icons-register';
vite.config.ts 配置插件
// vite.config.ts import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; export default ({command, mode}: ConfigEnv): UserConfig => { return ( { plugins: [ createSvgIconsPlugin({ // 指定需要缓存的图标文件夹 iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], // 指定symbolId格式 symbolId: 'icon-[dir]-[name]', }) ] } ) }SVG 组件封装
<!-- src/components/SvgIcon/index.vue --> <script setup lang="ts"> const props = defineProps({ prefix: { type: String, default: "icon", }, iconClass: { type: String, required: false, }, color: { type: String, }, size: { type: String, default: "1em", }, }); const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`); </script> <template> <svg aria-hidden="true" class="svg-icon" :style="'width:' + size + ';height:' + size" > <use :xlink:href="symbolId" :fill="color" /> </svg> </template> <style scoped> .svg-icon { display: inline-block; outline: none; width: 1em; height: 1em; vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */ fill: currentColor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */ overflow: hidden; } </style>组件使用
<!-- src/components/HelloWorld.vue --> <template> <el-button type="info"><svg-icon icon-class="block"/>SVG 本地图标</el-button> </template>

整合 SCSS
一款CSS预处理语言,SCSS 是 Sass 3 引入新的语法,其语法完全兼容 CSS3,并且继承了 Sass 的强大功能。
安装依赖
npm i -D sass
创建 variables.scss 变量文件,添加变量 $bg-color 定义,注意规范变量以 $ 开头
// src/styles/variables.scss $bg-color:#242424;
Vite 配置导入 SCSS 全局变量文件
// vite.config.ts css: { // CSS 预处理器 preprocessorOptions: { //define global scss variable scss: { javascriptEnabled: true, additionalData: `@use "@/styles/variables.scss" as *;` } } }style 标签使用SCSS全局变量
<!-- src/components/HelloWorld.vue --> <template> <div class="box" /> </template> <style lang="scss" scoped> .box { width: 100px; height: 100px; background-color: $bg-color; } </style>上面导入的 SCSS 全局变量在 TypeScript 不生效的,需要创建一个以 .module.scss 结尾的文件
// src/styles/variables.module.scss // 导出 variables.scss 文件的变量 :export{ bgColor:$bg-color }TypeScript 使用 SCSS 全局变量
<!-- src/components/HelloWorld.vue --> <script setup> import variables from "@/styles/variables.module.scss"; console.log(variables.bgColor) </script> <template> <div style="width:100px;height:100px" :style="{ 'background-color': variables.bgColor }" /> </template>整合 UnoCSS
UnoCSS 是一个具有高性能且极具灵活性的即时原子化 CSS 引擎 。
安装依赖
npm install -D unocss
vite.config.ts 配置
// vite.config.ts import UnoCSS from 'unocss/vite' export default { plugins: [ UnoCSS({ /* options */ }), ], }main.ts 引入 uno.css
// src/main.ts import 'uno.css'
VSCode 安装 UnoCSS 插件

再看下具体使用方式和实际效果:
| 代码 | 效果 |
|---|---|
![]() | ![]() |
如果UnoCSS 插件智能提示不生效,请参考:VSCode插件UnoCSS智能提示不生效解决 。
整合 Pinia
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
参考:Pinia 官方文档
安装依赖
npm install pinia
main.ts 引入 pinia
// src/main.ts import { createPinia } from "pinia"; import App from "./App.vue"; createApp(App).use(createPinia()).mount("#app");定义 Store
根据 Pinia 官方文档-核心概念 描述 ,Store 定义分为选项式和组合式 , 先比较下两种写法的区别:
| 选项式 Option Store | 组合式 Setup Store |
|---|---|
![]() | ![]() |
至于如何选择,官方给出的建议 :选择你觉得最舒服的那一个就好 。
这里选择组合式,新建文件 src/store/counter.ts
// src/store/counter.ts import { defineStore } from "pinia"; export const useCounterStore = defineStore("counter", () => { // ref变量 → state 属性 const count = ref(0); // computed计算属性 → getters const double = computed(() => { return count.value * 2; }); // function函数 → actions function increment() { count.value++; } return { count, double, increment }; });父组件
<!-- src/App.vue --> <script setup> import HelloWorld from "@/components/HelloWorld.vue"; import { useCounterStore } from "@/store/counter"; const counterStore = useCounterStore(); </script> <template> <h1>vue3-element-admin-父组件</h1> <el-button type="primary" @click="counterStore.increment">count++</el-button> <HelloWorld /> </template>子组件
<!-- src/components/HelloWorld.vue --> <script setup> import { useCounterStore } from "@/store/counter"; const counterStore = useCounterStore(); </script> <template> <el-card class="text-left text-white border-white border-1 border-solid mt-10 bg-[#242424]" > <template #header> 子组件 HelloWorld.vue</template> <el-form> <el-form-item label="数字:"> {{ counterStore.count }}</el-form-item> <el-form-item label="加倍:"> {{ counterStore.double }}</el-form-item> </el-form> </el-card> </template>效果预览

环境变量
Vite 环境变量主要是为了区分开发、测试、生产等环境的变量
参考: Vite 环境变量配置官方文档
env配置文件
项目根目录新建 .env.development 、.env.production
开发环境变量配置:.env.development
# 变量必须以 VITE_ 为前缀才能暴露给外部读取 VITE_APP_TITLE = 'vue3-element-admin' VITE_APP_PORT = 3000 VITE_APP_BASE_API = '/dev-api'
生产环境变量配置:.env.production
VITE_APP_TITLE = 'vue3-element-admin' VITE_APP_PORT = 3000 VITE_APP_BASE_API = '/prod-api'
环境变量智能提示
新建 src/types/env.d.ts文件存放环境变量TS类型声明
// src/types/env.d.ts interface ImportMetaEnv { /** * 应用标题 */ VITE_APP_TITLE: string; /** * 应用端口 */ VITE_APP_PORT: number; /** * API基础路径(反向代理) */ VITE_APP_BASE_API: string; } interface ImportMeta { readonly env: ImportMetaEnv; }使用自定义环境变量就会有智能提示,环境变量的读取和使用请看下一节的跨域处理中的 vite.config.ts的配置。

跨域处理
跨域原理
浏览器同源策略: 协议、域名和端口都相同是同源,浏览器会限制非同源请求读取响应结果。
本地开发环境通过 Vite 配置反向代理解决浏览器跨域问题,生产环境则是通过 nginx 配置反向代理 。
vite.config.ts 配置代理

表面肉眼看到的请求地址: http://localhost:3000/dev-api/api/v1/users/me
真实访问的代理目标地址: http://vapi.youlai.tech/api/v1/users/me

整合 Axios
Axios 基于promise可以用于浏览器和node.js的网络请求库
参考: Axios 官方文档
安装依赖
npm install axios
Axios 工具类封装
// src/utils/request.ts import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; import { useUserStoreHook } from '@/store/modules/user'; // 创建 axios 实例 const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, timeout: 50000, headers: { 'Content-Type': 'application/json;charset=utf-8' } }); // 请求拦截器 service.interceptors.request.use( (config: InternalAxiosRequestConfig) => { const userStore = useUserStoreHook(); if (userStore.token) { config.headers.Authorization = userStore.token; } return config; }, (error: any) => { return Promise.reject(error); } ); // 响应拦截器 service.interceptors.response.use( (response: AxiosResponse) => { const { code, msg } = response.data; // 登录成功 if (code === '00000') { return response.data; } ElMessage.error(msg || '系统出错'); return Promise.reject(new Error(msg || 'Error')); }, (error: any) => { if (error.response.data) { const { code, msg } = error.response.data; // token 过期,跳转登录页 if (code === 'A0230') { ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', { confirmButtonText: '确定', 【vue3-element-admin 】
声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。





















