iview-admin之showTitle与useI18n
越来越被自己的无知征服,写了这么长时间的iview-admin竟然不知道useI18n的用途,为什么要说这个呢?还是源于一个需求,就是不同的模块有相同的功能项,比如说个人中心和流程管理里面都有我的流程这个子页面,这时点击任何一个我的流程菜单项都会跳转到位置靠前的路由里面,后面的不会跳转,这是为什么呢?看到这你肯定会说这不合理啊,菜单的名字虽然相同但是对应的跳转路由应该不同啊?是啊,我也是这么想的,因为是从别人手里接受的项目,所以就沿用之前人的写法,而之前人的useI18n设置的是true,这种设置会导致什么结果呢?嗯,没错,会导致你的菜单显示的名字大概率会是你router.js里面的name属性的值(为什么说是大概率呢,后面揭晓),而这个值就是路由跳转的标识,所以就出现了之前的那种状况,即使你的meta.title设置了也不会显示,这也是我纳闷的地方,这种情况我早就发现了,但是原来没有这种同名子菜单的情况,所以就把他给忽略了(按理说name应该起个英文名,但我为了显示中文,苟且的给这个name属性值全都赋了中文值),如今不得不把他给解决掉,在写了一下午bug后兜兜转转的发现了useI18n的秘密,也算是没瞎干一场。
你肯定会问我:干了一下午都干什么了呢?嗯,我告诉你,我是怎么干的,你可不许笑话我,既然meta里的title不好使,那我索性就把每个路由加上一个title属性,赋成中文值,也就是之前name里的值,再把name属性赋了唯一的英文名,像这样
/*router.js*/
{
path: 'split_pane_page',
name: 'split_pane_page',
title:'分割窗口'
meta: {
icon: 'md-pause',
title: '分割窗口'
},
component: () => import('@/view/components/split-pane/split-pane.vue')
},
用来做路由标识然后把showtitle方法做了个改造(改造方法放后面),稀里糊涂的改了好长时间也就是在这个过程中对这个useI18n产生了兴趣,于是仔细地读了一下代码,并发现了useI18n的秘密。
这个useI18n到底是干什么的呢?,其实原来我原来看过这个东东,在这个地方@/config/index.js
* @description 是否使用国际化,默认为false
* 如果不使用,则需要在路由中给需要在菜单中展示的路由设置meta: {title: 'xxx'}
* 用来在菜单中显示文字
*/
useI18n: true,
我一看国际化三个字就直接绕过了,这三个字和我实在没什么关系,但实际上却是大有关系,注释里只是说用来在菜单里显示文字,这里说的很笼统只说了如果设置了false就需要设置meta里的title,而且也没说不设置会怎样(会显示name里的文字),也没说设置为true之后会怎样,只给出了三个字‘国际化’,(大概率还是菜单里显示name里的文字,后面揭晓原因)呵呵。那这个useI18n是怎么决定菜单文字显示的呢,没错就是这个showTitle
现在让我们来好好的看一下这个showTitle,让真相彻底大白吧
之前为什么说设置为true大概率会显示name里的文字,看我手写的true下面的前两个if,第一个
if (title.includes('{{') && title.includes('}}') && useI18n)
这种情况对应的是
{
path: 'i18n_page',
name: 'i18n_page',
meta: {
icon: 'md-planet',
title: 'i18n - {{ i18n_page }}'
},
component: () => import('@/view/i18n/i18n-page.vue')
}
meta里的title是一个带插值的字符串的情况然后执行这个
title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())))
用这个$t来实现国际化也就是语言的切换
第二个
else if (__titleIsFunction__) title = item.meta.title
这种情况对应的是
{
path: 'params/:id',
name: 'params',
meta: {
icon: 'md-flower',
title: route => `{{ params }}-${route.params.id}`,
notCache: true,
beforeCloseName: 'before_close_normal'
},
component: () => import('@/view/argu-page/params.vue')
},
title是一个函数的运算结果
也就是路由传参的情况,
以上这两种很少用到,所以我说是大概率会是下面这种
else title = vm.$t(item.name)
也就造成了菜单显示文字和路由的名字混而为一的情况
再看false这种情况
else title = (item.meta && item.meta.title) || item.name
熟悉js的同学应该能看懂(我就不解释了,看不懂的去看一下js逻辑运算中的短路相关知识点)
如果存在meta和meta.title的话就显示meta.title里的文字,否则显示name里的文字
也就是说我的需求到这就可以实现了,那为什么还要改造呢?
再说回我的改造过程,如果只给路由添加title属性(我自己加的那个name下面的title,不是meta里的)是没用的还需要改一个地方@/libs/util.js
export const getMenuByRouter = (list, access) => {
let res = []
forEach(list, item => {
if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
let obj = {
icon: (item.meta && item.meta.icon) || '',
name: item.name,
meta: item.meta,
//我添加的
title:item.title
}
if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {
obj.children = getMenuByRouter(item.children, access)
}
if (item.meta && item.meta.href) obj.href = item.meta.href
if (showThisMenuEle(item, access)) res.push(obj)
}
})
return res
}
故名思意,这个方法是用来生成菜单的,通过谁生成呢,通过Router,所以要再把title引出去,看到这你肯定会说你这不是脱了那啥那啥吗?人家meta里的title已经可以用来做菜单显示文字了,你这简直是多此一举。是的,但是你忽略了一点,就是这个showTitle不仅用来生成菜单的文字,还有其他两个用途------windows标签和导航栏里的标签
菜单因为有父级菜单所以相同的子菜单不会造成混淆,但是标签是没有父级标签的,(如果不看面包屑的话)
所以我这种改造方法真正的意义在这里-----就是让标签和菜单里的文字不同,如何做到呢?还是继续看代码
看菜单模块
/*side-Menu-item*/
<template>
<Submenu :name="`${parentName}`">
<template slot="title">
<common-icon :type="parentItem.icon || ''"/>
<span>{{ showTitle(parentItem) }}</span>
</template>
<template v-for="item in children">
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item, true)" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Submenu>
</template>
/*side-Menu*/
<template>
<div class="side-menu-wrapper">
<slot></slot>
<Menu ref="menu" v-show="!collapsed" :active-name="activeName" :open-names="openedNames" :accordion="accordion" :theme="theme" width="auto" @on-select="handleSelect">
<template v-for="item in menuList">
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item, true)" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Menu>
<div class="menu-collapsed" v-show="collapsed" :list="menuList">
<template v-for="item in menuList">
<collapsed-menu v-if="item.children && item.children.length > 1" @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
<Tooltip transfer v-else :content="showTitle(item.children && item.children[0] ? item.children[0] : item)" placement="right" :key="`drop-menu-${item.name}`">
<a @click="handleSelect(getNameOrHref(item, true))" class="drop-menu-a" :style="{textAlign: 'center'}"><common-icon :size="rootIconSize" :color="textColor" :type="item.icon || (item.children && item.children[0].icon)"/></a>
</Tooltip>
</template>
</div>
</div>
</template>
你看显示什么其实靠的就是showTitle,所以我的方法其实就是加一个showTItlemine方法只不过show的是我加的这个title,把这两个模块里的所有showTitle替换成showTItlemine方法
export const showTitlemine = (item, vm) => {
let { title, __titleIsFunction__ } = item.meta
if (!title) return
if (useI18n) {
if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())))
else if (__titleIsFunction__) title = item.meta.title
//else title = vm.$t(item.name)改成
else title = vm.$t(item.Title)
//} else title = (item.meta && item.meta.title) || item.name改成
} else title = (item.meta && item.meta.title) || item.title
return title
}
这样就达到了增加一个显示的目的(不影响windows标签和导航栏标签)。(以上纯属个人行为请勿模仿,如有闪失概不负责)