mirror of https://github.com/veops/cmdb.git
Merge pull request #588 from veops/dev_ui_240731
feat(ui): update ci type
This commit is contained in:
commit
c08e4529de
|
@ -3,3 +3,4 @@ VUE_APP_PREVIEW=false
|
|||
VUE_APP_API_BASE_URL=http://127.0.0.1:5000/api
|
||||
VUE_APP_BUILD_PACKAGES="ticket,calendar,acl"
|
||||
VUE_APP_IS_OUTER=true
|
||||
VUE_APP_IS_OPEN_SOURCE=true
|
||||
|
|
|
@ -69,6 +69,8 @@ Vue.prototype.$httpError = function (err, describe) {
|
|||
|
||||
window.$message = Vue.prototype.$message
|
||||
|
||||
Vue.prototype.isOpenSource = process.env.VUE_APP_IS_OPEN_SOURCE === 'true'
|
||||
|
||||
Vue.use(Antd)
|
||||
Vue.use(Viser)
|
||||
|
||||
|
|
|
@ -1,232 +1,257 @@
|
|||
import { axios } from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取 所有的 ci_types
|
||||
* @param parameter
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function getCITypes(parameter) {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types',
|
||||
method: 'GET',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 某个 ci_types
|
||||
* @param CITypeName
|
||||
* @param parameter
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function getCIType(CITypeName, parameter) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeName}`,
|
||||
method: 'GET',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 ci_type
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function createCIType(data) {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 ci_type
|
||||
* @param CITypeId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function updateCIType(CITypeId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}`,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 ci_type
|
||||
* @param CITypeId
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function deleteCIType(CITypeId) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}`,
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 某个 ci_type 的分组
|
||||
* @param CITypeId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function getCITypeGroupById(CITypeId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}/attribute_groups`,
|
||||
method: 'GET',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 某个 ci_type 的分组
|
||||
* @param CITypeId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function createCITypeGroupById(CITypeId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}/attribute_groups`,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 某个 ci_type 的分组
|
||||
* @param groupId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function updateCITypeGroupById(groupId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/attribute_groups/${groupId}`,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 某个 ci_type 的分组
|
||||
* @param groupId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function deleteCITypeGroupById(groupId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/attribute_groups/${groupId}`,
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function getUniqueConstraintList(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function addUniqueConstraint(type_id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUniqueConstraint(type_id, id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteUniqueConstraint(type_id, id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
export function getTriggerList(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function addTrigger(type_id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateTrigger(type_id, id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteTrigger(type_id, id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// CMDB的模型和实例的授权接口
|
||||
export function grantCiType(type_id, rid, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/roles/${rid}/grant`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// CMDB的模型和实例的删除授权接口
|
||||
export function revokeCiType(type_id, rid, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/roles/${rid}/revoke`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// CMDB的模型和实例的过滤的权限
|
||||
export function ciTypeFilterPermissions(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/filters/permissions`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// parent_ids, child_id
|
||||
export function postCiTypeInheritance(data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/inheritance`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// parent_id, child_id
|
||||
export function deleteCiTypeInheritance(data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/inheritance`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getCITypeIcons() {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types/icons',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取 所有的 ci_types
|
||||
* @param parameter
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function getCITypes(parameter) {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types',
|
||||
method: 'GET',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 某个 ci_types
|
||||
* @param CITypeName
|
||||
* @param parameter
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function getCIType(CITypeName, parameter) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeName}`,
|
||||
method: 'GET',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 ci_type
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function createCIType(data) {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 ci_type
|
||||
* @param CITypeId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function updateCIType(CITypeId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}`,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 ci_type
|
||||
* @param CITypeId
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function deleteCIType(CITypeId) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}`,
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 某个 ci_type 的分组
|
||||
* @param CITypeId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function getCITypeGroupById(CITypeId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}/attribute_groups`,
|
||||
method: 'GET',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 某个 ci_type 的分组
|
||||
* @param CITypeId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function createCITypeGroupById(CITypeId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}/attribute_groups`,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 某个 ci_type 的分组
|
||||
* @param groupId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function updateCITypeGroupById(groupId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/attribute_groups/${groupId}`,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 某个 ci_type 的分组
|
||||
* @param groupId
|
||||
* @param data
|
||||
* @returns {AxiosPromise}
|
||||
*/
|
||||
export function deleteCITypeGroupById(groupId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/attribute_groups/${groupId}`,
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取级联属性配置
|
||||
* @param {*} typeId
|
||||
* @returns
|
||||
*/
|
||||
export function getCITypeCascadeAttributes(typeId) {
|
||||
return axios({
|
||||
url: `/v0.1/cascade_attributes/ci_types/${typeId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取级联属性数据
|
||||
* @param {*} typeId
|
||||
* @returns
|
||||
*/
|
||||
export function postCITypeCascadeAttributesValues(attrId, data) {
|
||||
return axios({
|
||||
url: `/v0.1/cascade_attributes/${attrId}/values`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getUniqueConstraintList(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function addUniqueConstraint(type_id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUniqueConstraint(type_id, id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteUniqueConstraint(type_id, id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
export function getTriggerList(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function addTrigger(type_id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateTrigger(type_id, id, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteTrigger(type_id, id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/triggers/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// CMDB的模型和实例的授权接口
|
||||
export function grantCiType(type_id, rid, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/roles/${rid}/grant`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// CMDB的模型和实例的删除授权接口
|
||||
export function revokeCiType(type_id, rid, data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/roles/${rid}/revoke`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// CMDB的模型和实例的过滤的权限
|
||||
export function ciTypeFilterPermissions(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${type_id}/filters/permissions`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// parent_ids, child_id
|
||||
export function postCiTypeInheritance(data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/inheritance`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// parent_id, child_id
|
||||
export function deleteCiTypeInheritance(data) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/inheritance`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getCITypeIcons() {
|
||||
return axios({
|
||||
url: '/v0.1/ci_types/icons',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
class="category-side-children-item-corporate"
|
||||
v-if="ruleType === 'private_cloud' || (ruleType === 'http' && (categoryIndex !== 0 || itemIndex !== 0))"
|
||||
>
|
||||
企
|
||||
{{ $t('cmdb.enterpriseVersionFlag') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,7 +67,7 @@
|
|||
class="corporate-flag"
|
||||
v-if="ruleType === 'private_cloud' || (ruleType === 'http' && (categoryIndex !== 0 || itemIndex !== 0))"
|
||||
>
|
||||
<span class="corporate-flag-text">企</span>
|
||||
<span class="corporate-flag-text">{{ $t('cmdb.enterpriseVersionFlag') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,8 @@ const cmdb_en = {
|
|||
relation: 'Relation',
|
||||
attribute: 'Attributes',
|
||||
configTable: 'Config Table',
|
||||
enterpriseVersionFlag: 'Pro',
|
||||
enterpriseVersionTip: 'Enterprise version only',
|
||||
menu: {
|
||||
views: 'Views',
|
||||
topologyView: 'Topology Views',
|
||||
|
@ -274,8 +276,12 @@ const cmdb_en = {
|
|||
attrAlias: 'Attr Alias',
|
||||
attrCode: 'Attr Code',
|
||||
computedAttrTip1: 'Reference attributes follow jinja2 syntax',
|
||||
computedAttrTip2: `Multi-valued attributes (lists) are rendered with [ ] included by default, if you want to remove it, the reference method is: ‘’‘{{ attr_name | join(’,‘)}}}’‘’ where commas are separators`,
|
||||
example: 'Example'
|
||||
computedAttrTip2: `Multi-valued attributes (lists) are rendered with [ ] included by default, if you want to remove it, the reference method is: """{{ attr_name | join(',') }}""" where commas are separators`,
|
||||
example: 'Example',
|
||||
attrFilterTip: `The third column of values allows you to select attributes of this model to cascade attributes`,
|
||||
rule: 'Rule',
|
||||
cascadeAttr: 'Cascade',
|
||||
cascadeAttrTip: 'Cascading attributes note the order',
|
||||
},
|
||||
components: {
|
||||
unselectAttributes: 'Unselected',
|
||||
|
@ -325,6 +331,7 @@ const cmdb_en = {
|
|||
sub: 'subscription',
|
||||
selectBelow: 'Please select below',
|
||||
subSuccess: 'Subscription successful',
|
||||
subFailed: 'Subscription failed, please try again later',
|
||||
selectMethods: 'Please select a method',
|
||||
noAuthRequest: 'No certification requested yet',
|
||||
noParamRequest: 'No parameter certification yet',
|
||||
|
@ -380,6 +387,8 @@ const cmdb_en = {
|
|||
yearsAgo: 'years ago',
|
||||
just: 'just now',
|
||||
searchPlaceholder: 'Please search CIType',
|
||||
subCITable: 'Data',
|
||||
subCITree: 'Tree',
|
||||
},
|
||||
custom_dashboard: {
|
||||
charts: 'Chart',
|
||||
|
@ -639,6 +648,7 @@ if __name__ == "__main__":
|
|||
rollbackingTips: 'Rollbacking',
|
||||
batchRollbacking: 'Deleting {total} items in total, {successNum} items successful, {errorNum} items failed',
|
||||
baselineTips: 'Changes at this point in time will also be rollbacked, Unique ID, password and dynamic attributes do not support',
|
||||
cover: 'Cover',
|
||||
},
|
||||
serviceTree: {
|
||||
remove: 'Remove',
|
||||
|
|
|
@ -2,6 +2,8 @@ const cmdb_zh = {
|
|||
relation: '关系',
|
||||
attribute: '属性',
|
||||
configTable: '配置表格',
|
||||
enterpriseVersionFlag: '企',
|
||||
enterpriseVersionTip: '仅限企业版',
|
||||
menu: {
|
||||
views: '视图',
|
||||
topologyView: '拓扑视图',
|
||||
|
@ -274,8 +276,12 @@ const cmdb_zh = {
|
|||
attrAlias: '属性别名',
|
||||
attrCode: '属性代码',
|
||||
computedAttrTip1: '引用属性遵循jinja2语法',
|
||||
computedAttrTip2: `多值属性(列表)默认呈现包括[ ], 如果要去掉, 引用方法为: """{{ attr_name | join(',')}}""" 其中逗号为分隔符`,
|
||||
example: '例如'
|
||||
computedAttrTip2: `多值属性(列表)默认呈现包括[ ], 如果要去掉, 引用方法为: """{{ attr_name | join(',') }}""" 其中逗号为分隔符`,
|
||||
example: '例如',
|
||||
attrFilterTip: '第三列值可选择本模型的属性,来实现级联属性的功能',
|
||||
rule: '规则',
|
||||
cascadeAttr: '级联',
|
||||
cascadeAttrTip: '级联属性注意顺序',
|
||||
},
|
||||
components: {
|
||||
unselectAttributes: '未选属性',
|
||||
|
@ -325,6 +331,7 @@ const cmdb_zh = {
|
|||
sub: '订阅',
|
||||
selectBelow: '请在下方进行选择',
|
||||
subSuccess: '订阅成功',
|
||||
subFailed: '订阅失败,请稍后再试',
|
||||
selectMethods: '请选择方式',
|
||||
noAuthRequest: '暂无请求认证',
|
||||
noParamRequest: '暂无参数认证',
|
||||
|
@ -379,6 +386,8 @@ const cmdb_zh = {
|
|||
yearsAgo: '年前',
|
||||
just: '刚刚',
|
||||
searchPlaceholder: '请搜索模型',
|
||||
subCITable: '数据订阅',
|
||||
subCITree: '层级订阅',
|
||||
},
|
||||
custom_dashboard: {
|
||||
charts: '图表',
|
||||
|
@ -638,6 +647,7 @@ if __name__ == "__main__":
|
|||
rollbackingTips: '正在批量回滚中',
|
||||
batchRollbacking: '正在回滚,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||
baselineTips: '该时间点的变更也会被回滚, 唯一标识、密码属性、动态属性不支持回滚',
|
||||
cover: '覆盖',
|
||||
},
|
||||
serviceTree: {
|
||||
remove: '移除',
|
||||
|
|
|
@ -1,431 +1,504 @@
|
|||
<template>
|
||||
<CustomDrawer
|
||||
:title="title + CIType.alias"
|
||||
width="800"
|
||||
@close="handleClose"
|
||||
:maskClosable="false"
|
||||
:visible="visible"
|
||||
wrapClassName="create-instance-form"
|
||||
:bodyStyle="{ paddingTop: 0 }"
|
||||
:headerStyle="{ borderBottom: 'none' }"
|
||||
>
|
||||
<div class="custom-drawer-bottom-action">
|
||||
<a-button @click="handleClose">{{ $t('cancel') }}</a-button>
|
||||
<a-button type="primary" @click="createInstance">{{ $t('submit') }}</a-button>
|
||||
</div>
|
||||
<template v-if="action === 'create'">
|
||||
<template v-for="group in attributesByGroup">
|
||||
<CreateInstanceFormByGroup
|
||||
:ref="`createInstanceFormByGroup_${group.id}`"
|
||||
:key="group.id || group.name"
|
||||
:group="group"
|
||||
@handleFocusInput="handleFocusInput"
|
||||
:attributeList="attributeList"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="parentsType && parentsType.length">
|
||||
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">{{
|
||||
$t('cmdb.menu.citypeRelation')
|
||||
}}</a-divider>
|
||||
<a-form>
|
||||
<a-row :gutter="24" align="top" type="flex">
|
||||
<a-col :span="12" v-for="item in parentsType" :key="item.id">
|
||||
<a-form-item :label="item.alias || item.name" :colon="false">
|
||||
<a-input-group compact style="width: 100%">
|
||||
<a-select v-model="parentsForm[item.name].attr">
|
||||
<a-select-option
|
||||
:title="attr.alias || attr.name"
|
||||
v-for="attr in item.attributes"
|
||||
:key="attr.name"
|
||||
:value="attr.name"
|
||||
>
|
||||
{{ attr.alias || attr.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-input
|
||||
:placeholder="$t('cmdb.ci.tips1')"
|
||||
v-model="parentsForm[item.name].value"
|
||||
style="width: 50%"
|
||||
/>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="action === 'update'">
|
||||
<a-form :form="form">
|
||||
<p>{{ $t('cmdb.ci.tips2') }}</p>
|
||||
<a-row :gutter="24" v-for="list in batchUpdateLists" :key="list.name">
|
||||
<a-col :span="11">
|
||||
<a-form-item>
|
||||
<el-select showSearch size="small" filterable v-model="list.name" :placeholder="$t('cmdb.ci.tips3')">
|
||||
<el-option
|
||||
v-for="attr in attributeList"
|
||||
:key="attr.name"
|
||||
:value="attr.name"
|
||||
:disabled="batchUpdateLists.findIndex((item) => item.name === attr.name) > -1"
|
||||
:label="attr.alias || attr.name"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<a-form-item>
|
||||
<a-select
|
||||
:style="{ width: '100%' }"
|
||||
v-decorator="[list.name, { rules: [{ required: false }] }]"
|
||||
:placeholder="$t('placeholder2')"
|
||||
v-if="getFieldType(list.name).split('%%')[0] === 'select'"
|
||||
:mode="getFieldType(list.name).split('%%')[1] === 'multiple' ? 'multiple' : 'default'"
|
||||
showSearch
|
||||
allowClear
|
||||
>
|
||||
<a-select-option
|
||||
:value="choice[0]"
|
||||
:key="'New_' + choice + choice_idx"
|
||||
v-for="(choice, choice_idx) in getSelectFieldOptions(list.name)"
|
||||
>
|
||||
<span :style="choice[1] ? choice[1].style || {} : {}">
|
||||
<ops-icon
|
||||
:style="{ color: choice[1].icon.color }"
|
||||
v-if="choice[1] && choice[1].icon && choice[1].icon.name"
|
||||
:type="choice[1].icon.name"
|
||||
/>
|
||||
{{ choice[0] }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-input-number
|
||||
v-decorator="[list.name, { rules: [{ required: false }] }]"
|
||||
style="width: 100%"
|
||||
v-if="getFieldType(list.name) === 'input_number'"
|
||||
/>
|
||||
<a-date-picker
|
||||
v-decorator="[list.name, { rules: [{ required: false }] }]"
|
||||
style="width: 100%"
|
||||
:format="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||
:valueFormat="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||
v-if="getFieldType(list.name) === '4' || getFieldType(list.name) === '3'"
|
||||
:showTime="getFieldType(list.name) === '4' ? false : { format: 'HH:mm:ss' }"
|
||||
/>
|
||||
<a-input
|
||||
v-if="getFieldType(list.name) === 'input'"
|
||||
@focus="(e) => handleFocusInput(e, list)"
|
||||
v-decorator="[list.name, { rules: [{ required: false }] }]"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="2">
|
||||
<a-form-item>
|
||||
<a :style="{ color: 'red', marginTop: '2px' }" @click="handleDelete(list.name)">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" ghost icon="plus" @click="handleAdd">{{ $t('cmdb.ci.newUpdateField') }}</a-button>
|
||||
</a-form>
|
||||
</template>
|
||||
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
||||
</CustomDrawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
import { Select, Option } from 'element-ui'
|
||||
import { getCIType, getCITypeGroupById } from '@/modules/cmdb/api/CIType'
|
||||
import { addCI } from '@/modules/cmdb/api/ci'
|
||||
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
||||
import { valueTypeMap } from '../../../utils/const'
|
||||
import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue'
|
||||
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
||||
|
||||
export default {
|
||||
name: 'CreateInstanceForm',
|
||||
components: {
|
||||
ElSelect: Select,
|
||||
ElOption: Option,
|
||||
JsonEditor,
|
||||
CreateInstanceFormByGroup,
|
||||
},
|
||||
props: {
|
||||
typeIdFromRelation: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
action: '',
|
||||
form: this.$form.createForm(this),
|
||||
visible: false,
|
||||
attributeList: [],
|
||||
|
||||
CIType: {},
|
||||
|
||||
batchUpdateLists: [],
|
||||
editAttr: null,
|
||||
attributesByGroup: [],
|
||||
parentsType: [],
|
||||
parentsForm: {},
|
||||
canEdit: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.action === 'create' ? this.$t('create') + ' ' : this.$t('cmdb.ci.batchUpdate') + ' '
|
||||
},
|
||||
typeId() {
|
||||
if (this.typeIdFromRelation) {
|
||||
return this.typeIdFromRelation
|
||||
}
|
||||
return this.$router.currentRoute.meta.typeId
|
||||
},
|
||||
valueTypeMap() {
|
||||
return valueTypeMap()
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
getFieldType: this.getFieldType,
|
||||
}
|
||||
},
|
||||
inject: ['attrList'],
|
||||
methods: {
|
||||
moment,
|
||||
async getCIType() {
|
||||
await getCIType(this.typeId).then((res) => {
|
||||
this.CIType = res.ci_types[0]
|
||||
})
|
||||
},
|
||||
async getAttributeList() {
|
||||
const _attrList = this.attrList()
|
||||
this.attributeList = _attrList.sort((x, y) => y.is_required - x.is_required)
|
||||
await getCITypeGroupById(this.typeId).then((res1) => {
|
||||
const _attributesByGroup = res1.map((g) => {
|
||||
g.attributes = g.attributes.filter((attr) => !attr.is_computed)
|
||||
return g
|
||||
})
|
||||
const attrHasGroupIds = []
|
||||
res1.forEach((g) => {
|
||||
const id = g.attributes.map((attr) => attr.id)
|
||||
attrHasGroupIds.push(...id)
|
||||
})
|
||||
const otherGroupAttr = this.attributeList.filter(
|
||||
(attr) => !attrHasGroupIds.includes(attr.id) && !attr.is_computed
|
||||
)
|
||||
if (otherGroupAttr.length) {
|
||||
_attributesByGroup.push({ id: -1, name: this.$t('other'), attributes: otherGroupAttr })
|
||||
}
|
||||
console.log(otherGroupAttr, _attributesByGroup)
|
||||
this.attributesByGroup = _attributesByGroup
|
||||
})
|
||||
},
|
||||
createInstance() {
|
||||
const _this = this
|
||||
if (_this.action === 'update') {
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
Object.keys(values).forEach((k) => {
|
||||
const _tempFind = this.attributeList.find((item) => item.name === k)
|
||||
if (
|
||||
_tempFind.value_type === '3' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
if (
|
||||
_tempFind.value_type === '4' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD')
|
||||
}
|
||||
if (_tempFind.value_type === '6') {
|
||||
values[k] = values[k] ? JSON.parse(values[k]) : undefined
|
||||
}
|
||||
})
|
||||
|
||||
_this.$emit('submit', values)
|
||||
})
|
||||
} else {
|
||||
let values = {}
|
||||
for (let i = 0; i < this.attributesByGroup.length; i++) {
|
||||
const data = this.$refs[`createInstanceFormByGroup_${this.attributesByGroup[i].id}`][0].getData()
|
||||
if (data === 'error') {
|
||||
return
|
||||
}
|
||||
values = { ...values, ...data }
|
||||
}
|
||||
|
||||
Object.keys(values).forEach((k) => {
|
||||
const _tempFind = this.attributeList.find((item) => item.name === k)
|
||||
if (
|
||||
_tempFind.value_type === '3' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
if (
|
||||
_tempFind.value_type === '4' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD')
|
||||
}
|
||||
if (_tempFind.value_type === '6') {
|
||||
values[k] = values[k] ? JSON.parse(values[k]) : undefined
|
||||
}
|
||||
})
|
||||
values.ci_type = _this.typeId
|
||||
console.log(this.parentsForm)
|
||||
Object.keys(this.parentsForm).forEach((type) => {
|
||||
if (this.parentsForm[type].value) {
|
||||
values[`$${type}.${this.parentsForm[type].attr}`] = this.parentsForm[type].value
|
||||
}
|
||||
})
|
||||
addCI(values).then((res) => {
|
||||
_this.$message.success(this.$t('addSuccess'))
|
||||
_this.visible = false
|
||||
_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
|
||||
},
|
||||
handleOpen(visible, action) {
|
||||
this.visible = visible
|
||||
this.action = action
|
||||
this.$nextTick(() => {
|
||||
this.form.resetFields()
|
||||
Promise.all([this.getCIType(), this.getAttributeList()]).then(() => {
|
||||
this.batchUpdateLists = [{ name: this.attributeList[0].name }]
|
||||
})
|
||||
if (action === 'create') {
|
||||
getCITypeParent(this.typeId).then(async (res) => {
|
||||
for (let i = 0; i < res.parents.length; i++) {
|
||||
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
|
||||
this.canEdit = {
|
||||
..._.cloneDeep(this.canEdit),
|
||||
[res.parents[i].id]: p_res.result,
|
||||
}
|
||||
})
|
||||
}
|
||||
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
|
||||
const _parentsForm = {}
|
||||
res.parents.forEach((item) => {
|
||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||
_parentsForm[item.name] = { attr: _find.name, value: '' }
|
||||
})
|
||||
this.parentsForm = _parentsForm
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getFieldType(name) {
|
||||
const _find = this.attributeList.find((item) => item.name === name)
|
||||
if (_find) {
|
||||
if (_find.is_choice) {
|
||||
if (_find.is_list) {
|
||||
return 'select%%multiple'
|
||||
}
|
||||
return 'select'
|
||||
} else if ((_find.value_type === '0' || _find.value_type === '1') && !_find.is_list) {
|
||||
return 'input_number'
|
||||
} else if (_find.value_type === '4' || _find.value_type === '3') {
|
||||
return _find.value_type
|
||||
} else {
|
||||
return 'input'
|
||||
}
|
||||
}
|
||||
return 'input'
|
||||
},
|
||||
getSelectFieldOptions(name) {
|
||||
const _find = this.attributeList.find((item) => item.name === name)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
handleAdd() {
|
||||
this.batchUpdateLists.push({ name: undefined })
|
||||
},
|
||||
handleDelete(name) {
|
||||
const _idx = this.batchUpdateLists.findIndex((item) => item.name === name)
|
||||
if (_idx > -1) {
|
||||
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)
|
||||
if (_tempFind.value_type === '6') {
|
||||
this.editAttr = attr
|
||||
e.srcElement.blur()
|
||||
const jsonData = this.form.getFieldValue(attr.name)
|
||||
this.$refs.jsonEditor.open(null, null, jsonData ? JSON.parse(jsonData) : {})
|
||||
} else {
|
||||
this.editAttr = null
|
||||
}
|
||||
},
|
||||
jsonEditorOk(jsonData) {
|
||||
this.form.setFieldsValue({ [this.editAttr.name]: JSON.stringify(jsonData) })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.create-instance-form {
|
||||
.ant-form-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.ant-drawer-body {
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 110px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<CustomDrawer
|
||||
:title="title + CIType.alias"
|
||||
width="800"
|
||||
@close="handleClose"
|
||||
:maskClosable="false"
|
||||
:visible="visible"
|
||||
wrapClassName="create-instance-form"
|
||||
:bodyStyle="{ paddingTop: 0 }"
|
||||
:headerStyle="{ borderBottom: 'none' }"
|
||||
>
|
||||
<div class="custom-drawer-bottom-action">
|
||||
<a-button @click="handleClose">{{ $t('cancel') }}</a-button>
|
||||
<a-button type="primary" @click="createInstance">{{ $t('submit') }}</a-button>
|
||||
</div>
|
||||
<template v-if="action === 'create'">
|
||||
<template v-for="group in attributesByGroup">
|
||||
<CreateInstanceFormByGroup
|
||||
:ref="`createInstanceFormByGroup_${group.id}`"
|
||||
:key="group.id || group.name"
|
||||
:group="group"
|
||||
:attributeList="attributeList"
|
||||
@handleFocusInput="handleFocusInput"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="parentsType && parentsType.length">
|
||||
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">{{
|
||||
$t('cmdb.menu.citypeRelation')
|
||||
}}</a-divider>
|
||||
<a-form>
|
||||
<a-row :gutter="24" align="top" type="flex">
|
||||
<a-col :span="12" v-for="item in parentsType" :key="item.id">
|
||||
<a-form-item :label="item.alias || item.name" :colon="false">
|
||||
<a-input-group compact style="width: 100%">
|
||||
<a-select v-model="parentsForm[item.name].attr">
|
||||
<a-select-option
|
||||
:title="attr.alias || attr.name"
|
||||
v-for="attr in item.attributes"
|
||||
:key="attr.name"
|
||||
:value="attr.name"
|
||||
>
|
||||
{{ attr.alias || attr.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-input
|
||||
:placeholder="$t('cmdb.ci.tips1')"
|
||||
v-model="parentsForm[item.name].value"
|
||||
style="width: 50%"
|
||||
/>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="action === 'update'">
|
||||
<a-form :form="form">
|
||||
<p>{{ $t('cmdb.ci.tips2') }}</p>
|
||||
<a-row :gutter="8" v-for="list in batchUpdateLists" :key="list.name">
|
||||
<a-col :span="6">
|
||||
<a-form-item>
|
||||
<el-select showSearch size="small" filterable v-model="list.name" :placeholder="$t('cmdb.ci.tips3')">
|
||||
<el-option
|
||||
v-for="attr in attributeList"
|
||||
:key="attr.name"
|
||||
:value="attr.name"
|
||||
:disabled="batchUpdateLists.findIndex((item) => item.name === attr.name) > -1"
|
||||
:label="attr.alias || attr.name"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="showListOperation(list.name)" :span="3">
|
||||
<a-form-item>
|
||||
<el-select size="small" filterable v-model="list.operation" :placeholder="$t('placeholder2')">
|
||||
<el-option
|
||||
v-for="(option) in listOperationOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:label="$t(option.label)"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="showListOperation(list.name) ? 10 : 13">
|
||||
<a-form-item>
|
||||
<a-select
|
||||
:style="{ width: '100%' }"
|
||||
v-decorator="[list.name, { rules: getDecoratorRules(list) }]"
|
||||
:placeholder="$t('placeholder2')"
|
||||
v-if="getFieldType(list.name).split('%%')[0] === 'select'"
|
||||
:mode="getFieldType(list.name).split('%%')[1] === 'multiple' ? 'multiple' : 'default'"
|
||||
showSearch
|
||||
allowClear
|
||||
>
|
||||
<a-select-option
|
||||
:value="choice[0]"
|
||||
:key="'New_' + choice + choice_idx"
|
||||
v-for="(choice, choice_idx) in getSelectFieldOptions(list.name)"
|
||||
>
|
||||
<span :style="choice[1] ? choice[1].style || {} : {}">
|
||||
<ops-icon
|
||||
:style="{ color: choice[1].icon.color }"
|
||||
v-if="choice[1] && choice[1].icon && choice[1].icon.name"
|
||||
:type="choice[1].icon.name"
|
||||
/>
|
||||
{{ choice[0] }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-input-number
|
||||
v-decorator="[list.name, { rules: getDecoratorRules(list) }]"
|
||||
style="width: 100%"
|
||||
v-if="getFieldType(list.name) === 'input_number'"
|
||||
/>
|
||||
<a-date-picker
|
||||
v-decorator="[list.name, { rules: getDecoratorRules(list) }]"
|
||||
style="width: 100%"
|
||||
:format="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||
:valueFormat="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||
v-if="getFieldType(list.name) === '4' || getFieldType(list.name) === '3'"
|
||||
:showTime="getFieldType(list.name) === '4' ? false : { format: 'HH:mm:ss' }"
|
||||
/>
|
||||
<a-input
|
||||
v-if="getFieldType(list.name) === 'input'"
|
||||
@focus="(e) => handleFocusInput(e, list)"
|
||||
v-decorator="[list.name, { rules: getDecoratorRules(list) }]"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="2">
|
||||
<a-form-item>
|
||||
<a :style="{ color: 'red', marginTop: '2px' }" @click="handleDelete(list.name)">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" ghost icon="plus" @click="handleAdd">{{ $t('cmdb.ci.newUpdateField') }}</a-button>
|
||||
</a-form>
|
||||
</template>
|
||||
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
||||
</CustomDrawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
import { Select, Option } from 'element-ui'
|
||||
import { getCIType, getCITypeGroupById } from '@/modules/cmdb/api/CIType'
|
||||
import { addCI } from '@/modules/cmdb/api/ci'
|
||||
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
||||
import { valueTypeMap } from '../../../utils/const'
|
||||
import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue'
|
||||
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
||||
|
||||
export default {
|
||||
name: 'CreateInstanceForm',
|
||||
components: {
|
||||
ElSelect: Select,
|
||||
ElOption: Option,
|
||||
JsonEditor,
|
||||
CreateInstanceFormByGroup,
|
||||
},
|
||||
props: {
|
||||
typeIdFromRelation: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
action: '',
|
||||
form: this.$form.createForm(this),
|
||||
visible: false,
|
||||
attributeList: [],
|
||||
|
||||
CIType: {},
|
||||
|
||||
batchUpdateLists: [],
|
||||
editAttr: null,
|
||||
attributesByGroup: [],
|
||||
parentsType: [],
|
||||
parentsForm: {},
|
||||
canEdit: {},
|
||||
listOperationOptions: [
|
||||
{
|
||||
value: 'cover',
|
||||
label: 'cmdb.ci.cover'
|
||||
},
|
||||
{
|
||||
value: 'add',
|
||||
label: 'add'
|
||||
},
|
||||
{
|
||||
value: 'delete',
|
||||
label: 'delete'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.action === 'create' ? this.$t('create') + ' ' : this.$t('cmdb.ci.batchUpdate') + ' '
|
||||
},
|
||||
typeId() {
|
||||
if (this.typeIdFromRelation) {
|
||||
return this.typeIdFromRelation
|
||||
}
|
||||
return this.$router.currentRoute.meta.typeId
|
||||
},
|
||||
valueTypeMap() {
|
||||
return valueTypeMap()
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
getFieldType: this.getFieldType,
|
||||
}
|
||||
},
|
||||
inject: ['attrList'],
|
||||
methods: {
|
||||
moment,
|
||||
async getCIType() {
|
||||
await getCIType(this.typeId).then((res) => {
|
||||
this.CIType = res.ci_types[0]
|
||||
})
|
||||
},
|
||||
async getAttributeList() {
|
||||
const _attrList = this.attrList()
|
||||
this.attributeList = _attrList.sort((x, y) => y.is_required - x.is_required)
|
||||
await getCITypeGroupById(this.typeId).then((res1) => {
|
||||
const _attributesByGroup = res1.map((g) => {
|
||||
g.attributes = g.attributes.filter((attr) => !attr.is_computed)
|
||||
return g
|
||||
})
|
||||
const attrHasGroupIds = []
|
||||
res1.forEach((g) => {
|
||||
const id = g.attributes.map((attr) => attr.id)
|
||||
attrHasGroupIds.push(...id)
|
||||
})
|
||||
const otherGroupAttr = this.attributeList.filter(
|
||||
(attr) => !attrHasGroupIds.includes(attr.id) && !attr.is_computed
|
||||
)
|
||||
if (otherGroupAttr.length) {
|
||||
_attributesByGroup.push({ id: -1, name: this.$t('other'), attributes: otherGroupAttr })
|
||||
}
|
||||
console.log(otherGroupAttr, _attributesByGroup)
|
||||
this.attributesByGroup = _attributesByGroup
|
||||
})
|
||||
},
|
||||
createInstance() {
|
||||
const _this = this
|
||||
if (_this.action === 'update') {
|
||||
this.form.validateFields({ force: true }, (err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
Object.keys(values).forEach((k) => {
|
||||
const _tempFind = this.attributeList.find((item) => item.name === k)
|
||||
if (
|
||||
_tempFind.value_type === '3' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
if (
|
||||
_tempFind.value_type === '4' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD')
|
||||
}
|
||||
if (_tempFind.value_type === '6') {
|
||||
values[k] = values[k] ? JSON.parse(values[k]) : undefined
|
||||
}
|
||||
|
||||
if (_tempFind.is_list) {
|
||||
const operation = this.batchUpdateLists?.find((item) => item.name === k)?.operation || 'cover'
|
||||
switch (operation) {
|
||||
case 'add':
|
||||
case 'delete':
|
||||
values[k] = {
|
||||
op: operation,
|
||||
v: values[k]
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
_this.$emit('submit', values)
|
||||
})
|
||||
} else {
|
||||
let values = {}
|
||||
for (let i = 0; i < this.attributesByGroup.length; i++) {
|
||||
const data = this.$refs[`createInstanceFormByGroup_${this.attributesByGroup[i].id}`][0].getData()
|
||||
if (data === 'error') {
|
||||
return
|
||||
}
|
||||
values = { ...values, ...data }
|
||||
}
|
||||
|
||||
Object.keys(values).forEach((k) => {
|
||||
const _tempFind = this.attributeList.find((item) => item.name === k)
|
||||
if (
|
||||
_tempFind.value_type === '3' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
if (
|
||||
_tempFind.value_type === '4' &&
|
||||
values[k] &&
|
||||
Object.prototype.toString.call(values[k]) === '[object Object]'
|
||||
) {
|
||||
values[k] = values[k].format('YYYY-MM-DD')
|
||||
}
|
||||
if (_tempFind.value_type === '6') {
|
||||
values[k] = values[k] ? JSON.parse(values[k]) : undefined
|
||||
}
|
||||
})
|
||||
values.ci_type = _this.typeId
|
||||
console.log(this.parentsForm)
|
||||
Object.keys(this.parentsForm).forEach((type) => {
|
||||
if (this.parentsForm[type].value) {
|
||||
values[`$${type}.${this.parentsForm[type].attr}`] = this.parentsForm[type].value
|
||||
}
|
||||
})
|
||||
addCI(values).then((res) => {
|
||||
_this.$message.success(this.$t('addSuccess'))
|
||||
_this.visible = false
|
||||
_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
|
||||
},
|
||||
handleOpen(visible, action) {
|
||||
this.visible = visible
|
||||
this.action = action
|
||||
this.$nextTick(() => {
|
||||
this.form.resetFields()
|
||||
Promise.all([this.getCIType(), this.getAttributeList()]).then(() => {
|
||||
this.batchUpdateLists = [{
|
||||
name: this.attributeList?.[0]?.name || undefined,
|
||||
operation: 'cover'
|
||||
}]
|
||||
})
|
||||
if (action === 'create') {
|
||||
getCITypeParent(this.typeId).then(async (res) => {
|
||||
for (let i = 0; i < res.parents.length; i++) {
|
||||
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
|
||||
this.canEdit = {
|
||||
..._.cloneDeep(this.canEdit),
|
||||
[res.parents[i].id]: p_res.result,
|
||||
}
|
||||
})
|
||||
}
|
||||
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
|
||||
const _parentsForm = {}
|
||||
res.parents.forEach((item) => {
|
||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||
_parentsForm[item.name] = { attr: _find.name, value: '' }
|
||||
})
|
||||
this.parentsForm = _parentsForm
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getFieldType(name) {
|
||||
const _find = this.attributeList.find((item) => item.name === name)
|
||||
if (_find) {
|
||||
if (_find.is_choice) {
|
||||
if (_find.is_list) {
|
||||
return 'select%%multiple'
|
||||
}
|
||||
return 'select'
|
||||
} else if ((_find.value_type === '0' || _find.value_type === '1') && !_find.is_list) {
|
||||
return 'input_number'
|
||||
} else if (_find.value_type === '4' || _find.value_type === '3') {
|
||||
return _find.value_type
|
||||
} else {
|
||||
return 'input'
|
||||
}
|
||||
}
|
||||
return 'input'
|
||||
},
|
||||
getSelectFieldOptions(name) {
|
||||
const _find = this.attributeList.find((item) => item.name === name)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
handleAdd() {
|
||||
this.batchUpdateLists.push({
|
||||
name: undefined,
|
||||
operation: 'cover'
|
||||
})
|
||||
},
|
||||
handleDelete(name) {
|
||||
const _idx = this.batchUpdateLists.findIndex((item) => item.name === name)
|
||||
if (_idx > -1) {
|
||||
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)
|
||||
if (_tempFind.value_type === '6') {
|
||||
this.editAttr = attr
|
||||
e.srcElement.blur()
|
||||
const jsonData = this.form.getFieldValue(attr.name)
|
||||
this.$refs.jsonEditor.open(null, null, jsonData ? JSON.parse(jsonData) : {})
|
||||
} else {
|
||||
this.editAttr = null
|
||||
}
|
||||
},
|
||||
jsonEditorOk(jsonData) {
|
||||
this.form.setFieldsValue({ [this.editAttr.name]: JSON.stringify(jsonData) })
|
||||
},
|
||||
|
||||
showListOperation(name) {
|
||||
if (!name) {
|
||||
return false
|
||||
}
|
||||
const attr = this.attributeList.find((attr) => attr.name === name)
|
||||
|
||||
return attr && attr.is_list
|
||||
},
|
||||
|
||||
getDecoratorRules(data) {
|
||||
const { name, operation } = data
|
||||
const isList = this.showListOperation(name)
|
||||
const rules = [
|
||||
{ required: false }
|
||||
]
|
||||
if (isList && ['delete', 'add'].includes(operation)) {
|
||||
rules[0] = {
|
||||
required: true,
|
||||
message: this.$t('placeholder1')
|
||||
}
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.create-instance-form {
|
||||
.ant-form-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.ant-drawer-body {
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 110px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -92,7 +92,7 @@ export default {
|
|||
}
|
||||
|
||||
attr.groupId = -1
|
||||
attr.groupName = '其他'
|
||||
attr.groupName = this.$t('other')
|
||||
attr.code = `{{ ${attr.name} }}`
|
||||
attr.typeText = typeMap?.[attr.value_type] ?? ''
|
||||
})
|
||||
|
|
|
@ -454,10 +454,15 @@ export default {
|
|||
query_expr: _findADT.query_expr || '',
|
||||
enabled: _findADT?.enabled ?? true,
|
||||
}
|
||||
|
||||
const allMachineIndex = this.agentTypeRadioList.findIndex((item) => item.value === 'all')
|
||||
|
||||
if (_findADT.query_expr) {
|
||||
this.agent_type = 'query_expr'
|
||||
} else if (_findADT.agent_id) {
|
||||
this.agent_type = _findADT.agent_id === '0x0000' ? 'master' : 'agent_id'
|
||||
} else if (_findADT.agent_id === '' && allMachineIndex !== -1) {
|
||||
this.agent_type = 'all'
|
||||
} else {
|
||||
this.agent_type = this.agentTypeRadioList[0].value
|
||||
}
|
||||
|
|
|
@ -345,6 +345,7 @@
|
|||
:canDefineScript="canDefineScript"
|
||||
ref="preValueArea"
|
||||
:disabled="isShowComputedArea"
|
||||
:CITypeId="CITypeId"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
|
@ -18,19 +18,19 @@
|
|||
<a-button @click="handleOpenUniqueConstraint" size="small">{{ $t('cmdb.ciType.uniqueConstraint') }}</a-button>
|
||||
<div>
|
||||
<a-tooltip
|
||||
v-for="type in Object.keys(valueTypeMap)"
|
||||
:key="type"
|
||||
:title="$t('cmdb.ciType.filterTips', { name: valueTypeMap[type] })"
|
||||
v-for="typeKey in Object.keys(valueTypeMap)"
|
||||
:key="typeKey"
|
||||
:title="$t('cmdb.ciType.filterTips', { name: valueTypeMap[typeKey] })"
|
||||
>
|
||||
<span
|
||||
@click="handleFilterType(type)"
|
||||
@click="handleFilterType(typeKey)"
|
||||
:class="{
|
||||
'ci-types-attributes-filter': true,
|
||||
'ci-types-attributes-filter-selected': attrTypeFilter.includes(type),
|
||||
'ci-types-attributes-filter-selected': attrTypeFilter.includes(typeKey),
|
||||
}"
|
||||
>
|
||||
<ops-icon :type="getPropertyIcon({ value_type: type })" />
|
||||
{{ valueTypeMap[type] }}
|
||||
<ops-icon :type="getPropertyIcon({ value_type: typeKey })" />
|
||||
{{ valueTypeMap[typeKey] }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
|
|
|
@ -333,7 +333,12 @@
|
|||
</a-col>
|
||||
<a-col :span="24" v-if="!['6', '7'].includes(currentValueType)">
|
||||
<a-form-item :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }" :label="$t('cmdb.ciType.choiceValue')">
|
||||
<PreValueArea ref="preValueArea" :canDefineScript="canDefineScript" :disabled="isShowComputedArea" />
|
||||
<PreValueArea
|
||||
ref="preValueArea"
|
||||
:canDefineScript="canDefineScript"
|
||||
:disabled="isShowComputedArea"
|
||||
:CITypeId="CITypeId"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24" v-if="!['6', '7'].includes(currentValueType)">
|
||||
|
@ -402,6 +407,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
CITypeId: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -32,6 +32,8 @@ import TriggerTable from './triggerTable.vue'
|
|||
import ADTab from './adTab.vue'
|
||||
import GrantComp from '../../components/cmdbGrant/grantComp.vue'
|
||||
|
||||
const ACTIVE_KEY_STORAGE_KEY = 'ops_model_config_tab_key'
|
||||
|
||||
export default {
|
||||
name: 'CITypeDetail',
|
||||
components: {
|
||||
|
@ -53,11 +55,24 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
activeKey: '1',
|
||||
activeKey: localStorage.getItem(ACTIVE_KEY_STORAGE_KEY) || '1',
|
||||
}
|
||||
},
|
||||
beforeCreate() {},
|
||||
mounted() {},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
switch (this.activeKey) {
|
||||
case '6':
|
||||
this.$refs.triggerTable.getTableData()
|
||||
break
|
||||
case '5':
|
||||
this.$refs.reconciliationTable.getTableData()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
|
@ -66,15 +81,23 @@ export default {
|
|||
methods: {
|
||||
changeTab(activeKey) {
|
||||
this.activeKey = activeKey
|
||||
localStorage.setItem(ACTIVE_KEY_STORAGE_KEY, activeKey)
|
||||
this.$nextTick(() => {
|
||||
if (activeKey === '1') {
|
||||
this.$refs.attributesTable.getCITypeGroupData()
|
||||
}
|
||||
if (activeKey === '5') {
|
||||
this.$refs.triggerTable.getTableData()
|
||||
switch (activeKey) {
|
||||
case '1':
|
||||
this.$refs.attributesTable.getCITypeGroupData()
|
||||
break
|
||||
case '6':
|
||||
this.$refs.triggerTable.getTableData()
|
||||
break
|
||||
case '5':
|
||||
this.$refs.reconciliationTable.getTableData()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
<template>
|
||||
<a-tabs v-model="activeKey" size="small" :tabBarStyle="{ borderBottom: 'none' }">
|
||||
<a-tabs v-model="activeKey" size="small" :tabBarStyle="{ borderBottom: 'none' }" @change="handleTabsChange">
|
||||
<a-tab-pane key="expr" :disabled="!canDefineComputed">
|
||||
<span style="font-size:12px;" slot="tab">{{ $t('cmdb.ciType.expr') }}</span>
|
||||
<a-textarea v-model="compute_expr" :placeholder="`{{a}}+{{b}}`" :rows="2" :disabled="!canDefineComputed" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="script" :disabled="!canDefineComputed">
|
||||
<span style="font-size:12px;" slot="tab">{{ $t('cmdb.ciType.code') }}</span>
|
||||
<codemirror
|
||||
style="z-index: 9999"
|
||||
:options="cmOptions"
|
||||
v-model="compute_script"
|
||||
@input="onCodeChange"
|
||||
></codemirror>
|
||||
<CustomCodeMirror
|
||||
codeMirrorId="cmdb-computed-attr"
|
||||
ref="codemirror"
|
||||
@changeCodeContent="onCodeChange"
|
||||
></CustomCodeMirror>
|
||||
</a-tab-pane>
|
||||
<template slot="tabBarExtraContent">
|
||||
<a-button size="small" @click="showAllPropDrawer">
|
||||
|
@ -33,7 +32,8 @@
|
|||
|
||||
<script>
|
||||
import AllAttrDrawer from './allAttrDrawer.vue'
|
||||
import { codemirror } from 'vue-codemirror'
|
||||
|
||||
import CustomCodeMirror from '@/components/CustomCodeMirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/monokai.css'
|
||||
|
||||
|
@ -41,7 +41,7 @@ require('codemirror/mode/python/python.js')
|
|||
export default {
|
||||
name: 'ComputedArea',
|
||||
components: {
|
||||
codemirror,
|
||||
CustomCodeMirror,
|
||||
AllAttrDrawer
|
||||
},
|
||||
props: {
|
||||
|
@ -59,33 +59,6 @@ export default {
|
|||
activeKey: 'expr', // expr script
|
||||
compute_expr: '',
|
||||
compute_script: 'def computed(): \n return',
|
||||
cmOptions: {
|
||||
lineNumbers: true,
|
||||
mode: 'python',
|
||||
height: '200px',
|
||||
theme: 'monokai',
|
||||
tabSize: 4,
|
||||
indentUnit: 4,
|
||||
lineWrapping: false,
|
||||
readOnly: !this.canDefineComputed,
|
||||
extraKeys: {
|
||||
Tab: (cm) => {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('add')
|
||||
} else {
|
||||
cm.replaceSelection(Array(cm.getOption('indentUnit') + 1).join(' '), 'end', '+input')
|
||||
}
|
||||
},
|
||||
'Shift-Tab': (cm) => {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('subtract')
|
||||
} else {
|
||||
const cursor = cm.getCursor()
|
||||
cm.setCursor({ line: cursor.line, ch: cursor.ch - 4 })
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -103,6 +76,9 @@ export default {
|
|||
this.compute_script = compute_script || 'def computed(): \n return'
|
||||
if (compute_script) {
|
||||
this.activeKey = 'script'
|
||||
this.$nextTick(() => {
|
||||
this.$refs.codemirror.initCodeMirror(this.compute_script)
|
||||
})
|
||||
} else {
|
||||
this.activeKey = 'expr'
|
||||
}
|
||||
|
@ -122,6 +98,15 @@ export default {
|
|||
},
|
||||
showAllPropDrawer() {
|
||||
this.$refs.allAttrDrawer.open()
|
||||
},
|
||||
|
||||
handleTabsChange(activeKey) {
|
||||
console.log('handleTabsChange', activeKey)
|
||||
if (activeKey === 'script') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.codemirror.initCodeMirror(this.compute_script)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<a-tabs v-model="activeKey">
|
||||
<a-tab-pane key="1" :tab="$t('cmdb.ciType.addAttribute')">
|
||||
<div :style="{ overflow: 'auto', maxHeight: '480px' }">
|
||||
<create-new-attribute ref="createNewAttribute" :hasFooter="false" @done="handleAddNewAttr" />
|
||||
<create-new-attribute ref="createNewAttribute" :hasFooter="false" :CITypeId="CITypeId" @done="handleAddNewAttr" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="$t('cmdb.ciType.existedAttributes')" force-render>
|
||||
|
|
|
@ -61,12 +61,12 @@
|
|||
<a-tab-pane key="choice_other" :disabled="disabled">
|
||||
<span style="font-size:12px;" slot="tab">{{ $t('cmdb.ciType.choiceOther') }}</span>
|
||||
<a-row :gutter="[24, 24]">
|
||||
<a-col :span="12">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
:style="{ lineHeight: '24px', marginBottom: '5px' }"
|
||||
:label="$t('cmdb.ciType.ciType')"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 12 }"
|
||||
>
|
||||
<treeselect
|
||||
:disable-branch-nodes="true"
|
||||
|
@ -117,12 +117,12 @@
|
|||
</treeselect>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="choice_other.type_ids && choice_other.type_ids.length">
|
||||
<a-col :span="24" v-if="choice_other.type_ids && choice_other.type_ids.length">
|
||||
<a-form-item
|
||||
:style="{ marginBottom: '5px' }"
|
||||
:label="$t('cmdb.ciType.attributes')"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 12 }"
|
||||
>
|
||||
<treeselect
|
||||
:disable-branch-nodes="true"
|
||||
|
@ -162,15 +162,17 @@
|
|||
:style="{ marginBottom: '5px' }"
|
||||
class="pre-value-filter"
|
||||
:label="$t('cmdb.ciType.filter')"
|
||||
:label-col="{ span: 2 }"
|
||||
:wrapper-col="{ span: 22 }"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 19 }"
|
||||
>
|
||||
<FilterComp
|
||||
ref="filterComp"
|
||||
<AttrFilter
|
||||
ref="attrFilter"
|
||||
:isDropdown="false"
|
||||
:canSearchPreferenceAttrList="typeAttrs"
|
||||
@setExpFromFilter="setExpFromFilter"
|
||||
:CITypeId="CITypeId"
|
||||
:expression="filterExp ? `q=${filterExp}` : ''"
|
||||
:curModelAttrList="curModelAttrList"
|
||||
@setExpFromFilter="setExpFromFilter"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
@ -178,6 +180,42 @@
|
|||
</a-tab-pane>
|
||||
<a-tab-pane key="script" :disabled="disabled || !canDefineScript">
|
||||
<span style="font-size:12px;" slot="tab">{{ $t('cmdb.ciType.code') }}</span>
|
||||
<a-form-item
|
||||
:style="{ marginBottom: '5px' }"
|
||||
:label="$t('cmdb.ciType.cascadeAttr')"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 19 }"
|
||||
:extra="scriptCodeExtraText"
|
||||
labelAlign="left"
|
||||
>
|
||||
<a-select
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
placeholder="Please select"
|
||||
optionFilterProp="title"
|
||||
v-model="cascade_attributes"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="attr in curModelAttrList"
|
||||
:key="attr.id"
|
||||
:title="attr.name"
|
||||
|
||||
>
|
||||
{{ attr.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<div class="script-tip">
|
||||
<div>1. {{ $t('cmdb.ciType.computedAttrTip1') }}</div>
|
||||
<div>2. {{ $t('cmdb.ciType.computedAttrTip2') }}</div>
|
||||
</div>
|
||||
|
||||
<a-button size="small" @click="showAllPropDrawer">
|
||||
{{ $t('cmdb.ciType.viewAllAttr') }}
|
||||
</a-button>
|
||||
<AllAttrDrawer ref="allAttrDrawer" />
|
||||
|
||||
<CustomCodeMirror
|
||||
codeMirrorId="cmdb-pre-value"
|
||||
ref="codemirror"
|
||||
|
@ -196,16 +234,18 @@ import { defautValueColor } from '../../utils/const'
|
|||
import ColorPicker from '../../components/colorPicker/index.vue'
|
||||
import Webhook from '../../components/webhook'
|
||||
import { getCITypeGroups } from '../../api/ciTypeGroup'
|
||||
import { getCITypeCommonAttributesByTypeIds } from '../../api/CITypeAttr'
|
||||
import FilterComp from '@/components/CMDBFilterComp'
|
||||
import { getCITypeCommonAttributesByTypeIds, getCITypeAttributesById } from '../../api/CITypeAttr'
|
||||
import AttrFilter from './preValueAttr/attrFilter/index.vue'
|
||||
import AllAttrDrawer from './allAttrDrawer.vue'
|
||||
|
||||
import CustomCodeMirror from '@/components/CustomCodeMirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/monokai.css'
|
||||
require('codemirror/mode/python/python.js')
|
||||
|
||||
export default {
|
||||
name: 'PreValueArea',
|
||||
components: { draggable, PreValueTag, ColorPicker, Webhook, FilterComp, CustomCodeMirror },
|
||||
components: { draggable, PreValueTag, ColorPicker, Webhook, AttrFilter, CustomCodeMirror, AllAttrDrawer },
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
|
@ -215,6 +255,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
CITypeId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -242,6 +286,13 @@ export default {
|
|||
lineWrapping: true,
|
||||
readOnly: this.disabled || !this.canDefineScript,
|
||||
},
|
||||
curModelAttrList: [], // 当前模型属性
|
||||
cascade_attributes: [] // 级联属性id列表
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scriptCodeExtraText() {
|
||||
return this.$t('cmdb.ciType.cascadeAttrTip') + (this.isOpenSource ? ` (${this.$t('cmdb.enterpriseVersionTip')})` : '')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -276,8 +327,18 @@ export default {
|
|||
return { ..._.cloneDeep(item) }
|
||||
})
|
||||
})
|
||||
this.getCITypeAttributesById()
|
||||
},
|
||||
methods: {
|
||||
async getCITypeAttributesById() {
|
||||
const res = await getCITypeAttributesById(this.CITypeId)
|
||||
let curModelAttrList = []
|
||||
if (res?.attributes?.length) {
|
||||
curModelAttrList = res.attributes.filter(attr => !attr.is_password)
|
||||
}
|
||||
this.curModelAttrList = curModelAttrList
|
||||
},
|
||||
|
||||
addNewValue(newValue, newStyle, newIcon) {
|
||||
if (newValue) {
|
||||
const idx = this.valueList.findIndex((v) => v[0] === newValue)
|
||||
|
@ -321,12 +382,13 @@ export default {
|
|||
choice_web_hook: null,
|
||||
choice_other: {
|
||||
script: this.script,
|
||||
cascade_attributes: this.cascade_attributes,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
let choice_other = {}
|
||||
if (this.choice_other.type_ids && this.choice_other.type_ids.length) {
|
||||
this.$refs.filterComp.handleSubmit()
|
||||
this.$refs.attrFilter.handleSubmit()
|
||||
choice_other = { ...this.choice_other, filter: this.filterExp }
|
||||
}
|
||||
return {
|
||||
|
@ -355,9 +417,10 @@ export default {
|
|||
const { type_ids, attr_id, filter } = choice_other
|
||||
this.choice_other = { type_ids, attr_id }
|
||||
this.filterExp = filter
|
||||
this.cascade_attributes = choice_other?.cascade_attributes || []
|
||||
if (type_ids && type_ids.length) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.filterComp.visibleChange(true, false)
|
||||
this.$refs.attrFilter.init(true, false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -390,6 +453,10 @@ export default {
|
|||
})
|
||||
}
|
||||
},
|
||||
|
||||
showAllPropDrawer() {
|
||||
this.$refs.allAttrDrawer.open()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -408,6 +475,12 @@ export default {
|
|||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.script-tip {
|
||||
font-size: 12px;
|
||||
line-height: 22px;
|
||||
color: #a5a9bc;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
||||
<div v-if="ruleList.length > 1" :style="{ width: '60px', height: rowHeight, position: 'relative' }">
|
||||
<treeselect
|
||||
v-if="index !== 0"
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '60px', '--custom-height': rowHeight, position: 'absolute', top: '-24px' }"
|
||||
v-model="item.type"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="ruleTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</treeselect>
|
||||
</div>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '120px', '--custom-height': rowHeight }"
|
||||
v-model="item.property"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="canSearchPreferenceAttrList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.name,
|
||||
label: node.alias || node.name,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<div
|
||||
v-if="node.id !== '$count'"
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
class="property-label"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" />
|
||||
{{ node.label }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
class="property-label"
|
||||
:style="{ borderBottom: '1px solid #E4E7ED', marginBottom: '8px' }"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" />
|
||||
{{ node.label }}
|
||||
</div>
|
||||
<div
|
||||
class="property-label"
|
||||
slot="value-label"
|
||||
slot-scope="{ node }"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '90px', '--custom-height': rowHeight }"
|
||||
v-model="item.exp"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="getExpListByProperty(item.property)"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
@select="(value) => handleChangeExp(value, item, index)"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</treeselect>
|
||||
<ValueControls
|
||||
:rule="ruleList[index]"
|
||||
:attrList="canSearchPreferenceAttrList"
|
||||
:disabled="disabled"
|
||||
:curModelAttrList="curModelAttrList"
|
||||
:rowHeight="rowHeight"
|
||||
@change="(value) => handleChangeValue(value, index)"
|
||||
/>
|
||||
<template v-if="!disabled">
|
||||
<a-tooltip :title="$t('copy')">
|
||||
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="veops-copy"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('delete')">
|
||||
<a class="operation" @click="handleDeleteRule(item)"><a-icon type="minus-circle"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('cmdbFilterComp.addHere')">
|
||||
<a class="operation" @click="handleAddRuleAt(item)"><a-icon type="plus-circle"/></a>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
<div class="table-filter-add" v-if="!disabled && ruleList.length === 0">
|
||||
<a @click="handleAddRule">+ {{ $t('new') }}</a>
|
||||
</div>
|
||||
<div class="attr-filter-tip">{{ $t('cmdb.ciType.attrFilterTip') }}{{ isOpenSource ? ` (${$t('cmdb.enterpriseVersionTip')})` : '' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from '../constants.js'
|
||||
import ValueTypeMapIcon from '@/components/CMDBValueTypeMapIcon'
|
||||
import ValueControls from './valueControls.vue'
|
||||
|
||||
export default {
|
||||
name: 'Expression',
|
||||
components: {
|
||||
ValueTypeMapIcon,
|
||||
ValueControls
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
curModelAttrList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
compareTypeList,
|
||||
rowHeight: '36px'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ruleList: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
ruleTypeList() {
|
||||
return ruleTypeList()
|
||||
},
|
||||
expList() {
|
||||
return expList()
|
||||
},
|
||||
advancedExpList() {
|
||||
return advancedExpList()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getExpListByProperty(property) {
|
||||
if (property === '$count') {
|
||||
return [
|
||||
{ value: 'is', label: this.$t('cmdbFilterComp.is') },
|
||||
{ value: '~is', label: this.$t('cmdbFilterComp.~is') },
|
||||
{ value: 'compare', label: this.$t('cmdbFilterComp.compare') }
|
||||
]
|
||||
}
|
||||
if (property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
||||
return [
|
||||
{ value: 'is', label: this.$t('cmdbFilterComp.is') },
|
||||
{ value: '~is', label: this.$t('cmdbFilterComp.~is') },
|
||||
{ value: '~value', label: this.$t('cmdbFilterComp.~value') }, // 为空的定义有点绕
|
||||
{ value: 'value', label: this.$t('cmdbFilterComp.value') },
|
||||
...this.advancedExpList
|
||||
]
|
||||
}
|
||||
}
|
||||
return [...this.expList, ...this.advancedExpList]
|
||||
},
|
||||
isChoiceByProperty(property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.is_choice
|
||||
}
|
||||
return false
|
||||
},
|
||||
handleAddRule() {
|
||||
this.ruleList.push({
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
})
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleCopyRule(item) {
|
||||
this.ruleList.push({ ...item, id: uuidv4() })
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleDeleteRule(item) {
|
||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||
if (idx > -1) {
|
||||
this.ruleList.splice(idx, 1)
|
||||
}
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleAddRuleAt(item) {
|
||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||
if (idx > -1) {
|
||||
this.ruleList.splice(idx + 1, 0, {
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
})
|
||||
}
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
getChoiceValueByProperty(property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
handleChangeExp({ value }, item, index) {
|
||||
const _ruleList = _.cloneDeep(this.ruleList)
|
||||
if (value === 'range') {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
min: '',
|
||||
max: '',
|
||||
exp: value,
|
||||
}
|
||||
} else if (value === 'compare') {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
compareType: '1',
|
||||
exp: value,
|
||||
}
|
||||
} else {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
exp: value,
|
||||
}
|
||||
}
|
||||
this.ruleList = _ruleList
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
|
||||
handleChangeValue(value, index) {
|
||||
const _ruleList = _.cloneDeep(this.ruleList)
|
||||
_ruleList[index] = value
|
||||
this.$emit('change', _ruleList)
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 150px;
|
||||
|
||||
&-range-icon {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.property-label {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.attr-filter-tip {
|
||||
color: #86909C;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<div>
|
||||
<Expression
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
:disabled="false"
|
||||
:curModelAttrList="curModelAttrList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { compareTypeList } from '../constants.js'
|
||||
|
||||
import Expression from './expression.vue'
|
||||
|
||||
export default {
|
||||
name: 'AttrFilter',
|
||||
components: {
|
||||
Expression
|
||||
},
|
||||
props: {
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
expression: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
regQ: {
|
||||
type: String,
|
||||
default: '(?<=q=).+(?=&)|(?<=q=).+$',
|
||||
},
|
||||
CITypeId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
curModelAttrList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
compareTypeList,
|
||||
visible: false,
|
||||
ruleList: [],
|
||||
filterExp: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init(open, isInitOne = true) {
|
||||
// isInitOne 初始化exp为空时,ruleList是否默认给一条
|
||||
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
|
||||
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
|
||||
: null
|
||||
if (open && exp) {
|
||||
const expArray = exp.split(',').map((item) => {
|
||||
let has_not = ''
|
||||
const key = item.split(':')[0]
|
||||
const val = item
|
||||
.split(':')
|
||||
.slice(1)
|
||||
.join(':')
|
||||
let type, property, exp, value, min, max, compareType
|
||||
if (key.includes('-')) {
|
||||
type = 'or'
|
||||
if (key.includes('~')) {
|
||||
property = key.substring(2)
|
||||
has_not = '~'
|
||||
} else {
|
||||
property = key.substring(1)
|
||||
}
|
||||
} else {
|
||||
type = 'and'
|
||||
if (key.includes('~')) {
|
||||
property = key.substring(1)
|
||||
has_not = '~'
|
||||
} else {
|
||||
property = key
|
||||
}
|
||||
}
|
||||
|
||||
const in_reg = /(?<=\().+(?=\))/g
|
||||
const range_reg = /(?<=\[).+(?=\])/g
|
||||
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
|
||||
if (val === '*') {
|
||||
exp = has_not + 'value'
|
||||
value = ''
|
||||
} else if (in_reg.test(val)) {
|
||||
exp = has_not + 'in'
|
||||
value = val.match(in_reg)[0]
|
||||
} else if (range_reg.test(val)) {
|
||||
exp = has_not + 'range'
|
||||
value = val.match(range_reg)[0]
|
||||
min = value.split('_TO_')[0]
|
||||
max = value.split('_TO_')[1]
|
||||
} else if (compare_reg.test(val)) {
|
||||
exp = has_not + 'compare'
|
||||
value = val.match(compare_reg)[0]
|
||||
const _compareType = val.substring(0, val.match(compare_reg)['index'])
|
||||
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
|
||||
compareType = compareTypeList[idx].value
|
||||
} else if (!val.includes('*')) {
|
||||
exp = has_not + 'is'
|
||||
value = val
|
||||
} else {
|
||||
const resList = [
|
||||
['contain', /(?<=\*).*(?=\*)/g],
|
||||
['end_with', /(?<=\*).+/g],
|
||||
['start_with', /.+(?=\*)/g],
|
||||
]
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const reg = resList[i]
|
||||
if (reg[1].test(val)) {
|
||||
exp = has_not + reg[0]
|
||||
value = val.match(reg[1])[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: uuidv4(),
|
||||
type,
|
||||
property,
|
||||
exp,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
compareType,
|
||||
}
|
||||
})
|
||||
this.ruleList = [...expArray]
|
||||
} else if (open) {
|
||||
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
|
||||
this.ruleList = isInitOne
|
||||
? [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property:
|
||||
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
|
||||
? _canSearchPreferenceAttrList[0].name
|
||||
: undefined,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
},
|
||||
|
||||
handleSubmit() {
|
||||
if (this.ruleList && this.ruleList.length) {
|
||||
this.ruleList[0].type = 'and' // 增删后,以防万一第一个不是and
|
||||
this.filterExp = ''
|
||||
const expList = this.ruleList.map((rule) => {
|
||||
let singleRuleExp = ''
|
||||
let _exp = rule.exp
|
||||
if (rule.type === 'or') {
|
||||
singleRuleExp += '-'
|
||||
}
|
||||
if (rule.exp.includes('~')) {
|
||||
singleRuleExp += '~'
|
||||
_exp = rule.exp.split('~')[1]
|
||||
}
|
||||
singleRuleExp += `${rule.property}:`
|
||||
if (_exp === 'is') {
|
||||
singleRuleExp += `${rule.value ?? ''}`
|
||||
}
|
||||
if (_exp === 'contain') {
|
||||
singleRuleExp += `*${rule.value ?? ''}*`
|
||||
}
|
||||
if (_exp === 'start_with') {
|
||||
singleRuleExp += `${rule.value ?? ''}*`
|
||||
}
|
||||
if (_exp === 'end_with') {
|
||||
singleRuleExp += `*${rule.value ?? ''}`
|
||||
}
|
||||
if (_exp === 'value') {
|
||||
singleRuleExp += `*`
|
||||
}
|
||||
if (_exp === 'in') {
|
||||
singleRuleExp += `(${rule.value ?? ''})`
|
||||
}
|
||||
if (_exp === 'range') {
|
||||
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
|
||||
}
|
||||
if (_exp === 'compare') {
|
||||
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
|
||||
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
|
||||
}
|
||||
return singleRuleExp
|
||||
})
|
||||
this.filterExp = expList.join(',')
|
||||
this.$emit('setExpFromFilter', this.filterExp)
|
||||
} else {
|
||||
this.$emit('setExpFromFilter', '')
|
||||
}
|
||||
this.visible = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -0,0 +1,273 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="control-group" v-if="controlType === 'choice'" >
|
||||
<div
|
||||
class="choice-group"
|
||||
@click="handleControlType('input')"
|
||||
>
|
||||
<a-icon class="choice-group-icon" type="caret-down" />
|
||||
</div>
|
||||
<treeselect
|
||||
class="custom-treeselect input-group"
|
||||
:style="{ '--custom-height': rowHeight }"
|
||||
:value="choiceValue"
|
||||
@input="(value) => handleChange('value', value)"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="curModelAttrList"
|
||||
:placeholder="$t('placeholder2')"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.name,
|
||||
label: node.name,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
{{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
</div>
|
||||
<div class="control-group" v-else>
|
||||
<div
|
||||
class="text-group"
|
||||
@click="handleControlType('choice')"
|
||||
>
|
||||
<ops-icon class="text-group-icon" type="veops-text" />
|
||||
</div>
|
||||
<div
|
||||
class="input-group"
|
||||
v-if="isChoiceByProperty(rule.property) && (rule.exp === 'is' || rule.exp === '~is')"
|
||||
>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ '--custom-height': rowHeight }"
|
||||
:value="rule.value"
|
||||
@input="(value) => handleChange('value', value)"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="getChoiceValueByProperty(rule.property)"
|
||||
:placeholder="$t('placeholder2')"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node[0],
|
||||
label: node[0],
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
{{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
</div>
|
||||
<div
|
||||
compact
|
||||
v-else-if="rule.exp === 'range' || rule.exp === '~range'"
|
||||
class="input-group"
|
||||
>
|
||||
<a-input
|
||||
class="ops-input"
|
||||
:placeholder="$t('min')"
|
||||
:disabled="disabled"
|
||||
:value="rule.min"
|
||||
@change="(e) => handleChange('min', e.target.value)"
|
||||
/>
|
||||
<span class="input-group-range-icon">~</span>
|
||||
<a-input
|
||||
class="ops-input"
|
||||
v-model="rule.max"
|
||||
:placeholder="$t('max')"
|
||||
:disabled="disabled"
|
||||
:value="rule.max"
|
||||
@change="(e) => handleChange('max', e.target.value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group" compact v-else-if="rule.exp === 'compare'">
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '70px', '--custom-height': rowHeight, 'flex-shrink': 0 }"
|
||||
:value="rule.compareType"
|
||||
@input="(value) => handleChange('compareType', value)"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="compareTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</treeselect>
|
||||
<a-input :value="rule.value" @change="(e) => handleChange('value', e.target.value)" class="ops-input"/>
|
||||
</div>
|
||||
<div class="input-group" v-else-if="rule.exp !== 'value' && rule.exp !== '~value'">
|
||||
<a-input
|
||||
:value="rule.value"
|
||||
@change="(e) => handleChange('value', e.target.value)"
|
||||
:placeholder="rule.exp === 'in' || rule.exp === '~in' ? $t('cmdbFilterComp.split', { separator: ';' }) : ''"
|
||||
class="ops-input"
|
||||
:disabled="disabled"
|
||||
></a-input>
|
||||
</div>
|
||||
<div v-else :style="{ width: '136px' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { compareTypeList } from '../constants.js'
|
||||
|
||||
export default {
|
||||
name: 'ValueControls',
|
||||
props: {
|
||||
rule: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
attrList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 当前模型属性
|
||||
curModelAttrList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 行高
|
||||
rowHeight: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
compareTypeList,
|
||||
controlType: 'input',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
choiceValue() {
|
||||
const regex = /\{\{([^}]+)\}\}/g
|
||||
const val = regex.exec(this?.rule?.value || '')
|
||||
return val ? val?.[1]?.trim() || '' : this?.value?.value || ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isChoiceByProperty(property) {
|
||||
const _find = this.attrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.is_choice
|
||||
}
|
||||
return false
|
||||
},
|
||||
getChoiceValueByProperty(property) {
|
||||
const _find = this.attrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
handleControlType(type) {
|
||||
this.controlType = type
|
||||
},
|
||||
handleChange(key, value) {
|
||||
if (this.controlType === 'choice' && key === 'value') {
|
||||
value = `{{ ${value} }}`
|
||||
}
|
||||
|
||||
this.$emit('change', {
|
||||
...this.rule,
|
||||
[key]: value
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.control-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 136px;
|
||||
|
||||
&-range-icon {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.choice-group {
|
||||
width: 14px;
|
||||
height: 36px;
|
||||
flex-shrink: 0;
|
||||
background-color: #00B3CC;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.text-group {
|
||||
width: 14px;
|
||||
height: 36px;
|
||||
flex-shrink: 0;
|
||||
background-color: #2F54EB;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,41 @@
|
|||
import i18n from '@/lang'
|
||||
|
||||
export const ruleTypeList = () => {
|
||||
return [
|
||||
{ value: 'and', label: i18n.t('cmdbFilterComp.and') },
|
||||
{ value: 'or', label: i18n.t('cmdbFilterComp.or') },
|
||||
// { value: 'not', label: '非' },
|
||||
]
|
||||
}
|
||||
|
||||
export const expList = () => {
|
||||
return [
|
||||
{ value: 'is', label: i18n.t('cmdbFilterComp.is') },
|
||||
{ value: '~is', label: i18n.t('cmdbFilterComp.~is') },
|
||||
{ value: 'contain', label: i18n.t('cmdbFilterComp.contain') },
|
||||
{ value: '~contain', label: i18n.t('cmdbFilterComp.~contain') },
|
||||
{ value: 'start_with', label: i18n.t('cmdbFilterComp.start_with') },
|
||||
{ value: '~start_with', label: i18n.t('cmdbFilterComp.~start_with') },
|
||||
{ value: 'end_with', label: i18n.t('cmdbFilterComp.end_with') },
|
||||
{ value: '~end_with', label: i18n.t('cmdbFilterComp.~end_with') },
|
||||
{ value: '~value', label: i18n.t('cmdbFilterComp.~value') },
|
||||
{ value: 'value', label: i18n.t('cmdbFilterComp.value') },
|
||||
]
|
||||
}
|
||||
|
||||
export const advancedExpList = () => {
|
||||
return [
|
||||
{ value: 'in', label: i18n.t('cmdbFilterComp.in') },
|
||||
{ value: '~in', label: i18n.t('cmdbFilterComp.~in') },
|
||||
{ value: 'range', label: i18n.t('cmdbFilterComp.range') },
|
||||
{ value: '~range', label: i18n.t('cmdbFilterComp.~range') },
|
||||
{ value: 'compare', label: i18n.t('cmdbFilterComp.compare') },
|
||||
]
|
||||
}
|
||||
|
||||
export const compareTypeList = [
|
||||
{ value: '1', label: '>' },
|
||||
{ value: '2', label: '>=' },
|
||||
{ value: '3', label: '<' },
|
||||
{ value: '4', label: '<=' },
|
||||
]
|
|
@ -181,12 +181,19 @@
|
|||
</span>
|
||||
</div>
|
||||
<div v-else class="cmdb-preference-footor-unsubscribed">
|
||||
<span
|
||||
@click="openSubscribeSetting(item)"
|
||||
><ops-icon :style="{ marginRight: '3px' }" type="cmdb-preference-subscribe" />{{
|
||||
$t('cmdb.preference.sub')
|
||||
}}</span
|
||||
<a
|
||||
@click="handleSubscribeCIType(item)"
|
||||
class="cmdb-preference-footor-unsubscribed-item"
|
||||
>
|
||||
<ops-icon type="cmdb-ci" />{{ $t('cmdb.preference.subCITable') }}
|
||||
</a>
|
||||
<span class="cmdb-preference-footor-unsubscribed-gap"></span>
|
||||
<a
|
||||
@click="openSubscribeSetting(item, '2')"
|
||||
class="cmdb-preference-footor-unsubscribed-item"
|
||||
>
|
||||
<ops-icon type="cmdb-tree" />{{ $t('cmdb.preference.subCITree') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<i></i><i></i><i></i><i></i><i></i>
|
||||
|
@ -221,6 +228,7 @@ import {
|
|||
subscribeTreeView,
|
||||
preferenceCitypeOrder,
|
||||
} from '@/modules/cmdb/api/preference'
|
||||
import { getCITypeAttributesByName } from '@/modules/cmdb/api/CITypeAttr'
|
||||
import CollapseTransition from '@/components/CollapseTransition'
|
||||
import SubscribeSetting from '../../components/subscribeSetting/subscribeSetting'
|
||||
import { getCIAdcStatistics } from '../../api/ci'
|
||||
|
@ -381,9 +389,39 @@ export default {
|
|||
this.getCITypes()
|
||||
})
|
||||
},
|
||||
|
||||
async handleSubscribeCIType(ciType) {
|
||||
try {
|
||||
const res = await getCITypeAttributesByName(ciType.id)
|
||||
const attributes = res?.attributes || []
|
||||
const subscribeList = attributes
|
||||
.filter((item) => item?.default_show)
|
||||
.map((item) => {
|
||||
return [item?.id?.toString(), false]
|
||||
})
|
||||
if (subscribeList.length === 0) {
|
||||
const uniqueItem = attributes.find((item) => item?.id === res?.unique_id)
|
||||
if (uniqueItem) {
|
||||
subscribeList.push([uniqueItem?.id?.toString(), false])
|
||||
}
|
||||
}
|
||||
|
||||
await subscribeCIType(
|
||||
ciType.id,
|
||||
subscribeList
|
||||
)
|
||||
this.$message.success(this.$t('cmdb.components.subSuccess'))
|
||||
this.resetRoute()
|
||||
} catch (error) {
|
||||
console.error('handleSubscribeCIType failed', error)
|
||||
this.$message.success(this.$t('cmdb.components.subFailed'))
|
||||
}
|
||||
},
|
||||
|
||||
openSubscribeSetting(ciType, activeKey = '1') {
|
||||
this.$refs.subscribeSetting.open({ ...ciType, type_id: ciType.id }, activeKey)
|
||||
},
|
||||
|
||||
changeGroupExpand(group) {
|
||||
const _idx = this.expandKeys.findIndex((expand) => expand === group.id)
|
||||
if (_idx > -1) {
|
||||
|
@ -653,11 +691,27 @@ export default {
|
|||
}
|
||||
}
|
||||
.cmdb-preference-footor-unsubscribed {
|
||||
text-align: center;
|
||||
> span {
|
||||
color: @primary-color;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 10px;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.76);
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
&-gap {
|
||||
width: 1px;
|
||||
height: 18px;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
}
|
||||
.cmdb-preference-footor-subscribed {
|
||||
|
|
Loading…
Reference in New Issue