feat: dynamic attribute (#534)

This commit is contained in:
pycook 2024-06-07 10:29:32 +08:00 committed by GitHub
parent 1a774490ac
commit fa737e75c3
15 changed files with 205 additions and 96 deletions

View File

@ -383,12 +383,12 @@ class CIManager(object):
computed_attrs.append(attr.to_dict())
elif attr.is_password:
if attr.name in ci_dict:
password_dict[attr.id] = ci_dict.pop(attr.name)
password_dict[attr.id] = (ci_dict.pop(attr.name), attr.is_dynamic)
elif attr.alias in ci_dict:
password_dict[attr.id] = ci_dict.pop(attr.alias)
password_dict[attr.id] = (ci_dict.pop(attr.alias), attr.is_dynamic)
if attr.re_check and password_dict.get(attr.id):
value_manager.check_re(attr.re_check, password_dict[attr.id])
value_manager.check_re(attr.re_check, password_dict[attr.id][0])
if computed_attrs:
value_manager.handle_ci_compute_attributes(ci_dict, computed_attrs, ci)
@ -421,7 +421,8 @@ class CIManager(object):
operate_type = OperateType.UPDATE if ci is not None else OperateType.ADD
try:
ci = ci or CI.create(type_id=ci_type.id, is_auto_discovery=is_auto_discovery)
record_id = value_manager.create_or_update_attr_value(ci, ci_dict, key2attr, ticket_id=ticket_id)
record_id, has_dynamic = value_manager.create_or_update_attr_value(
ci, ci_dict, key2attr, ticket_id=ticket_id)
except BadRequest as e:
if existed is None:
cls.delete(ci.id)
@ -431,7 +432,7 @@ class CIManager(object):
for attr_id in password_dict:
record_id = cls.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci_type.id)
if record_id: # has change
if record_id or has_dynamic: # has change
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE)
if ref_ci_dict: # add relations
@ -440,7 +441,6 @@ class CIManager(object):
return ci.id
def update(self, ci_id, _is_admin=False, ticket_id=None, __sync=False, **ci_dict):
current_app.logger.info((ci_id, ci_dict, __sync))
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
ci = self.confirm_ci_existed(ci_id)
@ -465,12 +465,12 @@ class CIManager(object):
computed_attrs.append(attr.to_dict())
elif attr.is_password:
if attr.name in ci_dict:
password_dict[attr.id] = ci_dict.pop(attr.name)
password_dict[attr.id] = (ci_dict.pop(attr.name), attr.is_dynamic)
elif attr.alias in ci_dict:
password_dict[attr.id] = ci_dict.pop(attr.alias)
password_dict[attr.id] = (ci_dict.pop(attr.alias), attr.is_dynamic)
if attr.re_check and password_dict.get(attr.id):
value_manager.check_re(attr.re_check, password_dict[attr.id])
value_manager.check_re(attr.re_check, password_dict[attr.id][0])
if computed_attrs:
value_manager.handle_ci_compute_attributes(ci_dict, computed_attrs, ci)
@ -495,7 +495,8 @@ class CIManager(object):
ci_dict.pop(k)
try:
record_id = value_manager.create_or_update_attr_value(ci, ci_dict, key2attr, ticket_id=ticket_id)
record_id, has_dynamic = value_manager.create_or_update_attr_value(
ci, ci_dict, key2attr, ticket_id=ticket_id)
except BadRequest as e:
raise e
@ -503,25 +504,25 @@ class CIManager(object):
for attr_id in password_dict:
record_id = self.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci.type_id)
if record_id: # has change
if record_id or has_dynamic: # has change
if not __sync:
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE)
else:
ci_cache((ci_id, OperateType.UPDATE, record_id))
ci_cache(ci_id, OperateType.UPDATE, record_id)
ref_ci_dict = {k: v for k, v in ci_dict.items() if k.startswith("$") and "." in k}
if ref_ci_dict:
if not __sync:
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id), queue=CMDB_QUEUE)
else:
ci_relation_add((ref_ci_dict, ci.id))
ci_relation_add(ref_ci_dict, ci.id)
@staticmethod
def update_unique_value(ci_id, unique_name, unique_value):
ci = CI.get_by_id(ci_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(ci_id)))
key2attr = {unique_name: AttributeCache.get(unique_name)}
record_id = AttributeValueManager().create_or_update_attr_value(ci, {unique_name: unique_value}, key2attr)
record_id, _ = AttributeValueManager().create_or_update_attr_value(ci, {unique_name: unique_value}, key2attr)
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE)
@ -761,6 +762,7 @@ class CIManager(object):
@classmethod
def save_password(cls, ci_id, attr_id, value, record_id, type_id):
value, is_dynamic = value
changed = None
encrypt_value = None
value_table = ValueTypeMap.table[ValueTypeEnum.PASSWORD]
@ -777,14 +779,18 @@ class CIManager(object):
if existed is None:
if value:
value_table.create(ci_id=ci_id, attr_id=attr_id, value=encrypt_value)
changed = [(ci_id, attr_id, OperateType.ADD, '', PASSWORD_DEFAULT_SHOW, type_id)]
if not is_dynamic:
changed = [(ci_id, attr_id, OperateType.ADD, '', PASSWORD_DEFAULT_SHOW, type_id)]
elif existed.value != encrypt_value:
if value:
existed.update(ci_id=ci_id, attr_id=attr_id, value=encrypt_value)
changed = [(ci_id, attr_id, OperateType.UPDATE, PASSWORD_DEFAULT_SHOW, PASSWORD_DEFAULT_SHOW, type_id)]
if not is_dynamic:
changed = [(ci_id, attr_id, OperateType.UPDATE, PASSWORD_DEFAULT_SHOW,
PASSWORD_DEFAULT_SHOW, type_id)]
else:
existed.delete()
changed = [(ci_id, attr_id, OperateType.DELETE, PASSWORD_DEFAULT_SHOW, '', type_id)]
if not is_dynamic:
changed = [(ci_id, attr_id, OperateType.DELETE, PASSWORD_DEFAULT_SHOW, '', type_id)]
if current_app.config.get('SECRETS_ENGINE') == 'vault':
vault = VaultClient(current_app.config.get('VAULT_URL'), current_app.config.get('VAULT_TOKEN'))

