【vue】I18N国际化管理系统
随着系统走向国际化,支持多语言成为必备功能。先前已经成功的把若依的管理系统更换名字成功,由于若依框架提供了良好的国际化(i18n)支持,本文将详细介绍如何为若依前后端分离版添加英语支持,实现中英文切换。
安装vue-i18n插件
根据您的Vue版本安装对应的vue-i18n插件:
npm install --save vue-i18n
参考若依的官方手册,下面是完整的步骤:
前端国际化流程
1、package.json
中dependencies
节点添加vue-i18n
"vue-i18n": "7.3.2",
2、src
目录下创建lang目录,存放国际化文件
此处包含三个文件,分别是 index.js
zh.js
en.js
// index.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
import enLocale from './en'
import zhLocale from './zh'Vue.use(VueI18n)const messages = {en_US: {...enLocale,...elementEnLocale},zh_CN: {...zhLocale,...elementZhLocale}
}const i18n = new VueI18n({// 设置语言 选项 en | zhlocale: Cookies.get('language') || 'zh_CN',// 设置文本内容messages
})export default i18n
// zh.js
export default {login: {title: '若依后台管理系统',logIn: '登录',username: '账号',password: '密码',code: '验证码',rememberMe: '记住密码'},tagsView: {refresh: '刷新',close: '关闭',closeOthers: '关闭其它',closeAll: '关闭所有'},settings: {title: '系统布局配置',theme: '主题色',tagsView: '开启 Tags-View',fixedHeader: '固定 Header',sidebarLogo: '侧边栏 Logo'}
}
// en.js
export default {login: {title: 'RuoYi Login Form',logIn: 'Login in',username: 'Username',password: 'Password',code: 'Code',rememberMe: 'Remember Me'},tagsView: {refresh: 'Refresh',close: 'Close',closeOthers: 'Close Others',closeAll: 'Close All'},settings: {title: 'Page style setting',theme: 'Theme Color',tagsView: 'Open Tags-View',fixedHeader: 'Fixed Header',sidebarLogo: 'Sidebar Logo'}
}
3、在src/main.js
中增量添加i18n
import i18n from './lang'Vue.use(Element, {i18n: (key, value) => i18n.t(key, value),size: Cookies.get('size') || 'medium'
})Vue.config.productionTip = falsenew Vue({el: '#app',router,store,i18n,render: h => h(App)
})
4、在src/store/getters.js
中添加language
language: state => state.app.language,
5、在src/store/modules/app.js
中增量添加i18n
const state = {language: Cookies.get('language') || 'en'
}const mutations = {SET_LANGUAGE: (state, language) => {state.language = languageCookies.set('language', language)}
}const actions = {setLanguage({ commit }, language) {commit('SET_LANGUAGE', language)}
}
6、在src/components/LangSelect/index.vue
中创建汉化组件
<template><el-dropdown trigger="click" class="international" @command="handleSetLanguage"><div><svg-icon class-name="international-icon" icon-class="language" /></div><el-dropdown-menu slot="dropdown"><el-dropdown-item :disabled="language==='zh_CN'" command="zh_CN">中文</el-dropdown-item><el-dropdown-item :disabled="language==='en_US'" command="en_US">English</el-dropdown-item></el-dropdown-menu></el-dropdown>
</template><script>
import { changeLanguage } from "@/api/login";export default {computed: {language() {return this.$store.getters.language}},methods: {handleSetLanguage(value) {this.$i18n.locale = valuethis.$store.dispatch('app/setLanguage', value)this.$message({ message: '设置语言成功', type: 'success' })changeLanguage(value).then(response => {window.location.reload();});}}
}
</script>
7、在login.js
新增修改语言方法
// 修改语言
export function changeLanguage(lang){return request({url: '/changeLanguage',method: 'get',headers: {isToken: false,},params: {lang: lang}})
}
8、登录页面汉化
<template><div class="login"><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">{{ $t('login.title') }}</h3><lang-select class="set-language" /><el-form-item prop="username"><el-inputv-model="loginForm.username"type="text"auto-complete="off":placeholder="$t('login.username')"><svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"auto-complete="off":placeholder="$t('login.password')"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="code" v-if="captchaEnabled"><el-inputv-model="loginForm.code"auto-complete="off":placeholder="$t('login.code')"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item><el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">{{ $t('login.rememberMe') }}</el-checkbox><el-form-item style="width:100%;"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="handleLogin"><span v-if="!loading">{{ $t('login.logIn') }}</span><span v-else>登 录 中...</span></el-button><div style="float: right;" v-if="register"><router-link class="link-type" :to="'/register'">立即注册</router-link></div></el-form-item></el-form><!-- 底部 --><div class="el-login-footer"><span>Copyright © 2018-2024 ruoyi.vip All Rights Reserved.</span></div></div>
</template><script>
import LangSelect from '@/components/LangSelect'
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",components: { LangSelect },data() {return {codeUrl: "",loginForm: {username: "admin",password: "admin123",rememberMe: false,code: "",uuid: ""},loginRules: {username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],code: [{ required: true, trigger: "change", message: "请输入验证码" }]},loading: false,// 验证码开关captchaEnabled: true,// 注册开关register: false,redirect: undefined};},watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect;},immediate: true}},created() {this.getCode();this.getCookie();},methods: {getCode() {getCodeImg().then(res => {this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;}});},getCookie() {const username = Cookies.get("username");const password = Cookies.get("password");const rememberMe = Cookies.get('rememberMe')this.loginForm = {username: username === undefined ? this.loginForm.username : username,password: password === undefined ? this.loginForm.password : decrypt(password),rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)};},handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true;if (this.loginForm.rememberMe) {Cookies.set("username", this.loginForm.username, { expires: 30 });Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });} else {Cookies.remove("username");Cookies.remove("password");Cookies.remove('rememberMe');}this.$store.dispatch("Login", this.loginForm).then(() => {this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;if (this.captchaEnabled) {this.getCode();}});}});}}
};
</script><style rel="stylesheet/scss" lang="scss">
.login {display: flex;justify-content: center;align-items: center;height: 100%;background-image: url("../assets/images/login-background.jpg");background-size: cover;
}
.title {margin: 0px auto 30px auto;text-align: center;color: #707070;
}.login-form {border-radius: 6px;background: #ffffff;width: 400px;padding: 25px 25px 5px 25px;.el-input {height: 38px;input {height: 38px;}}.input-icon {height: 39px;width: 14px;margin-left: 2px;}
}
.login-tip {font-size: 13px;text-align: center;color: #bfbfbf;
}
.login-code {width: 33%;height: 38px;float: right;img {cursor: pointer;vertical-align: middle;}
}
.el-login-footer {height: 40px;line-height: 40px;position: fixed;bottom: 0;width: 100%;text-align: center;color: #fff;font-family: Arial;font-size: 12px;letter-spacing: 1px;
}
.login-code-img {height: 38px;
}
</style>
到这里是刚刚开始哦,目前只是把基础打好了
后台国际化流程
如果不做接口返回的数据国际化的话,做下面两步就够了。
1、在SysLoginController.java新增修改语言方法
@GetMapping("/changeLanguage")
public AjaxResult changeLanguage(String lang)
{return AjaxResult.success();
}
2、在SecurityConfig.java允许匿名访问此方法
.antMatchers("/changeLanguage").permitAll()
页面国际化
上面都是官方手册中的基础配置,现在开始替换各页面和组件的部分
切换语言组件
官方的切换组件不太好看,下面是我从其他博客中找到的简单的组件,如果不需要可以跳过这一步;组件效果如下:
代码如下:
汉化组件
在src/components/LangSwitch/index.vue
中创建汉化组件
<template><el-dropdown trigger="click" class="dingtalk-language-switcher"@command="handleSetLanguage"><div class="language-trigger"><span class="current-language">{{ currentLanguage.label }}</span><i class="el-icon-arrow-down"></i></div><el-dropdown-menu slot="dropdown" class="language-menu"><el-dropdown-item v-for="lang in languages" :key="lang.value":command="lang.value":class="{ 'active': language === lang.value }">{{ lang.label }}</el-dropdown-item></el-dropdown-menu></el-dropdown>
</template><script>
import { changeLanguage } from "@/api/login";export default {data() {return {languages: [{ value: 'en_US', label: 'English' },{ value: 'zh_CN', label: '简体中文' }// 可以继续添加其他语言]}},computed: {language() {return this.$store.getters.language || 'zh_CN'},currentLanguage() {return this.languages.find(lang => lang.value === this.language) || this.languages[0]}},methods: {handleSetLanguage(lang) {if (this.language === lang) return;this.$i18n.locale = lang;this.$store.dispatch('app/setLanguage', lang);changeLanguage(lang).then(() => {this.$message.success(this.$t('navbar.switchLanguageSuccess'));}).catch(() => {this.$message.error(this.$t('navbar.switchLanguageFailed'));});}}
}
</script><style lang="scss" scoped>
.dingtalk-language-switcher {cursor: pointer;margin: 0 10px;font-size: 14px;color: #333;.language-trigger {display: flex;align-items: center;padding: 0 5px;height: 100%;.current-language {margin-right: 4px;}.el-icon-arrow-down {font-size: 12px;color: #999;transition: transform 0.3s;}}&:hover {color: var(--el-color-primary);.el-icon-arrow-down {color: var(--el-color-primary);}}// 下拉菜单展开时箭头旋转&.is-active {.el-icon-arrow-down {transform: rotate(180deg);}}
}.language-menu {min-width: 160px;padding: 5px 0;.el-dropdown-menu__item {padding: 0 16px;line-height: 36px;font-size: 14px;&:hover {color: var(--el-color-primary);background-color: #f5f5f5;}&.active {color: var(--el-color-primary);background-color: #f0f7ff;}}
}
</style>
登录界面修改
src/views/login.vue
<template><div class="login"><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">{{ $t('login.title') }}</h3><el-form-item prop="username"><el-inputv-model="loginForm.username"type="text"auto-complete="off":placeholder="$t('login.username')"><svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"auto-complete="off":placeholder="$t('login.password')"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="code" v-if="captchaEnabled"><el-inputv-model="loginForm.code"auto-complete="off":placeholder="$t('login.code')"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item><!-- 底部选项区域 --><div class="login-options"><div class="options-left"><el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">{{ $t('login.rememberMe') }}</el-checkbox></div><div class="options-right" style="margin:0px 0px 25px 0px;"><lang-switch /></div></div><el-form-item style="width:100%;"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="handleLogin"><span v-if="!loading">{{ $t('login.logIn') }}</span><span v-else>登 录 中...</span></el-button><div style="float: right;" v-if="register"><router-link class="link-type" :to="'/register'">立即注册</router-link></div></el-form-item></el-form><!-- 底部 --><div class="el-login-footer"><span>Copyright © 2018-2024 ruoyi.vip All Rights Reserved.</span></div></div>
</template><script>
import LangSwitch from '@/components/LangSwitch'
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",components: { LangSwitch },data() {return {codeUrl: "",loginForm: {username: "admin",password: "admin123",rememberMe: false,code: "",uuid: ""},loginRules: {username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],code: [{ required: true, trigger: "change", message: "请输入验证码" }]},loading: false,// 验证码开关captchaEnabled: true,// 注册开关register: false,redirect: undefined};},watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect;},immediate: true}},created() {this.getCode();this.getCookie();},methods: {getCode() {getCodeImg().then(res => {this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;}});},getCookie() {const username = Cookies.get("username");const password = Cookies.get("password");const rememberMe = Cookies.get('rememberMe')this.loginForm = {username: username === undefined ? this.loginForm.username : username,password: password === undefined ? this.loginForm.password : decrypt(password),rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)};},handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true;if (this.loginForm.rememberMe) {Cookies.set("username", this.loginForm.username, { expires: 30 });Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });} else {Cookies.remove("username");Cookies.remove("password");Cookies.remove('rememberMe');}this.$store.dispatch("Login", this.loginForm).then(() => {this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;if (this.captchaEnabled) {this.getCode();}});}});}}
};
</script><style rel="stylesheet/scss" lang="scss">
.login {display: flex;justify-content: center;align-items: center;height: 100%;background-image: url("../assets/images/login-background.jpg");background-size: cover;
}
.title {margin: 0px auto 30px auto;text-align: center;color: #707070;
}.login-form {border-radius: 6px;background: #ffffff;width: 400px;padding: 25px 25px 5px 25px;.el-input {height: 38px;input {height: 38px;}}.input-icon {height: 39px;width: 14px;margin-left: 2px;}
}
.login-tip {font-size: 13px;text-align: center;color: #bfbfbf;
}
.login-code {width: 33%;height: 38px;float: right;img {cursor: pointer;vertical-align: middle;}
}
.el-login-footer {height: 40px;line-height: 40px;position: fixed;bottom: 0;width: 100%;text-align: center;color: #fff;font-family: Arial;font-size: 12px;letter-spacing: 1px;
}
.login-code-img {height: 38px;
}
.login-options {display: flex;justify-content: space-between;align-items: center;margin-bottom: 25px;height: 20px; /* 确保与复选框高度一致 */line-height: 20px; /* 确保文字垂直居中 */.options-left {/* 左侧样式保持原样 */.el-checkbox {margin: 0;}}.options-right {/* 语言切换器样式 */.language-switcher {display: inline-block;.language-trigger {display: flex;align-items: center;color: #606266;font-size: 14px;cursor: pointer;.current-language {margin-right: 5px;}.el-icon-arrow-down {font-size: 12px;color: #c0c4cc;transition: transform 0.2s;}}&:hover {.language-trigger {color: var(--el-color-primary);.el-icon-arrow-down {color: var(--el-color-primary);}}}&.is-active {.el-icon-arrow-down {transform: rotate(180deg);}}}}
}
</style>
导航栏部分
在src\layout\components\Navbar.vue引入我们之前写好的组件
<lang-switch class="right-menu-item hover-effect"/>import LangSwitch from '@/components/LangSwitch'export default {components: {LangSwitch},
}
完整代码如下:
<template><div class="navbar"><hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /><breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" /><top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" /><div class="right-menu"><template v-if="device!=='mobile'"><search id="header-search" class="right-menu-item" /><screenfull id="screenfull" class="right-menu-item hover-effect" /><el-tooltip content="布局大小" effect="dark" placement="bottom"><size-select id="size-select" class="right-menu-item hover-effect" /></el-tooltip><lang-switch class="right-menu-item hover-effect"/></template><el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover"><div class="avatar-wrapper"><img :src="avatar" class="user-avatar"><span class="user-nickname"> {{ nickName }} </span></div><el-dropdown-menu slot="dropdown"><router-link to="/user/profile"><el-dropdown-item>{{ $t('navbar.personalCenter') }}</el-dropdown-item></router-link><el-dropdown-item @click.native="setLayout" v-if="setting"><span>{{ $t('navbar.layoutSettings') }}</span></el-dropdown-item><el-dropdown-item divided @click.native="logout"><span>{{ $t('navbar.logOut') }}</span></el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template><script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import LangSwitch from '@/components/LangSwitch'export default {emits: ['setLayout'],components: {Breadcrumb,TopNav,Hamburger,Screenfull,SizeSelect,Search,LangSwitch},computed: {...mapGetters(['sidebar','avatar','device','nickName']),setting: {get() {return this.$store.state.settings.showSettings}},topNav: {get() {return this.$store.state.settings.topNav}}},methods: {toggleSideBar() {this.$store.dispatch('app/toggleSideBar')},setLayout(event) {this.$emit('setLayout')},logout() {this.$confirm('确定注销并退出系统吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$store.dispatch('LogOut').then(() => {location.href = '/index'})}).catch(() => {})}}
}
</script><style lang="scss" scoped>
.navbar {height: 50px;overflow: hidden;position: relative;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);.hamburger-container {line-height: 46px;height: 100%;float: left;cursor: pointer;transition: background .3s;-webkit-tap-highlight-color:transparent;&:hover {background: rgba(0, 0, 0, .025)}}.breadcrumb-container {float: left;}.topmenu-container {position: absolute;left: 50px;}.errLog-container {display: inline-block;vertical-align: top;}.right-menu {float: right;height: 100%;line-height: 50px;&:focus {outline: none;}.right-menu-item {display: inline-block;padding: 0 8px;height: 100%;font-size: 18px;color: #5a5e66;vertical-align: text-bottom;&.hover-effect {cursor: pointer;transition: background .3s;&:hover {background: rgba(0, 0, 0, .025)}}}.avatar-container {margin-right: 0px;padding-right: 0px;.avatar-wrapper {margin-top: 10px;right: 8px;position: relative;.user-avatar {cursor: pointer;width: 30px;height: 30px;border-radius: 50%;}.user-nickname{position: relative;bottom: 10px;left: 2px;font-size: 14px;font-weight: bold;}.el-icon-caret-bottom {cursor: pointer;position: absolute;right: -20px;top: 25px;font-size: 12px;}}}}
}
</style>
侧边栏菜单
菜单的显示来源有两个,路由和菜单管理。所以这两个都需要分别进行国际化的修改
路由国际化
src\router\index.js,以首页为例:修改title部分
{path: '',component: Layout,redirect: 'index',children: [{path: 'index',component: () => import('@/views/index'),name: 'Index',meta: { title: 'route.home', icon: 'dashboard', affix: true }}]}
在语言包中分别添加这个值
//zh.js
route: {home: '首页',personalCenter: '个人中心',}//en.js
route: {home: 'Home',personalCenter: 'Personal Center',},
修改侧边栏文件(src\layout\components\Sidebar\SidebarItem.vue)
主要是menusTitle方法,找到翻译语言包会替换翻译,没找到还是显示原文。
menusTitle(item) {if (this.$te(item)) {return this.$t(item);} else {return item;}},
完整代码如下:
<template><div v-if="!item.hidden"><templatev-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"><app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)"><el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"><item :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)":title="menusTitle(onlyOneChild.meta.title)" /></el-menu-item></app-link></template><el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body><template slot="title"><item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="menusTitle(item.meta.title)" /></template><sidebar-item v-for="(child, index) in item.children" :key="child.path + index" :is-nest="true" :item="child":base-path="resolvePath(child.path)" class="nest-menu" /></el-submenu></div>
</template><script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'export default {name: 'SidebarItem',components: { Item, AppLink },mixins: [FixiOSBug],props: {// route objectitem: {type: Object,required: true},isNest: {type: Boolean,default: false},basePath: {type: String,default: ''}},data() {this.onlyOneChild = nullreturn {}},methods: {menusTitle(item) {if (this.$te(item)) {return this.$t(item);} else {return item;}},hasOneShowingChild(children = [], parent) {if (!children) {children = [];}const showingChildren = children.filter(item => {if (item.hidden) {return false}// Temp set(will be used if only has one showing child)this.onlyOneChild = itemreturn true})// When there is only one child router, the child router is displayed by defaultif (showingChildren.length === 1) {return true}// Show parent if there are no child router to displayif (showingChildren.length === 0) {this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }return true}return false},resolvePath(routePath, routeQuery) {if (isExternal(routePath)) {return routePath}if (isExternal(this.basePath)) {return this.basePath}if (routeQuery) {let query = JSON.parse(routeQuery);return { path: path.resolve(this.basePath, routePath), query: query }}return path.resolve(this.basePath, routePath)}}
}
</script>
菜单管理国际化
不是每个菜单都在(src\router\index.js)中配置了路由,有的只是在菜单管理里新增了一下,那么这种菜单如何显示为英文呢,请看下图,把菜单名称改成动态的语言包属性即可。
面包屑
src\components\Breadcrumb\index.vue
<template><el-breadcrumb class="app-breadcrumb" separator="/"><transition-group name="breadcrumb"><el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path"><span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ $t(item.meta.title) }}</span><a v-else @click.prevent="handleLink(item)">{{ $t(item.meta.title) }}</a></el-breadcrumb-item></transition-group></el-breadcrumb>
</template><script>
export default {data() {return {levelList: null}},watch: {$route(route) {// if you go to the redirect page, do not update the breadcrumbsif (route.path.startsWith('/redirect/')) {return}this.getBreadcrumb()}},created() {this.getBreadcrumb()},methods: {getBreadcrumb() {// only show routes with meta.titlelet matched = []const router = this.$routeconst pathNum = this.findPathNum(router.path)// multi-level menuif (pathNum > 2) {const reg = /\/\w+/giconst pathList = router.path.match(reg).map((item, index) => {if (index !== 0) item = item.slice(1)return item})this.getMatched(pathList, this.$store.getters.defaultRoutes, matched)} else {matched = router.matched.filter(item => item.meta && this.$t(item.meta.title))}// 判断是否为首页if (!this.isDashboard(matched[0])) {matched = [{ path: "/index", meta: { title: 'route.home'} }].concat(matched)}this.levelList = matched.filter(item => item.meta && this.$t(item.meta.title) && item.meta.breadcrumb !== false)},findPathNum(str, char = "/") {let index = str.indexOf(char)let num = 0while (index !== -1) {num++index = str.indexOf(char, index + 1)}return num},getMatched(pathList, routeList, matched) {let data = routeList.find(item => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0])if (data) {matched.push(data)if (data.children && pathList.length) {pathList.shift()this.getMatched(pathList, data.children, matched)}}},isDashboard(route) {const name = route && route.nameif (!name) {return false}return name.trim() === 'Index'},handleLink(item) {const { redirect, path } = itemif (redirect) {this.$router.push(redirect)return}this.$router.push(path)}}
}
</script><style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {display: inline-block;font-size: 14px;line-height: 50px;margin-left: 8px;.no-redirect {color: #97a8be;cursor: text;}
}
</style>
tagsView
src\layout\components\TagsView\index.vue
<template><div id="tags-view-container" class="tags-view-container"><scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll"><router-link v-for="tag in visitedViews" ref="tag" :key="tag.path" :class="isActive(tag) ? 'active' : ''":to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" tag="span" class="tags-view-item":style="activeStyle(tag)" @click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''"@contextmenu.prevent.native="openMenu(tag, $event)">{{ menusTitle(tag.title) }}<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /></router-link></scroll-pane><ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu"><li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> {{ $t('tagsView.refresh') }}</li><li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> {{ $t('tagsView.close') }}</li><li @click="closeOthersTags"><i class="el-icon-circle-close"></i> {{ $t('tagsView.closeOthers') }}</li><li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> {{ $t('tagsView.closeLeft') }}</li><li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> {{ $t('tagsView.closeRight') }}</li><li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> {{ $t('tagsView.closeAll') }}</li></ul></div>
</template><script>
import ScrollPane from './ScrollPane'
import path from 'path'export default {components: { ScrollPane },data() {return {visible: false,top: 0,left: 0,selectedTag: {},affixTags: []}},computed: {visitedViews() {return this.$store.state.tagsView.visitedViews},routes() {return this.$store.state.permission.routes},theme() {return this.$store.state.settings.theme;}},watch: {$route() {this.addTags()this.moveToCurrentTag()},visible(value) {if (value) {document.body.addEventListener('click', this.closeMenu)} else {document.body.removeEventListener('click', this.closeMenu)}}},mounted() {this.initTags()this.addTags()},methods: {menusTitle(item) {if (this.$te(item)) {return this.$t(item);} else {return item;}},isActive(route) {return route.path === this.$route.path},activeStyle(tag) {if (!this.isActive(tag)) return {};return {"background-color": this.theme,"border-color": this.theme};},isAffix(tag) {return tag.meta && tag.meta.affix},isFirstView() {try {return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath} catch (err) {return false}},isLastView() {try {return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath} catch (err) {return false}},filterAffixTags(routes, basePath = '/') {let tags = []routes.forEach(route => {if (route.meta && route.meta.affix) {const tagPath = path.resolve(basePath, route.path)tags.push({fullPath: tagPath,path: tagPath,name: route.name,meta: { ...route.meta }})}if (route.children) {const tempTags = this.filterAffixTags(route.children, route.path)if (tempTags.length >= 1) {tags = [...tags, ...tempTags]}}})return tags},initTags() {const affixTags = this.affixTags = this.filterAffixTags(this.routes)for (const tag of affixTags) {// Must have tag nameif (tag.name) {this.$store.dispatch('tagsView/addVisitedView', tag)}}},addTags() {const { name } = this.$routeif (name) {this.$store.dispatch('tagsView/addView', this.$route)}},moveToCurrentTag() {const tags = this.$refs.tagthis.$nextTick(() => {for (const tag of tags) {if (tag.to.path === this.$route.path) {this.$refs.scrollPane.moveToTarget(tag)// when query is different then updateif (tag.to.fullPath !== this.$route.fullPath) {this.$store.dispatch('tagsView/updateVisitedView', this.$route)}break}}})},refreshSelectedTag(view) {this.$tab.refreshPage(view);if (this.$route.meta.link) {this.$store.dispatch('tagsView/delIframeView', this.$route)}},closeSelectedTag(view) {this.$tab.closePage(view).then(({ visitedViews }) => {if (this.isActive(view)) {this.toLastView(visitedViews, view)}})},closeRightTags() {this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {this.toLastView(visitedViews)}})},closeLeftTags() {this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {this.toLastView(visitedViews)}})},closeOthersTags() {this.$router.push(this.selectedTag.fullPath).catch(() => { });this.$tab.closeOtherPage(this.selectedTag).then(() => {this.moveToCurrentTag()})},closeAllTags(view) {this.$tab.closeAllPage().then(({ visitedViews }) => {if (this.affixTags.some(tag => tag.path === this.$route.path)) {return}this.toLastView(visitedViews, view)})},toLastView(visitedViews, view) {const latestView = visitedViews.slice(-1)[0]if (latestView) {this.$router.push(latestView.fullPath)} else {// now the default is to redirect to the home page if there is no tags-view,// you can adjust it according to your needs.if (view.name === 'Dashboard') {// to reload home pagethis.$router.replace({ path: '/redirect' + view.fullPath })} else {this.$router.push('/')}}},openMenu(tag, e) {const menuMinWidth = 105const offsetLeft = this.$el.getBoundingClientRect().left // container margin leftconst offsetWidth = this.$el.offsetWidth // container widthconst maxLeft = offsetWidth - menuMinWidth // left boundaryconst left = e.clientX - offsetLeft + 15 // 15: margin rightif (left > maxLeft) {this.left = maxLeft} else {this.left = left}this.top = e.clientYthis.visible = truethis.selectedTag = tag},closeMenu() {this.visible = false},handleScroll() {this.closeMenu()}}
}
</script><style lang="scss" scoped>
.tags-view-container {height: 34px;width: 100%;background: #fff;border-bottom: 1px solid #d8dce5;box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);.tags-view-wrapper {.tags-view-item {display: inline-block;position: relative;cursor: pointer;height: 26px;line-height: 26px;border: 1px solid #d8dce5;color: #495060;background: #fff;padding: 0 8px;font-size: 12px;margin-left: 5px;margin-top: 4px;&:first-of-type {margin-left: 15px;}&:last-of-type {margin-right: 15px;}&.active {background-color: #42b983;color: #fff;border-color: #42b983;&::before {content: '';background: #fff;display: inline-block;width: 8px;height: 8px;border-radius: 50%;position: relative;margin-right: 2px;}}}}.contextmenu {margin: 0;background: #fff;z-index: 3000;position: absolute;list-style-type: none;padding: 5px 0;border-radius: 4px;font-size: 12px;font-weight: 400;color: #333;box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);li {margin: 0;padding: 7px 16px;cursor: pointer;&:hover {background: #eee;}}}
}
</style><style lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {.tags-view-item {.el-icon-close {width: 16px;height: 16px;vertical-align: 2px;border-radius: 50%;text-align: center;transition: all .3s cubic-bezier(.645, .045, .355, 1);transform-origin: 100% 50%;&:before {transform: scale(.6);display: inline-block;vertical-align: -3px;}&:hover {background-color: #b4bccc;color: #fff;}}}
}
</style>
settings组件
src\layout\components\Settings\index.vue
<template><el-drawer size="280px" :visible="showSettings" :with-header="false" :append-to-body="true" :before-close="closeSetting" :lock-scroll="false"><div class="drawer-container"><div><div class="setting-drawer-content"><div class="setting-drawer-title"><h3 class="drawer-title">{{ $t('settings.title') }}</h3></div><div class="setting-drawer-block-checbox"><div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')"><img src="@/assets/images/dark.svg" alt="dark"><div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"><i aria-label="图标: check" class="anticon anticon-check"><svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class=""><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/></svg></i></div></div><div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')"><img src="@/assets/images/light.svg" alt="light"><div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"><i aria-label="图标: check" class="anticon anticon-check"><svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class=""><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/></svg></i></div></div></div><div class="drawer-item"><span>{{ $t('settings.theme') }}</span><theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /></div></div><el-divider/><h3 class="drawer-title">{{ $t('settings.configuration') }}</h3><div class="drawer-item"><span>{{ $t('settings.topNav') }}</span><el-switch v-model="topNav" class="drawer-switch" /></div><div class="drawer-item"><span>{{ $t('settings.tagsView') }}</span><el-switch v-model="tagsView" class="drawer-switch" /></div><div class="drawer-item"><span>{{ $t('settings.fixedHeader') }}</span><el-switch v-model="fixedHeader" class="drawer-switch" /></div><div class="drawer-item"><span>{{ $t('settings.sidebarLogo') }}</span><el-switch v-model="sidebarLogo" class="drawer-switch" /></div><div class="drawer-item"><span>{{ $t('settings.dynamicTitle') }}</span><el-switch v-model="dynamicTitle" class="drawer-switch" /></div><el-divider/><el-button size="small" type="primary" plain icon="el-icon-document-add" @click="saveSetting">{{ $t('settings.saveSetting') }}</el-button><el-button size="small" plain icon="el-icon-refresh" @click="resetSetting">{{ $t('settings.resetSetting') }}</el-button></div></div></el-drawer>
</template><script>
import ThemePicker from '@/components/ThemePicker'export default {components: { ThemePicker },expose: ['openSetting'],data() {return {theme: this.$store.state.settings.theme,sideTheme: this.$store.state.settings.sideTheme,showSettings: false}},computed: {fixedHeader: {get() {return this.$store.state.settings.fixedHeader},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'fixedHeader',value: val})}},topNav: {get() {return this.$store.state.settings.topNav},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'topNav',value: val})if (!val) {this.$store.dispatch('app/toggleSideBarHide', false)this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes)}}},tagsView: {get() {return this.$store.state.settings.tagsView},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'tagsView',value: val})}},tagsIcon: {get() {return this.$store.state.settings.tagsIcon},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'tagsIcon',value: val})}},sidebarLogo: {get() {return this.$store.state.settings.sidebarLogo},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'sidebarLogo',value: val})}},dynamicTitle: {get() {return this.$store.state.settings.dynamicTitle},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'dynamicTitle',value: val})this.$store.dispatch('settings/setTitle', this.$store.state.settings.title)}},footerVisible: {get() {return this.$store.state.settings.footerVisible},set(val) {this.$store.dispatch('settings/changeSetting', {key: 'footerVisible',value: val})}}},methods: {themeChange(val) {this.$store.dispatch('settings/changeSetting', {key: 'theme',value: val})this.theme = val},handleTheme(val) {this.$store.dispatch('settings/changeSetting', {key: 'sideTheme',value: val})this.sideTheme = val},openSetting() {this.showSettings = true},closeSetting(){this.showSettings = false},saveSetting() {this.$modal.loading("正在保存到本地,请稍候...")this.$cache.local.set("layout-setting",`{"topNav":${this.topNav},"tagsView":${this.tagsView},"tagsIcon":${this.tagsIcon},"fixedHeader":${this.fixedHeader},"sidebarLogo":${this.sidebarLogo},"dynamicTitle":${this.dynamicTitle},"footerVisible":${this.footerVisible},"sideTheme":"${this.sideTheme}","theme":"${this.theme}"}`)setTimeout(this.$modal.closeLoading(), 1000)},resetSetting() {this.$modal.loading("正在清除设置缓存并刷新,请稍候...")this.$cache.local.remove("layout-setting")setTimeout("window.location.reload()", 1000)}}
}
</script><style lang="scss" scoped>.setting-drawer-content {.setting-drawer-title {margin-bottom: 12px;color: rgba(0, 0, 0, .85);font-size: 14px;line-height: 22px;font-weight: bold;}.setting-drawer-block-checbox {display: flex;justify-content: flex-start;align-items: center;margin-top: 10px;margin-bottom: 20px;.setting-drawer-block-checbox-item {position: relative;margin-right: 16px;border-radius: 2px;cursor: pointer;img {width: 48px;height: 48px;}.setting-drawer-block-checbox-selectIcon {position: absolute;top: 0;right: 0;width: 100%;height: 100%;padding-top: 15px;padding-left: 24px;color: #1890ff;font-weight: 700;font-size: 14px;}}}}.drawer-container {padding: 20px;font-size: 14px;line-height: 1.5;word-wrap: break-word;.drawer-title {margin-bottom: 12px;color: rgba(0, 0, 0, .85);font-size: 14px;line-height: 22px;}.drawer-item {color: rgba(0, 0, 0, .65);font-size: 14px;padding: 12px 0;}.drawer-switch {float: right}}
</style>
个人中心
src\views\system\user\profile\index.vue 修改<template>部分即可,把文字内容都替换成语言包中的名字:
<template><div class="app-container"><el-row :gutter="20"><el-col :span="6" :xs="24"><el-card class="box-card"><div slot="header" class="clearfix"><span>{{ $t('profile.personalInformation') }}</span></div><div><div class="text-center"><userAvatar /></div><ul class="list-group list-group-striped"><li class="list-group-item"><svg-icon icon-class="user" />{{ $t('profile.userName') }}<div class="pull-right">{{ user.userName }}</div></li><li class="list-group-item"><svg-icon icon-class="phone" />{{ $t('profile.phonenumber') }}<div class="pull-right">{{ user.phonenumber }}</div></li><li class="list-group-item"><svg-icon icon-class="email" />{{ $t('profile.email') }}<div class="pull-right">{{ user.email }}</div></li><li class="list-group-item"><svg-icon icon-class="tree" />{{ $t('profile.dept') }}<div class="pull-right" v-if="user.dept">{{ user.dept.deptName }} / {{ postGroup }}</div></li><li class="list-group-item"><svg-icon icon-class="peoples" />{{ $t('profile.roleGroup') }}<div class="pull-right">{{ roleGroup }}</div></li><li class="list-group-item"><svg-icon icon-class="date" />{{ $t('profile.createTime') }}<div class="pull-right">{{ user.createTime }}</div></li></ul></div></el-card></el-col><el-col :span="18" :xs="24"><el-card><div slot="header" class="clearfix"><span>{{ $t('profile.basicInformation') }}</span></div><el-tabs v-model="selectedTab"><el-tab-pane :label="$t('profile.basicInformation')" name="userinfo"><userInfo :user="user" /></el-tab-pane><el-tab-pane :label="$t('profile.resetPwd')" name="resetPwd"><resetPwd /></el-tab-pane></el-tabs></el-card></el-col></el-row></div>
</template>
src\views\system\user\profile\userInfo.vue 修改<template>部分即可
<template><el-form ref="form" :model="form" :rules="rules" label-width="80px"><el-form-item :label="$t('profile.nickName')" prop="nickName"><el-input v-model="form.nickName" maxlength="30" /></el-form-item> <el-form-item :label="$t('profile.phonenumber')" prop="phonenumber"><el-input v-model="form.phonenumber" maxlength="11" /></el-form-item><el-form-item :label="$t('profile.email')" prop="email"><el-input v-model="form.email" maxlength="50" /></el-form-item><el-form-item :label="$t('profile.sex')"><el-radio-group v-model="form.sex"><el-radio label="0">{{ $t('profile.men') }}</el-radio><el-radio label="1">{{ $t('profile.women') }}</el-radio></el-radio-group></el-form-item><el-form-item><el-button type="primary" size="mini" @click="submit">{{ $t('common.save') }}</el-button><el-button type="danger" size="mini" @click="close">{{ $t('common.close') }}</el-button></el-form-item></el-form>
</template>
src\views\system\user\profile\resetPwd.vue 修改<template>部分即可
<template><el-form ref="form" :model="user" :rules="rules" label-width="80px"><el-form-item :label="$t('profile.oldPassword')" prop="oldPassword"><el-input v-model="user.oldPassword" :placeholder="$t('profile.oldPassword_placeholder')" type="password" show-password/></el-form-item><el-form-item :label="$t('profile.newPassword')" prop="newPassword"><el-input v-model="user.newPassword" :placeholder="$t('profile.newPassword_placeholder')" type="password" show-password/></el-form-item><el-form-item :label="$t('profile.confirmPassword')" prop="confirmPassword"><el-input v-model="user.confirmPassword" :placeholder="$t('profile.confirmPassword_placeholder')" type="password" show-password/></el-form-item><el-form-item><el-button type="primary" size="mini" @click="submit">{{ $t('common.save') }}</el-button><el-button type="danger" size="mini" @click="close">{{ $t('common.close') }}</el-button></el-form-item></el-form>
</template>
其他国际化就不一 一展示了,业务代码参照个人中心页面的国际化,对文字部分进行替换就行
语言包
zh.js
export default {login: {title: '管理系统登录',logIn: '登录',username: '账号',password: '密码',code: '验证码',rememberMe: '记住密码'},tagsView: {refresh: '刷新页面',close: '关闭当前',closeRight: '关闭右侧',closeLeft: '关闭左侧',closeOthers: '关闭其它',closeAll: '全部关闭'},common: {confirm: '确定',cancle: '取消',save: '保 存',submit: '提 交',approve2: '审 批',reject: '驳 回',close: '关闭',option: '操作',add: '新增',add2: '添加',edit: '修改',delete: '删除',export: '导出',import: '导入',approve: '审核',search: '搜索',reset: '重置',date: '日期',select_placeholder: '请选择',startTime_placeholder: '开始时间',endTime_placeholder: '结束时间',startDate_placeholder: '开始日期',endDate_placeholder: '结束日期',index: '序号',attachment: '附件',attachment_select: '选取文件',tip: '提示'},menus: {system: '系统管理',monitor: '系统监控',},route: {home: '首页',personalCenter: '个人中心',changrole: '分配角色',changuser: '分配用户',data: '字典数据',jobLog: '调度日志',genEdit: '修改生成配置',},settings: {title: '主题风格设置',theme: '主题颜色',configuration: '系统布局配置',topNav: '开启 TopNav',tagsView: '开启 Tags-View',fixedHeader: '固定 Header',sidebarLogo: '侧边栏 Logo',dynamicTitle: '动态标题',saveSetting: '保存配置',resetSetting: '重置配置'},navbar: {personalCenter: '个人中心',layoutSettings: '布局设置',logOut: '退出登录',layoutSize: '布局大小',confirmText: '确定注销并退出系统吗?',switchLanguageSuccess: '设置语言成功',},profile: {personalInformation: '个人信息',basicInformation: '基本资料',userName: '用户账号',phonenumber: '手机号码',email: '用户邮箱',dept: '所属部门',roleGroup: '所属角色',createTime: '创建日期',resetPwd: '修改密码',nickName: '中文名',sex: '性别',men: '男',women: '女',oldPassword: '旧密码',newPassword: '新密码',confirmPassword: '确认密码',oldPassword_placeholder: '请输入旧密码',newPassword_placeholder: '请输入新密码',confirmPassword_placeholder: '请确认新密码',},
}
en.js
export default {login: {title: 'Management System',logIn: 'Login in',username: 'Username',password: 'Password',code: 'Code',rememberMe: 'Remember Me'},tagsView: {refresh: 'Refresh',close: 'Close',closeRight: 'Close Right',closeLeft: 'Close Left',closeOthers: 'Close Others',closeAll: 'Close All'},common: {confirm: 'Confirm',cancle: 'Cancle',save: 'Save',submit: 'Submit',close: 'Close',approve2: 'Approve',reject: 'Reject',option: 'Option',add: 'Add',add2: 'Add',edit: 'Edit',delete: 'Delete',export: 'Export',import: 'Import',approve: 'Approve',search: 'Search',reset: 'Reset',date: 'Date',select_placeholder: 'Please select',startTime_placeholder: 'StartTime',endTime_placeholder: 'EndTime',startDate_placeholder: 'StartDate',endDate_placeholder: 'EndDate',index: 'Index',attachment: 'Attachment',attachment_select: 'Select',tip: 'Tip'},menus: {system: 'System Management',monitor: 'System Monitoring',},route: {home: 'Home',personalCenter: 'Personal Center',changrole: 'Assign Roles',changuser: 'Assign Users',data: 'Dictionary Data',jobLog: 'Dispatching Log',genEdit: 'Modify the generation configuration',},settings: {title: 'Theme Style Setting',theme: 'Theme Color',configuration: 'System Layout Configuration',topNav: 'Open TopNav',tagsView: 'Open Tags-View',fixedHeader: 'Fixed Header',sidebarLogo: 'Sidebar Logo',dynamicTitle: 'Dynamic Title',saveSetting: 'Save',resetSetting: 'Reset'},navbar: {personalCenter: 'Personal Center',layoutSettings: 'Layout Settings',logOut: 'Log Out',layoutSize: 'Layout Size',confirmText: 'Are you sure to log out and exit the system?',switchLanguageSuccess: 'Language Setting Successful',},profile: {personalInformation: 'Personal Information',basicInformation: 'Basic Information',userName: 'User Name',phonenumber: 'Phone',email: 'Email',dept: 'Dept',roleGroup: 'Role',createTime: 'Create Time',resetPwd: 'Reset Password',nickName: 'Name',sex: 'Sex',men: 'Men',women: 'Women',oldPassword: 'Old',newPassword: 'New',confirmPassword: 'Confirm',oldPassword_placeholder: 'Please enter old password',newPassword_placeholder: 'Please enter a new password',confirmPassword_placeholder: 'Please confirm the new password',},
}