mirror of
https://github.com/veops/cmdb.git
synced 2025-09-06 05:17:03 +08:00
Compare commits
18 Commits
2.4.3
...
fix_issue#
Author | SHA1 | Date | |
---|---|---|---|
|
c96c4ffd54 | ||
|
ef25c94b5d | ||
|
06ae1bcf13 | ||
|
9ead4e7d8d | ||
|
994a28dd25 | ||
|
74b587e46c | ||
|
091cd882bd | ||
|
73093db467 | ||
|
66e268ce68 | ||
|
a41d1a5e97 | ||
|
b4b728fe28 | ||
|
d16462d8b7 | ||
|
de7d98c0b4 | ||
|
51332c7236 | ||
|
bf1076fe4a | ||
|
3454a98cfb | ||
|
506dcbb40e | ||
|
5ac4517187 |
@@ -263,10 +263,11 @@ class CIManager(object):
|
||||
ci_ids = None
|
||||
for attr_id in constraint.attr_ids:
|
||||
value_table = TableMap(attr_name=id2name[attr_id]).table
|
||||
|
||||
_ci_ids = set([i.ci_id for i in value_table.get_by(attr_id=attr_id,
|
||||
to_dict=False,
|
||||
value=ci_dict.get(id2name[attr_id]) or None)])
|
||||
values = value_table.get_by(attr_id=attr_id,
|
||||
value=ci_dict.get(id2name[attr_id]) or None,
|
||||
only_query=True).join(
|
||||
CI, CI.id == value_table.ci_id).filter(CI.type_id == type_id)
|
||||
_ci_ids = set([i.ci_id for i in values])
|
||||
if ci_ids is None:
|
||||
ci_ids = _ci_ids
|
||||
else:
|
||||
@@ -441,10 +442,13 @@ class CIManager(object):
|
||||
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
ci = self.confirm_ci_existed(ci_id)
|
||||
|
||||
raw_dict = copy.deepcopy(ci_dict)
|
||||
|
||||
attrs = CITypeAttributeManager.get_all_attributes(ci.type_id)
|
||||
ci_type_attrs_name = {attr.name: attr for _, attr in attrs}
|
||||
ci_type_attrs_alias2name = {attr.alias: attr.name for _, attr in attrs}
|
||||
ci_dict = {ci_type_attrs_alias2name[k] if k in ci_type_attrs_alias2name else k: v for k, v in ci_dict.items()}
|
||||
|
||||
raw_dict = copy.deepcopy(ci_dict)
|
||||
|
||||
ci_attr2type_attr = {type_attr.attr_id: type_attr for type_attr, _ in attrs}
|
||||
for _, attr in attrs:
|
||||
if attr.default and attr.default.get('default') == AttributeDefaultValueEnum.UPDATED_AT:
|
||||
@@ -825,6 +829,105 @@ class CIManager(object):
|
||||
|
||||
return data.get('v')
|
||||
|
||||
def baseline(self, ci_ids, before_date):
|
||||
ci_list = self.get_cis_by_ids(ci_ids, ret_key=RetKey.ALIAS)
|
||||
if not ci_list:
|
||||
return dict()
|
||||
|
||||
ci2changed = dict()
|
||||
changed = AttributeHistoryManger.get_records_for_attributes(
|
||||
before_date, None, None, 1, 100000, None, None, ci_ids=ci_ids, more=True)[1]
|
||||
for records in changed:
|
||||
for change in records[1]:
|
||||
if change['is_computed'] or change['is_password']:
|
||||
continue
|
||||
|
||||
if change.get('default') and change['default'].get('default') == AttributeDefaultValueEnum.UPDATED_AT:
|
||||
continue
|
||||
|
||||
ci2changed.setdefault(change['ci_id'], {})
|
||||
item = (change['old'],
|
||||
change['new'],
|
||||
change.get('is_list'),
|
||||
change.get('value_type'),
|
||||
change['operate_type'])
|
||||
if change.get('is_list'):
|
||||
ci2changed[change['ci_id']].setdefault(change.get('attr_alias'), []).append(item)
|
||||
else:
|
||||
ci2changed[change['ci_id']].update({change.get('attr_alias'): item})
|
||||
|
||||
type2show_name = {}
|
||||
result = []
|
||||
for ci in ci_list:
|
||||
list_attr2item = {}
|
||||
for alias_name, v in (ci2changed.get(ci['_id']) or {}).items():
|
||||
if not alias_name:
|
||||
continue
|
||||
if alias_name == ci.get('unique_alias'):
|
||||
continue
|
||||
|
||||
if ci.get('_type') not in type2show_name:
|
||||
ci_type = CITypeCache.get(ci.get('_type'))
|
||||
show_id = ci_type.show_id or ci_type.unique_id
|
||||
type2show_name[ci['_type']] = AttributeCache.get(show_id).alias
|
||||
|
||||
if isinstance(v, list):
|
||||
for old, new, is_list, value_type, operate_type in v:
|
||||
if alias_name not in list_attr2item:
|
||||
list_attr2item[alias_name] = dict(instance=ci.get(type2show_name[ci['_type']]),
|
||||
attr_name=alias_name,
|
||||
value_type=value_type,
|
||||
is_list=is_list,
|
||||
ci_type=ci.get('ci_type'),
|
||||
unique_alias=ci.get('unique_alias'),
|
||||
unique_value=ci.get(ci['unique_alias']),
|
||||
cur=copy.deepcopy(ci.get(alias_name)),
|
||||
to=ci.get(alias_name) or [])
|
||||
|
||||
old = ValueTypeMap.deserialize[value_type](old) if old else old
|
||||
new = ValueTypeMap.deserialize[value_type](new) if new else new
|
||||
if operate_type == OperateType.ADD:
|
||||
list_attr2item[alias_name]['to'].remove(new)
|
||||
elif operate_type == OperateType.DELETE and old not in list_attr2item[alias_name]['to']:
|
||||
list_attr2item[alias_name]['to'].append(old)
|
||||
continue
|
||||
|
||||
old, value_type = v[0], v[3]
|
||||
old = ValueTypeMap.deserialize[value_type](old) if old else old
|
||||
if isinstance(old, (datetime.datetime, datetime.date)):
|
||||
old = str(old)
|
||||
if ci.get(alias_name) != old:
|
||||
item = dict(instance=ci.get(type2show_name[ci['_type']]),
|
||||
attr_name=alias_name,
|
||||
value_type=value_type,
|
||||
ci_type=ci.get('ci_type'),
|
||||
unique_alias=ci.get('unique_alias'),
|
||||
unique_value=ci.get(ci['unique_alias']),
|
||||
cur=ci.get(alias_name),
|
||||
to=old)
|
||||
result.append(item)
|
||||
|
||||
for alias_name, item in list_attr2item.items():
|
||||
if sorted(item['cur'] or []) != sorted(item['to'] or []):
|
||||
result.append(item)
|
||||
|
||||
return result
|
||||
|
||||
def rollback(self, ci_id, before_date):
|
||||
baseline_ci = self.baseline([ci_id], before_date)
|
||||
|
||||
payload = dict()
|
||||
for item in baseline_ci:
|
||||
payload[item.get('attr_name')] = item.get('to')
|
||||
|
||||
if payload:
|
||||
payload['ci_type'] = baseline_ci[0]['ci_type']
|
||||
payload[baseline_ci[0]['unique_alias']] = baseline_ci[0]['unique_value']
|
||||
|
||||
self.update(ci_id, **payload)
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
class CIRelationManager(object):
|
||||
"""
|
||||
|
@@ -253,6 +253,13 @@ class CITypeManager(object):
|
||||
for item in CITypeInheritance.get_by(child_id=type_id, to_dict=False):
|
||||
item.delete(commit=False)
|
||||
|
||||
try:
|
||||
from api.models.cmdb import CITypeReconciliation
|
||||
for item in CITypeReconciliation.get_by(type_id=type_id, to_dict=False):
|
||||
item.delete(commit=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
db.session.commit()
|
||||
|
||||
ci_type.soft_delete()
|
||||
@@ -414,9 +421,6 @@ class CITypeGroupManager(object):
|
||||
existed = CITypeGroup.get_by_id(gid) or abort(
|
||||
404, ErrFormat.ci_type_group_not_found.format("id={}".format(gid)))
|
||||
if name is not None and name != existed.name:
|
||||
if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"):
|
||||
return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG))
|
||||
|
||||
existed.update(name=name)
|
||||
|
||||
max_order = max([i.order or 0 for i in CITypeGroupItem.get_by(group_id=gid, to_dict=False)] or [0])
|
||||
|
@@ -55,6 +55,9 @@ class CITypeOperateType(BaseEnum):
|
||||
DELETE_UNIQUE_CONSTRAINT = "11" # 删除联合唯一
|
||||
ADD_RELATION = "12" # 新增关系
|
||||
DELETE_RELATION = "13" # 删除关系
|
||||
ADD_RECONCILIATION = "14" # 删除关系
|
||||
UPDATE_RECONCILIATION = "15" # 删除关系
|
||||
DELETE_RECONCILIATION = "16" # 删除关系
|
||||
|
||||
|
||||
class RetKey(BaseEnum):
|
||||
@@ -98,6 +101,12 @@ class AttributeDefaultValueEnum(BaseEnum):
|
||||
AUTO_INC_ID = "$auto_inc_id"
|
||||
|
||||
|
||||
class ExecuteStatusEnum(BaseEnum):
|
||||
COMPLETED = '0'
|
||||
FAILED = '1'
|
||||
RUNNING = '2'
|
||||
|
||||
|
||||
CMDB_QUEUE = "one_cmdb_async"
|
||||
REDIS_PREFIX_CI = "ONE_CMDB"
|
||||
REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION"
|
||||
|
@@ -26,7 +26,7 @@ from api.models.cmdb import OperationRecord
|
||||
class AttributeHistoryManger(object):
|
||||
@staticmethod
|
||||
def get_records_for_attributes(start, end, username, page, page_size, operate_type, type_id,
|
||||
ci_id=None, attr_id=None):
|
||||
ci_id=None, attr_id=None, ci_ids=None, more=False):
|
||||
|
||||
records = db.session.query(OperationRecord, AttributeHistory).join(
|
||||
AttributeHistory, OperationRecord.id == AttributeHistory.record_id)
|
||||
@@ -48,6 +48,9 @@ class AttributeHistoryManger(object):
|
||||
if ci_id is not None:
|
||||
records = records.filter(AttributeHistory.ci_id == ci_id)
|
||||
|
||||
if ci_ids and isinstance(ci_ids, list):
|
||||
records = records.filter(AttributeHistory.ci_id.in_(ci_ids))
|
||||
|
||||
if attr_id is not None:
|
||||
records = records.filter(AttributeHistory.attr_id == attr_id)
|
||||
|
||||
@@ -62,6 +65,12 @@ class AttributeHistoryManger(object):
|
||||
if attr_hist['attr']:
|
||||
attr_hist['attr_name'] = attr_hist['attr'].name
|
||||
attr_hist['attr_alias'] = attr_hist['attr'].alias
|
||||
if more:
|
||||
attr_hist['is_list'] = attr_hist['attr'].is_list
|
||||
attr_hist['is_computed'] = attr_hist['attr'].is_computed
|
||||
attr_hist['is_password'] = attr_hist['attr'].is_password
|
||||
attr_hist['default'] = attr_hist['attr'].default
|
||||
attr_hist['value_type'] = attr_hist['attr'].value_type
|
||||
attr_hist.pop("attr")
|
||||
|
||||
if record_id not in res:
|
||||
@@ -161,6 +170,7 @@ class AttributeHistoryManger(object):
|
||||
record = i.OperationRecord
|
||||
item = dict(attr_name=attr.name,
|
||||
attr_alias=attr.alias,
|
||||
value_type=attr.value_type,
|
||||
operate_type=hist.operate_type,
|
||||
username=user and user.nickname,
|
||||
old=hist.old,
|
||||
@@ -271,7 +281,7 @@ class CITypeHistoryManager(object):
|
||||
return numfound, result
|
||||
|
||||
@staticmethod
|
||||
def add(operate_type, type_id, attr_id=None, trigger_id=None, unique_constraint_id=None, change=None):
|
||||
def add(operate_type, type_id, attr_id=None, trigger_id=None, unique_constraint_id=None, change=None, rc_id=None):
|
||||
if type_id is None and attr_id is not None:
|
||||
from api.models.cmdb import CITypeAttribute
|
||||
type_ids = [i.type_id for i in CITypeAttribute.get_by(attr_id=attr_id, to_dict=False)]
|
||||
@@ -284,6 +294,7 @@ class CITypeHistoryManager(object):
|
||||
uid=current_user.uid,
|
||||
attr_id=attr_id,
|
||||
trigger_id=trigger_id,
|
||||
rc_id=rc_id,
|
||||
unique_constraint_id=unique_constraint_id,
|
||||
change=change)
|
||||
|
||||
|
@@ -78,6 +78,8 @@ class ErrFormat(CommonErrFormat):
|
||||
unique_constraint_invalid = _l("Uniquely constrained attributes cannot be JSON and multi-valued")
|
||||
ci_type_trigger_duplicate = _l("Duplicated trigger") # 重复的触发器
|
||||
ci_type_trigger_not_found = _l("Trigger {} does not exist") # 触发器 {} 不存在
|
||||
ci_type_reconciliation_duplicate = _l("Duplicated reconciliation rule") # 重复的校验规则
|
||||
ci_type_reconciliation_not_found = _l("Reconciliation rule {} does not exist") # 规则 {} 不存在
|
||||
|
||||
record_not_found = _l("Operation record {} does not exist") # 操作记录 {} 不存在
|
||||
cannot_delete_unique = _l("Unique identifier cannot be deleted") # 不能删除唯一标识
|
||||
|
@@ -275,25 +275,36 @@ class AttributeValueManager(object):
|
||||
if attr.is_list:
|
||||
existed_attrs = value_table.get_by(attr_id=attr.id, ci_id=ci.id, to_dict=False)
|
||||
existed_values = [i.value for i in existed_attrs]
|
||||
added = set(value) - set(existed_values)
|
||||
deleted = set(existed_values) - set(value)
|
||||
for v in added:
|
||||
value_table.create(ci_id=ci.id, attr_id=attr.id, value=v, flush=False, commit=False)
|
||||
changed.append((ci.id, attr.id, OperateType.ADD, None, v, ci.type_id))
|
||||
|
||||
# Comparison array starts from which position changes
|
||||
min_len = min(len(value), len(existed_values))
|
||||
index = 0
|
||||
while index < min_len:
|
||||
if value[index] != existed_values[index]:
|
||||
break
|
||||
index += 1
|
||||
added = value[index:]
|
||||
deleted = existed_values[index:]
|
||||
|
||||
# Delete first and then add to ensure id sorting
|
||||
for v in deleted:
|
||||
existed_attr = existed_attrs[existed_values.index(v)]
|
||||
existed_attr.delete(flush=False, commit=False)
|
||||
changed.append((ci.id, attr.id, OperateType.DELETE, v, None, ci.type_id))
|
||||
for v in added:
|
||||
value_table.create(ci_id=ci.id, attr_id=attr.id, value=v, flush=False, commit=False)
|
||||
changed.append((ci.id, attr.id, OperateType.ADD, None, v, ci.type_id))
|
||||
else:
|
||||
existed_attr = value_table.get_by(attr_id=attr.id, ci_id=ci.id, first=True, to_dict=False)
|
||||
existed_value = existed_attr and existed_attr.value
|
||||
existed_value = (ValueTypeMap.serialize[attr.value_type](existed_value) if
|
||||
existed_value or existed_value == 0 else existed_value)
|
||||
if existed_value is None and value is not None:
|
||||
value_table.create(ci_id=ci.id, attr_id=attr.id, value=value, flush=False, commit=False)
|
||||
|
||||
changed.append((ci.id, attr.id, OperateType.ADD, None, value, ci.type_id))
|
||||
else:
|
||||
if existed_value != value:
|
||||
if existed_value != value and existed_attr:
|
||||
if value is None:
|
||||
existed_attr.delete(flush=False, commit=False)
|
||||
else:
|
||||
|
@@ -10,6 +10,11 @@ from api.lib.perm.acl.role import RoleCRUD, RoleRelationCRUD
|
||||
from api.lib.perm.acl.user import UserCRUD
|
||||
|
||||
|
||||
def validate_app(app_id):
|
||||
app = AppCache.get(app_id)
|
||||
return app.id if app else None
|
||||
|
||||
|
||||
class ACLManager(object):
|
||||
def __init__(self, app_name='acl', uid=None):
|
||||
self.log = current_app.logger
|
||||
@@ -133,7 +138,8 @@ class ACLManager(object):
|
||||
numfound, res = ResourceCRUD.search(q, u, self.validate_app().id, rt_id, page, page_size)
|
||||
return res
|
||||
|
||||
def grant_resource(self, rid, resource_id, perms):
|
||||
@staticmethod
|
||||
def grant_resource(rid, resource_id, perms):
|
||||
PermissionCRUD.grant(rid, perms, resource_id=resource_id, group_id=None)
|
||||
|
||||
@staticmethod
|
||||
@@ -141,3 +147,7 @@ class ACLManager(object):
|
||||
rt = AppCRUD.add(**payload)
|
||||
|
||||
return rt.to_dict()
|
||||
|
||||
def role_has_perms(self, rid, resource_name, resource_type_name, perm):
|
||||
app_id = validate_app(self.app_name)
|
||||
return RoleCRUD.has_permission(rid, resource_name, resource_type_name, app_id, perm)
|
||||
|
37
cmdb-api/api/lib/common_setting/decorator.py
Normal file
37
cmdb-api/api/lib/common_setting/decorator.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import functools
|
||||
|
||||
from flask import abort, session
|
||||
from api.lib.common_setting.acl import ACLManager
|
||||
from api.lib.common_setting.resp_format import ErrFormat
|
||||
|
||||
|
||||
def perms_role_required(app_name, resource_type_name, resource_name, perm, role_name=None):
|
||||
def decorator_perms_role_required(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_required(*args, **kwargs):
|
||||
acl = ACLManager(app_name)
|
||||
has_perms = False
|
||||
try:
|
||||
has_perms = acl.role_has_perms(session["acl"]['rid'], resource_name, resource_type_name, perm)
|
||||
except Exception as e:
|
||||
# resource_type not exist, continue check role
|
||||
if role_name:
|
||||
if role_name not in session.get("acl", {}).get("parentRoles", []):
|
||||
abort(403, ErrFormat.role_required.format(role_name))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
abort(403, ErrFormat.resource_no_permission.format(resource_name, perm))
|
||||
|
||||
if not has_perms:
|
||||
if role_name:
|
||||
if role_name not in session.get("acl", {}).get("parentRoles", []):
|
||||
abort(403, ErrFormat.role_required.format(role_name))
|
||||
else:
|
||||
abort(403, ErrFormat.resource_no_permission.format(resource_name, perm))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper_required
|
||||
|
||||
return decorator_perms_role_required
|
@@ -80,3 +80,5 @@ class ErrFormat(CommonErrFormat):
|
||||
ldap_test_username_required = _l("LDAP test username required") # LDAP测试用户名必填
|
||||
|
||||
company_wide = _l("Company wide") # 全公司
|
||||
|
||||
resource_no_permission = _l("No permission to access resource {}, perm {} ") # 没有权限访问 {} 资源的 {} 权限"
|
||||
|
59
cmdb-api/api/lib/common_setting/role_perm_base.py
Normal file
59
cmdb-api/api/lib/common_setting/role_perm_base.py
Normal file
@@ -0,0 +1,59 @@
|
||||
class OperationPermission(object):
|
||||
|
||||
def __init__(self, resource_perms):
|
||||
for _r in resource_perms:
|
||||
setattr(self, _r['page'], _r['page'])
|
||||
for _p in _r['perms']:
|
||||
setattr(self, _p, _p)
|
||||
|
||||
|
||||
class BaseApp(object):
|
||||
resource_type_name = 'OperationPermission'
|
||||
all_resource_perms = []
|
||||
|
||||
def __init__(self):
|
||||
self.admin_name = None
|
||||
self.roles = []
|
||||
self.app_name = 'acl'
|
||||
self.require_create_resource_type = self.resource_type_name
|
||||
self.extra_create_resource_type_list = []
|
||||
|
||||
self.op = None
|
||||
|
||||
@staticmethod
|
||||
def format_role(role_name, role_type, acl_rid, resource_perms, description=''):
|
||||
return dict(
|
||||
role_name=role_name,
|
||||
role_type=role_type,
|
||||
acl_rid=acl_rid,
|
||||
description=description,
|
||||
resource_perms=resource_perms,
|
||||
)
|
||||
|
||||
|
||||
class CMDBApp(BaseApp):
|
||||
all_resource_perms = [
|
||||
{"page": "Big_Screen", "page_cn": "大屏", "perms": ["read"]},
|
||||
{"page": "Dashboard", "page_cn": "仪表盘", "perms": ["read"]},
|
||||
{"page": "Resource_Search", "page_cn": "资源搜索", "perms": ["read"]},
|
||||
{"page": "Auto_Discovery_Pool", "page_cn": "自动发现池", "perms": ["read"]},
|
||||
{"page": "My_Subscriptions", "page_cn": "我的订阅", "perms": ["read"]},
|
||||
{"page": "Bulk_Import", "page_cn": "批量导入", "perms": ["read"]},
|
||||
{"page": "Model_Configuration", "page_cn": "模型配置",
|
||||
"perms": ["read", "create_CIType", "create_CIType_group", "update_CIType_group",
|
||||
"delete_CIType_group", "download_CIType"]},
|
||||
{"page": "Backend_Management", "page_cn": "后台管理", "perms": ["read"]},
|
||||
{"page": "Customized_Dashboard", "page_cn": "定制仪表盘", "perms": ["read"]},
|
||||
{"page": "Service_Tree_Definition", "page_cn": "服务树定义", "perms": ["read"]},
|
||||
{"page": "Model_Relationships", "page_cn": "模型关系", "perms": ["read"]},
|
||||
{"page": "Operation_Audit", "page_cn": "操作审计", "perms": ["read"]},
|
||||
{"page": "Relationship_Types", "page_cn": "关系类型", "perms": ["read"]},
|
||||
{"page": "Auto_Discovery", "page_cn": "自动发现", "perms": ["read"]}]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.admin_name = 'cmdb_admin'
|
||||
self.app_name = 'cmdb'
|
||||
|
||||
self.op = OperationPermission(self.all_resource_perms)
|
@@ -224,16 +224,22 @@ class RoleRelationCache(object):
|
||||
@classmethod
|
||||
@flush_db
|
||||
def rebuild(cls, rid, app_id):
|
||||
cls.clean(rid, app_id)
|
||||
|
||||
cls.get_parent_ids(rid, app_id)
|
||||
cls.get_child_ids(rid, app_id)
|
||||
resources = cls.get_resources(rid, app_id)
|
||||
if resources.get('id2perms') or resources.get('group2perms'):
|
||||
HasResourceRoleCache.add(rid, app_id)
|
||||
if app_id is None:
|
||||
app_ids = [None] + [i.id for i in App.get_by(to_dict=False)]
|
||||
else:
|
||||
HasResourceRoleCache.remove(rid, app_id)
|
||||
cls.get_resources2(rid, app_id)
|
||||
app_ids = [app_id]
|
||||
|
||||
for _app_id in app_ids:
|
||||
cls.clean(rid, _app_id)
|
||||
|
||||
cls.get_parent_ids(rid, _app_id)
|
||||
cls.get_child_ids(rid, _app_id)
|
||||
resources = cls.get_resources(rid, _app_id)
|
||||
if resources.get('id2perms') or resources.get('group2perms'):
|
||||
HasResourceRoleCache.add(rid, _app_id)
|
||||
else:
|
||||
HasResourceRoleCache.remove(rid, _app_id)
|
||||
cls.get_resources2(rid, _app_id)
|
||||
|
||||
@classmethod
|
||||
@flush_db
|
||||
|
@@ -274,12 +274,14 @@ class PermissionCRUD(object):
|
||||
perm2resource.setdefault(_perm, []).append(resource_id)
|
||||
for _perm in perm2resource:
|
||||
perm = PermissionCache.get(_perm, resource_type_id)
|
||||
existeds = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
__func_in___key_resource_id=perm2resource[_perm],
|
||||
to_dict=False)
|
||||
for existed in existeds:
|
||||
if perm is None:
|
||||
continue
|
||||
exists = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
__func_in___key_resource_id=perm2resource[_perm],
|
||||
to_dict=False)
|
||||
for existed in exists:
|
||||
existed.deleted = True
|
||||
existed.deleted_at = datetime.datetime.now()
|
||||
db.session.add(existed)
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
|
||||
from api.extensions import db
|
||||
from api.lib.perm.acl.audit import AuditCRUD
|
||||
@@ -127,11 +126,18 @@ class ResourceTypeCRUD(object):
|
||||
existed_ids = [i.id for i in existed]
|
||||
current_ids = []
|
||||
|
||||
rebuild_rids = set()
|
||||
for i in existed:
|
||||
if i.name not in perms:
|
||||
i.soft_delete()
|
||||
i.soft_delete(commit=False)
|
||||
for rp in RolePermission.get_by(perm_id=i.id, to_dict=False):
|
||||
rp.soft_delete(commit=False)
|
||||
rebuild_rids.add((rp.app_id, rp.rid))
|
||||
else:
|
||||
current_ids.append(i.id)
|
||||
db.session.commit()
|
||||
for _app_id, _rid in rebuild_rids:
|
||||
role_rebuild.apply_async(args=(_rid, _app_id), queue=ACL_QUEUE)
|
||||
|
||||
for i in perms:
|
||||
if i not in existed_names:
|
||||
|
@@ -3,12 +3,14 @@
|
||||
|
||||
import time
|
||||
|
||||
import redis_lock
|
||||
import six
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from sqlalchemy import or_
|
||||
|
||||
from api.extensions import db
|
||||
from api.extensions import rd
|
||||
from api.lib.perm.acl.app import AppCRUD
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditScope
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
@@ -62,7 +64,9 @@ class RoleRelationCRUD(object):
|
||||
|
||||
id2parents = {}
|
||||
for i in res:
|
||||
id2parents.setdefault(rid2uid.get(i.child_id, i.child_id), []).append(RoleCache.get(i.parent_id).to_dict())
|
||||
parent = RoleCache.get(i.parent_id)
|
||||
if parent:
|
||||
id2parents.setdefault(rid2uid.get(i.child_id, i.child_id), []).append(parent.to_dict())
|
||||
|
||||
return id2parents
|
||||
|
||||
@@ -141,24 +145,27 @@ class RoleRelationCRUD(object):
|
||||
|
||||
@classmethod
|
||||
def add(cls, role, parent_id, child_ids, app_id):
|
||||
result = []
|
||||
for child_id in child_ids:
|
||||
existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, app_id=app_id)
|
||||
if existed:
|
||||
continue
|
||||
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD"):
|
||||
db.session.commit()
|
||||
|
||||
RoleRelationCache.clean(parent_id, app_id)
|
||||
RoleRelationCache.clean(child_id, app_id)
|
||||
result = []
|
||||
for child_id in child_ids:
|
||||
existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, app_id=app_id)
|
||||
if existed:
|
||||
continue
|
||||
|
||||
if parent_id in cls.recursive_child_ids(child_id, app_id):
|
||||
return abort(400, ErrFormat.inheritance_dead_loop)
|
||||
RoleRelationCache.clean(parent_id, app_id)
|
||||
RoleRelationCache.clean(child_id, app_id)
|
||||
|
||||
if app_id is None:
|
||||
for app in AppCRUD.get_all():
|
||||
if app.name != "acl":
|
||||
RoleRelationCache.clean(child_id, app.id)
|
||||
if parent_id in cls.recursive_child_ids(child_id, app_id):
|
||||
return abort(400, ErrFormat.inheritance_dead_loop)
|
||||
|
||||
result.append(RoleRelation.create(parent_id=parent_id, child_id=child_id, app_id=app_id).to_dict())
|
||||
if app_id is None:
|
||||
for app in AppCRUD.get_all():
|
||||
if app.name != "acl":
|
||||
RoleRelationCache.clean(child_id, app.id)
|
||||
|
||||
result.append(RoleRelation.create(parent_id=parent_id, child_id=child_id, app_id=app_id).to_dict())
|
||||
|
||||
AuditCRUD.add_role_log(app_id, AuditOperateType.role_relation_add,
|
||||
AuditScope.role_relation, role.id, {}, {},
|
||||
|
@@ -432,6 +432,7 @@ class CITypeHistory(Model):
|
||||
|
||||
attr_id = db.Column(db.Integer)
|
||||
trigger_id = db.Column(db.Integer)
|
||||
rc_id = db.Column(db.Integer)
|
||||
unique_constraint_id = db.Column(db.Integer)
|
||||
|
||||
uid = db.Column(db.Integer, index=True)
|
||||
|
@@ -3,12 +3,13 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from celery_once import QueueOnce
|
||||
import redis_lock
|
||||
from flask import current_app
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from api.extensions import celery
|
||||
from api.extensions import rd
|
||||
from api.lib.decorator import flush_db
|
||||
from api.lib.decorator import reconnect_db
|
||||
from api.lib.perm.acl.audit import AuditCRUD
|
||||
@@ -25,14 +26,14 @@ from api.models.acl import Role
|
||||
from api.models.acl import Trigger
|
||||
|
||||
|
||||
@celery.task(name="acl.role_rebuild",
|
||||
queue=ACL_QUEUE,)
|
||||
@celery.task(name="acl.role_rebuild", queue=ACL_QUEUE, )
|
||||
@flush_db
|
||||
@reconnect_db
|
||||
def role_rebuild(rids, app_id):
|
||||
rids = rids if isinstance(rids, list) else [rids]
|
||||
for rid in rids:
|
||||
RoleRelationCache.rebuild(rid, app_id)
|
||||
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id)):
|
||||
RoleRelationCache.rebuild(rid, app_id)
|
||||
|
||||
current_app.logger.info("Role {0} App {1} rebuild..........".format(rids, app_id))
|
||||
|
||||
|
@@ -16,6 +16,7 @@ from api.lib.cmdb.const import RetKey
|
||||
from api.lib.cmdb.perms import has_perm_for_ci
|
||||
from api.lib.cmdb.search import SearchError
|
||||
from api.lib.cmdb.search.ci import search
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
@@ -254,3 +255,23 @@ class CIPasswordView(APIView):
|
||||
|
||||
def post(self, ci_id, attr_id):
|
||||
return self.get(ci_id, attr_id)
|
||||
|
||||
|
||||
class CIBaselineView(APIView):
|
||||
url_prefix = ("/ci/baseline", "/ci/<int:ci_id>/baseline/rollback")
|
||||
|
||||
@args_required("before_date")
|
||||
def get(self):
|
||||
ci_ids = handle_arg_list(request.values.get('ci_ids'))
|
||||
before_date = request.values.get('before_date')
|
||||
|
||||
return self.jsonify(CIManager().baseline(list(map(int, ci_ids)), before_date))
|
||||
|
||||
@args_required("before_date")
|
||||
def post(self, ci_id):
|
||||
if 'rollback' in request.url:
|
||||
before_date = request.values.get('before_date')
|
||||
|
||||
return self.jsonify(**CIManager().rollback(ci_id, before_date))
|
||||
|
||||
return self.get(ci_id)
|
||||
|
@@ -7,7 +7,6 @@ from io import BytesIO
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
from flask import session
|
||||
|
||||
from api.lib.cmdb.cache import AttributeCache
|
||||
from api.lib.cmdb.cache import CITypeCache
|
||||
@@ -19,10 +18,12 @@ from api.lib.cmdb.ci_type import CITypeManager
|
||||
from api.lib.cmdb.ci_type import CITypeTemplateManager
|
||||
from api.lib.cmdb.ci_type import CITypeTriggerManager
|
||||
from api.lib.cmdb.ci_type import CITypeUniqueConstraintManager
|
||||
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
|
||||
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum
|
||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
||||
from api.lib.cmdb.preference import PreferenceManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.common_setting.decorator import perms_role_required
|
||||
from api.lib.common_setting.role_perm_base import CMDBApp
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
@@ -36,6 +37,8 @@ from api.lib.perm.auth import auth_with_app_token
|
||||
from api.lib.utils import handle_arg_list
|
||||
from api.resource import APIView
|
||||
|
||||
app_cli = CMDBApp()
|
||||
|
||||
|
||||
class CITypeView(APIView):
|
||||
url_prefix = ("/ci_types", "/ci_types/<int:type_id>", "/ci_types/<string:type_name>",
|
||||
@@ -116,7 +119,6 @@ class CITypeInheritanceView(APIView):
|
||||
class CITypeGroupView(APIView):
|
||||
url_prefix = ("/ci_types/groups",
|
||||
"/ci_types/groups/config",
|
||||
"/ci_types/groups/order",
|
||||
"/ci_types/groups/<int:gid>")
|
||||
|
||||
def get(self):
|
||||
@@ -125,7 +127,8 @@ class CITypeGroupView(APIView):
|
||||
|
||||
return self.jsonify(CITypeGroupManager.get(need_other, config_required))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.create_CIType_group, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
@args_validate(CITypeGroupManager.cls)
|
||||
def post(self):
|
||||
@@ -136,15 +139,6 @@ class CITypeGroupView(APIView):
|
||||
|
||||
@args_validate(CITypeGroupManager.cls)
|
||||
def put(self, gid=None):
|
||||
if "/order" in request.url:
|
||||
if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"):
|
||||
return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG))
|
||||
|
||||
group_ids = request.values.get('group_ids')
|
||||
CITypeGroupManager.order(group_ids)
|
||||
|
||||
return self.jsonify(group_ids=group_ids)
|
||||
|
||||
name = request.values.get('name') or abort(400, ErrFormat.argument_value_required.format("name"))
|
||||
type_ids = request.values.get('type_ids')
|
||||
|
||||
@@ -152,7 +146,8 @@ class CITypeGroupView(APIView):
|
||||
|
||||
return self.jsonify(gid=gid)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.delete_CIType_group, app_cli.admin_name)
|
||||
def delete(self, gid):
|
||||
type_ids = request.values.get("type_ids")
|
||||
CITypeGroupManager.delete(gid, type_ids)
|
||||
@@ -160,6 +155,18 @@ class CITypeGroupView(APIView):
|
||||
return self.jsonify(gid=gid)
|
||||
|
||||
|
||||
class CITypeGroupOrderView(APIView):
|
||||
url_prefix = "/ci_types/groups/order"
|
||||
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.update_CIType_group, app_cli.admin_name)
|
||||
def put(self):
|
||||
group_ids = request.values.get('group_ids')
|
||||
CITypeGroupManager.order(group_ids)
|
||||
|
||||
return self.jsonify(group_ids=group_ids)
|
||||
|
||||
|
||||
class CITypeQueryView(APIView):
|
||||
url_prefix = "/ci_types/query"
|
||||
|
||||
@@ -352,14 +359,16 @@ class CITypeAttributeGroupView(APIView):
|
||||
class CITypeTemplateView(APIView):
|
||||
url_prefix = ("/ci_types/template/import", "/ci_types/template/export", "/ci_types/<int:type_id>/template/export")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.download_CIType, app_cli.admin_name)
|
||||
def get(self, type_id=None): # export
|
||||
if type_id is not None:
|
||||
return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template_by_type(type_id)))
|
||||
|
||||
return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template()))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.download_CIType, app_cli.admin_name)
|
||||
def post(self): # import
|
||||
tpt = request.values.get('ci_type_template') or {}
|
||||
|
||||
@@ -379,7 +388,8 @@ class CITypeCanDefineComputed(APIView):
|
||||
class CITypeTemplateFileView(APIView):
|
||||
url_prefix = ("/ci_types/template/import/file", "/ci_types/template/export/file")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.download_CIType, app_cli.admin_name)
|
||||
def get(self): # export
|
||||
tpt_json = CITypeTemplateManager.export_template()
|
||||
tpt_json = dict(ci_type_template=tpt_json)
|
||||
@@ -394,7 +404,8 @@ class CITypeTemplateFileView(APIView):
|
||||
mimetype='application/json',
|
||||
max_age=0)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.download_CIType, app_cli.admin_name)
|
||||
def post(self): # import
|
||||
f = request.files.get('file')
|
||||
|
||||
|
@@ -11,6 +11,8 @@ from api.lib.cmdb.const import ResourceTypeEnum
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.preference import PreferenceManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.common_setting.decorator import perms_role_required
|
||||
from api.lib.common_setting.role_perm_base import CMDBApp
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
@@ -18,6 +20,8 @@ from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.resource import APIView
|
||||
|
||||
app_cli = CMDBApp()
|
||||
|
||||
|
||||
class GetChildrenView(APIView):
|
||||
url_prefix = ("/ci_type_relations/<int:parent_id>/children",
|
||||
@@ -41,7 +45,8 @@ class GetParentsView(APIView):
|
||||
class CITypeRelationView(APIView):
|
||||
url_prefix = ("/ci_type_relations", "/ci_type_relations/<int:parent_id>/<int:child_id>")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def get(self):
|
||||
res, type2attributes = CITypeRelationManager.get()
|
||||
|
||||
@@ -69,7 +74,8 @@ class CITypeRelationView(APIView):
|
||||
class CITypeRelationDelete2View(APIView):
|
||||
url_prefix = "/ci_type_relations/<int:ctr_id>"
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Relationships,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def delete(self, ctr_id):
|
||||
CITypeRelationManager.delete(ctr_id)
|
||||
|
||||
|
@@ -3,14 +3,16 @@
|
||||
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.custom_dashboard import CustomDashboardManager
|
||||
from api.lib.cmdb.custom_dashboard import SystemConfigManager
|
||||
from api.lib.common_setting.decorator import perms_role_required
|
||||
from api.lib.common_setting.role_perm_base import CMDBApp
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.resource import APIView
|
||||
|
||||
app_cli = CMDBApp()
|
||||
|
||||
|
||||
class CustomDashboardApiView(APIView):
|
||||
url_prefix = ("/custom_dashboard", "/custom_dashboard/<int:_id>", "/custom_dashboard/batch",
|
||||
@@ -19,7 +21,8 @@ class CustomDashboardApiView(APIView):
|
||||
def get(self):
|
||||
return self.jsonify(CustomDashboardManager.get())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Customized_Dashboard,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_validate(CustomDashboardManager.cls)
|
||||
def post(self):
|
||||
if request.url.endswith("/preview"):
|
||||
@@ -32,7 +35,8 @@ class CustomDashboardApiView(APIView):
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Customized_Dashboard,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_validate(CustomDashboardManager.cls)
|
||||
def put(self, _id=None):
|
||||
if _id is not None:
|
||||
@@ -47,7 +51,8 @@ class CustomDashboardApiView(APIView):
|
||||
|
||||
return self.jsonify(id2options=request.values.get('id2options'))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Customized_Dashboard,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def delete(self, _id):
|
||||
CustomDashboardManager.delete(_id)
|
||||
|
||||
@@ -57,12 +62,14 @@ class CustomDashboardApiView(APIView):
|
||||
class SystemConfigApiView(APIView):
|
||||
url_prefix = ("/system_config",)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name", value_required=True)
|
||||
def get(self):
|
||||
return self.jsonify(SystemConfigManager.get(request.values['name']))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_validate(SystemConfigManager.cls)
|
||||
@args_required("name", value_required=True)
|
||||
@args_required("option", value_required=True)
|
||||
@@ -74,7 +81,8 @@ class SystemConfigApiView(APIView):
|
||||
def put(self, _id=None):
|
||||
return self.post()
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
def delete(self):
|
||||
CustomDashboardManager.delete(request.values['name'])
|
||||
|
@@ -5,28 +5,29 @@ import datetime
|
||||
|
||||
from flask import abort
|
||||
from flask import request
|
||||
from flask import session
|
||||
|
||||
from api.lib.cmdb.ci import CIManager
|
||||
from api.lib.cmdb.const import PermEnum
|
||||
from api.lib.cmdb.const import ResourceTypeEnum
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.history import AttributeHistoryManger
|
||||
from api.lib.cmdb.history import CITriggerHistoryManager
|
||||
from api.lib.cmdb.history import CITypeHistoryManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.common_setting.decorator import perms_role_required
|
||||
from api.lib.common_setting.role_perm_base import CMDBApp
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.resource import APIView
|
||||
|
||||
app_cli = CMDBApp()
|
||||
|
||||
|
||||
class RecordView(APIView):
|
||||
url_prefix = ("/history/records/attribute", "/history/records/relation")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Operation_Audit,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def get(self):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
@@ -80,18 +81,21 @@ class CIHistoryView(APIView):
|
||||
|
||||
|
||||
class CITriggerHistoryView(APIView):
|
||||
url_prefix = ("/history/ci_triggers/<int:ci_id>", "/history/ci_triggers")
|
||||
url_prefix = ("/history/ci_triggers/<int:ci_id>",)
|
||||
|
||||
@has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.READ, CIManager.get_type_name)
|
||||
def get(self, ci_id=None):
|
||||
if ci_id is not None:
|
||||
result = CITriggerHistoryManager.get_by_ci_id(ci_id)
|
||||
def get(self, ci_id):
|
||||
result = CITriggerHistoryManager.get_by_ci_id(ci_id)
|
||||
|
||||
return self.jsonify(result)
|
||||
return self.jsonify(result)
|
||||
|
||||
if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"):
|
||||
return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG))
|
||||
|
||||
class CIsTriggerHistoryView(APIView):
|
||||
url_prefix = ("/history/ci_triggers",)
|
||||
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Operation_Audit,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def get(self):
|
||||
type_id = request.values.get("type_id")
|
||||
trigger_id = request.values.get("trigger_id")
|
||||
operate_type = request.values.get("operate_type")
|
||||
@@ -115,7 +119,8 @@ class CITriggerHistoryView(APIView):
|
||||
class CITypeHistoryView(APIView):
|
||||
url_prefix = "/history/ci_types"
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Operation_Audit,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def get(self):
|
||||
type_id = request.values.get("type_id")
|
||||
username = request.values.get("username")
|
||||
|
@@ -8,20 +8,22 @@ from flask import request
|
||||
from api.lib.cmdb.ci_type import CITypeManager
|
||||
from api.lib.cmdb.const import PermEnum
|
||||
from api.lib.cmdb.const import ResourceTypeEnum
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
||||
from api.lib.cmdb.preference import PreferenceManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.common_setting.decorator import perms_role_required
|
||||
from api.lib.common_setting.role_perm_base import CMDBApp
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.perm.acl.acl import validate_permission
|
||||
from api.lib.utils import handle_arg_list
|
||||
from api.resource import APIView
|
||||
|
||||
app_cli = CMDBApp()
|
||||
|
||||
|
||||
class PreferenceShowCITypesView(APIView):
|
||||
url_prefix = ("/preference/ci_types", "/preference/ci_types2")
|
||||
@@ -104,7 +106,8 @@ class PreferenceRelationApiView(APIView):
|
||||
|
||||
return self.jsonify(views=views, id2type=id2type, name2id=name2id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
@args_required("cr_ids")
|
||||
@args_validate(PreferenceManager.pref_rel_cls)
|
||||
@@ -118,14 +121,16 @@ class PreferenceRelationApiView(APIView):
|
||||
|
||||
return self.jsonify(views=views, id2type=id2type, name2id=name2id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
def put(self, _id):
|
||||
views, id2type, name2id = PreferenceManager.create_or_update_relation_view(_id=_id, **request.values)
|
||||
|
||||
return self.jsonify(views=views, id2type=id2type, name2id=name2id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
def delete(self):
|
||||
name = request.values.get("name")
|
||||
|
@@ -4,14 +4,16 @@
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.relation_type import RelationTypeManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.common_setting.decorator import perms_role_required
|
||||
from api.lib.common_setting.role_perm_base import CMDBApp
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.resource import APIView
|
||||
|
||||
app_cli = CMDBApp()
|
||||
|
||||
|
||||
class RelationTypeView(APIView):
|
||||
url_prefix = ("/relation_types", "/relation_types/<int:rel_id>")
|
||||
@@ -19,7 +21,8 @@ class RelationTypeView(APIView):
|
||||
def get(self):
|
||||
return self.jsonify([i.to_dict() for i in RelationTypeManager.get_all()])
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Relationship_Types,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
@args_validate(RelationTypeManager.cls)
|
||||
def post(self):
|
||||
@@ -28,7 +31,8 @@ class RelationTypeView(APIView):
|
||||
|
||||
return self.jsonify(rel.to_dict())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Relationship_Types,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
@args_required("name")
|
||||
@args_validate(RelationTypeManager.cls)
|
||||
def put(self, rel_id):
|
||||
@@ -37,7 +41,8 @@ class RelationTypeView(APIView):
|
||||
|
||||
return self.jsonify(rel.to_dict())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Relationship_Types,
|
||||
app_cli.op.read, app_cli.admin_name)
|
||||
def delete(self, rel_id):
|
||||
RelationTypeManager.delete(rel_id)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3857903 */
|
||||
src: url('iconfont.woff2?t=1713335725699') format('woff2'),
|
||||
url('iconfont.woff?t=1713335725699') format('woff'),
|
||||
url('iconfont.ttf?t=1713335725699') format('truetype');
|
||||
src: url('iconfont.woff2?t=1713840593232') format('woff2'),
|
||||
url('iconfont.woff?t=1713840593232') format('woff'),
|
||||
url('iconfont.ttf?t=1713840593232') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,30 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.ops-setting-application-selected:before {
|
||||
content: "\e919";
|
||||
}
|
||||
|
||||
.ops-setting-application:before {
|
||||
content: "\e918";
|
||||
}
|
||||
|
||||
.ops-setting-basic:before {
|
||||
content: "\e889";
|
||||
}
|
||||
|
||||
.ops-setting-basic-selected:before {
|
||||
content: "\e917";
|
||||
}
|
||||
|
||||
.ops-setting-security:before {
|
||||
content: "\e915";
|
||||
}
|
||||
|
||||
.ops-setting-theme:before {
|
||||
content: "\e916";
|
||||
}
|
||||
|
||||
.veops-show:before {
|
||||
content: "\e914";
|
||||
}
|
||||
@@ -21,7 +45,7 @@
|
||||
content: "\e913";
|
||||
}
|
||||
|
||||
.a-itsm-workload1:before {
|
||||
.itsm-workload:before {
|
||||
content: "\e912";
|
||||
}
|
||||
|
||||
@@ -269,10 +293,6 @@
|
||||
content: "\e8d5";
|
||||
}
|
||||
|
||||
.ops-setting-auth-selected:before {
|
||||
content: "\e8d4";
|
||||
}
|
||||
|
||||
.itsm-knowledge2:before {
|
||||
content: "\e8d2";
|
||||
}
|
||||
@@ -501,10 +521,6 @@
|
||||
content: "\e89c";
|
||||
}
|
||||
|
||||
.ops-setting-duty-selected:before {
|
||||
content: "\e89b";
|
||||
}
|
||||
|
||||
.datainsight-sequential:before {
|
||||
content: "\e899";
|
||||
}
|
||||
@@ -669,38 +685,6 @@
|
||||
content: "\e870";
|
||||
}
|
||||
|
||||
.ops-itsm-ticketsetting-selected:before {
|
||||
content: "\e860";
|
||||
}
|
||||
|
||||
.ops-itsm-reports-selected:before {
|
||||
content: "\e861";
|
||||
}
|
||||
|
||||
.ops-itsm-servicecatalog-selected:before {
|
||||
content: "\e862";
|
||||
}
|
||||
|
||||
.ops-itsm-ticketmanage-selected:before {
|
||||
content: "\e863";
|
||||
}
|
||||
|
||||
.ops-itsm-knowledge-selected:before {
|
||||
content: "\e864";
|
||||
}
|
||||
|
||||
.ops-itsm-workstation-selected:before {
|
||||
content: "\e865";
|
||||
}
|
||||
|
||||
.ops-itsm-servicedesk-selected:before {
|
||||
content: "\e866";
|
||||
}
|
||||
|
||||
.ops-itsm-planticket-selected:before {
|
||||
content: "\e867";
|
||||
}
|
||||
|
||||
.ops-itsm-servicecatalog:before {
|
||||
content: "\e868";
|
||||
}
|
||||
@@ -1073,26 +1057,10 @@
|
||||
content: "\e816";
|
||||
}
|
||||
|
||||
.ops-cmdb-batch-selected:before {
|
||||
content: "\e803";
|
||||
}
|
||||
|
||||
.ops-cmdb-batch:before {
|
||||
content: "\e80a";
|
||||
}
|
||||
|
||||
.ops-cmdb-adc-selected:before {
|
||||
content: "\e7f7";
|
||||
}
|
||||
|
||||
.ops-cmdb-resource-selected:before {
|
||||
content: "\e7f8";
|
||||
}
|
||||
|
||||
.ops-cmdb-preference-selected:before {
|
||||
content: "\e7f9";
|
||||
}
|
||||
|
||||
.ops-cmdb-preference:before {
|
||||
content: "\e7fa";
|
||||
}
|
||||
@@ -1101,22 +1069,10 @@
|
||||
content: "\e7fb";
|
||||
}
|
||||
|
||||
.ops-cmdb-tree-selected:before {
|
||||
content: "\e7fc";
|
||||
}
|
||||
|
||||
.ops-cmdb-relation-selected:before {
|
||||
content: "\e7fd";
|
||||
}
|
||||
|
||||
.ops-cmdb-adc:before {
|
||||
content: "\e7fe";
|
||||
}
|
||||
|
||||
.ops-cmdb-search-selected:before {
|
||||
content: "\e7ff";
|
||||
}
|
||||
|
||||
.ops-cmdb-relation:before {
|
||||
content: "\e800";
|
||||
}
|
||||
@@ -1125,14 +1081,6 @@
|
||||
content: "\e801";
|
||||
}
|
||||
|
||||
.ops-cmdb-citype-selected:before {
|
||||
content: "\e802";
|
||||
}
|
||||
|
||||
.ops-cmdb-dashboard-selected:before {
|
||||
content: "\e804";
|
||||
}
|
||||
|
||||
.ops-cmdb-citype:before {
|
||||
content: "\e805";
|
||||
}
|
||||
@@ -1141,10 +1089,6 @@
|
||||
content: "\e806";
|
||||
}
|
||||
|
||||
.ops-cmdb-screen-selected:before {
|
||||
content: "\e807";
|
||||
}
|
||||
|
||||
.ops-cmdb-resource:before {
|
||||
content: "\e808";
|
||||
}
|
||||
@@ -1493,14 +1437,6 @@
|
||||
content: "\e7a6";
|
||||
}
|
||||
|
||||
.ops-setting-role-selected:before {
|
||||
content: "\e7a0";
|
||||
}
|
||||
|
||||
.ops-setting-group-selected:before {
|
||||
content: "\e7a1";
|
||||
}
|
||||
|
||||
.ops-setting-role:before {
|
||||
content: "\e7a2";
|
||||
}
|
||||
@@ -1941,18 +1877,10 @@
|
||||
content: "\e738";
|
||||
}
|
||||
|
||||
.ops-setting-notice-email-selected-copy:before {
|
||||
content: "\e889";
|
||||
}
|
||||
|
||||
.ops-setting-notice:before {
|
||||
content: "\e72f";
|
||||
}
|
||||
|
||||
.ops-setting-notice-selected:before {
|
||||
content: "\e730";
|
||||
}
|
||||
|
||||
.ops-setting-notice-email-selected:before {
|
||||
content: "\e731";
|
||||
}
|
||||
@@ -1977,10 +1905,6 @@
|
||||
content: "\e736";
|
||||
}
|
||||
|
||||
.ops-setting-companyStructure-selected:before {
|
||||
content: "\e72b";
|
||||
}
|
||||
|
||||
.ops-setting-companyStructure:before {
|
||||
content: "\e72c";
|
||||
}
|
||||
@@ -1989,10 +1913,6 @@
|
||||
content: "\e72d";
|
||||
}
|
||||
|
||||
.ops-setting-companyInfo-selected:before {
|
||||
content: "\e72e";
|
||||
}
|
||||
|
||||
.ops-email:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
@@ -3033,14 +2953,6 @@
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.ops-dag-dashboard-selected:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.ops-dag-applet-selected:before {
|
||||
content: "\e602";
|
||||
}
|
||||
|
||||
.ops-dag-applet:before {
|
||||
content: "\e603";
|
||||
}
|
||||
@@ -3049,62 +2961,26 @@
|
||||
content: "\e604";
|
||||
}
|
||||
|
||||
.ops-dag-terminal-selected:before {
|
||||
content: "\e605";
|
||||
}
|
||||
|
||||
.ops-dag-cron:before {
|
||||
content: "\e606";
|
||||
}
|
||||
|
||||
.ops-dag-cron-selected:before {
|
||||
content: "\e608";
|
||||
}
|
||||
|
||||
.ops-dag-history:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.ops-dag-history-selected:before {
|
||||
content: "\e60a";
|
||||
}
|
||||
|
||||
.ops-dag-dags-selected:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.ops-dag-dagreview:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.ops-dag-dagreview-selected:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.ops-dag-panel:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.ops-dag-panel-selected:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.ops-dag-variables:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.ops-dag-variables-selected:before {
|
||||
content: "\e618";
|
||||
}
|
||||
|
||||
.ops-dag-appletadmin:before {
|
||||
content: "\e65c";
|
||||
}
|
||||
|
||||
.ops-dag-appletadmin-selected:before {
|
||||
content: "\e65d";
|
||||
}
|
||||
|
||||
.ops-dag-dags:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,48 @@
|
||||
"css_prefix_text": "",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "40043662",
|
||||
"name": "ops-setting-application-selected",
|
||||
"font_class": "ops-setting-application-selected",
|
||||
"unicode": "e919",
|
||||
"unicode_decimal": 59673
|
||||
},
|
||||
{
|
||||
"icon_id": "40043685",
|
||||
"name": "ops-setting-application",
|
||||
"font_class": "ops-setting-application",
|
||||
"unicode": "e918",
|
||||
"unicode_decimal": 59672
|
||||
},
|
||||
{
|
||||
"icon_id": "40043049",
|
||||
"name": "ops-setting-basic",
|
||||
"font_class": "ops-setting-basic",
|
||||
"unicode": "e889",
|
||||
"unicode_decimal": 59529
|
||||
},
|
||||
{
|
||||
"icon_id": "40043047",
|
||||
"name": "ops-setting-basic-selected",
|
||||
"font_class": "ops-setting-basic-selected",
|
||||
"unicode": "e917",
|
||||
"unicode_decimal": 59671
|
||||
},
|
||||
{
|
||||
"icon_id": "40038753",
|
||||
"name": "ops-setting-security",
|
||||
"font_class": "ops-setting-security",
|
||||
"unicode": "e915",
|
||||
"unicode_decimal": 59669
|
||||
},
|
||||
{
|
||||
"icon_id": "40038752",
|
||||
"name": "ops-setting-theme",
|
||||
"font_class": "ops-setting-theme",
|
||||
"unicode": "e916",
|
||||
"unicode_decimal": 59670
|
||||
},
|
||||
{
|
||||
"icon_id": "39948814",
|
||||
"name": "veops-show",
|
||||
@@ -22,7 +64,7 @@
|
||||
{
|
||||
"icon_id": "39926833",
|
||||
"name": "itsm-workload (1)",
|
||||
"font_class": "a-itsm-workload1",
|
||||
"font_class": "itsm-workload",
|
||||
"unicode": "e912",
|
||||
"unicode_decimal": 59666
|
||||
},
|
||||
@@ -453,13 +495,6 @@
|
||||
"unicode": "e8d5",
|
||||
"unicode_decimal": 59605
|
||||
},
|
||||
{
|
||||
"icon_id": "38547389",
|
||||
"name": "setting-authentication-selected",
|
||||
"font_class": "ops-setting-auth-selected",
|
||||
"unicode": "e8d4",
|
||||
"unicode_decimal": 59604
|
||||
},
|
||||
{
|
||||
"icon_id": "38533133",
|
||||
"name": "itsm-knowledge (2)",
|
||||
@@ -859,13 +894,6 @@
|
||||
"unicode": "e89c",
|
||||
"unicode_decimal": 59548
|
||||
},
|
||||
{
|
||||
"icon_id": "37940033",
|
||||
"name": "ops-setting-duty-selected",
|
||||
"font_class": "ops-setting-duty-selected",
|
||||
"unicode": "e89b",
|
||||
"unicode_decimal": 59547
|
||||
},
|
||||
{
|
||||
"icon_id": "37841524",
|
||||
"name": "datainsight-sequential",
|
||||
@@ -1153,62 +1181,6 @@
|
||||
"unicode": "e870",
|
||||
"unicode_decimal": 59504
|
||||
},
|
||||
{
|
||||
"icon_id": "35984161",
|
||||
"name": "ops-itsm-ticketsetting-selected",
|
||||
"font_class": "ops-itsm-ticketsetting-selected",
|
||||
"unicode": "e860",
|
||||
"unicode_decimal": 59488
|
||||
},
|
||||
{
|
||||
"icon_id": "35984162",
|
||||
"name": "ops-itsm-reports-selected",
|
||||
"font_class": "ops-itsm-reports-selected",
|
||||
"unicode": "e861",
|
||||
"unicode_decimal": 59489
|
||||
},
|
||||
{
|
||||
"icon_id": "35984163",
|
||||
"name": "ops-itsm-servicecatalog-selected",
|
||||
"font_class": "ops-itsm-servicecatalog-selected",
|
||||
"unicode": "e862",
|
||||
"unicode_decimal": 59490
|
||||
},
|
||||
{
|
||||
"icon_id": "35984164",
|
||||
"name": "ops-itsm-ticketmanage-selected",
|
||||
"font_class": "ops-itsm-ticketmanage-selected",
|
||||
"unicode": "e863",
|
||||
"unicode_decimal": 59491
|
||||
},
|
||||
{
|
||||
"icon_id": "35984165",
|
||||
"name": "ops-itsm-knowledge-selected",
|
||||
"font_class": "ops-itsm-knowledge-selected",
|
||||
"unicode": "e864",
|
||||
"unicode_decimal": 59492
|
||||
},
|
||||
{
|
||||
"icon_id": "35984166",
|
||||
"name": "ops-itsm-workstation-selected",
|
||||
"font_class": "ops-itsm-workstation-selected",
|
||||
"unicode": "e865",
|
||||
"unicode_decimal": 59493
|
||||
},
|
||||
{
|
||||
"icon_id": "35984167",
|
||||
"name": "ops-itsm-servicedesk-selected",
|
||||
"font_class": "ops-itsm-servicedesk-selected",
|
||||
"unicode": "e866",
|
||||
"unicode_decimal": 59494
|
||||
},
|
||||
{
|
||||
"icon_id": "35984168",
|
||||
"name": "ops-itsm-planticket-selected",
|
||||
"font_class": "ops-itsm-planticket-selected",
|
||||
"unicode": "e867",
|
||||
"unicode_decimal": 59495
|
||||
},
|
||||
{
|
||||
"icon_id": "35984169",
|
||||
"name": "ops-itsm-servicecatalog",
|
||||
@@ -1860,13 +1832,6 @@
|
||||
"unicode": "e816",
|
||||
"unicode_decimal": 59414
|
||||
},
|
||||
{
|
||||
"icon_id": "35400645",
|
||||
"name": "ops-cmdb-batch-selected",
|
||||
"font_class": "ops-cmdb-batch-selected",
|
||||
"unicode": "e803",
|
||||
"unicode_decimal": 59395
|
||||
},
|
||||
{
|
||||
"icon_id": "35400646",
|
||||
"name": "ops-cmdb-batch",
|
||||
@@ -1874,27 +1839,6 @@
|
||||
"unicode": "e80a",
|
||||
"unicode_decimal": 59402
|
||||
},
|
||||
{
|
||||
"icon_id": "35395300",
|
||||
"name": "ops-cmdb-adc-selected",
|
||||
"font_class": "ops-cmdb-adc-selected",
|
||||
"unicode": "e7f7",
|
||||
"unicode_decimal": 59383
|
||||
},
|
||||
{
|
||||
"icon_id": "35395301",
|
||||
"name": "ops-cmdb-resource-selected",
|
||||
"font_class": "ops-cmdb-resource-selected",
|
||||
"unicode": "e7f8",
|
||||
"unicode_decimal": 59384
|
||||
},
|
||||
{
|
||||
"icon_id": "35395302",
|
||||
"name": "ops-cmdb-preference-selected",
|
||||
"font_class": "ops-cmdb-preference-selected",
|
||||
"unicode": "e7f9",
|
||||
"unicode_decimal": 59385
|
||||
},
|
||||
{
|
||||
"icon_id": "35395303",
|
||||
"name": "ops-cmdb-preference",
|
||||
@@ -1909,20 +1853,6 @@
|
||||
"unicode": "e7fb",
|
||||
"unicode_decimal": 59387
|
||||
},
|
||||
{
|
||||
"icon_id": "35395305",
|
||||
"name": "ops-cmdb-tree-selected",
|
||||
"font_class": "ops-cmdb-tree-selected",
|
||||
"unicode": "e7fc",
|
||||
"unicode_decimal": 59388
|
||||
},
|
||||
{
|
||||
"icon_id": "35395306",
|
||||
"name": "ops-cmdb-relation-selected",
|
||||
"font_class": "ops-cmdb-relation-selected",
|
||||
"unicode": "e7fd",
|
||||
"unicode_decimal": 59389
|
||||
},
|
||||
{
|
||||
"icon_id": "35395307",
|
||||
"name": "ops-cmdb-adc",
|
||||
@@ -1930,13 +1860,6 @@
|
||||
"unicode": "e7fe",
|
||||
"unicode_decimal": 59390
|
||||
},
|
||||
{
|
||||
"icon_id": "35395308",
|
||||
"name": "ops-cmdb-search-selected",
|
||||
"font_class": "ops-cmdb-search-selected",
|
||||
"unicode": "e7ff",
|
||||
"unicode_decimal": 59391
|
||||
},
|
||||
{
|
||||
"icon_id": "35395309",
|
||||
"name": "ops-cmdb-relation",
|
||||
@@ -1951,20 +1874,6 @@
|
||||
"unicode": "e801",
|
||||
"unicode_decimal": 59393
|
||||
},
|
||||
{
|
||||
"icon_id": "35395311",
|
||||
"name": "ops-cmdb-citype-selected",
|
||||
"font_class": "ops-cmdb-citype-selected",
|
||||
"unicode": "e802",
|
||||
"unicode_decimal": 59394
|
||||
},
|
||||
{
|
||||
"icon_id": "35395313",
|
||||
"name": "ops-cmdb-dashboard-selected",
|
||||
"font_class": "ops-cmdb-dashboard-selected",
|
||||
"unicode": "e804",
|
||||
"unicode_decimal": 59396
|
||||
},
|
||||
{
|
||||
"icon_id": "35395314",
|
||||
"name": "ops-cmdb-citype",
|
||||
@@ -1979,13 +1888,6 @@
|
||||
"unicode": "e806",
|
||||
"unicode_decimal": 59398
|
||||
},
|
||||
{
|
||||
"icon_id": "35395316",
|
||||
"name": "ops-cmdb-screen-selected",
|
||||
"font_class": "ops-cmdb-screen-selected",
|
||||
"unicode": "e807",
|
||||
"unicode_decimal": 59399
|
||||
},
|
||||
{
|
||||
"icon_id": "35395317",
|
||||
"name": "ops-cmdb-resource",
|
||||
@@ -2595,20 +2497,6 @@
|
||||
"unicode": "e7a6",
|
||||
"unicode_decimal": 59302
|
||||
},
|
||||
{
|
||||
"icon_id": "34792792",
|
||||
"name": "ops-setting-role-selected",
|
||||
"font_class": "ops-setting-role-selected",
|
||||
"unicode": "e7a0",
|
||||
"unicode_decimal": 59296
|
||||
},
|
||||
{
|
||||
"icon_id": "34792793",
|
||||
"name": "ops-setting-group-selected",
|
||||
"font_class": "ops-setting-group-selected",
|
||||
"unicode": "e7a1",
|
||||
"unicode_decimal": 59297
|
||||
},
|
||||
{
|
||||
"icon_id": "34792794",
|
||||
"name": "ops-setting-role",
|
||||
@@ -3379,13 +3267,6 @@
|
||||
"unicode": "e738",
|
||||
"unicode_decimal": 59192
|
||||
},
|
||||
{
|
||||
"icon_id": "37575490",
|
||||
"name": "ops-setting-notice-email-selected",
|
||||
"font_class": "ops-setting-notice-email-selected-copy",
|
||||
"unicode": "e889",
|
||||
"unicode_decimal": 59529
|
||||
},
|
||||
{
|
||||
"icon_id": "34108346",
|
||||
"name": "ops-setting-notice",
|
||||
@@ -3393,13 +3274,6 @@
|
||||
"unicode": "e72f",
|
||||
"unicode_decimal": 59183
|
||||
},
|
||||
{
|
||||
"icon_id": "34108348",
|
||||
"name": "ops-setting-notice-selected",
|
||||
"font_class": "ops-setting-notice-selected",
|
||||
"unicode": "e730",
|
||||
"unicode_decimal": 59184
|
||||
},
|
||||
{
|
||||
"icon_id": "34108504",
|
||||
"name": "ops-setting-notice-email-selected",
|
||||
@@ -3442,13 +3316,6 @@
|
||||
"unicode": "e736",
|
||||
"unicode_decimal": 59190
|
||||
},
|
||||
{
|
||||
"icon_id": "34108244",
|
||||
"name": "ops-setting-companyStructure-selected",
|
||||
"font_class": "ops-setting-companyStructure-selected",
|
||||
"unicode": "e72b",
|
||||
"unicode_decimal": 59179
|
||||
},
|
||||
{
|
||||
"icon_id": "34108296",
|
||||
"name": "ops-setting-companyStructure",
|
||||
@@ -3463,13 +3330,6 @@
|
||||
"unicode": "e72d",
|
||||
"unicode_decimal": 59181
|
||||
},
|
||||
{
|
||||
"icon_id": "34108330",
|
||||
"name": "ops-setting-companyInfo-selected",
|
||||
"font_class": "ops-setting-companyInfo-selected",
|
||||
"unicode": "e72e",
|
||||
"unicode_decimal": 59182
|
||||
},
|
||||
{
|
||||
"icon_id": "34099810",
|
||||
"name": "ops-email",
|
||||
@@ -5290,20 +5150,6 @@
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "33053294",
|
||||
"name": "ops-dag-dashboard-selected",
|
||||
"font_class": "ops-dag-dashboard-selected",
|
||||
"unicode": "e601",
|
||||
"unicode_decimal": 58881
|
||||
},
|
||||
{
|
||||
"icon_id": "33053330",
|
||||
"name": "ops-dag-applet-selected",
|
||||
"font_class": "ops-dag-applet-selected",
|
||||
"unicode": "e602",
|
||||
"unicode_decimal": 58882
|
||||
},
|
||||
{
|
||||
"icon_id": "33053531",
|
||||
"name": "ops-dag-applet",
|
||||
@@ -5318,13 +5164,6 @@
|
||||
"unicode": "e604",
|
||||
"unicode_decimal": 58884
|
||||
},
|
||||
{
|
||||
"icon_id": "33053589",
|
||||
"name": "ops-dag-terminal-selected",
|
||||
"font_class": "ops-dag-terminal-selected",
|
||||
"unicode": "e605",
|
||||
"unicode_decimal": 58885
|
||||
},
|
||||
{
|
||||
"icon_id": "33053591",
|
||||
"name": "ops-dag-cron",
|
||||
@@ -5332,13 +5171,6 @@
|
||||
"unicode": "e606",
|
||||
"unicode_decimal": 58886
|
||||
},
|
||||
{
|
||||
"icon_id": "33053609",
|
||||
"name": "ops-dag-cron-selected",
|
||||
"font_class": "ops-dag-cron-selected",
|
||||
"unicode": "e608",
|
||||
"unicode_decimal": 58888
|
||||
},
|
||||
{
|
||||
"icon_id": "33053615",
|
||||
"name": "ops-dag-history",
|
||||
@@ -5346,20 +5178,6 @@
|
||||
"unicode": "e609",
|
||||
"unicode_decimal": 58889
|
||||
},
|
||||
{
|
||||
"icon_id": "33053617",
|
||||
"name": "ops-dag-history-selected",
|
||||
"font_class": "ops-dag-history-selected",
|
||||
"unicode": "e60a",
|
||||
"unicode_decimal": 58890
|
||||
},
|
||||
{
|
||||
"icon_id": "33053681",
|
||||
"name": "ops-dag-dags-selected",
|
||||
"font_class": "ops-dag-dags-selected",
|
||||
"unicode": "e60c",
|
||||
"unicode_decimal": 58892
|
||||
},
|
||||
{
|
||||
"icon_id": "33053682",
|
||||
"name": "ops-dag-dagreview",
|
||||
@@ -5367,13 +5185,6 @@
|
||||
"unicode": "e60d",
|
||||
"unicode_decimal": 58893
|
||||
},
|
||||
{
|
||||
"icon_id": "33053684",
|
||||
"name": "ops-dag-dagreview-selected",
|
||||
"font_class": "ops-dag-dagreview-selected",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "33053691",
|
||||
"name": "ops-dag-panel",
|
||||
@@ -5381,13 +5192,6 @@
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "33053692",
|
||||
"name": "ops-dag-panel-selected",
|
||||
"font_class": "ops-dag-panel-selected",
|
||||
"unicode": "e615",
|
||||
"unicode_decimal": 58901
|
||||
},
|
||||
{
|
||||
"icon_id": "33053707",
|
||||
"name": "ops-dag-variables",
|
||||
@@ -5395,27 +5199,6 @@
|
||||
"unicode": "e616",
|
||||
"unicode_decimal": 58902
|
||||
},
|
||||
{
|
||||
"icon_id": "33053715",
|
||||
"name": "ops-dag-variables-selected",
|
||||
"font_class": "ops-dag-variables-selected",
|
||||
"unicode": "e618",
|
||||
"unicode_decimal": 58904
|
||||
},
|
||||
{
|
||||
"icon_id": "33053718",
|
||||
"name": "ops-dag-appletadmin",
|
||||
"font_class": "ops-dag-appletadmin",
|
||||
"unicode": "e65c",
|
||||
"unicode_decimal": 58972
|
||||
},
|
||||
{
|
||||
"icon_id": "33053720",
|
||||
"name": "ops-dag-appletadmin-selected",
|
||||
"font_class": "ops-dag-appletadmin-selected",
|
||||
"unicode": "e65d",
|
||||
"unicode_decimal": 58973
|
||||
},
|
||||
{
|
||||
"icon_id": "33055163",
|
||||
"name": "ops-dag-dags",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -73,7 +73,7 @@ export default {
|
||||
.custom-drawer-close {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
background: #custom_colors[color_1];
|
||||
background: @primary-color;
|
||||
color: white;
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
|
@@ -298,14 +298,14 @@ export default {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
background-color: #custom_colors[color_2];
|
||||
color: #custom_colors[color_1];
|
||||
background-color: @primary-color_5;
|
||||
color: @primary-color;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
@@ -161,7 +161,7 @@ export default {
|
||||
cursor: pointer;
|
||||
&-selected,
|
||||
&:hover {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,7 @@ export default {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
border-left: 2px solid #custom_colors[color_1];
|
||||
border-left: 2px solid @primary-color;
|
||||
padding-left: 6px;
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
@@ -185,14 +185,14 @@ export default {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
background-color: #custom_colors[color_2];
|
||||
color: #custom_colors[color_1];
|
||||
background-color: @primary-color_5;
|
||||
color: @primary-color;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ export default {
|
||||
background-color: transparent;
|
||||
}
|
||||
.sidebar-list-item.sidebar-list-item-selected::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
.sidebar-list-item-dotline {
|
||||
padding-left: 20px;
|
||||
|
@@ -97,8 +97,8 @@ export default {
|
||||
|
||||
<style lang="less">
|
||||
.color {
|
||||
color: #custom_colors[color_1];
|
||||
background-color: #custom_colors[color_2];
|
||||
color: @primary-color;
|
||||
background-color: @primary-color_5;
|
||||
}
|
||||
.custom-user {
|
||||
.custom-user-item {
|
||||
@@ -117,7 +117,7 @@ export default {
|
||||
.locale {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -54,3 +54,36 @@ export function getCiTriggersByCiId(ci_id, params) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getCiRelatedTickets(params) {
|
||||
return axios({
|
||||
url: `/itsm/v1/process_ticket/get_tickets_by`,
|
||||
method: 'POST',
|
||||
data: params,
|
||||
isShowMessage: false
|
||||
})
|
||||
}
|
||||
|
||||
export function judgeItsmInstalled() {
|
||||
return axios({
|
||||
url: `/itsm/v1/process_ticket/itsm_existed`,
|
||||
method: 'GET',
|
||||
isShowMessage: false
|
||||
})
|
||||
}
|
||||
|
||||
export function getCIsBaseline(params) {
|
||||
return axios({
|
||||
url: `/v0.1/ci/baseline`,
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function CIBaselineRollback(ciId, params) {
|
||||
return axios({
|
||||
url: `/v0.1/ci/${ciId}/baseline/rollback`,
|
||||
method: 'POST',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
BIN
cmdb-ui/src/modules/cmdb/assets/itsm_uninstalled.png
Normal file
BIN
cmdb-ui/src/modules/cmdb/assets/itsm_uninstalled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@@ -230,7 +230,7 @@ export default {
|
||||
background-color: #f9fbff;
|
||||
border-bottom: none;
|
||||
.ant-transfer-list-header-title {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -258,7 +258,7 @@ export default {
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
background-color: #fff;
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
border-radius: 4px;
|
||||
width: 12px;
|
||||
}
|
||||
@@ -271,7 +271,7 @@ export default {
|
||||
font-size: 12px;
|
||||
color: #cacdd9;
|
||||
&:hover {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.move-icon {
|
||||
@@ -291,8 +291,8 @@ export default {
|
||||
}
|
||||
}
|
||||
.ant-transfer-list-content-item-selected {
|
||||
background-color: #custom_colors[color_2];
|
||||
border-color: #custom_colors[color_1];
|
||||
background-color: @primary-color_5;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="cmdb-grant" :style="{ maxHeight: `${windowHeight - 130}px` }">
|
||||
<div class="cmdb-grant" :style="{ }">
|
||||
<template v-if="cmdbGrantType.includes('ci_type')">
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciTypeGrant') }}</div>
|
||||
<CiTypeGrant
|
||||
|
@@ -75,10 +75,10 @@ export default {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
// background-color: #custom_colors[color_1];
|
||||
// background-color: @primary-color;
|
||||
border-radius: 2px;
|
||||
border: 14px solid transparent;
|
||||
border-left-color: #custom_colors[color_1];
|
||||
border-left-color: @primary-color;
|
||||
transform: rotate(225deg);
|
||||
top: -16px;
|
||||
left: -17px;
|
||||
|
@@ -175,8 +175,8 @@ export default {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
background-color: #custom_colors[color_2];
|
||||
color: #custom_colors[color_1];
|
||||
background-color: @primary-color_5;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ export default {
|
||||
|
||||
.notice-content {
|
||||
.w-e-bar {
|
||||
background-color: #custom_colors[color_2];
|
||||
background-color: @primary-color_5;
|
||||
}
|
||||
.w-e-text-placeholder {
|
||||
line-height: 1.5;
|
||||
|
@@ -253,7 +253,7 @@ export default {
|
||||
}
|
||||
.cmdb-subscribe-drawer-tree-header {
|
||||
border-radius: 4px;
|
||||
background-color: #custom_colors[color_2];
|
||||
background-color: @primary-color_5;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 12px;
|
||||
@@ -264,7 +264,7 @@ export default {
|
||||
> span {
|
||||
display: inline-block;
|
||||
background-color: #fff;
|
||||
border-left: 2px solid #custom_colors[color_1];
|
||||
border-left: 2px solid @primary-color;
|
||||
padding: 3px 12px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
@@ -272,7 +272,7 @@ export default {
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
&:hover {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,7 +316,7 @@ export default {
|
||||
<style lang="less">
|
||||
.cmdb-subscribe-drawer {
|
||||
.ant-tabs-bar {
|
||||
background-color: #custom_colors[color_2];
|
||||
background-color: @primary-color_5;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@
|
||||
>
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> {{ $t('cmdb.components.noParamRequest') }} </span>
|
||||
<a-button @click="add" type="primary" size="small" icon="plus" class="ops-button-primary">
|
||||
<a-button @click="add" type="primary" size="small" icon="plus">
|
||||
{{ $t('add') }}
|
||||
</a-button>
|
||||
</a-empty>
|
||||
|
@@ -402,7 +402,15 @@ const cmdb_en = {
|
||||
noModifications: 'No Modifications',
|
||||
attr: 'attribute',
|
||||
attrId: 'attribute id',
|
||||
changeDescription: 'attribute id: {attr_id}, {before_days} day(s) in advance, Subject: {subject}\nContent: {body}\nNotify At: {notify_at}'
|
||||
changeDescription: 'attribute id: {attr_id}, {before_days} day(s) in advance, Subject: {subject}\nContent: {body}\nNotify At: {notify_at}',
|
||||
ticketStartTime: 'Start Time',
|
||||
ticketCreator: 'Creator',
|
||||
ticketTitle: 'Title',
|
||||
ticketFinishTime: 'Finish Time',
|
||||
ticketNodeName: 'Node Name',
|
||||
itsmUninstalled: 'Please use it in combination with VE ITSM',
|
||||
applyItsm: 'Free Apply ITSM',
|
||||
ticketId: 'Ticket ID',
|
||||
},
|
||||
relation_type: {
|
||||
addRelationType: 'New',
|
||||
@@ -494,7 +502,8 @@ if __name__ == "__main__":
|
||||
copyFailed: 'Copy failed',
|
||||
noLevel: 'No hierarchical relationship!',
|
||||
batchAddRelation: 'Batch Add Relation',
|
||||
history: 'History',
|
||||
history: 'Change Logs',
|
||||
relITSM: 'Related Tickets',
|
||||
topo: 'Topology',
|
||||
table: 'Table',
|
||||
m2mTips: 'The current CIType relationship is many-to-many, please go to the SerivceTree(relation view) to add or delete',
|
||||
@@ -512,7 +521,21 @@ if __name__ == "__main__":
|
||||
newUpdateField: 'Add a Attribute',
|
||||
attributeSettings: 'Attribute Settings',
|
||||
share: 'Share',
|
||||
noPermission: 'No Permission'
|
||||
noPermission: 'No Permission',
|
||||
rollback: 'Rollback',
|
||||
rollbackHeader: 'Instance Rollback',
|
||||
rollbackTo: 'Rollback to',
|
||||
rollbackToTips: 'Please select rollback time',
|
||||
baselineDiff: 'Difference from baseline',
|
||||
instance: 'Instance',
|
||||
rollbackBefore: 'Current value',
|
||||
rollbackAfter: 'After rollback',
|
||||
noDiff: 'CI data has not changed after {baseline}',
|
||||
rollbackConfirm: 'Are you sure you want to rollback?',
|
||||
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',
|
||||
},
|
||||
serviceTree: {
|
||||
remove: 'Remove',
|
||||
|
@@ -402,7 +402,15 @@ const cmdb_zh = {
|
||||
noModifications: '没有修改',
|
||||
attr: '属性名',
|
||||
attrId: '属性ID',
|
||||
changeDescription: '属性ID:{attr_id},提前:{before_days}天,主题:{subject}\n内容:{body}\n通知时间:{notify_at}'
|
||||
changeDescription: '属性ID:{attr_id},提前:{before_days}天,主题:{subject}\n内容:{body}\n通知时间:{notify_at}',
|
||||
ticketStartTime: '工单发起时间',
|
||||
ticketCreator: '发起人',
|
||||
ticketTitle: '工单名称',
|
||||
ticketFinishTime: '节点完成时间',
|
||||
ticketNodeName: '节点名称',
|
||||
itsmUninstalled: '请结合维易ITSM使用',
|
||||
applyItsm: '免费申请',
|
||||
ticketId: '工单ID',
|
||||
},
|
||||
relation_type: {
|
||||
addRelationType: '新增关系类型',
|
||||
@@ -494,7 +502,8 @@ if __name__ == "__main__":
|
||||
copyFailed: '复制失败!',
|
||||
noLevel: '无层级关系!',
|
||||
batchAddRelation: '批量添加关系',
|
||||
history: '操作历史',
|
||||
history: '变更记录',
|
||||
relITSM: '关联工单',
|
||||
topo: '拓扑',
|
||||
table: '表格',
|
||||
m2mTips: '当前模型关系为多对多,请前往关系视图进行增删操作',
|
||||
@@ -512,7 +521,21 @@ if __name__ == "__main__":
|
||||
newUpdateField: '新增修改字段',
|
||||
attributeSettings: '字段设置',
|
||||
share: '分享',
|
||||
noPermission: '暂无权限'
|
||||
noPermission: '暂无权限',
|
||||
rollback: '回滚',
|
||||
rollbackHeader: '实例回滚',
|
||||
rollbackTo: '回滚至: ',
|
||||
rollbackToTips: '请选择回滚时间点',
|
||||
baselineDiff: '基线对比结果',
|
||||
instance: '实例',
|
||||
rollbackBefore: '当前值',
|
||||
rollbackAfter: '回滚后',
|
||||
noDiff: '在【{baseline}】后数据没有发生变化',
|
||||
rollbackConfirm: '确认要回滚吗 ?',
|
||||
rollbackSuccess: '回滚成功',
|
||||
rollbackingTips: '正在批量回滚中',
|
||||
batchRollbacking: '正在回滚,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||
baselineTips: '该时间点的变更也会被回滚, 唯一标识、密码属性不支持回滚',
|
||||
},
|
||||
serviceTree: {
|
||||
remove: '移除',
|
||||
|
@@ -13,7 +13,7 @@ const genCmdbRoutes = async () => {
|
||||
{
|
||||
path: '/cmdb/dashboard',
|
||||
name: 'cmdb_dashboard',
|
||||
meta: { title: 'dashboard', icon: 'ops-cmdb-dashboard', selectedIcon: 'ops-cmdb-dashboard-selected', keepAlive: false },
|
||||
meta: { title: 'dashboard', icon: 'ops-cmdb-dashboard', selectedIcon: 'ops-cmdb-dashboard', keepAlive: false },
|
||||
component: () => import('../views/dashboard/index_v2.vue')
|
||||
},
|
||||
{
|
||||
@@ -25,7 +25,7 @@ const genCmdbRoutes = async () => {
|
||||
path: '/cmdb/resourceviews',
|
||||
name: 'cmdb_resource_views',
|
||||
component: RouteView,
|
||||
meta: { title: 'cmdb.menu.ciTable', icon: 'ops-cmdb-resource', selectedIcon: 'ops-cmdb-resource-selected', keepAlive: true },
|
||||
meta: { title: 'cmdb.menu.ciTable', icon: 'ops-cmdb-resource', selectedIcon: 'ops-cmdb-resource', keepAlive: true },
|
||||
hideChildrenInMenu: false,
|
||||
children: []
|
||||
},
|
||||
@@ -33,7 +33,7 @@ const genCmdbRoutes = async () => {
|
||||
path: '/cmdb/tree_views',
|
||||
component: () => import('../views/tree_views'),
|
||||
name: 'cmdb_tree_views',
|
||||
meta: { title: 'cmdb.menu.ciTree', icon: 'ops-cmdb-tree', selectedIcon: 'ops-cmdb-tree-selected', keepAlive: false },
|
||||
meta: { title: 'cmdb.menu.ciTree', icon: 'ops-cmdb-tree', selectedIcon: 'ops-cmdb-tree', keepAlive: false },
|
||||
hideChildrenInMenu: true,
|
||||
children: [
|
||||
{
|
||||
@@ -47,13 +47,13 @@ const genCmdbRoutes = async () => {
|
||||
{
|
||||
path: '/cmdb/resourcesearch',
|
||||
name: 'cmdb_resource_search',
|
||||
meta: { title: 'cmdb.menu.ciSearch', icon: 'ops-cmdb-search', selectedIcon: 'ops-cmdb-search-selected', keepAlive: false },
|
||||
meta: { title: 'cmdb.menu.ciSearch', icon: 'ops-cmdb-search', selectedIcon: 'ops-cmdb-search', keepAlive: false },
|
||||
component: () => import('../views/resource_search/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/cmdb/adc',
|
||||
name: 'cmdb_auto_discovery_ci',
|
||||
meta: { title: 'cmdb.menu.adCIs', icon: 'ops-cmdb-adc', selectedIcon: 'ops-cmdb-adc-selected', keepAlive: false },
|
||||
meta: { title: 'cmdb.menu.adCIs', icon: 'ops-cmdb-adc', selectedIcon: 'ops-cmdb-adc', keepAlive: false },
|
||||
component: () => import('../views/discoveryCI/index.vue')
|
||||
},
|
||||
{
|
||||
@@ -72,19 +72,19 @@ const genCmdbRoutes = async () => {
|
||||
path: '/cmdb/preference',
|
||||
component: () => import('../views/preference/index'),
|
||||
name: 'cmdb_preference',
|
||||
meta: { title: 'cmdb.menu.preference', icon: 'ops-cmdb-preference', selectedIcon: 'ops-cmdb-preference-selected', keepAlive: false }
|
||||
meta: { title: 'cmdb.menu.preference', icon: 'ops-cmdb-preference', selectedIcon: 'ops-cmdb-preference', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/cmdb/batch',
|
||||
component: () => import('../views/batch'),
|
||||
name: 'cmdb_batch',
|
||||
meta: { 'title': 'cmdb.menu.batchUpload', icon: 'ops-cmdb-batch', selectedIcon: 'ops-cmdb-batch-selected', keepAlive: false }
|
||||
meta: { 'title': 'cmdb.menu.batchUpload', icon: 'ops-cmdb-batch', selectedIcon: 'ops-cmdb-batch', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/cmdb/ci_types',
|
||||
name: 'ci_type',
|
||||
component: () => import('../views/ci_types/index'),
|
||||
meta: { title: 'cmdb.menu.citypeManage', icon: 'ops-cmdb-citype', selectedIcon: 'ops-cmdb-citype-selected', keepAlive: false, permission: ['cmdb_admin', 'admin'] }
|
||||
meta: { title: 'cmdb.menu.citypeManage', icon: 'ops-cmdb-citype', selectedIcon: 'ops-cmdb-citype', keepAlive: false, permission: ['cmdb_admin', 'admin'] }
|
||||
},
|
||||
{
|
||||
path: '/cmdb/disabled3',
|
||||
@@ -166,7 +166,7 @@ const genCmdbRoutes = async () => {
|
||||
path: `/cmdb/relationviews/${item[1]}`,
|
||||
name: `cmdb_relation_views_${item[1]}`,
|
||||
component: () => import('../views/relation_views/index'),
|
||||
meta: { title: item[0], icon: 'ops-cmdb-relation', selectedIcon: 'ops-cmdb-relation-selected', keepAlive: false, name: item[0] },
|
||||
meta: { title: item[0], icon: 'ops-cmdb-relation', selectedIcon: 'ops-cmdb-relation', keepAlive: false, name: item[0] },
|
||||
}
|
||||
})
|
||||
routes.children.splice(2, 0, ...relationViews)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="ci-detail-header">{{ this.type.alias }}</div>
|
||||
<div class="ci-detail-page">
|
||||
<CiDetailTab ref="ciDetailTab" :typeId="typeId" />
|
||||
<ci-detail-tab ref="ciDetailTab" :typeId="typeId" :attributeHistoryTableHeight="windowHeight - 250" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -23,6 +23,11 @@ export default {
|
||||
attributes: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
attrList: () => {
|
||||
@@ -55,7 +60,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.ci-detail-header {
|
||||
border-left: 3px solid @primary-color;
|
||||
padding-left: 10px;
|
||||
|
@@ -68,6 +68,8 @@
|
||||
<span @click="openBatchDownload">{{ $t('download') }}</span>
|
||||
<a-divider type="vertical" />
|
||||
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||
<a-divider type="vertical" />
|
||||
<span @click="batchRollback">{{ $t('cmdb.ci.rollback') }}</span>
|
||||
<span>{{ $t('cmdb.ci.selectRows', { rows: selectedRowKeys.length }) }}</span>
|
||||
</div>
|
||||
</SearchForm>
|
||||
@@ -293,6 +295,7 @@
|
||||
<create-instance-form ref="create" @reload="reloadData" @submit="batchUpdate" />
|
||||
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
||||
<BatchDownload ref="batchDownload" @batchDownload="batchDownload" />
|
||||
<ci-rollback-form ref="ciRollbackForm" @batchRollbackAsync="batchRollbackAsync($event)" :ciIds="selectedRowKeys" />
|
||||
<MetadataDrawer ref="metadataDrawer" />
|
||||
<CMDBGrant ref="cmdbGrant" resourceTypeName="CIType" app_id="cmdb" />
|
||||
</a-spin>
|
||||
@@ -325,6 +328,8 @@ import MetadataDrawer from './modules/MetadataDrawer.vue'
|
||||
import CMDBGrant from '../../components/cmdbGrant'
|
||||
import { ops_move_icon as OpsMoveIcon } from '@/core/icons'
|
||||
import { getAttrPassword } from '../../api/CITypeAttr'
|
||||
import CiRollbackForm from './modules/ciRollbackForm.vue'
|
||||
import { CIBaselineRollback } from '../../api/history'
|
||||
|
||||
export default {
|
||||
name: 'InstanceList',
|
||||
@@ -340,6 +345,7 @@ export default {
|
||||
MetadataDrawer,
|
||||
CMDBGrant,
|
||||
OpsMoveIcon,
|
||||
CiRollbackForm,
|
||||
},
|
||||
computed: {
|
||||
windowHeight() {
|
||||
@@ -429,6 +435,12 @@ export default {
|
||||
// window.onkeypress = (e) => {
|
||||
// this.handleKeyPress(e)
|
||||
// }
|
||||
this.$nextTick(() => {
|
||||
const loadingNode = document.getElementsByClassName('ant-drawer-mask')
|
||||
if (loadingNode?.style) {
|
||||
loadingNode.style.zIndex = 8
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
this.columnDrop()
|
||||
}, 1000)
|
||||
@@ -661,7 +673,7 @@ export default {
|
||||
message: this.$t('warning'),
|
||||
description: errorMsg,
|
||||
duration: 0,
|
||||
style: { whiteSpace: 'break-spaces' },
|
||||
style: { whiteSpace: 'break-spaces', overflow: 'auto', maxHeight: this.windowHeight - 80 + 'px' },
|
||||
})
|
||||
errorNum += 1
|
||||
})
|
||||
@@ -744,6 +756,55 @@ export default {
|
||||
},
|
||||
})
|
||||
},
|
||||
batchRollback() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ciRollbackForm.onOpen(true)
|
||||
})
|
||||
},
|
||||
async batchRollbackAsync(params) {
|
||||
const mask = document.querySelector('.ant-drawer-mask')
|
||||
const oldValue = mask.style.zIndex
|
||||
mask.style.zIndex = 2
|
||||
let successNum = 0
|
||||
let errorNum = 0
|
||||
this.loading = true
|
||||
this.loadTip = this.$t('cmdb.ci.rollbackingTips')
|
||||
const floor = Math.ceil(this.selectedRowKeys.length / 6)
|
||||
for (let i = 0; i < floor; i++) {
|
||||
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
|
||||
const promises = itemList.map((x) => CIBaselineRollback(x, params))
|
||||
await Promise.allSettled(promises)
|
||||
.then((res) => {
|
||||
res.forEach((r) => {
|
||||
if (r.status === 'fulfilled') {
|
||||
successNum += 1
|
||||
} else {
|
||||
errorNum += 1
|
||||
}
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadTip = this.$t('cmdb.ci.batchRollbacking', {
|
||||
total: this.selectedRowKeys.length,
|
||||
successNum: successNum,
|
||||
errorNum: errorNum,
|
||||
})
|
||||
})
|
||||
}
|
||||
this.loading = false
|
||||
this.loadTip = ''
|
||||
mask.style.zIndex = oldValue
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.xTable.getVxetableRef().clearCheckboxReserve()
|
||||
this.$nextTick(() => {
|
||||
if (this.currentPage === 1) {
|
||||
this.loadTableData()
|
||||
} else {
|
||||
this.currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
async refreshAfterEditAttrs() {
|
||||
await this.loadPreferenceAttrList()
|
||||
await this.loadTableData()
|
||||
|
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<CustomDrawer
|
||||
width="80%"
|
||||
width="90%"
|
||||
placement="left"
|
||||
@close="
|
||||
() => {
|
||||
visible = false
|
||||
}
|
||||
"
|
||||
style="transform: translateX(0px)!important"
|
||||
:visible="visible"
|
||||
:hasTitle="false"
|
||||
:hasFooter="false"
|
||||
|
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div :style="{ height: '100%' }" v-if="itsmInstalled">
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
resizable
|
||||
border
|
||||
size="small"
|
||||
class="ops-unstripe-table"
|
||||
:span-method="mergeRowMethod"
|
||||
:data="tableData"
|
||||
v-bind="ci_id ? { height: 'auto' } : { height: `${windowHeight - 225}px` }"
|
||||
>
|
||||
<template #empty>
|
||||
<a-empty :image-style="{ height: '100px' }" :style="{ paddingTop: '10%' }">
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> {{ $t('noData') }} </span>
|
||||
</a-empty>
|
||||
</template>
|
||||
<vxe-column field="ticket.ticket_id" min-width="80" :title="$t('cmdb.history.ticketId')"> </vxe-column>
|
||||
<vxe-column field="ticket.created_at" width="160" :title="$t('cmdb.history.ticketStartTime')"> </vxe-column>
|
||||
<vxe-column field="ticket.creator_name" min-width="80" :title="$t('cmdb.history.ticketCreator')"> </vxe-column>
|
||||
<vxe-column field="ticket.title" min-width="150" :title="$t('cmdb.history.ticketTitle')">
|
||||
<template slot-scope="{ row }">
|
||||
<a target="_blank" :href="row.ticket.url">{{ row.ticket.title }}</a>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="ticket.node_finish_time" width="160" :title="$t('cmdb.history.ticketFinishTime')">
|
||||
</vxe-column>
|
||||
<vxe-column field="ticket.node_name" min-width="100" :title="$t('cmdb.history.ticketNodeName')"> </vxe-column>
|
||||
<vxe-table-column
|
||||
field="operate_type"
|
||||
min-width="100"
|
||||
:filters="[
|
||||
{ value: 0, label: $t('new') },
|
||||
{ value: 1, label: $t('delete') },
|
||||
{ value: 2, label: $t('update') },
|
||||
]"
|
||||
:filter-method="filterOperateMethod"
|
||||
:title="$t('operation')"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ operateTypeMap[row.operate_type] }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="attr_alias"
|
||||
min-width="100"
|
||||
:title="$t('cmdb.attribute')"
|
||||
:filters="[]"
|
||||
:filter-method="filterAttrMethod"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="old" min-width="100" :title="$t('cmdb.history.old')"></vxe-table-column>
|
||||
<vxe-table-column field="new" min-width="100" :title="$t('cmdb.history.new')"></vxe-table-column>
|
||||
</vxe-table>
|
||||
<div :style="{ textAlign: 'right' }" v-if="!ci_id">
|
||||
<a-pagination
|
||||
size="small"
|
||||
show-size-changer
|
||||
show-quick-jumper
|
||||
:page-size-options="pageSizeOptions"
|
||||
:current="tablePage.currentPage"
|
||||
:total="tablePage.totalResult"
|
||||
:show-total="(total, range) => $t('cmdb.history.totalItems', { total: total })"
|
||||
:page-size="tablePage.pageSize"
|
||||
:default-current="1"
|
||||
@change="pageOrSizeChange"
|
||||
@showSizeChange="pageOrSizeChange"
|
||||
>
|
||||
</a-pagination>
|
||||
</div>
|
||||
</div>
|
||||
<a-empty
|
||||
v-else
|
||||
:image-style="{
|
||||
height: '200px',
|
||||
}"
|
||||
:style="{ paddingTop: '10%' }"
|
||||
>
|
||||
<img slot="image" :src="require('@/modules/cmdb/assets/itsm_uninstalled.png')" />
|
||||
<span slot="description"> {{ $t('cmdb.history.itsmUninstalled') }} </span>
|
||||
<a-button href="https://veops.cn/apply" target="_blank" type="primary">
|
||||
{{ $t('cmdb.history.applyItsm') }}
|
||||
</a-button>
|
||||
</a-empty>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCiRelatedTickets } from '../../../api/history'
|
||||
export default {
|
||||
name: 'RelatedItsmTable',
|
||||
props: {
|
||||
ci_id: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
ciHistory: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
itsmInstalled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
attrList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
tablePage: {
|
||||
currentPage: 1,
|
||||
pageSize: 50,
|
||||
totalResult: 0,
|
||||
},
|
||||
pageSizeOptions: ['50', '100', '200'],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
operateTypeMap() {
|
||||
return {
|
||||
0: this.$t('new'),
|
||||
1: this.$t('delete'),
|
||||
2: this.$t('update'),
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateTableData()
|
||||
},
|
||||
methods: {
|
||||
updateTableData(currentPage = 1, pageSize = this.tablePage.pageSize) {
|
||||
const params = { page: currentPage, page_size: pageSize, next_todo_ids: [] }
|
||||
if (this.ci_id) {
|
||||
const tableData = []
|
||||
this.ciHistory.forEach((item) => {
|
||||
if (item.ticket_id) {
|
||||
params.next_todo_ids.push(item.ticket_id)
|
||||
tableData.push(item)
|
||||
}
|
||||
})
|
||||
if (params.next_todo_ids.length) {
|
||||
getCiRelatedTickets(params)
|
||||
.then((res) => {
|
||||
const ticketId2Detail = {}
|
||||
res.forEach((item) => {
|
||||
ticketId2Detail[item.next_todo_id] = item
|
||||
})
|
||||
this.tableData = tableData.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
ticket: ticketId2Detail[item.ticket_id],
|
||||
}
|
||||
})
|
||||
this.updateAttrFilter()
|
||||
})
|
||||
.catch((e) => {})
|
||||
}
|
||||
} else {
|
||||
}
|
||||
},
|
||||
updateAttrFilter() {
|
||||
this.$nextTick(() => {
|
||||
const $table = this.$refs.xTable
|
||||
if ($table) {
|
||||
const attrColumn = $table.getColumnByField('attr_alias')
|
||||
if (attrColumn) {
|
||||
$table.setFilter(
|
||||
attrColumn,
|
||||
this.attrList().map((attr) => {
|
||||
return { value: attr.alias || attr.name, label: attr.alias || attr.name }
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
mergeRowMethod({ row, _rowIndex, column, visibleData }) {
|
||||
const fields = [
|
||||
'ticket.ticket_id',
|
||||
'ticket.created_at',
|
||||
'ticket.creator_name',
|
||||
'ticket.title',
|
||||
'ticket.node_finish_time',
|
||||
'ticket.node_name',
|
||||
]
|
||||
const cellValue1 = row.ticket.ticket_id
|
||||
const cellValue2 = row.ticket.node_name
|
||||
if (cellValue1 && cellValue2 && fields.includes(column.property)) {
|
||||
const prevRow = visibleData[_rowIndex - 1]
|
||||
let nextRow = visibleData[_rowIndex + 1]
|
||||
if (prevRow && prevRow.ticket.ticket_id === cellValue1 && prevRow.ticket.node_name === cellValue2) {
|
||||
return { rowspan: 0, colspan: 0 }
|
||||
} else {
|
||||
let countRowspan = 1
|
||||
while (nextRow && nextRow.ticket.ticket_id === cellValue1 && nextRow.ticket.node_name === cellValue2) {
|
||||
nextRow = visibleData[++countRowspan + _rowIndex]
|
||||
}
|
||||
if (countRowspan > 1) {
|
||||
return { rowspan: countRowspan, colspan: 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pageOrSizeChange(currentPage, pageSize) {
|
||||
this.updateTableData(currentPage, pageSize)
|
||||
},
|
||||
filterOperateMethod({ value, row, column }) {
|
||||
return Number(row.operate_type) === Number(value)
|
||||
},
|
||||
filterAttrMethod({ value, row, column }) {
|
||||
return row.attr_alias === value
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@@ -20,7 +20,7 @@
|
||||
:key="attr.name"
|
||||
v-for="attr in group.attributes"
|
||||
>
|
||||
<CiDetailAttrContent :ci="ci" :attr="attr" @refresh="refresh" @updateCIByself="updateCIByself" />
|
||||
<ci-detail-attr-content :ci="ci" :attr="attr" @refresh="refresh" @updateCIByself="updateCIByself" />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
@@ -28,22 +28,39 @@
|
||||
<a-tab-pane key="tab_2">
|
||||
<span slot="tab"><a-icon type="branches" />{{ $t('cmdb.relation') }}</span>
|
||||
<div :style="{ height: '100%', padding: '24px', overflow: 'auto' }">
|
||||
<CiDetailRelation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" />
|
||||
<ci-detail-relation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_3">
|
||||
<span slot="tab"><a-icon type="clock-circle" />{{ $t('cmdb.ci.history') }}</span>
|
||||
<div :style="{ padding: '24px', height: '100%' }">
|
||||
<a-space :style="{ 'margin-bottom': '10px', display: 'flex' }">
|
||||
<a-button type="primary" class="ops-button-ghost" ghost @click="handleRollbackCI()">
|
||||
<ops-icon type="shishizhuangtai" />{{ $t('cmdb.ci.rollback') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
<ci-rollback-form ref="ciRollbackForm" :ciIds="[ciId]" @getCIHistory="getCIHistory" />
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
:data="ciHistory"
|
||||
size="small"
|
||||
height="auto"
|
||||
:height="tableHeight"
|
||||
highlight-hover-row
|
||||
:span-method="mergeRowMethod"
|
||||
:scroll-y="{ enabled: false, gt: 20 }"
|
||||
:scroll-x="{ enabled: false, gt: 0 }"
|
||||
border
|
||||
:scroll-y="{ enabled: false }"
|
||||
class="ops-stripe-table"
|
||||
resizable
|
||||
class="ops-unstripe-table"
|
||||
>
|
||||
<template #empty>
|
||||
<a-empty :image-style="{ height: '100px' }" :style="{ paddingTop: '10%' }">
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> {{ $t('noData') }} </span>
|
||||
</a-empty>
|
||||
</template>
|
||||
<vxe-table-column sortable field="created_at" :title="$t('created_at')"></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="username"
|
||||
@@ -56,7 +73,7 @@
|
||||
:filters="[
|
||||
{ value: 0, label: $t('new') },
|
||||
{ value: 1, label: $t('delete') },
|
||||
{ value: 3, label: $t('update') },
|
||||
{ value: 2, label: $t('update') },
|
||||
]"
|
||||
:filter-method="filterOperateMethod"
|
||||
:title="$t('operation')"
|
||||
@@ -71,8 +88,18 @@
|
||||
:filters="[]"
|
||||
:filter-method="filterAttrMethod"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column field="old" :title="$t('cmdb.history.old')"></vxe-table-column>
|
||||
<vxe-table-column field="new" :title="$t('cmdb.history.new')"></vxe-table-column>
|
||||
<vxe-table-column field="old" :title="$t('cmdb.history.old')">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.value_type === '6'">{{ JSON.parse(row.old) }}</span>
|
||||
<span v-else>{{ row.old }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="new" :title="$t('cmdb.history.new')">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.value_type === '6'">{{ JSON.parse(row.new) }}</span>
|
||||
<span v-else>{{ row.new }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
@@ -82,6 +109,12 @@
|
||||
<TriggerTable :ci_id="ci._id" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_5">
|
||||
<span slot="tab"><ops-icon type="itsm-association" />{{ $t('cmdb.ci.relITSM') }}</span>
|
||||
<div :style="{ padding: '24px', height: '100%' }">
|
||||
<related-itsm-table :ci_id="ci._id" :ciHistory="ciHistory" :itsmInstalled="itsmInstalled" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-empty
|
||||
v-else
|
||||
@@ -100,11 +133,15 @@
|
||||
import _ from 'lodash'
|
||||
import { Descriptions, DescriptionsItem } from 'element-ui'
|
||||
import { getCITypeGroupById, getCITypes } from '@/modules/cmdb/api/CIType'
|
||||
import { getCIHistory } from '@/modules/cmdb/api/history'
|
||||
import { getCIHistory, judgeItsmInstalled } from '@/modules/cmdb/api/history'
|
||||
import { getCIById } from '@/modules/cmdb/api/ci'
|
||||
import CiDetailAttrContent from './ciDetailAttrContent.vue'
|
||||
import CiDetailRelation from './ciDetailRelation.vue'
|
||||
import TriggerTable from '../../operation_history/modules/triggerTable.vue'
|
||||
import RelatedItsmTable from './ciDetailRelatedItsmTable.vue'
|
||||
import CiRollbackForm from './ciRollbackForm.vue'
|
||||
import { sleep } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'CiDetailTab',
|
||||
components: {
|
||||
@@ -113,6 +150,8 @@ export default {
|
||||
CiDetailAttrContent,
|
||||
CiDetailRelation,
|
||||
TriggerTable,
|
||||
RelatedItsmTable,
|
||||
CiRollbackForm,
|
||||
},
|
||||
props: {
|
||||
typeId: {
|
||||
@@ -123,10 +162,15 @@ export default {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
attributeHistoryTableHeight: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ci: {},
|
||||
item: [],
|
||||
attributeGroups: [],
|
||||
activeTabKey: 'tab_1',
|
||||
rowSpanMap: {},
|
||||
@@ -134,6 +178,8 @@ export default {
|
||||
ciId: null,
|
||||
ci_types: [],
|
||||
hasPermission: true,
|
||||
itsmInstalled: true,
|
||||
tableHeight: this.attributeHistoryTableHeight || (this.$store.state.windowHeight - 120),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -179,6 +225,7 @@ export default {
|
||||
}
|
||||
this.ciId = ciId
|
||||
await this.getCI()
|
||||
await this.judgeItsmInstalled()
|
||||
if (this.hasPermission) {
|
||||
this.getAttributes()
|
||||
this.getCIHistory()
|
||||
@@ -203,7 +250,16 @@ export default {
|
||||
this.hasPermission = false
|
||||
}
|
||||
})
|
||||
.catch((e) => {})
|
||||
.catch((e) => {
|
||||
if (e.response.status === 404) {
|
||||
this.itsmInstalled = false
|
||||
}
|
||||
})
|
||||
},
|
||||
async judgeItsmInstalled() {
|
||||
await judgeItsmInstalled().catch((e) => {
|
||||
this.itsmInstalled = false
|
||||
})
|
||||
},
|
||||
|
||||
getCIHistory() {
|
||||
@@ -343,6 +399,11 @@ export default {
|
||||
this.$message.error(this.$t('cmdb.ci.copyFailed'))
|
||||
})
|
||||
},
|
||||
handleRollbackCI() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ciRollbackForm.onOpen()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
165
cmdb-ui/src/modules/cmdb/views/ci/modules/ciRollbackForm.vue
Normal file
165
cmdb-ui/src/modules/cmdb/views/ci/modules/ciRollbackForm.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<CustomDrawer
|
||||
:closable="true"
|
||||
:title="drawerTitle"
|
||||
:visible="drawerVisible"
|
||||
@close="onClose"
|
||||
placement="right"
|
||||
width="800"
|
||||
:bodyStyle="{ paddingTop: 0 }"
|
||||
>
|
||||
<div class="custom-drawer-bottom-action">
|
||||
<a-button @click="onClose">{{ $t('cancel') }}</a-button>
|
||||
<a-button type="primary" @click="handleSubmit" :loading="loading" :disabled="!hasDiff">{{
|
||||
$t('submit')
|
||||
}}</a-button>
|
||||
</div>
|
||||
<a-form :form="form" :style="{ paddingTop: '20px' }">
|
||||
<a-form-item :label="$t('cmdb.ci.rollbackTo')" required :help="$t('cmdb.ci.baselineTips')">
|
||||
<a-date-picker
|
||||
:style="{ width: '278px' }"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
@ok="getBaselineDiff"
|
||||
:show-time="{ format: 'HH:mm:ss' }"
|
||||
:placeholder="$t('cmdb.ci.rollbackToTips')"
|
||||
v-decorator="['before_date', { rules: [{ required: true, message: $t('cmdb.ci.rollbackToTips') }] }]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<span :style="{ fontWeight: 'bold' }">{{ $t('cmdb.ci.baselineDiff') }}</span>
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
resizable
|
||||
border
|
||||
size="small"
|
||||
:span-method="mergeRowMethod"
|
||||
:data="tableData"
|
||||
:scroll-y="{ enabled: false, gt: 20 }"
|
||||
:scroll-x="{ enabled: false, gt: 0 }"
|
||||
class="ops-unstripe-table"
|
||||
>
|
||||
<template #empty>
|
||||
<a-empty :image-style="{ height: '100px' }" :style="{ paddingTop: '10%' }">
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> {{ dataLoad }} </span>
|
||||
</a-empty>
|
||||
</template>
|
||||
<vxe-column field="instance" min-width="80" :title="$t('cmdb.ci.instance')"> </vxe-column>
|
||||
<vxe-column field="attr_name" min-width="80" :title="$t('cmdb.attribute')"> </vxe-column>
|
||||
<vxe-column field="cur" min-width="80" :title="$t('cmdb.ci.rollbackBefore')">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.value_type === '6'">{{ JSON.stringify(row.cur) }}</span>
|
||||
<span v-else>{{ row.cur }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="to" min-width="80" :title="$t('cmdb.ci.rollbackAfter')">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.value_type === '6'">{{ JSON.stringify(row.to) }}</span>
|
||||
<span v-else>{{ row.to }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</a-form>
|
||||
</CustomDrawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCIsBaseline, CIBaselineRollback } from '../../../api/history'
|
||||
export default {
|
||||
name: 'CiRollbackForm',
|
||||
props: {
|
||||
ciIds: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: this.$form.createForm(this),
|
||||
drawerTitle: this.$t('cmdb.ci.rollbackHeader'),
|
||||
drawerVisible: false,
|
||||
|
||||
formLayout: 'horizontal',
|
||||
|
||||
tableData: [],
|
||||
dataLoad: this.$t('noData'),
|
||||
loading: false,
|
||||
hasDiff: false,
|
||||
batched: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClose() {
|
||||
this.drawerVisible = false
|
||||
this.form.resetFields()
|
||||
this.tableData = []
|
||||
this.dataLoad = this.$t('noData')
|
||||
},
|
||||
onOpen(batched = false) {
|
||||
this.drawerTitle = this.$t('cmdb.ci.rollbackHeader')
|
||||
this.drawerVisible = true
|
||||
this.batched = batched
|
||||
},
|
||||
handleSubmit() {
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
const that = this
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: that.$t('cmdb.ci.rollbackConfirm'),
|
||||
onOk() {
|
||||
if (that.batched) {
|
||||
that.$emit('batchRollbackAsync', values)
|
||||
} else {
|
||||
that.rollbackCI(values)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
rollbackCI(params) {
|
||||
CIBaselineRollback(this.ciIds[0], params).then((res) => {
|
||||
this.$message.success(this.$t('cmdb.ci.rollbackSuccess'))
|
||||
this.form.resetFields()
|
||||
this.$emit('getCIHistory')
|
||||
})
|
||||
},
|
||||
getBaselineDiff(value) {
|
||||
this.dataLoad = 'loading...'
|
||||
this.loading = true
|
||||
this.hasDiff = false
|
||||
getCIsBaseline({ ci_ids: this.ciIds.join(','), before_date: value }).then((res) => {
|
||||
this.tableData = res
|
||||
this.loading = false
|
||||
if (!res.length) {
|
||||
this.dataLoad = this.$t('cmdb.ci.noDiff', { baseline: value })
|
||||
} else {
|
||||
this.hasDiff = true
|
||||
}
|
||||
})
|
||||
},
|
||||
mergeRowMethod({ row, _rowIndex, column, visibleData }) {
|
||||
const fields = ['instance']
|
||||
const cellValue1 = row.instance
|
||||
if (cellValue1 && fields.includes(column.property)) {
|
||||
const prevRow = visibleData[_rowIndex - 1]
|
||||
let nextRow = visibleData[_rowIndex + 1]
|
||||
if (prevRow && prevRow.instance === cellValue1) {
|
||||
return { rowspan: 0, colspan: 0 }
|
||||
} else {
|
||||
let countRowspan = 1
|
||||
while (nextRow && nextRow.instance === cellValue1) {
|
||||
nextRow = visibleData[++countRowspan + _rowIndex]
|
||||
}
|
||||
if (countRowspan > 1) {
|
||||
return { rowspan: countRowspan, colspan: 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@@ -50,7 +50,6 @@
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="plus"
|
||||
class="ops-button-primary"
|
||||
>
|
||||
{{ $t('add') }}
|
||||
</a-button>
|
||||
@@ -214,7 +213,7 @@ export default {
|
||||
line-height: 32px;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid #custom_colors[color_1];
|
||||
border-left: 4px solid @primary-color;
|
||||
font-size: 16px;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
@@ -171,7 +171,7 @@ export default {
|
||||
margin-right: 60px;
|
||||
.ant-input-group.ant-input-group-compact > *:first-child,
|
||||
.ant-input-group.ant-input-group-compact > .ant-select:first-child > .ant-select-selection {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
@@ -223,10 +223,10 @@
|
||||
<a-form-item :label="$t('cmdb.ciType.isInherit')">
|
||||
<a-radio-group v-model="isInherit">
|
||||
<a-radio :value="true">
|
||||
是
|
||||
{{ $t('yes') }}
|
||||
</a-radio>
|
||||
<a-radio :value="false">
|
||||
否
|
||||
{{ $t('no') }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
@@ -6,7 +6,6 @@
|
||||
@click="handleCreate"
|
||||
type="primary"
|
||||
size="small"
|
||||
class="ops-button-primary"
|
||||
icon="plus"
|
||||
>{{ $t('cmdb.ciType.addRelation') }}</a-button
|
||||
>
|
||||
@@ -18,7 +17,6 @@
|
||||
show-header-overflow
|
||||
highlight-hover-row
|
||||
keep-source
|
||||
:height="windowHeight - 190"
|
||||
class="ops-stripe-table"
|
||||
:row-class-name="rowClass"
|
||||
:edit-config="{ trigger: 'dblclick', mode: 'cell', showIcon: false }"
|
||||
@@ -38,9 +36,9 @@
|
||||
<template #default="{row}">
|
||||
<span v-if="row.isParent && constraintMap[row.constraint]">{{
|
||||
constraintMap[row.constraint]
|
||||
.split('')
|
||||
.split(' ')
|
||||
.reverse()
|
||||
.join('')
|
||||
.join(' ')
|
||||
}}</span>
|
||||
<span v-else>{{ constraintMap[row.constraint] }}</span>
|
||||
</template>
|
||||
|
@@ -157,7 +157,6 @@
|
||||
"
|
||||
type="primary"
|
||||
size="small"
|
||||
class="ops-button-primary"
|
||||
>{{ `${showCustomEmail ? $t('delete') : $t('add')}` }}{{ $t('cmdb.ciType.customEmail') }}</a-button
|
||||
>
|
||||
</div>
|
||||
|
@@ -5,7 +5,6 @@
|
||||
type="primary"
|
||||
@click="handleAddTrigger"
|
||||
size="small"
|
||||
class="ops-button-primary"
|
||||
icon="plus"
|
||||
>{{ $t('cmdb.ciType.newTrigger') }}</a-button
|
||||
>
|
||||
|
@@ -309,7 +309,4 @@ export default {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.ops-button-primary:hover {
|
||||
background-color: #2f54eb !important;
|
||||
}
|
||||
</style>
|
||||
|
@@ -153,7 +153,7 @@ export default {
|
||||
line-height: 32px;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid #custom_colors[color_1];
|
||||
border-left: 4px solid @primary-color;
|
||||
justify-content: space-between;
|
||||
> div {
|
||||
font-weight: bold;
|
||||
|
@@ -5,10 +5,16 @@
|
||||
show-header-overflow
|
||||
stripe
|
||||
size="small"
|
||||
class="ops-stripe-table"
|
||||
class="ops-unstripe-table"
|
||||
:data="tableData"
|
||||
v-bind="ci_id ? { height: 'auto' } : { height: `${windowHeight - 225}px` }"
|
||||
>
|
||||
<template #empty>
|
||||
<a-empty :image-style="{ height: '100px' }" :style="{ paddingTop: '10%' }">
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> {{ $t('noData') }} </span>
|
||||
</a-empty>
|
||||
</template>
|
||||
<vxe-column field="trigger_name" :title="$t('cmdb.history.triggerName')"> </vxe-column>
|
||||
<vxe-column field="type" :title="$t('type')">
|
||||
<template #default="{ row }">
|
||||
|
@@ -494,7 +494,7 @@ export default {
|
||||
.cmdb-preference-group-content-action {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
@@ -592,7 +592,7 @@ export default {
|
||||
.cmdb-preference-footor-unsubscribed {
|
||||
text-align: center;
|
||||
> span {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -605,7 +605,7 @@ export default {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
> span:nth-child(2) {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
:triggerLength="18"
|
||||
>
|
||||
<template #one>
|
||||
<div class="relation-views-left" :style="{ height: `${windowHeight - 115}px` }">
|
||||
<div class="relation-views-left" :style="{ height: `${windowHeight - 64}px` }">
|
||||
<div class="relation-views-left-header" :title="$route.meta.name">{{ $route.meta.name }}</div>
|
||||
<a-input
|
||||
:placeholder="$t('cmdb.serviceTree.searchTips')"
|
||||
@@ -607,7 +607,7 @@ export default {
|
||||
this.reload()
|
||||
},
|
||||
pageNo: function(newPage, oldPage) {
|
||||
this.loadData({ params: { pageNo: newPage }, refreshType: undefined, sortByTable: this.sortByTable })
|
||||
this.loadData({ parameter: { pageNo: newPage }, refreshType: undefined, sortByTable: this.sortByTable })
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1193,7 +1193,7 @@ export default {
|
||||
that.$refs.xTable.clearCheckboxRow()
|
||||
that.$refs.xTable.clearCheckboxReserve()
|
||||
that.selectedRowKeys = []
|
||||
that.loadData({ params: {}, refreshType: 'refreshNumber' })
|
||||
that.loadData({ parameter: {}, refreshType: 'refreshNumber' })
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1241,7 +1241,7 @@ export default {
|
||||
this.sortByTable = sortByTable
|
||||
this.$nextTick(() => {
|
||||
if (this.pageNo === 1) {
|
||||
this.loadData({ params: {}, refreshType: undefined, sortByTable })
|
||||
this.loadData({ parameter: {}, refreshType: undefined, sortByTable })
|
||||
} else {
|
||||
this.pageNo = 1
|
||||
}
|
||||
@@ -1400,7 +1400,7 @@ export default {
|
||||
onOk() {
|
||||
deleteCI(record.ci_id || record._id).then((res) => {
|
||||
that.$message.success(that.$t('deleteSuccess'))
|
||||
that.loadData({ params: {}, refreshType: 'refreshNumber' })
|
||||
that.loadData({ parameter: {}, refreshType: 'refreshNumber' })
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1420,7 +1420,7 @@ export default {
|
||||
}
|
||||
addCIRelationView(first_ci_id, ci_id, { ancestor_ids }).then((res) => {
|
||||
setTimeout(() => {
|
||||
this.loadData({ params: {}, refreshType: 'refreshNumber' })
|
||||
this.loadData({ parameter: {}, refreshType: 'refreshNumber' })
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
@@ -1458,7 +1458,7 @@ export default {
|
||||
.finally(() => {
|
||||
that.loading = false
|
||||
setTimeout(() => {
|
||||
that.loadData({ params: {} })
|
||||
that.loadData({ parameter: {} })
|
||||
}, 800)
|
||||
})
|
||||
},
|
||||
@@ -1521,7 +1521,7 @@ export default {
|
||||
that.selectedRowKeys = []
|
||||
that.$refs.xTable.clearCheckboxRow()
|
||||
that.$refs.xTable.clearCheckboxReserve()
|
||||
that.loadData({ params: {}, refreshType: 'refreshNumber' })
|
||||
that.loadData({ parameter: {}, refreshType: 'refreshNumber' })
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1539,7 +1539,7 @@ export default {
|
||||
this.$refs.xTable.refreshColumn()
|
||||
},
|
||||
relationViewRefreshNumber() {
|
||||
this.loadData({ params: {}, refreshType: 'refreshNumber' })
|
||||
this.loadData({ parameter: {}, refreshType: 'refreshNumber' })
|
||||
},
|
||||
onShowSizeChange(current, pageSize) {
|
||||
this.pageSize = pageSize
|
||||
@@ -1762,12 +1762,29 @@ export default {
|
||||
}
|
||||
if (node.children) {
|
||||
node.children = node.children.filter((child) => {
|
||||
if (predicateCiIds.some((id) => child.key.includes(String(id)))) {
|
||||
if (
|
||||
predicateCiIds.some(
|
||||
(id) =>
|
||||
child.key
|
||||
.split('@^@')
|
||||
.map((item) => Number(item.split('%')[0]))
|
||||
.indexOf(id) > -1
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return filterTree(child, predicate)
|
||||
})
|
||||
if (node.children.length && !predicateCiIds.some((id) => node.key.includes(String(id)))) {
|
||||
if (
|
||||
node.children.length &&
|
||||
!predicateCiIds.some(
|
||||
(id) =>
|
||||
node.key
|
||||
.split('@^@')
|
||||
.map((item) => Number(item.split('%')[0]))
|
||||
.indexOf(id) > -1
|
||||
)
|
||||
) {
|
||||
_expandedKeys.push(node.key)
|
||||
}
|
||||
return node.children.length > 0
|
||||
|
@@ -57,47 +57,37 @@ export const generatorDynamicRouter = async () => {
|
||||
{
|
||||
path: '/setting/companyinfo',
|
||||
name: 'company_info',
|
||||
meta: { title: 'cs.menu.companyInfo', appName: 'backend', icon: 'ops-setting-companyInfo', selectedIcon: 'ops-setting-companyInfo-selected', permission: ['公司信息', 'backend_admin'] },
|
||||
meta: { title: 'cs.menu.companyInfo', appName: 'backend', icon: 'ops-setting-companyInfo', selectedIcon: 'ops-setting-companyInfo', permission: ['公司信息', 'backend_admin'] },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/companyInfo/index')
|
||||
},
|
||||
{
|
||||
path: '/setting/companystructure',
|
||||
name: 'company_structure',
|
||||
meta: { title: 'cs.menu.companyStructure', appName: 'backend', icon: 'ops-setting-companyStructure', selectedIcon: 'ops-setting-companyStructure-selected', permission: ['公司架构', 'backend_admin'] },
|
||||
meta: { title: 'cs.menu.companyStructure', appName: 'backend', icon: 'ops-setting-companyStructure', selectedIcon: 'ops-setting-companyStructure', permission: ['公司架构', 'backend_admin'] },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/companyStructure/index')
|
||||
},
|
||||
{
|
||||
path: '/setting/notice',
|
||||
name: 'notice',
|
||||
component: RouteView,
|
||||
meta: { title: 'cs.menu.notice', appName: 'backend', icon: 'ops-setting-notice', selectedIcon: 'ops-setting-notice-selected', permission: ['通知设置', 'backend_admin'] },
|
||||
meta: { title: 'cs.menu.notice', appName: 'backend', icon: 'ops-setting-notice', selectedIcon: 'ops-setting-notice', permission: ['通知设置', 'backend_admin'] },
|
||||
redirect: '/setting/notice/email',
|
||||
children: [{
|
||||
path: '/setting/notice/basic',
|
||||
name: 'notice_basic',
|
||||
meta: { title: 'cs.menu.basic', icon: 'ops-setting-basic', selectedIcon: 'ops-setting-basic-selected' },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/basic')
|
||||
}, {
|
||||
path: '/setting/notice/email',
|
||||
name: 'notice_email',
|
||||
meta: { title: 'cs.menu.email', icon: 'ops-setting-notice-email', selectedIcon: 'ops-setting-notice-email-selected' },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/email/index')
|
||||
}, {
|
||||
path: '/setting/notice/wx',
|
||||
name: 'notice_wx',
|
||||
meta: { title: 'cs.menu.wx', icon: 'ops-setting-notice-wx', selectedIcon: 'ops-setting-notice-wx-selected' },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/wx')
|
||||
}, {
|
||||
path: '/setting/notice/dingding',
|
||||
name: 'notice_dingding',
|
||||
meta: { title: 'cs.menu.dingding', icon: 'ops-setting-notice-dingding', selectedIcon: 'ops-setting-notice-dingding-selected' },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/dingding')
|
||||
}, {
|
||||
path: '/setting/notice/feishu',
|
||||
name: 'notice_feishu',
|
||||
meta: { title: 'cs.menu.feishu', icon: 'ops-setting-notice-feishu', selectedIcon: 'ops-setting-notice-feishu-selected' },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/feishu')
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/setting/auth',
|
||||
name: 'company_auth',
|
||||
meta: { title: 'cs.menu.auth', appName: 'backend', icon: 'ops-setting-auth', selectedIcon: 'ops-setting-auth-selected', permission: ['acl_admin'] },
|
||||
meta: { title: 'cs.menu.auth', appName: 'backend', icon: 'ops-setting-auth', selectedIcon: 'ops-setting-auth', permission: ['acl_admin'] },
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/auth/index')
|
||||
},
|
||||
]
|
||||
|
@@ -1152,13 +1152,6 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
// button
|
||||
.ops-button-primary {
|
||||
background-color: @primary-color_4;
|
||||
border-color: @primary-color_4;
|
||||
color: @primary-color;
|
||||
box-shadow: none;
|
||||
}
|
||||
// button
|
||||
.ops-button-ghost.ant-btn-background-ghost.ant-btn-primary {
|
||||
background-color: @primary-color_5!important;
|
||||
|
@@ -46,11 +46,6 @@
|
||||
@layout-sidebar-selected-font-color: @primary-color;
|
||||
@layout-sidebar-disabled-font-color: @text-color_4;
|
||||
|
||||
#custom_colors() {
|
||||
color_1: #2f54eb; //primary color
|
||||
color_2: #f0f5ff; //light background color
|
||||
color_3: #d2e2ff;
|
||||
}
|
||||
|
||||
.ops_display_wrapper(@backgroundColor:@primary-color_5) {
|
||||
cursor: pointer;
|
||||
|
@@ -309,10 +309,10 @@ export default {
|
||||
.notice-center-left:hover,
|
||||
.notice-center-left-select {
|
||||
background-color: #f0f5ff;
|
||||
border-color: #custom_colors[color_1];
|
||||
border-color: @primary-color;
|
||||
> span:nth-child(2) {
|
||||
background-color: #fff;
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.notice-center-header {
|
||||
@@ -329,7 +329,7 @@ export default {
|
||||
}
|
||||
> .notice-center-header-app:hover,
|
||||
.notice-center-header-app-selected {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
.notice-center-categories {
|
||||
@@ -342,7 +342,7 @@ export default {
|
||||
> span:hover,
|
||||
.notice-center-categories-selected {
|
||||
color: #fff;
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,34 @@
|
||||
<template>
|
||||
<a-tabs type="card" class="ops-tab" v-model="activeKey" @change="changeActiveKey">
|
||||
<a-tab-pane v-for="item in authList" :key="item.value">
|
||||
<span slot="tab">
|
||||
{{ item.label }}
|
||||
<a-icon
|
||||
v-if="enable_list.find((en) => en.auth_type === item.value)"
|
||||
type="check-circle"
|
||||
theme="filled"
|
||||
style="color:#2f54eb"
|
||||
/>
|
||||
</span>
|
||||
<div class="setting-auth">
|
||||
<components :ref="item.value" :is="item.value === 'OIDC' ? 'OAUTH2' : item.value" :data_type="item.value" />
|
||||
<a-row>
|
||||
<a-col :offset="item.value === 'AuthCommonConfig' ? 5 : 3">
|
||||
<a-space>
|
||||
<a-button :loading="loading" type="primary" @click="handleSave">{{ $t('save') }}</a-button>
|
||||
<template v-if="item.value === 'LDAP'">
|
||||
<a-button :loading="loading" ghost type="primary" @click="handleTest('connect')">{{
|
||||
$t('cs.auth.testConnect')
|
||||
}}</a-button>
|
||||
<a-button :loading="loading" ghost type="primary" @click="handleTest('login')">{{
|
||||
$t('cs.auth.testLogin')
|
||||
}}</a-button>
|
||||
</template>
|
||||
<a-button :loading="loading" @click="handleReset">{{ $t('reset') }}</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<LoginModal v-if="item.value === 'LDAP'" ref="loginModal" @handleOK="(values) => handleTest('login', values)" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div class="ops-setting-auth">
|
||||
<a-tabs class="ops-tab" v-model="activeKey" @change="changeActiveKey">
|
||||
<a-tab-pane v-for="item in authList" :key="item.value">
|
||||
<span slot="tab">
|
||||
{{ item.label }}
|
||||
<a-icon v-if="enable_list.find((en) => en.auth_type === item.value)" type="check-circle" theme="filled" />
|
||||
</span>
|
||||
<div class="setting-auth">
|
||||
<components :ref="item.value" :is="item.value === 'OIDC' ? 'OAUTH2' : item.value" :data_type="item.value" />
|
||||
<a-row>
|
||||
<a-col :offset="item.value === 'AuthCommonConfig' ? 5 : 3">
|
||||
<a-space>
|
||||
<a-button :loading="loading" type="primary" @click="handleSave">{{ $t('save') }}</a-button>
|
||||
<template v-if="item.value === 'LDAP'">
|
||||
<a-button :loading="loading" ghost type="primary" @click="handleTest('connect')">{{
|
||||
$t('cs.auth.testConnect')
|
||||
}}</a-button>
|
||||
<a-button :loading="loading" ghost type="primary" @click="handleTest('login')">{{
|
||||
$t('cs.auth.testLogin')
|
||||
}}</a-button>
|
||||
</template>
|
||||
<a-button :loading="loading" @click="handleReset">{{ $t('reset') }}</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<LoginModal v-if="item.value === 'LDAP'" ref="loginModal" @handleOK="(values) => handleTest('login', values)" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -148,12 +145,20 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ops-setting-auth {
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
background-color: #fff;
|
||||
border-radius: @border-radius-box;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
.setting-auth {
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 128px);
|
||||
height: calc(100vh - 150px);
|
||||
overflow: auto;
|
||||
border-radius: 0 5px 5px 5px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -161,10 +166,10 @@ export default {
|
||||
.setting-auth {
|
||||
.jsoneditor-outer {
|
||||
height: var(--custom-height) !important;
|
||||
border: 1px solid #2f54eb;
|
||||
border: 1px solid @primary-color;
|
||||
}
|
||||
div.jsoneditor-menu {
|
||||
background-color: #2f54eb;
|
||||
background-color: @primary-color;
|
||||
}
|
||||
.jsoneditor-modes {
|
||||
display: none;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="ops-setting-companyinfo" :style="{ height: `${windowHeight - 64}px` }">
|
||||
<div class="ops-setting-companyinfo">
|
||||
<a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rule">
|
||||
<SpanTitle>{{ $t('cs.companyInfo.spanCompany') }}</SpanTitle>
|
||||
<a-form-model-item :label="$t('cs.companyInfo.name')" prop="name">
|
||||
@@ -35,9 +35,6 @@
|
||||
<a-input v-model="infoData.email" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<SpanTitle>{{ $t('cs.companyInfo.spanLogo') }}</SpanTitle>
|
||||
<a-form-model-item :label="$t('cs.companyInfo.messenger')" prop="messenger">
|
||||
<a-input v-model="infoData.messenger" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.companyInfo.domainName')" prop="domainName">
|
||||
<a-input v-model="infoData.domainName" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
@@ -51,7 +48,6 @@
|
||||
|
||||
<script>
|
||||
import { getCompanyInfo, postCompanyInfo, putCompanyInfo } from '@/api/company'
|
||||
import { mapState } from 'vuex'
|
||||
import SpanTitle from '../components/spanTitle.vue'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
|
||||
@@ -74,7 +70,6 @@ export default {
|
||||
phone: '',
|
||||
faxCode: '',
|
||||
email: '',
|
||||
messenger: '',
|
||||
domainName: '',
|
||||
},
|
||||
getId: -1,
|
||||
@@ -90,9 +85,6 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '公司信息', ['update'])
|
||||
},
|
||||
@@ -157,7 +149,6 @@ export default {
|
||||
phone: '',
|
||||
faxCode: '',
|
||||
email: '',
|
||||
messenger: '',
|
||||
domainName: '',
|
||||
}
|
||||
},
|
||||
@@ -167,56 +158,15 @@ export default {
|
||||
|
||||
<style lang="less">
|
||||
.ops-setting-companyinfo {
|
||||
padding-top: 15px;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: @border-radius-box;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
.ops-setting-companyinfo-upload-show {
|
||||
position: relative;
|
||||
width: 290px;
|
||||
height: 100px;
|
||||
max-height: 100px;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
height: calc(100vh - 64px);
|
||||
|
||||
.delete-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ant-upload:hover .delete-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
color: rgb(247, 85, 85);
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ant-form-item label {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.avatar-uploader > .ant-upload {
|
||||
// max-width: 100px;
|
||||
max-height: 100px;
|
||||
}
|
||||
// .ant-upload.ant-upload-select-picture-card {
|
||||
// width: 100%;
|
||||
// > .ant-upload {
|
||||
// padding: 0px;
|
||||
.ant-upload-picture-card-wrapper {
|
||||
height: 100px;
|
||||
.ant-upload.ant-upload-select-picture-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
> .ant-upload {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -173,9 +173,9 @@ li {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
font-size: 14px;
|
||||
.ops-setting-companystructure-sidebar-group-tree-info:hover {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
> .ops-setting-companystructure-sidebar-group-tree-info::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
// .ops-setting-companystructure-sidebar-group-tree-info:first-child::before {
|
||||
@@ -313,7 +313,7 @@ li {
|
||||
}
|
||||
}
|
||||
> .ops-setting-companystructure-sidebar-group-tree-info::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@
|
||||
:max="500"
|
||||
:paneLengthPixel.sync="paneLengthPixel"
|
||||
appName="setting-structure"
|
||||
triggerColor="#F0F5FF"
|
||||
:triggerLength="18"
|
||||
>
|
||||
<template #one>
|
||||
@@ -396,11 +395,6 @@ export default {
|
||||
title: this.$t('cs.companyStructure.employee'),
|
||||
icon: 'user',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: this.$t('cs.companyStructure.departmentName'),
|
||||
icon: 'apartment',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -884,7 +878,7 @@ export default {
|
||||
&:hover {
|
||||
background-color: #e1efff;
|
||||
.ops-setting-structure-sidebar-group-header-title {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.ops-setting-structure-sidebar-group-header-avatar {
|
||||
@@ -925,9 +919,9 @@ export default {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
&:hover {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
&::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
&::before {
|
||||
@@ -1004,19 +998,19 @@ export default {
|
||||
}
|
||||
}
|
||||
.item-selected {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
&::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.group-selected {
|
||||
background-color: #e1efff;
|
||||
.ops-setting-structure-sidebar-group-header-avatar {
|
||||
background-color: #custom_colors[color_1];
|
||||
background-color: @primary-color;
|
||||
}
|
||||
.ops-setting-structure-sidebar-group-header-title {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1042,7 +1036,7 @@ export default {
|
||||
display: inline-block;
|
||||
.ops_display_wrapper(#fff);
|
||||
.screening-box-scene-icon {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
font-size: 12px;
|
||||
}
|
||||
.history-scene-item {
|
||||
@@ -1054,7 +1048,7 @@ export default {
|
||||
.search-form-bar-filter {
|
||||
.ops_display_wrapper();
|
||||
.search-form-bar-filter-icon {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
@@ -68,7 +68,7 @@ name: 'SearchForm',
|
||||
background-color: rgb(240, 245, 255);
|
||||
.ops_display_wrapper();
|
||||
.search-form-bar-filter-icon {
|
||||
color: #custom_colors[color_1];
|
||||
color: @primary-color;
|
||||
font-size: 12px;
|
||||
}
|
||||
.search-form-bar-filter-icon_selected{
|
||||
|
@@ -23,10 +23,9 @@ export default {
|
||||
height: 28px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 28px;
|
||||
padding-left: 24px;
|
||||
border-radius: 0px 20px 20px 0px;
|
||||
padding-left: 12px;
|
||||
font-weight: 700;
|
||||
color: #0637bf;
|
||||
background-color: #e0e9ff;
|
||||
color: #000;
|
||||
border-left: 3px solid @primary-color;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,20 +1,25 @@
|
||||
const cs_en = {
|
||||
menu: {
|
||||
person: 'My Profile',
|
||||
companyInfo: 'Company Info',
|
||||
companyStructure: 'Company Structure',
|
||||
notice: 'Notification Settings',
|
||||
email: 'Email Settings',
|
||||
wx: 'WeChat Work',
|
||||
dingding: 'DingTalk',
|
||||
feishu: 'Feishu',
|
||||
auth: 'Auth Settings',
|
||||
role: 'Role Management',
|
||||
sys: 'System Role',
|
||||
technician: 'Technician Role',
|
||||
user: 'User Role',
|
||||
group: 'User Group',
|
||||
duty: 'Duty Management',
|
||||
menu: {
|
||||
person: 'My Profile',
|
||||
companyInfo: 'Company Info',
|
||||
companyStructure: 'Company Structure',
|
||||
notice: 'Notification Settings',
|
||||
email: 'Email Settings',
|
||||
wx: 'WeChat Work',
|
||||
dingding: 'DingTalk',
|
||||
feishu: 'Feishu',
|
||||
auth: 'Auth Settings',
|
||||
authority: 'Authority Management',
|
||||
sys: 'System Role',
|
||||
technician: 'Technician Role',
|
||||
user: 'User Role',
|
||||
group: 'User Group',
|
||||
duty: 'Duty Management',
|
||||
role: 'Role Management',
|
||||
app: 'APP Authority',
|
||||
basic: 'Basic Settings',
|
||||
theme: 'Theme Settings',
|
||||
security: 'Security Settings'
|
||||
},
|
||||
companyInfo: {
|
||||
spanCompany: 'Description',
|
||||
@@ -37,11 +42,6 @@ const cs_en = {
|
||||
emailValidate: 'Please enter a valid email address',
|
||||
messenger: 'Messenger Address',
|
||||
domainName: 'Deployment Domain',
|
||||
logo: 'Company Logo',
|
||||
upload: 'Upload',
|
||||
editCompanyLogo: 'Edit Company Logo',
|
||||
editCompanyLogoSmall: 'Edit Company Logo Thumbnail',
|
||||
imageSizeLimit2MB: 'Image size does not exceed 2MB',
|
||||
checkInputCorrect: 'Please check if the input content is correct',
|
||||
},
|
||||
companyStructure: {
|
||||
|
@@ -9,12 +9,17 @@ const cs_zh = {
|
||||
dingding: '钉钉',
|
||||
feishu: '飞书',
|
||||
auth: '认证设置',
|
||||
role: '角色管理',
|
||||
authority: '权限管理',
|
||||
sys: '系统角色',
|
||||
technician: '技术员角色',
|
||||
user: '用户角色',
|
||||
group: '用户分组',
|
||||
duty: '值班管理',
|
||||
role: '角色管理',
|
||||
app: '应用权限',
|
||||
basic: '基础设置',
|
||||
theme: '主题配置',
|
||||
security: '安全配置'
|
||||
},
|
||||
companyInfo: {
|
||||
spanCompany: '公司描述',
|
||||
@@ -37,11 +42,6 @@ const cs_zh = {
|
||||
emailValidate: '请输入正确的邮箱地址',
|
||||
messenger: 'Messenger地址',
|
||||
domainName: '部署域名',
|
||||
logo: '公司logo',
|
||||
upload: '上传',
|
||||
editCompanyLogo: '编辑公司logo',
|
||||
editCompanyLogoSmall: '编辑公司logo缩略图',
|
||||
imageSizeLimit2MB: '图片大小不超过2MB',
|
||||
checkInputCorrect: '请检查输入内容是否正确',
|
||||
},
|
||||
companyStructure: {
|
||||
|
80
cmdb-ui/src/views/setting/notice/basic.vue
Normal file
80
cmdb-ui/src/views/setting/notice/basic.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="ops-setting-notice-basic">
|
||||
<a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-form-model-item :label="$t('cs.companyInfo.messenger')" prop="messenger">
|
||||
<a-input v-model="infoData.messenger" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :wrapper-col="{ span: 14, offset: 3 }" v-if="isEditable">
|
||||
<a-button type="primary" @click="onSubmit"> {{ $t('save') }}</a-button>
|
||||
<a-button ghost type="primary" style="margin-left: 28px" @click="resetForm"> {{ $t('reset') }}</a-button>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCompanyInfo, postCompanyInfo, putCompanyInfo } from '@/api/company'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
|
||||
export default {
|
||||
name: 'CompanyInfo',
|
||||
mixins: [mixinPermissions],
|
||||
data() {
|
||||
return {
|
||||
labelCol: { span: 3 },
|
||||
wrapperCol: { span: 10 },
|
||||
infoData: {
|
||||
messenger: '',
|
||||
},
|
||||
getId: -1,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const res = await getCompanyInfo()
|
||||
if (!res.id) {
|
||||
this.getId = -1
|
||||
} else {
|
||||
this.infoData = res.info
|
||||
this.getId = res.id
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '公司信息', ['update'])
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
this.$refs.infoData.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (this.getId === -1) {
|
||||
await postCompanyInfo(this.infoData)
|
||||
} else {
|
||||
await putCompanyInfo(this.getId, this.infoData)
|
||||
}
|
||||
this.$message.success(this.$t('saveSuccess'))
|
||||
} else {
|
||||
this.$message.warning(this.$t('cs.companyInfo.checkInputCorrect'))
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.infoData = {
|
||||
messenger: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.ops-setting-notice-basic {
|
||||
padding-top: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: @border-radius-box;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
</style>
|
@@ -1,106 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
:data="tableData"
|
||||
size="mini"
|
||||
stripe
|
||||
class="ops-stripe-table"
|
||||
show-overflow
|
||||
:edit-config="{ showIcon: false, trigger: 'manual', mode: 'row' }"
|
||||
>
|
||||
<vxe-column v-for="col in columns" :key="col.field" :field="col.field" :title="col.title" :edit-render="{}">
|
||||
<template #header> <span v-if="col.required" :style="{ color: 'red' }">* </span>{{ col.title }} </template>
|
||||
<template #edit="{ row }">
|
||||
<vxe-input v-model="row[col.field]" type="text"></vxe-input>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column :title="$t('operation')" width="80" v-if="!disabled">
|
||||
<template #default="{ row }">
|
||||
<template v-if="$refs.xTable.isActiveByRow(row)">
|
||||
<a @click="saveRowEvent(row)"><a-icon type="save"/></a>
|
||||
</template>
|
||||
<a-space v-else>
|
||||
<a @click="editRowEvent(row)"><ops-icon type="icon-xianxing-edit"/></a>
|
||||
<a style="color:red" @click="deleteRowEvent(row)"><ops-icon type="icon-xianxing-delete"/></a>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<div :style="{ color: '#f5222d' }" v-if="errorFlag">{{ $t('cs.notice.robotConfigErrorTips') }}</div>
|
||||
<a-button v-if="!disabled" icon="plus-circle" class="ops-button-primary" type="primary" @click="insertEvent">{{
|
||||
$t('add')
|
||||
}}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Bot',
|
||||
props: {
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
errorFlag: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async insertEvent() {
|
||||
const $table = this.$refs.xTable
|
||||
const record = {
|
||||
name: '',
|
||||
url: '',
|
||||
}
|
||||
const { row: newRow } = await $table.insertAt(record, -1)
|
||||
await $table.setActiveRow(newRow)
|
||||
},
|
||||
saveRowEvent(row) {
|
||||
const $table = this.$refs.xTable
|
||||
$table.clearActived()
|
||||
},
|
||||
editRowEvent(row) {
|
||||
const $table = this.$refs.xTable
|
||||
$table.setActiveRow(row)
|
||||
},
|
||||
deleteRowEvent(row) {
|
||||
const $table = this.$refs.xTable
|
||||
$table.remove(row)
|
||||
},
|
||||
getData(callback) {
|
||||
const $table = this.$refs.xTable
|
||||
const { fullData: _tableData } = $table.getTableData()
|
||||
const requiredObj = {}
|
||||
this.columns.forEach((col) => {
|
||||
if (col.required) {
|
||||
requiredObj[col.field] = true
|
||||
}
|
||||
})
|
||||
let flag = true
|
||||
_tableData.forEach((td) => {
|
||||
Object.keys(requiredObj).forEach((key) => {
|
||||
if (requiredObj[key]) {
|
||||
flag = !!(flag && td[`${key}`])
|
||||
}
|
||||
})
|
||||
})
|
||||
this.errorFlag = !flag
|
||||
callback(flag, _tableData)
|
||||
},
|
||||
setData(value) {
|
||||
this.tableData = value
|
||||
this.errorFlag = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@@ -1,151 +0,0 @@
|
||||
<template>
|
||||
<div class="notice-dingding-wrapper" :style="{ height: `${windowHeight - 64}px` }">
|
||||
<a-form-model ref="dingdingForm" :model="dingdingData" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<SpanTitle>{{ $t('cs.duty.basicSetting') }}</SpanTitle>
|
||||
<a-form-model-item :label="$t('cs.notice.appKey')">
|
||||
<a-input v-model="dingdingData.appKey" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.appSecret')">
|
||||
<a-input v-model="dingdingData.appSecret" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.robotCode')">
|
||||
<a-input v-model="dingdingData.robotCode" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.robot')">
|
||||
<Bot
|
||||
ref="bot"
|
||||
:disabled="!isEditable"
|
||||
:columns="[
|
||||
{
|
||||
field: 'name',
|
||||
title: $t('cs.notice.title'),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
title: $t('cs.notice.webhookAddress'),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'token',
|
||||
title: 'token',
|
||||
required: false,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<!-- <a-form-model-item :label="测试邮件设置">
|
||||
<a-button type="primary" ghost>测试回收箱</a-button>
|
||||
<br />
|
||||
<span
|
||||
class="notice-dingding-wrapper-tips"
|
||||
><ops-icon type="icon-shidi-quxiao" :style="{ color: '#D81E06' }" /> 邮件接收失败</span
|
||||
>
|
||||
<br />
|
||||
<span>邮箱服务器未配置,请配置一个邮箱服务器 | <a>故障诊断</a></span>
|
||||
</a-form-model-item> -->
|
||||
<a-row v-if="isEditable">
|
||||
<a-col :span="16" :offset="3">
|
||||
<a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-button type="primary" @click="onSubmit"> {{ $t('save') }} </a-button>
|
||||
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> {{ $t('reset') }} </a-button>
|
||||
</a-form-model-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import SpanTitle from '../components/spanTitle.vue'
|
||||
import { getNoticeConfigByPlatform, postNoticeConfigByPlatform, putNoticeConfigByPlatform } from '@/api/noticeSetting'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
import Bot from './bot.vue'
|
||||
|
||||
export default {
|
||||
name: 'NoticeDingding',
|
||||
components: { SpanTitle, Bot },
|
||||
mixins: [mixinPermissions],
|
||||
data() {
|
||||
return {
|
||||
labelCol: { lg: 3, md: 5, sm: 8 },
|
||||
wrapperCol: { lg: 15, md: 19, sm: 16 },
|
||||
id: null,
|
||||
dingdingData: {
|
||||
appKey: '',
|
||||
appSecret: '',
|
||||
robotCode: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '通知设置', ['update'])
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
getNoticeConfigByPlatform({ platform: 'dingdingApp' }).then((res) => {
|
||||
this.id = res?.id ?? null
|
||||
if (this.id) {
|
||||
this.dingdingData = res.info
|
||||
this.$refs.bot.setData(res?.info?.bot)
|
||||
}
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.dingdingForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.$refs.bot.getData(async (flag, bot) => {
|
||||
if (flag) {
|
||||
if (this.id) {
|
||||
await putNoticeConfigByPlatform(this.id, { info: { ...this.dingdingData, bot, label: this.$t('cs.person.dingdingApp') } })
|
||||
} else {
|
||||
await postNoticeConfigByPlatform({
|
||||
platform: 'dingdingApp',
|
||||
info: { ...this.dingdingData, bot, label: this.$t('cs.person.dingdingApp') },
|
||||
})
|
||||
}
|
||||
this.$message.success(this.$t('saveSuccess'))
|
||||
this.getData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.dingdingData = {
|
||||
appKey: '',
|
||||
appSecret: '',
|
||||
robotCode: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.notice-dingding-wrapper {
|
||||
background-color: #fff;
|
||||
padding-top: 15px;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
border-radius: @border-radius-box;
|
||||
.notice-dingding-wrapper-tips {
|
||||
display: inline-block;
|
||||
background-color: #ffdfdf;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
width: 300px;
|
||||
color: #000000;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,6 +1,5 @@
|
||||
.notice-email-wrapper {
|
||||
background-color: #fff;
|
||||
padding-top: 24px;
|
||||
overflow: auto;
|
||||
.notice-email-error-tips {
|
||||
display: inline-block;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div :style="{ marginBottom: '-24px' }">
|
||||
<a-tabs :activeKey="activeKey" @change="changeTab" class="ops-tab" type="card">
|
||||
<div class="ops-setting-notice-mail">
|
||||
<a-tabs :activeKey="activeKey" @change="changeTab" class="ops-tab">
|
||||
<!-- <a-tab-pane key="1" tab="接收服务器">
|
||||
<Receive />
|
||||
</a-tab-pane> -->
|
||||
@@ -30,4 +30,14 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style lang="less" scoped>
|
||||
.ops-setting-notice-mail {
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
background-color: #fff;
|
||||
border-radius: @border-radius-box;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="notice-email-wrapper" :style="{ height: `${windowHeight - 104}px` }">
|
||||
<div class="notice-email-wrapper">
|
||||
<a-form-model ref="sendForm" :model="settingData" :label-col="labelCol" :rules="rules" :wrapper-col="wrapperCol">
|
||||
<SpanTitle>{{ $t('cs.duty.basicSetting') }}</SpanTitle>
|
||||
<a-form-model-item :label="$t('cs.notice.isEncrypted')">
|
||||
|
@@ -1,131 +0,0 @@
|
||||
<template>
|
||||
<div class="notice-feishu-wrapper" :style="{ height: `${windowHeight - 64}px` }">
|
||||
<a-form-model ref="feishuForm" :model="feishuData" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<SpanTitle>{{ $t('cs.duty.basicSetting') }}</SpanTitle>
|
||||
<a-form-model-item :label="$t('cs.notice.appKey')">
|
||||
<a-input v-model="feishuData.id" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.appSecret')">
|
||||
<a-input v-model="feishuData.password" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.robot')">
|
||||
<Bot
|
||||
ref="bot"
|
||||
:disabled="!isEditable"
|
||||
:columns="[
|
||||
{
|
||||
field: 'name',
|
||||
title: $t('cs.notice.title'),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
title: $t('cs.notice.webhookAddress'),
|
||||
required: true,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-row v-if="isEditable">
|
||||
<a-col :span="16" :offset="3">
|
||||
<a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-button type="primary" @click="onSubmit"> {{ $t('save') }} </a-button>
|
||||
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> {{ $t('reset') }} </a-button>
|
||||
</a-form-model-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import SpanTitle from '../components/spanTitle.vue'
|
||||
import { getNoticeConfigByPlatform, postNoticeConfigByPlatform, putNoticeConfigByPlatform } from '@/api/noticeSetting'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
import Bot from './bot.vue'
|
||||
|
||||
export default {
|
||||
name: 'NoticeFeishu',
|
||||
components: { SpanTitle, Bot },
|
||||
mixins: [mixinPermissions],
|
||||
data() {
|
||||
return {
|
||||
labelCol: { lg: 3, md: 5, sm: 8 },
|
||||
wrapperCol: { lg: 15, md: 19, sm: 16 },
|
||||
id: null,
|
||||
feishuData: {
|
||||
id: '',
|
||||
password: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '通知设置', ['update'])
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
getNoticeConfigByPlatform({ platform: 'feishuApp' }).then((res) => {
|
||||
this.id = res?.id ?? null
|
||||
if (this.id) {
|
||||
this.feishuData = res.info
|
||||
this.$refs.bot.setData(res?.info?.bot)
|
||||
}
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.feishuForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.$refs.bot.getData(async (flag, bot) => {
|
||||
if (flag) {
|
||||
if (this.id) {
|
||||
await putNoticeConfigByPlatform(this.id, { info: { ...this.feishuData, bot, label: this.$t('cs.person.feishuApp') } })
|
||||
} else {
|
||||
await postNoticeConfigByPlatform({
|
||||
platform: 'feishuApp',
|
||||
info: { ...this.feishuData, bot, label: this.$t('cs.person.feishuApp') },
|
||||
})
|
||||
}
|
||||
this.$message.success(this.$t('saveSuccess'))
|
||||
this.getData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.feishuData = {
|
||||
id: '',
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.notice-feishu-wrapper {
|
||||
background-color: #fff;
|
||||
padding-top: 15px;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
border-radius: @border-radius-box;
|
||||
.notice-feishu-wrapper-tips {
|
||||
display: inline-block;
|
||||
background-color: #ffdfdf;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
width: 300px;
|
||||
color: #000000;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,152 +0,0 @@
|
||||
<template>
|
||||
<div class="notice-wx-wrapper" :style="{ height: `${windowHeight - 64}px` }">
|
||||
<a-form-model ref="wxForm" :model="wxData" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<SpanTitle>{{ $t('cs.duty.basicSetting') }}</SpanTitle>
|
||||
<a-form-model-item :label="$t('cs.notice.corpid')">
|
||||
<a-input v-model="wxData.corpid" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.agentid')">
|
||||
<a-input v-model="wxData.agentid" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.corpsecret')">
|
||||
<a-input-password v-model="wxData.corpsecret" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="ITSM AppId">
|
||||
<a-input v-model="wxData.itsm_app_id" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cs.notice.robot')">
|
||||
<Bot
|
||||
ref="bot"
|
||||
:disabled="!isEditable"
|
||||
:columns="[
|
||||
{
|
||||
field: 'name',
|
||||
title: $t('cs.notice.title'),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
title: $t('cs.notice.webhookAddress'),
|
||||
required: true,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<!-- <a-form-model-item :label="测试邮件设置">
|
||||
<a-button type="primary" ghost>测试回收箱</a-button>
|
||||
<br />
|
||||
<span
|
||||
class="notice-wx-wrapper-tips"
|
||||
><ops-icon type="icon-shidi-quxiao" :style="{ color: '#D81E06' }" /> 邮件接收失败</span
|
||||
>
|
||||
<br />
|
||||
<span>邮箱服务器未配置,请配置一个邮箱服务器 | <a>故障诊断</a></span>
|
||||
</a-form-model-item> -->
|
||||
<a-row v-if="isEditable">
|
||||
<a-col :span="16" :offset="3">
|
||||
<a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-button type="primary" @click="onSubmit"> {{ $t('save') }} </a-button>
|
||||
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> {{ $t('reset') }} </a-button>
|
||||
</a-form-model-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import SpanTitle from '../components/spanTitle.vue'
|
||||
import { getNoticeConfigByPlatform, postNoticeConfigByPlatform, putNoticeConfigByPlatform } from '@/api/noticeSetting'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
import Bot from './bot.vue'
|
||||
export default {
|
||||
name: 'NoticeWx',
|
||||
mixins: [mixinPermissions],
|
||||
components: { SpanTitle, Bot },
|
||||
data() {
|
||||
return {
|
||||
labelCol: { lg: 3, md: 5, sm: 8 },
|
||||
wrapperCol: { lg: 15, md: 19, sm: 16 },
|
||||
id: null,
|
||||
wxData: {
|
||||
corpid: '',
|
||||
agentid: '',
|
||||
corpsecret: '',
|
||||
itsm_app_id: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '通知设置', ['update'])
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
getNoticeConfigByPlatform({ platform: 'wechatApp' }).then((res) => {
|
||||
this.id = res?.id ?? null
|
||||
if (this.id) {
|
||||
this.wxData = res.info
|
||||
this.$refs.bot.setData(res?.info?.bot)
|
||||
}
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.wxForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.$refs.bot.getData(async (flag, bot) => {
|
||||
if (flag) {
|
||||
if (this.id) {
|
||||
await putNoticeConfigByPlatform(this.id, {
|
||||
info: { ...this.wxData, bot, label: this.$t('cs.person.wechatApp') },
|
||||
})
|
||||
} else {
|
||||
await postNoticeConfigByPlatform({
|
||||
platform: 'wechatApp',
|
||||
info: { ...this.wxData, bot, label: this.$t('cs.person.wechatApp') },
|
||||
})
|
||||
}
|
||||
this.$message.success(this.$t('saveSuccess'))
|
||||
this.getData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.wxData = {
|
||||
corpid: '',
|
||||
agentid: '',
|
||||
corpsecret: '',
|
||||
itsm_app_id: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.notice-wx-wrapper {
|
||||
background-color: #fff;
|
||||
padding-top: 15px;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
border-radius: @border-radius-box;
|
||||
.notice-wx-wrapper-tips {
|
||||
display: inline-block;
|
||||
background-color: #ffdfdf;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
width: 300px;
|
||||
color: #000000;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -372,7 +372,7 @@ export default {
|
||||
margin-bottom: 5px;
|
||||
&:hover {
|
||||
.ops_popover_item_selected();
|
||||
border-color: #custom_colors[color_1];
|
||||
border-color: @primary-color;
|
||||
}
|
||||
> i {
|
||||
margin-right: 10px;
|
||||
@@ -380,7 +380,7 @@ export default {
|
||||
}
|
||||
.setting-person-left-item-selected {
|
||||
.ops_popover_item_selected();
|
||||
border-color: #custom_colors[color_1];
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
.setting-person-right {
|
||||
@@ -390,7 +390,7 @@ export default {
|
||||
border-radius: @border-radius-box;
|
||||
padding: 24px 48px;
|
||||
.setting-person-right-disabled {
|
||||
background-color: #custom_colors[color_2];
|
||||
background-color: @primary-color_5;
|
||||
border-radius: 4px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
@@ -45,7 +45,7 @@ services:
|
||||
- redis
|
||||
|
||||
cmdb-api:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.3
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.4
|
||||
# build:
|
||||
# context: .
|
||||
# target: cmdb-api
|
||||
@@ -84,7 +84,7 @@ services:
|
||||
- cmdb-api
|
||||
|
||||
cmdb-ui:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.3
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.4
|
||||
# build:
|
||||
# context: .
|
||||
# target: cmdb-ui
|
||||
|
@@ -342,8 +342,8 @@ key = "Your API key"
|
||||
secret = "Your API secret"
|
||||
|
||||
def build_api_key(path, params):
|
||||
values = "".join([str(params[k]) for k in sorted(params.keys())
|
||||
if params[k] is not None and not k.startswith('_')]) if params.keys() else ""
|
||||
values = "".join([str(params[k]) for k in sorted((params or {}).keys())
|
||||
if k not in ("_key", "_secret") and not isinstance(params[k], (dict, list))])
|
||||
_secret = "".join([path, secret, values]).encode("utf-8")
|
||||
params["_secret"] = hashlib.sha1(_secret).hexdigest()
|
||||
params["_key"] = key
|
||||
@@ -365,8 +365,8 @@ SECRET = "Your API secret"
|
||||
|
||||
|
||||
def build_api_key(path, params):
|
||||
values = "".join([str(params[k]) for k in sorted(params.keys())
|
||||
if params[k] is not None and not k.startswith('_')]) if params.keys() else ""
|
||||
values = "".join([str(params[k]) for k in sorted((params or {}).keys())
|
||||
if k not in ("_key", "_secret") and not isinstance(params[k], (dict, list))])
|
||||
_secret = "".join([path, SECRET, values]).encode("utf-8")
|
||||
params["_secret"] = hashlib.sha1(_secret).hexdigest()
|
||||
params["_key"] = KEY
|
||||
@@ -395,8 +395,8 @@ SECRET = "Your API secret"
|
||||
|
||||
|
||||
def build_api_key(path, params):
|
||||
values = "".join([str(params[k]) for k in sorted(params.keys())
|
||||
if params[k] is not None and not k.startswith('_')]) if params.keys() else ""
|
||||
values = "".join([str(params[k]) for k in sorted((params or {}).keys())
|
||||
if k not in ("_key", "_secret") and not isinstance(params[k], (dict, list))])
|
||||
_secret = "".join([path, SECRET, values]).encode("utf-8")
|
||||
params["_secret"] = hashlib.sha1(_secret).hexdigest()
|
||||
params["_key"] = KEY
|
||||
|
Reference in New Issue
Block a user