feat(cmdb-ui): attributes relation (#463)

This commit is contained in:
dagongren 2024-04-03 15:27:54 +08:00 committed by GitHub
parent 099ddd6ca9
commit c7d4bec988
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 360 additions and 44 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -59,7 +59,6 @@ export default {
width: 100%; width: 100%;
.two-column-layout-sidebar { .two-column-layout-sidebar {
height: 100%; height: 100%;
border-radius: 15px;
overflow-y: auto; overflow-y: auto;
} }
.two-column-layout-main { .two-column-layout-main {

View File

@ -30,11 +30,11 @@ export function getRelationTypes(CITypeID, parameter) {
}) })
} }
export function createRelation(parentId, childrenId, relationTypeId, constraint) { export function createRelation(parentId, childrenId, data) {
return axios({ return axios({
url: `/v0.1/ci_type_relations/${parentId}/${childrenId}`, url: `/v0.1/ci_type_relations/${parentId}/${childrenId}`,
method: 'post', method: 'post',
data: { relation_type_id: relationTypeId, constraint } data
}) })
} }
@ -42,7 +42,6 @@ export function deleteRelation(parentId, childrenId) {
return axios({ return axios({
url: `/v0.1/ci_type_relations/${parentId}/${childrenId}`, url: `/v0.1/ci_type_relations/${parentId}/${childrenId}`,
method: 'delete' method: 'delete'
}) })
} }

View File

