【vue3-element-admin 】

栏目:云星空知识作者:金蝶来源:金蝶云社区发布:2024-09-16浏览:1

【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,当然也可各取所需。

项目预览

在线预览

http://vue3.youlai.tech/

首页控制台

明亮模式
暗黑模式

接口文档

接口文档

权限管理系统

用户管理角色管理
菜单管理字典管理

扩展生态

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 RouterVue.js 的官方路由https://router.vuejs.org/zh/
wangEditorTypescript 开发的 Web 富文本编辑器https://www.wangeditor.com/
Echarts一个基于 JavaScript 的开源可视化图表库https://echarts.apache.org/zh/
vue-i18nVue 国际化多语言插件https://vue-i18n.intlify.dev/
VueUse基于Vue组合式API的实用工具集(类比HuTool工具)http://www.vueusejs.com/

前/后端源码


GiteeGithub
前端vue3-element-adminvue3-element-admin
后端youlai-bootyoulai-boot

接口文档

环境准备


名称备注
开发工具VSCode 下载-
运行环境Node 16+ 下载image-20230224222640120
VSCode插件(必装)插件市场搜索 Vue Language Features (Volar) 和 TypeScript Vue Plugin (Volar) 安装,且禁用 Veturimage-20230224222541797

项目初始化

按照 【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按需自动导入APIref,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 按需自动导入

需要完成上面一节的 自动导入 的安装和配置

安装 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 引擎 。

参考:Vite 安装 UnoCSS 官方文档

安装依赖

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 插件

再看下具体使用方式和实际效果:

代码效果
image-20230222220856251

