mirror of https://github.com/veops/cmdb.git
add basic
This commit is contained in:
parent
680a547df4
commit
ba142266fb
|
@ -33,6 +33,7 @@
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-clipboard2": "^0.2.1",
|
"vue-clipboard2": "^0.2.1",
|
||||||
"vue-cropper": "0.4.4",
|
"vue-cropper": "0.4.4",
|
||||||
|
"vue-i18n": "^8.15.3",
|
||||||
"vue-json-excel": "^0.2.98",
|
"vue-json-excel": "^0.2.98",
|
||||||
"vue-ls": "^3.2.0",
|
"vue-ls": "^3.2.0",
|
||||||
"vue-quill-editor": "^3.0.6",
|
"vue-quill-editor": "^3.0.6",
|
||||||
|
@ -62,11 +63,11 @@
|
||||||
"eslint-plugin-vue": "^5.2.3",
|
"eslint-plugin-vue": "^5.2.3",
|
||||||
"less": "^3.0.4",
|
"less": "^3.0.4",
|
||||||
"less-loader": "^5.0.0",
|
"less-loader": "^5.0.0",
|
||||||
"vue-template-compiler": "^2.6.10",
|
|
||||||
"vue-svg-icon-loader": "^2.1.1",
|
|
||||||
"webpack-theme-color-replacer": "^1.2.17",
|
|
||||||
"opencollective": "^1.0.3",
|
"opencollective": "^1.0.3",
|
||||||
"opencollective-postinstall": "^2.0.2"
|
"opencollective-postinstall": "^2.0.2",
|
||||||
|
"vue-svg-icon-loader": "^2.1.1",
|
||||||
|
"vue-template-compiler": "^2.6.10",
|
||||||
|
"webpack-theme-color-replacer": "^1.2.17"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
|
import i18n from '@/locales'
|
||||||
import { AppDeviceEnquire } from '@/utils/mixin'
|
import { AppDeviceEnquire } from '@/utils/mixin'
|
||||||
|
import { mixin } from '@/store/i18n-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [AppDeviceEnquire],
|
mixins: [AppDeviceEnquire, mixin],
|
||||||
provide () {
|
provide () {
|
||||||
return {
|
return {
|
||||||
reload: this.reload
|
reload: this.reload
|
||||||
|
@ -19,11 +20,16 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
locale: zhCN,
|
locale: {},
|
||||||
alive: true
|
alive: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {},
|
created () {
|
||||||
|
this.$watch('currentLang', () => {
|
||||||
|
this.locale = i18n.getLocaleMessage(this.currentLang).antLocale
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
reload () {
|
reload () {
|
||||||
this.alive = false
|
this.alive = false
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<div class="header-index-wide">
|
<div class="header-index-wide">
|
||||||
<div class="header-index-left">
|
<div class="header-index-left">
|
||||||
<logo class="top-nav-header" :show-title="device !== 'mobile'"/>
|
<logo class="top-nav-header" :show-title="device !== 'mobile'"/>
|
||||||
<s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" />
|
<s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" :i18n-render="i18nRender" />
|
||||||
<a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" />
|
<a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" />
|
||||||
</div>
|
</div>
|
||||||
<top-menu></top-menu>
|
<top-menu></top-menu>
|
||||||
|
@ -33,6 +33,7 @@ import TopMenu from '../tools/TopMenu'
|
||||||
import SMenu from '../Menu/'
|
import SMenu from '../Menu/'
|
||||||
import Logo from '../tools/Logo'
|
import Logo from '../tools/Logo'
|
||||||
import { mixin } from '@/utils/mixin'
|
import { mixin } from '@/utils/mixin'
|
||||||
|
import { i18nRender } from '@/locales'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'GlobalHeader',
|
name: 'GlobalHeader',
|
||||||
|
@ -79,6 +80,7 @@ export default {
|
||||||
document.addEventListener('scroll', this.handleScroll, { passive: true })
|
document.addEventListener('scroll', this.handleScroll, { passive: true })
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
i18nRender,
|
||||||
handleScroll () {
|
handleScroll () {
|
||||||
if (!this.autoHideHeader) {
|
if (!this.autoHideHeader) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
:menu="menus"
|
:menu="menus"
|
||||||
:theme="theme"
|
:theme="theme"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
|
:i18n-render="i18nRender"
|
||||||
@select="onSelect"
|
@select="onSelect"
|
||||||
style="padding: 16px 0px;"></s-menu>
|
style="padding: 16px 0px;"></s-menu>
|
||||||
</a-layout-sider>
|
</a-layout-sider>
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Logo from '@/components/tools/Logo'
|
import Logo from '@/components/tools/Logo'
|
||||||
import SMenu from './index'
|
import SMenu from './index'
|
||||||
|
import { i18nRender } from '@/locales'
|
||||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -53,6 +55,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
i18nRender,
|
||||||
onSelect (obj) {
|
onSelect (obj) {
|
||||||
this.$emit('menuSelect', obj)
|
this.$emit('menuSelect', obj)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,99 @@
|
||||||
import Menu from 'ant-design-vue/es/menu'
|
import { Menu, Icon } from 'ant-design-vue'
|
||||||
import Icon from 'ant-design-vue/es/icon'
|
|
||||||
|
|
||||||
const { Item, SubMenu } = Menu
|
const menuProps = {
|
||||||
|
menu: {
|
||||||
export default {
|
type: Array,
|
||||||
name: 'SMenu',
|
required: true
|
||||||
props: {
|
|
||||||
menu: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'dark'
|
|
||||||
},
|
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'inline'
|
|
||||||
},
|
|
||||||
collapsed: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'inline'
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
i18nRender: {
|
||||||
|
type: Function,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultI18nRender = (key) => `${key}`
|
||||||
|
|
||||||
|
// render
|
||||||
|
const renderItem = (h, menu, i18nRender) => {
|
||||||
|
if (!menu.hidden) {
|
||||||
|
// const localeKey = `menu.${menu.name}`
|
||||||
|
// const localeKey = menu.meta && menu.meta.title
|
||||||
|
// i18nRender(localeKey)
|
||||||
|
return menu.children && !menu.hideChildrenInMenu ? renderSubMenu(h, menu, i18nRender) : renderMenuItem(h, menu, i18nRender)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderMenuItem = (h, menu, i18nRender) => {
|
||||||
|
const target = menu.meta.target || null
|
||||||
|
const CustomTag = target && 'a' || 'router-link'
|
||||||
|
const props = { to: { name: menu.name } }
|
||||||
|
const attrs = { href: menu.path, target: menu.meta.target }
|
||||||
|
|
||||||
|
if (menu.children && menu.hideChildrenInMenu) {
|
||||||
|
// 把有子菜单的 并且 父菜单是要隐藏子菜单的
|
||||||
|
// 都给子菜单增加一个 hidden 属性
|
||||||
|
// 用来给刷新页面时, selectedKeys 做控制用
|
||||||
|
menu.children.forEach(item => {
|
||||||
|
item.meta = Object.assign(item.meta, { hidden: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu.Item {...{ key: menu.path }}>
|
||||||
|
<CustomTag {...{ props, attrs }}>
|
||||||
|
{renderIcon(h, menu.meta.icon)}
|
||||||
|
<span>{i18nRender(menu.meta.title)}</span>
|
||||||
|
</CustomTag>
|
||||||
|
</Menu.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderSubMenu = (h, menu, i18nRender) => {
|
||||||
|
const itemArr = []
|
||||||
|
if (!menu.hideChildrenInMenu) {
|
||||||
|
menu.children.forEach(item => itemArr.push(renderItem(h, item, i18nRender)))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Menu.SubMenu {...{ key: menu.path }}>
|
||||||
|
<span slot="title">
|
||||||
|
{renderIcon(h, menu.meta.icon)}
|
||||||
|
<span>{i18nRender(menu.meta.title)}</span>
|
||||||
|
</span>
|
||||||
|
{itemArr}
|
||||||
|
</Menu.SubMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderIcon = (h, icon) => {
|
||||||
|
if (icon === 'none' || icon === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const props = {}
|
||||||
|
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||||
|
return (
|
||||||
|
<Icon {...{ props }}/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SMenu = {
|
||||||
|
name: 'SMenu',
|
||||||
|
props: menuProps,
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
openKeys: [],
|
openKeys: [],
|
||||||
|
@ -41,24 +109,20 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
this.$watch('collapsed', collapsed => {
|
||||||
},
|
if (collapsed) {
|
||||||
mounted () {
|
|
||||||
this.updateMenu()
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
collapsed (val) {
|
|
||||||
if (val) {
|
|
||||||
this.cachedOpenKeys = this.openKeys.concat()
|
this.cachedOpenKeys = this.openKeys.concat()
|
||||||
this.openKeys = []
|
this.openKeys = []
|
||||||
} else {
|
} else {
|
||||||
this.openKeys = this.cachedOpenKeys
|
this.openKeys = this.cachedOpenKeys
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
$route: function () {
|
this.$watch('$route', () => {
|
||||||
this.updateMenu()
|
this.updateMenu()
|
||||||
}
|
})
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.updateMenu()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// select menu item
|
// select menu item
|
||||||
|
@ -78,7 +142,6 @@ export default {
|
||||||
},
|
},
|
||||||
updateMenu () {
|
updateMenu () {
|
||||||
const routes = this.$route.matched.concat()
|
const routes = this.$route.matched.concat()
|
||||||
|
|
||||||
const { hidden } = this.$route.meta
|
const { hidden } = this.$route.meta
|
||||||
if (routes.length >= 3 && hidden) {
|
if (routes.length >= 3 && hidden) {
|
||||||
routes.pop()
|
routes.pop()
|
||||||
|
@ -94,67 +157,10 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
||||||
},
|
|
||||||
// render
|
|
||||||
renderItem (menu) {
|
|
||||||
if (!menu.hidden) {
|
|
||||||
return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
renderMenuItem (menu) {
|
|
||||||
const target = menu.meta.target || null
|
|
||||||
const tag = target && 'a' || 'router-link'
|
|
||||||
const props = { to: { name: menu.name } }
|
|
||||||
const attrs = { href: menu.path, target: menu.meta.target }
|
|
||||||
|
|
||||||
if (menu.children && menu.hideChildrenInMenu) {
|
|
||||||
// 把有子菜单的 并且 父菜单是要隐藏子菜单的
|
|
||||||
// 都给子菜单增加一个 hidden 属性
|
|
||||||
// 用来给刷新页面时, selectedKeys 做控制用
|
|
||||||
menu.children.forEach(item => {
|
|
||||||
item.meta = Object.assign(item.meta, { hidden: true })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Item {...{ key: menu.path }}>
|
|
||||||
<tag {...{ props, attrs }}>
|
|
||||||
{this.renderIcon(menu.meta.icon)}
|
|
||||||
<span>{menu.meta.title}</span>
|
|
||||||
</tag>
|
|
||||||
</Item>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
renderSubMenu (menu) {
|
|
||||||
const itemArr = []
|
|
||||||
if (!menu.hideChildrenInMenu) {
|
|
||||||
menu.children.forEach(item => itemArr.push(this.renderItem(item)))
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<SubMenu {...{ key: menu.path }}>
|
|
||||||
<span slot="title">
|
|
||||||
{this.renderIcon(menu.meta.icon)}
|
|
||||||
<span>{menu.meta.title}</span>
|
|
||||||
</span>
|
|
||||||
{itemArr}
|
|
||||||
</SubMenu>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
renderIcon (icon) {
|
|
||||||
if (icon === 'none' || icon === undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const props = {}
|
|
||||||
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
|
||||||
return (
|
|
||||||
<Icon {... { props }} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
render (h) {
|
||||||
render () {
|
const { mode, theme, menu, i18nRender = defaultI18nRender } = this
|
||||||
const { mode, theme, menu } = this
|
|
||||||
const props = {
|
const props = {
|
||||||
mode: mode,
|
mode: mode,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
|
@ -172,7 +178,7 @@ export default {
|
||||||
if (item.hidden) {
|
if (item.hidden) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return this.renderItem(item)
|
return renderItem(h, item, i18nRender)
|
||||||
})
|
})
|
||||||
// {...{ props, on: on }}
|
// {...{ props, on: on }}
|
||||||
return (
|
return (
|
||||||
|
@ -182,3 +188,5 @@ export default {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default SMenu
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
import Menu from 'ant-design-vue/es/menu'
|
||||||
|
import Icon from 'ant-design-vue/es/icon'
|
||||||
|
|
||||||
|
const { Item, SubMenu } = Menu
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SMenu',
|
||||||
|
props: {
|
||||||
|
menu: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'inline'
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
openKeys: [],
|
||||||
|
selectedKeys: [],
|
||||||
|
cachedOpenKeys: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rootSubmenuKeys: vm => {
|
||||||
|
const keys = []
|
||||||
|
vm.menu.forEach(item => keys.push(item.path))
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.updateMenu()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
collapsed (val) {
|
||||||
|
if (val) {
|
||||||
|
this.cachedOpenKeys = this.openKeys.concat()
|
||||||
|
this.openKeys = []
|
||||||
|
} else {
|
||||||
|
this.openKeys = this.cachedOpenKeys
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$route: function () {
|
||||||
|
this.updateMenu()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderIcon: function (h, icon) {
|
||||||
|
if (icon === 'none' || icon === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const props = {}
|
||||||
|
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||||
|
return h(Icon, { props: { ...props } })
|
||||||
|
},
|
||||||
|
renderMenuItem: function (h, menu, pIndex, index) {
|
||||||
|
const target = menu.meta.target || null
|
||||||
|
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index }, [
|
||||||
|
h('router-link', { attrs: { to: { name: menu.name }, target: target } }, [
|
||||||
|
this.renderIcon(h, menu.meta.icon),
|
||||||
|
h('span', [menu.meta.title])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
},
|
||||||
|
renderSubMenu: function (h, menu, pIndex, index) {
|
||||||
|
const this2_ = this
|
||||||
|
const subItem = [h('span', { slot: 'title' }, [this.renderIcon(h, menu.meta.icon), h('span', [menu.meta.title])])]
|
||||||
|
const itemArr = []
|
||||||
|
const pIndex_ = pIndex + '_' + index
|
||||||
|
console.log('menu', menu)
|
||||||
|
if (!menu.hideChildrenInMenu) {
|
||||||
|
menu.children.forEach(function (item, i) {
|
||||||
|
itemArr.push(this2_.renderItem(h, item, pIndex_, i))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return h(SubMenu, { key: menu.path ? menu.path : 'submenu_' + pIndex + '_' + index }, subItem.concat(itemArr))
|
||||||
|
},
|
||||||
|
renderItem: function (h, menu, pIndex, index) {
|
||||||
|
if (!menu.hidden) {
|
||||||
|
return menu.children && !menu.hideChildrenInMenu
|
||||||
|
? this.renderSubMenu(h, menu, pIndex, index)
|
||||||
|
: this.renderMenuItem(h, menu, pIndex, index)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderMenu: function (h, menuTree) {
|
||||||
|
const this2_ = this
|
||||||
|
const menuArr = []
|
||||||
|
menuTree.forEach(function (menu, i) {
|
||||||
|
if (!menu.hidden) {
|
||||||
|
menuArr.push(this2_.renderItem(h, menu, '0', i))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return menuArr
|
||||||
|
},
|
||||||
|
onOpenChange (openKeys) {
|
||||||
|
const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
|
||||||
|
if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
|
||||||
|
this.openKeys = openKeys
|
||||||
|
} else {
|
||||||
|
this.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateMenu () {
|
||||||
|
const routes = this.$route.matched.concat()
|
||||||
|
|
||||||
|
if (routes.length >= 4 && this.$route.meta.hidden) {
|
||||||
|
routes.pop()
|
||||||
|
this.selectedKeys = [routes[2].path]
|
||||||
|
} else {
|
||||||
|
this.selectedKeys = [routes.pop().path]
|
||||||
|
}
|
||||||
|
|
||||||
|
const openKeys = []
|
||||||
|
if (this.mode === 'inline') {
|
||||||
|
routes.forEach(item => {
|
||||||
|
openKeys.push(item.path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render (h) {
|
||||||
|
return h(
|
||||||
|
Menu,
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
theme: this.$props.theme,
|
||||||
|
mode: this.$props.mode,
|
||||||
|
openKeys: this.openKeys,
|
||||||
|
selectedKeys: this.selectedKeys
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
openChange: this.onOpenChange,
|
||||||
|
select: obj => {
|
||||||
|
this.selectedKeys = obj.selectedKeys
|
||||||
|
this.$emit('select', obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this.renderMenu(h, this.menu)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="page-header-index-wide">
|
<div class="page-header-index-wide">
|
||||||
<s-breadcrumb />
|
<s-breadcrumb :i18n-render="i18nRender" />
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -32,7 +32,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/* eslint-disable */
|
||||||
import Breadcrumb from '@/components/tools/Breadcrumb'
|
import Breadcrumb from '@/components/tools/Breadcrumb'
|
||||||
|
import { i18nRender } from '@/locales'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageHeader',
|
name: 'PageHeader',
|
||||||
|
@ -58,6 +60,9 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {}
|
return {}
|
||||||
|
},
|
||||||
|
methods () {
|
||||||
|
i18nRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,14 +4,20 @@
|
||||||
<router-link
|
<router-link
|
||||||
v-if="item.name != name && index != 1"
|
v-if="item.name != name && index != 1"
|
||||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||||
>{{ item.meta.title }}</router-link>
|
>{{ i18nRender(item.meta.title) }}</router-link>
|
||||||
<span v-else>{{ item.meta.title }}</span>
|
<span v-else>{{ i18nRender(item.meta.title) }}</span>
|
||||||
</a-breadcrumb-item>
|
</a-breadcrumb-item>
|
||||||
</a-breadcrumb>
|
</a-breadcrumb>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
props: {
|
||||||
|
i18nRender: {
|
||||||
|
type: Function,
|
||||||
|
default: (r) => `${r}`
|
||||||
|
}
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<template>
|
||||||
|
<a-dropdown>
|
||||||
|
<span class="action global-lang">
|
||||||
|
<a-icon type="global" style="font-size: 16px"/>
|
||||||
|
</span>
|
||||||
|
<a-menu slot="overlay" style="width: 150px;" @click="SwitchLang">
|
||||||
|
<a-menu-item key="zh-CN">
|
||||||
|
<a rel="noopener noreferrer">
|
||||||
|
<span role="img" aria-label="简体中文">🇨🇳</span> 简体中文
|
||||||
|
</a>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="en-US">
|
||||||
|
<a rel="noopener noreferrer">
|
||||||
|
<span role="img" aria-label="English">🇬🇧</span> English
|
||||||
|
</a>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mixin as langMixin } from '@/store/i18n-mixin'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LangSelect',
|
||||||
|
mixins: [langMixin],
|
||||||
|
data () {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
SwitchLang (row) {
|
||||||
|
this.setLang(row.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -39,6 +39,7 @@
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
|
<lang-select />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -46,11 +47,12 @@
|
||||||
<script>
|
<script>
|
||||||
import NoticeIcon from '@/components/NoticeIcon'
|
import NoticeIcon from '@/components/NoticeIcon'
|
||||||
import { mapActions, mapGetters } from 'vuex'
|
import { mapActions, mapGetters } from 'vuex'
|
||||||
|
import LangSelect from '@/components/tools/LangSelect'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UserMenu',
|
name: 'UserMenu',
|
||||||
components: {
|
components: {
|
||||||
NoticeIcon
|
NoticeIcon, LangSelect
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['Logout']),
|
...mapActions(['Logout']),
|
||||||
|
|
|
@ -10,21 +10,21 @@ const cmdbRouter = [
|
||||||
path: '/preference',
|
path: '/preference',
|
||||||
component: () => import('@/views/cmdb/preference'),
|
component: () => import('@/views/cmdb/preference'),
|
||||||
name: 'cmdb_preference',
|
name: 'cmdb_preference',
|
||||||
meta: { title: '我的订阅', icon: 'book', keepAlive: true }
|
meta: { title: 'menu.preference', icon: 'book', keepAlive: true }
|
||||||
},
|
},
|
||||||
// relation views
|
// relation views
|
||||||
{
|
{
|
||||||
path: '/relation_views',
|
path: '/relation_views',
|
||||||
component: () => import('@/views/cmdb/relation_views'),
|
component: () => import('@/views/cmdb/relation_views'),
|
||||||
name: 'cmdb_relation_views',
|
name: 'cmdb_relation_views',
|
||||||
meta: { title: '关系视图', icon: 'link', keepAlive: true },
|
meta: { title: 'menu.relationViews', icon: 'link', keepAlive: true },
|
||||||
hideChildrenInMenu: true,
|
hideChildrenInMenu: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/relation_views/:viewId',
|
path: '/relation_views/:viewId',
|
||||||
name: 'cmdb_relation_views_item',
|
name: 'cmdb_relation_views_item',
|
||||||
component: () => import('@/views/cmdb/relation_views'),
|
component: () => import('@/views/cmdb/relation_views'),
|
||||||
meta: { title: '关系视图', keepAlive: true },
|
meta: { title: 'menu.relationViews', keepAlive: true },
|
||||||
hidden: true
|
hidden: true
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
@ -33,14 +33,14 @@ const cmdbRouter = [
|
||||||
path: '/tree_views',
|
path: '/tree_views',
|
||||||
component: () => import('@/views/cmdb/tree_views'),
|
component: () => import('@/views/cmdb/tree_views'),
|
||||||
name: 'cmdb_tree_views',
|
name: 'cmdb_tree_views',
|
||||||
meta: { title: '树形视图', icon: 'share-alt', keepAlive: true },
|
meta: { title: 'menu.treeViews', icon: 'share-alt', keepAlive: true },
|
||||||
hideChildrenInMenu: true,
|
hideChildrenInMenu: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/tree_views/:typeId',
|
path: '/tree_views/:typeId',
|
||||||
name: 'cmdb_tree_views_item',
|
name: 'cmdb_tree_views_item',
|
||||||
component: () => import('@/views/cmdb/tree_views'),
|
component: () => import('@/views/cmdb/tree_views'),
|
||||||
meta: { title: '树形视图', keepAlive: true },
|
meta: { title: 'menu.treeViews', keepAlive: true },
|
||||||
hidden: true
|
hidden: true
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
@ -49,14 +49,14 @@ const cmdbRouter = [
|
||||||
path: '/batch',
|
path: '/batch',
|
||||||
component: () => import('@/views/cmdb/batch'),
|
component: () => import('@/views/cmdb/batch'),
|
||||||
name: 'cmdb_batch',
|
name: 'cmdb_batch',
|
||||||
meta: { 'title': '批量导入', icon: 'upload', keepAlive: true }
|
meta: { 'title': 'menu.batch', icon: 'upload', keepAlive: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/config//ci_types',
|
path: '/config//ci_types',
|
||||||
name: 'cmdb_ci_type',
|
name: 'cmdb_ci_type',
|
||||||
component: RouteView,
|
component: RouteView,
|
||||||
redirect: '/ci_types',
|
redirect: '/ci_types',
|
||||||
meta: { title: '模型配置', icon: 'setting', permission: ['admin'] },
|
meta: { title: 'menu.ciType', icon: 'setting', permission: ['admin'] },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/config/ci_types',
|
path: '/config/ci_types',
|
||||||
|
@ -101,7 +101,7 @@ const cmdbRouter = [
|
||||||
name: 'cmdb_acl',
|
name: 'cmdb_acl',
|
||||||
component: RouteView,
|
component: RouteView,
|
||||||
redirect: '/acl/users',
|
redirect: '/acl/users',
|
||||||
meta: { title: '权限管理', icon: 'safety-certificate', permission: ['admin'] },
|
meta: { title: 'menu.acl', icon: 'safety-certificate', permission: ['admin'] },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/acl/users',
|
path: '/acl/users',
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueI18n from 'vue-i18n'
|
||||||
|
// default language
|
||||||
|
import enUS from './lang/en-US'
|
||||||
|
// change default accept-language
|
||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
Vue.use(VueI18n)
|
||||||
|
|
||||||
|
export const defaultLang = 'en-US'
|
||||||
|
|
||||||
|
const messages = {
|
||||||
|
'en-US': {
|
||||||
|
...enUS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const i18n = new VueI18n({
|
||||||
|
locale: defaultLang,
|
||||||
|
fallbackLocale: defaultLang,
|
||||||
|
messages
|
||||||
|
})
|
||||||
|
|
||||||
|
export default i18n
|
||||||
|
|
||||||
|
const loadedLanguages = [defaultLang]
|
||||||
|
|
||||||
|
// 从缓存設置中加载当前语言
|
||||||
|
// if (Vue.ls.get('lang') !== null && defaultLang !== Vue.ls.get('lang')) {
|
||||||
|
// loadLanguageAsync(localStorage.lang)
|
||||||
|
// }
|
||||||
|
|
||||||
|
function setI18nLanguage (lang) {
|
||||||
|
i18n.locale = lang
|
||||||
|
axios.defaults.headers.common['Accept-Language'] = lang
|
||||||
|
document.querySelector('html').setAttribute('lang', lang)
|
||||||
|
return lang
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i18n Render
|
||||||
|
* @param key
|
||||||
|
* @returns rendered string
|
||||||
|
*/
|
||||||
|
export function i18nRender (key) {
|
||||||
|
return i18n.t(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadLanguageAsync (lang = defaultLang) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// 缓存语言设置
|
||||||
|
Vue.ls.set('lang', lang)
|
||||||
|
if (i18n.locale !== lang) {
|
||||||
|
if (!loadedLanguages.includes(lang)) {
|
||||||
|
return import(/* webpackChunkName: "lang-[request]" */ `./lang/${lang}`).then(msg => {
|
||||||
|
i18n.setLocaleMessage(lang, msg.default)
|
||||||
|
loadedLanguages.push(lang)
|
||||||
|
return setI18nLanguage(lang)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return resolve(setI18nLanguage(lang))
|
||||||
|
}
|
||||||
|
return resolve(lang)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import enUS from 'ant-design-vue/es/locale-provider/en_US'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
antLocale: enUS,
|
||||||
|
menu: {
|
||||||
|
preference: 'Preference',
|
||||||
|
relationViews: 'RelationViews',
|
||||||
|
treeViews: 'Treeviews',
|
||||||
|
batch: 'Batch',
|
||||||
|
ciType: 'CiType',
|
||||||
|
acl: 'ACL'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import zhCN from 'ant-design-vue/es/locale-provider/zh_CN'
|
||||||
|
export default {
|
||||||
|
antLocale: zhCN,
|
||||||
|
menu: {
|
||||||
|
preference: '我的订阅',
|
||||||
|
relationViews: '关系视图',
|
||||||
|
treeViews: '树状视图',
|
||||||
|
batch: '批量导入',
|
||||||
|
ciType: '模型配置',
|
||||||
|
acl: '权限管理'
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,8 @@ import Vue from 'vue'
|
||||||
import EventBus from './EventBus'
|
import EventBus from './EventBus'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store/'
|
import store from './store'
|
||||||
|
import i18n from './locales'
|
||||||
import { VueAxios } from './utils/request'
|
import { VueAxios } from './utils/request'
|
||||||
|
|
||||||
import bootstrap from './core/bootstrap'
|
import bootstrap from './core/bootstrap'
|
||||||
|
@ -22,6 +23,7 @@ Vue.use(VueAxios)
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
|
i18n,
|
||||||
created: bootstrap,
|
created: bootstrap,
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount('#app')
|
}).$mount('#app')
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
|
const mixin = {
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
currentLang: state => state.i18n.lang
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setLang (lang) {
|
||||||
|
this.$store.dispatch('SetLang', lang)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mixin }
|
|
@ -3,6 +3,7 @@ import Vuex from 'vuex'
|
||||||
|
|
||||||
import app from './modules/app'
|
import app from './modules/app'
|
||||||
import user from './modules/user'
|
import user from './modules/user'
|
||||||
|
import i18n from './modules/i18n'
|
||||||
import permission from './modules/permission'
|
import permission from './modules/permission'
|
||||||
import getters from './getters'
|
import getters from './getters'
|
||||||
|
|
||||||
|
@ -12,7 +13,8 @@ export default new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
app,
|
app,
|
||||||
user,
|
user,
|
||||||
permission
|
permission,
|
||||||
|
i18n
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { loadLanguageAsync } from '@/locales'
|
||||||
|
|
||||||
|
const i18n = {
|
||||||
|
state: {
|
||||||
|
lang: 'en-US'
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_LANG: (state, lang) => {
|
||||||
|
state.lang = lang
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
// 设置界面语言
|
||||||
|
SetLang ({ commit }, lang) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
commit('SET_LANG', lang)
|
||||||
|
loadLanguageAsync(lang)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default i18n
|
|
@ -11976,6 +11976,11 @@ vue-hot-reload-api@^2.3.0:
|
||||||
resolved "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"
|
resolved "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"
|
||||||
integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==
|
integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==
|
||||||
|
|
||||||
|
vue-i18n@^8.15.3:
|
||||||
|
version "8.15.3"
|
||||||
|
resolved "https://registry.npm.taobao.org/vue-i18n/download/vue-i18n-8.15.3.tgz#9f947802d9b734fcb92e2ce724da654f2f9fc0f4"
|
||||||
|
integrity sha1-n5R4Atm3NPy5LiznJNplTy+fwPQ=
|
||||||
|
|
||||||
vue-jest@^3.0.5:
|
vue-jest@^3.0.5:
|
||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
resolved "https://registry.npm.taobao.org/vue-jest/download/vue-jest-3.0.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-jest%2Fdownload%2Fvue-jest-3.0.5.tgz#d6f124b542dcbff207bf9296c19413f4c40b70c9"
|
resolved "https://registry.npm.taobao.org/vue-jest/download/vue-jest-3.0.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-jest%2Fdownload%2Fvue-jest-3.0.5.tgz#d6f124b542dcbff207bf9296c19413f4c40b70c9"
|
||||||
|
|
Loading…
Reference in New Issue