@ -187,7 +187,14 @@ const cmdb_en = {
downloadType: 'Download CIType', downloadType: 'Download CIType',
deleteCIType: 'Delete CIType', deleteCIType: 'Delete CIType',
otherGroupTips: 'Non sortable within the other group', otherGroupTips: 'Non sortable within the other group',
filterTips: 'click to show {name}' filterTips: 'click to show {name}',
attributeAssociation: 'Attribute Association',
attributeAssociationTip1: 'Automatically establish relationships through the attributes except password, json and multiple of two models',
attributeAssociationTip2: 'Double click to edit',
attributeAssociationTip3: 'Two Attributes must be selected',
attributeAssociationTip4: 'Please select a attribute from Source CIType',
attributeAssociationTip5: 'Please select a attribute from Target CIType',
}, },
components: { components: {
unselectAttributes: 'Unselected', unselectAttributes: 'Unselected',

View File

@ -129,7 +129,7 @@ const cmdb_zh = {
addRelation: '新增关系', addRelation: '新增关系',
sourceCIType: '源模型', sourceCIType: '源模型',
sourceCITypeTips: '请选择源模型', sourceCITypeTips: '请选择源模型',
dstCIType: '目标模型', dstCIType: '目标模型',
dstCITypeTips: '请选择目标模型', dstCITypeTips: '请选择目标模型',
relationType: '关联类型', relationType: '关联类型',
relationTypeTips: '请选择关联类型', relationTypeTips: '请选择关联类型',
@ -187,7 +187,13 @@ const cmdb_zh = {
downloadType: '下载模型', downloadType: '下载模型',
deleteCIType: '删除模型', deleteCIType: '删除模型',
otherGroupTips: '其他分组属性不可排序', otherGroupTips: '其他分组属性不可排序',
filterTips: '点击可仅查看{name}属性' filterTips: '点击可仅查看{name}属性',
attributeAssociation: '属性关联',
attributeAssociationTip1: '通过2个模型的属性值(除密码、json、多值)来自动建立关系',
attributeAssociationTip2: '双击可编辑',
attributeAssociationTip3: '属性关联必须选择两个属性',
attributeAssociationTip4: '请选择原模型属性',
attributeAssociationTip5: '请选择目标模型属性',
}, },
components: { components: {
unselectAttributes: '未选属性', unselectAttributes: '未选属性',

View File

@ -4,7 +4,7 @@
<a-tab-pane key="1" :tab="$t('cmdb.ciType.attributes')"> <a-tab-pane key="1" :tab="$t('cmdb.ciType.attributes')">
<AttributesTable ref="attributesTable" :CITypeId="CITypeId" :CITypeName="CITypeName"></AttributesTable> <AttributesTable ref="attributesTable" :CITypeId="CITypeId" :CITypeName="CITypeName"></AttributesTable>
</a-tab-pane> </a-tab-pane>
<a-tab-pane forceRender key="2" :tab="$t('cmdb.ciType.relation')"> <a-tab-pane key="2" :tab="$t('cmdb.ciType.relation')">
<RelationTable :CITypeId="CITypeId" :CITypeName="CITypeName"></RelationTable> <RelationTable :CITypeId="CITypeId" :CITypeName="CITypeName"></RelationTable>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="3" :tab="$t('cmdb.ciType.trigger')"> <a-tab-pane key="3" :tab="$t('cmdb.ciType.trigger')">

View File

@ -15,11 +15,16 @@
:data="tableData" :data="tableData"
size="small" size="small"
show-overflow show-overflow
show-header-overflow
highlight-hover-row highlight-hover-row
keep-source keep-source
:max-height="windowHeight - 180" :height="windowHeight - 190"
class="ops-stripe-table" class="ops-stripe-table"
:row-class-name="rowClass" :row-class-name="rowClass"
:edit-config="{ trigger: 'dblclick', mode: 'cell', showIcon: false }"
resizable
@edit-closed="handleEditClose"
@edit-actived="handleEditActived"
> >
<vxe-column field="source_ci_type_name" :title="$t('cmdb.ciType.sourceCIType')"></vxe-column> <vxe-column field="source_ci_type_name" :title="$t('cmdb.ciType.sourceCIType')"></vxe-column>
<vxe-column field="relation_type" :title="$t('cmdb.ciType.relationType')"> <vxe-column field="relation_type" :title="$t('cmdb.ciType.relationType')">
@ -40,6 +45,59 @@
<span v-else>{{ constraintMap[row.constraint] }}</span> <span v-else>{{ constraintMap[row.constraint] }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column :width="250" field="attributeAssociation" :edit-render="{}">
<template #header>
<span>
<a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')">
<a><a-icon type="question-circle"/></a>
</a-tooltip>
{{ $t('cmdb.ciType.attributeAssociation') }}
<span :style="{ fontSize: '10px', fontWeight: 'normal' }" class="text-color-4">{{
$t('cmdb.ciType.attributeAssociationTip2')
}}</span>
</span>
</template>
<template #default="{row}">
<span
v-if="row.parent_attr_id && row.child_attr_id"
>{{ getAttrNameById(row.isParent ? row.attributes : attributes, row.parent_attr_id) }}=>
{{ getAttrNameById(row.isParent ? attributes : row.attributes, row.child_attr_id) }}</span
>
</template>
<template #edit="{ row }">
<div style="display:inline-flex;align-items:center;">
<a-select
allowClear
size="small"
v-model="parent_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
>
<a-select-option
v-for="attr in filterAttributes(row.isParent ? row.attributes : attributes)"
:key="attr.id"
>
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
=>
<a-select
allowClear
size="small"
v-model="child_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
>
<a-select-option
v-for="attr in filterAttributes(row.isParent ? attributes : row.attributes)"
:key="attr.id"
>
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
</div>
</template>
</vxe-column>
<vxe-column field="operation" :title="$t('operation')" width="100"> <vxe-column field="operation" :title="$t('operation')" width="100">
<template #default="{row}"> <template #default="{row}">
<a-space v-if="!row.isParent && row.source_ci_type_id"> <a-space v-if="!row.isParent && row.source_ci_type_id">
@ -63,12 +121,13 @@
:visible="visible" :visible="visible"
@cancel="onClose" @cancel="onClose"
@ok="handleSubmit" @ok="handleSubmit"
width="500px" width="700px"
> >
<a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }"> <a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
<a-form-item :label="$t('cmdb.ciType.sourceCIType')"> <a-form-item :label="$t('cmdb.ciType.sourceCIType')">
<a-select <a-select
name="source_ci_type_id" name="source_ci_type_id"
:placeholder="$t('cmdb.ciType.sourceCITypeTips')"
v-decorator="[ v-decorator="[
'source_ci_type_id', 'source_ci_type_id',
{ rules: [{ required: true, message: $t('cmdb.ciType.sourceCITypeTips') }] }, { rules: [{ required: true, message: $t('cmdb.ciType.sourceCITypeTips') }] },
@ -83,8 +142,10 @@
<a-select <a-select
showSearch showSearch
name="ci_type_id" name="ci_type_id"
:placeholder="$t('cmdb.ciType.dstCITypeTips')"
v-decorator="['ci_type_id', { rules: [{ required: true, message: $t('cmdb.ciType.dstCITypeTips') }] }]" v-decorator="['ci_type_id', { rules: [{ required: true, message: $t('cmdb.ciType.dstCITypeTips') }] }]"
:filterOption="filterOption" :filterOption="filterOption"
@change="changeChild"
> >
<a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in CITypes"> <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in CITypes">
{{ CIType.alias || CIType.name }} {{ CIType.alias || CIType.name }}
@ -95,6 +156,7 @@
<a-form-item :label="$t('cmdb.ciType.relationType')"> <a-form-item :label="$t('cmdb.ciType.relationType')">
<a-select <a-select
name="relation_type_id" name="relation_type_id"
:placeholder="$t('cmdb.ciType.relationTypeTips')"
v-decorator="[ v-decorator="[
'relation_type_id', 'relation_type_id',
{ rules: [{ required: true, message: $t('cmdb.ciType.relationTypeTips') }] }, { rules: [{ required: true, message: $t('cmdb.ciType.relationTypeTips') }] },
@ -105,9 +167,9 @@
}}</a-select-option> }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item :label="$t('cmdb.ciType.relationConstraint')"> <a-form-item :label="$t('cmdb.ciType.relationConstraint')">
<a-select <a-select
:placeholder="$t('cmdb.ciType.relationConstraintTips')"
v-decorator="[ v-decorator="[
'constraint', 'constraint',
{ rules: [{ required: true, message: $t('cmdb.ciType.relationConstraintTips') }] }, { rules: [{ required: true, message: $t('cmdb.ciType.relationConstraintTips') }] },
@ -118,6 +180,39 @@
<a-select-option value="2">{{ $t('cmdb.ciType.many2Many') }}</a-select-option> <a-select-option value="2">{{ $t('cmdb.ciType.many2Many') }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item :label="$t('cmdb.ciType.attributeAssociation')">
<a-row>
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip4')"
allowClear
v-decorator="['parent_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(attributes)" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="2" :style="{ textAlign: 'center' }">
=>
</a-col>
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip5')"
allowClear
v-decorator="['child_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
</a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
<CMDBGrant ref="cmdbGrant" resourceType="CITypeRelation" app_id="cmdb" /> <CMDBGrant ref="cmdbGrant" resourceType="CITypeRelation" app_id="cmdb" />
@ -133,6 +228,8 @@ import {
getRelationTypes, getRelationTypes,
} from '@/modules/cmdb/api/CITypeRelation' } from '@/modules/cmdb/api/CITypeRelation'
import { getCITypes } from '@/modules/cmdb/api/CIType' import { getCITypes } from '@/modules/cmdb/api/CIType'
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import CMDBGrant from '../../components/cmdbGrant' import CMDBGrant from '../../components/cmdbGrant'
export default { export default {
@ -163,6 +260,10 @@ export default {
relationTypes: [], relationTypes: [],
tableData: [], tableData: [],
parentTableData: [], parentTableData: [],
attributes: [],
parent_attr_id: undefined,
child_attr_id: undefined,
modalChildAttributes: [],
} }
}, },
computed: { computed: {
@ -181,14 +282,20 @@ export default {
}, },
}, },
async mounted() { async mounted() {
getCITypeAttributesById(this.CITypeId).then((res) => {
this.attributes = res?.attributes ?? []
})
this.getCITypes() this.getCITypes()
this.getRelationTypes() this.getRelationTypes()
if (!this.isInGrantComp) {
await this.getCITypeParent()
}
this.getData() this.getData()
}, },
methods: { methods: {
async getData() {
if (!this.isInGrantComp) {
await this.getCITypeParent()
}
this.getCITypeChildren()
},
async getCITypeParent() { async getCITypeParent() {
await getCITypeParent(this.CITypeId).then((res) => { await getCITypeParent(this.CITypeId).then((res) => {
this.parentTableData = res.parents.map((item) => { this.parentTableData = res.parents.map((item) => {
@ -201,7 +308,7 @@ export default {
}) })
}) })
}, },
getData() { getCITypeChildren() {
getCITypeChildren(this.CITypeId).then((res) => { getCITypeChildren(this.CITypeId).then((res) => {
const data = res.children.map((obj) => { const data = res.children.map((obj) => {
return { return {
@ -230,12 +337,9 @@ export default {
handleDelete(record) { handleDelete(record) {
deleteRelation(record.source_ci_type_id, record.id).then((res) => { deleteRelation(record.source_ci_type_id, record.id).then((res) => {
this.$message.success(this.$t('deleteSuccess')) this.$message.success(this.$t('deleteSuccess'))
this.handleOk() this.getData()
}) })
}, },
handleOk() {
this.getData()
},
handleCreate() { handleCreate() {
this.drawerTitle = this.$t('cmdb.ciType.addRelation') this.drawerTitle = this.$t('cmdb.ciType.addRelation')
@ -258,14 +362,29 @@ export default {
if (!err) { if (!err) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('Received values of form: ', values) console.log('Received values of form: ', values)
const {
source_ci_type_id,
ci_type_id,
relation_type_id,
constraint,
parent_attr_id = undefined,
child_attr_id = undefined,
} = values
createRelation(values.source_ci_type_id, values.ci_type_id, values.relation_type_id, values.constraint).then( if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
(res) => { this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
this.$message.success(this.$t('addSuccess')) return
this.onClose() }
this.handleOk() createRelation(source_ci_type_id, ci_type_id, {
} relation_type_id,
) constraint,
parent_attr_id,
child_attr_id,
}).then((res) => {
this.$message.success(this.$t('addSuccess'))
this.onClose()
this.getData()
})
} }
}) })
}, },
@ -283,6 +402,42 @@ export default {
if (row.isDivider) return 'relation-table-divider' if (row.isDivider) return 'relation-table-divider'
if (row.isParent) return 'relation-table-parent' if (row.isParent) return 'relation-table-parent'
}, },
handleEditActived({ row }) {
this.parent_attr_id = row?.parent_attr_id ?? undefined
this.child_attr_id = row?.child_attr_id ?? undefined
},
async handleEditClose({ row }) {
const { source_ci_type_id: parentId, id: childrenId, constraint, relation_type } = row
const { parent_attr_id, child_attr_id } = this
const _find = this.relationTypes.find((item) => item.name === relation_type)
const relation_type_id = _find?.id
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
await createRelation(row.isParent ? childrenId : parentId, row.isParent ? parentId : childrenId, {
relation_type_id,
constraint,
parent_attr_id,
child_attr_id,
}).finally(() => {
this.getData()
})
},
getAttrNameById(attributes, id) {
const _find = attributes.find((attr) => attr.id === id)
return _find?.alias ?? _find?.name ?? id
},
changeChild(value) {
this.form.setFieldsValue({ child_attr_id: undefined })
getCITypeAttributesById(value).then((res) => {
this.modalChildAttributes = res?.attributes ?? []
})
},
filterAttributes(attributes) {
// filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
},
}, },
} }
</script> </script>

View File

@ -10,7 +10,7 @@
:visible="visible" :visible="visible"
@cancel="onClose" @cancel="onClose"
@ok="handleSubmit" @ok="handleSubmit"
width="500px" width="700px"
> >
<a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }"> <a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
<a-form-item :label="$t('cmdb.ciType.sourceCIType')"> <a-form-item :label="$t('cmdb.ciType.sourceCIType')">
@ -69,6 +69,39 @@
<a-select-option value="2">{{ $t('cmdb.ciType.many2Many') }}</a-select-option> <a-select-option value="2">{{ $t('cmdb.ciType.many2Many') }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item :label="$t('cmdb.ciType.attributeAssociation')">
<a-row>
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip4')"
allowClear
v-decorator="['parent_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(modalParentAttributes)" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="2" :style="{ textAlign: 'center' }">
=>
</a-col>
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip5')"
allowClear
v-decorator="['child_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
</a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
</div> </div>
@ -80,6 +113,8 @@ import { searchResourceType } from '@/modules/acl/api/resource'
import { getCITypeGroupsConfig } from '@/modules/cmdb/api/ciTypeGroup' import { getCITypeGroupsConfig } from '@/modules/cmdb/api/ciTypeGroup'
import { getCITypes } from '@/modules/cmdb/api/CIType' import { getCITypes } from '@/modules/cmdb/api/CIType'
import { createRelation, deleteRelation, getCITypeChildren, getRelationTypes } from '@/modules/cmdb/api/CITypeRelation' import { createRelation, deleteRelation, getCITypeChildren, getRelationTypes } from '@/modules/cmdb/api/CITypeRelation'
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
export default { export default {
name: 'Index', name: 'Index',
components: { components: {
@ -101,6 +136,9 @@ export default {
sourceCITypeId: undefined, sourceCITypeId: undefined,
targetCITypeId: undefined, targetCITypeId: undefined,
modalParentAttributes: [],
modalChildAttributes: [],
} }
}, },
computed: { computed: {
@ -206,13 +244,29 @@ export default {
e.preventDefault() e.preventDefault()
this.form.validateFields((err, values) => { this.form.validateFields((err, values) => {
if (!err) { if (!err) {
createRelation(values.source_ci_type_id, values.ci_type_id, values.relation_type_id, values.constraint).then( const {
(res) => { source_ci_type_id,
this.$message.success(this.$t('addSuccess')) ci_type_id,
this.onClose() relation_type_id,
this.handleOk() constraint,
} parent_attr_id = undefined,
) child_attr_id = undefined,
} = values
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
createRelation(source_ci_type_id, ci_type_id, {
relation_type_id,
constraint,
parent_attr_id,
child_attr_id,
}).then((res) => {
this.$message.success(this.$t('addSuccess'))
this.onClose()
this.handleOk()
})
} }
}) })
this.sourceCITypeId = undefined this.sourceCITypeId = undefined
@ -230,13 +284,25 @@ export default {
}, },
handleSourceTypeChange(value) { handleSourceTypeChange(value) {
this.sourceCITypeId = value this.sourceCITypeId = value
this.form.setFieldsValue({ parent_attr_id: undefined })
getCITypeAttributesById(value).then((res) => {
this.modalParentAttributes = res?.attributes ?? []
})
}, },
handleTargetTypeChange(value) { handleTargetTypeChange(value) {
this.targetCITypeId = value this.targetCITypeId = value
this.form.setFieldsValue({ child_attr_id: undefined })
getCITypeAttributesById(value).then((res) => {
this.modalChildAttributes = res?.attributes ?? []
})
}, },
filterOption(input, option) { filterOption(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}, },
filterAttributes(attributes) {
// filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
},
}, },
} }
</script> </script>

View File

@ -10,6 +10,9 @@
:height="`${windowHeight - 160}px`" :height="`${windowHeight - 160}px`"
:data="tableData" :data="tableData"
:sort-config="{ defaultSort: { field: 'created_at', order: 'desc' } }" :sort-config="{ defaultSort: { field: 'created_at', order: 'desc' } }"
:edit-config="{ trigger: 'dblclick', mode: 'cell', showIcon: false }"
@edit-closed="handleEditClose"
@edit-actived="handleEditActived"
> >
<vxe-column field="created_at" :title="$t('created_at')" sortable width="159px"></vxe-column> <vxe-column field="created_at" :title="$t('created_at')" sortable width="159px"></vxe-column>
<vxe-column field="parent.alias" :title="$t('cmdb.ciType.sourceCIType')"></vxe-column> <vxe-column field="parent.alias" :title="$t('cmdb.ciType.sourceCIType')"></vxe-column>
@ -26,8 +29,59 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="child.alias" :title="$t('cmdb.ciType.dstCIType')"></vxe-column> <vxe-column field="child.alias" :title="$t('cmdb.ciType.dstCIType')"></vxe-column>
<vxe-column field="constraint" :title="$t('cmdb.ciType.relationConstraint')"></vxe-column> <vxe-column field="constraint" :title="$t('cmdb.ciType.relationConstraint')">
<vxe-column field="authorization" :title="$t('operation')" width="89px"> <template #default="{row}">
{{ handleConstraint(row.constraint) }}
</template>
</vxe-column>
<vxe-column :width="250" field="attributeAssociation" :edit-render="{}">
<template #header>
<span>
<a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')">
<a><a-icon type="question-circle"/></a>
</a-tooltip>
{{ $t('cmdb.ciType.attributeAssociation') }}
<span :style="{ fontSize: '10px', fontWeight: 'normal' }" class="text-color-4">{{
$t('cmdb.ciType.attributeAssociationTip2')
}}</span>
</span>
</template>
<template #default="{row}">
<span
v-if="row.parent_attr_id && row.child_attr_id"
>{{ getAttrNameById(type2attributes[row.parent_id], row.parent_attr_id) }}=>
{{ getAttrNameById(type2attributes[row.child_id], row.child_attr_id) }}</span
>
</template>
<template #edit="{ row }">
<div style="display:inline-flex;align-items:center;">
<a-select
allowClear
size="small"
v-model="parent_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
>
<a-select-option v-for="attr in filterAttributes(type2attributes[row.parent_id])" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
=>
<a-select
allowClear
size="small"
v-model="child_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
>
<a-select-option v-for="attr in filterAttributes(type2attributes[row.child_id])" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
</div>
</template>
</vxe-column>
<vxe-column field="operation" :title="$t('operation')" width="89px">
<template #default="{ row }"> <template #default="{ row }">
<a-space> <a-space>
<a @click="handleOpenGrant(row)"><a-icon type="user-add"/></a> <a @click="handleOpenGrant(row)"><a-icon type="user-add"/></a>
@ -43,7 +97,7 @@
</template> </template>
<script> <script>
import { getCITypeRelations, deleteRelation } from '@/modules/cmdb/api/CITypeRelation' import { getCITypeRelations, deleteRelation, createRelation } from '@/modules/cmdb/api/CITypeRelation'
import { getRelationTypes } from '@/modules/cmdb/api/relationType' import { getRelationTypes } from '@/modules/cmdb/api/relationType'
import CMDBGrant from '../../../components/cmdbGrant' import CMDBGrant from '../../../components/cmdbGrant'
@ -53,6 +107,9 @@ export default {
drawerVisible: false, drawerVisible: false,
tableData: [], tableData: [],
relationTypeList: null, relationTypeList: null,
type2attributes: {},
parent_attr_id: undefined,
child_attr_id: undefined,
} }
}, },
components: { components: {
@ -79,11 +136,9 @@ export default {
await this.getMainData() await this.getMainData()
}, },
async getMainData() { async getMainData() {
const res = await getCITypeRelations() const { relations, type2attributes } = await getCITypeRelations()
res.forEach((item) => { this.tableData = relations
item.constraint = this.handleConstraint(item.constraint) this.type2attributes = type2attributes
})
this.tableData = res
}, },
// 获取关系 // 获取关系
async getRelationTypes() { async getRelationTypes() {
@ -115,6 +170,34 @@ export default {
this.refresh() this.refresh()
}) })
}, },
handleEditActived({ row }) {
this.parent_attr_id = row?.parent_attr_id ?? undefined
this.child_attr_id = row?.child_attr_id ?? undefined
},
async handleEditClose({ row }) {
const { parent_id, child_id, constraint, relation_type_id } = row
const { parent_attr_id = undefined, child_attr_id = undefined } = this
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
await createRelation(parent_id, child_id, {
relation_type_id,
constraint,
parent_attr_id,
child_attr_id,
}).finally(() => {
this.getMainData()
})
},
getAttrNameById(attributes, id) {
const _find = attributes.find((attr) => attr.id === id)
return _find?.alias ?? _find?.name ?? id
},
filterAttributes(attributes) {
// filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
},
}, },
} }
</script> </script>

