mirror of
https://github.com/veops/cmdb.git
synced 2025-08-25 17:46:52 +08:00
feat(ui): CI - update relation layout style
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
</template>
|
||||
<span
|
||||
class="ci-icon-letter"
|
||||
v-else
|
||||
v-else-if="title"
|
||||
>
|
||||
<span>
|
||||
{{ title[0].toUpperCase() }}
|
||||
|
@@ -721,6 +721,7 @@ if __name__ == "__main__":
|
||||
batchRollbacking: 'Deleting {total} items in total, {successNum} items successful, {errorNum} items failed',
|
||||
baselineTips: 'Changes at this point in time will also be rollbacked, Unique ID, password and dynamic attributes do not support',
|
||||
cover: 'Cover',
|
||||
detail: 'Detail'
|
||||
},
|
||||
serviceTree: {
|
||||
remove: 'Remove',
|
||||
|
@@ -720,6 +720,7 @@ if __name__ == "__main__":
|
||||
batchRollbacking: '正在回滚,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||
baselineTips: '该时间点的变更也会被回滚, 唯一标识、密码属性、动态属性不支持回滚',
|
||||
cover: '覆盖',
|
||||
detail: '详情'
|
||||
},
|
||||
serviceTree: {
|
||||
remove: '移除',
|
||||
|
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="ci-detail-table-title">
|
||||
{{ title }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CIDetailTableTitle',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ci-detail-table-title {
|
||||
height: 42px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: @text-color_1;
|
||||
padding: 0px 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #EBF0F9;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
height: 44px;
|
||||
width: 300px;
|
||||
background: #F8F9FD60;
|
||||
transform: rotate(40deg);
|
||||
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 25%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
height: 44px;
|
||||
width: 300px;
|
||||
background: #F8F9FD60;
|
||||
transform: rotate(40deg);
|
||||
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: calc(25% + 100px);
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="ci-detail-title">
|
||||
<CIIcon :icon="icon" size="20" />
|
||||
<span class="ci-detail-title-text">{{ title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CIIcon from '@/modules/cmdb/components/ciIcon'
|
||||
|
||||
export default {
|
||||
name: 'CIDetailTitle',
|
||||
components: {
|
||||
CIIcon
|
||||
},
|
||||
props: {
|
||||
ci: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
ci_types: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
findCIType() {
|
||||
return this.ci_types?.find?.((item) => item?.id === this.ci?._type)
|
||||
},
|
||||
icon() {
|
||||
return this?.findCiType?.icon || ''
|
||||
},
|
||||
title() {
|
||||
return this?.ci?.[this.findCIType?.show_name] || this?.ci?.[this.findCIType?.unique_key] || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ci-detail-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
column-gap: 9px;
|
||||
|
||||
&-text {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: @text-color_1;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,614 @@
|
||||
<template>
|
||||
<div v-if="allCITypes.length" class="ci-relation-table">
|
||||
<CIDetailTableTitle :title="$t('cmdb.relation')" />
|
||||
|
||||
<div class="ci-relation-table-wrap">
|
||||
<div class="ci-relation-table-tab">
|
||||
<div
|
||||
v-for="(item) in tabList"
|
||||
:key="item.value"
|
||||
:class="`tab-item ${item.value === currentTab ? 'tab-item-active' : ''}`"
|
||||
@click="clickTab(item.value)"
|
||||
>
|
||||
<span class="tab-item-name">
|
||||
<a-tooltip :title="item.name">
|
||||
<span class="tab-item-name-text">{{ item.name }}</span>
|
||||
</a-tooltip>
|
||||
<span
|
||||
v-if="item.count"
|
||||
class="tab-item-name-count"
|
||||
>
|
||||
({{ item.count }})
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-if="item.value === currentTab && item.showAdd"
|
||||
class="tab-item-add"
|
||||
@click="openAddModal(item)"
|
||||
>
|
||||
<a-icon type="plus" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="ci-relation-table-container"
|
||||
v-if="tableIDList.length"
|
||||
>
|
||||
<div
|
||||
v-for="(item) in tableIDList"
|
||||
:key="item.id"
|
||||
class="ci-relation-table-item"
|
||||
>
|
||||
<div
|
||||
v-if="currentTab === 'all'"
|
||||
class="ci-relation-table-item-name"
|
||||
>
|
||||
<span class="ci-relation-table-item-name-text">{{ item.name }}</span>
|
||||
<span class="ci-relation-table-item-name-count">({{ item.count }})</span>
|
||||
</div>
|
||||
|
||||
<vxe-grid
|
||||
bordered
|
||||
size="mini"
|
||||
:columns="allColumns[item.id]"
|
||||
:data="allCIList[item.id]"
|
||||
overflow
|
||||
showOverflow="tooltip"
|
||||
showHeaderOverflow="tooltip"
|
||||
resizable
|
||||
class="ops-stripe-table"
|
||||
max-height="300px"
|
||||
>
|
||||
<template #reference_default="{ row, column }">
|
||||
<a
|
||||
v-for="(id) in (column.params.attr.is_list ? row[column.field] : [row[column.field]])"
|
||||
:key="id"
|
||||
:href="`/cmdb/cidetail/${column.params.attr.reference_type_id}/${id}`"
|
||||
target="_blank"
|
||||
>
|
||||
{{ getReferenceName(id, column) }}
|
||||
</a>
|
||||
</template>
|
||||
<template #operation_default="{ row }">
|
||||
<a-popconfirm
|
||||
arrowPointAtCenter
|
||||
:title="$t('cmdb.ci.confirmDeleteRelation')"
|
||||
@confirm="deleteRelation(row)"
|
||||
>
|
||||
<a
|
||||
:disabled="!allCanEdit[item.id]"
|
||||
:style="{
|
||||
color: !allCanEdit[item.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
|
||||
}"
|
||||
>
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</vxe-grid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AddTableModal ref="addTableModal" @reload="refreshTableData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
||||
import { deleteCIRelationView } from '@/modules/cmdb/api/CIRelation'
|
||||
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||
|
||||
import CIDetailTableTitle from './ciDetailTableTitle.vue'
|
||||
import AddTableModal from '@/modules/cmdb/views/relation_views/modules/AddTableModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'CIRelationTable',
|
||||
components: {
|
||||
CIDetailTableTitle,
|
||||
AddTableModal
|
||||
},
|
||||
inject: {
|
||||
ci_types: { from: 'ci_types' },
|
||||
relationViewRefreshNumber: {
|
||||
from: 'relationViewRefreshNumber',
|
||||
default: () => null,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
ciId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
typeId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
ci: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
relationData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
tabList: [],
|
||||
currentTab: 'all',
|
||||
allCITypes: [],
|
||||
allColumns: {},
|
||||
allJSONAttr: {},
|
||||
allCIList: {},
|
||||
allCanEdit: {},
|
||||
referenceCINameMap: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
tableIDList() {
|
||||
let baseIDs = []
|
||||
|
||||
switch (this.currentTab) {
|
||||
case 'all':
|
||||
baseIDs = this.tabList.filter((item) => item.value !== 'all').map((item) => item.value)
|
||||
break
|
||||
default:
|
||||
baseIDs = [this.currentTab]
|
||||
break
|
||||
}
|
||||
|
||||
return baseIDs.filter((id) => this.allCIList?.[id]?.length).map((id) => {
|
||||
const findTab = this.tabList.find((item) => item.value === id) || {}
|
||||
|
||||
return {
|
||||
id,
|
||||
name: findTab?.name || '',
|
||||
count: findTab?.count || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
relationData: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.init(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init(relationData) {
|
||||
const ci_types_list = this.ci_types()
|
||||
const _findCiType = ci_types_list.find((item) => item.id === this.typeId)
|
||||
if (!_findCiType) {
|
||||
return
|
||||
}
|
||||
|
||||
const cloneRelationData = _.cloneDeep(relationData)
|
||||
|
||||
const [
|
||||
{
|
||||
ciTypes: parentCITypes,
|
||||
columns: parentColumns,
|
||||
jsonAttr: parentJSONAttr,
|
||||
canEdit: parentCanEdit
|
||||
},
|
||||
{
|
||||
ciTypes: childCITypes,
|
||||
columns: childColumns,
|
||||
jsonAttr: childJSONAttr,
|
||||
canEdit: childCanEdit
|
||||
},
|
||||
] = await Promise.all([
|
||||
this.getParentCITypes(cloneRelationData.parentCITypeList),
|
||||
this.getChildCITypes(cloneRelationData.childCITypeList)
|
||||
])
|
||||
|
||||
this.allCITypes = [
|
||||
...parentCITypes,
|
||||
...childCITypes
|
||||
]
|
||||
this.allColumns = {
|
||||
...parentColumns,
|
||||
...childColumns
|
||||
}
|
||||
this.allJSONAttr = {
|
||||
...parentJSONAttr,
|
||||
...childJSONAttr
|
||||
}
|
||||
this.allCanEdit = {
|
||||
...parentCanEdit,
|
||||
...childCanEdit
|
||||
}
|
||||
|
||||
const [parentCIs, childCIs] = await Promise.all([
|
||||
this.getParentCIs(cloneRelationData.parentCIList),
|
||||
this.getChildCIs(cloneRelationData.childCIList)
|
||||
])
|
||||
this.allCIList = {
|
||||
...parentCIs,
|
||||
...childCIs
|
||||
}
|
||||
|
||||
const tabList = this.allCITypes.map((item) => {
|
||||
return {
|
||||
name: item?.alias ?? item?.name ?? '',
|
||||
value: item.id,
|
||||
count: this.allCIList?.[item.id]?.length || 0,
|
||||
showAdd: this.allCanEdit?.[item.id] ?? false
|
||||
}
|
||||
})
|
||||
tabList.unshift({
|
||||
name: this.$t('all'),
|
||||
value: 'all',
|
||||
count: Object.values(this.allCIList).reduce((acc, cur) => acc + (cur?.length || 0), 0),
|
||||
showAdd: false
|
||||
})
|
||||
this.tabList = tabList
|
||||
|
||||
this.handleReferenceCINameMap()
|
||||
},
|
||||
|
||||
async getParentCITypes(ciTypes) {
|
||||
const canEdit = {}
|
||||
for (let i = 0; i < ciTypes.length; i++) {
|
||||
ciTypes[i].isParent = true
|
||||
await getCanEditByParentIdChildId(ciTypes[i].id, this.typeId).then((p_res) => {
|
||||
canEdit[ciTypes[i].id] = p_res.result
|
||||
})
|
||||
}
|
||||
|
||||
const { columns, jsonAttr } = this.handleCITypeList(ciTypes || [])
|
||||
|
||||
return {
|
||||
ciTypes,
|
||||
columns,
|
||||
jsonAttr,
|
||||
canEdit
|
||||
}
|
||||
},
|
||||
|
||||
async getChildCITypes(ciTypes) {
|
||||
const canEdit = {}
|
||||
for (let i = 0; i < ciTypes.length; i++) {
|
||||
ciTypes[i].isParent = false
|
||||
await getCanEditByParentIdChildId(this.typeId, ciTypes[i].id).then((c_res) => {
|
||||
canEdit[ciTypes[i].id] = c_res.result
|
||||
})
|
||||
}
|
||||
|
||||
const { columns, jsonAttr } = this.handleCITypeList(ciTypes)
|
||||
return {
|
||||
ciTypes,
|
||||
columns,
|
||||
jsonAttr,
|
||||
canEdit
|
||||
}
|
||||
},
|
||||
|
||||
handleCITypeList(list) {
|
||||
const CIColumns = {}
|
||||
const CIJSONAttr = {}
|
||||
|
||||
list.forEach((item) => {
|
||||
const columns = []
|
||||
const jsonAttr = []
|
||||
item.attributes.forEach((attr) => {
|
||||
const column = {
|
||||
key: 'p_' + attr.id,
|
||||
field: attr.name,
|
||||
title: attr.alias,
|
||||
minWidth: '100px',
|
||||
params: {
|
||||
attr
|
||||
},
|
||||
}
|
||||
if (attr.is_reference) {
|
||||
column.slots = {
|
||||
default: 'reference_default'
|
||||
}
|
||||
}
|
||||
columns.push(column)
|
||||
|
||||
if (attr.value_type === '6') {
|
||||
jsonAttr.push(attr.name)
|
||||
}
|
||||
})
|
||||
CIJSONAttr[item.id] = jsonAttr
|
||||
CIColumns[item.id] = columns
|
||||
CIColumns[item.id].push({
|
||||
key: 'p_operation',
|
||||
field: 'operation',
|
||||
title: this.$t('operation'),
|
||||
width: '60px',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'operation_default',
|
||||
},
|
||||
align: 'center',
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
columns: CIColumns,
|
||||
jsonAttr: CIJSONAttr
|
||||
}
|
||||
},
|
||||
|
||||
async getParentCIs(ciList) {
|
||||
const cis = {}
|
||||
ciList.forEach((item) => {
|
||||
this.allJSONAttr[item._type].forEach((attr) => {
|
||||
item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : ''
|
||||
})
|
||||
this.formatCI(item)
|
||||
item.isParent = true
|
||||
|
||||
if (item._type in cis) {
|
||||
cis[item._type].push(item)
|
||||
} else {
|
||||
cis[item._type] = [item]
|
||||
}
|
||||
})
|
||||
|
||||
return cis
|
||||
},
|
||||
|
||||
async getChildCIs(ciList) {
|
||||
const cis = {}
|
||||
ciList.forEach((item) => {
|
||||
this.allJSONAttr[item._type].forEach((attr) => {
|
||||
item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : ''
|
||||
})
|
||||
this.formatCI(item)
|
||||
item.isParent = false
|
||||
|
||||
if (item._type in cis) {
|
||||
cis[item._type].push(item)
|
||||
} else {
|
||||
cis[item._type] = [item]
|
||||
}
|
||||
})
|
||||
|
||||
return cis
|
||||
},
|
||||
|
||||
formatCI(ci) {
|
||||
Object.keys(ci).forEach((key) => {
|
||||
const attr = this.allColumns?.[ci?._type]?.find((item) => item?.params?.attr?.name === key)?.params?.attr
|
||||
if (attr?.is_choice && attr?.choice_value?.length) {
|
||||
if (attr?.is_list) {
|
||||
ci[key] = ci[key].map((value) => {
|
||||
const label = attr?.choice_value?.find((choice) => choice?.[0] === value)?.[1]?.label
|
||||
return label || ci[key]
|
||||
})
|
||||
} else {
|
||||
const label = attr?.choice_value?.find((choice) => choice?.[0] === ci[key])?.[1]?.label
|
||||
ci[key] = label || ci[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return ci
|
||||
},
|
||||
|
||||
async handleReferenceCINameMap() {
|
||||
const referenceCINameMap = {}
|
||||
this.allCITypes.forEach((CIType) => {
|
||||
CIType.attributes.forEach((attr) => {
|
||||
if (attr?.is_reference && attr?.reference_type_id) {
|
||||
const currentCIList = this.allCIList[CIType.id]
|
||||
if (currentCIList?.length) {
|
||||
currentCIList.forEach((ci) => {
|
||||
const ids = Array.isArray(ci[attr.name]) ? ci[attr.name] : ci[attr.name] ? [ci[attr.name]] : []
|
||||
|
||||
if (ids.length) {
|
||||
if (!referenceCINameMap?.[attr.reference_type_id]) {
|
||||
referenceCINameMap[attr.reference_type_id] = {}
|
||||
}
|
||||
ids.forEach((id) => {
|
||||
referenceCINameMap[attr.reference_type_id][id] = ''
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (!Object.keys(referenceCINameMap).length) {
|
||||
return
|
||||
}
|
||||
|
||||
const allRes = await Promise.all(
|
||||
Object.keys(referenceCINameMap).map((key) => {
|
||||
return searchCI({
|
||||
q: `_type:${key},_id:(${Object.keys(referenceCINameMap[key]).join(';')})`,
|
||||
count: 9999
|
||||
})
|
||||
})
|
||||
)
|
||||
const CITypeList = this.ci_types()
|
||||
const showNameMap = {}
|
||||
|
||||
Object.keys(referenceCINameMap).forEach((id) => {
|
||||
const CIType = CITypeList.find((CIType) => Number(CIType.id) === Number(id))
|
||||
|
||||
showNameMap[id] = {
|
||||
show_name: CIType?.show_name,
|
||||
unique_key: CIType?.unique_key
|
||||
}
|
||||
})
|
||||
|
||||
allRes.forEach((res) => {
|
||||
res.result.forEach((item) => {
|
||||
if (referenceCINameMap?.[item._type]?.[item._id] === '') {
|
||||
const showName = showNameMap?.[item._type]
|
||||
|
||||
referenceCINameMap[item._type][item._id] = item?.[showName?.show_name] ?? item?.[showName?.unique_key] ?? ''
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.referenceCINameMap = referenceCINameMap
|
||||
},
|
||||
|
||||
getReferenceName(id, column) {
|
||||
const typeId = column?.params?.attr?.reference_type_id
|
||||
return this.referenceCINameMap?.[typeId]?.[id] || id
|
||||
},
|
||||
|
||||
clickTab(value) {
|
||||
this.currentTab = value
|
||||
},
|
||||
|
||||
deleteRelation(row) {
|
||||
const first_ci_id = row?.isParent ? row?._id : this.ciId
|
||||
const second_ci_id = row?.isParent ? this.ciId : row?._id
|
||||
|
||||
deleteCIRelationView(first_ci_id, second_ci_id).then(() => {
|
||||
this.refreshTableData()
|
||||
if (this.relationViewRefreshNumber) {
|
||||
this.relationViewRefreshNumber()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
openAddModal(tabData) {
|
||||
const ciType = this.allCITypes.find((item) => item.id === tabData.value)
|
||||
|
||||
this.$refs.addTableModal.openModal(
|
||||
{
|
||||
[`${this.ci.unique}`]: this.ci?.[this.ci.unique]
|
||||
},
|
||||
this.ciId,
|
||||
ciType,
|
||||
ciType?.isParent ? 'parents' : 'children'
|
||||
)
|
||||
},
|
||||
|
||||
async refreshTableData() {
|
||||
this.$emit('refreshRelationCI')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ci-relation-table {
|
||||
width: 100%;
|
||||
margin-top: 32px;
|
||||
|
||||
&-wrap {
|
||||
border: solid 1px #E4E7ED;
|
||||
border-top: none;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-tab {
|
||||
flex-shrink: 0;
|
||||
width: 160px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 6px 0px;
|
||||
border-right: solid 1px #E4E7ED;
|
||||
|
||||
.tab-item {
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-left: 16px;
|
||||
padding-right: 10px;
|
||||
background-color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
|
||||
&-name {
|
||||
font-size: 14px;
|
||||
color: @text-color_1;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
max-width: calc(100% - 16px);
|
||||
|
||||
&-text {
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
color: @text-color_2;
|
||||
}
|
||||
|
||||
&-count {
|
||||
color: @text-color_3;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-add {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 14px;
|
||||
background-color: #FFFFFF;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: @primary-color;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background-color: #F0F5FF;
|
||||
|
||||
.tab-item-name-text {
|
||||
color: @text-color_1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.tab-item-name-text {
|
||||
color: @text-color_1;
|
||||
}
|
||||
|
||||
.tab-item-add {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
width: 100%;
|
||||
padding: 15px 17px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-item {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&-name {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: @text-color_1;
|
||||
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
&-count {
|
||||
font-size: 12px;
|
||||
color: @text-color_3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,95 @@
|
||||
import { getCITypeChildren, getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
|
||||
import { searchCIRelation } from '@/modules/cmdb/api/CIRelation'
|
||||
|
||||
const RelationMixin = {
|
||||
data() {
|
||||
return {
|
||||
relationData: {
|
||||
parentCITypeList: [],
|
||||
childCITypeList: [],
|
||||
parentCIList: [],
|
||||
childCIList: []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async initRelationData(typeId, ciId) {
|
||||
const {
|
||||
parentCITypeList,
|
||||
childCITypeList
|
||||
} = await this.getRelationCITypeList(typeId)
|
||||
const {
|
||||
parentCIList,
|
||||
childCIList
|
||||
} = await this.getRelationCIList(ciId)
|
||||
this.relationData = {
|
||||
parentCITypeList,
|
||||
childCITypeList,
|
||||
parentCIList,
|
||||
childCIList
|
||||
}
|
||||
},
|
||||
|
||||
async getRelationCITypeList(typeId) {
|
||||
let parentCITypeList = []
|
||||
let childCITypeList = []
|
||||
|
||||
if (typeId) {
|
||||
parentCITypeList = await this.getParentCITypeList(typeId)
|
||||
childCITypeList = await this.getChildCITypeList(typeId)
|
||||
}
|
||||
|
||||
return {
|
||||
parentCITypeList,
|
||||
childCITypeList
|
||||
}
|
||||
},
|
||||
|
||||
async getRelationCIList(ciId) {
|
||||
let parentCIList = []
|
||||
let childCIList = []
|
||||
|
||||
if (ciId) {
|
||||
parentCIList = await this.getParentCIList(ciId)
|
||||
childCIList = await this.getChildCIList(ciId)
|
||||
}
|
||||
|
||||
return {
|
||||
parentCIList,
|
||||
childCIList
|
||||
}
|
||||
},
|
||||
|
||||
async refreshRelationCI(ciId) {
|
||||
const {
|
||||
parentCIList,
|
||||
childCIList
|
||||
} = await this.getRelationCIList(ciId)
|
||||
this.relationData.parentCIList = parentCIList
|
||||
this.relationData.childCIList = childCIList
|
||||
},
|
||||
|
||||
async getParentCITypeList(typeId) {
|
||||
const res = await getCITypeParent(typeId)
|
||||
return res?.parents || []
|
||||
},
|
||||
|
||||
async getChildCITypeList(typeId) {
|
||||
const res = await getCITypeChildren(typeId)
|
||||
return res.children || []
|
||||
},
|
||||
|
||||
async getParentCIList(ciId) {
|
||||
const res = await searchCIRelation(`root_id=${ciId}&level=1&reverse=1&count=10000`)
|
||||
return res?.result || []
|
||||
},
|
||||
|
||||
async getChildCIList(ciId) {
|
||||
const res = await searchCIRelation(`root_id=${ciId}&level=1&reverse=0&count=10000`)
|
||||
return res?.result || []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default RelationMixin
|
@@ -1,146 +1,16 @@
|
||||
<template>
|
||||
<div class="ci-detail-relation">
|
||||
<a-radio-group v-model="activeKey" size="small" @change="handleChangeActiveKey">
|
||||
<a-radio-button value="1">
|
||||
{{ $t('cmdb.ci.topo') }}
|
||||
</a-radio-button>
|
||||
<a-radio-button value="2">
|
||||
{{ $t('cmdb.ci.table') }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
<CiDetailRelationTopo ref="ciDetailRelationTopo" v-if="activeKey === '1'" />
|
||||
<template v-if="activeKey === '2'">
|
||||
<template v-for="parent in parentCITypes">
|
||||
<div :key="'ctr_' + parent.ctr_id">
|
||||
<div class="ci-detail-relation-table-title">
|
||||
{{ parent.alias || parent.name }}
|
||||
<a
|
||||
:disabled="!canEdit[parent.id]"
|
||||
@click="
|
||||
() => {
|
||||
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, parent, 'parents')
|
||||
}
|
||||
"
|
||||
><a-icon
|
||||
type="plus-square"
|
||||
/></a>
|
||||
<span v-if="!canEdit[parent.id]">({{ $t('cmdb.ci.m2mTips') }})</span>
|
||||
</div>
|
||||
<vxe-grid
|
||||
v-if="firstCIs[parent.name]"
|
||||
bordered
|
||||
size="mini"
|
||||
:columns="firstCIColumns[parent.id]"
|
||||
:data="firstCIs[parent.name]"
|
||||
overflow
|
||||
showOverflow="tooltip"
|
||||
showHeaderOverflow="tooltip"
|
||||
resizable
|
||||
class="ops-stripe-table"
|
||||
>
|
||||
<template #reference_default="{ row, column }">
|
||||
<a
|
||||
v-for="(id) in (column.params.attr.is_list ? row[column.field] : [row[column.field]])"
|
||||
:key="id"
|
||||
:href="`/cmdb/cidetail/${column.params.attr.reference_type_id}/${id}`"
|
||||
target="_blank"
|
||||
>
|
||||
{{ getReferenceName(id, column) }}
|
||||
</a>
|
||||
</template>
|
||||
<template #operation_default="{ row }">
|
||||
<a-popconfirm
|
||||
arrowPointAtCenter
|
||||
:title="$t('cmdb.ci.confirmDeleteRelation')"
|
||||
@confirm="deleteRelation(row._id, ciId)"
|
||||
>
|
||||
<a
|
||||
:disabled="!canEdit[parent.id]"
|
||||
:style="{
|
||||
color: !canEdit[parent.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
|
||||
}"
|
||||
><a-icon
|
||||
type="delete"
|
||||
/></a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</vxe-grid>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider />
|
||||
<template v-for="child in childCITypes">
|
||||
<div :key="'ctr_' + child.ctr_id">
|
||||
<div class="ci-detail-relation-table-title">
|
||||
{{ child.alias || child.name }}
|
||||
<a
|
||||
:disabled="!canEdit[child.id]"
|
||||
@click="
|
||||
() => {
|
||||
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, child, 'children')
|
||||
}
|
||||
"
|
||||
><a-icon
|
||||
type="plus-square"
|
||||
/></a>
|
||||
<span v-if="!canEdit[child.id]">({{ $t('cmdb.ci.m2mTips') }})</span>
|
||||
</div>
|
||||
<vxe-grid
|
||||
v-if="secondCIs[child.name]"
|
||||
bordered
|
||||
size="mini"
|
||||
:columns="secondCIColumns[child.id]"
|
||||
:data="secondCIs[child.name]"
|
||||
showOverflow="tooltip"
|
||||
showHeaderOverflow="tooltip"
|
||||
resizable
|
||||
class="ops-stripe-table"
|
||||
>
|
||||
<template #reference_default="{ row, column }">
|
||||
<a
|
||||
v-for="(id) in (column.params.attr.is_list ? row[column.field] : [row[column.field]])"
|
||||
:key="id"
|
||||
:href="`/cmdb/cidetail/${column.params.attr.reference_type_id}/${id}`"
|
||||
target="_blank"
|
||||
>
|
||||
{{ getReferenceName(id, column) }}
|
||||
</a>
|
||||
</template>
|
||||
<template #operation_default="{ row }">
|
||||
<a-popconfirm
|
||||
arrowPointAtCenter
|
||||
:title="$t('cmdb.ci.confirmDeleteRelation')"
|
||||
@confirm="deleteRelation(ciId, row._id)"
|
||||
>
|
||||
<a
|
||||
:disabled="!canEdit[child.id]"
|
||||
:style="{
|
||||
color: !canEdit[child.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
|
||||
}"
|
||||
><a-icon
|
||||
type="delete"
|
||||
/></a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</vxe-grid>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<AddTableModal ref="addTableModal" @reload="reload" />
|
||||
<CiDetailRelationTopo ref="ciDetailRelationTopo"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { getCITypeChildren, getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
||||
import { searchCIRelation, deleteCIRelationView } from '@/modules/cmdb/api/CIRelation'
|
||||
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||
import CiDetailRelationTopo from './ciDetailRelationTopo/index.vue'
|
||||
import Node from './ciDetailRelationTopo/node.js'
|
||||
import AddTableModal from '../../relation_views/modules/AddTableModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'CiDetailRelation',
|
||||
components: { CiDetailRelationTopo, AddTableModal },
|
||||
name: 'CIDetailRelation',
|
||||
components: { CiDetailRelationTopo },
|
||||
props: {
|
||||
ciId: {
|
||||
type: Number,
|
||||
@@ -154,41 +24,32 @@ export default {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
initQueryLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
relationData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeKey: '1',
|
||||
parentCITypes: [],
|
||||
childCITypes: [],
|
||||
firstCIs: {},
|
||||
firstCIColumns: {},
|
||||
secondCIs: {},
|
||||
secondCIColumns: {},
|
||||
firstCIJsonAttr: {},
|
||||
secondCIJsonAttr: {},
|
||||
canEdit: {},
|
||||
topoData: {
|
||||
nodes: {},
|
||||
edges: []
|
||||
},
|
||||
referenceCINameMap: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
exsited_ci() {
|
||||
const _exsited_ci = [this.ciId]
|
||||
this.parentCITypes.forEach((parent) => {
|
||||
this.relationData.parentCITypeList.forEach((parent) => {
|
||||
if (this.firstCIs[parent.name]) {
|
||||
this.firstCIs[parent.name].forEach((parentCi) => {
|
||||
_exsited_ci.push(parentCi._id)
|
||||
})
|
||||
}
|
||||
})
|
||||
this.childCITypes.forEach((child) => {
|
||||
this.relationData.childCITypeList.forEach((child) => {
|
||||
if (this.secondCIs[child.name]) {
|
||||
this.secondCIs[child.name].forEach((childCi) => {
|
||||
_exsited_ci.push(childCi._id)
|
||||
@@ -207,43 +68,39 @@ export default {
|
||||
default: () => null,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.initQueryLoading) {
|
||||
this.init(true)
|
||||
|
||||
watch: {
|
||||
relationData: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.init(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init(isFirst) {
|
||||
async init(relationData) {
|
||||
const ci_types_list = this.ci_types()
|
||||
const _findCiType = ci_types_list.find((item) => item.id === this.typeId)
|
||||
if (!_findCiType) {
|
||||
return
|
||||
}
|
||||
|
||||
await Promise.all([this.getParentCITypes(), this.getChildCITypes()])
|
||||
Promise.all([this.getFirstCIs(), this.getSecondCIs()]).then(() => {
|
||||
this.getFirstCIs(relationData.parentCIList)
|
||||
this.getSecondCIs(relationData.childCIList)
|
||||
|
||||
this.handleTopoData()
|
||||
if (
|
||||
isFirst &&
|
||||
this.$refs.ciDetailRelationTopo &&
|
||||
ci_types_list.length
|
||||
) {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ciDetailRelationTopo) {
|
||||
this.$refs.ciDetailRelationTopo.exsited_ci = this.exsited_ci
|
||||
this.$refs.ciDetailRelationTopo.setTopoData(this.topoData)
|
||||
}
|
||||
|
||||
this.handleReferenceCINameMap()
|
||||
})
|
||||
},
|
||||
async getFirstCIs() {
|
||||
await searchCIRelation(`root_id=${Number(this.ciId)}&level=1&reverse=1&count=10000`)
|
||||
.then((res) => {
|
||||
async getFirstCIs(parentCIList) {
|
||||
const firstCIs = {}
|
||||
res.result.forEach((item) => {
|
||||
this.firstCIJsonAttr[item._type].forEach((attr) => {
|
||||
item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : ''
|
||||
})
|
||||
this.formatCI(item, this.firstCIColumns)
|
||||
parentCIList.forEach((item) => {
|
||||
if (item.ci_type in firstCIs) {
|
||||
firstCIs[item.ci_type].push(item)
|
||||
} else {
|
||||
@@ -251,18 +108,10 @@ export default {
|
||||
}
|
||||
})
|
||||
this.firstCIs = firstCIs
|
||||
})
|
||||
.catch((e) => {})
|
||||
},
|
||||
async getSecondCIs() {
|
||||
await searchCIRelation(`root_id=${Number(this.ciId)}&level=1&reverse=0&count=10000`)
|
||||
.then((res) => {
|
||||
async getSecondCIs(childCIList) {
|
||||
const secondCIs = {}
|
||||
res.result.forEach((item) => {
|
||||
this.secondCIJsonAttr[item._type].forEach((attr) => {
|
||||
item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : ''
|
||||
})
|
||||
this.formatCI(item, this.secondCIColumns)
|
||||
childCIList.forEach((item) => {
|
||||
if (item.ci_type in secondCIs) {
|
||||
secondCIs[item.ci_type].push(item)
|
||||
} else {
|
||||
@@ -270,251 +119,8 @@ export default {
|
||||
}
|
||||
})
|
||||
this.secondCIs = secondCIs
|
||||
})
|
||||
.catch((e) => {})
|
||||
},
|
||||
|
||||
formatCI(ci, columns) {
|
||||
Object.keys(ci).forEach((key) => {
|
||||
const attr = columns?.[ci?._type]?.find((item) => item?.params?.attr?.name === key)?.params?.attr
|
||||
if (attr?.is_choice && attr?.choice_value?.length) {
|
||||
if (attr?.is_list) {
|
||||
ci[key] = ci[key].map((value) => {
|
||||
const label = attr?.choice_value?.find((choice) => choice?.[0] === value)?.[1]?.label
|
||||
return label || ci[key]
|
||||
})
|
||||
} else {
|
||||
const label = attr?.choice_value?.find((choice) => choice?.[0] === ci[key])?.[1]?.label
|
||||
ci[key] = label || ci[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return ci
|
||||
},
|
||||
|
||||
async getParentCITypes() {
|
||||
const res = await getCITypeParent(this.typeId)
|
||||
this.parentCITypes = res.parents
|
||||
for (let i = 0; i < res.parents.length; i++) {
|
||||
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
|
||||
this.canEdit = {
|
||||
..._.cloneDeep(this.canEdit),
|
||||
[res.parents[i].id]: p_res.result,
|
||||
}
|
||||
})
|
||||
}
|
||||
const firstCIColumns = {}
|
||||
const firstCIJsonAttr = {}
|
||||
res.parents.forEach((item) => {
|
||||
const columns = []
|
||||
const jsonAttr = []
|
||||
item.attributes.forEach((attr) => {
|
||||
const column = {
|
||||
key: 'p_' + attr.id,
|
||||
field: attr.name,
|
||||
title: attr.alias,
|
||||
minWidth: '100px',
|
||||
params: {
|
||||
attr
|
||||
},
|
||||
}
|
||||
if (attr.is_reference) {
|
||||
column.slots = {
|
||||
default: 'reference_default'
|
||||
}
|
||||
}
|
||||
columns.push(column)
|
||||
|
||||
if (attr.value_type === '6') {
|
||||
jsonAttr.push(attr.name)
|
||||
}
|
||||
})
|
||||
firstCIJsonAttr[item.id] = jsonAttr
|
||||
firstCIColumns[item.id] = columns
|
||||
firstCIColumns[item.id].push({
|
||||
key: 'p_operation',
|
||||
field: 'operation',
|
||||
title: this.$t('operation'),
|
||||
width: '60px',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'operation_default',
|
||||
},
|
||||
align: 'center',
|
||||
})
|
||||
})
|
||||
|
||||
this.firstCIColumns = firstCIColumns
|
||||
this.firstCIJsonAttr = firstCIJsonAttr
|
||||
},
|
||||
async getChildCITypes() {
|
||||
const res = await getCITypeChildren(this.typeId)
|
||||
|
||||
this.childCITypes = res.children
|
||||
for (let i = 0; i < res.children.length; i++) {
|
||||
await getCanEditByParentIdChildId(this.typeId, res.children[i].id).then((c_res) => {
|
||||
this.canEdit = {
|
||||
..._.cloneDeep(this.canEdit),
|
||||
[res.children[i].id]: c_res.result,
|
||||
}
|
||||
})
|
||||
}
|
||||
const secondCIColumns = {}
|
||||
const secondCIJsonAttr = {}
|
||||
res.children.forEach((item) => {
|
||||
const columns = []
|
||||
const jsonAttr = []
|
||||
item.attributes.forEach((attr) => {
|
||||
const column = {
|
||||
key: 'c_' + attr.id,
|
||||
field: attr.name,
|
||||
title: attr.alias,
|
||||
minWidth: '100px',
|
||||
params: {
|
||||
attr
|
||||
},
|
||||
}
|
||||
if (attr.is_reference) {
|
||||
column.slots = {
|
||||
default: 'reference_default'
|
||||
}
|
||||
}
|
||||
columns.push(column)
|
||||
|
||||
if (attr.value_type === '6') {
|
||||
jsonAttr.push(attr.name)
|
||||
}
|
||||
})
|
||||
secondCIJsonAttr[item.id] = jsonAttr
|
||||
secondCIColumns[item.id] = columns
|
||||
secondCIColumns[item.id].push({
|
||||
key: 'c_operation',
|
||||
field: 'operation',
|
||||
title: this.$t('operation'),
|
||||
width: '60px',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'operation_default',
|
||||
},
|
||||
align: 'center',
|
||||
})
|
||||
})
|
||||
|
||||
this.secondCIColumns = secondCIColumns
|
||||
this.secondCIJsonAttr = secondCIJsonAttr
|
||||
},
|
||||
|
||||
async handleReferenceCINameMap() {
|
||||
const CITypes = _.unionBy(
|
||||
[
|
||||
...this.parentCITypes,
|
||||
...this.childCITypes
|
||||
],
|
||||
'id'
|
||||
)
|
||||
const CIList = _.unionBy(
|
||||
_.flatten(
|
||||
[
|
||||
...Object.values(this.firstCIs),
|
||||
...Object.values(this.secondCIs)
|
||||
]
|
||||
),
|
||||
'_id'
|
||||
)
|
||||
|
||||
const CIMap = {}
|
||||
CIList.forEach((ci) => {
|
||||
if (!CIMap[ci._type]) {
|
||||
CIMap[ci._type] = []
|
||||
}
|
||||
CIMap[ci._type].push(ci)
|
||||
})
|
||||
|
||||
const referenceCINameMap = {}
|
||||
CITypes.forEach((CIType) => {
|
||||
CIType.attributes.forEach((attr) => {
|
||||
if (attr?.is_reference && attr?.reference_type_id) {
|
||||
const currentCIList = CIMap[CIType.id]
|
||||
if (currentCIList?.length) {
|
||||
currentCIList.forEach((ci) => {
|
||||
const ids = Array.isArray(ci[attr.name]) ? ci[attr.name] : ci[attr.name] ? [ci[attr.name]] : []
|
||||
|
||||
if (ids.length) {
|
||||
if (!referenceCINameMap?.[attr.reference_type_id]) {
|
||||
referenceCINameMap[attr.reference_type_id] = {}
|
||||
}
|
||||
ids.forEach((id) => {
|
||||
referenceCINameMap[attr.reference_type_id][id] = ''
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (!Object.keys(referenceCINameMap).length) {
|
||||
return
|
||||
}
|
||||
|
||||
const allRes = await Promise.all(
|
||||
Object.keys(referenceCINameMap).map((key) => {
|
||||
return searchCI({
|
||||
q: `_type:${key},_id:(${Object.keys(referenceCINameMap[key]).join(';')})`,
|
||||
count: 9999
|
||||
})
|
||||
})
|
||||
)
|
||||
const CITypeList = this.ci_types()
|
||||
const showNameMap = {}
|
||||
|
||||
Object.keys(referenceCINameMap).forEach((id) => {
|
||||
const CIType = CITypeList.find((CIType) => Number(CIType.id) === Number(id))
|
||||
|
||||
showNameMap[id] = {
|
||||
show_name: CIType?.show_name,
|
||||
unique_key: CIType?.unique_key
|
||||
}
|
||||
})
|
||||
|
||||
allRes.forEach((res) => {
|
||||
res.result.forEach((item) => {
|
||||
if (referenceCINameMap?.[item._type]?.[item._id] === '') {
|
||||
const showName = showNameMap?.[item._type]
|
||||
|
||||
referenceCINameMap[item._type][item._id] = item?.[showName?.show_name] ?? item?.[showName?.unique_key] ?? ''
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.referenceCINameMap = referenceCINameMap
|
||||
},
|
||||
|
||||
getReferenceName(id, column) {
|
||||
const typeId = column?.params?.attr?.reference_type_id
|
||||
return this.referenceCINameMap?.[typeId]?.[id] || id
|
||||
},
|
||||
|
||||
reload() {
|
||||
this.init()
|
||||
},
|
||||
deleteRelation(first_ci_id, second_ci_id) {
|
||||
deleteCIRelationView(first_ci_id, second_ci_id).then((res) => {
|
||||
this.init()
|
||||
if (this.relationViewRefreshNumber) {
|
||||
this.relationViewRefreshNumber()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleChangeActiveKey(e) {
|
||||
if (e.target.value === '1') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ciDetailRelationTopo.exsited_ci = this.exsited_ci
|
||||
this.$refs.ciDetailRelationTopo.setTopoData(this.topoData)
|
||||
})
|
||||
}
|
||||
},
|
||||
handleTopoData() {
|
||||
const ci_types_list = this.ci_types()
|
||||
if (!ci_types_list?.length) {
|
||||
@@ -555,7 +161,7 @@ export default {
|
||||
children: [],
|
||||
}
|
||||
const edges = []
|
||||
this.parentCITypes.forEach((parent) => {
|
||||
this.relationData.parentCITypeList.forEach((parent) => {
|
||||
const _findCiType = ci_types_list.find((item) => item.id === parent.id)
|
||||
if (this.firstCIs[parent.name] && _findCiType) {
|
||||
const unique_id = _findCiType.show_id || _findCiType.unique_id
|
||||
@@ -598,7 +204,7 @@ export default {
|
||||
})
|
||||
}
|
||||
})
|
||||
this.childCITypes.forEach((child) => {
|
||||
this.relationData.childCITypeList.forEach((child) => {
|
||||
const _findCiType = ci_types_list.find((item) => item.id === child.id)
|
||||
if (this.secondCIs[child.name] && _findCiType) {
|
||||
const unique_id = _findCiType.show_id || _findCiType.unique_id
|
||||
@@ -653,12 +259,5 @@ export default {
|
||||
<style lang="less" scoped>
|
||||
.ci-detail-relation {
|
||||
height: 100%;
|
||||
.ci-detail-relation-table-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 5px;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
id="ci-detail-relation-topo"
|
||||
class="ci-detail-relation-topo"
|
||||
:style="{ width: '100%', marginTop: '20px', height: 'calc(100% - 44px)' }"
|
||||
:style="{ width: '100%', height: '100%' }"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
@@ -25,7 +25,6 @@ export default {
|
||||
}
|
||||
},
|
||||
inject: ['ci_types'],
|
||||
mounted() {},
|
||||
methods: {
|
||||
init() {
|
||||
const root = document.getElementById('ci-detail-relation-topo')
|
||||
|
@@ -6,29 +6,79 @@
|
||||
{{ $t('cmdb.ci.share') }}
|
||||
</a>
|
||||
<a-tab-pane key="tab_1">
|
||||
<span slot="tab"><a-icon type="book" />{{ $t('cmdb.attribute') }}</span>
|
||||
<div class="ci-detail-attr">
|
||||
<el-descriptions
|
||||
:title="group.name || $t('other')"
|
||||
:key="group.name"
|
||||
<span slot="tab"><a-icon type="book" />{{ $t('cmdb.ci.detail') }}</span>
|
||||
|
||||
<div class="ci-detail-table">
|
||||
<CIDetailTitle :ci="ci" :ci_types="ci_types" />
|
||||
|
||||
<div class="ci-detail-table-attr">
|
||||
<CIDetailTableTitle :title="$t('cmdb.attribute')" />
|
||||
|
||||
<div class="ci-detail-table-attr-wrap">
|
||||
<div
|
||||
v-for="group in attributeGroups"
|
||||
border
|
||||
:column="3"
|
||||
:key="group.name"
|
||||
class="ci-detail-table-attr-group"
|
||||
>
|
||||
<el-descriptions-item
|
||||
:label="`${attr.alias || attr.name}`"
|
||||
:key="attr.name"
|
||||
<div class="ci-detail-table-attr-group-name">
|
||||
{{ group.name || $t('other') }}
|
||||
</div>
|
||||
|
||||
<a-row :gutter="[18, 14]">
|
||||
<a-col
|
||||
v-for="attr in group.attributes"
|
||||
:key="attr.name"
|
||||
:span="8"
|
||||
>
|
||||
<ci-detail-attr-content :ci="ci" :attr="attr" @refresh="refresh" @updateCIByself="updateCIByself" @refreshReferenceAttr="handleReferenceAttr" />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<a-row :gutter="[8, 0]">
|
||||
<a-col :span="8">
|
||||
<span class="ci-detail-table-attr-label">
|
||||
<a-tooltip :title="attr.alias || attr.name">
|
||||
<span class="ci-detail-table-attr-label-text">{{ attr.alias || attr.name }}</span>
|
||||
</a-tooltip>
|
||||
<span class="ci-detail-table-attr-label-colon">:</span>
|
||||
</span>
|
||||
</a-col>
|
||||
|
||||
<a-col
|
||||
:span="16"
|
||||
class="ci-detail-table-attr-content"
|
||||
>
|
||||
<CIDetailAttrContent
|
||||
:ci="ci"
|
||||
:attr="attr"
|
||||
:attributeGroups="attributeGroups"
|
||||
@updateChoiceValue="updateChoiceValue"
|
||||
@refresh="refresh"
|
||||
@updateCIByself="updateCIByself"
|
||||
@refreshReferenceAttr="handleReferenceAttr"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CIRelationTable
|
||||
:ciId="ciId"
|
||||
:typeId="typeId"
|
||||
:ci="ci"
|
||||
:relationData="relationData"
|
||||
@refreshRelationCI="refreshRelationCI(ciId)"
|
||||
/>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_2">
|
||||
<span slot="tab"><a-icon type="branches" />{{ $t('cmdb.relation') }}</span>
|
||||
<span slot="tab"><a-icon type="branches" />{{ $t('cmdb.ci.topo') }}</span>
|
||||
<div :style="{ height: '100%', padding: '24px', overflow: 'auto' }">
|
||||
<ci-detail-relation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" :initQueryLoading="initQueryLoading" />
|
||||
<CIDetailRelation
|
||||
:ciId="ciId"
|
||||
:typeId="typeId"
|
||||
:ci="ci"
|
||||
:relationData="relationData"
|
||||
/>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_3">
|
||||
@@ -42,7 +92,7 @@
|
||||
<ops-icon type="veops-export" />{{ $t('export') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
<ci-rollback-form ref="ciRollbackForm" :ciIds="[ciId]" @getCIHistory="getCIHistory" />
|
||||
<CIRollbackForm ref="ciRollbackForm" :ciIds="[ciId]" @getCIHistory="getCIHistory" />
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
show-overflow
|
||||
@@ -134,25 +184,33 @@
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { Descriptions, DescriptionsItem } from 'element-ui'
|
||||
import { getCITypeGroupById, getCITypes } from '@/modules/cmdb/api/CIType'
|
||||
import { getCIHistory, judgeItsmInstalled } from '@/modules/cmdb/api/history'
|
||||
import { getCIById, searchCI } from '@/modules/cmdb/api/ci'
|
||||
import CiDetailAttrContent from './ciDetailAttrContent.vue'
|
||||
import CiDetailRelation from './ciDetailRelation.vue'
|
||||
|
||||
import RelationMixin from './ciDetailMixin/relationMixin.js'
|
||||
|
||||
import CIDetailTitle from './ciDetailComponent/ciDetailTitle.vue'
|
||||
import CIDetailTableTitle from './ciDetailComponent/ciDetailTableTitle.vue'
|
||||
import CIDetailAttrContent from './ciDetailAttrContent.vue'
|
||||
import CIRelationTable from './ciDetailComponent/ciRelationTable.vue'
|
||||
import CIDetailRelation from './ciDetailRelation.vue'
|
||||
import TriggerTable from '../../operation_history/modules/triggerTable.vue'
|
||||
import RelatedItsmTable from './ciDetailRelatedItsmTable.vue'
|
||||
import CiRollbackForm from './ciRollbackForm.vue'
|
||||
import CIRollbackForm from './ciRollbackForm.vue'
|
||||
|
||||
export default {
|
||||
name: 'CiDetailTab',
|
||||
mixins: [RelationMixin],
|
||||
components: {
|
||||
ElDescriptions: Descriptions,
|
||||
ElDescriptionsItem: DescriptionsItem,
|
||||
CiDetailAttrContent,
|
||||
CiDetailRelation,
|
||||
CIDetailAttrContent,
|
||||
CIDetailRelation,
|
||||
TriggerTable,
|
||||
RelatedItsmTable,
|
||||
CiRollbackForm,
|
||||
CIRollbackForm,
|
||||
CIDetailTitle,
|
||||
CIDetailTableTitle,
|
||||
CIRelationTable
|
||||
},
|
||||
props: {
|
||||
typeId: {
|
||||
@@ -218,15 +276,11 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async create(ciId, activeTabKey = 'tab_1', ciDetailRelationKey = '1') {
|
||||
async create(ciId, activeTabKey = 'tab_1') {
|
||||
this.initQueryLoading = true
|
||||
this.activeTabKey = activeTabKey
|
||||
if (activeTabKey === 'tab_2') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ciDetailRelation.activeKey = ciDetailRelationKey
|
||||
})
|
||||
}
|
||||
this.ciId = ciId
|
||||
|
||||
await this.getCI()
|
||||
await this.judgeItsmInstalled()
|
||||
if (this.hasPermission) {
|
||||
@@ -234,9 +288,8 @@ export default {
|
||||
this.getCIHistory()
|
||||
const ciTypeRes = await getCITypes()
|
||||
this.ci_types = ciTypeRes.ci_types
|
||||
if (this.activeTabKey === 'tab_2') {
|
||||
this.$refs.ciDetailRelation.init(true)
|
||||
}
|
||||
|
||||
this.initRelationData(this.typeId, this.ciId)
|
||||
}
|
||||
this.initQueryLoading = false
|
||||
},
|
||||
@@ -509,23 +562,68 @@ export default {
|
||||
.ant-tabs-extra-content {
|
||||
line-height: 44px;
|
||||
}
|
||||
.ci-detail-attr {
|
||||
.ci-detail-table {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 24px;
|
||||
.el-descriptions-item__content {
|
||||
cursor: default;
|
||||
|
||||
&-attr {
|
||||
width: 100%;
|
||||
margin-top: 14px;
|
||||
|
||||
&-wrap {
|
||||
padding: 13px;
|
||||
width: 100%;
|
||||
border: solid 1px #E4E7ED;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&-group {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&-name {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: @text-color_1;
|
||||
margin-bottom: 7.5px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: @text-color_3;
|
||||
display: inline-flex;
|
||||
max-width: 100%;
|
||||
|
||||
&-text {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
&-colon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
overflow-wrap: break-word;
|
||||
|
||||
&:hover a {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
.el-descriptions:first-child > .el-descriptions__header {
|
||||
margin-top: 0;
|
||||
}
|
||||
.el-descriptions__header {
|
||||
margin-bottom: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user