如果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: '确定',           type: 'warning'         }).then(() => {           localStorage.clear(); // @vueuse/core 自动导入           window.location.href = '/';         });       }else{           ElMessage.error(msg || '系统出错');       }     }     return Promise.reject(error.message);   } ); // 导出 axios 实例 export default service;

登录接口实战

访问 vue3-element-admin 在线接口文档, 查看登录接口请求参数和响应数据类型

点击 生成代码 获取登录响应数据 TypeScript 类型定义

将类型定义复制到 src/api/auth/types.ts 文件中

/**  * 登录请求参数  */ export interface LoginData {   /**    * 用户名    */   username: string;   /**    * 密码    */   password: string; } /**  * 登录响应  */ export interface LoginResult {   /**    * 访问token    */   accessToken?: string;   /**    * 过期时间(单位:毫秒)    */   expires?: number;   /**    * 刷新token    */   refreshToken?: string;   /**    * token 类型    */   tokenType?: string; }

登录 API 定义

// src/api/auth/index.ts import request from '@/utils/request'; import { AxiosPromise } from 'axios'; import { LoginData, LoginResult } from './types'; /**  * 登录API   *   * @param data {LoginData}  * @returns  */ export function loginApi(data: LoginData): AxiosPromise<LoginResult> {   return request({     url: '/api/v1/auth/login',     method: 'post',     params: data   }); }

登录 API 调用

// src/store/modules/user.ts import { loginApi } from '@/api/auth'; import { LoginData } from '@/api/auth/types'; /**  * 登录调用  *  * @param {LoginData}  * @returns  */ function login(loginData: LoginData) {   return new Promise<void>((resolve, reject) => {     loginApi(loginData)       .then(response => {         const { tokenType, accessToken } = response.data;         token.value = tokenType + ' ' + accessToken; // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx         resolve();       })       .catch(error => {         reject(error);       });   }); }

动态路由

安装 vue-router

npm install vue-router@next

路由实例

创建路由实例,顺带初始化静态路由,而动态路由需要用户登录,根据用户拥有的角色进行权限校验后进行初始化

// src/router/index.ts import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; export const Layout = () => import('@/layout/index.vue'); // 静态路由 export const constantRoutes: RouteRecordRaw[] = [   {     path: '/redirect',     component: Layout,     meta: { hidden: true },     children: [       {         path: '/redirect/:path(.*)',         component: () => import('@/views/redirect/index.vue')       }     ]   },   {     path: '/login',     component: () => import('@/views/login/index.vue'),     meta: { hidden: true }   },   {     path: '/',     component: Layout,     redirect: '/dashboard',     children: [       {         path: 'dashboard',         component: () => import('@/views/dashboard/index.vue'),         name: 'Dashboard',         meta: { title: 'dashboard', icon: 'homepage', affix: true }       }     ]   } ]; /**  * 创建路由  */ const router = createRouter({   history: createWebHashHistory(),   routes: constantRoutes as RouteRecordRaw[],   // 刷新时,滚动条位置还原   scrollBehavior: () => ({ left: 0, top: 0 }) }); /**  * 重置路由  */ export function resetRouter() {   router.replace({ path: '/login' });   location.reload(); } export default router;

全局注册路由实例

// main.ts import router from "@/router"; app.use(router).mount('#app')

动态权限路由

路由守卫 src/permission.ts ,获取当前登录用户的角色信息进行动态路由的初始化

最终调用 permissionStore.generateRoutes(roles) 方法生成动态路由

// src/store/modules/permission.ts  import { listRoutes } from '@/api/menu'; export const usePermissionStore = defineStore('permission', () => {   const routes = ref<RouteRecordRaw[]>([]);   function setRoutes(newRoutes: RouteRecordRaw[]) {     routes.value = constantRoutes.concat(newRoutes);   }   /**    * 生成动态路由    *    * @param roles 用户角色集合    * @returns    */   function generateRoutes(roles: string[]) {     return new Promise<RouteRecordRaw[]>((resolve, reject) => {       // 接口获取所有路由       listRoutes()         .then(({ data: asyncRoutes }) => {           // 根据角色获取有访问权限的路由           const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);           setRoutes(accessedRoutes);           resolve(accessedRoutes);         })         .catch(error => {           reject(error);         });     });   }   // 导出 store 的动态路由数据 routes    return { routes, setRoutes, generateRoutes }; });

接口获取得到的路由数据

根据路由数据 (routes)生成菜单的关键代码

src/layout/componets/Sidebar/index.vuesrc/layout/componets/Sidebar/SidebarItem.vue
image-20230326145836872

按钮权限

除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外,Vue 还允许你注册自定义的指令 (Custom Directives),以下就通过自定义指令的方式实现按钮权限控制。

参考:Vue 官方文档-自定义指令

**自定义指令 **

// src/directive/permission/index.ts import { useUserStoreHook } from '@/store/modules/user'; import { Directive, DirectiveBinding } from 'vue'; /**  * 按钮权限  */ export const hasPerm: Directive = {   mounted(el: HTMLElement, binding: DirectiveBinding) {     // 「超级管理员」拥有所有的按钮权限     const { roles, perms } = useUserStoreHook();     if (roles.includes('ROOT')) {       return true;     }     // 「其他角色」按钮权限校验     const { value } = binding;     if (value) {       const requiredPerms = value; // DOM绑定需要的按钮权限标识       const hasPerm = perms?.some(perm => {         return requiredPerms.includes(perm);       });       if (!hasPerm) {         el.parentNode && el.parentNode.removeChild(el);       }     } else {       throw new Error(         "need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\""       );     }   } };

全局注册自定义指令

// src/directive/index.ts import type { App } from 'vue'; import { hasPerm } from './permission'; // 全局注册 directive 方法 export function setupDirective(app: App<Element>) {   // 使 v-hasPerm 在所有组件中都可用   app.directive('hasPerm', hasPerm); }
// src/main.ts import { setupDirective } from '@/directive'; const app = createApp(App); // 全局注册 自定义指令(directive) setupDirective(app);

组件使用自定义指令

// src/views/system/user/index.vue <el-button v-hasPerm="['sys:user:add']">新增</el-button> <el-button v-hasPerm="['sys:user:delete']">删除</el-button>

国际化

国际化分为两个部分,Element Plus 框架国际化(官方提供了国际化方式)和自定义国际化(通过 vue-i18n 国际化插件)

Element Plus 国际化

简单的使用方式请参考 Element Plus 官方文档-国际化示例,以下介绍 vue3-element-admin 整合 pinia 实现国际化语言切换。

Element Plus 提供了一个 Vue 组件 ConfigProvider 用于全局配置国际化的设置。

<!-- src/App.vue --> <script setup> import { ElConfigProvider } from 'element-plus'; import { useAppStore } from '@/store/modules/app'; const appStore = useAppStore(); </script> <template>   <el-config-provider :locale="appStore.locale" >     <router-view />   </el-config-provider> </template>

定义 store

// src/store/modules/app.ts import { defineStore } from 'pinia'; import { useStorage } from '@vueuse/core'; import defaultSettings from '@/settings'; // 导入 Element Plus 中英文语言包 import zhCn from 'element-plus/es/locale/lang/zh-cn'; import en from 'element-plus/es/locale/lang/en'; // setup export const useAppStore = defineStore('app', () => {        const language = useStorage('language', defaultSettings.language);        /**    * 根据语言标识读取对应的语言包    */   const locale = computed(() => {     if (language?.value == 'en') {       return en;     } else {       return zhCn;     }   });   /**    * 切换语言    */   function changeLanguage(val: string) {     language.value = val;   }   return {     language,     locale,     changeLanguage   }; });

切换语言组件调用

<!-- src/components/LangSelect/index.vue --> <script setup> import { useI18n } from 'vue-i18n'; import SvgIcon from '@/components/SvgIcon/index.vue'; import { useAppStore } from '@/store/modules/app'; const appStore = useAppStore(); const { locale } = useI18n(); function handleLanguageChange(lang: string) {   locale.value = lang;   appStore.changeLanguage(lang);   if (lang == 'en') {     ElMessage.success('Switch Language Successful!');   } else {     ElMessage.success('切换语言成功!');   } } </script> <template>   <el-dropdown trigger="click" @command="handleLanguageChange">     <div>       <svg-icon icon-class="language" />     </div>     <template #dropdown>       <el-dropdown-menu>         <el-dropdown-item           :disabled="appStore.language === 'zh-cn'"           command="zh-cn"         >           中文         </el-dropdown-item>         <el-dropdown-item :disabled="appStore.language === 'en'" command="en">           English         </el-dropdown-item>       </el-dropdown-menu>     </template>   </el-dropdown> </template>

从 Element Plus 分页组件看下国际化的效果

vue-i18n 自定义国际化

i18n 英文全拼 internationalization ,国际化的意思,英文 i 和 n 中间18个英文字母

参考:vue-i18n 官方文档 - installation

安装 vue-i18n

npm install vue-i18n@9

自定义语言包

创建 src/lang/package 语言包目录,存放自定义的语言文件

中文语言包 zh-cn.ts英文语言包 en.ts

【vue3-element-admin 】

【vue3-element-admin 】基于 Vue3 + Vite4 + TypeScript + Element-Plus 从0到1搭建后台管理系统(前后端开源@有来开源组织) vu...
点击下载文档
确认删除?