cmdb/cmdb-ui/src/components/Menu/menu.js

318 lines
9.9 KiB
JavaScript

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 { 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: [],
resource_type: {}
}
},
computed: {
rootSubmenuKeys: vm => {
const keys = []
vm.menu.forEach(item => keys.push(item.path))
return keys
},
},
provide() {
return {
resource_type: () => {
return this.resource_type
},
}
},
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
}
},
$route: function () {
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) {
// 在水平模式下时执行,并且不再执行后续
if (this.mode === 'horizontal') {
this.openKeys = openKeys
return
}
// 非水平模式时
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()
const { hidden } = this.$route.meta
if (routes.length >= 3 && hidden) {
routes.pop()
this.selectedKeys = [routes[routes.length - 1].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
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) {
if (customIcon.split('$$')[2]) {
return <img style={{ maxHeight: '14px', maxWidth: '14px', marginRight: '10px' }} src={`/api/common-setting/v1/file/${customIcon.split('$$')[3]}`}></img >
}
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() {
const { mode, theme, menu } = this
const props = {
mode: mode,
theme: theme,
openKeys: this.openKeys
}
const on = {
select: obj => {
// this.selectedKeys = obj.selectedKeys
this.$emit('select', obj)
},
openChange: this.onOpenChange
}
const menuTree = menu.map(item => {
if (item.hidden) {
return null
}
return this.renderItem(item)
})
// {...{ props, on: on }}
return (
<Menu class="ops-side-bar" selectedKeys={this.selectedKeys} {...{ props, on: on }}>
{menuTree}
</Menu>
)
}
}