View File

@ -856,7 +856,9 @@ class CITypeRelationManager(object):
if ci_type is None:
return nodes, edges
nodes.append(ci_type.to_dict())
ci_type_dict = ci_type.to_dict()
ci_type_dict.setdefault('level', [0])
nodes.append(ci_type_dict)
node_ids.add(ci_type.id)
def _find(_id, lv):

View File

@ -172,6 +172,7 @@ class TopologyViewManager(object):
_type = CITypeCache.get(central_node_type)
if not _type:
return dict(nodes=nodes, links=links)
type2meta = {_type.id: _type.icon}
root_ids = []
show_key = AttributeCache.get(_type.show_id or _type.unique_id)
@ -192,7 +193,6 @@ class TopologyViewManager(object):
prefix = REDIS_PREFIX_CI_RELATION
key = list(map(str, root_ids))
id2node = {}
type2meta = {}
for level in sorted([i for i in path.keys() if int(i) > 0]):
type_ids = {int(i) for i in path[level]}

View File

@ -253,6 +253,9 @@ def is_app_admin(app=None):
if app is None:
return False
if hasattr(current_user, 'username') and current_user.username == 'worker':
return True
app_id = app.id
if 'acl_admin' in session.get("acl", {}).get("parentRoles", []):
return True

View File

@ -79,7 +79,8 @@ class PermissionCRUD(object):
return r and cls.get_all(r.id)
@staticmethod
def grant(rid, perms, resource_id=None, group_id=None, rebuild=True, source=AuditOperateSource.acl):
def grant(rid, perms, resource_id=None, group_id=None, rebuild=True,
source=AuditOperateSource.acl, force_update=False):
app_id = None
rt_id = None
@ -106,8 +107,23 @@ class PermissionCRUD(object):
if not perms:
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(group.resource_type_id)]
_role_permissions = []
if force_update:
revoke_role_permissions = []
existed_perms = RolePermission.get_by(rid=rid,
app_id=app_id,
group_id=group_id,
resource_id=resource_id,
to_dict=False)
for role_perm in existed_perms:
perm = PermissionCache.get(role_perm.perm_id, rt_id)
if perm and perm.name not in perms:
role_perm.soft_delete()
revoke_role_permissions.append(role_perm)
AuditCRUD.add_permission_log(app_id, AuditOperateType.revoke, rid, rt_id,
revoke_role_permissions, source=source)
_role_permissions = []
for _perm in set(perms):
perm = PermissionCache.get(_perm, rt_id)
if not perm:

