Merge pull request #528 from veops/feat/dev_ui_240604

feat(ui): update model relation
This commit is contained in:
Leo Song 2024-06-04 12:05:42 +08:00 committed by GitHub
commit df3dc7cb1b
5 changed files with 1694 additions and 1255 deletions

View File

@ -196,6 +196,7 @@ const cmdb_en = {
attributeAssociationTip3: 'Two Attributes must be selected', attributeAssociationTip3: 'Two Attributes must be selected',
attributeAssociationTip4: 'Please select a attribute from Source CIType', attributeAssociationTip4: 'Please select a attribute from Source CIType',
attributeAssociationTip5: 'Please select a attribute from Target CIType', attributeAssociationTip5: 'Please select a attribute from Target CIType',
attributeAssociationTip6: 'Cannot be deleted again.',
show: 'show attribute', show: 'show attribute',
setAsShow: 'Set as show attribute', setAsShow: 'Set as show attribute',
cancelSetAsShow: 'Cancel show attribute', cancelSetAsShow: 'Cancel show attribute',

View File

@ -196,6 +196,7 @@ const cmdb_zh = {
attributeAssociationTip3: '属性关联必须选择两个属性', attributeAssociationTip3: '属性关联必须选择两个属性',
attributeAssociationTip4: '请选择原模型属性', attributeAssociationTip4: '请选择原模型属性',
attributeAssociationTip5: '请选择目标模型属性', attributeAssociationTip5: '请选择目标模型属性',
attributeAssociationTip6: '不可再删除',
show: '展示属性', show: '展示属性',
setAsShow: '设置为展示属性', setAsShow: '设置为展示属性',
cancelSetAsShow: '取消设置为展示属性', cancelSetAsShow: '取消设置为展示属性',

View File

@ -1,5 +1,5 @@
<template> <template>
<div :style="{ padding: '0 20px 20px' }"> <div class="relation-table" :style="{ padding: '0 20px 20px' }">
<a-button <a-button
v-if="!isInGrantComp" v-if="!isInGrantComp"
style="margin-bottom: 10px" style="margin-bottom: 10px"
@ -18,6 +18,7 @@
highlight-hover-row highlight-hover-row
keep-source keep-source
class="ops-stripe-table" class="ops-stripe-table"
min-height="500"
:row-class-name="rowClass" :row-class-name="rowClass"
:edit-config="{ trigger: 'dblclick', mode: 'cell', showIcon: false }" :edit-config="{ trigger: 'dblclick', mode: 'cell', showIcon: false }"
resizable resizable
@ -43,7 +44,7 @@
<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="{}"> <vxe-column :width="300" field="attributeAssociation" :edit-render="{}">
<template #header> <template #header>
<span> <span>
<a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')"> <a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')">
@ -56,43 +57,73 @@
</span> </span>
</template> </template>
<template #default="{row}"> <template #default="{row}">
<span <template
v-if="row.parent_attr_id && row.child_attr_id" v-for="item in row.parentAndChildAttrList"
>{{ getAttrNameById(row.isParent ? row.attributes : attributes, row.parent_attr_id) }}=>
{{ getAttrNameById(row.isParent ? attributes : row.attributes, row.child_attr_id) }}</span
> >
<div
:key="item.id"
v-if="item.parentAttrId && item.childAttrId"
>
{{ getAttrNameById(row.isParent ? row.attributes : attributes, item.parentAttrId) }}=>
{{ getAttrNameById(row.isParent ? attributes : row.attributes, item.childAttrId) }}
</div>
</template>
</template> </template>
<template #edit="{ row }"> <template #edit="{ row }">
<div style="display:inline-flex;align-items:center;"> <div
v-for="item in tableAttrList"
:key="item.id"
class="table-attribute-row"
>
<a-select <a-select
allowClear allowClear
size="small" size="small"
v-model="parent_attr_id" v-model="item.parentAttrId"
:getPopupContainer="(trigger) => trigger.parentNode" :getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }" :style="{ width: '100px' }"
show-search
optionFilterProp="title"
> >
<a-select-option <a-select-option
v-for="attr in filterAttributes(row.isParent ? row.attributes : attributes)" v-for="attr in filterAttributes(row.isParent ? row.attributes : attributes)"
:key="attr.id" :key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
> >
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
=> <span class="table-attribute-row-link">=></span>
<a-select <a-select
allowClear allowClear
size="small" size="small"
v-model="child_attr_id" v-model="item.childAttrId"
:getPopupContainer="(trigger) => trigger.parentNode" :getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }" :style="{ width: '100px' }"
show-search
optionFilterProp="title"
> >
<a-select-option <a-select-option
v-for="attr in filterAttributes(row.isParent ? attributes : row.attributes)" v-for="attr in filterAttributes(row.isParent ? attributes : row.attributes)"
:key="attr.id" :key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
> >
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
<a
class="table-attribute-row-action"
@click="removeTableAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="table-attribute-row-action"
@click="addTableAttr"
>
<a-icon type="plus-circle" />
</a>
</div> </div>
</template> </template>
</vxe-column> </vxe-column>
@ -179,13 +210,16 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item :label="$t('cmdb.ciType.attributeAssociation')"> <a-form-item :label="$t('cmdb.ciType.attributeAssociation')">
<a-row> <a-row
<a-col :span="11"> v-for="item in modalAttrList"
:key="item.id"
>
<a-col :span="10">
<a-form-item> <a-form-item>
<a-select <a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip4')" :placeholder="$t('cmdb.ciType.attributeAssociationTip4')"
allowClear allowClear
v-decorator="['parent_attr_id', { rules: [{ required: false }] }]" v-model="item.parentAttrId"
> >
<a-select-option v-for="attr in filterAttributes(attributes)" :key="attr.id"> <a-select-option v-for="attr in filterAttributes(attributes)" :key="attr.id">
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
@ -196,12 +230,12 @@
<a-col :span="2" :style="{ textAlign: 'center' }"> <a-col :span="2" :style="{ textAlign: 'center' }">
=> =>
</a-col> </a-col>
<a-col :span="11"> <a-col :span="9">
<a-form-item> <a-form-item>
<a-select <a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip5')" :placeholder="$t('cmdb.ciType.attributeAssociationTip5')"
allowClear allowClear
v-decorator="['child_attr_id', { rules: [{ required: false }] }]" v-model="item.childAttrId"
> >
<a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id"> <a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id">
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
@ -209,6 +243,20 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="3">
<a
class="modal-attribute-action"
@click="removeModalAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="modal-attribute-action"
@click="addModalAttr"
>
<a-icon type="plus-circle" />
</a>
</a-col>
</a-row> </a-row>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -227,6 +275,7 @@ import {
} 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 { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import { v4 as uuidv4 } from 'uuid'
import CMDBGrant from '../../components/cmdbGrant' import CMDBGrant from '../../components/cmdbGrant'
@ -259,8 +308,8 @@ export default {
tableData: [], tableData: [],
parentTableData: [], parentTableData: [],
attributes: [], attributes: [],
parent_attr_id: undefined, tableAttrList: [],
child_attr_id: undefined, modalAttrList: [],
modalChildAttributes: [], modalChildAttributes: [],
} }
}, },
@ -297,8 +346,11 @@ export default {
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) => {
const parentAndChildAttrList = this.handleAttrList(item)
return { return {
...item, ...item,
parentAndChildAttrList,
source_ci_type_name: this.CITypeName, source_ci_type_name: this.CITypeName,
source_ci_type_id: this.CITypeId, source_ci_type_id: this.CITypeId,
isParent: true, isParent: true,
@ -309,8 +361,11 @@ export default {
getCITypeChildren() { getCITypeChildren() {
getCITypeChildren(this.CITypeId).then((res) => { getCITypeChildren(this.CITypeId).then((res) => {
const data = res.children.map((obj) => { const data = res.children.map((obj) => {
const parentAndChildAttrList = this.handleAttrList(obj)
return { return {
...obj, ...obj,
parentAndChildAttrList,
source_ci_type_name: this.CITypeName, source_ci_type_name: this.CITypeName,
source_ci_type_id: this.CITypeId, source_ci_type_id: this.CITypeId,
} }
@ -322,6 +377,20 @@ export default {
} }
}) })
}, },
handleAttrList(data) {
const length = Math.min(data?.parent_attr_ids?.length || 0, data.child_attr_ids?.length || 0)
const parentAndChildAttrList = []
for (let i = 0; i < length; i++) {
parentAndChildAttrList.push({
id: uuidv4(),
parentAttrId: data?.parent_attr_ids?.[i] ?? '',
childAttrId: data?.child_attr_ids?.[i] ?? ''
})
}
return parentAndChildAttrList
},
getCITypes() { getCITypes() {
getCITypes().then((res) => { getCITypes().then((res) => {
this.CITypes = res.ci_types this.CITypes = res.ci_types
@ -342,6 +411,13 @@ export default {
handleCreate() { handleCreate() {
this.drawerTitle = this.$t('cmdb.ciType.addRelation') this.drawerTitle = this.$t('cmdb.ciType.addRelation')
this.visible = true this.visible = true
this.$set(this, 'modalAttrList', [
{
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
}
])
this.$nextTick(() => { this.$nextTick(() => {
this.form.setFieldsValue({ this.form.setFieldsValue({
source_ci_type_id: this.CITypeId, source_ci_type_id: this.CITypeId,
@ -365,19 +441,22 @@ export default {
ci_type_id, ci_type_id,
relation_type_id, relation_type_id,
constraint, constraint,
parent_attr_id = undefined,
child_attr_id = undefined,
} = values } = values
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) { const {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3')) parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.modalAttrList)
if (!validate) {
return return
} }
createRelation(source_ci_type_id, ci_type_id, { createRelation(source_ci_type_id, ci_type_id, {
relation_type_id, relation_type_id,
constraint, constraint,
parent_attr_id, parent_attr_ids,
child_attr_id, child_attr_ids,
}).then((res) => { }).then((res) => {
this.$message.success(this.$t('addSuccess')) this.$message.success(this.$t('addSuccess'))
this.onClose() this.onClose()
@ -386,6 +465,37 @@ export default {
} }
}) })
}, },
/**
* 校验属性列表
* @param {*} attrList
*/
handleValidateAttrList(attrList) {
const parent_attr_ids = []
const child_attr_ids = []
attrList.map((attr) => {
if (attr.parentAttrId) {
parent_attr_ids.push(attr.parentAttrId)
}
if (attr.childAttrId) {
child_attr_ids.push(attr.childAttrId)
}
})
if (parent_attr_ids.length !== child_attr_ids.length) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return {
validate: false
}
}
return {
validate: true,
parent_attr_ids,
child_attr_ids
}
},
handleOpenGrant(record) { handleOpenGrant(record) {
this.$refs.cmdbGrant.open({ this.$refs.cmdbGrant.open({
name: `${record.source_ci_type_name} -> ${record.name}`, name: `${record.source_ci_type_name} -> ${record.name}`,
@ -401,23 +511,45 @@ export default {
if (row.isParent) return 'relation-table-parent' if (row.isParent) return 'relation-table-parent'
}, },
handleEditActived({ row }) { handleEditActived({ row }) {
this.parent_attr_id = row?.parent_attr_id ?? undefined const tableAttrList = []
this.child_attr_id = row?.child_attr_id ?? undefined
const length = Math.min(row?.parent_attr_ids?.length || 0, row.child_attr_ids?.length || 0)
if (length) {
for (let i = 0; i < length; i++) {
tableAttrList.push({
id: uuidv4(),
parentAttrId: row?.parent_attr_ids?.[i] ?? undefined,
childAttrId: row?.child_attr_ids?.[i] ?? undefined
})
}
} else {
tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
}
this.$set(this, 'tableAttrList', tableAttrList)
}, },
async handleEditClose({ row }) { async handleEditClose({ row }) {
const { source_ci_type_id: parentId, id: childrenId, constraint, relation_type } = 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 _find = this.relationTypes.find((item) => item.name === relation_type)
const relation_type_id = _find?.id 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')) const {
parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.tableAttrList)
if (!validate) {
return return
} }
await createRelation(row.isParent ? childrenId : parentId, row.isParent ? parentId : childrenId, { await createRelation(row.isParent ? childrenId : parentId, row.isParent ? parentId : childrenId, {
relation_type_id, relation_type_id,
constraint, constraint,
parent_attr_id, parent_attr_ids,
child_attr_id, child_attr_ids,
}).finally(() => { }).finally(() => {
this.getData() this.getData()
}) })
@ -427,7 +559,9 @@ export default {
return _find?.alias ?? _find?.name ?? id return _find?.alias ?? _find?.name ?? id
}, },
changeChild(value) { changeChild(value) {
this.form.setFieldsValue({ child_attr_id: undefined }) this.modalAttrList.forEach((item) => {
item.childAttrId = undefined
})
getCITypeAttributesById(value).then((res) => { getCITypeAttributesById(value).then((res) => {
this.modalChildAttributes = res?.attributes ?? [] this.modalChildAttributes = res?.attributes ?? []
}) })
@ -436,10 +570,75 @@ export default {
// filter password/json/is_list // filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6') return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
}, },
addTableAttr() {
this.tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeTableAttr(id) {
if (this.tableAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.tableAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.tableAttrList.splice(index, 1)
}
},
addModalAttr() {
this.modalAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeModalAttr(id) {
if (this.modalAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.modalAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.modalAttrList.splice(index, 1)
}
}
}, },
} }
</script> </script>
<style lang="less" scoped>
.relation-table {
/deep/ .vxe-cell {
max-height: max-content !important;
}
}
.table-attribute-row {
display: inline-flex;
align-items: center;
margin-top: 5px;
&:last-child {
margin-bottom: 5px;
}
&-link {
margin: 0 5px;
}
&-action {
margin-left: 5px;
}
}
.modal-attribute-action {
margin-left: 5px;
}
</style>
<style lang="less"> <style lang="less">
.ops-stripe-table .vxe-body--row.row--stripe.relation-table-divider { .ops-stripe-table .vxe-body--row.row--stripe.relation-table-divider {
background-color: #b1b8d3 !important; background-color: #b1b8d3 !important;

View File

@ -70,13 +70,16 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item :label="$t('cmdb.ciType.attributeAssociation')"> <a-form-item :label="$t('cmdb.ciType.attributeAssociation')">
<a-row> <a-row
<a-col :span="11"> v-for="item in modalAttrList"
:key="item.id"
>
<a-col :span="10">
<a-form-item> <a-form-item>
<a-select <a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip4')" :placeholder="$t('cmdb.ciType.attributeAssociationTip4')"
allowClear allowClear
v-decorator="['parent_attr_id', { rules: [{ required: false }] }]" v-model="item.parentAttrId"
> >
<a-select-option v-for="attr in filterAttributes(modalParentAttributes)" :key="attr.id"> <a-select-option v-for="attr in filterAttributes(modalParentAttributes)" :key="attr.id">
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
@ -87,12 +90,12 @@
<a-col :span="2" :style="{ textAlign: 'center' }"> <a-col :span="2" :style="{ textAlign: 'center' }">
=> =>
</a-col> </a-col>
<a-col :span="11"> <a-col :span="9">
<a-form-item> <a-form-item>
<a-select <a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip5')" :placeholder="$t('cmdb.ciType.attributeAssociationTip5')"
allowClear allowClear
v-decorator="['child_attr_id', { rules: [{ required: false }] }]" v-model="item.childAttrId"
> >
<a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id"> <a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id">
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
@ -100,6 +103,20 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="3">
<a
class="modal-attribute-action"
@click="removeModalAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="modal-attribute-action"
@click="addModalAttr"
>
<a-icon type="plus-circle" />
</a>
</a-col>
</a-row> </a-row>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -114,6 +131,7 @@ 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' import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import { v4 as uuidv4 } from 'uuid'
export default { export default {
name: 'Index', name: 'Index',
@ -139,6 +157,7 @@ export default {
modalParentAttributes: [], modalParentAttributes: [],
modalChildAttributes: [], modalChildAttributes: [],
modalAttrList: [],
} }
}, },
computed: { computed: {
@ -228,6 +247,13 @@ export default {
handleCreate() { handleCreate() {
this.drawerTitle = this.$t('cmdb.ciType.addRelation') this.drawerTitle = this.$t('cmdb.ciType.addRelation')
this.visible = true this.visible = true
this.$set(this, 'modalAttrList', [
{
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
}
])
this.$nextTick(() => { this.$nextTick(() => {
this.form.setFieldsValue({ this.form.setFieldsValue({
source_ci_type_id: this.sourceCITypeId, source_ci_type_id: this.sourceCITypeId,
@ -249,19 +275,22 @@ export default {
ci_type_id, ci_type_id,
relation_type_id, relation_type_id,
constraint, constraint,
parent_attr_id = undefined,
child_attr_id = undefined,
} = values } = values
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) { const {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3')) parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.modalAttrList)
if (!validate) {
return return
} }
createRelation(source_ci_type_id, ci_type_id, { createRelation(source_ci_type_id, ci_type_id, {
relation_type_id, relation_type_id,
constraint, constraint,
parent_attr_id, parent_attr_ids,
child_attr_id, child_attr_ids,
}).then((res) => { }).then((res) => {
this.$message.success(this.$t('addSuccess')) this.$message.success(this.$t('addSuccess'))
this.onClose() this.onClose()
@ -272,6 +301,37 @@ export default {
this.sourceCITypeId = undefined this.sourceCITypeId = undefined
this.targetCITypeId = undefined this.targetCITypeId = undefined
}, },
/**
* 校验属性列表
* @param {*} attrList
*/
handleValidateAttrList(attrList) {
const parent_attr_ids = []
const child_attr_ids = []
attrList.map((attr) => {
if (attr.parentAttrId) {
parent_attr_ids.push(attr.parentAttrId)
}
if (attr.childAttrId) {
child_attr_ids.push(attr.childAttrId)
}
})
if (parent_attr_ids.length !== child_attr_ids.length) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return {
validate: false
}
}
return {
validate: true,
parent_attr_ids,
child_attr_ids
}
},
handleOk() { handleOk() {
this.$refs.table.refresh() this.$refs.table.refresh()
}, },
@ -284,14 +344,18 @@ export default {
}, },
handleSourceTypeChange(value) { handleSourceTypeChange(value) {
this.sourceCITypeId = value this.sourceCITypeId = value
this.form.setFieldsValue({ parent_attr_id: undefined }) this.modalAttrList.forEach((item) => {
item.parentAttrId = undefined
})
getCITypeAttributesById(value).then((res) => { getCITypeAttributesById(value).then((res) => {
this.modalParentAttributes = res?.attributes ?? [] this.modalParentAttributes = res?.attributes ?? []
}) })
}, },
handleTargetTypeChange(value) { handleTargetTypeChange(value) {
this.targetCITypeId = value this.targetCITypeId = value
this.form.setFieldsValue({ child_attr_id: undefined }) this.modalAttrList.forEach((item) => {
item.childAttrId = undefined
})
getCITypeAttributesById(value).then((res) => { getCITypeAttributesById(value).then((res) => {
this.modalChildAttributes = res?.attributes ?? [] this.modalChildAttributes = res?.attributes ?? []
}) })
@ -303,12 +367,30 @@ export default {
// filter password/json/is_list // filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6') return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
}, },
addModalAttr() {
this.modalAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeModalAttr(id) {
if (this.modalAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.modalAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.modalAttrList.splice(index, 1)
}
}
}, },
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.model-relation { .model-relation {
background-color: #fff; background-color: #fff;
border-radius: @border-radius-box; border-radius: @border-radius-box;
@ -316,4 +398,8 @@ export default {
height: calc(100vh - 64px); height: calc(100vh - 64px);
margin-bottom: -24px; margin-bottom: -24px;
} }
.modal-attribute-action {
margin-left: 5px;
}
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="model-relation-table">
<vxe-table <vxe-table
ref="xTable" ref="xTable"
stripe stripe
@ -7,6 +7,7 @@
show-header-overflow show-header-overflow
show-overflow show-overflow
resizable resizable
:scroll-y="{enabled: false}"
: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' } }"
@ -34,7 +35,7 @@
{{ handleConstraint(row.constraint) }} {{ handleConstraint(row.constraint) }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column :width="250" field="attributeAssociation" :edit-render="{}"> <vxe-column :width="300" field="attributeAssociation" :edit-render="{}">
<template #header> <template #header>
<span> <span>
<a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')"> <a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')">
@ -47,37 +48,73 @@
</span> </span>
</template> </template>
<template #default="{row}"> <template #default="{row}">
<span <template
v-if="row.parent_attr_id && row.child_attr_id" v-for="item in row.parentAndChildAttrList"
>{{ getAttrNameById(type2attributes[row.parent_id], row.parent_attr_id) }}=>
{{ getAttrNameById(type2attributes[row.child_id], row.child_attr_id) }}</span
> >
<div
:key="item.id"
v-if="item.parentAttrId && item.childAttrId"
>
{{ getAttrNameById(type2attributes[row.parent_id], item.parentAttrId) }}=>
{{ getAttrNameById(type2attributes[row.child_id], item.childAttrId) }}
</div>
</template>
</template> </template>
<template #edit="{ row }"> <template #edit="{ row }">
<div style="display:inline-flex;align-items:center;"> <div
v-for="item in tableAttrList"
:key="item.id"
class="table-attribute-row"
>
<a-select <a-select
allowClear allowClear
size="small" size="small"
v-model="parent_attr_id" v-model="item.parentAttrId"
:getPopupContainer="(trigger) => trigger.parentNode" :getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }" :style="{ width: '100px' }"
show-search
optionFilterProp="title"
> >
<a-select-option v-for="attr in filterAttributes(type2attributes[row.parent_id])" :key="attr.id"> <a-select-option
v-for="attr in filterAttributes(type2attributes[row.parent_id])"
:key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
>
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
=> <span class="table-attribute-row-link">=></span>
<a-select <a-select
allowClear allowClear
size="small" size="small"
v-model="child_attr_id" v-model="item.childAttrId"
:getPopupContainer="(trigger) => trigger.parentNode" :getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }" :style="{ width: '100px' }"
show-search
optionFilterProp="title"
> >
<a-select-option v-for="attr in filterAttributes(type2attributes[row.child_id])" :key="attr.id"> <a-select-option
v-for="attr in filterAttributes(type2attributes[row.child_id])"
:key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
>
{{ attr.alias || attr.name }} {{ attr.alias || attr.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
<a
class="table-attribute-row-action"
@click="removeTableAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="table-attribute-row-action"
@click="addTableAttr"
>
<a-icon type="plus-circle" />
</a>
</div> </div>
</template> </template>
</vxe-column> </vxe-column>
@ -97,6 +134,7 @@
</template> </template>
<script> <script>
import { v4 as uuidv4 } from 'uuid'
import { getCITypeRelations, deleteRelation, createRelation } 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'
@ -108,8 +146,7 @@ export default {
tableData: [], tableData: [],
relationTypeList: null, relationTypeList: null,
type2attributes: {}, type2attributes: {},
parent_attr_id: undefined, tableAttrList: [],
child_attr_id: undefined,
} }
}, },
components: { components: {
@ -137,9 +174,29 @@ export default {
}, },
async getMainData() { async getMainData() {
const { relations, type2attributes } = await getCITypeRelations() const { relations, type2attributes } = await getCITypeRelations()
this.tableData = relations this.tableData = relations.map((item) => {
const parentAndChildAttrList = this.handleAttrList(item)
return {
...item,
parentAndChildAttrList
}
})
this.type2attributes = type2attributes this.type2attributes = type2attributes
}, },
handleAttrList(data) {
const length = Math.min(data?.parent_attr_ids?.length || 0, data.child_attr_ids?.length || 0)
const parentAndChildAttrList = []
for (let i = 0; i < length; i++) {
parentAndChildAttrList.push({
id: uuidv4(),
parentAttrId: data?.parent_attr_ids?.[i] ?? '',
childAttrId: data?.child_attr_ids?.[i] ?? ''
})
}
return parentAndChildAttrList
},
// 获取关系 // 获取关系
async getRelationTypes() { async getRelationTypes() {
const res = await getRelationTypes() const res = await getRelationTypes()
@ -171,21 +228,75 @@ export default {
}) })
}, },
handleEditActived({ row }) { handleEditActived({ row }) {
this.parent_attr_id = row?.parent_attr_id ?? undefined const tableAttrList = []
this.child_attr_id = row?.child_attr_id ?? undefined
const length = Math.min(row?.parent_attr_ids?.length || 0, row.child_attr_ids?.length || 0)
if (length) {
for (let i = 0; i < length; i++) {
tableAttrList.push({
id: uuidv4(),
parentAttrId: row?.parent_attr_ids?.[i] ?? undefined,
childAttrId: row?.child_attr_ids?.[i] ?? undefined
})
}
} else {
tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
}
console.log('handleEditActived', tableAttrList)
this.$set(this, 'tableAttrList', tableAttrList)
}, },
/**
* 校验属性列表
* @param {*} attrList
*/
handleValidateAttrList(attrList) {
const parent_attr_ids = []
const child_attr_ids = []
attrList.map((attr) => {
if (attr.parentAttrId) {
parent_attr_ids.push(attr.parentAttrId)
}
if (attr.childAttrId) {
child_attr_ids.push(attr.childAttrId)
}
})
if (parent_attr_ids.length !== child_attr_ids.length) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return {
validate: false
}
}
return {
validate: true,
parent_attr_ids,
child_attr_ids
}
},
async handleEditClose({ row }) { async handleEditClose({ row }) {
const { parent_id, child_id, constraint, relation_type_id } = 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)) { const {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3')) parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.tableAttrList)
if (!validate) {
return return
} }
await createRelation(parent_id, child_id, { await createRelation(parent_id, child_id, {
relation_type_id, relation_type_id,
constraint, constraint,
parent_attr_id, parent_attr_ids,
child_attr_id, child_attr_ids,
}).finally(() => { }).finally(() => {
this.getMainData() this.getMainData()
}) })
@ -198,8 +309,49 @@ export default {
// filter password/json/is_list // filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6') return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
}, },
addTableAttr() {
this.tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeTableAttr(id) {
if (this.tableAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.tableAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.tableAttrList.splice(index, 1)
}
},
}, },
} }
</script> </script>
<style></style> <style lang="less" scoped>
.relation-table {
/deep/ .vxe-cell {
max-height: max-content !important;
}
}
.table-attribute-row {
display: inline-flex;
align-items: center;
margin-top: 5px;
&:last-child {
margin-bottom: 5px;
}
&-link {
margin: 0 5px;
}
&-action {
margin-left: 5px;
}
}
</style>