mirror of
https://github.com/veops/cmdb.git
synced 2025-08-08 22:04:04 +08:00
add basic
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
<div class="header-index-wide">
|
||||
<div class="header-index-left">
|
||||
<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" />
|
||||
</div>
|
||||
<top-menu></top-menu>
|
||||
@@ -33,6 +33,7 @@ import TopMenu from '../tools/TopMenu'
|
||||
import SMenu from '../Menu/'
|
||||
import Logo from '../tools/Logo'
|
||||
import { mixin } from '@/utils/mixin'
|
||||
import { i18nRender } from '@/locales'
|
||||
|
||||
export default {
|
||||
name: 'GlobalHeader',
|
||||
@@ -79,6 +80,7 @@ export default {
|
||||
document.addEventListener('scroll', this.handleScroll, { passive: true })
|
||||
},
|
||||
methods: {
|
||||
i18nRender,
|
||||
handleScroll () {
|
||||
if (!this.autoHideHeader) {
|
||||
return
|
||||
|
@@ -11,6 +11,7 @@
|
||||
:menu="menus"
|
||||
:theme="theme"
|
||||
:mode="mode"
|
||||
:i18n-render="i18nRender"
|
||||
@select="onSelect"
|
||||
style="padding: 16px 0px;"></s-menu>
|
||||
</a-layout-sider>
|
||||
@@ -20,6 +21,7 @@
|
||||
<script>
|
||||
import Logo from '@/components/tools/Logo'
|
||||
import SMenu from './index'
|
||||
import { i18nRender } from '@/locales'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||
|
||||
export default {
|
||||
@@ -53,6 +55,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
i18nRender,
|
||||
onSelect (obj) {
|
||||
this.$emit('menuSelect', obj)
|
||||
}
|
||||
|
@@ -1,31 +1,99 @@
|
||||
import Menu from 'ant-design-vue/es/menu'
|
||||
import Icon from 'ant-design-vue/es/icon'
|
||||
import { Menu, Icon } from 'ant-design-vue'
|
||||
|
||||
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
|
||||
}
|
||||
const menuProps = {
|
||||
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
|
||||
},
|
||||
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 () {
|
||||
return {
|
||||
openKeys: [],
|
||||
@@ -41,24 +109,20 @@ export default {
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
||||
},
|
||||
mounted () {
|
||||
this.updateMenu()
|
||||
},
|
||||
watch: {
|
||||
collapsed (val) {
|
||||
if (val) {
|
||||
this.$watch('collapsed', collapsed => {
|
||||
if (collapsed) {
|
||||
this.cachedOpenKeys = this.openKeys.concat()
|
||||
this.openKeys = []
|
||||
} else {
|
||||
this.openKeys = this.cachedOpenKeys
|
||||
}
|
||||
},
|
||||
$route: function () {
|
||||
})
|
||||
this.$watch('$route', () => {
|
||||
this.updateMenu()
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.updateMenu()
|
||||
},
|
||||
methods: {
|
||||
// select menu item
|
||||
@@ -78,7 +142,6 @@ export default {
|
||||
},
|
||||
updateMenu () {
|
||||
const routes = this.$route.matched.concat()
|
||||
|
||||
const { hidden } = this.$route.meta
|
||||
if (routes.length >= 3 && hidden) {
|
||||
routes.pop()
|
||||
@@ -94,67 +157,10 @@ export default {
|
||||
}
|
||||
|
||||
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 () {
|
||||
const { mode, theme, menu } = this
|
||||
render (h) {
|
||||
const { mode, theme, menu, i18nRender = defaultI18nRender } = this
|
||||
const props = {
|
||||
mode: mode,
|
||||
theme: theme,
|
||||
@@ -172,7 +178,7 @@ export default {
|
||||
if (item.hidden) {
|
||||
return null
|
||||
}
|
||||
return this.renderItem(item)
|
||||
return renderItem(h, item, i18nRender)
|
||||
})
|
||||
// {...{ props, on: on }}
|
||||
return (
|
||||
@@ -182,3 +188,5 @@ export default {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SMenu
|
||||
|
156
cmdb-ui/src/components/Menu/menu.render.js
Normal file
156
cmdb-ui/src/components/Menu/menu.render.js
Normal file
@@ -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>
|
||||
<div class="page-header">
|
||||
<div class="page-header-index-wide">
|
||||
<s-breadcrumb />
|
||||
<s-breadcrumb :i18n-render="i18nRender" />
|
||||
<div class="detail">
|
||||
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
||||
<div class="row">
|
||||
@@ -32,7 +32,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable */
|
||||
import Breadcrumb from '@/components/tools/Breadcrumb'
|
||||
import { i18nRender } from '@/locales'
|
||||
|
||||
export default {
|
||||
name: 'PageHeader',
|
||||
@@ -58,6 +60,9 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
methods () {
|
||||
i18nRender
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -4,14 +4,20 @@
|
||||
<router-link
|
||||
v-if="item.name != name && index != 1"
|
||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||
>{{ item.meta.title }}</router-link>
|
||||
<span v-else>{{ item.meta.title }}</span>
|
||||
>{{ i18nRender(item.meta.title) }}</router-link>
|
||||
<span v-else>{{ i18nRender(item.meta.title) }}</span>
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
i18nRender: {
|
||||
type: Function,
|
||||
default: (r) => `${r}`
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
name: '',
|
||||
|
36
cmdb-ui/src/components/tools/LangSelect.vue
Normal file
36
cmdb-ui/src/components/tools/LangSelect.vue
Normal file
@@ -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>
|
||||
</a-dropdown>
|
||||
<lang-select />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -46,11 +47,12 @@
|
||||
<script>
|
||||
import NoticeIcon from '@/components/NoticeIcon'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import LangSelect from '@/components/tools/LangSelect'
|
||||
|
||||
export default {
|
||||
name: 'UserMenu',
|
||||
components: {
|
||||
NoticeIcon
|
||||
NoticeIcon, LangSelect
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['Logout']),
|
||||
|
Reference in New Issue
Block a user