View File

@ -51,12 +51,12 @@ def _auth_with_key():
user, authenticated = User.query.authenticate_with_key(key, secret, req_args, path)
if user and authenticated:
login_user(user)
reset_session(user)
# reset_session(user)
return True
role, authenticated = Role.query.authenticate_with_key(key, secret, req_args, path)
if role and authenticated:
reset_session(None, role=role.name)
# reset_session(None, role=role.name)
return True
return False

View File

@ -104,6 +104,7 @@ class Attribute(Model):
is_link = db.Column(db.Boolean, default=False)
is_password = db.Column(db.Boolean, default=False)
is_sortable = db.Column(db.Boolean, default=False)
is_dynamic = db.Column(db.Boolean, default=False)
default = db.Column(db.JSON) # {"default": None}

View File

@ -200,7 +200,9 @@ const cmdb_en = {
show: 'show attribute',
setAsShow: 'Set as show attribute',
cancelSetAsShow: 'Cancel show attribute',
showTips: 'The names of nodes in the service tree and topology view'
showTips: 'The names of nodes in the service tree and topology view',
isDynamic: 'Dynamic',
dynamicTips: 'For example, for monitoring data and frequently updated data, it is recommended to set it as a dynamic attribute, so that the change history of the attribute will not be recorded.',
},
components: {
unselectAttributes: 'Unselected',
@ -538,7 +540,7 @@ if __name__ == "__main__":
rollbackSuccess: 'Rollback successfully',
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 and password attributes do not support',
baselineTips: 'Changes at this point in time will also be rollbacked, Unique ID, password and dynamic attributes do not support',
},
serviceTree: {
remove: 'Remove',

View File

@ -78,7 +78,7 @@ const cmdb_zh = {
password: '密码',
link: '链接',
list: '多值',
listTips: '字段的值是1个或者多个接口返回的值的类型是list',
listTips: '属性的值是1个或者多个接口返回的值的类型是list',
computeForAllCITips: '所有CI触发计算',
confirmcomputeForAllCITips: '确认触发所有CI的计算',
isUnique: '是否唯一',
@ -89,7 +89,7 @@ const cmdb_zh = {
isSortable: '可排序',
isIndex: '是否索引',
index: '索引',
indexTips: '字段可被用于检索,加速查询',
indexTips: '属性可被用于全文检索,加速查询',
confirmDelete: '确认删除【{name}】?',
confirmDelete2: '确认删除?',
computeSuccess: '触发成功!',
@ -200,7 +200,9 @@ const cmdb_zh = {
show: '展示属性',
setAsShow: '设置为展示属性',
cancelSetAsShow: '取消设置为展示属性',
showTips: '服务树和拓扑视图里节点的名称'
showTips: '服务树和拓扑视图里节点的名称',
isDynamic: '动态属性',
dynamicTips: '譬如监控类的数据, 频繁更新的数据, 建议设置为动态属性, 则不会记录该属性的变更历史',
},
components: {
unselectAttributes: '未选属性',
@ -538,7 +540,7 @@ if __name__ == "__main__":
rollbackSuccess: '回滚成功',
rollbackingTips: '正在批量回滚中',
batchRollbacking: '正在回滚,共{total}个,成功{successNum}个,失败{errorNum}个',
baselineTips: '该时间点的变更也会被回滚, 唯一标识、密码属性不支持回滚',
baselineTips: '该时间点的变更也会被回滚, 唯一标识、密码属性、动态属性不支持回滚',
},
serviceTree: {
remove: '移除',

View File

@ -163,6 +163,12 @@ export default {
width: 110,
help: this.$t('cmdb.ci.tips10'),
},
{
field: 'is_dynamic',
title: this.$t('cmdb.ciType.isDynamic'),
width: 110,
help: this.$t('cmdb.ciType.dynamicTips'),
},
]
},
},

View File

@ -180,6 +180,10 @@ export default {
label: this.$t('cmdb.ciType.isIndex'),
property: 'is_index',
},
{
label: this.$t('cmdb.ciType.isDynamic'),
property: 'is_dynamic',
},
]
},
inherited() {

View File

@ -157,33 +157,6 @@
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item
:label-col="horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('required')"
>
<a-switch
@change="(checked) => onChange(checked, 'is_required')"
name="is_required"
v-decorator="['is_required', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6" v-if="currentValueType !== '6' && currentValueType !== '7'">
<a-form-item
:label-col="{ span: 8 }"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('cmdb.ciType.unique')"
>
<a-switch
:disabled="isShowComputedArea"
@change="onChange"
name="is_unique"
v-decorator="['is_unique', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="currentValueType === '2' ? 6 : 0" v-if="currentValueType !== '6'">
<a-form-item
:hidden="currentValueType === '2' ? false : true"
@ -196,7 +169,7 @@
>{{ $t('cmdb.ciType.index') }}
<a-tooltip :title="$t('cmdb.ciType.indexTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
style="position:absolute;top:2px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
@click="
@ -217,10 +190,37 @@
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-col :span="6" v-if="currentValueType !== '6' && currentValueType !== '7'">
<a-form-item
:label-col="currentValueType === '2' ? { span: 8 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('cmdb.ciType.unique')"
>
<a-switch
:disabled="isShowComputedArea"
@change="onChange"
name="is_unique"
v-decorator="['is_unique', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item
:label-col="['2', '6', '7'].findIndex(i => currentValueType === i) === -1 ? { span: 8 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('required')"
>
<a-switch
@change="(checked) => onChange(checked, 'is_required')"
name="is_required"
v-decorator="['is_required', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item
:label-col="currentValueType === '2' ? { span: 12 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
>
<template slot="label">
<span
@ -228,7 +228,7 @@
>{{ $t('cmdb.ciType.defaultShow') }}
<a-tooltip :title="$t('cmdb.ciType.defaultShowTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
style="position:absolute;top:2px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
@click="
@ -295,6 +295,37 @@
/>
</a-form-item>
</a-col>
<a-col span="6">
<a-form-item
:label-col="['2', '6', '7'].findIndex(i => currentValueType === i) === -1 ? { span: 12 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
>
<template slot="label">
<span
style="position:relative;white-space:pre;"
>{{ $t('cmdb.ciType.isDynamic') }}
<a-tooltip :title="$t('cmdb.ciType.dynamicTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
@click="
(e) => {
e.stopPropagation()
e.preventDefault()
}
"
/>
</a-tooltip>
</span>
</template>
<a-switch
@change="(checked) => onChange(checked, 'is_dynamic')"
name="is_dynamic"
v-decorator="['is_dynamic', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-divider style="font-size:14px;margin-top:6px;">{{ $t('cmdb.ciType.advancedSettings') }}</a-divider>
<a-row>
<a-col :span="24" v-if="!['6'].includes(currentValueType)">
@ -534,6 +565,7 @@ export default {
is_index: _record.is_index,
is_sortable: _record.is_sortable,
is_computed: _record.is_computed,
is_dynamic: _record.is_dynamic,
})
}
console.log(_record)

