mirror of
https://github.com/veops/cmdb.git
synced 2025-08-25 12:39:06 +08:00
feat(ui): update resource view menu display and CMDB route redirection
This commit is contained in:
60
cmdb-ui/src/components/Ellipsis/Ellipsis.vue
Normal file
60
cmdb-ui/src/components/Ellipsis/Ellipsis.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<script>
|
||||
import Tooltip from 'ant-design-vue/es/tooltip'
|
||||
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/util'
|
||||
/*
|
||||
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
|
||||
|
||||
const TooltipOverlayStyle = {
|
||||
overflowWrap: 'break-word',
|
||||
wordWrap: 'break-word',
|
||||
};
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'Ellipsis',
|
||||
components: {
|
||||
Tooltip,
|
||||
},
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-ellipsis',
|
||||
},
|
||||
tooltip: {
|
||||
type: Boolean,
|
||||
},
|
||||
length: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
lines: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
fullWidthRecognition: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getStrDom(str, fullLength) {
|
||||
return <span>{cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '')}</span>
|
||||
},
|
||||
getTooltip(fullStr, fullLength) {
|
||||
return (
|
||||
<Tooltip overlayStyle={{ maxWidth: '700px' }}>
|
||||
<template slot="title">{fullStr}</template>
|
||||
{this.getStrDom(fullStr, fullLength)}
|
||||
</Tooltip>
|
||||
)
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { tooltip, length } = this.$props
|
||||
const str = this.$slots.default.map((vNode) => vNode.text).join('')
|
||||
const fullLength = getStrFullLength(str)
|
||||
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
|
||||
return strDom
|
||||
},
|
||||
}
|
||||
</script>
|
3
cmdb-ui/src/components/Ellipsis/index.js
Normal file
3
cmdb-ui/src/components/Ellipsis/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import Ellipsis from './Ellipsis'
|
||||
|
||||
export default Ellipsis
|
38
cmdb-ui/src/components/Ellipsis/index.md
Normal file
38
cmdb-ui/src/components/Ellipsis/index.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Ellipsis 文本自动省略号
|
||||
|
||||
文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import Ellipsis from '@/components/Ellipsis'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Ellipsis
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<ellipsis :length="100" tooltip>
|
||||
There were injuries alleged in three cases in 2015, and a
|
||||
fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.
|
||||
</ellipsis>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
tooltip | 移动到文本展示完整内容的提示 | boolean | -
|
||||
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -
|
@@ -1,14 +1,6 @@
|
||||
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'
|
||||
import styles from './index.module.less'
|
||||
import { mapActions } from 'vuex'
|
||||
|
||||
@@ -87,40 +79,6 @@ export default {
|
||||
inject: ['reload'],
|
||||
methods: {
|
||||
...mapActions(['UpdateCMDBSEarchValue']),
|
||||
cancelAttributes(e, menu) {
|
||||
const that = this
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
this.$confirm({
|
||||
title: this.$t('warning'),
|
||||
content: this.$t('cmdb.preference.confirmcancelSub2', { name: 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(that.$t('cmdb.preference.cancelSubSuccess'))
|
||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
||||
if (Number(citypeId) === Number(lastTypeId)) {
|
||||
localStorage.setItem('ops_ci_typeid', '')
|
||||
}
|
||||
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') {
|
||||
@@ -170,7 +128,6 @@ export default {
|
||||
return this.$t(`${title}`)
|
||||
},
|
||||
renderMenuItem(menu) {
|
||||
const isShowDot = menu.path.substr(0, 22) === '/cmdb/instances/types/'
|
||||
const target = menu.meta.target || null
|
||||
const tag = target && 'a' || 'router-link'
|
||||
const props = { to: { name: menu.name } }
|
||||
@@ -187,26 +144,11 @@ export default {
|
||||
<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 style={menu.meta.style} class={this.renderI18n(menu.meta.title).length > 10 ? 'scroll' : ''}>{this.renderI18n(menu.meta.title)}</span>
|
||||
{isShowDot && !menu.meta.disabled &&
|
||||
<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" />{ this.renderI18n('grant') }</div>
|
||||
<div onClick={e => this.cancelAttributes(e, menu)} class="custom-menu-extra-submenu-item"><a-icon type="star" />{ this.renderI18n('cmdb.preference.cancelSub') }</div>
|
||||
</div>}
|
||||
>
|
||||
<a-icon type="menu" ref="extraEllipsis" class="custom-menu-extra-ellipsis"></a-icon>
|
||||
</a-popover>
|
||||
}
|
||||
<span style={menu.meta.style} class={this.renderI18n(menu.meta.title).length > 10 ? 'scroll' : ''}>
|
||||
{this.renderI18n(menu.meta.title)}
|
||||
</span>
|
||||
</span>
|
||||
</tag>
|
||||
{isShowDot && <CMDBGrant ref="cmdbGrantCIType" resourceType="CIType" app_id="cmdb" />}
|
||||
</Item>
|
||||
)
|
||||
},
|
||||
@@ -269,27 +211,6 @@ export default {
|
||||
)
|
||||
}
|
||||
},
|
||||
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(this.$t('noPermission'))
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
jumpCMDBSearch(value) {
|
||||
this.UpdateCMDBSEarchValue(value)
|
||||
|
@@ -2,10 +2,12 @@ import MultiTab from '@/components/MultiTab'
|
||||
import Result from '@/components/Result'
|
||||
import TagSelect from '@/components/TagSelect'
|
||||
import ExceptionPage from '@/components/Exception'
|
||||
import Ellipsis from '@/components/Ellipsis'
|
||||
|
||||
export {
|
||||
MultiTab,
|
||||
Result,
|
||||
ExceptionPage,
|
||||
TagSelect
|
||||
TagSelect,
|
||||
Ellipsis
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { gridSvg, top_agent, top_acl } from '@/core/icons'
|
||||
import { getPreference } from '@/modules/cmdb/api/preference'
|
||||
|
||||
export default {
|
||||
name: 'TopMenu',
|
||||
components: { gridSvg, top_agent, top_acl },
|
||||
@@ -77,18 +77,7 @@ export default {
|
||||
async handleClick(route) {
|
||||
this.visible = false
|
||||
if (route.name !== this.current) {
|
||||
if (route.name === 'cmdb') {
|
||||
const preference = await getPreference()
|
||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
||||
if (lastTypeId && preference.type_ids.some((item) => item === Number(lastTypeId))) {
|
||||
this.$router.push(`/cmdb/instances/types/${lastTypeId}`)
|
||||
} else {
|
||||
this.$router.push('/cmdb/dashboard')
|
||||
}
|
||||
} else {
|
||||
this.$router.push(route.redirect)
|
||||
}
|
||||
// this.current = route.name
|
||||
this.$router.push(route.redirect)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@@ -2,3 +2,8 @@ export const getCurrentRowStyle = ({ row }, addedRids) => {
|
||||
const idx = addedRids.findIndex(item => item.rid === row.rid)
|
||||
return idx > -1 ? 'background-color:#E0E7FF!important' : ''
|
||||
}
|
||||
|
||||
export const getCurrentRowClass = ({ row }, addedRids) => {
|
||||
const idx = addedRids.findIndex(item => item.rid === row.rid)
|
||||
return idx > -1 ? 'grant-table-row-focus' : ''
|
||||
}
|
||||
|
@@ -81,8 +81,6 @@
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import router, { resetRouter } from '@/router'
|
||||
import store from '@/store'
|
||||
import {
|
||||
subscribeCIType,
|
||||
getSubscribeAttributes,
|
||||
@@ -223,9 +221,8 @@ export default {
|
||||
selectedAttrList.map((item) => {
|
||||
return [item, !!this.fixedList.includes(item)]
|
||||
})
|
||||
).then((res) => {
|
||||
).then(() => {
|
||||
this.$message.success(this.$t('cmdb.components.subSuccess'))
|
||||
this.resetRoute()
|
||||
if (this.selectedAttrList.length > 0) {
|
||||
this.instanceSubscribed = true
|
||||
} else {
|
||||
@@ -233,13 +230,7 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
resetRoute() {
|
||||
resetRouter()
|
||||
const roles = store.getters.roles
|
||||
store.dispatch('GenerateRoutes', { roles }, { root: true }).then(() => {
|
||||
router.addRoutes(store.getters.appRoutes)
|
||||
})
|
||||
},
|
||||
|
||||
setTargetKeys(targetKeys) {
|
||||
this.selectedAttrList = targetKeys
|
||||
},
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { RouteView, BasicLayout } from '@/layouts'
|
||||
import { getPreference, getRelationView } from '@/modules/cmdb/api/preference'
|
||||
import { getRelationView } from '@/modules/cmdb/api/preference'
|
||||
|
||||
const genCmdbRoutes = async () => {
|
||||
const routes = {
|
||||
@@ -7,6 +7,7 @@ const genCmdbRoutes = async () => {
|
||||
name: 'cmdb',
|
||||
component: BasicLayout,
|
||||
meta: { title: 'CMDB', keepAlive: false },
|
||||
redirect: '/cmdb/instances/types',
|
||||
children: [
|
||||
// preference
|
||||
// views
|
||||
@@ -39,12 +40,10 @@ const genCmdbRoutes = async () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/cmdb/resourceviews',
|
||||
path: '/cmdb/instances/types/:typeId?',
|
||||
name: 'cmdb_resource_views',
|
||||
component: RouteView,
|
||||
meta: { title: 'cmdb.menu.ciTable', icon: 'ops-cmdb-resource', selectedIcon: 'ops-cmdb-resource', keepAlive: true },
|
||||
hideChildrenInMenu: false,
|
||||
children: []
|
||||
component: () => import(`../views/ci/index`),
|
||||
meta: { title: 'cmdb.menu.ciTable', icon: 'ops-cmdb-resource', selectedIcon: 'ops-cmdb-resource', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/cmdb/tree_views',
|
||||
@@ -176,35 +175,8 @@ const genCmdbRoutes = async () => {
|
||||
}
|
||||
]
|
||||
}
|
||||
// Dynamically add subscription items and business relationships
|
||||
const [preference, relation] = await Promise.all([getPreference(), getRelationView()])
|
||||
const resourceViewsIndex = routes.children.findIndex(item => item.name === 'cmdb_resource_views')
|
||||
preference.group_types.forEach(group => {
|
||||
if (preference.group_types.length > 1) {
|
||||
routes.children[resourceViewsIndex].children.push({
|
||||
path: `/cmdb/instances/types/group${group.id}`,
|
||||
name: `cmdb_instances_group_${group.id}`,
|
||||
meta: { title: group.name || 'other', disabled: true, style: 'margin-left: 12px' },
|
||||
})
|
||||
}
|
||||
group.ci_types.forEach(item => {
|
||||
routes.children[resourceViewsIndex].children.push({
|
||||
path: `/cmdb/instances/types/${item.id}`,
|
||||
component: () => import(`../views/ci/index`),
|
||||
name: `cmdb_${item.id}`,
|
||||
meta: { title: item.alias, keepAlive: false, typeId: item.id, name: item.name, customIcon: item.icon },
|
||||
// hideChildrenInMenu: true // Force display of MenuItem instead of SubMenu
|
||||
})
|
||||
})
|
||||
})
|
||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
||||
if (lastTypeId && preference.type_ids.some(item => item === Number(lastTypeId))) {
|
||||
routes.redirect = `/cmdb/instances/types/${lastTypeId}`
|
||||
} else if (routes.children[resourceViewsIndex]?.children?.length > 0) {
|
||||
routes.redirect = routes.children[resourceViewsIndex].children.find(item => !item.hidden && !item.meta.disabled)?.path
|
||||
} else {
|
||||
routes.redirect = '/cmdb/dashboard'
|
||||
}
|
||||
// get service tree dynamic display menu
|
||||
const relation = await getRelationView()
|
||||
|
||||
if (relation?.name2id?.length === 0) {
|
||||
const relationViewRouteIndex = routes.children?.findIndex?.((route) => route.name === 'cmdb_relation_views')
|
||||
|
File diff suppressed because it is too large
Load Diff
829
cmdb-ui/src/modules/cmdb/views/ci/instanceList.vue
Normal file
829
cmdb-ui/src/modules/cmdb/views/ci/instanceList.vue
Normal file
@@ -0,0 +1,829 @@
|
||||
<template>
|
||||
<div id="ciIndex" class="cmdb-ci">
|
||||
<a-spin :tip="loadTip" :spinning="loading" >
|
||||
<div class="cmdb-views-header">
|
||||
<span>
|
||||
<span class="cmdb-views-header-title">{{ CIType.alias || CIType.name }}</span>
|
||||
<span
|
||||
@click="
|
||||
() => {
|
||||
$refs.metadataDrawer.open(typeId)
|
||||
}
|
||||
"
|
||||
class="cmdb-views-header-metadata"
|
||||
>
|
||||
<a-icon type="info-circle" />{{ $t('cmdb.ci.attributeDesc') }}
|
||||
</span>
|
||||
</span>
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ops-button-ghost"
|
||||
ghost
|
||||
@click="$refs.create.handleOpen(true, 'create')"
|
||||
><ops-icon type="veops-increase" />
|
||||
{{ $t('create') }}
|
||||
</a-button>
|
||||
<EditAttrsPopover :typeId="typeId" class="operation-icon" @refresh="refreshAfterEditAttrs">
|
||||
<a-button
|
||||
type="primary"
|
||||
ghost
|
||||
class="ops-button-ghost"
|
||||
><ops-icon type="veops-configuration_table" />{{ $t('cmdb.configTable') }}</a-button
|
||||
>
|
||||
</EditAttrsPopover>
|
||||
<a-dropdown v-model="visible">
|
||||
<a-button type="primary" ghost class="ops-button-ghost">···</a-button>
|
||||
<a-menu slot="overlay" @click="handleMenuClick">
|
||||
<a-menu-item @click="handlePerm" key="grant">
|
||||
<a-icon type="user-add" />
|
||||
{{ $t('grant') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item
|
||||
v-if="!autoSub.enabled"
|
||||
key="cancelSub"
|
||||
@click="unsubscribe"
|
||||
>
|
||||
<a-icon type="star" />
|
||||
{{ $t('cmdb.preference.cancelSub') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="cmdb-ci-main">
|
||||
<SearchForm
|
||||
ref="search"
|
||||
@refresh="handleSearch"
|
||||
:preferenceAttrList="preferenceAttrList"
|
||||
:typeId="typeId"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
@copyExpression="copyExpression"
|
||||
>
|
||||
<PreferenceSearch
|
||||
ref="preferenceSearch"
|
||||
v-show="!selectedRowKeys.length"
|
||||
@getQAndSort="getQAndSort"
|
||||
@setParamsFromPreferenceSearch="setParamsFromPreferenceSearch"
|
||||
/>
|
||||
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
|
||||
<span @click="$refs.create.handleOpen(true, 'update')">{{ $t('update') }}</span>
|
||||
<a-divider type="vertical" />
|
||||
<span @click="openBatchDownload">{{ $t('download') }}</span>
|
||||
<a-divider type="vertical" />
|
||||
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||
<a-divider type="vertical" />
|
||||
<span @click="batchRollback">{{ $t('cmdb.ci.rollback') }}</span>
|
||||
<span>{{ $t('cmdb.ci.selectRows', { rows: selectedRowKeys.length }) }}</span>
|
||||
</div>
|
||||
</SearchForm>
|
||||
<CiDetailDrawer ref="detail" :typeId="typeId" />
|
||||
|
||||
<CITable
|
||||
ref="xTable"
|
||||
:id="`cmdb-ci-${typeId}`"
|
||||
:loading="loading"
|
||||
:attrList="preferenceAttrList"
|
||||
:columns="columns"
|
||||
:passwordValue="passwordValue"
|
||||
:data="instanceList"
|
||||
:height="tableHeight"
|
||||
@onSelectChange="onSelectChange"
|
||||
@edit-closed="handleEditClose"
|
||||
@edit-actived="handleEditActived"
|
||||
@sort-change="handleSortCol"
|
||||
@openDetail="openDetail"
|
||||
@deleteCI="deleteCI"
|
||||
/>
|
||||
|
||||
<div :style="{ textAlign: 'right', marginTop: '4px' }">
|
||||
<a-pagination
|
||||
:showSizeChanger="true"
|
||||
:current="currentPage"
|
||||
size="small"
|
||||
:total="totalNumber"
|
||||
show-quick-jumper
|
||||
:page-size="pageSize"
|
||||
:page-size-options="pageSizeOptions"
|
||||
@showSizeChange="onShowSizeChange"
|
||||
:show-total="
|
||||
(total, range) =>
|
||||
$t('pagination.total', {
|
||||
range0: range[0],
|
||||
range1: range[1],
|
||||
total,
|
||||
})
|
||||
"
|
||||
@change="
|
||||
(page) => {
|
||||
currentPage = page
|
||||
}
|
||||
"
|
||||
>
|
||||
<template slot="buildOptionText" slot-scope="props">
|
||||
<span v-if="props.value !== '100000'">{{ props.value }}{{ $t('itemsPerPage') }}</span>
|
||||
<span v-if="props.value === '100000'">{{ $t('cmdb.ci.all') }}</span>
|
||||
</template>
|
||||
</a-pagination>
|
||||
</div>
|
||||
<create-instance-form
|
||||
ref="create"
|
||||
:typeIdFromProp="typeId"
|
||||
@reload="reloadData"
|
||||
@submit="batchUpdate"
|
||||
/>
|
||||
<BatchDownload ref="batchDownload" @batchDownload="batchDownload" />
|
||||
<CiRollbackForm ref="ciRollbackForm" @batchRollbackAsync="batchRollbackAsync($event)" :ciIds="selectedRowKeys" />
|
||||
<MetadataDrawer ref="metadataDrawer" />
|
||||
<CMDBGrant ref="cmdbGrant" resourceTypeName="CIType" app_id="cmdb" />
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
import { searchCI, updateCI, deleteCI } from '@/modules/cmdb/api/ci'
|
||||
import { getSubscribeAttributes, subscribeCIType, subscribeTreeView } from '@/modules/cmdb/api/preference'
|
||||
import { getCITypeAttributesById, getAttrPassword } from '@/modules/cmdb/api/CITypeAttr'
|
||||
import { roleHasPermissionToGrant } from '@/modules/acl/api/permission'
|
||||
import { searchResourceType } from '@/modules/acl/api/resource'
|
||||
import { CIBaselineRollback } from '@/modules/cmdb/api/history'
|
||||
|
||||
import { getCITableColumns } from '../../utils/helper'
|
||||
import { intersection } from '@/utils/functions/set'
|
||||
import BatchDownload from '../../components/batchDownload/batchDownload.vue'
|
||||
import PreferenceSearch from '../../components/preferenceSearch/preferenceSearch.vue'
|
||||
import MetadataDrawer from './modules/MetadataDrawer.vue'
|
||||
import CMDBGrant from '../../components/cmdbGrant'
|
||||
import CiRollbackForm from './modules/ciRollbackForm.vue'
|
||||
import SearchForm from '@/modules/cmdb/components/searchForm/SearchForm.vue'
|
||||
import CreateInstanceForm from './modules/CreateInstanceForm'
|
||||
import CiDetailDrawer from './modules/ciDetailDrawer.vue'
|
||||
import EditAttrsPopover from './modules/editAttrsPopover'
|
||||
import CITable from '@/modules/cmdb/components/ciTable/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'InstanceList',
|
||||
components: {
|
||||
SearchForm,
|
||||
CreateInstanceForm,
|
||||
CiDetailDrawer,
|
||||
EditAttrsPopover,
|
||||
BatchDownload,
|
||||
PreferenceSearch,
|
||||
MetadataDrawer,
|
||||
CMDBGrant,
|
||||
CiRollbackForm,
|
||||
CITable
|
||||
},
|
||||
props: {
|
||||
typeId: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
CIType: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
autoSub: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
tableHeight() {
|
||||
// if (this.selectedRowKeys && this.selectedRowKeys.length) {
|
||||
// return this.windowHeight - 246
|
||||
// }
|
||||
return this.windowHeight - 240
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
loading: false,
|
||||
currentPage: 1,
|
||||
pageSizeOptions: ['50', '100', '200', '100000'],
|
||||
pageSize: 50,
|
||||
totalNumber: 0,
|
||||
loadTip: '',
|
||||
form: this.$form.createForm(this),
|
||||
preferenceAttrList: [],
|
||||
|
||||
instanceList: [],
|
||||
columns: [],
|
||||
// custom table alert & rowSelection
|
||||
selectedRowKeys: [],
|
||||
// Check whether to edit
|
||||
initialInstanceList: [],
|
||||
sortByTable: undefined,
|
||||
isEditActive: false,
|
||||
attrList: [],
|
||||
attributes: {},
|
||||
// Table drag parameters
|
||||
tableDragClassName: [],
|
||||
|
||||
resource_type: {},
|
||||
|
||||
initialPasswordValue: {},
|
||||
passwordValue: {},
|
||||
lastEditCiId: null,
|
||||
isContinueCloseEdit: true,
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentPage: function(newVal, oldVal) {
|
||||
this.loadTableData(this.sortByTable)
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
handleSearch: this.handleSearch,
|
||||
setPreferenceSearchCurrent: this.setPreferenceSearchCurrent,
|
||||
attrList: () => {
|
||||
return this.attrList
|
||||
},
|
||||
attributes: () => {
|
||||
return this.attributes
|
||||
},
|
||||
filterCompPreferenceSearch: () => {
|
||||
return { type_id: this.typeId }
|
||||
},
|
||||
resource_type: () => {
|
||||
return this.resource_type
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.loading = true
|
||||
await this.getAttributeList()
|
||||
await this.loadPreferenceAttrList()
|
||||
await this.loadTableData()
|
||||
this.loading = false
|
||||
|
||||
this.$nextTick(() => {
|
||||
const loadingNode = document.getElementsByClassName('ant-drawer-mask')
|
||||
if (loadingNode?.style) {
|
||||
loadingNode.style.zIndex = 8
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
this.columnDrop()
|
||||
}, 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
// window.onkeypress = null
|
||||
if (this.sortable) {
|
||||
this.sortable.destroy()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getAttributeList() {
|
||||
await getCITypeAttributesById(this.typeId).then((res) => {
|
||||
this.attrList = res.attributes
|
||||
this.attributes = res
|
||||
})
|
||||
},
|
||||
handleSearch() {
|
||||
this.$refs.xTable.getVxetableRef().clearSort()
|
||||
this.sortByTable = undefined
|
||||
this.$nextTick(() => {
|
||||
if (this.currentPage === 1) {
|
||||
this.reloadData()
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
async loadTableData(sortByTable = undefined) {
|
||||
try {
|
||||
this.loading = true
|
||||
// If fuzzy search is possible, queryParam can be deleted later.
|
||||
// const queryParams = this.$refs['search'].queryParam || {}
|
||||
const fuzzySearch = this.$refs['search'].fuzzySearch
|
||||
const expression = this.$refs['search'].expression || ''
|
||||
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
const regSort = /(?<=sort=).+/g
|
||||
|
||||
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
||||
let sort
|
||||
if (sortByTable) {
|
||||
sort = sortByTable
|
||||
} else {
|
||||
sort = expression.match(regSort) ? expression.match(regSort)[0] : undefined
|
||||
}
|
||||
const res = await searchCI({
|
||||
q: `_type:${this.typeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
|
||||
count: this.pageSize,
|
||||
page: this.currentPage,
|
||||
sort,
|
||||
})
|
||||
this.totalNumber = res['numfound']
|
||||
this.columns = this.getColumns(res.result, this.preferenceAttrList)
|
||||
this.columns.forEach((col) => {
|
||||
if (col.is_password) {
|
||||
this.initialPasswordValue[col.field] = ''
|
||||
this.passwordValue[col.field] = ''
|
||||
}
|
||||
})
|
||||
const jsonAttrList = this.attrList.filter((attr) => attr.value_type === '6')
|
||||
this.instanceList = res['result'].map((item) => {
|
||||
jsonAttrList.forEach(
|
||||
(jsonAttr) => (item[jsonAttr.name] = item[jsonAttr.name] ? JSON.stringify(item[jsonAttr.name]) : '')
|
||||
)
|
||||
return { ..._.cloneDeep(item) }
|
||||
})
|
||||
this.initialInstanceList = _.cloneDeep(this.instanceList)
|
||||
this.$nextTick(() => {
|
||||
// this.setSelectRows()
|
||||
this.$refs.xTable.getVxetableRef().refreshColumn()
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
getColumns(data, attrList) {
|
||||
const width = document.getElementById('ciIndex').clientWidth - 50
|
||||
return getCITableColumns(data, attrList, width)
|
||||
},
|
||||
setSelectRows() {
|
||||
const cached = new Set(this.selectedRowKeys)
|
||||
const loaded = new Set(this.instanceList.map((i) => i.ci_id || i._id))
|
||||
|
||||
const inter = Array.from(intersection(cached, loaded))
|
||||
|
||||
if (inter.length === this.instanceList.length) {
|
||||
this.$refs['xTable'].getVxetableRef().setAllCheckboxRow(true)
|
||||
} else {
|
||||
const rows = []
|
||||
inter.forEach((rid) => {
|
||||
rows.push(this.$refs['xTable'].getVxetableRef().getRowById(rid))
|
||||
})
|
||||
this.$refs['xTable'].getVxetableRef().setCheckboxRow(rows, true)
|
||||
}
|
||||
},
|
||||
async loadPreferenceAttrList() {
|
||||
const subscribed = await getSubscribeAttributes(this.typeId)
|
||||
this.preferenceAttrList = subscribed.attributes // All columns that have been subscribed
|
||||
},
|
||||
onSelectChange(records) {
|
||||
this.selectedRowKeys = records.map((i) => i.ci_id || i._id)
|
||||
},
|
||||
reloadData() {
|
||||
this.loadTableData()
|
||||
},
|
||||
|
||||
handleEditClose({ row, rowIndex, column }) {
|
||||
if (!this.isContinueCloseEdit) {
|
||||
return
|
||||
}
|
||||
const $table = this.$refs['xTable'].getVxetableRef()
|
||||
const data = {}
|
||||
this.columns.forEach((item) => {
|
||||
if (
|
||||
!(item.field in this.initialPasswordValue) &&
|
||||
!_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])
|
||||
) {
|
||||
data[item.field] = row[item.field] ?? null
|
||||
}
|
||||
})
|
||||
Object.keys(this.initialPasswordValue).forEach((key) => {
|
||||
if (this.initialPasswordValue[key] !== this.passwordValue[key]) {
|
||||
data[key] = this.passwordValue[key]
|
||||
row[key] = this.passwordValue[key]
|
||||
}
|
||||
})
|
||||
this.isEditActive = false
|
||||
this.lastEditCiId = null
|
||||
if (JSON.stringify(data) !== '{}') {
|
||||
updateCI(row.ci_id || row._id, data)
|
||||
.then(() => {
|
||||
this.$message.success(this.$t('saveSuccess'))
|
||||
$table.reloadRow(row, null)
|
||||
const _initialInstanceList = _.cloneDeep(this.initialInstanceList)
|
||||
_initialInstanceList[rowIndex] = {
|
||||
..._initialInstanceList[rowIndex],
|
||||
...data,
|
||||
}
|
||||
this.initialInstanceList = _initialInstanceList
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
this.loadTableData()
|
||||
})
|
||||
}
|
||||
this.columns.forEach((col) => {
|
||||
if (col.is_password) {
|
||||
this.initialPasswordValue[col.field] = ''
|
||||
this.passwordValue[col.field] = ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async openBatchDownload() {
|
||||
this.$refs.batchDownload.open({
|
||||
preferenceAttrList: this.preferenceAttrList.filter((attr) => !attr?.is_reference),
|
||||
ciTypeName: this.CIType.alias || this.CIType.name,
|
||||
})
|
||||
},
|
||||
batchDownload({ filename, type, checkedKeys }) {
|
||||
const jsonAttrList = []
|
||||
checkedKeys.forEach((key) => {
|
||||
const _find = this.attrList.find((attr) => attr.name === key)
|
||||
if (_find && _find.value_type === '6') jsonAttrList.push(key)
|
||||
})
|
||||
const data = _.cloneDeep([
|
||||
...this.$refs.xTable.getVxetableRef().getCheckboxReserveRecords(),
|
||||
...this.$refs.xTable.getVxetableRef().getCheckboxRecords(true),
|
||||
])
|
||||
this.$refs.xTable.getVxetableRef().exportData({
|
||||
filename,
|
||||
type,
|
||||
columnFilterMethod({ column }) {
|
||||
return checkedKeys.includes(column.property)
|
||||
},
|
||||
data: [
|
||||
...data.map((item) => {
|
||||
jsonAttrList.forEach((jsonAttr) => (item[jsonAttr] = item[jsonAttr] ? JSON.stringify(item[jsonAttr]) : ''))
|
||||
return { ...item }
|
||||
}),
|
||||
],
|
||||
})
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxReserve()
|
||||
},
|
||||
batchUpdate(values) {
|
||||
const that = this
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: that.$t('cmdb.ci.batchUpdateConfirm'),
|
||||
async onOk() {
|
||||
that.batchUpdateAsync(values)
|
||||
},
|
||||
})
|
||||
},
|
||||
async batchUpdateAsync(values) {
|
||||
let successNum = 0
|
||||
let errorNum = 0
|
||||
this.loading = true
|
||||
this.loadTip = this.$t('cmdb.ci.batchUpdateInProgress') + '...'
|
||||
const payload = {}
|
||||
Object.keys(values).forEach((key) => {
|
||||
// Field values support blanking
|
||||
// There are currently field values that do not support blanking and will be returned by the backend.
|
||||
if (values[key] === undefined || values[key] === null) {
|
||||
payload[key] = null
|
||||
} else {
|
||||
payload[key] = values[key]
|
||||
}
|
||||
})
|
||||
this.$refs.create.visible = false
|
||||
const key = 'updatable'
|
||||
let errorMsg = ''
|
||||
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||
await updateCI(this.selectedRowKeys[i], payload, false)
|
||||
.then(() => {
|
||||
successNum += 1
|
||||
})
|
||||
.catch((error) => {
|
||||
errorMsg = errorMsg + '\n' + `${this.selectedRowKeys[i]}:${error.response?.data?.message ?? ''}`
|
||||
this.$notification.warning({
|
||||
key,
|
||||
message: this.$t('warning'),
|
||||
description: errorMsg,
|
||||
duration: 0,
|
||||
style: { whiteSpace: 'break-spaces', overflow: 'auto', maxHeight: this.windowHeight - 80 + 'px' },
|
||||
})
|
||||
errorNum += 1
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadTip = this.$t('cmdb.ci.batchUpdateInProgress2', {
|
||||
total: this.selectedRowKeys.length,
|
||||
successNum: successNum,
|
||||
errorNum: errorNum,
|
||||
})
|
||||
})
|
||||
}
|
||||
this.loading = false
|
||||
this.loadTip = ''
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxReserve()
|
||||
this.reloadData()
|
||||
},
|
||||
batchDelete() {
|
||||
const that = this
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: that.$t('confirmDelete'),
|
||||
onOk() {
|
||||
that.batchDeleteAsync()
|
||||
},
|
||||
})
|
||||
},
|
||||
async batchDeleteAsync() {
|
||||
let successNum = 0
|
||||
let errorNum = 0
|
||||
this.loading = true
|
||||
this.loadTip = this.$t('cmdb.ci.batchDeleting')
|
||||
const floor = Math.ceil(this.selectedRowKeys.length / 6)
|
||||
for (let i = 0; i < floor; i++) {
|
||||
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
|
||||
const promises = itemList.map((x) => deleteCI(x, false))
|
||||
await Promise.allSettled(promises)
|
||||
.then((res) => {
|
||||
res.forEach((r) => {
|
||||
if (r.status === 'fulfilled') {
|
||||
successNum += 1
|
||||
} else {
|
||||
errorNum += 1
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadTip = this.$t('cmdb.ci.batchDeleting2', {
|
||||
total: this.selectedRowKeys.length,
|
||||
successNum: successNum,
|
||||
errorNum: errorNum,
|
||||
})
|
||||
})
|
||||
}
|
||||
this.loading = false
|
||||
this.loadTip = ''
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxReserve()
|
||||
this.$nextTick(() => {
|
||||
if (this.currentPage === 1) {
|
||||
this.loadTableData()
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteCI(record) {
|
||||
const that = this
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: that.$t('confirmDelete'),
|
||||
onOk() {
|
||||
deleteCI(record.ci_id || record._id).then((res) => {
|
||||
// that.$refs.table.refresh(true)
|
||||
that.$message.success(that.$t('deleteSuccess'))
|
||||
that.reloadData()
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
batchRollback() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ciRollbackForm.onOpen(true)
|
||||
})
|
||||
},
|
||||
async batchRollbackAsync(params) {
|
||||
const mask = document.querySelector('.ant-drawer-mask')
|
||||
const oldValue = mask.style.zIndex
|
||||
mask.style.zIndex = 2
|
||||
let successNum = 0
|
||||
let errorNum = 0
|
||||
this.loading = true
|
||||
this.loadTip = this.$t('cmdb.ci.rollbackingTips')
|
||||
const floor = Math.ceil(this.selectedRowKeys.length / 6)
|
||||
for (let i = 0; i < floor; i++) {
|
||||
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
|
||||
const promises = itemList.map((x) => CIBaselineRollback(x, params))
|
||||
await Promise.allSettled(promises)
|
||||
.then((res) => {
|
||||
res.forEach((r) => {
|
||||
if (r.status === 'fulfilled') {
|
||||
successNum += 1
|
||||
} else {
|
||||
errorNum += 1
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadTip = this.$t('cmdb.ci.batchRollbacking', {
|
||||
total: this.selectedRowKeys.length,
|
||||
successNum: successNum,
|
||||
errorNum: errorNum,
|
||||
})
|
||||
})
|
||||
}
|
||||
this.loading = false
|
||||
this.loadTip = ''
|
||||
mask.style.zIndex = oldValue
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxReserve()
|
||||
this.$nextTick(() => {
|
||||
if (this.currentPage === 1) {
|
||||
this.loadTableData()
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
async refreshAfterEditAttrs() {
|
||||
await this.loadPreferenceAttrList()
|
||||
await this.loadTableData()
|
||||
},
|
||||
onShowSizeChange(current, pageSize) {
|
||||
this.pageSize = pageSize
|
||||
if (this.currentPage === 1) {
|
||||
this.reloadData()
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
setTimeout(() => {
|
||||
// this.setSelectRows()
|
||||
}, 500)
|
||||
},
|
||||
handleSortCol({ column, property, order, sortBy, sortList, $event }) {
|
||||
let sortByTable
|
||||
if (order === 'asc') {
|
||||
sortByTable = property
|
||||
} else if (order === 'desc') {
|
||||
sortByTable = `-${property}`
|
||||
}
|
||||
this.sortByTable = sortByTable
|
||||
this.$nextTick(() => {
|
||||
if (this.currentPage === 1) {
|
||||
this.loadTableData(sortByTable)
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
columnDrop() {
|
||||
this.$nextTick(() => {
|
||||
const xTable = this.$refs.xTable.getVxetableRef()
|
||||
this.sortable = Sortable.create(
|
||||
xTable.$el.querySelector('.body--wrapper>.vxe-table--header .vxe-header--row'),
|
||||
{
|
||||
handle: '.vxe-handle',
|
||||
onChoose: () => {
|
||||
const header = xTable.$el.querySelector('.body--wrapper>.vxe-table--header .vxe-header--row')
|
||||
const classNameList = []
|
||||
header.childNodes.forEach((item) => {
|
||||
classNameList.push(item.classList[1])
|
||||
})
|
||||
this.tableDragClassName = classNameList
|
||||
},
|
||||
onEnd: (params) => {
|
||||
// 由于开启了虚拟滚动,newIndex和oldIndex是虚拟的
|
||||
const { newIndex, oldIndex } = params
|
||||
// 从tableDragClassName拿到colid
|
||||
const fromColid = this.tableDragClassName[oldIndex]
|
||||
const toColid = this.tableDragClassName[newIndex]
|
||||
const fromColumn = xTable.getColumnById(fromColid)
|
||||
const toColumn = xTable.getColumnById(toColid)
|
||||
const fromIndex = xTable.getColumnIndex(fromColumn)
|
||||
const toIndex = xTable.getColumnIndex(toColumn)
|
||||
const tableColumn = xTable.getColumns()
|
||||
const currRow = tableColumn.splice(fromIndex, 1)[0]
|
||||
tableColumn.splice(toIndex, 0, currRow)
|
||||
xTable.loadColumn(tableColumn)
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
handleEditActived() {
|
||||
this.isEditActive = true
|
||||
const passwordCol = this.columns.filter((col) => col.is_password)
|
||||
this.$nextTick(() => {
|
||||
const editRecord = this.$refs.xTable.getVxetableRef().getEditRecord()
|
||||
const { row, column } = editRecord
|
||||
if (passwordCol.length && this.lastEditCiId !== row._id) {
|
||||
this.$nextTick(async () => {
|
||||
for (let i = 0; i < passwordCol.length; i++) {
|
||||
await getAttrPassword(row._id, passwordCol[i].attr_id).then((res) => {
|
||||
this.initialPasswordValue[passwordCol[i].field] = res.value
|
||||
this.passwordValue[passwordCol[i].field] = res.value
|
||||
})
|
||||
}
|
||||
this.isContinueCloseEdit = false
|
||||
await this.$refs.xTable.getVxetableRef().clearEdit()
|
||||
this.isContinueCloseEdit = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.xTable.getVxetableRef().setEditCell(row, column.field)
|
||||
})
|
||||
})
|
||||
}
|
||||
this.lastEditCiId = row._id
|
||||
})
|
||||
},
|
||||
getQAndSort() {
|
||||
const fuzzySearch = this.$refs['search'].fuzzySearch || ''
|
||||
const expression = this.$refs['search'].expression || ''
|
||||
this.$refs.preferenceSearch.savePreference({ fuzzySearch, expression })
|
||||
},
|
||||
setParamsFromPreferenceSearch(item) {
|
||||
const { fuzzySearch, expression } = item.option
|
||||
this.$refs.search.fuzzySearch = fuzzySearch
|
||||
this.$refs.search.expression = expression
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxReserve()
|
||||
this.$refs.xTable.getVxetableRef().clearSort()
|
||||
this.sortByTable = undefined
|
||||
this.$nextTick(() => {
|
||||
if (this.currentPage === 1) {
|
||||
this.loadTableData()
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
setPreferenceSearchCurrent(id = null) {
|
||||
this.$refs.preferenceSearch.currentPreferenceSearch = id
|
||||
},
|
||||
copyExpression() {
|
||||
const expression = this.$refs['search'].expression || ''
|
||||
const fuzzySearch = this.$refs['search'].fuzzySearch
|
||||
|
||||
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
|
||||
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
||||
const text = `q=_type:${this.typeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`
|
||||
this.$copyText(text)
|
||||
.then(() => {
|
||||
this.$message.success(this.$t('copySuccess'))
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error(this.$t('cmdb.ci.copyFailed'))
|
||||
})
|
||||
},
|
||||
unsubscribe() {
|
||||
this.$confirm({
|
||||
title: this.$t('warning'),
|
||||
content: this.$t('cmdb.preference.confirmcancelSub2', {
|
||||
name: `${this.CIType.alias || this.CIType.name}`,
|
||||
}),
|
||||
onOk: () => {
|
||||
const promises = [subscribeCIType(this.typeId, ''), subscribeTreeView(this.typeId, '')]
|
||||
Promise.all(promises).then(() => {
|
||||
this.$message.success(this.$t('cmdb.preference.cancelSubSuccess'))
|
||||
this.$emit('unSubscribe')
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
handlePerm() {
|
||||
roleHasPermissionToGrant({
|
||||
app_id: 'cmdb',
|
||||
resource_type_name: 'CIType',
|
||||
perm: 'grant',
|
||||
resource_name: this.CIType.name,
|
||||
}).then((res) => {
|
||||
if (res.result) {
|
||||
searchResourceType({ page_size: 9999, app_id: 'cmdb' }).then((res) => {
|
||||
this.resource_type = { groups: res.groups, id2perms: res.id2perms }
|
||||
this.$nextTick(() => {
|
||||
this.$refs.cmdbGrant.open({
|
||||
name: this.CIType.name,
|
||||
cmdbGrantType: 'ci',
|
||||
CITypeId: this.typeId,
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.$message.error(this.$t('noPermission'))
|
||||
}
|
||||
})
|
||||
},
|
||||
handleMenuClick(e) {
|
||||
if (e.key === 'grant') {
|
||||
this.visible = false
|
||||
}
|
||||
},
|
||||
openDetail(id, activeTabKey, ciDetailRelationKey) {
|
||||
this.$refs.detail.create(id, activeTabKey, ciDetailRelationKey)
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import '../index.less';
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.cmdb-ci {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: @border-radius-box;
|
||||
height: calc(100vh - 64px);
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
</style>
|
@@ -191,6 +191,10 @@ export default {
|
||||
CIReferenceAttr
|
||||
},
|
||||
props: {
|
||||
typeIdFromProp: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
typeIdFromRelation: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
@@ -235,7 +239,7 @@ export default {
|
||||
if (this.typeIdFromRelation) {
|
||||
return this.typeIdFromRelation
|
||||
}
|
||||
return this.$router.currentRoute.meta.typeId
|
||||
return this.typeIdFromProp
|
||||
},
|
||||
valueTypeMap() {
|
||||
return valueTypeMap()
|
||||
|
Reference in New Issue
Block a user