mirror of https://github.com/veops/cmdb.git
feat(cmdb-ui):service tree grant (#425)
This commit is contained in:
parent
482d34993b
commit
42feb4b862
|
@ -20,6 +20,7 @@
|
|||
}
|
||||
}
|
||||
"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</treeselect>
|
||||
</div>
|
||||
|
@ -42,6 +43,7 @@
|
|||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
|
@ -80,6 +82,7 @@
|
|||
@select="(value) => handleChangeExp(value, item, index)"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
|
@ -103,6 +106,7 @@
|
|||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
|
@ -125,6 +129,7 @@
|
|||
v-model="item.min"
|
||||
:style="{ width: '78px' }"
|
||||
:placeholder="$t('min')"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
~
|
||||
<a-input
|
||||
|
@ -133,6 +138,7 @@
|
|||
v-model="item.max"
|
||||
:style="{ width: '78px' }"
|
||||
:placeholder="$t('max')"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</a-input-group>
|
||||
<a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
|
||||
|
@ -155,6 +161,7 @@
|
|||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</treeselect>
|
||||
<a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
|
||||
|
@ -166,19 +173,22 @@
|
|||
:placeholder="item.exp === 'in' || item.exp === '~in' ? $t('cmdbFilterComp.split', { separator: ';' }) : ''"
|
||||
class="ops-input"
|
||||
:style="{ width: '175px' }"
|
||||
:disabled="disabled"
|
||||
></a-input>
|
||||
<div v-else :style="{ width: '175px' }"></div>
|
||||
<a-tooltip :title="$t('copy')">
|
||||
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('delete')">
|
||||
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('cmdbFilterComp.addHere')" v-if="needAddHere">
|
||||
<a class="operation" @click="handleAddRuleAt(item)"><a-icon type="plus-circle"/></a>
|
||||
</a-tooltip>
|
||||
<template v-if="!disabled">
|
||||
<a-tooltip :title="$t('copy')">
|
||||
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('delete')">
|
||||
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('cmdbFilterComp.addHere')" v-if="needAddHere">
|
||||
<a class="operation" @click="handleAddRuleAt(item)"><a-icon type="plus-circle"/></a>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
<div class="table-filter-add">
|
||||
<div class="table-filter-add" v-if="!disabled">
|
||||
<a @click="handleAddRule">+ {{ $t('new') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -211,6 +221,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
:needAddHere="needAddHere"
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
<a-divider :style="{ margin: '10px 0' }" />
|
||||
<div style="width:554px">
|
||||
|
@ -31,6 +32,7 @@
|
|||
v-else
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -69,6 +71,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -113,6 +113,10 @@ export default {
|
|||
},
|
||||
|
||||
mounted() {
|
||||
const paneLengthPixel = localStorage.getItem(`${this.appName}-paneLengthPixel`)
|
||||
if (paneLengthPixel) {
|
||||
this.$emit('update:paneLengthPixel', Number(paneLengthPixel))
|
||||
}
|
||||
this.parentContainer = document.querySelector(`.${this.appName}`)
|
||||
if (this.isExpanded) {
|
||||
document.querySelector(`.${this.appName} .pane-two`).style.display = 'none'
|
||||
|
|
|
@ -25,6 +25,7 @@ export default {
|
|||
deleting: 'Deleting',
|
||||
deletingTip: 'Deleting, total of {total}, {successNum} succeeded, {errorNum} failed',
|
||||
grant: 'Grant',
|
||||
revoke: 'Revoke',
|
||||
login_at: 'Login At',
|
||||
logout_at: 'Logout At',
|
||||
createSuccess: 'Create Success',
|
||||
|
|
|
@ -25,6 +25,7 @@ export default {
|
|||
deleting: '正在删除',
|
||||
deletingTip: '正在删除,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||
grant: '授权',
|
||||
revoke: '回收',
|
||||
login_at: '登录时间',
|
||||
logout_at: '登出时间',
|
||||
createSuccess: '创建成功',
|
||||
|
|
|
@ -223,3 +223,10 @@ export function deleteCiTypeInheritance(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getCITypeIcons() {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types/icons',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,9 +14,16 @@
|
|||
<script>
|
||||
import EmployeeTransfer from '@/components/EmployeeTransfer'
|
||||
import RoleTransfer from '@/components/RoleTransfer'
|
||||
|
||||
export default {
|
||||
name: 'GrantModal',
|
||||
components: { EmployeeTransfer, RoleTransfer },
|
||||
props: {
|
||||
customTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
|
@ -25,6 +32,9 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
title() {
|
||||
if (this.customTitle) {
|
||||
return this.customTitle
|
||||
}
|
||||
if (this.type === 'depart') {
|
||||
return this.$t('cmdb.components.grantUser')
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
{ value: 2, label: $t('cmdb.components.customize'), layout: 'vertical' },
|
||||
{ value: 3, label: $t('cmdb.components.none') },
|
||||
]"
|
||||
v-model="radioValue"
|
||||
:value="radioValue"
|
||||
@change="changeRadioValue"
|
||||
>
|
||||
<template slot="extra_2" v-if="radioValue === 2">
|
||||
<treeselect
|
||||
|
@ -128,6 +129,9 @@ export default {
|
|||
this.visible = true
|
||||
this.colType = colType
|
||||
this.row = row
|
||||
this.form = {
|
||||
name: '',
|
||||
}
|
||||
if (this.colType === 'read_ci') {
|
||||
await getCITypeAttributesByTypeIds({ type_ids: this.CITypeId }).then((res) => {
|
||||
this.canSearchPreferenceAttrList = res.attributes.filter((item) => item.value_type !== '6')
|
||||
|
@ -149,10 +153,6 @@ export default {
|
|||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.form = {
|
||||
name: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
async handleOk() {
|
||||
|
@ -198,6 +198,13 @@ export default {
|
|||
}
|
||||
this.expression = expression
|
||||
},
|
||||
changeRadioValue(value) {
|
||||
if (this.id_filter) {
|
||||
this.$message.warning(this.$t('cmdb.serviceTree.grantedByServiceTreeTips'))
|
||||
} else {
|
||||
this.radioValue = value
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<template>
|
||||
<a-modal :visible="visible" @cancel="handleCancel" @ok="handleOK" :title="$t('revoke')">
|
||||
<a-form-model :model="form" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-model-item :label="$t('user')">
|
||||
<EmployeeTreeSelect
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
:multiple="true"
|
||||
v-model="form.users"
|
||||
:placeholder="$t('cmdb.serviceTree.userPlaceholder')"
|
||||
:idType="2"
|
||||
departmentKey="acl_rid"
|
||||
employeeKey="acl_rid"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('role')">
|
||||
<treeselect
|
||||
v-model="form.roles"
|
||||
:multiple="true"
|
||||
:options="filterAllRoles"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
:limit="10"
|
||||
:limitText="(count) => `+ ${count}`"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
zIndex="1050"
|
||||
:placeholder="$t('cmdb.serviceTree.rolePlaceholder')"
|
||||
@search-change="searchRole"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||
import { getAllDepAndEmployee } from '@/api/company'
|
||||
import { searchRole } from '@/modules/acl/api/role'
|
||||
|
||||
export default {
|
||||
name: 'RevokeModal',
|
||||
components: { EmployeeTreeSelect },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
form: {
|
||||
users: undefined,
|
||||
roles: undefined,
|
||||
},
|
||||
allTreeDepAndEmp: [],
|
||||
allRoles: [],
|
||||
filterAllRoles: [],
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
provide_allTreeDepAndEmp: () => {
|
||||
return this.allTreeDepAndEmp
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAllDepAndEmployee()
|
||||
this.loadRoles()
|
||||
},
|
||||
methods: {
|
||||
async loadRoles() {
|
||||
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
|
||||
this.allRoles = res.roles
|
||||
this.filterAllRoles = this.allRoles.slice(0, 100)
|
||||
},
|
||||
getAllDepAndEmployee() {
|
||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||
this.allTreeDepAndEmp = res
|
||||
})
|
||||
},
|
||||
open() {
|
||||
this.visible = true
|
||||
this.$nextTick(() => {
|
||||
this.form = {
|
||||
users: undefined,
|
||||
roles: undefined,
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
},
|
||||
searchRole(searchQuery) {
|
||||
this.filterAllRoles = this.allRoles
|
||||
.filter((item) => item.name.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
.slice(0, 100)
|
||||
},
|
||||
handleOK() {
|
||||
this.$emit('handleRevoke', this.form)
|
||||
this.handleCancel()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -52,7 +52,7 @@
|
|||
:style="{ color: fuzzySearch ? '#2f54eb' : '', cursor: 'pointer' }"
|
||||
@click="emitRefresh"
|
||||
/>
|
||||
<a-tooltip slot="prefix" placement="bottom" :overlayStyle="{ maxWidth: '550px' }">
|
||||
<a-tooltip slot="prefix" placement="bottom" :overlayStyle="{ maxWidth: '550px', whiteSpace: 'pre-line' }">
|
||||
<template slot="title">
|
||||
{{ $t('cmdb.components.ciSearchTips') }}
|
||||
</template>
|
||||
|
@ -97,6 +97,7 @@
|
|||
</a-space>
|
||||
</div>
|
||||
<a-space>
|
||||
<slot name="extraContent"></slot>
|
||||
<a-button @click="reset" size="small">{{ $t('reset') }}</a-button>
|
||||
<a-tooltip :title="$t('cmdb.components.attributeDesc')" v-if="type === 'relationView'">
|
||||
<a
|
||||
|
@ -191,6 +192,9 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
// toggleAdvanced() {
|
||||
// this.advanced = !this.advanced
|
||||
// },
|
||||
getCITypeGroups() {
|
||||
getCITypeGroups({ need_other: true }).then((res) => {
|
||||
this.ciTypeGroup = res
|
||||
|
|
|
@ -46,8 +46,9 @@ const cmdb_en = {
|
|||
selectDefaultOrderAttr: 'Select default sorting attributes',
|
||||
asec: 'Forward order',
|
||||
desc: 'Reverse order',
|
||||
uniqueKey: 'Uniquely Identifies',
|
||||
uniqueKey: 'Unique Identifies',
|
||||
uniqueKeySelect: 'Please select a unique identifier',
|
||||
uniqueKeyTips: 'json/password/computed/choice can not be unique identifies',
|
||||
notfound: 'Can\'t find what you want?',
|
||||
cannotDeleteGroupTips: 'There is data under this group and cannot be deleted!',
|
||||
confirmDeleteGroup: 'Are you sure you want to delete group [{groupName}]?',
|
||||
|
@ -223,7 +224,7 @@ const cmdb_en = {
|
|||
pleaseSearch: 'Please search',
|
||||
conditionFilter: 'Conditional filtering',
|
||||
attributeDesc: 'Attribute Description',
|
||||
ciSearchTips: '1. JSON attributes cannot be searched<br />2. If the search content includes commas, they need to be escaped,<br />3. Only index attributes are searched, non-index attributes use conditional filtering',
|
||||
ciSearchTips: '1. JSON/password/link attributes cannot be searched\n2. If the search content includes commas, they need to be escaped\n3. Only index attributes are searched, non-index attributes use conditional filtering',
|
||||
ciSearchTips2: 'For example: q=hostname:*0.0.0.0*',
|
||||
subCIType: 'Subscription CIType',
|
||||
already: 'already',
|
||||
|
@ -466,7 +467,7 @@ const cmdb_en = {
|
|||
tips3: 'Please select the fields that need to be modified',
|
||||
tips4: 'At least one field must be selected',
|
||||
tips5: 'Search name | alias',
|
||||
tips6: 'Speed up retrieval, full-text search possible, no need to use conditional filtering\n\n json currently does not support indexing \n\nText characters longer than 190 cannot be indexed',
|
||||
tips6: 'Speed up retrieval, full-text search possible, no need to use conditional filtering\n\n json/link/password currently does not support indexing \n\nText characters longer than 190 cannot be indexed',
|
||||
tips7: 'The form of expression is a drop-down box, and the value must be in the predefined value',
|
||||
tips8: 'Multiple values, such as intranet IP',
|
||||
tips9: 'For front-end only',
|
||||
|
@ -483,6 +484,16 @@ const cmdb_en = {
|
|||
alert1: 'The administrator has not configured the ServiceTree(relation view), or you do not have permission to access it!',
|
||||
copyFailed: 'Copy failed',
|
||||
deleteRelationConfirm: 'Confirm to remove selected {name} from current relationship?',
|
||||
batch: 'Batch',
|
||||
grantTitle: 'Grant(read)',
|
||||
userPlaceholder: 'Please select users',
|
||||
rolePlaceholder: 'Please select roles',
|
||||
grantedByServiceTree: 'Granted By Service Tree:',
|
||||
grantedByServiceTreeTips: 'Please delete id_filter in Servive Tree',
|
||||
peopleHasRead: 'Personnel authorized to read:',
|
||||
authorizationPolicy: 'CI Authorization Policy:',
|
||||
idAuthorizationPolicy: 'Authorized by node:',
|
||||
view: 'View permissions'
|
||||
},
|
||||
tree: {
|
||||
tips1: 'Please go to Preference page first to complete your subscription!',
|
||||
|
|
|
@ -48,6 +48,7 @@ const cmdb_zh = {
|
|||
desc: '倒序',
|
||||
uniqueKey: '唯一标识',
|
||||
uniqueKeySelect: '请选择唯一标识',
|
||||
uniqueKeyTips: 'json、密码、计算属性、预定义值属性不能作为唯一标识',
|
||||
notfound: '找不到想要的?',
|
||||
cannotDeleteGroupTips: '该分组下有数据, 不能删除!',
|
||||
confirmDeleteGroup: '确定要删除分组 【{groupName}】 吗?',
|
||||
|
@ -223,7 +224,7 @@ const cmdb_zh = {
|
|||
pleaseSearch: '请查找',
|
||||
conditionFilter: '条件过滤',
|
||||
attributeDesc: '属性说明',
|
||||
ciSearchTips: '1. json属性不能搜索<br />2. 搜索内容包括逗号, 则需转义 ,<br />3. 只搜索索引属性, 非索引属性使用条件过滤',
|
||||
ciSearchTips: '1. json、密码、链接属性不能搜索\n2. 搜索内容包括逗号, 则需转义\n3. 只搜索索引属性, 非索引属性使用条件过滤',
|
||||
ciSearchTips2: '例: q=hostname:*0.0.0.0*',
|
||||
subCIType: '订阅模型',
|
||||
already: '已',
|
||||
|
@ -465,7 +466,7 @@ const cmdb_zh = {
|
|||
tips3: '请选择需要修改的字段',
|
||||
tips4: '必须至少选择一个字段',
|
||||
tips5: '搜索 名称 | 别名',
|
||||
tips6: '加快检索, 可以全文搜索, 无需使用条件过滤\n\n json目前不支持建索引 \n\n文本字符长度超过190不能建索引',
|
||||
tips6: '加快检索, 可以全文搜索, 无需使用条件过滤\n\n json、链接、密码目前不支持建索引 \n\n文本字符长度超过190不能建索引',
|
||||
tips7: '表现形式是下拉框, 值必须在预定义值里',
|
||||
tips8: '多值, 比如内网IP',
|
||||
tips9: '仅针对前端',
|
||||
|
@ -482,6 +483,16 @@ const cmdb_zh = {
|
|||
alert1: '管理员 还未配置业务关系, 或者你无权限访问!',
|
||||
copyFailed: '复制失败',
|
||||
deleteRelationConfirm: '确认将选中的 {name} 从当前关系中删除?',
|
||||
batch: '批量操作',
|
||||
grantTitle: '授权(查看权限)',
|
||||
userPlaceholder: '请选择用户',
|
||||
rolePlaceholder: '请选择角色',
|
||||
grantedByServiceTree: '服务树授权:',
|
||||
grantedByServiceTreeTips: '请先在服务树里删掉节点授权',
|
||||
peopleHasRead: '当前有查看权限的人员:',
|
||||
authorizationPolicy: '实例授权策略:',
|
||||
idAuthorizationPolicy: '按节点授权的:',
|
||||
view: '查看权限'
|
||||
},
|
||||
tree: {
|
||||
tips1: '请先到 我的订阅 页面完成订阅!',
|
||||
|
|
|
@ -220,6 +220,7 @@ export default {
|
|||
if (otherGroupAttr.length) {
|
||||
_attributesByGroup.push({ id: -1, name: this.$t('other'), attributes: otherGroupAttr })
|
||||
}
|
||||
console.log(otherGroupAttr, _attributesByGroup)
|
||||
this.attributesByGroup = _attributesByGroup
|
||||
})
|
||||
},
|
||||
|
@ -296,6 +297,38 @@ export default {
|
|||
_this.$emit('reload', { ci_id: res.ci_id })
|
||||
})
|
||||
}
|
||||
|
||||
// this.form.validateFields((err, values) => {
|
||||
// if (err) {
|
||||
// _this.$message.error('字段填写不符合要求!')
|
||||
// return
|
||||
// }
|
||||
// Object.keys(values).forEach((k) => {
|
||||
// if (Object.prototype.toString.call(values[k]) === '[object Object]' && values[k]) {
|
||||
// values[k] = values[k].format('YYYY-MM-DD HH:mm:ss')
|
||||
// }
|
||||
// const _tempFind = this.attributeList.find((item) => item.name === k)
|
||||
// if (_tempFind.value_type === '6') {
|
||||
// values[k] = values[k] ? JSON.parse(values[k]) : undefined
|
||||
// }
|
||||
// })
|
||||
|
||||
// if (_this.action === 'update') {
|
||||
// _this.$emit('submit', values)
|
||||
// return
|
||||
// }
|
||||
// values.ci_type = _this.typeId
|
||||
// console.log(values)
|
||||
// this.attributesByGroup.forEach((group) => {
|
||||
// this.$refs[`createInstanceFormByGroup_${group.id}`][0].getData()
|
||||
// })
|
||||
// console.log(1111)
|
||||
// // addCI(values).then((res) => {
|
||||
// // _this.$message.success('新增成功!')
|
||||
// // _this.visible = false
|
||||
// // _this.$emit('reload')
|
||||
// // })
|
||||
// })
|
||||
},
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
|
@ -363,6 +396,9 @@ export default {
|
|||
this.batchUpdateLists.splice(_idx, 1)
|
||||
}
|
||||
},
|
||||
// filterOption(input, option) {
|
||||
// return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
// },
|
||||
handleFocusInput(e, attr) {
|
||||
console.log(attr)
|
||||
const _tempFind = this.attributeList.find((item) => item.name === attr.name)
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_2">
|
||||
<span slot="tab"><a-icon type="branches" />{{ $t('cmdb.relation') }}</span>
|
||||
<div :style="{ height: '100%', padding: '24px' }">
|
||||
<div :style="{ height: '100%', padding: '24px', overflow: 'auto' }">
|
||||
<CiDetailRelation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
|
|
|
@ -270,7 +270,17 @@
|
|||
</div>
|
||||
</el-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('cmdb.ciType.uniqueKey')">
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip :title="$t('cmdb.ciType.uniqueKeyTips')">
|
||||
<a-icon
|
||||
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
|
||||
type="question-circle"
|
||||
theme="filled"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<span>{{ $t('cmdb.ciType.uniqueKey') }}</span>
|
||||
</template>
|
||||
<el-select
|
||||
size="small"
|
||||
filterable
|
||||
|
|
|
@ -2,14 +2,55 @@
|
|||
<div :style="{ marginBottom: '-24px', overflow: 'hidden' }">
|
||||
<div v-if="relationViews.name2id && relationViews.name2id.length" class="relation-views-wrapper">
|
||||
<div class="cmdb-views-header">
|
||||
<span class="cmdb-views-header-title">{{ $route.meta.name }}</span>
|
||||
<span
|
||||
class="cmdb-views-header-title"
|
||||
>{{ $route.meta.name }}
|
||||
<div
|
||||
class="ops-list-batch-action"
|
||||
:style="{ backgroundColor: '#c0ceeb' }"
|
||||
v-if="showBatchLevel !== null && batchTreeKey && batchTreeKey.length"
|
||||
>
|
||||
<span
|
||||
@click="
|
||||
() => {
|
||||
$refs.grantModal.open('depart')
|
||||
}
|
||||
"
|
||||
>{{ $t('grant') }}</span
|
||||
>
|
||||
<a-divider type="vertical" />
|
||||
<span
|
||||
@click="
|
||||
() => {
|
||||
$refs.revokeModal.open()
|
||||
}
|
||||
"
|
||||
>{{ $t('revoke') }}</span
|
||||
>
|
||||
<template v-if="showBatchLevel > 0">
|
||||
<a-divider type="vertical" />
|
||||
<span @click="batchDeleteCIRelationFromTree">{{ $t('delete') }}</span>
|
||||
</template>
|
||||
<a-divider type="vertical" />
|
||||
<span
|
||||
@click="
|
||||
() => {
|
||||
showBatchLevel = null
|
||||
batchTreeKey = []
|
||||
}
|
||||
"
|
||||
>{{ $t('cancel') }}</span
|
||||
>
|
||||
<span>{{ $t('selectRows', { rows: batchTreeKey.length }) }}</span>
|
||||
</div>
|
||||
</span>
|
||||
<a-button size="small" icon="user-add" type="primary" ghost @click="handlePerm">{{ $t('grant') }}</a-button>
|
||||
</div>
|
||||
<SplitPane
|
||||
:min="200"
|
||||
:max="500"
|
||||
:paneLengthPixel.sync="paneLengthPixel"
|
||||
appName="cmdb-relation-views"
|
||||
:appName="`cmdb-relation-views-${viewId}`"
|
||||
triggerColor="#F0F5FF"
|
||||
:triggerLength="18"
|
||||
>
|
||||
|
@ -24,7 +65,6 @@
|
|||
@drop="onDrop"
|
||||
:expandedKeys="expandedKeys"
|
||||
>
|
||||
<a-icon slot="switcherIcon" type="down" />
|
||||
<template #title="{ key: treeKey, title, isLeaf }">
|
||||
<ContextMenu
|
||||
:title="title"
|
||||
|
@ -35,7 +75,10 @@
|
|||
:id2type="relationViews.id2type"
|
||||
@onContextMenuClick="onContextMenuClick"
|
||||
@onNodeClick="onNodeClick"
|
||||
:ciTypes="ciTypes"
|
||||
:ciTypeIcons="ciTypeIcons"
|
||||
:showBatchLevel="showBatchLevel"
|
||||
:batchTreeKey="batchTreeKey"
|
||||
@clickCheckbox="clickCheckbox"
|
||||
/>
|
||||
</template>
|
||||
</a-tree>
|
||||
|
@ -313,9 +356,8 @@
|
|||
v-else-if="relationViews.name2id && !relationViews.name2id.length"
|
||||
></a-alert>
|
||||
<AddTableModal ref="addTableModal" @reload="reload" />
|
||||
<!-- <GrantDrawer ref="grantDrawer" resourceTypeName="RelationView" app_id="cmdb" /> -->
|
||||
<CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" />
|
||||
|
||||
<GrantModal ref="grantModal" @handleOk="onRelationViewGrant" :customTitle="$t('cmdb.serviceTree.grantTitle')" />
|
||||
<CiDetailDrawer ref="detail" :typeId="Number(currentTypeId[0])" />
|
||||
<create-instance-form
|
||||
ref="create"
|
||||
|
@ -325,11 +367,12 @@
|
|||
/>
|
||||
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
||||
<BatchDownload ref="batchDownload" @batchDownload="batchDownload" />
|
||||
<ReadPermissionsModal ref="readPermissionsModal" />
|
||||
<RevokeModal ref="revokeModal" @handleRevoke="handleRevoke" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable no-useless-escape */
|
||||
import _ from 'lodash'
|
||||
import { Tree } from 'element-ui'
|
||||
import Sortable from 'sortablejs'
|
||||
|
@ -349,7 +392,7 @@ import {
|
|||
|
||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||
import { searchCI2, updateCI, deleteCI } from '@/modules/cmdb/api/ci'
|
||||
import { getCITypes } from '../../api/CIType'
|
||||
import { getCITypeIcons, grantCiType, revokeCiType } from '../../api/CIType'
|
||||
import { roleHasPermissionToGrant } from '@/modules/acl/api/permission'
|
||||
import { searchResourceType } from '@/modules/acl/api/resource'
|
||||
import SplitPane from '@/components/SplitPane'
|
||||
|
@ -361,8 +404,11 @@ import BatchDownload from '../../components/batchDownload/batchDownload.vue'
|
|||
import PasswordField from '../../components/passwordField/index.vue'
|
||||
import PreferenceSearch from '../../components/preferenceSearch/preferenceSearch.vue'
|
||||
import CMDBGrant from '../../components/cmdbGrant'
|
||||
import GrantModal from '../../components/cmdbGrant/grantModal.vue'
|
||||
import { ops_move_icon as OpsMoveIcon } from '@/core/icons'
|
||||
import { getAttrPassword } from '../../api/CITypeAttr'
|
||||
import ReadPermissionsModal from './modules/ReadPermissionsModal.vue'
|
||||
import RevokeModal from '../../components/cmdbGrant/revokeModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'RelationViews',
|
||||
|
@ -370,8 +416,8 @@ export default {
|
|||
SearchForm,
|
||||
AddTableModal,
|
||||
ContextMenu,
|
||||
// GrantDrawer,
|
||||
CMDBGrant,
|
||||
GrantModal,
|
||||
SplitPane,
|
||||
ElTree: Tree,
|
||||
EditAttrsPopover,
|
||||
|
@ -382,13 +428,15 @@ export default {
|
|||
PasswordField,
|
||||
PreferenceSearch,
|
||||
OpsMoveIcon,
|
||||
ReadPermissionsModal,
|
||||
RevokeModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
triggerSelect: false,
|
||||
treeNode: null,
|
||||
ciTypes: [],
|
||||
ciTypeIcons: {},
|
||||
relationViews: {},
|
||||
levels: [],
|
||||
showTypeIds: [],
|
||||
|
@ -430,6 +478,10 @@ export default {
|
|||
passwordValue: {},
|
||||
lastEditCiId: null,
|
||||
isContinueCloseEdit: true,
|
||||
|
||||
contextMenuKey: null,
|
||||
showBatchLevel: null,
|
||||
batchTreeKey: [],
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -452,6 +504,21 @@ export default {
|
|||
isShowBatchIcon() {
|
||||
return !!this.selectedRowKeys.length
|
||||
},
|
||||
topo_flatten() {
|
||||
return this.relationViews?.views[this.$route.meta.name]?.topo_flatten ?? []
|
||||
},
|
||||
descendant_ids() {
|
||||
return this.topo_flatten.slice(this.treeKeys.length).join(',')
|
||||
},
|
||||
descendant_ids_for_statistics() {
|
||||
return this.topo_flatten.slice(this.treeKeys.length + 1).join(',')
|
||||
},
|
||||
root_parent_path() {
|
||||
return this.treeKeys
|
||||
.slice(0, this.treeKeys.length)
|
||||
.map((item) => item.split('%')[0])
|
||||
.join(',')
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
|
@ -505,8 +572,8 @@ export default {
|
|||
})
|
||||
},
|
||||
getCITypesList() {
|
||||
getCITypes().then((res) => {
|
||||
this.ciTypes = res.ci_types
|
||||
getCITypeIcons().then((res) => {
|
||||
this.ciTypeIcons = res
|
||||
})
|
||||
},
|
||||
refreshTable() {
|
||||
|
@ -572,33 +639,38 @@ export default {
|
|||
q = q.slice(1)
|
||||
}
|
||||
if (this.treeKeys.length === 0) {
|
||||
await this.judgeCITypes(q)
|
||||
// await this.judgeCITypes(q)
|
||||
if (!refreshType) {
|
||||
this.loadRoot()
|
||||
await this.loadRoot()
|
||||
}
|
||||
|
||||
const fuzzySearch = (this.$refs['search'] || {}).fuzzySearch || ''
|
||||
if (fuzzySearch) {
|
||||
q = `q=_type:${this.currentTypeId[0]},*${fuzzySearch}*,` + q
|
||||
} else {
|
||||
q = `q=_type:${this.currentTypeId[0]},` + q
|
||||
}
|
||||
if (this.currentTypeId[0]) {
|
||||
const res = await searchCI2(q)
|
||||
this.pageNo = res.page
|
||||
this.numfound = res.numfound
|
||||
res.result.forEach((item, index) => (item.key = item._id))
|
||||
const jsonAttrList = this.preferenceAttrList.filter((attr) => attr.value_type === '6')
|
||||
console.log(jsonAttrList)
|
||||
this.instanceList = res['result'].map((item) => {
|
||||
jsonAttrList.forEach(
|
||||
(jsonAttr) => (item[jsonAttr.name] = item[jsonAttr.name] ? JSON.stringify(item[jsonAttr.name]) : '')
|
||||
)
|
||||
return { ..._.cloneDeep(item) }
|
||||
})
|
||||
this.initialInstanceList = _.cloneDeep(this.instanceList)
|
||||
this.calcColumns()
|
||||
}
|
||||
// const fuzzySearch = (this.$refs['search'] || {}).fuzzySearch || ''
|
||||
// if (fuzzySearch) {
|
||||
// q = `q=_type:${this.currentTypeId[0]},*${fuzzySearch}*,` + q
|
||||
// } else {
|
||||
// q = `q=_type:${this.currentTypeId[0]},` + q
|
||||
// }
|
||||
// if (this.currentTypeId[0] && this.treeData && this.treeData.length) {
|
||||
// // default select first node
|
||||
// this.onNodeClick(this.treeData[0].key)
|
||||
// const res = await searchCI2(q)
|
||||
// const root_id = this.treeData.map((item) => item.id).join(',')
|
||||
// q += `&root_id=${root_id}`
|
||||
|
||||
// this.pageNo = res.page
|
||||
// this.numfound = res.numfound
|
||||
// res.result.forEach((item, index) => (item.key = item._id))
|
||||
// const jsonAttrList = this.preferenceAttrList.filter((attr) => attr.value_type === '6')
|
||||
// console.log(jsonAttrList)
|
||||
// this.instanceList = res['result'].map((item) => {
|
||||
// jsonAttrList.forEach(
|
||||
// (jsonAttr) => (item[jsonAttr.name] = item[jsonAttr.name] ? JSON.stringify(item[jsonAttr.name]) : '')
|
||||
// )
|
||||
// return { ..._.cloneDeep(item) }
|
||||
// })
|
||||
// this.initialInstanceList = _.cloneDeep(this.instanceList)
|
||||
// this.calcColumns()
|
||||
// }
|
||||
} else {
|
||||
q += `&root_id=${this.treeKeys[this.treeKeys.length - 1].split('%')[0]}`
|
||||
|
||||
|
@ -634,10 +706,10 @@ export default {
|
|||
level = [1]
|
||||
}
|
||||
q += `&level=${level.join(',')}`
|
||||
await this.judgeCITypes(q)
|
||||
if (!refreshType) {
|
||||
this.loadNoRoot(this.treeKeys[this.treeKeys.length - 1], level)
|
||||
}
|
||||
await this.judgeCITypes(q)
|
||||
const fuzzySearch = (this.$refs['search'] || {}).fuzzySearch || ''
|
||||
if (fuzzySearch) {
|
||||
q = `q=_type:${this.currentTypeId[0]},*${fuzzySearch}*,` + q
|
||||
|
@ -645,8 +717,12 @@ export default {
|
|||
q = `q=_type:${this.currentTypeId[0]},` + q
|
||||
}
|
||||
if (Object.values(this.level2constraint).includes('2')) {
|
||||
q = q + `&&has_m2m=1`
|
||||
q = q + `&has_m2m=1`
|
||||
}
|
||||
if (this.root_parent_path) {
|
||||
q = q + `&root_parent_path=${this.root_parent_path}`
|
||||
}
|
||||
q = q + `&descendant_ids=${this.descendant_ids}`
|
||||
if (this.currentTypeId[0]) {
|
||||
const res = await searchCIRelation(q)
|
||||
|
||||
|
@ -666,7 +742,6 @@ export default {
|
|||
|
||||
this.calcColumns()
|
||||
}
|
||||
|
||||
if (refreshType === 'refreshNumber') {
|
||||
const promises = this.treeKeys.map((key, index) => {
|
||||
let ancestor_ids
|
||||
|
@ -684,8 +759,9 @@ export default {
|
|||
ancestor_ids,
|
||||
root_ids: key.split('%')[0],
|
||||
level: this.treeKeys.length - index,
|
||||
type_ids: this.showTypes.map((type) => type.id).join(','),
|
||||
type_ids: this.leaf2showTypes[this.leaf[0]].join(','),
|
||||
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
|
||||
descendant_ids: this.descendant_ids_for_statistics,
|
||||
}).then((res) => {
|
||||
let result
|
||||
const getTreeItem = (data, id) => {
|
||||
|
@ -741,22 +817,25 @@ export default {
|
|||
const promises = _showTypeIds.map((typeId) => {
|
||||
let _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1')
|
||||
if (Object.values(this.level2constraint).includes('2')) {
|
||||
_q = _q + `&&has_m2m=1`
|
||||
_q = _q + `&has_m2m=1`
|
||||
}
|
||||
console.log(_q)
|
||||
if (this.treeKeys.length === 0) {
|
||||
return searchCI2(_q).then((res) => {
|
||||
if (res.numfound !== 0) {
|
||||
showTypeIds.push(typeId)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return searchCIRelation(_q).then((res) => {
|
||||
if (res.numfound !== 0) {
|
||||
showTypeIds.push(typeId)
|
||||
}
|
||||
})
|
||||
if (this.root_parent_path) {
|
||||
_q = _q + `&root_parent_path=${this.root_parent_path}`
|
||||
}
|
||||
// if (this.treeKeys.length === 0) {
|
||||
// return searchCI2(_q).then((res) => {
|
||||
// if (res.numfound !== 0) {
|
||||
// showTypeIds.push(typeId)
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
_q = _q + `&descendant_ids=${this.descendant_ids}`
|
||||
return searchCIRelation(_q).then((res) => {
|
||||
if (res.numfound !== 0) {
|
||||
showTypeIds.push(typeId)
|
||||
}
|
||||
})
|
||||
// }
|
||||
})
|
||||
await Promise.all(promises).then(async () => {
|
||||
if (showTypeIds.length && showTypeIds.sort().join(',') !== this.showTypeIds.sort().join(',')) {
|
||||
|
@ -780,7 +859,7 @@ export default {
|
|||
},
|
||||
|
||||
async loadRoot() {
|
||||
searchCI2(`q=_type:(${this.levels[0].join(';')})&count=10000`).then(async (res) => {
|
||||
await searchCI2(`q=_type:(${this.levels[0].join(';')})&count=10000&use_id_filter=1`).then(async (res) => {
|
||||
const facet = []
|
||||
const ciIds = []
|
||||
res.result.forEach((item) => {
|
||||
|
@ -797,8 +876,9 @@ export default {
|
|||
return statisticsCIRelation({
|
||||
root_ids: ciIds.join(','),
|
||||
level: level,
|
||||
type_ids: this.showTypes.map((type) => type.id).join(','),
|
||||
type_ids: this.leaf2showTypes[this.leaf[0]].join(','),
|
||||
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
|
||||
descendant_ids: this.descendant_ids_for_statistics,
|
||||
}).then((num) => {
|
||||
facet.forEach((item, idx) => {
|
||||
item[1] += num[ciIds[idx] + '']
|
||||
|
@ -806,16 +886,17 @@ export default {
|
|||
})
|
||||
})
|
||||
await Promise.all(promises)
|
||||
this.wrapTreeData(facet, 'loadRoot')
|
||||
this.wrapTreeData(facet)
|
||||
// default select first node
|
||||
this.onNodeClick(this.treeData[0].key)
|
||||
})
|
||||
},
|
||||
|
||||
async loadNoRoot(rootIdAndTypeId, level) {
|
||||
const rootId = rootIdAndTypeId.split('%')[0]
|
||||
const typeId = Number(rootIdAndTypeId.split('%')[1])
|
||||
const topo_flatten = this.relationViews?.views[this.$route.meta.name]?.topo_flatten ?? []
|
||||
const index = topo_flatten.findIndex((id) => id === typeId)
|
||||
const _type = topo_flatten[index + 1]
|
||||
const index = this.topo_flatten.findIndex((id) => id === typeId)
|
||||
const _type = this.topo_flatten[index + 1]
|
||||
if (_type) {
|
||||
let q = `q=_type:${_type}&root_id=${rootId}&level=1&count=10000`
|
||||
if (
|
||||
|
@ -829,8 +910,12 @@ export default {
|
|||
.join(',')}`
|
||||
}
|
||||
if (Object.values(this.level2constraint).includes('2')) {
|
||||
q = q + `&&has_m2m=1`
|
||||
q = q + `&has_m2m=1`
|
||||
}
|
||||
if (this.root_parent_path) {
|
||||
q = q + `&root_parent_path=${this.root_parent_path}`
|
||||
}
|
||||
q = q + `&descendant_ids=${this.descendant_ids}`
|
||||
searchCIRelation(q).then(async (res) => {
|
||||
const facet = []
|
||||
const ciIds = []
|
||||
|
@ -852,8 +937,9 @@ export default {
|
|||
ancestor_ids,
|
||||
root_ids: ciIds.join(','),
|
||||
level: _level - 1,
|
||||
type_ids: this.showTypes.map((type) => type.id).join(','),
|
||||
type_ids: this.leaf2showTypes[this.leaf[0]].join(','),
|
||||
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
|
||||
descendant_ids: this.descendant_ids_for_statistics,
|
||||
}).then((num) => {
|
||||
facet.forEach((item, idx) => {
|
||||
item[1] += num[ciIds[idx] + '']
|
||||
|
@ -862,7 +948,7 @@ export default {
|
|||
}
|
||||
})
|
||||
await Promise.all(promises)
|
||||
this.wrapTreeData(facet, 'loadNoRoot')
|
||||
this.wrapTreeData(facet)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@ -917,6 +1003,7 @@ export default {
|
|||
}
|
||||
this.treeKeys = treeNode.eventKey.split('@^@').filter((item) => item !== '')
|
||||
this.treeNode = treeNode
|
||||
// this.refreshTable()
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
|
@ -979,27 +1066,37 @@ export default {
|
|||
this.$refs.xTable.refreshColumn()
|
||||
})
|
||||
},
|
||||
calculateParamsFromTreeKey(treeKey, menuKey) {
|
||||
const splitTreeKey = treeKey.split('@^@')
|
||||
const _tempTree = splitTreeKey[splitTreeKey.length - 1].split('%')
|
||||
const firstCIObj = JSON.parse(_tempTree[2])
|
||||
const firstCIId = _tempTree[0]
|
||||
let ancestor_ids
|
||||
if (
|
||||
Object.keys(this.level2constraint).some(
|
||||
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
|
||||
)
|
||||
) {
|
||||
const ancestor = treeKey
|
||||
.split('@^@')
|
||||
.slice(0, menuKey === 'delete' ? treeKey.split('@^@').length - 2 : treeKey.split('@^@').length - 1)
|
||||
ancestor_ids = ancestor.map((item) => item.split('%')[0]).join(',')
|
||||
}
|
||||
return { splitTreeKey, firstCIObj, firstCIId, _tempTree, ancestor_ids }
|
||||
},
|
||||
onContextMenuClick(treeKey, menuKey) {
|
||||
if (treeKey) {
|
||||
const splitTreeKey = treeKey.split('@^@')
|
||||
const _tempTree = splitTreeKey[splitTreeKey.length - 1].split('%')
|
||||
const firstCIObj = JSON.parse(_tempTree[2])
|
||||
const firstCIId = _tempTree[0]
|
||||
let ancestor_ids
|
||||
if (
|
||||
Object.keys(this.level2constraint).some(
|
||||
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
|
||||
)
|
||||
) {
|
||||
const ancestor = treeKey
|
||||
.split('@^@')
|
||||
.slice(0, menuKey === 'delete' ? treeKey.split('@^@').length - 2 : treeKey.split('@^@').length - 1)
|
||||
ancestor_ids = ancestor.map((item) => item.split('%')[0]).join(',')
|
||||
if (!['batchGrant', 'batchRevoke', 'batchDelete', 'batchCancel'].includes(menuKey)) {
|
||||
this.contextMenuKey = treeKey
|
||||
}
|
||||
|
||||
const { splitTreeKey, firstCIObj, firstCIId, _tempTree, ancestor_ids } = this.calculateParamsFromTreeKey(
|
||||
treeKey,
|
||||
menuKey
|
||||
)
|
||||
if (menuKey === 'delete') {
|
||||
const _tempTreeParent = splitTreeKey[splitTreeKey.length - 2].split('%')
|
||||
const that = this
|
||||
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: (h) => <div>{that.$t('confirmDelete2', { name: Object.values(firstCIObj)[0] })}</div>,
|
||||
|
@ -1012,6 +1109,24 @@ export default {
|
|||
})
|
||||
},
|
||||
})
|
||||
} else if (menuKey === 'grant') {
|
||||
this.$refs.grantModal.open('depart')
|
||||
} else if (menuKey === 'revoke') {
|
||||
this.$refs.revokeModal.open()
|
||||
} else if (menuKey === 'view') {
|
||||
this.$refs.readPermissionsModal.open(treeKey)
|
||||
} else if (menuKey === 'batch') {
|
||||
this.showBatchLevel = splitTreeKey.filter((item) => !!item).length - 1
|
||||
this.batchTreeKey = []
|
||||
} else if (menuKey === 'batchGrant') {
|
||||
this.$refs.grantModal.open('depart')
|
||||
} else if (menuKey === 'batchRevoke') {
|
||||
this.$refs.revokeModal.open()
|
||||
} else if (menuKey === 'batchDelete') {
|
||||
this.batchDeleteCIRelationFromTree()
|
||||
} else if (menuKey === 'batchCancel') {
|
||||
this.showBatchLevel = null
|
||||
this.batchTreeKey = []
|
||||
} else {
|
||||
const childTypeId = menuKey
|
||||
this.$refs.addTableModal.openModal(firstCIObj, firstCIId, childTypeId, 'children', ancestor_ids)
|
||||
|
@ -1066,8 +1181,10 @@ export default {
|
|||
const _splitTargetKey = targetKey.split('@^@').filter((item) => item !== '')
|
||||
if (_splitDragKey.length - 1 === _splitTargetKey.length) {
|
||||
const dragId = _splitDragKey[_splitDragKey.length - 1].split('%')[0]
|
||||
// const targetObj = JSON.parse(_splitTargetKey[_splitTargetKey.length - 1].split('%')[2])
|
||||
const targetId = _splitTargetKey[_splitTargetKey.length - 1].split('%')[0]
|
||||
console.log(_splitDragKey)
|
||||
// TODO 拖拽这里不造咋弄 等等再说吧
|
||||
batchUpdateCIRelationChildren([dragId], [targetId]).then((res) => {
|
||||
this.reload()
|
||||
})
|
||||
|
@ -1438,6 +1555,138 @@ export default {
|
|||
this.$message.error(this.$t('cmdb.serviceTreecopyFailed'))
|
||||
})
|
||||
},
|
||||
async onRelationViewGrant({ department, user }, type) {
|
||||
const result = []
|
||||
if (this.showBatchLevel !== null && this.batchTreeKey && this.batchTreeKey.length) {
|
||||
for (let i = 0; i < this.batchTreeKey.length; i++) {
|
||||
await this.relationViewGrant({ department, user }, this.batchTreeKey[i], (_result) => {
|
||||
result.push(..._result)
|
||||
})
|
||||
}
|
||||
this.showBatchLevel = null
|
||||
this.batchTreeKey = []
|
||||
} else {
|
||||
await this.relationViewGrant({ department, user }, this.contextMenuKey, (_result) => {
|
||||
result.push(..._result)
|
||||
})
|
||||
}
|
||||
if (result.every((r) => r.status === 'fulfilled')) {
|
||||
this.$message.success(this.$t('operateSuccess'))
|
||||
}
|
||||
},
|
||||
async relationViewGrant({ department, user }, nodeKey, callback) {
|
||||
const needGrantNodes = nodeKey
|
||||
.split('@^@')
|
||||
.filter((item) => !!item)
|
||||
.reverse()
|
||||
console.log(needGrantNodes)
|
||||
|
||||
const needGrantRids = [...department, ...user]
|
||||
const floor = Math.ceil(needGrantRids.length / 6)
|
||||
const result = []
|
||||
for (let i = 0; i < needGrantNodes.length; i++) {
|
||||
const grantNode = needGrantNodes[i]
|
||||
const _grantNode = grantNode.split('%')
|
||||
const ciId = _grantNode[0]
|
||||
const typeId = _grantNode[1]
|
||||
const uniqueValue = Object.entries(JSON.parse(_grantNode[2]))[0][1]
|
||||
const parent_path = needGrantNodes
|
||||
.slice(i + 1)
|
||||
.map((item) => {
|
||||
return Number(item.split('%')[0])
|
||||
})
|
||||
.reverse()
|
||||
.join(',')
|
||||
for (let j = 0; j < floor; j++) {
|
||||
const itemList = needGrantRids.slice(6 * j, 6 * j + 6)
|
||||
const promises = itemList.map((rid) =>
|
||||
grantCiType(typeId, rid, {
|
||||
id_filter: { [ciId]: { name: uniqueValue, parent_path } },
|
||||
is_recursive: Number(i > 0),
|
||||
})
|
||||
)
|
||||
const _result = await Promise.allSettled(promises)
|
||||
result.push(..._result)
|
||||
}
|
||||
}
|
||||
callback(result)
|
||||
},
|
||||
clickCheckbox(treeKey) {
|
||||
const _idx = this.batchTreeKey.findIndex((item) => item === treeKey)
|
||||
if (_idx > -1) {
|
||||
this.batchTreeKey.splice(_idx, 1)
|
||||
} else {
|
||||
this.batchTreeKey.push(treeKey)
|
||||
}
|
||||
},
|
||||
batchDeleteCIRelationFromTree() {
|
||||
const that = this
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: (h) => <div>{that.$t('confirmDelete')}</div>,
|
||||
async onOk() {
|
||||
for (let i = 0; i < that.batchTreeKey.length; i++) {
|
||||
const { splitTreeKey, firstCIObj, firstCIId, _tempTree, ancestor_ids } = that.calculateParamsFromTreeKey(
|
||||
that.batchTreeKey[i],
|
||||
'delete'
|
||||
)
|
||||
const _tempTreeParent = splitTreeKey[splitTreeKey.length - 2].split('%')
|
||||
await deleteCIRelationView(_tempTreeParent[0], _tempTree[0], { ancestor_ids }).then((res) => {})
|
||||
}
|
||||
that.$message.success(that.$t('deleteSuccess'))
|
||||
that.showBatchLevel = null
|
||||
that.batchTreeKey = []
|
||||
setTimeout(() => {
|
||||
that.reload()
|
||||
}, 500)
|
||||
},
|
||||
})
|
||||
},
|
||||
async handleSingleRevoke({ users = [], roles = [] }, treeKey, callback) {
|
||||
const rids = [...users.map((item) => Number(item.split('-')[1])), ...roles]
|
||||
const treeKeyPath = treeKey.split('@^@').filter((item) => !!item)
|
||||
const _treeKey = treeKeyPath.pop(-1).split('%')
|
||||
const id_filter = {}
|
||||
const typeId = _treeKey[1]
|
||||
const ciId = _treeKey[0]
|
||||
const uniqueValue = Object.entries(JSON.parse(_treeKey[2]))[0][1]
|
||||
|
||||
const parent_path = treeKeyPath
|
||||
.map((item) => {
|
||||
return Number(item.split('%')[0])
|
||||
})
|
||||
.join(',')
|
||||
id_filter[ciId] = { name: uniqueValue, parent_path }
|
||||
const floor = Math.ceil(rids.length / 6)
|
||||
const result = []
|
||||
for (let j = 0; j < floor; j++) {
|
||||
const itemList = rids.slice(6 * j, 6 * j + 6)
|
||||
const promises = itemList.map((rid) => revokeCiType(typeId, rid, { id_filter, perms: ['read'], parent_path }))
|
||||
const _result = await Promise.allSettled(promises)
|
||||
result.push(..._result)
|
||||
}
|
||||
callback(result)
|
||||
},
|
||||
async handleRevoke({ users = [], roles = [] }) {
|
||||
const result = []
|
||||
if (this.showBatchLevel !== null && this.batchTreeKey && this.batchTreeKey.length) {
|
||||
for (let i = 0; i < this.batchTreeKey.length; i++) {
|
||||
const treeKey = this.batchTreeKey[i]
|
||||
await this.handleSingleRevoke({ users, roles }, treeKey, (_result) => {
|
||||
result.push(..._result)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
await this.handleSingleRevoke({ users, roles }, this.contextMenuKey, (_result) => {
|
||||
result.push(..._result)
|
||||
})
|
||||
}
|
||||
if (result.every((r) => r.status === 'fulfilled')) {
|
||||
this.$message.success(this.$t('operateSuccess'))
|
||||
}
|
||||
this.showBatchLevel = null
|
||||
this.batchTreeKey = []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -11,24 +11,24 @@
|
|||
>
|
||||
<div :style="{ width: '100%' }" id="add-table-modal">
|
||||
<a-spin :spinning="loading">
|
||||
<!-- <a-input
|
||||
v-model="expression"
|
||||
class="ci-searchform-expression"
|
||||
:style="{ width, marginBottom: '10px' }"
|
||||
:placeholder="placeholder"
|
||||
@focus="
|
||||
() => {
|
||||
isFocusExpression = true
|
||||
}
|
||||
"
|
||||
/> -->
|
||||
<SearchForm
|
||||
ref="searchForm"
|
||||
:typeId="addTypeId"
|
||||
:preferenceAttrList="preferenceAttrList"
|
||||
@refresh="handleSearch"
|
||||
/>
|
||||
<!-- <a @click="handleSearch"><a-icon type="search"/></a> -->
|
||||
>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
$refs.createInstanceForm.handleOpen(true, 'create')
|
||||
}
|
||||
"
|
||||
slot="extraContent"
|
||||
type="primary"
|
||||
size="small"
|
||||
>新增</a-button
|
||||
>
|
||||
</SearchForm>
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
row-id="_id"
|
||||
|
@ -77,19 +77,31 @@
|
|||
/>
|
||||
</a-spin>
|
||||
</div>
|
||||
<CreateInstanceForm
|
||||
ref="createInstanceForm"
|
||||
:typeIdFromRelation="addTypeId"
|
||||
@reload="
|
||||
() => {
|
||||
currentPage = 1
|
||||
getTableData(true)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable no-useless-escape */
|
||||
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||
import { getSubscribeAttributes } from '@/modules/cmdb/api/preference'
|
||||
import { batchUpdateCIRelationChildren, batchUpdateCIRelationParents } from '@/modules/cmdb/api/CIRelation'
|
||||
import { getCITableColumns } from '../../../utils/helper'
|
||||
import SearchForm from '../../../components/searchForm/SearchForm.vue'
|
||||
import CreateInstanceForm from '../../ci/modules/CreateInstanceForm.vue'
|
||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||
|
||||
export default {
|
||||
name: 'AddTableModal',
|
||||
components: { SearchForm },
|
||||
components: { SearchForm, CreateInstanceForm },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
|
@ -106,6 +118,7 @@ export default {
|
|||
type: 'children',
|
||||
preferenceAttrList: [],
|
||||
ancestor_ids: undefined,
|
||||
attrList1: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -119,6 +132,13 @@ export default {
|
|||
return this.isFocusExpression ? '500px' : '100px'
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
attrList: () => {
|
||||
return this.attrList
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
async openModal(ciObj, ciId, addTypeId, type, ancestor_ids = undefined) {
|
||||
|
@ -132,6 +152,9 @@ export default {
|
|||
await getSubscribeAttributes(addTypeId).then((res) => {
|
||||
this.preferenceAttrList = res.attributes // 已经订阅的全部列
|
||||
})
|
||||
getCITypeAttributesById(addTypeId).then((res) => {
|
||||
this.attrList = res.attributes
|
||||
})
|
||||
this.getTableData(true)
|
||||
},
|
||||
async getTableData(isInit) {
|
||||
|
@ -207,6 +230,9 @@ export default {
|
|||
this.handleClose()
|
||||
this.$emit('reload')
|
||||
}, 500)
|
||||
} else {
|
||||
this.handleClose()
|
||||
this.$emit('reload')
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
|
|
|
@ -1,63 +1,81 @@
|
|||
<template>
|
||||
<a-dropdown :trigger="['contextmenu']">
|
||||
<a-menu slot="overlay" @click="({ key: menuKey }) => this.onContextMenuClick(this.treeKey, menuKey)">
|
||||
<a-menu-item v-for="item in menuList" :key="item.id">{{ $t('new') }} {{ item.alias }}</a-menu-item>
|
||||
<a-menu-item v-if="showDelete" key="delete">{{ $t('cmdb.serviceTree.deleteNode') }}</a-menu-item>
|
||||
</a-menu>
|
||||
<div
|
||||
:style="{
|
||||
width: '100%',
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
@click="clickNode"
|
||||
>
|
||||
<span
|
||||
:style="{
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
>
|
||||
<template v-if="icon">
|
||||
<img
|
||||
v-if="icon.split('$$')[2]"
|
||||
:src="`/api/common-setting/v1/file/${icon.split('$$')[3]}`"
|
||||
:style="{ maxHeight: '14px', maxWidth: '14px' }"
|
||||
/>
|
||||
<ops-icon
|
||||
v-else
|
||||
:style="{
|
||||
color: icon.split('$$')[1],
|
||||
fontSize: '14px',
|
||||
}"
|
||||
:type="icon.split('$$')[0]"
|
||||
/>
|
||||
</template>
|
||||
<span
|
||||
<div
|
||||
:class="{
|
||||
'relation-views-node': true,
|
||||
'relation-views-node-checkbox': showCheckbox,
|
||||
}"
|
||||
@click="clickNode"
|
||||
>
|
||||
<span>
|
||||
<a-checkbox @click.stop="clickCheckbox" class="relation-views-node-checkbox" v-if="showCheckbox" />
|
||||
<template v-if="icon">
|
||||
<img
|
||||
v-if="icon.includes('$$') && icon.split('$$')[2]"
|
||||
:src="`/api/common-setting/v1/file/${icon.split('$$')[3]}`"
|
||||
:style="{ maxHeight: '14px', maxWidth: '14px' }"
|
||||
/>
|
||||
<ops-icon
|
||||
v-else-if="icon.includes('$$') && icon.split('$$')[0]"
|
||||
:style="{
|
||||
display: 'inline-block',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: '#d3d3d3',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
lineHeight: '16px',
|
||||
fontSize: '12px',
|
||||
color: icon.split('$$')[1],
|
||||
fontSize: '14px',
|
||||
}"
|
||||
v-else
|
||||
>{{ ciTypeName ? ciTypeName[0].toUpperCase() : 'i' }}</span
|
||||
>
|
||||
<span :style="{ marginLeft: '5px' }">{{ this.title }}</span>
|
||||
</span>
|
||||
<a-icon :style="{ fontSize: '10px' }" v-if="childLength && !isLeaf" :type="switchIcon"></a-icon>
|
||||
</div>
|
||||
</a-dropdown>
|
||||
:type="icon.split('$$')[0]"
|
||||
/>
|
||||
<span class="relation-views-node-icon" v-else>{{ icon ? icon[0].toUpperCase() : 'i' }}</span>
|
||||
</template>
|
||||
<span class="relation-views-node-title">{{ this.title }}</span>
|
||||
</span>
|
||||
<a-dropdown>
|
||||
<a-menu slot="overlay" @click="({ key: menuKey }) => this.onContextMenuClick(this.treeKey, menuKey)">
|
||||
<template v-if="showBatchLevel === null">
|
||||
<a-menu-item
|
||||
v-for="item in menuList"
|
||||
:key="item.id"
|
||||
><a-icon type="plus-circle" />{{ $t('new') }} {{ item.alias }}</a-menu-item
|
||||
>
|
||||
<a-menu-item
|
||||
v-if="showDelete"
|
||||
key="delete"
|
||||
><ops-icon type="icon-xianxing-delete" />{{ $t('cmdb.serviceTree.deleteNode') }}</a-menu-item
|
||||
>
|
||||
<a-menu-divider />
|
||||
<a-menu-item key="grant"><a-icon type="user-add" />{{ $t('grant') }}</a-menu-item>
|
||||
<a-menu-item key="revoke"><a-icon type="user-delete" />{{ $t('revoke') }}</a-menu-item>
|
||||
<a-menu-item key="view"><a-icon type="eye" />{{ $t('cmdb.serviceTree.view') }}</a-menu-item>
|
||||
<a-menu-divider />
|
||||
<a-menu-item
|
||||
key="batch"
|
||||
><ops-icon type="icon-xianxing-copy" />{{ $t('cmdb.serviceTree.batch') }}</a-menu-item
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-menu-item
|
||||
:disabled="!batchTreeKey || !batchTreeKey.length"
|
||||
key="batchGrant"
|
||||
><a-icon type="user-add" />{{ $t('grant') }}</a-menu-item
|
||||
>
|
||||
<a-menu-item
|
||||
:disabled="!batchTreeKey || !batchTreeKey.length"
|
||||
key="batchRevoke"
|
||||
><a-icon type="user-delete" />{{ $t('revoke') }}</a-menu-item
|
||||
>
|
||||
<a-menu-divider />
|
||||
<template v-if="showBatchLevel > 0">
|
||||
<a-menu-item
|
||||
:disabled="!batchTreeKey || !batchTreeKey.length"
|
||||
key="batchDelete"
|
||||
><ops-icon type="icon-xianxing-delete" />{{ $t('delete') }}</a-menu-item
|
||||
>
|
||||
<a-menu-divider />
|
||||
</template>
|
||||
<a-menu-item key="batchCancel"><a-icon type="close-circle" />{{ $t('cancel') }}</a-menu-item>
|
||||
</template>
|
||||
</a-menu>
|
||||
<a-icon class="relation-views-node-operation" type="ellipsis" />
|
||||
</a-dropdown>
|
||||
<a-icon :style="{ fontSize: '10px' }" v-if="childLength && !isLeaf" :type="switchIcon"></a-icon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -88,7 +106,15 @@ export default {
|
|||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
ciTypes: {
|
||||
ciTypeIcons: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
showBatchLevel: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
batchTreeKey: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
@ -141,14 +167,10 @@ export default {
|
|||
icon() {
|
||||
const _split = this.treeKey.split('@^@')
|
||||
const currentNodeTypeId = _split[_split.length - 1].split('%')[1]
|
||||
const _find = this.ciTypes.find((type) => type.id === Number(currentNodeTypeId))
|
||||
return _find?.icon || null
|
||||
return this.ciTypeIcons[Number(currentNodeTypeId)] ?? null
|
||||
},
|
||||
ciTypeName() {
|
||||
const _split = this.treeKey.split('@^@')
|
||||
const currentNodeTypeId = _split[_split.length - 1].split('%')[1]
|
||||
const _find = this.ciTypes.find((type) => type.id === Number(currentNodeTypeId))
|
||||
return _find?.name || ''
|
||||
showCheckbox() {
|
||||
return this.showBatchLevel === this.treeKey.split('@^@').filter((item) => !!item).length - 1
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -159,8 +181,73 @@ export default {
|
|||
this.$emit('onNodeClick', this.treeKey)
|
||||
this.switchIcon = this.switchIcon === 'down' ? 'up' : 'down'
|
||||
},
|
||||
clickCheckbox() {
|
||||
this.$emit('clickCheckbox', this.treeKey)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style lang="less" scoped>
|
||||
.relation-views-node {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
> span {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.relation-views-node-icon {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: #d3d3d3;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.relation-views-node-title {
|
||||
padding-left: 5px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: calc(100% - 16px);
|
||||
}
|
||||
}
|
||||
.relation-views-node-operation {
|
||||
display: none;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.relation-views-node-checkbox,
|
||||
.relation-views-node-moveright {
|
||||
> span {
|
||||
.relation-views-node-checkbox {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.relation-views-node-title {
|
||||
width: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.relation-views-left .ant-tree-node-content-wrapper:hover {
|
||||
.relation-views-node-operation {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.relation-views-left {
|
||||
ul:has(.relation-views-node-checkbox) > li > ul {
|
||||
margin-left: 26px;
|
||||
}
|
||||
ul:has(.relation-views-node-checkbox) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<template>
|
||||
<a-modal
|
||||
width="600px"
|
||||
:bodyStyle="{
|
||||
paddingTop: 0,
|
||||
}"
|
||||
:visible="visible"
|
||||
:footer="null"
|
||||
@cancel="handleCancel"
|
||||
:title="$t('view')"
|
||||
>
|
||||
<div>
|
||||
<template v-if="readCIIdFilterPermissions && readCIIdFilterPermissions.length">
|
||||
<p>
|
||||
<strong>{{ $t('cmdb.serviceTree.idAuthorizationPolicy') }}</strong>
|
||||
<a
|
||||
@click="
|
||||
() => {
|
||||
showAllReadCIIdFilterPermissions = !showAllReadCIIdFilterPermissions
|
||||
}
|
||||
"
|
||||
v-if="readCIIdFilterPermissions.length > 10"
|
||||
><a-icon
|
||||
:type="showAllReadCIIdFilterPermissions ? 'caret-down' : 'caret-up'"
|
||||
/></a>
|
||||
</p>
|
||||
<a-tag
|
||||
v-for="item in showAllReadCIIdFilterPermissions
|
||||
? readCIIdFilterPermissions
|
||||
: readCIIdFilterPermissions.slice(0, 10)"
|
||||
:key="item.name"
|
||||
color="blue"
|
||||
:style="{ marginBottom: '5px' }"
|
||||
>{{ item.name }}</a-tag
|
||||
>
|
||||
<a-tag
|
||||
:style="{ marginBottom: '5px' }"
|
||||
v-if="readCIIdFilterPermissions.length > 10 && !showAllReadCIIdFilterPermissions"
|
||||
>+{{ readCIIdFilterPermissions.length - 10 }}</a-tag
|
||||
>
|
||||
</template>
|
||||
<a-empty v-else>
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> {{ $t('noData') }} </span>
|
||||
</a-empty>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ciTypeFilterPermissions, getCIType } from '../../../api/CIType'
|
||||
import FilterComp from '@/components/CMDBFilterComp'
|
||||
import { searchRole } from '@/modules/acl/api/role'
|
||||
|
||||
export default {
|
||||
name: 'ReadPermissionsModal',
|
||||
components: { FilterComp },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
filerPerimissions: {},
|
||||
readCIIdFilterPermissions: [],
|
||||
canSearchPreferenceAttrList: [],
|
||||
showAllReadCIIdFilterPermissions: false,
|
||||
allRoles: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadRoles()
|
||||
},
|
||||
methods: {
|
||||
async loadRoles() {
|
||||
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
|
||||
this.allRoles = res.roles
|
||||
},
|
||||
async open(treeKey) {
|
||||
this.visible = true
|
||||
const _splitTreeKey = treeKey.split('@^@').filter((item) => !!item)
|
||||
const _treeKey = _splitTreeKey.slice(_splitTreeKey.length - 1, _splitTreeKey.length)[0].split('%')
|
||||
|
||||
const typeId = _treeKey[1]
|
||||
const _treeKeyPath = _splitTreeKey.map((item) => item.split('%')[0]).join(',')
|
||||
await ciTypeFilterPermissions(typeId).then((res) => {
|
||||
this.filerPerimissions = res
|
||||
})
|
||||
const readCIIdFilterPermissions = []
|
||||
Object.entries(this.filerPerimissions).forEach(([k, v]) => {
|
||||
const { id_filter } = v
|
||||
if (id_filter && Object.keys(id_filter).includes(_treeKeyPath)) {
|
||||
const _find = this.allRoles.find((item) => item.id === Number(k))
|
||||
readCIIdFilterPermissions.push({ name: _find?.name ?? k, rid: k })
|
||||
}
|
||||
})
|
||||
this.readCIIdFilterPermissions = readCIIdFilterPermissions
|
||||
console.log(readCIIdFilterPermissions)
|
||||
},
|
||||
handleCancel() {
|
||||
this.showAllReadCIIdFilterPermissions = false
|
||||
this.visible = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -284,6 +284,10 @@ export default {
|
|||
const regSort = /(?<=sort=).+/g
|
||||
|
||||
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
||||
// if (exp) {
|
||||
// exp = exp.replace(/(\:)/g, '$1*')
|
||||
// exp = exp.replace(/(\,)/g, '*$1')
|
||||
// }
|
||||
// 如果是表格点击的排序 以表格为准
|
||||
let sort
|
||||
if (sortByTable) {
|
||||
|
@ -314,7 +318,9 @@ export default {
|
|||
this.columnsGroup = []
|
||||
this.instanceList = []
|
||||
this.totalNumber = res['numfound']
|
||||
|
||||
if (!res['numfound']) {
|
||||
return
|
||||
}
|
||||
const { attributes: resAllAttributes } = await getCITypeAttributesByTypeIds({
|
||||
type_ids: Object.keys(res.counter).join(','),
|
||||
})
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
:noOptionsText="$t('cs.components.empty')"
|
||||
:class="className ? className : 'ops-setting-treeselect'"
|
||||
value-consists-of="LEAF_PRIORITY"
|
||||
:limit="20"
|
||||
:limit="limit"
|
||||
:limitText="(count) => `+ ${count}`"
|
||||
v-bind="$attrs"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:flat="flat"
|
||||
>
|
||||
</treeselect>
|
||||
</template>
|
||||
|
@ -60,6 +61,14 @@ export default {
|
|||
type: String,
|
||||
default: 'employee_id',
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
flat: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
|
|
Loading…
Reference in New Issue