需求:后臺(tái)管理系統(tǒng),可以實(shí)現(xiàn)語(yǔ)言切換
實(shí)現(xiàn)過(guò)程:用的i18n來(lái)實(shí)現(xiàn)的語(yǔ)言切換,網(wǎng)上能看到好多模板,根據(jù)自己的需求,修改一下即可使用,大概都是差不多的,因?yàn)樯婕暗胶蠖?,所以要跟后端協(xié)商一致決定去寫(xiě),我的設(shè)計(jì)思路是跟著后端設(shè)計(jì)更改的,如下:
1.語(yǔ)言是后端接口返回的,不是前端寫(xiě)死的(eg:中文、English),由于我的切換語(yǔ)言,設(shè)計(jì)到了兩個(gè)地方,一個(gè)是登錄頁(yè)面,一個(gè)是登錄之后的頁(yè)面,后端不能給一個(gè)接口,要區(qū)分兩個(gè)接口給前端,所以關(guān)于這個(gè)需求,前端自己加了判斷(如果你們后端給一個(gè)接口,則可忽略我寫(xiě)的判斷)
2.根據(jù)自己選擇的哪一種語(yǔ)言,需要通過(guò)接口傳給后端,后端會(huì)將其存到某個(gè)用戶表里,這個(gè)接口也是兩個(gè),也需寫(xiě)判斷
3.如果用戶是從來(lái)沒(méi)有選擇過(guò)語(yǔ)言的用戶,則前端需要規(guī)定默認(rèn)語(yǔ)言,且要與后端的默認(rèn)語(yǔ)言保持一致,于是和后端協(xié)商一致決定,其默認(rèn)語(yǔ)言是后端返回的語(yǔ)言的第一個(gè)
4.如果后端有選擇是哪個(gè)語(yǔ)言,我們將其傳給了后端,后端講其存入了用戶表,用戶登錄之后,會(huì)在用戶接口里,返回給我們,我們將其存 localStorage,這時(shí)候,就算用戶再次退出到登錄頁(yè)面,我們就可以將用戶默認(rèn)語(yǔ)言做一個(gè)判斷,判斷存入localStorage的language是否有值,如果有值,則登錄頁(yè)面的語(yǔ)言取localStorage的language,如果沒(méi)有,則還是取后端語(yǔ)言接口返回的第一個(gè)值,這樣,就可以把用戶已經(jīng)選擇過(guò)的語(yǔ)言,在登錄頁(yè)面也能進(jìn)行判斷用戶習(xí)慣
文檔:?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下創(chuàng)建一個(gè)lang文件夾,其中包括en.js、zh.js、index.js文件
1.src/lang/en.js
export default {
login: {
username:'username',//用戶名
password:'password',//密碼
code:'code',//驗(yàn)證碼
login:'login',//登錄
logging: 'logging...',//登錄...
storage:'remember the password',//記住密碼
},
home:{
welcome:'Welcome to use',//歡迎使用
},
route:{
homepage:'Home page',//首頁(yè)
profile:'Profile',//個(gè)人中心
系統(tǒng)管理:"system manage",//系統(tǒng)管理
系統(tǒng)監(jiān)控:'system monitor',
系統(tǒng)工具:"system tools",
創(chuàng)建運(yùn)單:"create waybill",
批量運(yùn)單:'waybill batch',
運(yùn)單管理:"waybill manage",
審核管理:'audits manage',
充值管理:'recharge manage',
失敗訂單詳情:'failed order details',
運(yùn)費(fèi)管理:'freight manage',
賬單管理:'bill manage',
倉(cāng)庫(kù)管理:'warehouse manage',
用戶管理:'user query',
角色管理:'user query',
菜單管理:'menu manage',
部門(mén)管理:'dept manage',
崗位管理:'position manage',
字典管理:'dictionary manage',
參數(shù)設(shè)置:'parameter settings',
通知公告:'notice announcent',
日志管理:'log manage',
操作日志:'operation log',
登錄日志:'login log',
在線用戶:'online users',
定時(shí)任務(wù):'scheduled tasks',
數(shù)據(jù)監(jiān)控:'data monitor',
服務(wù)監(jiān)控:'service monitor',
運(yùn)單審核:'audits order',
充值審核:'audits recharge',
客戶充值:'customer recharge',
我的充值:'my recharge',
基礎(chǔ)運(yùn)費(fèi):'basic freight',
其他附加費(fèi):'other surcharges',
燃油附加費(fèi):'fuel surcharge',
旺季附加費(fèi):'peak surcharge',
全部賬單:'bill whole',
我的賬單:'bill my',
訂單詳情:'order details',
倉(cāng)庫(kù)配置:'warehouse configuration',
基礎(chǔ)成本價(jià):'basic cost',
其他成本價(jià):'other cost',
旺季成本價(jià):'peak cost',
查詢:'query',
新增:'add',
修改:'alter',
導(dǎo)入:'import',
導(dǎo)出:'export',
刪除:'remove',
重置密碼:'reset passwords',
倉(cāng)庫(kù):'warehouse',
批量強(qiáng)退:'batch forcing',
單條強(qiáng)退:'single strong back',
眾拓網(wǎng)通官網(wǎng):'Zhongtuo Netcom official website',
下載失敗訂單:'download failed order',
下載面單:'download sheet',
客戶查詢:'customer query',
已取消導(dǎo)出:'export cancelled',
取消審核中導(dǎo)出:'export cancel audit',
審核:'audit',
審核平賬:'audit and balance accounts',
審核平賬明細(xì):'review the balance of accounts details',
充值:'top up',
充值記錄:'recharge record',
消費(fèi)記錄:'expense calendar',
補(bǔ)交憑證:'resubmit documents',
回顯:'echo',
審核明細(xì):'audit detail',
充值明細(xì):'top-up details',
失敗訂單詳情查詢:'failed order details query',
復(fù)制:'copy',
基礎(chǔ)運(yùn)費(fèi)列表:'base freight list',
客戶列表:'customer list',
},
}
?2.src/lang/zh.js
export default {
login: {
username:'用戶名',//用戶名
password:'密碼',//密碼
code:'驗(yàn)證碼',//驗(yàn)證碼
login:'登錄',//登錄
logging: '登錄...',//登錄...
storage:'記住密碼',//記住密碼
},
home:{
welcome:'歡迎使用',//歡迎使用
},
route:{
homepage:'首頁(yè)',//首頁(yè)
profile:'個(gè)人中心',//個(gè)人中心
系統(tǒng)管理:"系統(tǒng)管理",//系統(tǒng)管理
系統(tǒng)監(jiān)控:'系統(tǒng)監(jiān)控',
系統(tǒng)工具:"系統(tǒng)工具",
創(chuàng)建運(yùn)單:"創(chuàng)建運(yùn)單",
批量運(yùn)單:'批量運(yùn)單',
運(yùn)單管理:"運(yùn)單管理",
審核管理:'審核管理',
充值管理:'充值管理',
失敗訂單詳情:'失敗訂單詳情',
運(yùn)費(fèi)管理:'運(yùn)費(fèi)管理',
賬單管理:'賬單管理',
倉(cāng)庫(kù)管理:'倉(cāng)庫(kù)管理',
用戶管理:'用戶管理',
角色管理:'角色管理',
菜單管理:'菜單管理',
部門(mén)管理:'部門(mén)管理',
崗位管理:'崗位管理',
字典管理:'字典管理',
參數(shù)設(shè)置:'參數(shù)設(shè)置',
通知公告:'通知公告',
日志管理:'日志管理',
操作日志:'操作日志',
登錄日志:'登錄日志',
在線用戶:'在線用戶',
定時(shí)任務(wù):'定時(shí)任務(wù)',
數(shù)據(jù)監(jiān)控:'數(shù)據(jù)監(jiān)控',
服務(wù)監(jiān)控:'服務(wù)監(jiān)控',
運(yùn)單審核:'運(yùn)單審核',
充值審核:'充值審核',
客戶充值:'客戶充值',
我的充值:'我的充值',
基礎(chǔ)運(yùn)費(fèi):'基礎(chǔ)運(yùn)費(fèi)',
其他附加費(fèi):'其他附加費(fèi)',
燃油附加費(fèi):'燃油附加費(fèi)',
旺季附加費(fèi):'旺季附加費(fèi)',
全部賬單:'全部賬單',
我的賬單:'我的賬單',
訂單詳情:'訂單詳情',
倉(cāng)庫(kù)配置:'倉(cāng)庫(kù)配置',
基礎(chǔ)成本價(jià):'基礎(chǔ)成本價(jià)',
其他成本價(jià):'其他成本價(jià)',
旺季成本價(jià):'旺季成本價(jià)',
查詢:'查詢',
新增:'新增',
修改:'修改',
導(dǎo)入:'導(dǎo)入',
導(dǎo)出:'導(dǎo)出',
刪除:'刪除',
重置密碼:'重置密碼',
倉(cāng)庫(kù):'倉(cāng)庫(kù)',
批量強(qiáng)退:'批量強(qiáng)退',
單條強(qiáng)退:'單條強(qiáng)退',
眾拓網(wǎng)通官網(wǎng):'眾拓網(wǎng)通官網(wǎng)',
下載失敗訂單:'下載失敗訂單',
下載面單:'下載面單',
客戶查詢:'客戶查詢',
已取消導(dǎo)出:'已取消導(dǎo)出',
取消審核中導(dǎo)出:'取消審核中導(dǎo)出',
審核:'審核',
審核平賬:'審核平賬',
審核平賬明細(xì):'審核平賬明細(xì)',
充值:'充值',
充值記錄:'充值記錄',
消費(fèi)記錄:'消費(fèi)記錄',
補(bǔ)交憑證:'補(bǔ)交憑證s',
回顯:'回顯',
審核明細(xì):'審核明細(xì)',
充值明細(xì):'充值明細(xì)',
失敗訂單詳情查詢:'失敗訂單詳情查詢',
復(fù)制:'復(fù)制',
基礎(chǔ)運(yùn)費(fèi)列表:'基礎(chǔ)運(yùn)費(fèi)列表',
客戶列表:'客戶列表',
},
}
??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下創(chuàng)建到LangSelect/index.vue文件(語(yǔ)言切換組件)
<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:{
//標(biāo)記他是那個(gè)頁(yè)面過(guò)來(lái)的,是logo頁(yè)面,還是登錄之后的主頁(yè)面
orientation:{
type:Number,
default:0
}
},
data(){
return{
languagelist:[],
lang:null,
}
},
computed: {
item() {
return item
},
language() {
return this.$store.getters.language
}
},
created() {
this.getlanguelist()
},
methods: {
//這個(gè)是獲取后端返回的語(yǔ)言接口
getlanguelist(){
//login頁(yè)面語(yǔ)言接口,接口有兩個(gè),但是接口性質(zhì)是一樣的,都是返回語(yǔ)言接口,只不過(guò)是一個(gè)在登錄頁(yè)面的,一個(gè)是登錄之后主頁(yè)面的(如果你們后端返回一個(gè)接口,九不用去寫(xiě)這個(gè)判斷了)
if(this.orientation==1){
languagelistt().then((res)=>{
this.languagelist=res.data
//拿取user接口存的language,如果有就拿user存的language,如果沒(méi)有就取后端接口返回的第一個(gè)
const language=localStorage.getItem('language') ||res.data[0].language
this.$store.dispatch('app/setLanguage', language)
this.$i18n.locale=language
})
}else {
//主頁(yè)面語(yǔ)言接口
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) {//點(diǎn)擊切換事件
this.$i18n.locale = lang.language
this.$store.dispatch('app/setLanguage', lang.language)
this.checkLanguage(lang.languageId)
this.$message({
message: 'Switch Language Success',
type: 'success'
})
},
//傳給后端,后端標(biāo)記是什么語(yǔ)言
checkLanguage(item){
//login頁(yè)面
if(this.orientation==1){
checkLanguagee(item).then((res)=>{
// 傳值給前端,前端再把獲取到的值,傳到login接口里面
this.$emit("languageid",res.data.languageId)
})
}else {
//主頁(yè)面
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
},
//語(yǔ)言
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)
},
//語(yǔ)言
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
//語(yǔ)言
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) { // 驗(yàn)證返回的roles是否是一個(gè)非空數(shù)組
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))
//注:用戶已進(jìn)入頁(yè)面,調(diào)用到用戶接口,將用戶接口里面的語(yǔ)言存儲(chǔ)到localStorage,可以方便用戶在登錄頁(yè)面的時(shí)候判斷語(yǔ)言是中文還是英文(登錄頁(yè)面語(yǔ)言應(yīng)與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)
})
})
},
// 退出系統(tǒng)
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
?六.登錄頁(yè)面
?效果圖:
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 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證:<a
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
},
//監(jiān)聽(tīng)語(yǔ)言的改變,實(shí)時(shí)改變校驗(yàn)
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: {
//獲取項(xiàng)目名字接口
getproject(){
config().then((res)=>{
this.title=res.msg
})
},
login,
handlelanguage(name){
this.loginForm.languageId=name
},
//獲取驗(yàn)證碼
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里面新建一個(gè)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
}
?七.根據(jù)語(yǔ)言的切換,面包屑的語(yǔ)言也隨之更新,src/components/Breadcrumb文件里
1.zh:?
?
2.en:?
?
<template>
<!-- 這個(gè)是上面的面包屑-->
<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
我用的是若依后臺(tái)管理系統(tǒng)框架,對(duì)于路由是后端接口返回的,所以,路由語(yǔ)言切換只有兩種實(shí)現(xiàn)方式?,后端接口返回,缺點(diǎn),每次新增修改路由,后端都要在數(shù)據(jù)庫(kù)里面新增、修改兩種語(yǔ)言,較為麻煩,所以路由的語(yǔ)言切換就純前端進(jìn)行控制。
<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>
?需要注意的是,對(duì)于首頁(yè),是前端寫(xiě)的路由,其余的頁(yè)面路由都是后端接口返回的,所以在前端,針對(duì)首頁(yè),需要改的是router/index.js
九.菜單管理,和路由同理,都是后端接口返回,接口返回的語(yǔ)言切換,純前端處理
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>
?十.樣式處理,可能中文的長(zhǎng)度很短,英文長(zhǎng)度很長(zhǎng),樣式就會(huì)錯(cuò)亂,對(duì)于這種情況進(jìn)行處理
<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">
<!-- 公告標(biāo)題-->
<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;
},
十一.對(duì)于校驗(yàn)的地方,語(yǔ)言切換之后,并不會(huì)再次觸發(fā)校驗(yàn)的語(yǔ)言切換?,關(guān)于這個(gè)點(diǎn),我是進(jìn)行了監(jiān)聽(tīng),語(yǔ)言是否改變,如果語(yǔ)言改變了,我就再次觸發(fā)校驗(yàn),缺點(diǎn),每次切換語(yǔ)言就會(huì)出發(fā)校驗(yàn),并不是,我點(diǎn)擊了某個(gè)提交按鈕,進(jìn)行的校驗(yàn)觸發(fā),例如我上面寫(xiě)的登錄頁(yè)面上的校驗(yàn)
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-518912.html
?以上就是整個(gè)實(shí)現(xiàn)過(guò)程啦,可能有點(diǎn)地方忘記寫(xiě)了,等想到了,再進(jìn)行完善文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-518912.html
到了這里,關(guān)于vue-i18n 實(shí)現(xiàn)國(guó)際化,支持切換不同語(yǔ)言的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!