需求:后台管理系统,可以实现语言切换
实现过程:用的i18n来实现的语言切换,网上能看到好多模板,根据自己的需求,修改一下即可使用,大概都是差不多的,因为涉及到后端,所以要跟后端协商一致决定去写,我的设计思路是跟着后端设计更改的,如下:
1.语言是后端接口返回的,不是前端写死的(eg:中文、English),由于我的切换语言,设计到了两个地方,一个是登录页面,一个是登录之后的页面,后端不能给一个接口,要区分两个接口给前端,所以关于这个需求,前端自己加了判断(如果你们后端给一个接口,则可忽略我写的判断)
2.根据自己选择的哪一种语言,需要通过接口传给后端,后端会将其存到某个用户表里,这个接口也是两个,也需写判断
3.如果用户是从来没有选择过语言的用户,则前端需要规定默认语言,且要与后端的默认语言保持一致,于是和后端协商一致决定,其默认语言是后端返回的语言的第一个
4.如果后端有选择是哪个语言,我们将其传给了后端,后端讲其存入了用户表,用户登录之后,会在用户接口里,返回给我们,我们将其存 localStorage,这时候,就算用户再次退出到登录页面,我们就可以将用户默认语言做一个判断,判断存入localStorage的language是否有值,如果有值,则登录页面的语言取localStorage的language,如果没有,则还是取后端语言接口返回的第一个值,这样,就可以把用户已经选择过的语言,在登录页面也能进行判断用户习惯
文档: vue-i18n
一.安装vue-i18n
npm install vue-i18n
二.在mian.js引入
//i18n
import i18n from "@/lang";
Vue.use(Element,{
size:Cookies.get('size') || 'small',
i18n:(key,value)=>i18n.t(key,value)
})
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})
三.在src下创建一个lang文件夹,其中包括en.js、zh.js、index.js文件
1.src/lang/en.js
export default {
login: {
username:'username',//用户名
password:'password',//密码
code:'code',//验证码
login:'login',//登录
logging: 'logging...',//登录...
storage:'remember the password',//记住密码
},
home:{
welcome:'Welcome to use',//欢迎使用
},
route:{
homepage:'Home page',//首页
profile:'Profile',//个人中心
系统管理:"system manage",//系统管理
系统监控:'system monitor',
系统工具:"system tools",
创建运单:"create waybill",
批量运单:'waybill batch',
运单管理:"waybill manage",
审核管理:'audits manage',
充值管理:'recharge manage',
失败订单详情:'failed order details',
运费管理:'freight manage',
账单管理:'bill manage',
仓库管理:'warehouse manage',
用户管理:'user query',
角色管理:'user query',
菜单管理:'menu manage',
部门管理:'dept manage',
岗位管理:'position manage',
字典管理:'dictionary manage',
参数设置:'parameter settings',
通知公告:'notice announcent',
日志管理:'log manage',
操作日志:'operation log',
登录日志:'login log',
在线用户:'online users',
定时任务:'scheduled tasks',
数据监控:'data monitor',
服务监控:'service monitor',
运单审核:'audits order',
充值审核:'audits recharge',
客户充值:'customer recharge',
我的充值:'my recharge',
基础运费:'basic freight',
其他附加费:'other surcharges',
燃油附加费:'fuel surcharge',
旺季附加费:'peak surcharge',
全部账单:'bill whole',
我的账单:'bill my',
订单详情:'order details',
仓库配置:'warehouse configuration',
基础成本价:'basic cost',
其他成本价:'other cost',
旺季成本价:'peak cost',
查询:'query',
新增:'add',
修改:'alter',
导入:'import',
导出:'export',
删除:'remove',
重置密码:'reset passwords',
仓库:'warehouse',
批量强退:'batch forcing',
单条强退:'single strong back',
众拓网通官网:'Zhongtuo Netcom official website',
下载失败订单:'download failed order',
下载面单:'download sheet',
客户查询:'customer query',
已取消导出:'export cancelled',
取消审核中导出:'export cancel audit',
审核:'audit',
审核平账:'audit and balance accounts',
审核平账明细:'review the balance of accounts details',
充值:'top up',
充值记录:'recharge record',
消费记录:'expense calendar',
补交凭证:'resubmit documents',
回显:'echo',
审核明细:'audit detail',
充值明细:'top-up details',
失败订单详情查询:'failed order details query',
复制:'copy',
基础运费列表:'base freight list',
客户列表:'customer list',
},
}
2.src/lang/zh.js
export default {
login: {
username:'用户名',//用户名
password:'密码',//密码
code:'验证码',//验证码
login:'登录',//登录
logging: '登录...',//登录...
storage:'记住密码',//记住密码
},
home:{
welcome:'欢迎使用',//欢迎使用
},
route:{
homepage:'首页',//首页
profile:'个人中心',//个人中心
系统管理:"系统管理",//系统管理
系统监控:'系统监控',
系统工具:"系统工具",
创建运单:"创建运单",
批量运单:'批量运单',
运单管理:"运单管理",
审核管理:'审核管理',
充值管理:'充值管理',
失败订单详情:'失败订单详情',
运费管理:'运费管理',
账单管理:'账单管理',
仓库管理:'仓库管理',
用户管理:'用户管理',
角色管理:'角色管理',
菜单管理:'菜单管理',
部门管理:'部门管理',
岗位管理:'岗位管理',
字典管理:'字典管理',
参数设置:'参数设置',
通知公告:'通知公告',
日志管理:'日志管理',
操作日志:'操作日志',
登录日志:'登录日志',
在线用户:'在线用户',
定时任务:'定时任务',
数据监控:'数据监控',
服务监控:'服务监控',
运单审核:'运单审核',
充值审核:'充值审核',
客户充值:'客户充值',
我的充值:'我的充值',
基础运费:'基础运费',
其他附加费:'其他附加费',
燃油附加费:'燃油附加费',
旺季附加费:'旺季附加费',
全部账单:'全部账单',
我的账单:'我的账单',
订单详情:'订单详情',
仓库配置:'仓库配置',
基础成本价:'基础成本价',
其他成本价:'其他成本价',
旺季成本价:'旺季成本价',
查询:'查询',
新增:'新增',
修改:'修改',
导入:'导入',
导出:'导出',
删除:'删除',
重置密码:'重置密码',
仓库:'仓库',
批量强退:'批量强退',
单条强退:'单条强退',
众拓网通官网:'众拓网通官网',
下载失败订单:'下载失败订单',
下载面单:'下载面单',
客户查询:'客户查询',
已取消导出:'已取消导出',
取消审核中导出:'取消审核中导出',
审核:'审核',
审核平账:'审核平账',
审核平账明细:'审核平账明细',
充值:'充值',
充值记录:'充值记录',
消费记录:'消费记录',
补交凭证:'补交凭证s',
回显:'回显',
审核明细:'审核明细',
充值明细:'充值明细',
失败订单详情查询:'失败订单详情查询',
复制:'复制',
基础运费列表:'基础运费列表',
客户列表:'客户列表',
},
}
3.src/lang/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: {
...enLocale,
...elementEnLocale
},
zh: {
...zhLocale,
...elementZhLocale
}
}
export function getLanguage() {
const chooseLanguage = Cookies.get('language')
if (chooseLanguage) return chooseLanguage
return 'en'
}
const i18n = new VueI18n({
locale: getLanguage(),
messages
})
export default i18n
三.在src/components下创建到LangSelect/index.vue文件(语言切换组件)
<script src="../../api/login.js"></script>
<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 v-for="item in languagelist" :disabled="$i18n.locale===item.language" :command={languageId:item.languageId,language:item.language}>
{{item.name}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
import {checkLanguage, languagelist} from "@/api/menu";
import item from "@/layout/components/Sidebar/Item.vue";
import {languagelistt,checkLanguagee} from '@/api/login'
export default {
props:{
//标记他是那个页面过来的,是logo页面,还是登录之后的主页面
orientation:{
type:Number,
default:0
}
},
data(){
return{
languagelist:[],
lang:null,
}
},
computed: {
item() {
return item
},
language() {
return this.$store.getters.language
}
},
created() {
this.getlanguelist()
},
methods: {
//这个是获取后端返回的语言接口
getlanguelist(){
//login页面语言接口,接口有两个,但是接口性质是一样的,都是返回语言接口,只不过是一个在登录页面的,一个是登录之后主页面的(如果你们后端返回一个接口,九不用去写这个判断了)
if(this.orientation==1){
languagelistt().then((res)=>{
this.languagelist=res.data
//拿取user接口存的language,如果有就拿user存的language,如果没有就取后端接口返回的第一个
const language=localStorage.getItem('language') ||res.data[0].language
this.$store.dispatch('app/setLanguage', language)
this.$i18n.locale=language
})
}else {
//主页面语言接口
languagelist().then((res)=>{
this.languagelist=res.data
const language=localStorage.getItem('language')
this.$store.dispatch('app/setLanguage', language)
this.$i18n.locale=localStorage.getItem('language')
})
}
},
handleSetLanguage(lang) {//点击切换事件
this.$i18n.locale = lang.language
this.$store.dispatch('app/setLanguage', lang.language)
this.checkLanguage(lang.languageId)
this.$message({
message: 'Switch Language Success',
type: 'success'
})
},
//传给后端,后端标记是什么语言
checkLanguage(item){
//login页面
if(this.orientation==1){
checkLanguagee(item).then((res)=>{
// 传值给前端,前端再把获取到的值,传到login接口里面
this.$emit("languageid",res.data.languageId)
})
}else {
//主页面
checkLanguage(item).then((res)=>{
this.updatePermission()
})
}
},
}
}
</script>
四.在src/store/modules/app.js 里将language存入vuex和cookie
import Cookies from 'js-cookie'
import { getLanguage } from '@/lang/index'
const state = {
sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
withoutAnimation: false,
hide: false
},
device: 'desktop',
language: getLanguage(),
size: Cookies.get('size') || 'medium'
}
const mutations = {
TOGGLE_SIDEBAR: state => {
if (state.sidebar.hide) {
return false;
}
state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 0)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
//语言
SET_LANGUAGE: (state, language) => {
state.language = language
Cookies.set('language', language)
},
SET_SIZE: (state, size) => {
state.size = size
Cookies.set('size', size)
},
SET_SIDEBAR_HIDE: (state, status) => {
state.sidebar.hide = status
}
}
const actions = {
toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR')
},
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
//语言
setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language)
},
setSize({ commit }, size) {
commit('SET_SIZE', size)
},
toggleSideBarHide({ commit }, status) {
commit('SET_SIDEBAR_HIDE', status)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
五.src/modules/user.js,将后端用户信息接口返回的language存入localStorage里
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: [],
permissions: [],
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
},
},
actions: {
// 登录
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
//语言
const languageId=userInfo.languageId
return new Promise((resolve, reject) => {
login(username, password, code, uuid,languageId).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(res => {
const user = res.user
const avatar = user.avatar == "" ? require("@/assets/image/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', res.roles)
commit('SET_PERMISSIONS', res.permissions)
sessionStorage.setItem('infoCustomers',JSON.stringify(res.customers))
sessionStorage.setItem('infouser',JSON.stringify(res.user.roles))
//注:用户已进入页面,调用到用户接口,将用户接口里面的语言存储到localStorage,可以方便用户在登录页面的时候判断语言是中文还是英文(登录页面语言应与user接口保持一直)
localStorage.setItem('language',JSON.stringify(res.user.language.language).replace(/\"/g, ""))
} else {
commit('SET_ROLES', ['ROLE_DEFAULT'])
}
commit('SET_NAME', user.userName)
commit('SET_AVATAR', avatar)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 退出系统
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}
export default user
六.登录页面
效果图:
1.en:
2.English:
<template>
<div class="login">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
<div class="titlebox">
<h3 class="title">{{setName}}</h3>
<lang-select class="set-language" @languageid="handlelanguage" :orientation='orientation'/>
</div>
<el-form-item prop="username">
<el-input v-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-input
v-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">
<el-input
v-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.storage')}}</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>{{$t('login.logging')}}</span>
</el-button>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2021-2022 xxx All Rights Reserved 增值电信业务经营许可证:<a
href="http://beian.miit.gov.cn" target="_blank">xxxxx</a></span>
</div>
</div>
</template>
<script>
import {getCodeImg, login} from "@/api/login";
import Cookies from "js-cookie";
import {decrypt, encrypt} from '@/utils/jsencrypt'
import langSelect from "@/components/LangSelect/index.vue";
import {config} from "@/api/menu";
import {mapGetters, mapState} from "vuex";
export default {
name: "Login",
components:{
langSelect
},
data() {
return {
orientation:1,
codeUrl: "",
cookiePassword: "",
loginForm: {
username: "admin",
password: "",
rememberMe: false,
code: "",
uuid: "",
languageId:"",
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: this.$t('rules.rulesusername') }
],
password: [
{ required: true, trigger: "blur", message: this.$t('rules.rulespassword') }
],
code: [{ required: true, trigger: "change", message: this.$t('rules.rulescode') }]
},
loading: false,
redirect: undefined,
title:"",
};
},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect;
},
immediate: true
},
//监听语言的改变,实时改变校验
language(newVal, oldVal){
this.loginRules={
username: [
{ required: true, trigger: "blur", message: this.$t('rules.rulesusername') }
],
password: [
{ required: true, trigger: "blur", message: this.$t('rules.rulespassword') }
],
code: [{ required: true, trigger: "change", message: this.$t('rules.rulescode') }]
}
}
},
created() {
this.getCode();
this.getCookie();
this.getproject()
},
computed:{
...mapGetters(['getSetName']),
setName() {
return this.getSetName || this.title
},
...mapState({
language: state => state.app.language
})
},
methods: {
//获取项目名字接口
getproject(){
config().then((res)=>{
this.title=res.msg
})
},
login,
handlelanguage(name){
this.loginForm.languageId=name
},
//获取验证码
getCode() {
getCodeImg().then(res => {
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(() => {
this.loading = false;
this.getCode();
});
}
});
}
}
};
</script>
七.在utils里面新建一个i18n.js
export function generateTitle(title) {
const hasKey = this.$te('route.' + title)
if (hasKey) {
// $t :this method from vue-i18n, inject in @/lang/index.js
const translatedTitle = this.$t('route.' + title)
return translatedTitle
}
return title
}
七.根据语言的切换,面包屑的语言也随之更新,src/components/Breadcrumb文件里
1.zh:
2.en:
<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(`route.${item.meta.title}`) }}-->
{{generateTitle(item.meta.title)}}
</span>
<a v-else @click.prevent="handleLink(item)">
<!-- {{ $t(`route.${item.meta.title}`)}}-->
{{generateTitle(item.meta.title)}}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
import { generateTitle } from '@/utils/i18n'
export default {
inject:['reload'],
data() {
return {
levelList: null
}
},
watch: {
$route(route) {
// if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) {
return
}
this.getBreadcrumb()
},
},
created() {
this.getBreadcrumb()
},
methods: {
generateTitle,
getBreadcrumb() {
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!this.isDashboard(first)) {
matched = [{ path: '/index', meta: { title: 'homepage' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
return name.trim() === 'Home page'
},
pathCompile(path) {
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(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>
八.src\layout\components\Sidebar\SidebarItem.vue文件
1.zh
2.en
我用的是若依后台管理系统框架,对于路由是后端接口返回的,所以,路由语言切换只有两种实现方式 ,后端接口返回,缺点,每次新增修改路由,后端都要在数据库里面新增、修改两种语言,较为麻烦,所以路由的语言切换就纯前端进行控制。
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="generateTitle(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="generateTitle(item.meta.title)" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template>
<script>
import path from 'path'
import { generateTitle } from '@/utils/i18n'
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 object
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ''
}
},
data() {
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => {
if (item.hidden) {
return false
} else {
// Temp set(will be used if only has one showing child)
this.onlyOneChild = item
return true
}
})
// When there is only one child router, the child router is displayed by default
if (showingChildren.length === 1) {
return true
}
// Show parent if there are no child router to display
if (showingChildren.length === 0) {
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return true
}
return false
},
resolvePath(routePath) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
},
generateTitle
}
}
</script>
需要注意的是,对于首页,是前端写的路由,其余的页面路由都是后端接口返回的,所以在前端,针对首页,需要改的是router/index.js
九.菜单管理,和路由同理,都是后端接口返回,接口返回的语言切换,纯前端处理
1.zh
2.en
<script>
computed: {
...mapState({
language: state => state.app.language
})
},
watch:{
language(newVal, oldVal){
this.getList()
}
},
methods:{
getList() {
this.loading = true;
listMenu(this.queryParams).then(response => {
this.menuList = this.handleTree(response.data, "menuId").map(item=>{
const newItem= {
...item,
menuName:this.$t(`route.${item.menuName}`)
}
if (newItem.children && newItem.children.length > 0) {
newItem.children = this.translateChildren(newItem.children);
}
return newItem;
});
this.loading = false;
});
},
translateChildren(children) {
return children.map(item => {
const newItem = {
...item,
menuName: this.$t(`route.${item.menuName}`),
};
if (newItem.children && newItem.children.length > 0) {
newItem.children = this.translateChildren(newItem.children);
}
return newItem;
});
},
}
</script>
十.样式处理,可能中文的长度很短,英文长度很长,样式就会错乱,对于这种情况进行处理
<el-dialog :title="title" :visible.sync="open" width="850px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" :label-width="getLabelWidth()">
<el-row>
<el-col :span="12">
<!-- 公告标题-->
<el-form-item :label="$t('notice.announcementtitle')" prop="noticeTitle" >
<el-input v-model="form.noticeTitle" :placeholder="$t('notice.titleipt')"
style="width: 260px"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">{{$t('operation.confirm')}}</el-button>
<el-button @click="cancel">{{$t('operation.cancel')}}</el-button>
</div>
</el-dialog>
getLabelWidth(){
let labelWidth = '80px';
if(this.language=='en'){
labelWidth = '150px';
}
return labelWidth;
},
十一.对于校验的地方,语言切换之后,并不会再次触发校验的语言切换 ,关于这个点,我是进行了监听,语言是否改变,如果语言改变了,我就再次触发校验,缺点,每次切换语言就会出发校验,并不是,我点击了某个提交按钮,进行的校验触发,例如我上面写的登录页面上的校验
以上就是整个实现过程啦,可能有点地方忘记写了,等想到了,再进行完善