View File

@ -154,7 +154,7 @@ export default {
this.getViewsData() this.getViewsData()
}, },
async getMainData() { async getMainData() {
const ciTypeRelations = await getCITypeRelations() const { relations: ciTypeRelations } = await getCITypeRelations()
const nodes = [] const nodes = []
const links = [] const links = []
ciTypeRelations.forEach((item) => { ciTypeRelations.forEach((item) => {

View File

@ -7,7 +7,7 @@
@click="clickNode" @click="clickNode"
> >
<span class="relation-views-node-switch"> <span class="relation-views-node-switch">
<a-icon v-if="childLength && !isLeaf" :type="switchIcon"></a-icon> <a-icon v-if="!isLeaf" :type="switchIcon"></a-icon>
</span> </span>
<span class="relation-views-node-content"> <span class="relation-views-node-content">
<a-checkbox @click.stop="clickCheckbox" class="relation-views-node-checkbox" v-if="showCheckbox" /> <a-checkbox @click.stop="clickCheckbox" class="relation-views-node-checkbox" v-if="showCheckbox" />

View File

@ -910,6 +910,7 @@ body {
.vue-treeselect__multi-value, .vue-treeselect__multi-value,
.vue-treeselect__multi-value-item { .vue-treeselect__multi-value-item {
line-height: var(--custom-multiple-lineHeight); line-height: var(--custom-multiple-lineHeight);
line-height: 18px;
} }
} }
.custom-treeselect.vue-treeselect--open-below .vue-treeselect__menu { .custom-treeselect.vue-treeselect--open-below .vue-treeselect__menu {