# vue3-element-admin **Repository Path**: jasonjiangsir/vue3-element-admin ## Basic Information - **Project Name**: vue3-element-admin - **Description**: Vue3 + TypeScript + Vite2 + Element-Plus 脚手架,基于 vue-element-admin 改造升级,有来商城 Vue3 版本项目模板。 - **Primary Language**: TypeScript - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://www.youlai.tech - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2100 - **Created**: 2022-02-23 - **Last Updated**: 2022-02-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: forked ## README ## 技术栈官网 - vue3: https://v3.cn.vuejs.org/guide/introduction.html - vite2: https://cn.vitejs.dev/guide/ - element-plus: https://element-plus.gitee.io/zh-CN/component/button.html - vue-router4 : https://next.router.vuejs.org/zh/introduction.html - ~~vuex4:https://next.vuex.vuejs.org/zh/index.html~~ (pinia替代) - pinia: https://pinia.vuejs.org ## Vite项目构建 Vite是一种新型前端构建工具,能够显著提升前端开发体验。 [Vite 官方中文文档](https://cn.vitejs.dev/ ) 项目构建命令: ```shell npm init vite@latest vue3-element-admin --template vue-ts cd vue3-element-admin npm install npm run dev ``` 访问本地: http://localhost:3000 ## vue-router ```text npm install vue-router@next ``` src 下创建 router/interface.ts ```typescript import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router' import HelloWord from '../components/HelloWorld.vue' const routes: Array = [ { path: '', redirect: (_) => { return {path: '/home'} } }, { path: '/home', name: 'HelloWord', component: HelloWord } ] const router = createRouter({ history: createWebHashHistory(), routes: routes }) export default router ``` **参考文档:** - [路由的 hash 模式和 history 模式的区别](https://www.cnblogs.com/GGbondLearn/p/12239651.html) ## pinia ```shell npm install pinia ``` src 下创建 store/index.ts ```typescript import { createPinia } from "pinia"; const store = createPinia(); export { store }; ``` main.ts ```typescript import {createApp} from 'vue' import App from './App.vue' import router from "./router"; import '@/styles/index.scss' import { store } from "./store"; import ElementPlus from 'element-plus' import 'element-plus/theme-chalk/index.css' import locale from 'element-plus/lib/locale/lang/zh-cn' import 'virtual:svg-icons-register'; // @see https://blog.csdn.net/qq_37213281/article/details/121422027 import * as ElIconModules from '@element-plus/icons' import '@/permission' import Pagination from '@/components/Pagination/index.vue' import {listDictsByCode} from '@/api/system/dict' const app = createApp(App) // 统一注册el-icon图标 // @link https://blog.csdn.net/Alloom/article/details/119415984 for (let iconName in ElIconModules) { app.component(iconName, (ElIconModules as any)[iconName]) } // 全局方法 app.config.globalProperties.$listDictsByCode = listDictsByCode app.component('Pagination', Pagination) // 全局组件 .use(store) .use(router) .use(ElementPlus, {locale}) .mount('#app') ``` ## element-plus ```shell npm install element-plus ``` ## main.ts ```typescript import { createApp } from 'vue' import App from './App.vue' import router from "./router"; import {store,key} from './store' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app=createApp(App) app .use(router) .use(store,key) .use(ElementPlus) .mount('#app') ``` ## Vite 设置路径别名 #### 安装 @types/node ```shell npm install --save-dev @types/node ``` 或者简写 ```shell npm i -D @types/node ``` #### vite.config.ts ```typescript import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' // 在 ts 模块中加载 node 核心模块需要安装 node 的类型补充模块: npm i -D @types/node import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { // Vite2设置别名路径方式一 /** alias:{ "/@":path.resolve("./src"), }, **/ // Vite2设置别名路径方式二 alias: [ { find: "@", replacement: path.resolve("./src") }, { find: "@image", replacement: path.resolve("./src/assets/images") }, { find: "@/router", replacement: path.resolve("./src/router") }, { find: "@/store", replacement: path.resolve("./src/store") }, { find: "@/api", replacement: path.resolve("./src/api") } ] } }) ``` #### tsconfig.json TS配置别名路径,否则使用别名路径会报错,下面关键配置 `baseUrl` 、`paths` 和 `include` ```json { "compilerOptions": { ... "baseUrl": "./", "paths": { "@": ["src"], "@*": ["src/*"] } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] } ``` ## Vite 环境变量配置 **官方环境变量配置文档:** https://cn.vitejs.dev/guide/env-and-mode.html #### 配置文件 在项目根目录分别添加 `开发环境配置` 、 `生产环境配置`和`模拟环境配置`文件 开发环境配置:`.env.development` ```properties # 开发环境变量 注意:变量必须以 VITE_ 为前缀才能暴露给外部读取 VITE_APP_TITLE = '管理系统' VITE_APP_PORT = 3000 VITE_APP_BASE_API = '/dev-api' ``` 生产环境配置:`.env.production` ```properties # 生产环境变量 VITE_APP_TITLE = '管理系统' VITE_APP_PORT = 3000 VITE_APP_BASE_API = '/prod-api' ``` 模拟环境配置:`.env.staging` ```properties # 模拟环境变量 VITE_APP_TITLE = '管理系统' VITE_APP_PORT = 3000 VITE_APP_BASE_API = '/stage-api' ``` #### 环境变量智能提示 `src/env.d.ts` 添加以下配置 ```typescript // 环境变量智能提示 interface ImportMetaEnv { VITE_APP_TITLE: string, VITE_APP_PORT: string, VITE_APP_BASE_API: string } ``` ## 生产打包配置 #### package.json ```json "scripts": { "dev": "vite serve --mode development", "build:prod": "vue-tsc --noEmit && vite build --mode production", "serve": "vite preview" } ``` #### tsconfig.json ```json { "compilerOptions": { ... "skipLibCheck": true // element-plus 生产打包报错,通过此配置修改 TS 不对第三方依赖类型检查 } } ``` 执行 `npm run build:prod` 命令打包,生成的打包文件在项目根目录 `dist` 目录下 ## axios 封装 #### 安装axios ``` npm i axios ``` #### 缓存工具类 **src/utils/storage.ts** ```typescript /** * window.localStorage 浏览器永久缓存 */ export const Local = { // 设置永久缓存 set(key: string, val: any) { window.localStorage.setItem(key, JSON.stringify(val)); }, // 获取永久缓存 get(key: string) { let json: any = window.localStorage.getItem(key); return JSON.parse(json); }, // 移除永久缓存 remove(key: string) { window.localStorage.removeItem(key); }, // 移除全部永久缓存 clear() { window.localStorage.clear(); }, }; /** * window.sessionStorage 浏览器临时缓存 */ export const Session = { // 设置临时缓存 set(key: string, val: any) { window.sessionStorage.setItem(key, JSON.stringify(val)); }, // 获取临时缓存 get(key: string) { let json: any = window.sessionStorage.getItem(key); return JSON.parse(json); }, // 移除临时缓存 remove(key: string) { window.sessionStorage.removeItem(key); }, // 移除全部临时缓存 clear() { window.sessionStorage.clear(); } }; ``` #### 创建axios实例 **src/utils/request.ts** ```typescript import axios from "axios"; import {ElMessage, ElMessageBox} from "element-plus"; import {Session} from "@/utils/storage"; // 创建 axios 实例 const service = axios.create({ baseURL: import.meta.env.VITE_BASE_API as any, timeout: 50000, headers: {'Content-Type': 'application/json;charset=utf-8'} }) // 请求拦截器 service.interceptors.request.use( (config) => { if (!config?.headers) { throw new Error(`Expected 'config' and 'config.headers' not to be undefined`); } if (Session.get('token')) { config.headers.Authorization = `${Session.get('token')}`; } }, (error) => { return Promise.reject(error); } ) // 响应拦截器 service.interceptors.response.use( ({data}) => { // 对响应数据做点什么 const {code, msg} = data; if (code === '00000') { return data; } else { ElMessage({ message: msg || '系统出错', type: 'error' }) return Promise.reject(new Error(msg || 'Error')) } }, (error) => { const {code, msg} = error.response.data if (code === 'A0230') { // token 过期 Session.clear(); // 清除浏览器全部临时缓存 window.location.href = '/'; // 跳转登录页 ElMessageBox.alert('当前页面已失效,请重新登录', '提示', {}) .then(() => { }) .catch(() => { }); } return Promise.reject(new Error(msg || 'Error')) } ); // 导出 axios 实例 export default service ``` ## HelloWorld.vue ```typescript ``` ## App.vue ```typescript ``` ## SVG图标 - [使用手册(详细)](https://github.com/anncwb/vite-plugin-svg-icons/blob/main/README.zh_CN.md) 1. 安装 ``` npm i vite-plugin-svg-icons -D ``` 2. 配置插件 源码坐标:[vite.config.ts](vite.config.ts) ``` viteSvgIcons({ // 指定需要缓存的图标文件夹 iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')], // 指定symbolId格式 symbolId: 'icon-[dir]-[name]', }) ``` 3. 组件封装 源码坐标:[src/components/SvgIcon/index.vue](src/components/SvgIcon/index.vue) 4. 使用示例 源码坐标:[src/components/IconSelect/index.vue](src/components/IconSelect/index.vue) ## 跨域处理 vite.config.ts 跨域配置 ``` // 本地反向代理解决浏览器跨域限制 server: { host: 'localhost', port: Number(env.VITE_APP_PORT), open: true, // 运行自动打开浏览器 proxy: { [env.VITE_APP_BASE_API]: { target: 'http://localhost:9999', changeOrigin: true, rewrite: path => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') } } } ``` ## NProgress 进度条 1. 安装 ``` npm install --save nprogress ``` 2. 使用案例 源码坐标:[permission.ts](src/permission.ts) ``` import NProgress from 'nprogress'; import 'nprogress/nprogress.css' NProgress.configure({showSpinner: false}) // 进度环显示/隐藏 router.beforeEach((to, from, next) => { NProgress.start() ... }) router.afterEach(() => { NProgress.done() }) ``` ## 富文本编辑器 wangEditor - [官方文档](https://www.wangeditor.com/v5/) - [Vue3 使用](https://www.wangeditor.com/v5/guide/for-frame.html#vue3) - [Vue3 示例](https://github.com/wangfupeng1988/vue3-wangeditor-demo) 1. 安装 ``` # 安装 editor npm install @wangeditor/editor --save # 安装 Vue3 组件 npm install @wangeditor/editor-for-vue@next --save ``` 2. 组件封装 - 源码坐标:[src/components/WangEditor/index.vue](src/components/WangEditor/index.vue) - wangEditor自定义图片上传 ```aidl const editorConfig = { placeholder: '请输入内容...', MENU_CONF: { uploadImage: { async customUpload(file, insertFn) { uploadFile(file).then(response => { const url = response.data insertFn(url) }) } } } } ``` 3. 使用示例 源码坐标:[src/views/pms/goods/components/GoodsInfo.vue](src/views/pms/goods/components/GoodsInfo.vue) ## 列表排序 sortablejs 1. 安装 ``` npm install sortablejs --save ```