View File

@ -150,42 +150,19 @@
</a-col>
</a-row>
<a-col :span="6">
<a-col :span="currentValueType === '2' ? 6 : 0" v-if="currentValueType !== '6'">
<a-form-item
:hidden="currentValueType === '2' ? false : true"
:label-col="horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('required')"
>
<a-switch
@change="(checked) => onChange(checked, 'is_required')"
name="is_required"
v-decorator="['is_required', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6" v-if="currentValueType !== '6' && currentValueType !== '7'">
<a-form-item
:label-col="{ span: 8 }"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('cmdb.ciType.unique')"
>
<a-switch
:disabled="isShowComputedArea"
@change="onChange"
name="is_unique"
v-decorator="['is_unique', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6" v-if="currentValueType === '2'">
<a-form-item :label-col="horizontalFormItemLayout.labelCol" :wrapper-col="horizontalFormItemLayout.wrapperCol">
<template slot="label">
<span
style="position:relative;white-space:pre;"
>{{ $t('cmdb.ciType.index') }}
<a-tooltip :title="$t('cmdb.ciType.indexTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
style="position:absolute;top:2px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
@click="
@ -206,10 +183,37 @@
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-col :span="6" v-if="currentValueType !== '6' && currentValueType !== '7'">
<a-form-item
:label-col="currentValueType === '2' ? { span: 8 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('cmdb.ciType.unique')"
>
<a-switch
:disabled="isShowComputedArea"
@change="onChange"
name="is_unique"
v-decorator="['is_unique', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item
:label-col="['2', '6', '7'].findIndex(i => currentValueType === i) === -1 ? { span: 8 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('required')"
>
<a-switch
@change="(checked) => onChange(checked, 'is_required')"
name="is_required"
v-decorator="['is_required', { rules: [], valuePropName: 'checked' }]"
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item
:label-col="currentValueType === '2' ? { span: 12 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
>
<template slot="label">
<span
@ -217,7 +221,7 @@
>{{ $t('cmdb.ciType.defaultShow') }}
<a-tooltip :title="$t('cmdb.ciType.defaultShowTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
style="position:absolute;top:2px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
@click="
@ -284,6 +288,37 @@
/>
</a-form-item>
</a-col>
<a-col span="6">
<a-form-item
:label-col="['2', '6', '7'].findIndex(i => currentValueType === i) === -1 ? { span: 12 } : horizontalFormItemLayout.labelCol"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
>
<template slot="label">
<span
style="position:relative;white-space:pre;"
>{{ $t('cmdb.ciType.isDynamic') }}
<a-tooltip :title="$t('cmdb.ciType.dynamicTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
@click="
(e) => {
e.stopPropagation()
e.preventDefault()
}
"
/>
</a-tooltip>
</span>
</template>
<a-switch
@change="(checked) => onChange(checked, 'is_dynamic')"
name="is_dynamic"
v-decorator="['is_dynamic', { rules: [], valuePropName: 'checked', initialValue: currentValueType === '6' ? true: false }]"
/>
</a-form-item>
</a-col>
<a-divider style="font-size:14px;margin-top:6px;">{{ $t('cmdb.ciType.advancedSettings') }}</a-divider>
<a-row>
<a-col :span="24" v-if="!['6'].includes(currentValueType)">
@ -404,8 +439,8 @@ export default {
}
console.log(values)
const { is_required, default_show, default_value } = values
const data = { is_required, default_show }
const { is_required, default_show, default_value, is_dynamic } = values
const data = { is_required, default_show, is_dynamic }
delete values.is_required
delete values.default_show
if (values.value_type === '0' && default_value) {

View File

@ -7,15 +7,15 @@
<a-tab-pane key="2" :tab="$t('cmdb.ciType.relation')">
<RelationTable :CITypeId="CITypeId" :CITypeName="CITypeName"></RelationTable>
</a-tab-pane>
<a-tab-pane key="3" :tab="$t('cmdb.ciType.trigger')">
<TriggerTable ref="triggerTable" :CITypeId="CITypeId"></TriggerTable>
</a-tab-pane>
<a-tab-pane key="4" :tab="$t('cmdb.ciType.attributeAD')">
<a-tab-pane key="3" :tab="$t('cmdb.ciType.attributeAD')">
<AttrAD :CITypeId="CITypeId"></AttrAD>
</a-tab-pane>
<a-tab-pane key="5" :tab="$t('cmdb.ciType.relationAD')">
<a-tab-pane key="4" :tab="$t('cmdb.ciType.relationAD')">
<RelationAD :CITypeId="CITypeId"></RelationAD>
</a-tab-pane>
<a-tab-pane key="5" :tab="$t('cmdb.ciType.trigger')">
<TriggerTable ref="triggerTable" :CITypeId="CITypeId"></TriggerTable>
</a-tab-pane>
<a-tab-pane key="6" :tab="$t('cmdb.ciType.grant')">
<GrantComp :CITypeId="CITypeId" resourceType="CIType" :resourceTypeName="CITypeName"></GrantComp>
<div class="citype-detail-title">{{ $t('cmdb.components.relationGrant') }}</div>
@ -67,7 +67,7 @@ export default {
if (activeKey === '1') {
this.$refs.attributesTable.getCITypeGroupData()
}
if (activeKey === '3') {
if (activeKey === '5') {
this.$refs.triggerTable.getTableData()
}
})

View File

@ -91,7 +91,7 @@
show-search
>
<a-select-option
v-for="attr in commonAttributes.filter((attr) => !attr.is_password)"
v-for="attr in commonAttributes.filter((attr) => !attr.is_password && attr.value_type !== '6')"
:key="attr.id"
:value="attr.id"
>{{ attr.alias || attr.name }}</a-select-option