mirror of
https://github.com/veops/cmdb.git
synced 2025-08-09 22:26:09 +08:00
前后端全面升级
This commit is contained in:
@@ -1,27 +1,26 @@
|
||||
<template>
|
||||
<a-layout-sider
|
||||
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
|
||||
width="256px"
|
||||
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null]"
|
||||
width="200px"
|
||||
:collapsible="collapsible"
|
||||
v-model="collapsed"
|
||||
:trigger="null">
|
||||
<logo />
|
||||
:trigger="null"
|
||||
>
|
||||
<logo :collapsed="collapsed" />
|
||||
<s-menu
|
||||
:collapsed="collapsed"
|
||||
:menu="menus"
|
||||
:theme="theme"
|
||||
:mode="mode"
|
||||
:i18n-render="i18nRender"
|
||||
@select="onSelect"
|
||||
style="padding: 16px 0px;"></s-menu>
|
||||
style="padding: 16px 0px;"
|
||||
></s-menu>
|
||||
</a-layout-sider>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Logo from '@/components/tools/Logo'
|
||||
import SMenu from './index'
|
||||
import { i18nRender } from '@/locales'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||
|
||||
export default {
|
||||
@@ -32,35 +31,33 @@ export default {
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'inline'
|
||||
default: 'inline',
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
default: 'dark',
|
||||
},
|
||||
collapsible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
i18nRender,
|
||||
onSelect (obj) {
|
||||
onSelect(obj) {
|
||||
this.$emit('menuSelect', obj)
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
}
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,104 +1,46 @@
|
||||
import { Menu, Icon } from 'ant-design-vue'
|
||||
import router, { resetRouter } from '@/router'
|
||||
import Menu from 'ant-design-vue/es/menu'
|
||||
import Icon from 'ant-design-vue/es/icon'
|
||||
import store from '@/store'
|
||||
import {
|
||||
subscribeCIType,
|
||||
subscribeTreeView,
|
||||
} from '@/modules/cmdb/api/preference'
|
||||
import { searchResourceType } from '@/modules/acl/api/resource'
|
||||
import { roleHasPermissionToGrant } from '@/modules/acl/api/permission'
|
||||
import CMDBGrant from '@/modules/cmdb/components/cmdbGrant'
|
||||
|
||||
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 { Item, SubMenu } = Menu
|
||||
|
||||
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 = {
|
||||
export default {
|
||||
name: 'SMenu',
|
||||
props: menuProps,
|
||||
data () {
|
||||
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: []
|
||||
cachedOpenKeys: [],
|
||||
resource_type: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -106,27 +48,73 @@ const SMenu = {
|
||||
const keys = []
|
||||
vm.menu.forEach(item => keys.push(item.path))
|
||||
return keys
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
resource_type: () => {
|
||||
return this.resource_type
|
||||
},
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$watch('collapsed', collapsed => {
|
||||
if (collapsed) {
|
||||
created() {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
searchResourceType({ page_size: 9999, app_id: 'cmdb' }).then(res => {
|
||||
this.resource_type = { groups: res.groups, id2perms: res.id2perms }
|
||||
})
|
||||
this.updateMenu()
|
||||
},
|
||||
watch: {
|
||||
collapsed(val) {
|
||||
if (val) {
|
||||
this.cachedOpenKeys = this.openKeys.concat()
|
||||
this.openKeys = []
|
||||
} else {
|
||||
this.openKeys = this.cachedOpenKeys
|
||||
}
|
||||
})
|
||||
this.$watch('$route', () => {
|
||||
},
|
||||
$route: function () {
|
||||
this.updateMenu()
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.updateMenu()
|
||||
},
|
||||
},
|
||||
inject: ['reload'],
|
||||
methods: {
|
||||
// 取消订阅
|
||||
cancelAttributes(e, menu) {
|
||||
const that = this
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
this.$confirm({
|
||||
title: '警告',
|
||||
content: `确认取消订阅 ${menu.meta.title}?`,
|
||||
onOk() {
|
||||
const citypeId = menu.meta.typeId
|
||||
const unsubCIType = subscribeCIType(citypeId, '')
|
||||
const unsubTree = subscribeTreeView(citypeId, '')
|
||||
Promise.all([unsubCIType, unsubTree]).then(() => {
|
||||
that.$message.success('取消订阅成功')
|
||||
// 删除路由
|
||||
const href = window.location.href
|
||||
const hrefSplit = href.split('/')
|
||||
if (Number(hrefSplit[hrefSplit.length - 1]) === Number(citypeId)) {
|
||||
that.$router.push('/cmdb/preference')
|
||||
}
|
||||
const roles = store.getters.roles
|
||||
resetRouter()
|
||||
store.dispatch('GenerateRoutes', { roles }, { root: true }).then(() => {
|
||||
router.addRoutes(store.getters.appRoutes)
|
||||
})
|
||||
if (hrefSplit[hrefSplit.length - 1] === 'preference') {
|
||||
that.reload()
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
// select menu item
|
||||
onOpenChange (openKeys) {
|
||||
onOpenChange(openKeys) {
|
||||
// 在水平模式下时执行,并且不再执行后续
|
||||
if (this.mode === 'horizontal') {
|
||||
this.openKeys = openKeys
|
||||
@@ -140,8 +128,9 @@ const SMenu = {
|
||||
this.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||
}
|
||||
},
|
||||
updateMenu () {
|
||||
updateMenu() {
|
||||
const routes = this.$route.matched.concat()
|
||||
|
||||
const { hidden } = this.$route.meta
|
||||
if (routes.length >= 3 && hidden) {
|
||||
routes.pop()
|
||||
@@ -157,10 +146,145 @@ const SMenu = {
|
||||
}
|
||||
|
||||
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
||||
},
|
||||
// render
|
||||
renderItem(menu) {
|
||||
if (this.collapsed && menu.meta.disabled) {
|
||||
return null
|
||||
}
|
||||
if (!menu.hidden) {
|
||||
return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
|
||||
}
|
||||
return null
|
||||
},
|
||||
renderMenuItem(menu) {
|
||||
const isShowDot = menu.path.substr(0, 22) === '/cmdb/instances/types/'
|
||||
const isShowGrant = menu.path.substr(0, 20) === '/cmdb/relationviews/'
|
||||
const target = menu.meta.target || null
|
||||
const tag = target && 'a' || 'router-link'
|
||||
const props = { to: { name: menu.name } }
|
||||
const attrs = { href: menu.meta.targetHref || 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 }} disabled={menu.meta.disabled || false}>
|
||||
<tag {...{ props, attrs }}>
|
||||
{this.renderIcon({ icon: menu.meta.icon, customIcon: menu.meta.customIcon, name: menu.meta.name, typeId: menu.meta.typeId, routeName: menu.name, selectedIcon: menu.meta.selectedIcon, })}
|
||||
<span>
|
||||
<span class={menu.meta.title.length > 10 ? 'scroll' : ''}>{menu.meta.title}</span>
|
||||
{isShowDot &&
|
||||
<a-popover
|
||||
overlayClassName="custom-menu-extra-submenu"
|
||||
placement="rightTop"
|
||||
arrowPointAtCenter
|
||||
autoAdjustOverflow={false}
|
||||
getPopupContainer={(trigger) => trigger}
|
||||
content={() =>
|
||||
<div>
|
||||
<div onClick={e => this.handlePerm(e, menu, 'CIType')} class="custom-menu-extra-submenu-item"><a-icon type="user-add" />授权</div>
|
||||
<div onClick={e => this.cancelAttributes(e, menu)} class="custom-menu-extra-submenu-item"><a-icon type="star" />取消订阅</div>
|
||||
</div>}
|
||||
>
|
||||
<a-icon type="menu" ref="extraEllipsis" class="custom-menu-extra-ellipsis"></a-icon>
|
||||
</a-popover>
|
||||
}
|
||||
{isShowGrant && <a-icon class="custom-menu-extra-ellipsis" onClick={e => this.handlePerm(e, menu, 'RelationView')} type="user-add" />}
|
||||
</span>
|
||||
</tag>
|
||||
{isShowDot && <CMDBGrant ref="cmdbGrantCIType" resourceType="CIType" app_id="cmdb" />}
|
||||
{isShowGrant && <CMDBGrant ref="cmdbGrantRelationView" resourceType="RelationView" app_id="cmdb" />}
|
||||
</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({ icon: menu.meta.icon, selectedIcon: menu.meta.selectedIcon, routeName: menu.name })}
|
||||
<span>{menu.meta.title}</span>
|
||||
</span>
|
||||
{itemArr}
|
||||
</SubMenu>
|
||||
)
|
||||
},
|
||||
renderIcon({ icon, selectedIcon, customIcon = undefined, name = undefined, typeId = undefined, routeName }) {
|
||||
if (typeId) {
|
||||
if (customIcon) {
|
||||
return <ops-icon
|
||||
style={{
|
||||
color: customIcon.split('$$')[1],
|
||||
}}
|
||||
type={customIcon.split('$$')[0]}
|
||||
/>
|
||||
}
|
||||
return <span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '14px',
|
||||
height: '14px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: '#d3d3d3',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
lineHeight: '14px',
|
||||
fontSize: '10px',
|
||||
marginRight: '10px'
|
||||
}}
|
||||
>{name[0].toUpperCase()}</span>
|
||||
}
|
||||
|
||||
if (icon === 'none' || icon === undefined) {
|
||||
return null
|
||||
}
|
||||
const props = {}
|
||||
if (this.$route.name === routeName && selectedIcon) {
|
||||
return <ops-icon type={selectedIcon}></ops-icon>
|
||||
} else if (icon.startsWith('ops-') || icon.startsWith('icon-xianxing') || icon.startsWith('icon-shidi')) {
|
||||
return <ops-icon type={icon}></ops-icon>
|
||||
} else {
|
||||
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||
return (
|
||||
<Icon {... { props }} />
|
||||
)
|
||||
}
|
||||
},
|
||||
handlePerm(e, menu, resource_type_name) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
roleHasPermissionToGrant({
|
||||
app_id: 'cmdb',
|
||||
resource_type_name,
|
||||
perm: 'grant',
|
||||
resource_name: menu.meta.name,
|
||||
}).then(res => {
|
||||
if (res.result) {
|
||||
console.log(menu)
|
||||
if (resource_type_name === 'CIType') {
|
||||
this.$refs.cmdbGrantCIType.open({ name: menu.meta.name, cmdbGrantType: 'ci', CITypeId: menu.meta?.typeId })
|
||||
} else {
|
||||
this.$refs.cmdbGrantRelationView.open({ name: menu.meta.name, cmdbGrantType: 'relation_view' })
|
||||
}
|
||||
} else {
|
||||
this.$message.error('权限不足!')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
const { mode, theme, menu, i18nRender = defaultI18nRender } = this
|
||||
|
||||
render() {
|
||||
const { mode, theme, menu } = this
|
||||
const props = {
|
||||
mode: mode,
|
||||
theme: theme,
|
||||
@@ -168,7 +292,7 @@ const SMenu = {
|
||||
}
|
||||
const on = {
|
||||
select: obj => {
|
||||
this.selectedKeys = obj.selectedKeys
|
||||
// this.selectedKeys = obj.selectedKeys
|
||||
this.$emit('select', obj)
|
||||
},
|
||||
openChange: this.onOpenChange
|
||||
@@ -178,15 +302,13 @@ const SMenu = {
|
||||
if (item.hidden) {
|
||||
return null
|
||||
}
|
||||
return renderItem(h, item, i18nRender)
|
||||
return this.renderItem(item)
|
||||
})
|
||||
// {...{ props, on: on }}
|
||||
return (
|
||||
<Menu vModel={this.selectedKeys} {...{ props, on: on }}>
|
||||
<Menu class="ops-side-bar" selectedKeys={this.selectedKeys} {...{ props, on: on }}>
|
||||
{menuTree}
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SMenu
|
||||
|
@@ -1,156 +0,0 @@
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user