chore: optimize the i18n code of router; :star2:
| | |
| | | <h1>{{systemName}}</h1> |
| | | </router-link> |
| | | </div> |
| | | <i-menu @i18nComplete="setRoutesI18n" :i18n="menuI18n" :theme="theme" :collapsed="collapsed" :options="menuData" @select="onSelect" class="menu"/> |
| | | <i-menu :theme="theme" :collapsed="collapsed" :options="menuData" @select="onSelect" class="menu"/> |
| | | </a-layout-sider> |
| | | </template> |
| | | |
| | | <script> |
| | | import IMenu from './menu' |
| | | import {mapState, mapMutations} from 'vuex' |
| | | import {mapState} from 'vuex' |
| | | export default { |
| | | name: 'SideMenu', |
| | | components: {IMenu}, |
| | | inject: ['menuI18n'], |
| | | props: { |
| | | collapsible: { |
| | | type: Boolean, |
| | |
| | | methods: { |
| | | onSelect (obj) { |
| | | this.$emit('menuSelect', obj) |
| | | }, |
| | | ...mapMutations('setting', ['setRoutesI18n']) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | |
| | | **/ |
| | | import Menu from 'ant-design-vue/es/menu' |
| | | import Icon from 'ant-design-vue/es/icon' |
| | | import '@/utils/Objects' |
| | | |
| | | const {Item, SubMenu} = Menu |
| | | |
| | |
| | | return this.theme == 'light' ? this.theme : 'dark' |
| | | } |
| | | }, |
| | | beforeMount() { |
| | | let CN = this.generateI18n(new Object(), this.options, 'name') |
| | | let US = this.generateI18n(new Object(), this.options, 'path') |
| | | this.$i18n.setLocaleMessage('CN', CN) |
| | | this.$i18n.setLocaleMessage('US', US) |
| | | if(this.i18n) { |
| | | Object.keys(this.i18n).forEach(key => { |
| | | this.$i18n.mergeLocaleMessage(key, this.i18n[key]) |
| | | }) |
| | | } |
| | | this.$emit('i18nComplete', this.$i18n._getMessages()) |
| | | }, |
| | | created () { |
| | | this.updateMenu() |
| | | this.formatOptions(this.options, '') |
| | | // 自定义国际化配置 |
| | | if(this.i18n && this.i18n.messages) { |
| | | const messages = this.i18n.messages |
| | | Object.keys(messages).forEach(key => { |
| | | this.$i18n.mergeLocaleMessage(key, messages[key]) |
| | | }) |
| | | } |
| | | }, |
| | | watch: { |
| | | collapsed (val) { |
| | |
| | | return this.getSelectedKey(route.parent) |
| | | } |
| | | return route.path |
| | | }, |
| | | generateI18n(lang, options, valueKey) { |
| | | options.forEach(menu => { |
| | | let keys = menu.fullPath.substring(1).split('/').concat('name') |
| | | lang.assignProps(keys, menu[valueKey]) |
| | | if (menu.children) { |
| | | this.generateI18n(lang, menu.children, valueKey) |
| | | } |
| | | }) |
| | | return lang |
| | | } |
| | | }, |
| | | render (h) { |
| | |
| | | }, |
| | | provide() { |
| | | return{ |
| | | layoutMinHeight: minHeight, |
| | | menuI18n: require('@/router/i18n').default |
| | | layoutMinHeight: minHeight |
| | | } |
| | | }, |
| | | computed: { |
| | |
| | | <template> |
| | | <div class="page-layout"> |
| | | <page-header :i18n="routesI18n" :breadcrumb="breadcrumb" :title="pageTitle" :logo="logo" :avatar="avatar"> |
| | | <page-header :breadcrumb="breadcrumb" :title="pageTitle" :logo="logo" :avatar="avatar"> |
| | | <slot name="action" slot="action"></slot> |
| | | <slot slot="content" name="headerContent"></slot> |
| | | <div slot="content" v-if="!this.$slots.headerContent && desc"> |
| | |
| | | } |
| | | }, |
| | | created() { |
| | | let i18n = this.routesI18n |
| | | Object.keys(i18n).forEach(key => { |
| | | this.$i18n.mergeLocaleMessage(key, i18n[key]) |
| | | }) |
| | | this.page = this.$route.meta.page |
| | | }, |
| | | computed: { |
| | | ...mapState('setting', ['layout', 'routesI18n']), |
| | | ...mapState('setting', ['layout']), |
| | | pageTitle() { |
| | | let pageTitle = this.page && this.page.title |
| | | return this.title || this.$t(pageTitle) || this.routeName |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState('setting', ['isMobile', 'multiPage', 'animate', 'routesI18n', 'dustbins']), |
| | | ...mapState('setting', ['isMobile', 'multiPage', 'animate', 'dustbins']), |
| | | desc() { |
| | | return this.page.desc |
| | | }, |
| | |
| | | <a-divider v-if="isMobile" type="vertical" /> |
| | | <a-icon v-if="layout === 'side'" class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggleCollapse"/> |
| | | <div v-if="layout == 'head' && !isMobile" class="admin-header-menu"> |
| | | <i-menu class="head-menu" style="height: 64px; line-height: 64px;box-shadow: none" @i18nComplete="setRoutesI18n" :i18n="menuI18n" :theme="headerTheme" mode="horizontal" :options="menuData" @select="onSelect"/> |
| | | <i-menu class="head-menu" style="height: 64px; line-height: 64px;box-shadow: none" :theme="headerTheme" mode="horizontal" :options="menuData" @select="onSelect"/> |
| | | </div> |
| | | <div :class="['admin-header-right', headerTheme]"> |
| | | <header-search class="header-item" /> |
| | |
| | | name: 'AdminHeader', |
| | | components: {IMenu, HeaderAvatar, HeaderNotice, HeaderSearch}, |
| | | props: ['collapsed', 'menuData'], |
| | | inject: ['menuI18n'], |
| | | data() { |
| | | return { |
| | | langList: [ |
| | |
| | | onSelect (obj) { |
| | | this.$emit('menuSelect', obj) |
| | | }, |
| | | ...mapMutations('setting', ['setLang', 'setRoutesI18n']) |
| | | ...mapMutations('setting', ['setLang']) |
| | | } |
| | | } |
| | | </script> |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState('setting', ['multiPage', 'animate', 'layout', 'dustbins', 'routesI18n']), |
| | | ...mapState('setting', ['multiPage', 'animate', 'layout', 'dustbins']), |
| | | menuItemList() { |
| | | return [ |
| | | { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') }, |
| | |
| | | const route = this.$route |
| | | this.pageList.push(route) |
| | | this.activePage = route.fullPath |
| | | let i18n = this.routesI18n |
| | | Object.keys(i18n).forEach(key => { |
| | | this.$i18n.mergeLocaleMessage(key, i18n[key]) |
| | | }) |
| | | }, |
| | | watch: { |
| | | '$route': function (newRoute) { |
| | |
| | | import '@/mock' |
| | | import store from './store' |
| | | import 'animate.css/source/animate.css' |
| | | import VueI18n from 'vue-i18n' |
| | | import Plugins from '@/plugins' |
| | | import {initI18n} from '@/utils/i18n' |
| | | |
| | | Vue.config.productionTip = false |
| | | Vue.use(Viser) |
| | | Vue.use(Antd) |
| | | Vue.use(VueI18n) |
| | | Vue.use(Plugins) |
| | | |
| | | |
| | | |
| | | const i18n = new VueI18n({ |
| | | locale: 'CN', |
| | | fallbackLocale: 'US', |
| | | silentFallbackWarn: true |
| | | }) |
| | | const i18n = initI18n(router, 'CN', 'US') |
| | | |
| | | new Vue({ |
| | | router, |
| | |
| | | HK: '中午好', |
| | | US: 'Good afternoon', |
| | | },{ |
| | | CN: '下午好', |
| | | HK: '下午好', |
| | | US: 'Good afternoon', |
| | | },{ |
| | | CN: '晚上好', |
| | | HK: '晚上好', |
| | | US: 'Good evening', |
| | | }, |
| | | } |
| | | ] |
| | | |
| | | const welcomeMessages = [ |
| | |
| | | timeFix () { |
| | | const time = new Date() |
| | | const hour = time.getHours() |
| | | return hour < 9 ? timeList[0] : (hour <= 11 ? timeList[1] : (hour <= 13 ? timeList[2] : (hour <= 20 ? timeList[3] : timeList[4]))) |
| | | return hour < 9 |
| | | ? timeList[0] : (hour <= 11 ? timeList[1] : (hour <= 13 ? timeList[2] : (hour <= 20 ? timeList[3] : timeList[4]))) |
| | | }, |
| | | avatar () { |
| | | return this.pick(avatars) |
| | |
| | | export default { |
| | | CN: { |
| | | home: {name: '首页'}, |
| | | }, |
| | | US: { |
| | | home: {name: 'home'}, |
| | | }, |
| | | HK: { |
| | | home: {name: '首頁'}, |
| | | dashboard: { |
| | | name: 'Dashboard', |
| | | workplace: {name: '工作台'}, |
| | | analysis: {name: '分析頁'} |
| | | module.exports = { |
| | | messages: { |
| | | CN: { |
| | | home: {name: '首页'}, |
| | | }, |
| | | form: { |
| | | name: '表單頁', |
| | | basic: {name: '基礎表單'}, |
| | | step: {name: '分步表單'}, |
| | | advance: {name: '分步表單'} |
| | | US: { |
| | | home: {name: 'home'}, |
| | | }, |
| | | list: { |
| | | name: '列表頁', |
| | | query: {name: '查詢表格'}, |
| | | primary: {name: '標準列表'}, |
| | | card: {name: '卡片列表'}, |
| | | search: { |
| | | name: '搜索列表', |
| | | article: {name: '文章'}, |
| | | application: {name: '應用'}, |
| | | project: {name: '項目'} |
| | | HK: { |
| | | home: {name: '首頁'}, |
| | | dashboard: { |
| | | name: 'Dashboard', |
| | | workplace: {name: '工作台'}, |
| | | analysis: {name: '分析頁'} |
| | | }, |
| | | form: { |
| | | name: '表單頁', |
| | | basic: {name: '基礎表單'}, |
| | | step: {name: '分步表單'}, |
| | | advance: {name: '分步表單'} |
| | | }, |
| | | list: { |
| | | name: '列表頁', |
| | | query: {name: '查詢表格'}, |
| | | primary: {name: '標準列表'}, |
| | | card: {name: '卡片列表'}, |
| | | search: { |
| | | name: '搜索列表', |
| | | article: {name: '文章'}, |
| | | application: {name: '應用'}, |
| | | project: {name: '項目'} |
| | | } |
| | | }, |
| | | details: { |
| | | name: '詳情頁', |
| | | basic: {name: '基礎詳情頁'}, |
| | | advance: {name: '高級詳情頁'} |
| | | }, |
| | | result: { |
| | | name: '結果頁', |
| | | success: {name: '成功'}, |
| | | error: {name: '失敗'} |
| | | }, |
| | | exception: { |
| | | name: '異常頁', |
| | | 404: {name: '404'}, |
| | | 403: {name: '403'}, |
| | | 500: {name: '500'} |
| | | }, |
| | | components: { |
| | | name: '小組件', |
| | | taskCard: {name: '任務卡片'}, |
| | | palette: {name: '顏色複選框'} |
| | | } |
| | | }, |
| | | details: { |
| | | name: '詳情頁', |
| | | basic: {name: '基礎詳情頁'}, |
| | | advance: {name: '高級詳情頁'} |
| | | }, |
| | | result: { |
| | | name: '結果頁', |
| | | success: {name: '成功'}, |
| | | error: {name: '失敗'} |
| | | }, |
| | | exception: { |
| | | name: '異常頁', |
| | | 404: {name: '404'}, |
| | | 403: {name: '403'}, |
| | | 500: {name: '500'} |
| | | }, |
| | | components: { |
| | | name: '小組件', |
| | | taskCard: {name: '任務卡片'}, |
| | | palette: {name: '顏色複選框'} |
| | | } |
| | | } |
| | | } |
| | |
| | | isMobile: false, |
| | | animates: ADMIN.animates, |
| | | palettes: ADMIN.palettes, |
| | | routesI18n: {}, |
| | | dustbins: [], |
| | | ...config, |
| | | }, |
| | |
| | | }, |
| | | setLang(state, lang) { |
| | | state.lang = lang |
| | | }, |
| | | setRoutesI18n(state, i18n) { |
| | | state.routesI18n = i18n |
| | | }, |
| | | setHideSetting(state, hideSetting) { |
| | | state.hideSetting = hideSetting |
New file |
| | |
| | | import Vue from 'vue' |
| | | import VueI18n from 'vue-i18n' |
| | | import routesI18n from '@/router/i18n' |
| | | import './Objects' |
| | | |
| | | /** |
| | | * 创建 i18n 配置 |
| | | * @param router 路由 |
| | | * @param locale 本地化语言 |
| | | * @param fallback 回退语言 |
| | | * @returns {VueI18n} |
| | | */ |
| | | function initI18n(router, locale, fallback) { |
| | | Vue.use(VueI18n) |
| | | const options = router.options.routes.find(item => item.path === '/').children |
| | | formatOptions(options, '') |
| | | const CN = generateI18n(new Object(), options, 'name') |
| | | const US = generateI18n(new Object(), options, 'path') |
| | | const i18n = new VueI18n({ |
| | | locale, |
| | | fallbackLocale: fallback, |
| | | silentFallbackWarn: true, |
| | | messages: {CN, US} |
| | | }) |
| | | const messages = routesI18n.messages |
| | | Object.keys(messages).forEach(key => { |
| | | i18n.mergeLocaleMessage(key, messages[key]) |
| | | }) |
| | | return i18n |
| | | } |
| | | |
| | | /** |
| | | * 根据 router options 配置生成 国际化语言 |
| | | * @param lang |
| | | * @param options |
| | | * @param valueKey |
| | | * @returns {*} |
| | | */ |
| | | function generateI18n(lang, options, valueKey) { |
| | | options.forEach(menu => { |
| | | let keys = menu.fullPath.substring(1).split('/').concat('name') |
| | | lang.assignProps(keys, menu[valueKey]) |
| | | if (menu.children) { |
| | | generateI18n(lang, menu.children, valueKey) |
| | | } |
| | | }) |
| | | return lang |
| | | } |
| | | |
| | | /** |
| | | * 格式化 router options,生成 fullPath |
| | | * @param options |
| | | * @param parentPath |
| | | */ |
| | | function formatOptions(options, parentPath) { |
| | | options.forEach(route => { |
| | | let isFullPath = route.path.substring(0, 1) === '/' |
| | | route.fullPath = isFullPath ? route.path : parentPath + '/' + route.path |
| | | if (route.children) { |
| | | formatOptions(route.children, route.fullPath) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | export { |
| | | initI18n |
| | | } |