mirror of
https://github.com/veops/cmdb.git
synced 2025-09-23 05:49:18 +08:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8217053abf | ||
|
8c17373e45 | ||
|
a8e2595327 | ||
|
dfbba103cd | ||
|
86b9d5a7f4 | ||
|
612922a1b7 | ||
|
2758c5e468 | ||
|
d85c86a839 | ||
|
8355137e43 |
@@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
### 相关文档
|
### 相关文档
|
||||||
|
|
||||||
- <a href="https://zhuanlan.zhihu.com/p/98453732" target="_blank">设计文档</a>
|
- <a href="https://mp.weixin.qq.com/s/v3eANth64UBW5xdyOkK3tg" target="_blank">概要设计</a>
|
||||||
- <a href="https://github.com/veops/cmdb/tree/master/docs/cmdb_api.md" target="_blank">API 文档</a>
|
- <a href="https://github.com/veops/cmdb/tree/master/docs/cmdb_api.md" target="_blank">API 文档</a>
|
||||||
- <a href="https://mp.weixin.qq.com/s/EflmmJ-qdUkddTx2hRt3pA" target="_blank">树形视图实践</a>
|
- <a href="https://mp.weixin.qq.com/s/rQaf4AES7YJsyNQG_MKOLg" target="_blank">自动发现</a>
|
||||||
|
|
||||||
### 特点
|
### 特点
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
- 多应用
|
- 多应用
|
||||||
1. 丰富视图展示维度
|
1. 丰富视图展示维度
|
||||||
2. 提供 Restful API
|
2. 提供 Restful API
|
||||||
3. 自定义字段触发器
|
3. 支持定义属性触发器、计算属性
|
||||||
|
|
||||||
### 主要功能
|
### 主要功能
|
||||||
|
|
||||||
|
@@ -177,7 +177,7 @@ class InitDepartment(object):
|
|||||||
else:
|
else:
|
||||||
resource_type = results[0]
|
resource_type = results[0]
|
||||||
|
|
||||||
for name in ['公司信息']:
|
for name in ['公司信息', '公司架构', '通知设置']:
|
||||||
payload = dict(
|
payload = dict(
|
||||||
type_id=resource_type['id'],
|
type_id=resource_type['id'],
|
||||||
app_id=acl.app_name,
|
app_id=acl.app_name,
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
import requests
|
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import session
|
from flask import session
|
||||||
@@ -23,6 +22,7 @@ from api.lib.cmdb.utils import ValueTypeMap
|
|||||||
from api.lib.decorator import kwargs_required
|
from api.lib.decorator import kwargs_required
|
||||||
from api.lib.perm.acl.acl import is_app_admin
|
from api.lib.perm.acl.acl import is_app_admin
|
||||||
from api.lib.perm.acl.acl import validate_permission
|
from api.lib.perm.acl.acl import validate_permission
|
||||||
|
from api.lib.webhook import webhook_request
|
||||||
from api.models.cmdb import Attribute
|
from api.models.cmdb import Attribute
|
||||||
from api.models.cmdb import CIType
|
from api.models.cmdb import CIType
|
||||||
from api.models.cmdb import CITypeAttribute
|
from api.models.cmdb import CITypeAttribute
|
||||||
@@ -40,15 +40,11 @@ class AttributeManager(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_choice_values_from_web_hook(choice_web_hook):
|
def _get_choice_values_from_webhook(choice_webhook, payload=None):
|
||||||
url = choice_web_hook.get('url')
|
ret_key = choice_webhook.get('ret_key')
|
||||||
ret_key = choice_web_hook.get('ret_key')
|
|
||||||
headers = choice_web_hook.get('headers') or {}
|
|
||||||
payload = choice_web_hook.get('payload') or {}
|
|
||||||
method = (choice_web_hook.get('method') or 'GET').lower()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = getattr(requests, method)(url, headers=headers, data=payload).json()
|
res = webhook_request(choice_webhook, payload or {}).json()
|
||||||
if ret_key:
|
if ret_key:
|
||||||
ret_key_list = ret_key.strip().split("##")
|
ret_key_list = ret_key.strip().split("##")
|
||||||
for key in ret_key_list[:-1]:
|
for key in ret_key_list[:-1]:
|
||||||
@@ -63,16 +59,41 @@ class AttributeManager(object):
|
|||||||
current_app.logger.error("get choice values failed: {}".format(e))
|
current_app.logger.error("get choice values failed: {}".format(e))
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_choice_values_from_other_ci(choice_other):
|
||||||
|
from api.lib.cmdb.search import SearchError
|
||||||
|
from api.lib.cmdb.search.ci import search
|
||||||
|
|
||||||
|
type_ids = choice_other.get('type_ids')
|
||||||
|
attr_id = choice_other.get('attr_id')
|
||||||
|
other_filter = choice_other.get('filter') or ''
|
||||||
|
|
||||||
|
query = "_type:({}),{}".format(";".join(map(str, type_ids)), other_filter)
|
||||||
|
s = search(query, fl=[str(attr_id)], facet=[str(attr_id)], count=1)
|
||||||
|
try:
|
||||||
|
_, _, _, _, _, facet = s.search()
|
||||||
|
return [[i[0], {}] for i in (list(facet.values()) or [[]])[0]]
|
||||||
|
except SearchError as e:
|
||||||
|
current_app.logger.error("get choice values from other ci failed: {}".format(e))
|
||||||
|
return []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_choice_values(cls, attr_id, value_type, choice_web_hook, choice_web_hook_parse=True):
|
def get_choice_values(cls, attr_id, value_type, choice_web_hook, choice_other,
|
||||||
|
choice_web_hook_parse=True, choice_other_parse=True):
|
||||||
if choice_web_hook:
|
if choice_web_hook:
|
||||||
if choice_web_hook_parse:
|
if choice_web_hook_parse and isinstance(choice_web_hook, dict):
|
||||||
if isinstance(choice_web_hook, dict):
|
return cls._get_choice_values_from_webhook(choice_web_hook)
|
||||||
return cls._get_choice_values_from_web_hook(choice_web_hook)
|
else:
|
||||||
|
return []
|
||||||
|
elif choice_other:
|
||||||
|
if choice_other_parse and isinstance(choice_other, dict):
|
||||||
|
return cls._get_choice_values_from_other_ci(choice_other)
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
choice_table = ValueTypeMap.choice.get(value_type)
|
choice_table = ValueTypeMap.choice.get(value_type)
|
||||||
|
if not choice_table:
|
||||||
|
return []
|
||||||
choice_values = choice_table.get_by(fl=["value", "option"], attr_id=attr_id)
|
choice_values = choice_table.get_by(fl=["value", "option"], attr_id=attr_id)
|
||||||
|
|
||||||
return [[choice_value['value'], choice_value['option']] for choice_value in choice_values]
|
return [[choice_value['value'], choice_value['option']] for choice_value in choice_values]
|
||||||
@@ -122,7 +143,8 @@ class AttributeManager(object):
|
|||||||
res = list()
|
res = list()
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
attr["is_choice"] and attr.update(
|
attr["is_choice"] and attr.update(
|
||||||
dict(choice_value=cls.get_choice_values(attr["id"], attr["value_type"], attr["choice_web_hook"])))
|
dict(choice_value=cls.get_choice_values(attr["id"], attr["value_type"],
|
||||||
|
attr["choice_web_hook"], attr.get("choice_other"))))
|
||||||
attr['is_choice'] and attr.pop('choice_web_hook', None)
|
attr['is_choice'] and attr.pop('choice_web_hook', None)
|
||||||
|
|
||||||
res.append(attr)
|
res.append(attr)
|
||||||
@@ -132,29 +154,38 @@ class AttributeManager(object):
|
|||||||
def get_attribute_by_name(self, name):
|
def get_attribute_by_name(self, name):
|
||||||
attr = Attribute.get_by(name=name, first=True)
|
attr = Attribute.get_by(name=name, first=True)
|
||||||
if attr.get("is_choice"):
|
if attr.get("is_choice"):
|
||||||
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], attr["choice_web_hook"])
|
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"],
|
||||||
|
attr["choice_web_hook"], attr.get("choice_other"))
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
def get_attribute_by_alias(self, alias):
|
def get_attribute_by_alias(self, alias):
|
||||||
attr = Attribute.get_by(alias=alias, first=True)
|
attr = Attribute.get_by(alias=alias, first=True)
|
||||||
if attr.get("is_choice"):
|
if attr.get("is_choice"):
|
||||||
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], attr["choice_web_hook"])
|
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"],
|
||||||
|
attr["choice_web_hook"], attr.get("choice_other"))
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
def get_attribute_by_id(self, _id):
|
def get_attribute_by_id(self, _id):
|
||||||
attr = Attribute.get_by_id(_id).to_dict()
|
attr = Attribute.get_by_id(_id).to_dict()
|
||||||
if attr.get("is_choice"):
|
if attr.get("is_choice"):
|
||||||
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], attr["choice_web_hook"])
|
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"],
|
||||||
|
attr["choice_web_hook"], attr.get("choice_other"))
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
def get_attribute(self, key, choice_web_hook_parse=True):
|
def get_attribute(self, key, choice_web_hook_parse=True, choice_other_parse=True):
|
||||||
attr = AttributeCache.get(key).to_dict()
|
attr = AttributeCache.get(key).to_dict()
|
||||||
if attr.get("is_choice"):
|
if attr.get("is_choice"):
|
||||||
attr["choice_value"] = self.get_choice_values(
|
attr["choice_value"] = self.get_choice_values(
|
||||||
attr["id"], attr["value_type"], attr["choice_web_hook"], choice_web_hook_parse=choice_web_hook_parse)
|
attr["id"],
|
||||||
|
attr["value_type"],
|
||||||
|
attr["choice_web_hook"],
|
||||||
|
attr.get("choice_other"),
|
||||||
|
choice_web_hook_parse=choice_web_hook_parse,
|
||||||
|
choice_other_parse=choice_other_parse,
|
||||||
|
)
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
@@ -181,12 +212,17 @@ class AttributeManager(object):
|
|||||||
def add(cls, **kwargs):
|
def add(cls, **kwargs):
|
||||||
choice_value = kwargs.pop("choice_value", [])
|
choice_value = kwargs.pop("choice_value", [])
|
||||||
kwargs.pop("is_choice", None)
|
kwargs.pop("is_choice", None)
|
||||||
is_choice = True if choice_value or kwargs.get('choice_web_hook') else False
|
is_choice = True if choice_value or kwargs.get('choice_web_hook') or kwargs.get('choice_other') else False
|
||||||
|
|
||||||
name = kwargs.pop("name")
|
name = kwargs.pop("name")
|
||||||
if name in BUILTIN_KEYWORDS:
|
if name in BUILTIN_KEYWORDS:
|
||||||
return abort(400, ErrFormat.attribute_name_cannot_be_builtin)
|
return abort(400, ErrFormat.attribute_name_cannot_be_builtin)
|
||||||
|
|
||||||
|
if kwargs.get('choice_other'):
|
||||||
|
if (not isinstance(kwargs['choice_other'], dict) or not kwargs['choice_other'].get('type_ids') or
|
||||||
|
not kwargs['choice_other'].get('attr_id')):
|
||||||
|
return abort(400, ErrFormat.attribute_choice_other_invalid)
|
||||||
|
|
||||||
alias = kwargs.pop("alias", "")
|
alias = kwargs.pop("alias", "")
|
||||||
alias = name if not alias else alias
|
alias = name if not alias else alias
|
||||||
Attribute.get_by(name=name, first=True) and abort(400, ErrFormat.attribute_name_duplicate.format(name))
|
Attribute.get_by(name=name, first=True) and abort(400, ErrFormat.attribute_name_duplicate.format(name))
|
||||||
@@ -301,12 +337,17 @@ class AttributeManager(object):
|
|||||||
|
|
||||||
self._change_index(attr, attr.is_index, kwargs['is_index'])
|
self._change_index(attr, attr.is_index, kwargs['is_index'])
|
||||||
|
|
||||||
|
if kwargs.get('choice_other'):
|
||||||
|
if (not isinstance(kwargs['choice_other'], dict) or not kwargs['choice_other'].get('type_ids') or
|
||||||
|
not kwargs['choice_other'].get('attr_id')):
|
||||||
|
return abort(400, ErrFormat.attribute_choice_other_invalid)
|
||||||
|
|
||||||
existed2 = attr.to_dict()
|
existed2 = attr.to_dict()
|
||||||
if not existed2['choice_web_hook'] and existed2['is_choice']:
|
if not existed2['choice_web_hook'] and not existed2.get('choice_other') and existed2['is_choice']:
|
||||||
existed2['choice_value'] = self.get_choice_values(attr.id, attr.value_type, attr.choice_web_hook)
|
existed2['choice_value'] = self.get_choice_values(attr.id, attr.value_type, None, None)
|
||||||
|
|
||||||
choice_value = kwargs.pop("choice_value", False)
|
choice_value = kwargs.pop("choice_value", False)
|
||||||
is_choice = True if choice_value or kwargs.get('choice_web_hook') else False
|
is_choice = True if choice_value or kwargs.get('choice_web_hook') or kwargs.get('choice_other') else False
|
||||||
kwargs['is_choice'] = is_choice
|
kwargs['is_choice'] = is_choice
|
||||||
|
|
||||||
if kwargs.get('default') and not (isinstance(kwargs['default'], dict) and 'default' in kwargs['default']):
|
if kwargs.get('default') and not (isinstance(kwargs['default'], dict) and 'default' in kwargs['default']):
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
|
||||||
|
|
||||||
import toposort
|
import toposort
|
||||||
from flask import abort
|
from flask import abort
|
||||||
@@ -25,7 +24,6 @@ from api.lib.cmdb.const import ValueTypeEnum
|
|||||||
from api.lib.cmdb.history import CITypeHistoryManager
|
from api.lib.cmdb.history import CITypeHistoryManager
|
||||||
from api.lib.cmdb.relation_type import RelationTypeManager
|
from api.lib.cmdb.relation_type import RelationTypeManager
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.cmdb.utils import TableMap
|
|
||||||
from api.lib.cmdb.value import AttributeValueManager
|
from api.lib.cmdb.value import AttributeValueManager
|
||||||
from api.lib.decorator import kwargs_required
|
from api.lib.decorator import kwargs_required
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
@@ -354,19 +352,20 @@ class CITypeAttributeManager(object):
|
|||||||
return [AttributeCache.get(attr.attr_id).name for attr in CITypeAttributesCache.get(type_id)]
|
return [AttributeCache.get(attr.attr_id).name for attr in CITypeAttributesCache.get(type_id)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_attributes_by_type_id(type_id, choice_web_hook_parse=True):
|
def get_attributes_by_type_id(type_id, choice_web_hook_parse=True, choice_other_parse=True):
|
||||||
has_config_perm = ACLManager('cmdb').has_permission(
|
has_config_perm = ACLManager('cmdb').has_permission(
|
||||||
CITypeManager.get_name_by_id(type_id), ResourceTypeEnum.CI, PermEnum.CONFIG)
|
CITypeManager.get_name_by_id(type_id), ResourceTypeEnum.CI, PermEnum.CONFIG)
|
||||||
|
|
||||||
attrs = CITypeAttributesCache.get(type_id)
|
attrs = CITypeAttributesCache.get(type_id)
|
||||||
result = list()
|
result = list()
|
||||||
for attr in sorted(attrs, key=lambda x: (x.order, x.id)):
|
for attr in sorted(attrs, key=lambda x: (x.order, x.id)):
|
||||||
attr_dict = AttributeManager().get_attribute(attr.attr_id, choice_web_hook_parse)
|
attr_dict = AttributeManager().get_attribute(attr.attr_id, choice_web_hook_parse, choice_other_parse)
|
||||||
attr_dict["is_required"] = attr.is_required
|
attr_dict["is_required"] = attr.is_required
|
||||||
attr_dict["order"] = attr.order
|
attr_dict["order"] = attr.order
|
||||||
attr_dict["default_show"] = attr.default_show
|
attr_dict["default_show"] = attr.default_show
|
||||||
if not has_config_perm:
|
if not has_config_perm:
|
||||||
attr_dict.pop('choice_web_hook', None)
|
attr_dict.pop('choice_web_hook', None)
|
||||||
|
attr_dict.pop('choice_other', None)
|
||||||
|
|
||||||
result.append(attr_dict)
|
result.append(attr_dict)
|
||||||
|
|
||||||
@@ -374,13 +373,25 @@ class CITypeAttributeManager(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_common_attributes(type_ids):
|
def get_common_attributes(type_ids):
|
||||||
|
has_config_perm = False
|
||||||
|
for type_id in type_ids:
|
||||||
|
has_config_perm |= ACLManager('cmdb').has_permission(
|
||||||
|
CITypeManager.get_name_by_id(type_id), ResourceTypeEnum.CI, PermEnum.CONFIG)
|
||||||
|
|
||||||
result = CITypeAttribute.get_by(__func_in___key_type_id=list(map(int, type_ids)), to_dict=False)
|
result = CITypeAttribute.get_by(__func_in___key_type_id=list(map(int, type_ids)), to_dict=False)
|
||||||
attr2types = {}
|
attr2types = {}
|
||||||
for i in result:
|
for i in result:
|
||||||
attr2types.setdefault(i.attr_id, []).append(i.type_id)
|
attr2types.setdefault(i.attr_id, []).append(i.type_id)
|
||||||
|
|
||||||
return [AttributeCache.get(attr_id).to_dict() for attr_id in attr2types
|
attrs = []
|
||||||
if len(attr2types[attr_id]) == len(type_ids)]
|
for attr_id in attr2types:
|
||||||
|
if len(attr2types[attr_id]) == len(type_ids):
|
||||||
|
attr = AttributeManager().get_attribute_by_id(attr_id)
|
||||||
|
if not has_config_perm:
|
||||||
|
attr.pop('choice_web_hook', None)
|
||||||
|
attrs.append(attr)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check(type_id, attr_ids):
|
def _check(type_id, attr_ids):
|
||||||
@@ -489,7 +500,7 @@ class CITypeAttributeManager(object):
|
|||||||
for ci in CI.get_by(type_id=type_id, to_dict=False):
|
for ci in CI.get_by(type_id=type_id, to_dict=False):
|
||||||
AttributeValueManager.delete_attr_value(attr_id, ci.id)
|
AttributeValueManager.delete_attr_value(attr_id, ci.id)
|
||||||
|
|
||||||
ci_cache.apply_async([ci.id], queue=CMDB_QUEUE)
|
ci_cache.apply_async(args=(ci.id, None, None), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
CITypeAttributeCache.clean(type_id, attr_id)
|
CITypeAttributeCache.clean(type_id, attr_id)
|
||||||
|
|
||||||
@@ -522,7 +533,7 @@ class CITypeAttributeManager(object):
|
|||||||
CITypeAttributesCache.clean(type_id)
|
CITypeAttributesCache.clean(type_id)
|
||||||
|
|
||||||
from api.tasks.cmdb import ci_type_attribute_order_rebuild
|
from api.tasks.cmdb import ci_type_attribute_order_rebuild
|
||||||
ci_type_attribute_order_rebuild.apply_async(args=(type_id,), queue=CMDB_QUEUE)
|
ci_type_attribute_order_rebuild.apply_async(args=(type_id, current_user.uid), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
|
|
||||||
class CITypeRelationManager(object):
|
class CITypeRelationManager(object):
|
||||||
@@ -847,7 +858,7 @@ class CITypeAttributeGroupManager(object):
|
|||||||
CITypeAttributesCache.clean(type_id)
|
CITypeAttributesCache.clean(type_id)
|
||||||
|
|
||||||
from api.tasks.cmdb import ci_type_attribute_order_rebuild
|
from api.tasks.cmdb import ci_type_attribute_order_rebuild
|
||||||
ci_type_attribute_order_rebuild.apply_async(args=(type_id,), queue=CMDB_QUEUE)
|
ci_type_attribute_order_rebuild.apply_async(args=(type_id, current_user.uid), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
|
|
||||||
class CITypeTemplateManager(object):
|
class CITypeTemplateManager(object):
|
||||||
@@ -1092,7 +1103,7 @@ class CITypeTemplateManager(object):
|
|||||||
|
|
||||||
for ci_type in tpt['ci_types']:
|
for ci_type in tpt['ci_types']:
|
||||||
tpt['type2attributes'][ci_type['id']] = CITypeAttributeManager.get_attributes_by_type_id(
|
tpt['type2attributes'][ci_type['id']] = CITypeAttributeManager.get_attributes_by_type_id(
|
||||||
ci_type['id'], choice_web_hook_parse=False)
|
ci_type['id'], choice_web_hook_parse=False, choice_other_parse=False)
|
||||||
|
|
||||||
tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id'])
|
tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id'])
|
||||||
|
|
||||||
|
@@ -116,7 +116,7 @@ class PreferenceManager(object):
|
|||||||
for i in result:
|
for i in result:
|
||||||
if i["is_choice"]:
|
if i["is_choice"]:
|
||||||
i.update(dict(choice_value=AttributeManager.get_choice_values(
|
i.update(dict(choice_value=AttributeManager.get_choice_values(
|
||||||
i["id"], i["value_type"], i["choice_web_hook"])))
|
i["id"], i["value_type"], i["choice_web_hook"], i.get("choice_other"))))
|
||||||
|
|
||||||
return is_subscribed, result
|
return is_subscribed, result
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ class ErrFormat(CommonErrFormat):
|
|||||||
cannot_edit_attribute = "您没有权限修改该属性!"
|
cannot_edit_attribute = "您没有权限修改该属性!"
|
||||||
cannot_delete_attribute = "目前只允许 属性创建人、管理员 删除属性!"
|
cannot_delete_attribute = "目前只允许 属性创建人、管理员 删除属性!"
|
||||||
attribute_name_cannot_be_builtin = "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type"
|
attribute_name_cannot_be_builtin = "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type"
|
||||||
|
attribute_choice_other_invalid = "预定义值: 其他模型请求参数不合法!"
|
||||||
|
|
||||||
ci_not_found = "CI {} 不存在"
|
ci_not_found = "CI {} 不存在"
|
||||||
unique_constraint = "多属性联合唯一校验不通过: {}"
|
unique_constraint = "多属性联合唯一校验不通过: {}"
|
||||||
|
@@ -92,7 +92,7 @@ class AttributeValueManager(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_is_choice(attr, value_type, value):
|
def _check_is_choice(attr, value_type, value):
|
||||||
choice_values = AttributeManager.get_choice_values(attr.id, value_type, attr.choice_web_hook)
|
choice_values = AttributeManager.get_choice_values(attr.id, value_type, attr.choice_web_hook, attr.choice_other)
|
||||||
if str(value) not in list(map(str, [i[0] for i in choice_values])):
|
if str(value) not in list(map(str, [i[0] for i in choice_values])):
|
||||||
return abort(400, ErrFormat.not_in_choice_values.format(value))
|
return abort(400, ErrFormat.not_in_choice_values.format(value))
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
from api.extensions import cache
|
||||||
from api.models.common_setting import CompanyInfo
|
from api.models.common_setting import CompanyInfo
|
||||||
|
|
||||||
|
|
||||||
@@ -11,14 +11,34 @@ class CompanyInfoCRUD(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(**kwargs):
|
def create(**kwargs):
|
||||||
return CompanyInfo.create(**kwargs)
|
res = CompanyInfo.create(**kwargs)
|
||||||
|
CompanyInfoCache.refresh(res.info)
|
||||||
|
return res
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update(_id, **kwargs):
|
def update(_id, **kwargs):
|
||||||
kwargs.pop('id', None)
|
kwargs.pop('id', None)
|
||||||
existed = CompanyInfo.get_by_id(_id)
|
existed = CompanyInfo.get_by_id(_id)
|
||||||
if not existed:
|
if not existed:
|
||||||
return CompanyInfoCRUD.create(**kwargs)
|
existed = CompanyInfoCRUD.create(**kwargs)
|
||||||
else:
|
else:
|
||||||
existed = existed.update(**kwargs)
|
existed = existed.update(**kwargs)
|
||||||
return existed
|
CompanyInfoCache.refresh(existed.info)
|
||||||
|
return existed
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyInfoCache(object):
|
||||||
|
key = 'CompanyInfoCache::'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls):
|
||||||
|
info = cache.get(cls.key)
|
||||||
|
if not info:
|
||||||
|
res = CompanyInfo.get_by(first=True) or {}
|
||||||
|
info = res.get('info', {})
|
||||||
|
cache.set(cls.key, info)
|
||||||
|
return info
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def refresh(cls, info):
|
||||||
|
cache.set(cls.key, info)
|
@@ -12,3 +12,10 @@ class OperatorType(BaseEnum):
|
|||||||
LESS_THAN = 6
|
LESS_THAN = 6
|
||||||
IS_EMPTY = 7
|
IS_EMPTY = 7
|
||||||
IS_NOT_EMPTY = 8
|
IS_NOT_EMPTY = 8
|
||||||
|
|
||||||
|
|
||||||
|
BotNameMap = {
|
||||||
|
'wechatApp': 'wechatBot',
|
||||||
|
'feishuApp': 'feishuBot',
|
||||||
|
'dingdingApp': 'dingdingBot',
|
||||||
|
}
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
import copy
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import requests
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy import or_, literal_column, func, not_, and_
|
from sqlalchemy import or_, literal_column, func, not_, and_
|
||||||
@@ -474,6 +475,60 @@ class EmployeeCRUD(object):
|
|||||||
|
|
||||||
return [r.to_dict() for r in results]
|
return [r.to_dict() for r in results]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_bind_notice_by_uid(_platform, _uid):
|
||||||
|
existed = EmployeeCRUD.get_employee_by_uid(_uid)
|
||||||
|
employee_data = existed.to_dict()
|
||||||
|
|
||||||
|
notice_info = employee_data.get('notice_info', {})
|
||||||
|
notice_info = copy.deepcopy(notice_info) if notice_info else {}
|
||||||
|
|
||||||
|
notice_info[_platform] = ''
|
||||||
|
|
||||||
|
existed.update(
|
||||||
|
notice_info=notice_info
|
||||||
|
)
|
||||||
|
return ErrFormat.notice_remove_bind_success
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def bind_notice_by_uid(_platform, _uid):
|
||||||
|
existed = EmployeeCRUD.get_employee_by_uid(_uid)
|
||||||
|
mobile = existed.mobile
|
||||||
|
if not mobile or len(mobile) == 0:
|
||||||
|
abort(400, ErrFormat.notice_bind_err_with_empty_mobile)
|
||||||
|
|
||||||
|
from api.lib.common_setting.notice_config import NoticeConfigCRUD
|
||||||
|
messenger = NoticeConfigCRUD.get_messenger_url()
|
||||||
|
if not messenger or len(messenger) == 0:
|
||||||
|
abort(400, ErrFormat.notice_please_config_messenger_first)
|
||||||
|
|
||||||
|
url = f"{messenger}/v1/uid/getbyphone"
|
||||||
|
try:
|
||||||
|
payload = dict(
|
||||||
|
phone=mobile,
|
||||||
|
sender=_platform
|
||||||
|
)
|
||||||
|
res = requests.post(url, json=payload)
|
||||||
|
result = res.json()
|
||||||
|
if res.status_code != 200:
|
||||||
|
raise Exception(result.get('msg', ''))
|
||||||
|
target_id = result.get('uid', '')
|
||||||
|
|
||||||
|
employee_data = existed.to_dict()
|
||||||
|
|
||||||
|
notice_info = employee_data.get('notice_info', {})
|
||||||
|
notice_info = copy.deepcopy(notice_info) if notice_info else {}
|
||||||
|
|
||||||
|
notice_info[_platform] = '' if not target_id else target_id
|
||||||
|
|
||||||
|
existed.update(
|
||||||
|
notice_info=notice_info
|
||||||
|
)
|
||||||
|
return ErrFormat.notice_bind_success
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return abort(400, ErrFormat.notice_bind_failed.format(str(e)))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_employee_notice_by_ids(employee_ids):
|
def get_employee_notice_by_ids(employee_ids):
|
||||||
criterion = [
|
criterion = [
|
||||||
|
@@ -1,41 +1,104 @@
|
|||||||
from api.models.common_setting import NoticeConfig
|
import requests
|
||||||
|
|
||||||
|
from api.lib.common_setting.const import BotNameMap
|
||||||
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
|
from api.models.common_setting import CompanyInfo, NoticeConfig
|
||||||
from wtforms import Form
|
from wtforms import Form
|
||||||
from wtforms import StringField
|
from wtforms import StringField
|
||||||
from wtforms import validators
|
from wtforms import validators
|
||||||
from flask import abort
|
from flask import abort, current_app
|
||||||
import smtplib
|
|
||||||
from email.mime.text import MIMEText
|
|
||||||
from email.utils import formataddr
|
|
||||||
|
|
||||||
|
|
||||||
class NoticeConfigCRUD(object):
|
class NoticeConfigCRUD(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_notice_config(**kwargs):
|
def add_notice_config(**kwargs):
|
||||||
NoticeConfigCRUD.check_platform(kwargs.get('platform'))
|
platform = kwargs.get('platform')
|
||||||
|
NoticeConfigCRUD.check_platform(platform)
|
||||||
|
info = kwargs.get('info', {})
|
||||||
|
if 'name' not in info:
|
||||||
|
info['name'] = platform
|
||||||
|
kwargs['info'] = info
|
||||||
try:
|
try:
|
||||||
return NoticeConfig.create(
|
NoticeConfigCRUD.update_messenger_config(**info)
|
||||||
|
res = NoticeConfig.create(
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return abort(400, str(e))
|
return abort(400, str(e))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_platform(platform):
|
def check_platform(platform):
|
||||||
NoticeConfig.get_by(first=True, to_dict=False, platform=platform) and abort(400, f"{platform} 已存在!")
|
NoticeConfig.get_by(first=True, to_dict=False, platform=platform) and \
|
||||||
|
abort(400, ErrFormat.notice_platform_existed.format(platform))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def edit_notice_config(_id, **kwargs):
|
def edit_notice_config(_id, **kwargs):
|
||||||
existed = NoticeConfigCRUD.get_notice_config_by_id(_id)
|
existed = NoticeConfigCRUD.get_notice_config_by_id(_id)
|
||||||
try:
|
try:
|
||||||
return existed.update(**kwargs)
|
info = kwargs.get('info', {})
|
||||||
|
if 'name' not in info:
|
||||||
|
info['name'] = existed.platform
|
||||||
|
kwargs['info'] = info
|
||||||
|
NoticeConfigCRUD.update_messenger_config(**info)
|
||||||
|
|
||||||
|
res = existed.update(**kwargs)
|
||||||
|
return res
|
||||||
|
except Exception as e:
|
||||||
|
return abort(400, str(e))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_messenger_url():
|
||||||
|
from api.lib.common_setting.company_info import CompanyInfoCache
|
||||||
|
com_info = CompanyInfoCache.get()
|
||||||
|
if not com_info:
|
||||||
|
return
|
||||||
|
messenger = com_info.get('messenger', '')
|
||||||
|
if len(messenger) == 0:
|
||||||
|
return
|
||||||
|
if messenger[-1] == '/':
|
||||||
|
messenger = messenger[:-1]
|
||||||
|
return messenger
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_messenger_config(**kwargs):
|
||||||
|
try:
|
||||||
|
messenger = NoticeConfigCRUD.get_messenger_url()
|
||||||
|
if not messenger or len(messenger) == 0:
|
||||||
|
raise Exception(ErrFormat.notice_please_config_messenger_first)
|
||||||
|
|
||||||
|
url = f"{messenger}/v1/senders"
|
||||||
|
name = kwargs.get('name')
|
||||||
|
bot_list = kwargs.pop('bot', None)
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
if isinstance(v, bool):
|
||||||
|
kwargs[k] = 'true' if v else 'false'
|
||||||
|
else:
|
||||||
|
kwargs[k] = str(v)
|
||||||
|
|
||||||
|
payload = {name: [kwargs]}
|
||||||
|
current_app.logger.info(f"update_messenger_config: {url}, {payload}")
|
||||||
|
res = requests.put(url, json=payload, timeout=2)
|
||||||
|
current_app.logger.info(f"update_messenger_config: {res.status_code}, {res.text}")
|
||||||
|
|
||||||
|
if not bot_list or len(bot_list) == 0:
|
||||||
|
return
|
||||||
|
bot_name = BotNameMap.get(name)
|
||||||
|
payload = {bot_name: bot_list}
|
||||||
|
current_app.logger.info(f"update_messenger_config: {url}, {payload}")
|
||||||
|
bot_res = requests.put(url, json=payload, timeout=2)
|
||||||
|
current_app.logger.info(f"update_messenger_config: {bot_res.status_code}, {bot_res.text}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return abort(400, str(e))
|
return abort(400, str(e))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_notice_config_by_id(_id):
|
def get_notice_config_by_id(_id):
|
||||||
return NoticeConfig.get_by(first=True, to_dict=False, id=_id) or abort(400, f"{_id} 配置项不存在!")
|
return NoticeConfig.get_by(first=True, to_dict=False, id=_id) or \
|
||||||
|
abort(400,
|
||||||
|
ErrFormat.notice_not_existed.format(_id))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all():
|
def get_all():
|
||||||
@@ -43,38 +106,46 @@ class NoticeConfigCRUD(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def test_send_email(receive_address, **kwargs):
|
def test_send_email(receive_address, **kwargs):
|
||||||
# 设置发送方和接收方的电子邮件地址
|
messenger = NoticeConfigCRUD.get_messenger_url()
|
||||||
sender_email = 'test@test.com'
|
if not messenger or len(messenger) == 0:
|
||||||
sender_name = 'Test Sender'
|
abort(400, ErrFormat.notice_please_config_messenger_first)
|
||||||
|
url = f"{messenger}/v1/message"
|
||||||
|
|
||||||
recipient_email = receive_address
|
recipient_email = receive_address
|
||||||
recipient_name = receive_address
|
|
||||||
|
|
||||||
subject = 'Test Email'
|
subject = 'Test Email'
|
||||||
body = 'This is a test email'
|
body = 'This is a test email'
|
||||||
|
payload = {
|
||||||
message = MIMEText(body, 'plain', 'utf-8')
|
"sender": 'email',
|
||||||
message['From'] = formataddr((sender_name, sender_email))
|
"msgtype": "text/plain",
|
||||||
message['To'] = formataddr((recipient_name, recipient_email))
|
"title": subject,
|
||||||
message['Subject'] = subject
|
"content": body,
|
||||||
|
"tos": [recipient_email],
|
||||||
smtp_server = kwargs.get('server')
|
}
|
||||||
smtp_port = kwargs.get('port')
|
current_app.logger.info(f"test_send_email: {url}, {payload}")
|
||||||
smtp_username = kwargs.get('username')
|
response = requests.post(url, json=payload)
|
||||||
smtp_password = kwargs.get('password')
|
if response.status_code != 200:
|
||||||
|
abort(400, response.text)
|
||||||
if kwargs.get('mail_type') == 'SMTP':
|
|
||||||
smtp_connection = smtplib.SMTP(smtp_server, smtp_port)
|
|
||||||
else:
|
|
||||||
smtp_connection = smtplib.SMTP_SSL(smtp_server, smtp_port)
|
|
||||||
|
|
||||||
if kwargs.get('is_login'):
|
|
||||||
smtp_connection.login(smtp_username, smtp_password)
|
|
||||||
|
|
||||||
smtp_connection.sendmail(sender_email, recipient_email, message.as_string())
|
|
||||||
smtp_connection.quit()
|
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_app_bot():
|
||||||
|
result = []
|
||||||
|
for notice_app in NoticeConfig.get_by(to_dict=False):
|
||||||
|
if notice_app.platform in ['email']:
|
||||||
|
continue
|
||||||
|
info = notice_app.info
|
||||||
|
name = info.get('name', '')
|
||||||
|
if name not in BotNameMap:
|
||||||
|
continue
|
||||||
|
result.append(dict(
|
||||||
|
name=info.get('name', ''),
|
||||||
|
label=info.get('label', ''),
|
||||||
|
bot=info.get('bot', []),
|
||||||
|
))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class NoticeConfigForm(Form):
|
class NoticeConfigForm(Form):
|
||||||
platform = StringField(validators=[
|
platform = StringField(validators=[
|
||||||
@@ -91,4 +162,4 @@ class NoticeConfigUpdateForm(Form):
|
|||||||
info = StringField(validators=[
|
info = StringField(validators=[
|
||||||
validators.DataRequired(message="信息 不能为空"),
|
validators.DataRequired(message="信息 不能为空"),
|
||||||
validators.Length(max=255),
|
validators.Length(max=255),
|
||||||
])
|
])
|
@@ -56,3 +56,10 @@ class ErrFormat(CommonErrFormat):
|
|||||||
email_send_timeout = "邮件发送超时"
|
email_send_timeout = "邮件发送超时"
|
||||||
|
|
||||||
common_data_not_found = "ID {} 找不到记录"
|
common_data_not_found = "ID {} 找不到记录"
|
||||||
|
notice_platform_existed = "{} 已存在"
|
||||||
|
notice_not_existed = "{} 配置项不存在"
|
||||||
|
notice_please_config_messenger_first = "请先配置 messenger"
|
||||||
|
notice_bind_err_with_empty_mobile = "绑定失败,手机号为空"
|
||||||
|
notice_bind_failed = "绑定失败: {}"
|
||||||
|
notice_bind_success = "绑定成功"
|
||||||
|
notice_remove_bind_success = "解绑成功"
|
||||||
|
@@ -3,10 +3,12 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
import six
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from markdownify import markdownify as md
|
from markdownify import markdownify as md
|
||||||
|
|
||||||
|
from api.lib.common_setting.notice_config import NoticeConfigCRUD
|
||||||
from api.lib.mail import send_mail
|
from api.lib.mail import send_mail
|
||||||
|
|
||||||
|
|
||||||
@@ -17,7 +19,15 @@ def _request_messenger(subject, body, tos, sender, payload):
|
|||||||
if not params['tos']:
|
if not params['tos']:
|
||||||
raise Exception("no receivers")
|
raise Exception("no receivers")
|
||||||
|
|
||||||
params['tos'] = [Template(i).render(payload) for i in params['tos'] if i.strip()]
|
flat_tos = []
|
||||||
|
for i in params['tos']:
|
||||||
|
if i.strip():
|
||||||
|
to = Template(i).render(payload)
|
||||||
|
if isinstance(to, list):
|
||||||
|
flat_tos.extend(to)
|
||||||
|
elif isinstance(to, six.string_types):
|
||||||
|
flat_tos.append(to)
|
||||||
|
params['tos'] = flat_tos
|
||||||
|
|
||||||
if sender == "email":
|
if sender == "email":
|
||||||
params['msgtype'] = 'text/html'
|
params['msgtype'] = 'text/html'
|
||||||
@@ -32,7 +42,14 @@ def _request_messenger(subject, body, tos, sender, payload):
|
|||||||
|
|
||||||
params['content'] = json.dumps(dict(content=content))
|
params['content'] = json.dumps(dict(content=content))
|
||||||
|
|
||||||
resp = requests.post(current_app.config.get('MESSENGER_URL'), json=params)
|
url = current_app.config.get('MESSENGER_URL') or NoticeConfigCRUD.get_messenger_url()
|
||||||
|
if not url:
|
||||||
|
raise Exception("no messenger url")
|
||||||
|
|
||||||
|
if not url.endswith("message"):
|
||||||
|
url = "{}/v1/message".format(url)
|
||||||
|
|
||||||
|
resp = requests.post(url, json=params)
|
||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
raise Exception(resp.text)
|
raise Exception(resp.text)
|
||||||
|
|
||||||
|
@@ -10,9 +10,7 @@ from sqlalchemy import or_
|
|||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.perm.acl.app import AppCRUD
|
from api.lib.perm.acl.app import AppCRUD
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditScope
|
||||||
from api.lib.perm.acl.audit import AuditOperateType
|
|
||||||
from api.lib.perm.acl.audit import AuditScope
|
|
||||||
from api.lib.perm.acl.cache import AppCache
|
from api.lib.perm.acl.cache import AppCache
|
||||||
from api.lib.perm.acl.cache import HasResourceRoleCache
|
from api.lib.perm.acl.cache import HasResourceRoleCache
|
||||||
from api.lib.perm.acl.cache import RoleCache
|
from api.lib.perm.acl.cache import RoleCache
|
||||||
@@ -71,16 +69,16 @@ class RoleRelationCRUD(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_parent_ids(rid, app_id):
|
def get_parent_ids(rid, app_id):
|
||||||
if app_id is not None:
|
if app_id is not None:
|
||||||
return ([i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=app_id, to_dict=False)] +
|
return [i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=app_id, to_dict=False)] + \
|
||||||
[i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=None, to_dict=False)])
|
[i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=None, to_dict=False)]
|
||||||
else:
|
else:
|
||||||
return [i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=app_id, to_dict=False)]
|
return [i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=app_id, to_dict=False)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_child_ids(rid, app_id):
|
def get_child_ids(rid, app_id):
|
||||||
if app_id is not None:
|
if app_id is not None:
|
||||||
return ([i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=app_id, to_dict=False)] +
|
return [i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=app_id, to_dict=False)] + \
|
||||||
[i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=None, to_dict=False)])
|
[i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=None, to_dict=False)]
|
||||||
else:
|
else:
|
||||||
return [i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=app_id, to_dict=False)]
|
return [i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=app_id, to_dict=False)]
|
||||||
|
|
||||||
@@ -215,7 +213,6 @@ class RoleCRUD(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def search(q, app_id, page=1, page_size=None, user_role=True, is_all=False, user_only=False):
|
def search(q, app_id, page=1, page_size=None, user_role=True, is_all=False, user_only=False):
|
||||||
|
|
||||||
if user_only: # only user role
|
if user_only: # only user role
|
||||||
query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(Role.uid.isnot(None))
|
query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(Role.uid.isnot(None))
|
||||||
|
|
||||||
@@ -273,6 +270,13 @@ class RoleCRUD(object):
|
|||||||
RoleCache.clean(rid)
|
RoleCache.clean(rid)
|
||||||
|
|
||||||
role = role.update(**kwargs)
|
role = role.update(**kwargs)
|
||||||
|
|
||||||
|
if origin['uid'] and kwargs.get('name') and kwargs.get('name') != origin['name']:
|
||||||
|
from api.models.acl import User
|
||||||
|
user = User.get_by(uid=origin['uid'], first=True, to_dict=False)
|
||||||
|
if user:
|
||||||
|
user.update(username=kwargs['name'])
|
||||||
|
|
||||||
AuditCRUD.add_role_log(role.app_id, AuditOperateType.update,
|
AuditCRUD.add_role_log(role.app_id, AuditOperateType.update,
|
||||||
AuditScope.role, role.id, origin, role.to_dict(), {},
|
AuditScope.role, role.id, origin, role.to_dict(), {},
|
||||||
)
|
)
|
||||||
@@ -291,12 +295,11 @@ class RoleCRUD(object):
|
|||||||
from api.lib.perm.acl.acl import is_admin
|
from api.lib.perm.acl.acl import is_admin
|
||||||
|
|
||||||
role = Role.get_by_id(rid) or abort(404, ErrFormat.role_not_found.format("rid={}".format(rid)))
|
role = Role.get_by_id(rid) or abort(404, ErrFormat.role_not_found.format("rid={}".format(rid)))
|
||||||
|
|
||||||
not force and role.uid and abort(400, ErrFormat.user_role_delete_invalid)
|
|
||||||
|
|
||||||
if not role.app_id and not is_admin():
|
if not role.app_id and not is_admin():
|
||||||
return abort(403, ErrFormat.admin_required)
|
return abort(403, ErrFormat.admin_required)
|
||||||
|
|
||||||
|
not force and role.uid and abort(400, ErrFormat.user_role_delete_invalid)
|
||||||
|
|
||||||
origin = role.to_dict()
|
origin = role.to_dict()
|
||||||
|
|
||||||
child_ids = []
|
child_ids = []
|
||||||
@@ -305,20 +308,18 @@ class RoleCRUD(object):
|
|||||||
|
|
||||||
for i in RoleRelation.get_by(parent_id=rid, to_dict=False):
|
for i in RoleRelation.get_by(parent_id=rid, to_dict=False):
|
||||||
child_ids.append(i.child_id)
|
child_ids.append(i.child_id)
|
||||||
i.soft_delete(commit=False)
|
i.soft_delete()
|
||||||
|
|
||||||
for i in RoleRelation.get_by(child_id=rid, to_dict=False):
|
for i in RoleRelation.get_by(child_id=rid, to_dict=False):
|
||||||
parent_ids.append(i.parent_id)
|
parent_ids.append(i.parent_id)
|
||||||
i.soft_delete(commit=False)
|
i.soft_delete()
|
||||||
|
|
||||||
role_permissions = []
|
role_permissions = []
|
||||||
for i in RolePermission.get_by(rid=rid, to_dict=False):
|
for i in RolePermission.get_by(rid=rid, to_dict=False):
|
||||||
role_permissions.append(i.to_dict())
|
role_permissions.append(i.to_dict())
|
||||||
i.soft_delete(commit=False)
|
i.soft_delete()
|
||||||
|
|
||||||
role.soft_delete(commit=False)
|
role.soft_delete()
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
role_rebuild.apply_async(args=(recursive_child_ids, role.app_id), queue=ACL_QUEUE)
|
role_rebuild.apply_async(args=(recursive_child_ids, role.app_id), queue=ACL_QUEUE)
|
||||||
|
|
||||||
|
@@ -90,6 +90,7 @@ class Attribute(Model):
|
|||||||
compute_script = db.Column(db.Text)
|
compute_script = db.Column(db.Text)
|
||||||
|
|
||||||
choice_web_hook = db.Column(db.JSON)
|
choice_web_hook = db.Column(db.JSON)
|
||||||
|
choice_other = db.Column(db.JSON)
|
||||||
|
|
||||||
uid = db.Column(db.Integer, index=True)
|
uid = db.Column(db.Integer, index=True)
|
||||||
|
|
||||||
|
@@ -41,10 +41,11 @@ def ci_cache(ci_id, operate_type, record_id):
|
|||||||
|
|
||||||
current_app.logger.info("{0} flush..........".format(ci_id))
|
current_app.logger.info("{0} flush..........".format(ci_id))
|
||||||
|
|
||||||
current_app.test_request_context().push()
|
if operate_type:
|
||||||
login_user(UserCache.get('worker'))
|
current_app.test_request_context().push()
|
||||||
|
login_user(UserCache.get('worker'))
|
||||||
|
|
||||||
CITriggerManager.fire(operate_type, ci_dict, record_id)
|
CITriggerManager.fire(operate_type, ci_dict, record_id)
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.batch_ci_cache", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.batch_ci_cache", queue=CMDB_QUEUE)
|
||||||
@@ -164,7 +165,7 @@ def ci_relation_delete(parent_id, child_id):
|
|||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_type_attribute_order_rebuild", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_type_attribute_order_rebuild", queue=CMDB_QUEUE)
|
||||||
def ci_type_attribute_order_rebuild(type_id):
|
def ci_type_attribute_order_rebuild(type_id, uid):
|
||||||
current_app.logger.info('rebuild attribute order')
|
current_app.logger.info('rebuild attribute order')
|
||||||
db.session.remove()
|
db.session.remove()
|
||||||
|
|
||||||
@@ -173,6 +174,9 @@ def ci_type_attribute_order_rebuild(type_id):
|
|||||||
attrs = CITypeAttributesCache.get(type_id)
|
attrs = CITypeAttributesCache.get(type_id)
|
||||||
id2attr = {attr.attr_id: attr for attr in attrs}
|
id2attr = {attr.attr_id: attr for attr in attrs}
|
||||||
|
|
||||||
|
current_app.test_request_context().push()
|
||||||
|
login_user(UserCache.get(uid))
|
||||||
|
|
||||||
res = CITypeAttributeGroupManager.get_by_type_id(type_id, True)
|
res = CITypeAttributeGroupManager.get_by_type_id(type_id, True)
|
||||||
order = 0
|
order = 0
|
||||||
for group in res:
|
for group in res:
|
||||||
|
@@ -506,4 +506,3 @@ class CITypeFilterPermissionView(APIView):
|
|||||||
@auth_with_app_token
|
@auth_with_app_token
|
||||||
def get(self, type_id):
|
def get(self, type_id):
|
||||||
return self.jsonify(CIFilterPermsCRUD().get(type_id))
|
return self.jsonify(CIFilterPermsCRUD().get(type_id))
|
||||||
|
|
||||||
|
@@ -156,3 +156,15 @@ class GetEmployeeNoticeByIds(APIView):
|
|||||||
else:
|
else:
|
||||||
result = EmployeeCRUD.get_employee_notice_by_ids(employee_ids)
|
result = EmployeeCRUD.get_employee_notice_by_ids(employee_ids)
|
||||||
return self.jsonify(result)
|
return self.jsonify(result)
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeBindNoticeWithACLID(APIView):
|
||||||
|
url_prefix = (f'{prefix}/by_uid/bind_notice/<string:platform>/<int:_uid>',)
|
||||||
|
|
||||||
|
def put(self, platform, _uid):
|
||||||
|
data = EmployeeCRUD.bind_notice_by_uid(platform, _uid)
|
||||||
|
return self.jsonify(info=data)
|
||||||
|
|
||||||
|
def delete(self, platform, _uid):
|
||||||
|
data = EmployeeCRUD.remove_bind_notice_by_uid(platform, _uid)
|
||||||
|
return self.jsonify(info=data)
|
||||||
|
@@ -69,3 +69,11 @@ class NoticeConfigGetView(APIView):
|
|||||||
def get(self):
|
def get(self):
|
||||||
res = NoticeConfigCRUD.get_all()
|
res = NoticeConfigCRUD.get_all()
|
||||||
return self.jsonify(res)
|
return self.jsonify(res)
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeAppBotView(APIView):
|
||||||
|
url_prefix = (f'{prefix}/app_bot',)
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
res = NoticeConfigCRUD.get_app_bot()
|
||||||
|
return self.jsonify(res)
|
||||||
|
@@ -97,4 +97,3 @@ BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, "Yes", "YES", "yes", 'Y', 'y'
|
|||||||
|
|
||||||
# # messenger
|
# # messenger
|
||||||
USE_MESSENGER = True
|
USE_MESSENGER = True
|
||||||
MESSENGER_URL = "http://{messenger_url}/v1/message"
|
|
||||||
|
@@ -54,6 +54,48 @@
|
|||||||
<div class="content unicode" style="display: block;">
|
<div class="content unicode" style="display: block;">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">wechatApp</div>
|
||||||
|
<div class="code-name">&#xe88e;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">robot</div>
|
||||||
|
<div class="code-name">&#xe88b;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">feishuApp</div>
|
||||||
|
<div class="code-name">&#xe88c;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">dingdingApp</div>
|
||||||
|
<div class="code-name">&#xe88d;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">email</div>
|
||||||
|
<div class="code-name">&#xe88a;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">setting-feishu</div>
|
||||||
|
<div class="code-name">&#xe887;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">setting-feishu-selected</div>
|
||||||
|
<div class="code-name">&#xe888;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">cmdb-histogram</div>
|
<div class="name">cmdb-histogram</div>
|
||||||
@@ -2100,6 +2142,12 @@
|
|||||||
<div class="code-name">&#xe738;</div>
|
<div class="code-name">&#xe738;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">ops-setting-notice-email-selected</div>
|
||||||
|
<div class="code-name">&#xe889;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">ops-setting-notice</div>
|
<div class="name">ops-setting-notice</div>
|
||||||
@@ -3954,9 +4002,9 @@
|
|||||||
<pre><code class="language-css"
|
<pre><code class="language-css"
|
||||||
>@font-face {
|
>@font-face {
|
||||||
font-family: 'iconfont';
|
font-family: 'iconfont';
|
||||||
src: url('iconfont.woff2?t=1694508259411') format('woff2'),
|
src: url('iconfont.woff2?t=1696815443987') format('woff2'),
|
||||||
url('iconfont.woff?t=1694508259411') format('woff'),
|
url('iconfont.woff?t=1696815443987') format('woff'),
|
||||||
url('iconfont.ttf?t=1694508259411') format('truetype');
|
url('iconfont.ttf?t=1696815443987') format('truetype');
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||||
@@ -3982,6 +4030,69 @@
|
|||||||
<div class="content font-class">
|
<div class="content font-class">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont wechatApp"></span>
|
||||||
|
<div class="name">
|
||||||
|
wechatApp
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.wechatApp
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont robot"></span>
|
||||||
|
<div class="name">
|
||||||
|
robot
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.robot
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont feishuApp"></span>
|
||||||
|
<div class="name">
|
||||||
|
feishuApp
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.feishuApp
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont dingdingApp"></span>
|
||||||
|
<div class="name">
|
||||||
|
dingdingApp
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.dingdingApp
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont email"></span>
|
||||||
|
<div class="name">
|
||||||
|
email
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.email
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont ops-setting-notice-feishu"></span>
|
||||||
|
<div class="name">
|
||||||
|
setting-feishu
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.ops-setting-notice-feishu
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont ops-setting-notice-feishu-selected"></span>
|
||||||
|
<div class="name">
|
||||||
|
setting-feishu-selected
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.ops-setting-notice-feishu-selected
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont cmdb-bar"></span>
|
<span class="icon iconfont cmdb-bar"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
@@ -7051,6 +7162,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont ops-setting-notice-email-selected-copy"></span>
|
||||||
|
<div class="name">
|
||||||
|
ops-setting-notice-email-selected
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.ops-setting-notice-email-selected-copy
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont ops-setting-notice"></span>
|
<span class="icon iconfont ops-setting-notice"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
@@ -9832,6 +9952,62 @@
|
|||||||
<div class="content symbol">
|
<div class="content symbol">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#wechatApp"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">wechatApp</div>
|
||||||
|
<div class="code-name">#wechatApp</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#robot"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">robot</div>
|
||||||
|
<div class="code-name">#robot</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#feishuApp"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">feishuApp</div>
|
||||||
|
<div class="code-name">#feishuApp</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#dingdingApp"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">dingdingApp</div>
|
||||||
|
<div class="code-name">#dingdingApp</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#email"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">email</div>
|
||||||
|
<div class="code-name">#email</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#ops-setting-notice-feishu"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">setting-feishu</div>
|
||||||
|
<div class="code-name">#ops-setting-notice-feishu</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#ops-setting-notice-feishu-selected"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">setting-feishu-selected</div>
|
||||||
|
<div class="code-name">#ops-setting-notice-feishu-selected</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#cmdb-bar"></use>
|
<use xlink:href="#cmdb-bar"></use>
|
||||||
@@ -12560,6 +12736,14 @@
|
|||||||
<div class="code-name">#ops-dot</div>
|
<div class="code-name">#ops-dot</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#ops-setting-notice-email-selected-copy"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">ops-setting-notice-email-selected</div>
|
||||||
|
<div class="code-name">#ops-setting-notice-email-selected-copy</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#ops-setting-notice"></use>
|
<use xlink:href="#ops-setting-notice"></use>
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 3857903 */
|
font-family: "iconfont"; /* Project id 3857903 */
|
||||||
src: url('iconfont.woff2?t=1694508259411') format('woff2'),
|
src: url('iconfont.woff2?t=1696815443987') format('woff2'),
|
||||||
url('iconfont.woff?t=1694508259411') format('woff'),
|
url('iconfont.woff?t=1696815443987') format('woff'),
|
||||||
url('iconfont.ttf?t=1694508259411') format('truetype');
|
url('iconfont.ttf?t=1696815443987') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@@ -13,6 +13,34 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wechatApp:before {
|
||||||
|
content: "\e88e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.robot:before {
|
||||||
|
content: "\e88b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.feishuApp:before {
|
||||||
|
content: "\e88c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.dingdingApp:before {
|
||||||
|
content: "\e88d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.email:before {
|
||||||
|
content: "\e88a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-setting-notice-feishu:before {
|
||||||
|
content: "\e887";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-setting-notice-feishu-selected:before {
|
||||||
|
content: "\e888";
|
||||||
|
}
|
||||||
|
|
||||||
.cmdb-bar:before {
|
.cmdb-bar:before {
|
||||||
content: "\e886";
|
content: "\e886";
|
||||||
}
|
}
|
||||||
@@ -1377,6 +1405,10 @@
|
|||||||
content: "\e738";
|
content: "\e738";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ops-setting-notice-email-selected-copy:before {
|
||||||
|
content: "\e889";
|
||||||
|
}
|
||||||
|
|
||||||
.ops-setting-notice:before {
|
.ops-setting-notice:before {
|
||||||
content: "\e72f";
|
content: "\e72f";
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,55 @@
|
|||||||
"css_prefix_text": "",
|
"css_prefix_text": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "37590786",
|
||||||
|
"name": "wechatApp",
|
||||||
|
"font_class": "wechatApp",
|
||||||
|
"unicode": "e88e",
|
||||||
|
"unicode_decimal": 59534
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37590798",
|
||||||
|
"name": "robot",
|
||||||
|
"font_class": "robot",
|
||||||
|
"unicode": "e88b",
|
||||||
|
"unicode_decimal": 59531
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37590794",
|
||||||
|
"name": "feishuApp",
|
||||||
|
"font_class": "feishuApp",
|
||||||
|
"unicode": "e88c",
|
||||||
|
"unicode_decimal": 59532
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37590791",
|
||||||
|
"name": "dingdingApp",
|
||||||
|
"font_class": "dingdingApp",
|
||||||
|
"unicode": "e88d",
|
||||||
|
"unicode_decimal": 59533
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37590776",
|
||||||
|
"name": "email",
|
||||||
|
"font_class": "email",
|
||||||
|
"unicode": "e88a",
|
||||||
|
"unicode_decimal": 59530
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37537876",
|
||||||
|
"name": "setting-feishu",
|
||||||
|
"font_class": "ops-setting-notice-feishu",
|
||||||
|
"unicode": "e887",
|
||||||
|
"unicode_decimal": 59527
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37537859",
|
||||||
|
"name": "setting-feishu-selected",
|
||||||
|
"font_class": "ops-setting-notice-feishu-selected",
|
||||||
|
"unicode": "e888",
|
||||||
|
"unicode_decimal": 59528
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "37334642",
|
"icon_id": "37334642",
|
||||||
"name": "cmdb-histogram",
|
"name": "cmdb-histogram",
|
||||||
@@ -2392,6 +2441,13 @@
|
|||||||
"unicode": "e738",
|
"unicode": "e738",
|
||||||
"unicode_decimal": 59192
|
"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",
|
"icon_id": "34108346",
|
||||||
"name": "ops-setting-notice",
|
"name": "ops-setting-notice",
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,127 +1,134 @@
|
|||||||
import { axios } from '@/utils/request'
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
export function getEmployeeList(params) {
|
export function getEmployeeList(params) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/common-setting/v1/employee',
|
url: '/common-setting/v1/employee',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: params,
|
params: params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// export function getEmployeeList(params, orderBy) {
|
// export function getEmployeeList(params, orderBy) {
|
||||||
// return axios({
|
// return axios({
|
||||||
// url: '/common-setting/v1/employee' + '/' + orderBy,
|
// url: '/common-setting/v1/employee' + '/' + orderBy,
|
||||||
// method: 'get',
|
// method: 'get',
|
||||||
// params: params,
|
// params: params,
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
export function postEmployee(data) {
|
export function postEmployee(data) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/common-setting/v1/employee',
|
url: '/common-setting/v1/employee',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function getEmployeeCount(params) {
|
export function getEmployeeCount(params) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/common-setting/v1/employee/count',
|
url: '/common-setting/v1/employee/count',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: params,
|
params: params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function deleteEmployee(_id) {
|
export function deleteEmployee(_id) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/${_id}`,
|
url: `/common-setting/v1/employee/${_id}`,
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function putEmployee(_id, data) {
|
export function putEmployee(_id, data) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/${_id}`,
|
url: `/common-setting/v1/employee/${_id}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data,
|
data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function batchEditEmployee(data) {
|
export function batchEditEmployee(data) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/common-setting/v1/employee/batch',
|
url: '/common-setting/v1/employee/batch',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function importEmployee(data) {
|
export function importEmployee(data) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/common-setting/v1/employee/import',
|
url: '/common-setting/v1/employee/import',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEmployeeByUid(uid) {
|
export function getEmployeeByUid(uid) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/by_uid/${uid}`,
|
url: `/common-setting/v1/employee/by_uid/${uid}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateEmployeeByUid(uid, data) {
|
export function updateEmployeeByUid(uid, data) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/by_uid/${uid}`,
|
url: `/common-setting/v1/employee/by_uid/${uid}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updatePasswordByUid(uid, data) {
|
export function updatePasswordByUid(uid, data) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/by_uid/change_password/${uid}`,
|
url: `/common-setting/v1/employee/by_uid/change_password/${uid}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindWxByUid(uid) {
|
export function bindPlatformByUid(platform, uid) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/by_uid/bind_work_wechat/${uid}`,
|
url: `/common-setting/v1/employee/by_uid/bind_notice/${platform}/${uid}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllPosition() {
|
export function unbindPlatformByUid(platform, uid) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/position`,
|
url: `/common-setting/v1/employee/by_uid/bind_notice/${platform}/${uid}`,
|
||||||
method: 'get',
|
method: 'delete',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEmployeeByEmployeeId(employee_id) {
|
export function getAllPosition() {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/common-setting/v1/employee/${employee_id}`,
|
url: `/common-setting/v1/employee/position`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载员工列表
|
export function getEmployeeByEmployeeId(employee_id) {
|
||||||
export function downloadAllEmployee(params) {
|
return axios({
|
||||||
return axios({
|
url: `/common-setting/v1/employee/${employee_id}`,
|
||||||
url: `/common-setting/v1/employee/export_all`,
|
method: 'get',
|
||||||
method: 'get',
|
})
|
||||||
params,
|
}
|
||||||
responseType: 'blob'
|
|
||||||
})
|
// 下载员工列表
|
||||||
}
|
export function downloadAllEmployee(params) {
|
||||||
|
return axios({
|
||||||
export function getEmployeeListByFilter(data) {
|
url: `/common-setting/v1/employee/export_all`,
|
||||||
return axios({
|
method: 'get',
|
||||||
url: '/common-setting/v1/employee/filter',
|
params,
|
||||||
method: 'post',
|
responseType: 'blob'
|
||||||
data
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
export function getEmployeeListByFilter(data) {
|
||||||
export function getNoticeByEmployeeIds(data) {
|
return axios({
|
||||||
return axios({
|
url: '/common-setting/v1/employee/filter',
|
||||||
url: '/common-setting/v1/employee/get_notice_by_ids',
|
method: 'post',
|
||||||
method: 'post',
|
data
|
||||||
data
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
export function getNoticeByEmployeeIds(data) {
|
||||||
|
return axios({
|
||||||
|
url: '/common-setting/v1/employee/get_notice_by_ids',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
40
cmdb-ui/src/api/noticeSetting.js
Normal file
40
cmdb-ui/src/api/noticeSetting.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
export function sendTestEmail(receive_address, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/common-setting/v1/notice_config/send_test_email?receive_address=${receive_address}`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNoticeConfigByPlatform = (platform) => {
|
||||||
|
return axios({
|
||||||
|
url: '/common-setting/v1/notice_config',
|
||||||
|
method: 'get',
|
||||||
|
params: { ...platform },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const postNoticeConfigByPlatform = (data) => {
|
||||||
|
return axios({
|
||||||
|
url: '/common-setting/v1/notice_config',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const putNoticeConfigByPlatform = (id, info) => {
|
||||||
|
return axios({
|
||||||
|
url: `/common-setting/v1/notice_config/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data: info
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNoticeConfigAppBot = () => {
|
||||||
|
return axios({
|
||||||
|
url: `/common-setting/v1/notice_config/app_bot`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
@@ -1,285 +1,293 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
||||||
<div :style="{ width: '50px', height: '24px', position: 'relative' }">
|
<div :style="{ width: '50px', height: '24px', position: 'relative' }">
|
||||||
<treeselect
|
<treeselect
|
||||||
v-if="index"
|
v-if="index"
|
||||||
class="custom-treeselect"
|
class="custom-treeselect"
|
||||||
:style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
|
:style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
|
||||||
v-model="item.type"
|
v-model="item.type"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
searchable
|
searchable
|
||||||
:options="ruleTypeList"
|
:options="ruleTypeList"
|
||||||
:normalizer="
|
:normalizer="
|
||||||
(node) => {
|
(node) => {
|
||||||
return {
|
return {
|
||||||
id: node.value,
|
id: node.value,
|
||||||
label: node.label,
|
label: node.label,
|
||||||
children: node.children,
|
children: node.children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
</treeselect>
|
</treeselect>
|
||||||
</div>
|
</div>
|
||||||
<treeselect
|
<treeselect
|
||||||
class="custom-treeselect"
|
class="custom-treeselect"
|
||||||
:style="{ width: '130px', '--custom-height': '24px' }"
|
:style="{ width: '130px', '--custom-height': '24px' }"
|
||||||
v-model="item.property"
|
v-model="item.property"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
searchable
|
searchable
|
||||||
:options="canSearchPreferenceAttrList"
|
:options="canSearchPreferenceAttrList"
|
||||||
:normalizer="
|
:normalizer="
|
||||||
(node) => {
|
(node) => {
|
||||||
return {
|
return {
|
||||||
id: node.name,
|
id: node.name,
|
||||||
label: node.alias || node.name,
|
label: node.alias || node.name,
|
||||||
children: node.children,
|
children: node.children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
appendToBody
|
||||||
<div
|
:zIndex="1050"
|
||||||
:title="node.label"
|
>
|
||||||
slot="option-label"
|
<div
|
||||||
slot-scope="{ node }"
|
:title="node.label"
|
||||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
slot="option-label"
|
||||||
>
|
slot-scope="{ node }"
|
||||||
<ValueTypeMapIcon :attr="node.raw" />
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
{{ node.label }}
|
>
|
||||||
</div>
|
<ValueTypeMapIcon :attr="node.raw" />
|
||||||
<div
|
{{ node.label }}
|
||||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
</div>
|
||||||
slot="value-label"
|
<div
|
||||||
slot-scope="{ node }"
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
>
|
slot="value-label"
|
||||||
<ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
|
slot-scope="{ node }"
|
||||||
</div>
|
>
|
||||||
</treeselect>
|
<ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
|
||||||
<treeselect
|
</div>
|
||||||
class="custom-treeselect"
|
</treeselect>
|
||||||
:style="{ width: '100px', '--custom-height': '24px' }"
|
<treeselect
|
||||||
v-model="item.exp"
|
class="custom-treeselect"
|
||||||
:multiple="false"
|
:style="{ width: '100px', '--custom-height': '24px' }"
|
||||||
:clearable="false"
|
v-model="item.exp"
|
||||||
searchable
|
:multiple="false"
|
||||||
:options="[...getExpListByProperty(item.property), ...advancedExpList]"
|
:clearable="false"
|
||||||
:normalizer="
|
searchable
|
||||||
(node) => {
|
:options="[...getExpListByProperty(item.property), ...advancedExpList]"
|
||||||
return {
|
:normalizer="
|
||||||
id: node.value,
|
(node) => {
|
||||||
label: node.label,
|
return {
|
||||||
children: node.children,
|
id: node.value,
|
||||||
}
|
label: node.label,
|
||||||
}
|
children: node.children,
|
||||||
"
|
}
|
||||||
@select="(value) => handleChangeExp(value, item, index)"
|
}
|
||||||
>
|
"
|
||||||
</treeselect>
|
@select="(value) => handleChangeExp(value, item, index)"
|
||||||
<treeselect
|
appendToBody
|
||||||
class="custom-treeselect"
|
:zIndex="1050"
|
||||||
:style="{ width: '175px', '--custom-height': '24px' }"
|
>
|
||||||
v-model="item.value"
|
</treeselect>
|
||||||
:multiple="false"
|
<treeselect
|
||||||
:clearable="false"
|
class="custom-treeselect"
|
||||||
searchable
|
:style="{ width: '175px', '--custom-height': '24px' }"
|
||||||
v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
|
v-model="item.value"
|
||||||
:options="getChoiceValueByProperty(item.property)"
|
:multiple="false"
|
||||||
placeholder="请选择"
|
:clearable="false"
|
||||||
:normalizer="
|
searchable
|
||||||
(node) => {
|
v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
|
||||||
return {
|
:options="getChoiceValueByProperty(item.property)"
|
||||||
id: node[0],
|
placeholder="请选择"
|
||||||
label: node[0],
|
:normalizer="
|
||||||
children: node.children,
|
(node) => {
|
||||||
}
|
return {
|
||||||
}
|
id: node[0],
|
||||||
"
|
label: node[0],
|
||||||
>
|
children: node.children,
|
||||||
<div
|
}
|
||||||
:title="node.label"
|
}
|
||||||
slot="option-label"
|
"
|
||||||
slot-scope="{ node }"
|
appendToBody
|
||||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
:zIndex="1050"
|
||||||
>
|
>
|
||||||
{{ node.label }}
|
<div
|
||||||
</div>
|
:title="node.label"
|
||||||
</treeselect>
|
slot="option-label"
|
||||||
<a-input-group
|
slot-scope="{ node }"
|
||||||
size="small"
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
compact
|
>
|
||||||
v-else-if="item.exp === 'range' || item.exp === '~range'"
|
{{ node.label }}
|
||||||
:style="{ width: '175px' }"
|
</div>
|
||||||
>
|
</treeselect>
|
||||||
<a-input class="ops-input" size="small" v-model="item.min" :style="{ width: '78px' }" placeholder="最小值" />
|
<a-input-group
|
||||||
~
|
size="small"
|
||||||
<a-input class="ops-input" size="small" v-model="item.max" :style="{ width: '78px' }" placeholder="最大值" />
|
compact
|
||||||
</a-input-group>
|
v-else-if="item.exp === 'range' || item.exp === '~range'"
|
||||||
<a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
|
:style="{ width: '175px' }"
|
||||||
<treeselect
|
>
|
||||||
class="custom-treeselect"
|
<a-input class="ops-input" size="small" v-model="item.min" :style="{ width: '78px' }" placeholder="最小值" />
|
||||||
:style="{ width: '60px', '--custom-height': '24px' }"
|
~
|
||||||
v-model="item.compareType"
|
<a-input class="ops-input" size="small" v-model="item.max" :style="{ width: '78px' }" placeholder="最大值" />
|
||||||
:multiple="false"
|
</a-input-group>
|
||||||
:clearable="false"
|
<a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
|
||||||
searchable
|
<treeselect
|
||||||
:options="compareTypeList"
|
class="custom-treeselect"
|
||||||
:normalizer="
|
:style="{ width: '60px', '--custom-height': '24px' }"
|
||||||
(node) => {
|
v-model="item.compareType"
|
||||||
return {
|
:multiple="false"
|
||||||
id: node.value,
|
:clearable="false"
|
||||||
label: node.label,
|
searchable
|
||||||
children: node.children,
|
:options="compareTypeList"
|
||||||
}
|
:normalizer="
|
||||||
}
|
(node) => {
|
||||||
"
|
return {
|
||||||
>
|
id: node.value,
|
||||||
</treeselect>
|
label: node.label,
|
||||||
<a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
|
children: node.children,
|
||||||
</a-input-group>
|
}
|
||||||
<a-input
|
}
|
||||||
v-else-if="item.exp !== 'value' && item.exp !== '~value'"
|
"
|
||||||
size="small"
|
appendToBody
|
||||||
v-model="item.value"
|
:zIndex="1050"
|
||||||
:placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
|
>
|
||||||
class="ops-input"
|
</treeselect>
|
||||||
></a-input>
|
<a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
|
||||||
<div v-else :style="{ width: '175px' }"></div>
|
</a-input-group>
|
||||||
<a-tooltip title="复制">
|
<a-input
|
||||||
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
|
v-else-if="item.exp !== 'value' && item.exp !== '~value'"
|
||||||
</a-tooltip>
|
size="small"
|
||||||
<a-tooltip title="删除">
|
v-model="item.value"
|
||||||
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
|
:placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
|
||||||
</a-tooltip>
|
class="ops-input"
|
||||||
</a-space>
|
></a-input>
|
||||||
<div class="table-filter-add">
|
<div v-else :style="{ width: '175px' }"></div>
|
||||||
<a @click="handleAddRule">+ 新增</a>
|
<a-tooltip title="复制">
|
||||||
</div>
|
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
|
||||||
</div>
|
</a-tooltip>
|
||||||
</template>
|
<a-tooltip title="删除">
|
||||||
|
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
|
||||||
<script>
|
</a-tooltip>
|
||||||
import _ from 'lodash'
|
</a-space>
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
<div class="table-filter-add">
|
||||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
|
<a @click="handleAddRule">+ 新增</a>
|
||||||
import ValueTypeMapIcon from '../CMDBValueTypeMapIcon'
|
</div>
|
||||||
|
</div>
|
||||||
export default {
|
</template>
|
||||||
name: 'Expression',
|
|
||||||
components: { ValueTypeMapIcon },
|
<script>
|
||||||
model: {
|
import _ from 'lodash'
|
||||||
prop: 'value',
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
event: 'change',
|
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
|
||||||
},
|
import ValueTypeMapIcon from '../CMDBValueTypeMapIcon'
|
||||||
props: {
|
|
||||||
value: {
|
export default {
|
||||||
type: Array,
|
name: 'Expression',
|
||||||
default: () => [],
|
components: { ValueTypeMapIcon },
|
||||||
},
|
model: {
|
||||||
canSearchPreferenceAttrList: {
|
prop: 'value',
|
||||||
type: Array,
|
event: 'change',
|
||||||
required: true,
|
},
|
||||||
default: () => [],
|
props: {
|
||||||
},
|
value: {
|
||||||
},
|
type: Array,
|
||||||
data() {
|
default: () => [],
|
||||||
return {
|
},
|
||||||
ruleTypeList,
|
canSearchPreferenceAttrList: {
|
||||||
expList,
|
type: Array,
|
||||||
advancedExpList,
|
required: true,
|
||||||
compareTypeList,
|
default: () => [],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
data() {
|
||||||
ruleList: {
|
return {
|
||||||
get() {
|
ruleTypeList,
|
||||||
return this.value
|
expList,
|
||||||
},
|
advancedExpList,
|
||||||
set(val) {
|
compareTypeList,
|
||||||
this.$emit('change', val)
|
}
|
||||||
return val
|
},
|
||||||
},
|
computed: {
|
||||||
},
|
ruleList: {
|
||||||
},
|
get() {
|
||||||
methods: {
|
return this.value
|
||||||
getExpListByProperty(property) {
|
},
|
||||||
if (property) {
|
set(val) {
|
||||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
this.$emit('change', val)
|
||||||
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
return val
|
||||||
return [
|
},
|
||||||
{ value: 'is', label: '等于' },
|
},
|
||||||
{ value: '~is', label: '不等于' },
|
},
|
||||||
{ value: '~value', label: '为空' }, // 为空的定义有点绕
|
methods: {
|
||||||
{ value: 'value', label: '不为空' },
|
getExpListByProperty(property) {
|
||||||
]
|
if (property) {
|
||||||
}
|
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||||
return this.expList
|
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
||||||
}
|
return [
|
||||||
return this.expList
|
{ value: 'is', label: '等于' },
|
||||||
},
|
{ value: '~is', label: '不等于' },
|
||||||
isChoiceByProperty(property) {
|
{ value: '~value', label: '为空' }, // 为空的定义有点绕
|
||||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
{ value: 'value', label: '不为空' },
|
||||||
if (_find) {
|
]
|
||||||
return _find.is_choice
|
}
|
||||||
}
|
return this.expList
|
||||||
return false
|
}
|
||||||
},
|
return this.expList
|
||||||
handleAddRule() {
|
},
|
||||||
this.ruleList.push({
|
isChoiceByProperty(property) {
|
||||||
id: uuidv4(),
|
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||||
type: 'and',
|
if (_find) {
|
||||||
property: this.canSearchPreferenceAttrList[0]?.name,
|
return _find.is_choice
|
||||||
exp: 'is',
|
}
|
||||||
value: null,
|
return false
|
||||||
})
|
},
|
||||||
this.$emit('change', this.ruleList)
|
handleAddRule() {
|
||||||
},
|
this.ruleList.push({
|
||||||
handleCopyRule(item) {
|
id: uuidv4(),
|
||||||
this.ruleList.push({ ...item, id: uuidv4() })
|
type: 'and',
|
||||||
this.$emit('change', this.ruleList)
|
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||||
},
|
exp: 'is',
|
||||||
handleDeleteRule(item) {
|
value: null,
|
||||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
})
|
||||||
if (idx > -1) {
|
this.$emit('change', this.ruleList)
|
||||||
this.ruleList.splice(idx, 1)
|
},
|
||||||
}
|
handleCopyRule(item) {
|
||||||
this.$emit('change', this.ruleList)
|
this.ruleList.push({ ...item, id: uuidv4() })
|
||||||
},
|
this.$emit('change', this.ruleList)
|
||||||
getChoiceValueByProperty(property) {
|
},
|
||||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
handleDeleteRule(item) {
|
||||||
if (_find) {
|
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||||
return _find.choice_value
|
if (idx > -1) {
|
||||||
}
|
this.ruleList.splice(idx, 1)
|
||||||
return []
|
}
|
||||||
},
|
this.$emit('change', this.ruleList)
|
||||||
handleChangeExp({ value }, item, index) {
|
},
|
||||||
const _ruleList = _.cloneDeep(this.ruleList)
|
getChoiceValueByProperty(property) {
|
||||||
if (value === 'range') {
|
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||||
_ruleList[index] = {
|
if (_find) {
|
||||||
..._ruleList[index],
|
return _find.choice_value
|
||||||
min: '',
|
}
|
||||||
max: '',
|
return []
|
||||||
exp: value,
|
},
|
||||||
}
|
handleChangeExp({ value }, item, index) {
|
||||||
} else if (value === 'compare') {
|
const _ruleList = _.cloneDeep(this.ruleList)
|
||||||
_ruleList[index] = {
|
if (value === 'range') {
|
||||||
..._ruleList[index],
|
_ruleList[index] = {
|
||||||
compareType: '1',
|
..._ruleList[index],
|
||||||
exp: value,
|
min: '',
|
||||||
}
|
max: '',
|
||||||
} else {
|
exp: value,
|
||||||
_ruleList[index] = {
|
}
|
||||||
..._ruleList[index],
|
} else if (value === 'compare') {
|
||||||
exp: value,
|
_ruleList[index] = {
|
||||||
}
|
..._ruleList[index],
|
||||||
}
|
compareType: '1',
|
||||||
this.ruleList = _ruleList
|
exp: value,
|
||||||
this.$emit('change', this.ruleList)
|
}
|
||||||
},
|
} else {
|
||||||
},
|
_ruleList[index] = {
|
||||||
}
|
..._ruleList[index],
|
||||||
</script>
|
exp: value,
|
||||||
|
}
|
||||||
<style></style>
|
}
|
||||||
|
this.ruleList = _ruleList
|
||||||
|
this.$emit('change', this.ruleList)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
|
@@ -205,11 +205,3 @@ export function ciTypeFilterPermissions(type_id) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllDagsName(params) {
|
|
||||||
return axios({
|
|
||||||
url: '/v1/dag/all_names',
|
|
||||||
method: 'GET',
|
|
||||||
params: params
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@@ -83,10 +83,10 @@ export default {
|
|||||||
getContent() {
|
getContent() {
|
||||||
const html = _.cloneDeep(this.editor.getHtml())
|
const html = _.cloneDeep(this.editor.getHtml())
|
||||||
const _html = html.replace(
|
const _html = html.replace(
|
||||||
/<span data-w-e-type="attachment" data-w-e-is-void data-w-e-is-inline.*?<\/span>/gm,
|
/<span data-w-e-type="attachment" (data-w-e-is-void|data-w-e-is-void="") (data-w-e-is-inline|data-w-e-is-inline="").*?<\/span>/gm,
|
||||||
(value) => {
|
(value) => {
|
||||||
const _match = value.match(/(?<=data-attachmentValue=").*?(?=")/)
|
const _match = value.match(/(?<=data-attachment(V|v)alue=").*?(?=")/)
|
||||||
return `{{${_match}}}`
|
return `{{${_match[0]}}}`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return { body_html: html, body: _html }
|
return { body_html: html, body: _html }
|
||||||
|
@@ -59,7 +59,7 @@ export default {
|
|||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
segmentedContentTypes,
|
segmentedContentTypes,
|
||||||
// contentType: 'none',
|
// contentType: 'none',
|
||||||
jsonData: {},
|
jsonData: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -74,6 +74,9 @@ export default {
|
|||||||
}
|
}
|
||||||
div.jsoneditor {
|
div.jsoneditor {
|
||||||
border-color: #f3f4f6;
|
border-color: #f3f4f6;
|
||||||
|
.jsoneditor-outer {
|
||||||
|
border-color: #f3f4f6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -1,379 +1,387 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :style="{ height: `${windowHeight - 156}px`, overflow: 'auto', position: 'relative' }">
|
<div :style="{ height: `${windowHeight - 156}px`, overflow: 'auto', position: 'relative' }">
|
||||||
<a
|
<a
|
||||||
v-if="!adrIsInner"
|
v-if="!adrIsInner"
|
||||||
:style="{ position: 'absolute', right: 0, top: 0 }"
|
:style="{ position: 'absolute', right: 0, top: 0 }"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
$emit('openEditDrawer', currentAdr, 'edit', 'agent')
|
$emit('openEditDrawer', currentAdr, 'edit', 'agent')
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-space>
|
<a-space>
|
||||||
<ops-icon type="icon-xianxing-edit" />
|
<ops-icon type="icon-xianxing-edit" />
|
||||||
<span>编辑</span>
|
<span>编辑</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a>
|
</a>
|
||||||
<div class="attr-ad-header">字段映射</div>
|
<div class="attr-ad-header">字段映射</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
v-if="adrType === 'agent'"
|
v-if="adrType === 'agent'"
|
||||||
ref="xTable"
|
ref="xTable"
|
||||||
:edit-config="{ trigger: 'click', mode: 'cell' }"
|
:edit-config="{ trigger: 'click', mode: 'cell' }"
|
||||||
size="mini"
|
size="mini"
|
||||||
stripe
|
stripe
|
||||||
class="ops-stripe-table"
|
class="ops-stripe-table"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
:style="{ width: '700px', marginBottom: '20px' }"
|
:style="{ width: '700px', marginBottom: '20px' }"
|
||||||
>
|
>
|
||||||
<vxe-colgroup title="自动发现">
|
<vxe-colgroup title="自动发现">
|
||||||
<vxe-column field="name" title="名称"> </vxe-column>
|
<vxe-column field="name" title="名称"> </vxe-column>
|
||||||
<vxe-column field="type" title="类型"> </vxe-column>
|
<vxe-column field="type" title="类型"> </vxe-column>
|
||||||
<vxe-column field="desc" title="描述"> </vxe-column>
|
<vxe-column field="desc" title="描述"> </vxe-column>
|
||||||
</vxe-colgroup>
|
</vxe-colgroup>
|
||||||
<vxe-colgroup title="模型属性">
|
<vxe-colgroup title="模型属性">
|
||||||
<vxe-column field="attr" title="名称" :edit-render="{}">
|
<vxe-column field="attr" title="名称" :edit-render="{}">
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
{{ row.attr }}
|
{{ row.attr }}
|
||||||
</template>
|
</template>
|
||||||
<template #edit="{ row }">
|
<template #edit="{ row }">
|
||||||
<vxe-select
|
<vxe-select
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
v-model="row.attr"
|
v-model="row.attr"
|
||||||
type="text"
|
type="text"
|
||||||
:options="ciTypeAttributes"
|
:options="ciTypeAttributes"
|
||||||
transfer
|
transfer
|
||||||
></vxe-select>
|
></vxe-select>
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
</vxe-colgroup>
|
</vxe-colgroup>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
<HttpSnmpAD
|
<HttpSnmpAD
|
||||||
v-else
|
v-else
|
||||||
:isEdit="true"
|
:isEdit="true"
|
||||||
ref="httpSnmpAd"
|
ref="httpSnmpAd"
|
||||||
:ruleType="adrType"
|
:ruleType="adrType"
|
||||||
:ruleName="adrName"
|
:ruleName="adrName"
|
||||||
:ciTypeAttributes="ciTypeAttributes"
|
:ciTypeAttributes="ciTypeAttributes"
|
||||||
:adCITypeList="adCITypeList"
|
:adCITypeList="adCITypeList"
|
||||||
:currentTab="currentTab"
|
:currentTab="currentTab"
|
||||||
:style="{ marginBottom: '20px' }"
|
:style="{ marginBottom: '20px' }"
|
||||||
/>
|
/>
|
||||||
<a-form-model
|
<a-form-model
|
||||||
v-if="adrType === 'http'"
|
v-if="adrType === 'http'"
|
||||||
:model="form2"
|
:model="form2"
|
||||||
:labelCol="{ span: 2 }"
|
:labelCol="{ span: 2 }"
|
||||||
:wrapperCol="{ span: 8 }"
|
:wrapperCol="{ span: 8 }"
|
||||||
:style="{ margin: '20px 0' }"
|
:style="{ margin: '20px 0' }"
|
||||||
>
|
>
|
||||||
<a-form-model-item label="key">
|
<a-form-model-item label="key">
|
||||||
<a-input-password v-model="form2.key" />
|
<a-input-password v-model="form2.key" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="secret">
|
<a-form-model-item label="secret">
|
||||||
<a-input-password v-model="form2.secret" />
|
<a-input-password v-model="form2.secret" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
</a-form-model>
|
</a-form-model>
|
||||||
<a-form :form="form3" v-if="adrType === 'snmp'" class="attr-ad-snmp-form">
|
<a-form :form="form3" v-if="adrType === 'snmp'" class="attr-ad-snmp-form">
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item label="节点" :labelCol="{ span: 2 }" :wrapperCol="{ span: 20 }">
|
<a-form-item label="节点" :labelCol="{ span: 2 }" :wrapperCol="{ span: 20 }">
|
||||||
<MonitorNodeSetting ref="monitorNodeSetting" :initNodes="nodes" :form="form3" />
|
<MonitorNodeSetting ref="monitorNodeSetting" :initNodes="nodes" :form="form3" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div class="attr-ad-header">执行配置</div>
|
<div class="attr-ad-header">执行配置</div>
|
||||||
<a-form-model :model="form" :labelCol="{ span: 2 }" :wrapperCol="{ span: 20 }">
|
<a-form-model :model="form" :labelCol="{ span: 2 }" :wrapperCol="{ span: 20 }">
|
||||||
<a-form-model-item label="执行机器">
|
<a-form-model-item label="执行机器">
|
||||||
<CustomRadio v-model="agent_type" :radioList="agentTypeRadioList">
|
<CustomRadio v-model="agent_type" :radioList="agentTypeRadioList">
|
||||||
<a-input
|
<a-input
|
||||||
:style="{ width: '300px' }"
|
:style="{ width: '300px' }"
|
||||||
placeholder="请输入以0x开头的16进制OneAgent ID"
|
placeholder="请输入以0x开头的16进制OneAgent ID"
|
||||||
v-show="agent_type === 'agent_id'"
|
v-show="agent_type === 'agent_id'"
|
||||||
slot="extra_agent_id"
|
slot="extra_agent_id"
|
||||||
v-model="form.agent_id"
|
v-model="form.agent_id"
|
||||||
/>
|
/>
|
||||||
<a-input
|
<a-input
|
||||||
:style="{ width: '300px' }"
|
:style="{ width: '300px' }"
|
||||||
v-show="agent_type === 'query_expr'"
|
v-show="agent_type === 'query_expr'"
|
||||||
slot="extra_query_expr"
|
slot="extra_query_expr"
|
||||||
placeholder="从CMDB选择"
|
placeholder="从CMDB选择"
|
||||||
v-model="form.query_expr"
|
v-model="form.query_expr"
|
||||||
>
|
>
|
||||||
<a @click="handleOpenCmdb" slot="suffix"><a-icon type="menu"/></a>
|
<a @click="handleOpenCmdb" slot="suffix"><a-icon type="menu"/></a>
|
||||||
</a-input>
|
</a-input>
|
||||||
</CustomRadio>
|
</CustomRadio>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="自动入库">
|
<a-form-model-item label="自动入库">
|
||||||
<a-switch v-model="form.auto_accept" />
|
<a-switch v-model="form.auto_accept" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
</a-form-model>
|
</a-form-model>
|
||||||
<div class="attr-ad-header">采集频率</div>
|
<div class="attr-ad-header">采集频率</div>
|
||||||
<CustomRadio :radioList="radioList" v-model="interval">
|
<CustomRadio :radioList="radioList" v-model="interval">
|
||||||
<span v-show="interval === 'interval'" slot="extra_interval">
|
<span v-show="interval === 'interval'" slot="extra_interval">
|
||||||
<a-input-number v-model="intervalValue" :min="1" /> 秒
|
<a-input-number v-model="intervalValue" :min="1" /> 秒
|
||||||
</span>
|
</span>
|
||||||
</CustomRadio>
|
</CustomRadio>
|
||||||
|
|
||||||
<div class="attr-ad-footer">
|
<div class="attr-ad-footer">
|
||||||
<a-button type="primary" @click="handleSave">保存</a-button>
|
<a-button type="primary" @click="handleSave">保存</a-button>
|
||||||
</div>
|
</div>
|
||||||
<CMDBExprDrawer ref="cmdbDrawer" @copySuccess="copySuccess" />
|
<CMDBExprDrawer ref="cmdbDrawer" @copySuccess="copySuccess" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import Vcrontab from '@/components/Crontab'
|
import Vcrontab from '@/components/Crontab'
|
||||||
import { putCITypeDiscovery } from '../../api/discovery'
|
import { putCITypeDiscovery } from '../../api/discovery'
|
||||||
import HttpSnmpAD from '../../components/httpSnmpAD'
|
import HttpSnmpAD from '../../components/httpSnmpAD'
|
||||||
import CMDBExprDrawer from '@/components/CMDBExprDrawer'
|
import CMDBExprDrawer from '@/components/CMDBExprDrawer'
|
||||||
import MonitorNodeSetting from '@/components/MonitorNodeSetting'
|
import MonitorNodeSetting from '@/components/MonitorNodeSetting'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AttrADTabpane',
|
name: 'AttrADTabpane',
|
||||||
components: { Vcrontab, HttpSnmpAD, CMDBExprDrawer, MonitorNodeSetting },
|
components: { Vcrontab, HttpSnmpAD, CMDBExprDrawer, MonitorNodeSetting },
|
||||||
props: {
|
props: {
|
||||||
currentTab: {
|
currentTab: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
adrList: {
|
adrList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
adCITypeList: {
|
adCITypeList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
currentAdt: {
|
currentAdt: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
currentAdr: {
|
currentAdr: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
ciTypeAttributes: {
|
ciTypeAttributes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const radioList = [
|
const radioList = [
|
||||||
{ value: 'interval', label: '按间隔' },
|
{ value: 'interval', label: '按间隔' },
|
||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
radioList,
|
radioList,
|
||||||
tableData: [],
|
tableData: [],
|
||||||
form: {
|
form: {
|
||||||
agent_id: '',
|
agent_id: '',
|
||||||
auto_accept: false,
|
auto_accept: false,
|
||||||
query_expr: '',
|
query_expr: '',
|
||||||
},
|
},
|
||||||
form2: {
|
form2: {
|
||||||
key: '',
|
key: '',
|
||||||
secret: '',
|
secret: '',
|
||||||
},
|
},
|
||||||
interval: 'interval', // interval cron
|
interval: 'interval', // interval cron
|
||||||
cron: '',
|
cron: '',
|
||||||
intervalValue: 3,
|
intervalValue: 3,
|
||||||
agent_type: 'agent_id',
|
agent_type: 'agent_id',
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
ip: '',
|
ip: '',
|
||||||
community: '',
|
community: '',
|
||||||
version: '',
|
version: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
windowHeight: (state) => state.windowHeight,
|
windowHeight: (state) => state.windowHeight,
|
||||||
userRoles: (state) => state.user.roles,
|
userRoles: (state) => state.user.roles,
|
||||||
}),
|
}),
|
||||||
adrType() {
|
adrType() {
|
||||||
return this.currentAdr.type
|
return this.currentAdr.type
|
||||||
},
|
},
|
||||||
adrName() {
|
adrName() {
|
||||||
return this.currentAdr.name
|
return this.currentAdr.name
|
||||||
},
|
},
|
||||||
adrIsInner() {
|
adrIsInner() {
|
||||||
return this.currentAdr.is_inner
|
return this.currentAdr.is_inner
|
||||||
},
|
},
|
||||||
agentTypeRadioList() {
|
agentTypeRadioList() {
|
||||||
const { permissions = [] } = this.userRoles
|
const { permissions = [] } = this.userRoles
|
||||||
if (permissions.includes('cmdb_admin') || permissions.includes('admin')) {
|
if (permissions.includes('cmdb_admin') || permissions.includes('admin')) {
|
||||||
return [
|
return [
|
||||||
{ value: 'all', label: '所有节点' },
|
{ value: 'all', label: '所有节点' },
|
||||||
{ value: 'agent_id', label: '指定节点' },
|
{ value: 'agent_id', label: '指定节点' },
|
||||||
{ value: 'query_expr', label: '从CMDB中选择 ' },
|
{ value: 'query_expr', label: '从CMDB中选择 ' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
{ value: 'agent_id', label: '指定节点' },
|
{ value: 'agent_id', label: '指定节点' },
|
||||||
{ value: 'query_expr', label: '从CMDB中选择 ' },
|
{ value: 'query_expr', label: '从CMDB中选择 ' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {},
|
mounted() {},
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
const _find = this.adrList.find((item) => Number(item.id) === Number(this.currentTab))
|
const _find = this.adrList.find((item) => Number(item.id) === Number(this.currentTab))
|
||||||
const _findADT = this.adCITypeList.find((item) => Number(item.adr_id) === Number(this.currentTab))
|
const _findADT = this.adCITypeList.find((item) => Number(item.adr_id) === Number(this.currentTab))
|
||||||
if (this.adrType === 'http') {
|
if (this.adrType === 'http') {
|
||||||
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
||||||
this.form2 = {
|
this.form2 = {
|
||||||
key,
|
key,
|
||||||
secret,
|
secret,
|
||||||
}
|
}
|
||||||
this.$refs.httpSnmpAd.setCurrentCate(category)
|
this.$refs.httpSnmpAd.setCurrentCate(category)
|
||||||
}
|
}
|
||||||
if (this.adrType === 'snmp') {
|
if (this.adrType === 'snmp') {
|
||||||
this.nodes = _findADT?.extra_option?.nodes ?? [
|
this.nodes = _findADT?.extra_option?.nodes ?? [
|
||||||
{
|
{
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
ip: '',
|
ip: '',
|
||||||
community: '',
|
community: '',
|
||||||
version: '',
|
version: '',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.monitorNodeSetting.initNodesFunc()
|
this.$refs.monitorNodeSetting.initNodesFunc()
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.monitorNodeSetting.setNodeField()
|
this.$refs.monitorNodeSetting.setNodeField()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.adrType === 'agent') {
|
if (this.adrType === 'agent') {
|
||||||
this.tableData = (_find?.attributes || []).map((item) => {
|
this.tableData = (_find?.attributes || []).map((item) => {
|
||||||
if (_findADT.attributes) {
|
if (_findADT.attributes) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
attr: _findADT.attributes[`${item.name}`],
|
attr: _findADT.attributes[`${item.name}`],
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const _find = this.ciTypeAttributes.find((ele) => ele.name === item.name)
|
const _find = this.ciTypeAttributes.find((ele) => ele.name === item.name)
|
||||||
if (_find) {
|
if (_find) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
attr: _find.name,
|
attr: _find.name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.form = {
|
this.form = {
|
||||||
auto_accept: _findADT?.auto_accept || false,
|
auto_accept: _findADT?.auto_accept || false,
|
||||||
agent_id: _findADT.agent_id || '',
|
agent_id: _findADT.agent_id || '',
|
||||||
query_expr: _findADT.query_expr || '',
|
query_expr: _findADT.query_expr || '',
|
||||||
}
|
}
|
||||||
if (_findADT.query_expr) {
|
if (_findADT.query_expr) {
|
||||||
this.agent_type = 'query_expr'
|
this.agent_type = 'query_expr'
|
||||||
} else if (_findADT.agent_id) {
|
} else if (_findADT.agent_id) {
|
||||||
this.agent_type = 'agent_id'
|
this.agent_type = 'agent_id'
|
||||||
} else {
|
} else {
|
||||||
this.agent_type = 'agent_id'
|
this.agent_type = this.agentTypeRadioList[0].value
|
||||||
}
|
}
|
||||||
if (_findADT.interval || (!_findADT.interval && !_findADT.cron)) {
|
if (_findADT.interval || (!_findADT.interval && !_findADT.cron)) {
|
||||||
this.interval = 'interval'
|
this.interval = 'interval'
|
||||||
this.intervalValue = _findADT.interval || ''
|
this.intervalValue = _findADT.interval || ''
|
||||||
} else {
|
} else {
|
||||||
this.interval = 'cron'
|
this.interval = 'cron'
|
||||||
this.cron = `0 ${_findADT.cron}`
|
this.cron = `0 ${_findADT.cron}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getAttrNameByAttrName(attrName) {
|
getAttrNameByAttrName(attrName) {
|
||||||
const _find = this.ciTypeAttributes.find((item) => item.name === attrName)
|
const _find = this.ciTypeAttributes.find((item) => item.name === attrName)
|
||||||
return _find?.alias || _find?.name || ''
|
return _find?.alias || _find?.name || ''
|
||||||
},
|
},
|
||||||
crontabFill(cron) {
|
crontabFill(cron) {
|
||||||
this.cron = cron
|
this.cron = cron
|
||||||
},
|
},
|
||||||
handleSave() {
|
handleSave() {
|
||||||
const { currentAdt } = this
|
const { currentAdt } = this
|
||||||
let params
|
let params
|
||||||
if (this.adrType === 'http') {
|
if (this.adrType === 'http') {
|
||||||
params = {
|
params = {
|
||||||
extra_option: {
|
extra_option: {
|
||||||
...this.form2,
|
...this.form2,
|
||||||
category: this.$refs.httpSnmpAd.currentCate,
|
category: this.$refs.httpSnmpAd.currentCate,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.adrType === 'snmp') {
|
if (this.adrType === 'snmp') {
|
||||||
params = {
|
params = {
|
||||||
extra_option: { nodes: this.$refs.monitorNodeSetting?.getNodeValue() ?? [] },
|
extra_option: { nodes: this.$refs.monitorNodeSetting?.getNodeValue() ?? [] },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.adrType === 'agent') {
|
if (this.adrType === 'agent') {
|
||||||
const $table = this.$refs.xTable
|
const $table = this.$refs.xTable
|
||||||
const { fullData: _tableData } = $table.getTableData()
|
const { fullData: _tableData } = $table.getTableData()
|
||||||
const attributes = {}
|
const attributes = {}
|
||||||
_tableData.forEach((td) => {
|
_tableData.forEach((td) => {
|
||||||
if (td.attr) {
|
if (td.attr) {
|
||||||
attributes[`${td.name}`] = td.attr
|
attributes[`${td.name}`] = td.attr
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
params = {
|
params = {
|
||||||
...params,
|
...params,
|
||||||
attributes,
|
attributes,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const _tableData = this.$refs.httpSnmpAd.getTableData()
|
const _tableData = this.$refs.httpSnmpAd.getTableData()
|
||||||
const attributes = {}
|
const attributes = {}
|
||||||
_tableData.forEach((td) => {
|
_tableData.forEach((td) => {
|
||||||
if (td.attr) {
|
if (td.attr) {
|
||||||
attributes[`${td.name}`] = td.attr
|
attributes[`${td.name}`] = td.attr
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
params = {
|
params = {
|
||||||
...params,
|
...params,
|
||||||
attributes,
|
attributes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.interval === 'cron') {
|
if (this.interval === 'cron') {
|
||||||
this.$refs.cronTab.submitFill()
|
this.$refs.cronTab.submitFill()
|
||||||
}
|
}
|
||||||
params = {
|
params = {
|
||||||
...params,
|
...params,
|
||||||
...this.form,
|
...this.form,
|
||||||
type_id: this.CITypeId,
|
type_id: this.CITypeId,
|
||||||
adr_id: currentAdt.adr_id,
|
adr_id: currentAdt.adr_id,
|
||||||
interval: this.interval === 'interval' ? this.intervalValue : null,
|
interval: this.interval === 'interval' ? this.intervalValue : null,
|
||||||
cron: this.interval === 'cron' ? this.cron : null,
|
cron: this.interval === 'cron' ? this.cron : null,
|
||||||
}
|
}
|
||||||
if (this.agent_type === 'agent_id' || this.agent_type === 'all') {
|
if (this.agent_type === 'agent_id' || this.agent_type === 'all') {
|
||||||
params.query_expr = ''
|
params.query_expr = ''
|
||||||
}
|
if (this.agent_type === 'agent_id' && !params.agent_id) {
|
||||||
if (this.agent_type === 'query_expr' || this.agent_type === 'all') {
|
this.$message.error('请填写指定节点!')
|
||||||
params.agent_id = ''
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
putCITypeDiscovery(currentAdt.id, params).then((res) => {
|
if (this.agent_type === 'query_expr' || this.agent_type === 'all') {
|
||||||
this.$message.success('保存成功')
|
params.agent_id = ''
|
||||||
})
|
if (this.agent_type === 'query_expr' && !params.query_expr) {
|
||||||
},
|
this.$message.error('请从cmdb中选择!')
|
||||||
handleOpenCmdb() {
|
return
|
||||||
this.$refs.cmdbDrawer.open()
|
}
|
||||||
},
|
}
|
||||||
copySuccess(text) {
|
|
||||||
this.form = {
|
putCITypeDiscovery(currentAdt.id, params).then((res) => {
|
||||||
...this.form,
|
this.$message.success('保存成功')
|
||||||
query_expr: `${text}`,
|
})
|
||||||
}
|
},
|
||||||
},
|
handleOpenCmdb() {
|
||||||
},
|
this.$refs.cmdbDrawer.open()
|
||||||
}
|
},
|
||||||
</script>
|
copySuccess(text) {
|
||||||
|
this.form = {
|
||||||
<style lang="less">
|
...this.form,
|
||||||
.attr-ad-snmp-form {
|
query_expr: `${text}`,
|
||||||
.ant-form-item {
|
}
|
||||||
margin-bottom: 0;
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</style>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.attr-ad-snmp-form {
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,195 +1,362 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-tabs id="preValueArea" v-model="activeKey" size="small" :tabBarStyle="{ borderBottom: 'none' }">
|
<a-tabs id="preValueArea" v-model="activeKey" size="small" :tabBarStyle="{ borderBottom: 'none' }">
|
||||||
<a-tab-pane key="define" :disabled="disabled">
|
<a-tab-pane key="define" :disabled="disabled">
|
||||||
<span style="font-size:12px;" slot="tab">定义</span>
|
<span style="font-size:12px;" slot="tab">定义</span>
|
||||||
<PreValueTag type="add" :item="[]" @add="addNewValue" :disabled="disabled">
|
<PreValueTag type="add" :item="[]" @add="addNewValue" :disabled="disabled">
|
||||||
<template #default>
|
<template #default>
|
||||||
<a-button
|
<a-button
|
||||||
:style="{ marginBottom: '10px', fontSize: '12px', padding: '1px 7px' }"
|
:style="{ marginBottom: '10px', fontSize: '12px', padding: '1px 7px' }"
|
||||||
type="primary"
|
type="primary"
|
||||||
ghost
|
ghost
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<a-icon type="plus" />添加</a-button
|
<a-icon type="plus" />添加</a-button
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</PreValueTag>
|
</PreValueTag>
|
||||||
<draggable :list="valueList" handle=".handle" :disabled="disabled">
|
<draggable :list="valueList" handle=".handle" :disabled="disabled">
|
||||||
<PreValueTag
|
<PreValueTag
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
v-for="(item, index) in valueList"
|
v-for="(item, index) in valueList"
|
||||||
:key="`${item[0]}_${index}`"
|
:key="`${item[0]}_${index}`"
|
||||||
:item="item"
|
:item="item"
|
||||||
@deleteValue="deleteValue"
|
@deleteValue="deleteValue"
|
||||||
@editValue="editValue"
|
@editValue="editValue"
|
||||||
/>
|
/>
|
||||||
</draggable>
|
</draggable>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="webhook" :disabled="disabled">
|
<a-tab-pane key="webhook" :disabled="disabled">
|
||||||
<span style="font-size:12px;" slot="tab">Webhook</span>
|
<span style="font-size:12px;" slot="tab">Webhook</span>
|
||||||
<a-form-model :model="form">
|
<Webhook ref="webhook" style="margin-top:10px" />
|
||||||
<a-row :gutter="24">
|
<a-form-model :model="form">
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-model-item label="地址" prop="url" :labelCol="{ span: 3 }" :wrapperCol="{ span: 16 }">
|
<a-form-model-item prop="ret_key" :labelCol="{ span: 3 }" :wrapperCol="{ span: 18 }">
|
||||||
<a-input v-model="form.url" :disabled="disabled">
|
<template slot="label">
|
||||||
<a-select
|
<span
|
||||||
:showArrow="false"
|
style="position:relative;white-space:pre;"
|
||||||
slot="addonBefore"
|
>{{ `过滤` }}
|
||||||
style="width:60px;"
|
<a-tooltip
|
||||||
v-model="form.method"
|
title="返回的结果按字段来过滤,层级嵌套用##分隔,比如k1##k2,web请求返回{k1: [{k2: 1}, {k2: 2}]}, 解析结果为[1, 2]"
|
||||||
:disabled="disabled"
|
>
|
||||||
>
|
<a-icon
|
||||||
<a-select-option value="get">
|
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
|
||||||
GET
|
type="question-circle"
|
||||||
</a-select-option>
|
theme="filled"
|
||||||
<a-select-option value="post">
|
/>
|
||||||
POST
|
</a-tooltip>
|
||||||
</a-select-option>
|
</span>
|
||||||
<a-select-option value="put">
|
</template>
|
||||||
PUT
|
<a-input style="width:150px;" v-model="form.ret_key" placeholder="k1##k2" :disabled="disabled" />
|
||||||
</a-select-option>
|
</a-form-model-item>
|
||||||
</a-select>
|
</a-col>
|
||||||
</a-input>
|
</a-form-model>
|
||||||
</a-form-model-item>
|
</a-tab-pane>
|
||||||
</a-col>
|
<a-tab-pane key="choice_other" :disabled="disabled">
|
||||||
</a-row>
|
<span style="font-size:12px;" slot="tab">其他模型属性</span>
|
||||||
<a-col :span="24">
|
<a-row :gutter="[24, 24]">
|
||||||
<a-form-model-item prop="ret_key" :labelCol="{ span: 3 }" :wrapperCol="{ span: 18 }">
|
<a-col :span="12">
|
||||||
<template slot="label">
|
<a-form-item
|
||||||
<span
|
:style="{ lineHeight: '24px', marginBottom: '5px' }"
|
||||||
style="position:relative;white-space:pre;"
|
label="模型"
|
||||||
>{{ `过滤` }}
|
:label-col="{ span: 4 }"
|
||||||
<a-tooltip
|
:wrapper-col="{ span: 20 }"
|
||||||
title="返回的结果按字段来过滤,层级嵌套用##分隔,比如k1##k2,web请求返回{k1: [{k2: 1}, {k2: 2}]}, 解析结果为[1, 2]"
|
>
|
||||||
>
|
<treeselect
|
||||||
<a-icon
|
:disable-branch-nodes="true"
|
||||||
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
|
:class="{
|
||||||
type="question-circle"
|
'custom-treeselect': true,
|
||||||
theme="filled"
|
'custom-treeselect-bgcAndBorder': true,
|
||||||
/>
|
}"
|
||||||
</a-tooltip>
|
:style="{
|
||||||
</span>
|
'--custom-height': '32px',
|
||||||
</template>
|
lineHeight: '32px',
|
||||||
<a-input style="width:150px;" v-model="form.ret_key" placeholder="k1##k2" :disabled="disabled" />
|
'--custom-bg-color': '#fff',
|
||||||
</a-form-model-item>
|
'--custom-border': '1px solid #d9d9d9',
|
||||||
</a-col>
|
'--custom-multiple-lineHeight': '14px',
|
||||||
</a-form-model>
|
}"
|
||||||
</a-tab-pane>
|
v-model="choice_other.type_ids"
|
||||||
</a-tabs>
|
:multiple="true"
|
||||||
</template>
|
:clearable="true"
|
||||||
|
searchable
|
||||||
<script>
|
:options="ciTypeGroup"
|
||||||
import _ from 'lodash'
|
value-consists-of="LEAF_PRIORITY"
|
||||||
import draggable from 'vuedraggable'
|
placeholder="请选择CMDB模型"
|
||||||
import PreValueTag from './preValueTag.vue'
|
:normalizer="
|
||||||
import { defautValueColor } from '../../utils/const'
|
(node) => {
|
||||||
import ColorPicker from '../../components/colorPicker/index.vue'
|
return {
|
||||||
|
id: node.id || -1,
|
||||||
export default {
|
label: node.alias || node.name || '其他',
|
||||||
name: 'PreValueArea',
|
title: node.alias || node.name || '其他',
|
||||||
components: { draggable, PreValueTag, ColorPicker },
|
children: node.ci_types,
|
||||||
props: {
|
}
|
||||||
disabled: {
|
}
|
||||||
type: Boolean,
|
"
|
||||||
default: true,
|
appendToBody
|
||||||
},
|
:zIndex="1050"
|
||||||
},
|
@select="
|
||||||
data() {
|
() => {
|
||||||
return {
|
choice_other.attr_id = undefined
|
||||||
defautValueColor,
|
}
|
||||||
activeKey: 'define', // define webhook
|
"
|
||||||
valueList: [],
|
>
|
||||||
form: {
|
<div
|
||||||
url: '',
|
:title="node.label"
|
||||||
method: 'get',
|
slot="option-label"
|
||||||
ret_key: '',
|
slot-scope="{ node }"
|
||||||
},
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
}
|
>
|
||||||
},
|
{{ node.label }}
|
||||||
watch: {
|
</div>
|
||||||
disabled: {
|
</treeselect>
|
||||||
immediate: false,
|
</a-form-item>
|
||||||
handler(newValue) {
|
</a-col>
|
||||||
const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
|
<a-col :span="12" v-if="choice_other.type_ids && choice_other.type_ids.length">
|
||||||
if (newValue) {
|
<a-form-item
|
||||||
// 如果是disabled 把tab 的ink-bar也置灰
|
:style="{ marginBottom: '5px' }"
|
||||||
dom.style.backgroundColor = '#00000040'
|
label="属性"
|
||||||
} else {
|
:label-col="{ span: 4 }"
|
||||||
dom.style.backgroundColor = '#2f54eb'
|
:wrapper-col="{ span: 20 }"
|
||||||
}
|
>
|
||||||
},
|
<treeselect
|
||||||
},
|
:disable-branch-nodes="true"
|
||||||
},
|
class="ops-setting-treeselect"
|
||||||
methods: {
|
v-model="choice_other.attr_id"
|
||||||
addNewValue(newValue, newStyle, newIcon) {
|
:multiple="false"
|
||||||
if (newValue) {
|
:clearable="true"
|
||||||
const idx = this.valueList.findIndex((v) => v[0] === newValue)
|
searchable
|
||||||
if (idx > -1) {
|
:options="typeAttrs"
|
||||||
this.$message.warning('当前值已存在!')
|
value-consists-of="LEAF_PRIORITY"
|
||||||
} else {
|
placeholder="请选择模型属性"
|
||||||
this.valueList.push([newValue, { style: newStyle, icon: { ...newIcon } }])
|
:normalizer="
|
||||||
}
|
(node) => {
|
||||||
}
|
return {
|
||||||
},
|
id: node.id || -1,
|
||||||
deleteValue(item) {
|
label: node.alias || node.name || '其他',
|
||||||
const _valueList = _.cloneDeep(this.valueList)
|
title: node.alias || node.name || '其他',
|
||||||
const idx = _valueList.findIndex((v) => v[0] === item[0])
|
}
|
||||||
if (idx > -1) {
|
}
|
||||||
_valueList.splice(idx, 1)
|
"
|
||||||
this.valueList = _valueList
|
appendToBody
|
||||||
}
|
:zIndex="1050"
|
||||||
},
|
>
|
||||||
editValue(item, newValue, newStyle, newIcon) {
|
<div
|
||||||
const _valueList = _.cloneDeep(this.valueList)
|
:title="node.label"
|
||||||
const idx = _valueList.findIndex((v) => v[0] === item[0])
|
slot="option-label"
|
||||||
if (idx > -1) {
|
slot-scope="{ node }"
|
||||||
_valueList[idx] = [newValue, { style: newStyle, icon: { ...newIcon } }]
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
this.valueList = _valueList
|
>
|
||||||
}
|
{{ node.label }}
|
||||||
},
|
</div>
|
||||||
getData() {
|
</treeselect>
|
||||||
if (this.activeKey === 'define') {
|
</a-form-item>
|
||||||
return {
|
</a-col>
|
||||||
choice_value: this.valueList,
|
<a-col :span="24" v-if="choice_other.type_ids && choice_other.type_ids.length">
|
||||||
choice_web_hook: null,
|
<a-form-item
|
||||||
}
|
:style="{ marginBottom: '5px' }"
|
||||||
} else {
|
class="pre-value-filter"
|
||||||
return { choice_value: [], choice_web_hook: this.form }
|
label="筛选"
|
||||||
}
|
:label-col="{ span: 2 }"
|
||||||
},
|
:wrapper-col="{ span: 22 }"
|
||||||
setData({ choice_value, choice_web_hook }) {
|
>
|
||||||
if (choice_web_hook) {
|
<FilterComp
|
||||||
this.form = choice_web_hook
|
ref="filterComp"
|
||||||
this.activeKey = 'webhook'
|
:isDropdown="false"
|
||||||
} else {
|
:canSearchPreferenceAttrList="typeAttrs"
|
||||||
this.valueList = choice_value
|
@setExpFromFilter="setExpFromFilter"
|
||||||
this.activeKey = 'define'
|
:expression="filterExp ? `q=${filterExp}` : ''"
|
||||||
}
|
/>
|
||||||
const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
|
</a-form-item>
|
||||||
if (this.disabled) {
|
</a-col>
|
||||||
// 如果是disabled 把tab 的ink-bar也置灰
|
</a-row>
|
||||||
dom.style.backgroundColor = '#00000040'
|
</a-tab-pane>
|
||||||
} else {
|
</a-tabs>
|
||||||
dom.style.backgroundColor = '#2f54eb'
|
</template>
|
||||||
}
|
|
||||||
},
|
<script>
|
||||||
},
|
import _ from 'lodash'
|
||||||
}
|
import draggable from 'vuedraggable'
|
||||||
</script>
|
import PreValueTag from './preValueTag.vue'
|
||||||
|
import { defautValueColor } from '../../utils/const'
|
||||||
<style lang="less" scoped>
|
import ColorPicker from '../../components/colorPicker/index.vue'
|
||||||
.pre-value-edit-color {
|
import Webhook from '../../components/webhook'
|
||||||
display: flex;
|
import { getCITypeGroups } from '../../api/ciTypeGroup'
|
||||||
flex-direction: row;
|
import { getCITypeCommonAttributesByTypeIds } from '../../api/CITypeAttr'
|
||||||
justify-content: space-between;
|
import FilterComp from '@/components/CMDBFilterComp'
|
||||||
flex-wrap: wrap;
|
|
||||||
.pre-value-edit-color-item {
|
export default {
|
||||||
cursor: pointer;
|
name: 'PreValueArea',
|
||||||
display: inline-block;
|
components: { draggable, PreValueTag, ColorPicker, Webhook, FilterComp },
|
||||||
width: 25px;
|
props: {
|
||||||
height: 20px;
|
disabled: {
|
||||||
margin: 5px;
|
type: Boolean,
|
||||||
}
|
default: true,
|
||||||
}
|
},
|
||||||
</style>
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
defautValueColor,
|
||||||
|
activeKey: 'define', // define webhook
|
||||||
|
valueList: [],
|
||||||
|
form: {
|
||||||
|
ret_key: '',
|
||||||
|
},
|
||||||
|
choice_other: {
|
||||||
|
type_ids: undefined,
|
||||||
|
attr_id: undefined,
|
||||||
|
},
|
||||||
|
ciTypeGroup: [],
|
||||||
|
typeAttrs: [],
|
||||||
|
filterExp: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
disabled: {
|
||||||
|
immediate: false,
|
||||||
|
handler(newValue) {
|
||||||
|
const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
|
||||||
|
if (newValue) {
|
||||||
|
// 如果是disabled 把tab 的ink-bar也置灰
|
||||||
|
dom.style.backgroundColor = '#00000040'
|
||||||
|
} else {
|
||||||
|
dom.style.backgroundColor = '#2f54eb'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'choice_other.type_ids': {
|
||||||
|
handler(newValue) {
|
||||||
|
if (newValue && newValue.length) {
|
||||||
|
getCITypeCommonAttributesByTypeIds({ type_ids: newValue.join(',') }).then((res) => {
|
||||||
|
this.typeAttrs = res.attributes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
getCITypeGroups({ need_other: true }).then((res) => {
|
||||||
|
this.ciTypeGroup = res
|
||||||
|
.filter((item) => item.ci_types && item.ci_types.length)
|
||||||
|
.map((item) => {
|
||||||
|
item.id = `parent_${item.id || -1}`
|
||||||
|
return { ..._.cloneDeep(item) }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addNewValue(newValue, newStyle, newIcon) {
|
||||||
|
if (newValue) {
|
||||||
|
const idx = this.valueList.findIndex((v) => v[0] === newValue)
|
||||||
|
if (idx > -1) {
|
||||||
|
this.$message.warning('当前值已存在!')
|
||||||
|
} else {
|
||||||
|
this.valueList.push([newValue, { style: newStyle, icon: { ...newIcon } }])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteValue(item) {
|
||||||
|
const _valueList = _.cloneDeep(this.valueList)
|
||||||
|
const idx = _valueList.findIndex((v) => v[0] === item[0])
|
||||||
|
if (idx > -1) {
|
||||||
|
_valueList.splice(idx, 1)
|
||||||
|
this.valueList = _valueList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editValue(item, newValue, newStyle, newIcon) {
|
||||||
|
const _valueList = _.cloneDeep(this.valueList)
|
||||||
|
const idx = _valueList.findIndex((v) => v[0] === item[0])
|
||||||
|
if (idx > -1) {
|
||||||
|
_valueList[idx] = [newValue, { style: newStyle, icon: { ...newIcon } }]
|
||||||
|
this.valueList = _valueList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getData() {
|
||||||
|
if (this.activeKey === 'define') {
|
||||||
|
return {
|
||||||
|
choice_value: this.valueList,
|
||||||
|
choice_web_hook: null,
|
||||||
|
choice_other: null,
|
||||||
|
}
|
||||||
|
} else if (this.activeKey === 'webhook') {
|
||||||
|
const choice_web_hook = this.$refs.webhook.getParams()
|
||||||
|
choice_web_hook.ret_key = this.form.ret_key
|
||||||
|
return { choice_value: [], choice_web_hook, choice_other: null }
|
||||||
|
} else {
|
||||||
|
let choice_other = {}
|
||||||
|
if (this.choice_other.type_ids && this.choice_other.type_ids.length) {
|
||||||
|
this.$refs.filterComp.handleSubmit()
|
||||||
|
choice_other = { ...this.choice_other, filter: this.filterExp }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
choice_value: [],
|
||||||
|
choice_web_hook: null,
|
||||||
|
choice_other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setData({ choice_value, choice_web_hook, choice_other }) {
|
||||||
|
if (choice_web_hook) {
|
||||||
|
this.activeKey = 'webhook'
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.webhook.setParams(choice_web_hook)
|
||||||
|
this.form.ret_key = choice_web_hook.ret_key ?? ''
|
||||||
|
})
|
||||||
|
} else if (choice_other) {
|
||||||
|
this.activeKey = 'choice_other'
|
||||||
|
const { type_ids, attr_id, filter } = choice_other
|
||||||
|
this.choice_other = { type_ids, attr_id }
|
||||||
|
this.filterExp = filter
|
||||||
|
if (type_ids && type_ids.length) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.filterComp.visibleChange(true, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.valueList = choice_value
|
||||||
|
this.activeKey = 'define'
|
||||||
|
}
|
||||||
|
const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
|
||||||
|
if (this.disabled) {
|
||||||
|
// 如果是disabled 把tab 的ink-bar也置灰
|
||||||
|
dom.style.backgroundColor = '#00000040'
|
||||||
|
} else {
|
||||||
|
dom.style.backgroundColor = '#2f54eb'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setExpFromFilter(filterExp) {
|
||||||
|
if (filterExp) {
|
||||||
|
this.filterExp = `${filterExp}`
|
||||||
|
} else {
|
||||||
|
this.filterExp = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.pre-value-edit-color {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.pre-value-edit-color-item {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
width: 25px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.pre-value-filter {
|
||||||
|
.ant-form-item-control {
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.table-filter-add {
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -156,12 +156,74 @@
|
|||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="通知方式" prop="method">
|
<a-form-model-item label="通知方式" prop="method">
|
||||||
<a-checkbox-group v-model="notifies.method">
|
<a-checkbox-group v-model="notifies.method">
|
||||||
<a-checkbox value="wechatApp">
|
<a-row :style="{ marginTop: '4px' }" :gutter="[0, 12]">
|
||||||
微信
|
<a-col :span="6">
|
||||||
</a-checkbox>
|
<a-checkbox value="email"> <ops-icon type="email" style="margin-right:5px" />邮件 </a-checkbox>
|
||||||
<a-checkbox value="email">
|
</a-col>
|
||||||
邮件
|
<a-col :span="6">
|
||||||
</a-checkbox>
|
<a-checkbox value="wechatApp">
|
||||||
|
<ops-icon type="wechatApp" style="margin-right:5px" />企业微信
|
||||||
|
</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-checkbox value="dingdingApp">
|
||||||
|
<ops-icon type="dingdingApp" style="margin-right:5px" />钉钉
|
||||||
|
</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-checkbox value="feishuApp"> <ops-icon type="feishuApp" style="margin-right:5px" />飞书 </a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4" :style="{ lineHeight: '32px' }">
|
||||||
|
<ops-icon type="robot" style="margin-right:5px" />机器人:
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="18">
|
||||||
|
<treeselect
|
||||||
|
:disable-branch-nodes="true"
|
||||||
|
:class="{
|
||||||
|
'custom-treeselect': true,
|
||||||
|
'custom-treeselect-bgcAndBorder': true,
|
||||||
|
}"
|
||||||
|
:style="{
|
||||||
|
'--custom-height': '32px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
'--custom-bg-color': '#fff',
|
||||||
|
'--custom-border': '1px solid #d9d9d9',
|
||||||
|
'--custom-multiple-lineHeight': '14px',
|
||||||
|
}"
|
||||||
|
v-model="selectedBot"
|
||||||
|
:multiple="true"
|
||||||
|
:clearable="true"
|
||||||
|
searchable
|
||||||
|
:options="appBot"
|
||||||
|
value-consists-of="LEAF_PRIORITY"
|
||||||
|
placeholder="请选择机器人"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node.name,
|
||||||
|
label: node.label || node.name,
|
||||||
|
children: node.bot,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
appendToBody
|
||||||
|
:zIndex="1050"
|
||||||
|
noChildrenText="暂无数据"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:title="node.label"
|
||||||
|
slot="option-label"
|
||||||
|
slot-scope="{ node }"
|
||||||
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
|
>
|
||||||
|
<ops-icon :type="node.id" v-if="node.children" />{{ node.label }}
|
||||||
|
</div>
|
||||||
|
<div slot="value-label" slot-scope="{ node }">
|
||||||
|
<ops-icon :type="node.parentNode.id" />{{ node.label }}
|
||||||
|
</div>
|
||||||
|
</treeselect>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</a-checkbox-group>
|
</a-checkbox-group>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
</a-form-model>
|
</a-form-model>
|
||||||
@@ -194,12 +256,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { addTrigger, updateTrigger, deleteTrigger, getAllDagsName } from '../../api/CIType'
|
import { addTrigger, updateTrigger, deleteTrigger } from '../../api/CIType'
|
||||||
import FilterComp from '@/components/CMDBFilterComp'
|
import FilterComp from '@/components/CMDBFilterComp'
|
||||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||||
import Webhook from '../../components/webhook'
|
import Webhook from '../../components/webhook'
|
||||||
import NoticeContent from '../../components/noticeContent'
|
import NoticeContent from '../../components/noticeContent'
|
||||||
import { getNoticeByEmployeeIds } from '@/api/employee'
|
import { getNoticeByEmployeeIds } from '@/api/employee'
|
||||||
|
import { getNoticeConfigAppBot } from '@/api/noticeSetting'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TriggerForm',
|
name: 'TriggerForm',
|
||||||
@@ -260,6 +323,8 @@ export default {
|
|||||||
isShow: false,
|
isShow: false,
|
||||||
dag_id: null,
|
dag_id: null,
|
||||||
showCustomEmail: false,
|
showCustomEmail: false,
|
||||||
|
appBot: [],
|
||||||
|
selectedBot: undefined,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -281,14 +346,14 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {},
|
mounted() {},
|
||||||
methods: {
|
methods: {
|
||||||
async getDags() {
|
async getNoticeConfigAppBot() {
|
||||||
await getAllDagsName().then((res) => {
|
await getNoticeConfigAppBot().then((res) => {
|
||||||
this.dags = res.map((dag) => ({ id: dag[1], label: dag[0] }))
|
this.appBot = res
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
createFromTriggerTable(attrList) {
|
createFromTriggerTable(attrList) {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
// this.getDags()
|
this.getNoticeConfigAppBot()
|
||||||
this.attrList = attrList
|
this.attrList = attrList
|
||||||
this.triggerId = null
|
this.triggerId = null
|
||||||
this.title = '新增触发器'
|
this.title = '新增触发器'
|
||||||
@@ -307,7 +372,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async open(property, attrList) {
|
async open(property, attrList) {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
// await this.getDags()
|
this.getNoticeConfigAppBot()
|
||||||
this.attrList = attrList
|
this.attrList = attrList
|
||||||
if (property.has_trigger) {
|
if (property.has_trigger) {
|
||||||
this.triggerId = property.trigger.id
|
this.triggerId = property.trigger.id
|
||||||
@@ -348,7 +413,7 @@ export default {
|
|||||||
const employee_ids = property?.trigger?.option?.employee_ids ?? undefined
|
const employee_ids = property?.trigger?.option?.employee_ids ?? undefined
|
||||||
const custom_email =
|
const custom_email =
|
||||||
tos
|
tos
|
||||||
.filter((t) => !t.employee_id)
|
.filter((t) => !t.employee_id && t.email)
|
||||||
.map((t) => t.email)
|
.map((t) => t.email)
|
||||||
.join(';') ?? ''
|
.join(';') ?? ''
|
||||||
|
|
||||||
@@ -360,7 +425,16 @@ export default {
|
|||||||
this.$refs.noticeContent.setContent(body_html)
|
this.$refs.noticeContent.setContent(body_html)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
this.notifies = { employee_ids, custom_email, subject, method }
|
const _method = method.filter((item) => ['email', 'wechatApp', 'dingdingApp', 'feishuApp'].includes(item))
|
||||||
|
const _flatAppBot = []
|
||||||
|
this.appBot.forEach((item) => {
|
||||||
|
_flatAppBot.push(...item.bot.map((b) => b.name))
|
||||||
|
})
|
||||||
|
const selectedBot = method.filter(
|
||||||
|
(item) => !['email', 'wechatApp', 'dingdingApp', 'feishuApp'].includes(item) && _flatAppBot.includes(item)
|
||||||
|
)
|
||||||
|
this.selectedBot = selectedBot
|
||||||
|
this.notifies = { employee_ids, custom_email, subject, method: _method }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.title = `新增触发器 ${property.alias || property.name}`
|
this.title = `新增触发器 ${property.alias || property.name}`
|
||||||
@@ -378,6 +452,7 @@ export default {
|
|||||||
this.category = 1
|
this.category = 1
|
||||||
this.triggerAction = '1'
|
this.triggerAction = '1'
|
||||||
this.filterExp = ''
|
this.filterExp = ''
|
||||||
|
this.selectedBot = undefined
|
||||||
this.visible = false
|
this.visible = false
|
||||||
},
|
},
|
||||||
filterChange(value) {
|
filterChange(value) {
|
||||||
@@ -415,11 +490,30 @@ export default {
|
|||||||
tos.push({ email })
|
tos.push({ email })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (this.selectedBot && this.selectedBot.length) {
|
||||||
|
this.selectedBot.forEach((bot) => {
|
||||||
|
tos.push({ [`${bot}`]: bot })
|
||||||
|
})
|
||||||
|
}
|
||||||
if (this.category === 2) {
|
if (this.category === 2) {
|
||||||
const { before_days, notify_at } = this.dateForm
|
const { before_days, notify_at } = this.dateForm
|
||||||
params.option.notifies = { tos, subject, body, body_html, method, before_days, notify_at }
|
params.option.notifies = {
|
||||||
|
tos,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
body_html,
|
||||||
|
method: [...method, ...(this.selectedBot ?? [])],
|
||||||
|
before_days,
|
||||||
|
notify_at,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
params.option.notifies = { tos, subject, body, body_html, method }
|
params.option.notifies = {
|
||||||
|
tos,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
body_html,
|
||||||
|
method: [...method, ...(this.selectedBot ?? [])],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case '2':
|
case '2':
|
||||||
|
@@ -29,6 +29,12 @@
|
|||||||
<span v-else-if="row.notify">通知</span>
|
<span v-else-if="row.notify">通知</span>
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
|
<vxe-column title="状态">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<a-tag color="green" v-if="row.is_ok">已完成</a-tag>
|
||||||
|
<a-tag color="red" v-else>未完成</a-tag>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
<vxe-column title="触发时间">
|
<vxe-column title="触发时间">
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
{{ row.updated_at || row.created_at }}
|
{{ row.updated_at || row.created_at }}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,34 @@ export const generatorDynamicRouter = async () => {
|
|||||||
meta: { title: '公司架构', appName: 'backend', icon: 'ops-setting-companyStructure', selectedIcon: 'ops-setting-companyStructure-selected', permission: ['acl_admin', 'backend_admin'] },
|
meta: { title: '公司架构', appName: 'backend', icon: 'ops-setting-companyStructure', selectedIcon: 'ops-setting-companyStructure-selected', permission: ['acl_admin', 'backend_admin'] },
|
||||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/companyStructure/index')
|
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/companyStructure/index')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/setting/notice',
|
||||||
|
name: 'notice',
|
||||||
|
component: RouteView,
|
||||||
|
meta: { title: '通知设置', appName: 'backend', icon: 'ops-setting-notice', selectedIcon: 'ops-setting-notice-selected', permission: ['通知设置', 'backend_admin'] },
|
||||||
|
redirect: '/setting/notice/email',
|
||||||
|
children: [{
|
||||||
|
path: '/setting/notice/email',
|
||||||
|
name: 'notice_email',
|
||||||
|
meta: { title: '邮件设置', 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: '企业微信', 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: '钉钉', 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: '飞书', icon: 'ops-setting-notice-feishu', selectedIcon: 'ops-setting-notice-feishu-selected' },
|
||||||
|
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/feishu')
|
||||||
|
}]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},])
|
},])
|
||||||
return routes
|
return routes
|
||||||
|
@@ -1,372 +1,379 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ops-setting-companyinfo" :style="{ height: `${windowHeight - 64}px` }">
|
<div class="ops-setting-companyinfo" :style="{ height: `${windowHeight - 64}px` }">
|
||||||
<a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rule">
|
<a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rule">
|
||||||
<SpanTitle>公司描述</SpanTitle>
|
<SpanTitle>公司描述</SpanTitle>
|
||||||
<a-form-model-item label="名称" prop="name">
|
<a-form-model-item label="名称" prop="name">
|
||||||
<a-input v-model="infoData.name" :disabled="!isEditable" />
|
<a-input v-model="infoData.name" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="描述">
|
<a-form-model-item label="描述">
|
||||||
<a-input v-model="infoData.description" type="textarea" :disabled="!isEditable" />
|
<a-input v-model="infoData.description" type="textarea" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<SpanTitle>公司地址</SpanTitle>
|
<SpanTitle>公司地址</SpanTitle>
|
||||||
<a-form-model-item label="国家/地区">
|
<a-form-model-item label="国家/地区">
|
||||||
<a-input v-model="infoData.country" :disabled="!isEditable" />
|
<a-input v-model="infoData.country" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="城市">
|
<a-form-model-item label="城市">
|
||||||
<a-input v-model="infoData.city" :disabled="!isEditable" />
|
<a-input v-model="infoData.city" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="地址">
|
<a-form-model-item label="地址">
|
||||||
<a-input v-model="infoData.address" :disabled="!isEditable" />
|
<a-input v-model="infoData.address" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="邮编">
|
<a-form-model-item label="邮编">
|
||||||
<a-input v-model="infoData.postCode" :disabled="!isEditable" />
|
<a-input v-model="infoData.postCode" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<SpanTitle>联系方式</SpanTitle>
|
<SpanTitle>联系方式</SpanTitle>
|
||||||
<a-form-model-item label="网站">
|
<a-form-model-item label="网站">
|
||||||
<a-input v-model="infoData.website" :disabled="!isEditable" />
|
<a-input v-model="infoData.website" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="电话号码" prop="phone">
|
<a-form-model-item label="电话号码" prop="phone">
|
||||||
<a-input v-model="infoData.phone" :disabled="!isEditable" />
|
<a-input v-model="infoData.phone" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="传真号码" prop="faxCode">
|
<a-form-model-item label="传真号码" prop="faxCode">
|
||||||
<a-input v-model="infoData.faxCode" :disabled="!isEditable" />
|
<a-input v-model="infoData.faxCode" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="电子邮箱" prop="email">
|
<a-form-model-item label="电子邮箱" prop="email">
|
||||||
<a-input v-model="infoData.email" :disabled="!isEditable" />
|
<a-input v-model="infoData.email" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<SpanTitle>公司标识</SpanTitle>
|
<SpanTitle>公司标识</SpanTitle>
|
||||||
<a-form-model-item label="部署域名" prop="domainName">
|
<a-form-model-item label="Messenger地址" prop="messenger">
|
||||||
<a-input v-model="infoData.domainName" :disabled="!isEditable" />
|
<a-input v-model="infoData.messenger" :disabled="!isEditable" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="公司logo">
|
<a-form-model-item label="部署域名" prop="domainName">
|
||||||
<a-space>
|
<a-input v-model="infoData.domainName" :disabled="!isEditable" />
|
||||||
<a-upload
|
</a-form-model-item>
|
||||||
:disabled="!isEditable"
|
<a-form-model-item label="公司logo">
|
||||||
name="avatar"
|
<a-space>
|
||||||
list-type="picture-card"
|
<a-upload
|
||||||
class="avatar-uploader"
|
:disabled="!isEditable"
|
||||||
:show-upload-list="false"
|
name="avatar"
|
||||||
:customRequest="customRequest"
|
list-type="picture-card"
|
||||||
:before-upload="beforeUpload"
|
class="avatar-uploader"
|
||||||
:style="{ width: '400px', height: '80px' }"
|
:show-upload-list="false"
|
||||||
accept=".png,.jpg,.jpeg"
|
:customRequest="customRequest"
|
||||||
>
|
:before-upload="beforeUpload"
|
||||||
<div
|
:style="{ width: '400px', height: '80px' }"
|
||||||
class="ops-setting-companyinfo-upload-show"
|
accept=".png,.jpg,.jpeg"
|
||||||
v-if="infoData.logoName"
|
>
|
||||||
:style="{ width: '400px', height: '80px' }"
|
<div
|
||||||
@click="eidtImageOption.type = 'Logo'"
|
class="ops-setting-companyinfo-upload-show"
|
||||||
>
|
v-if="infoData.logoName"
|
||||||
<img :src="`/api/common-setting/v1/file/${infoData.logoName}`" alt="avatar" />
|
:style="{ width: '400px', height: '80px' }"
|
||||||
<a-icon
|
@click="eidtImageOption.type = 'Logo'"
|
||||||
v-if="isEditable"
|
>
|
||||||
type="minus-circle"
|
<img :src="`/api/common-setting/v1/file/${infoData.logoName}`" alt="avatar" />
|
||||||
theme="filled"
|
<a-icon
|
||||||
class="delete-icon"
|
v-if="isEditable"
|
||||||
@click.stop="deleteLogo"
|
type="minus-circle"
|
||||||
/>
|
theme="filled"
|
||||||
</div>
|
class="delete-icon"
|
||||||
<div v-else @click="eidtImageOption.type = 'Logo'">
|
@click.stop="deleteLogo"
|
||||||
<a-icon type="plus" />
|
/>
|
||||||
<div class="ant-upload-text">上传</div>
|
</div>
|
||||||
</div>
|
<div v-else @click="eidtImageOption.type = 'Logo'">
|
||||||
</a-upload>
|
<a-icon type="plus" />
|
||||||
|
<div class="ant-upload-text">上传</div>
|
||||||
<a-upload
|
</div>
|
||||||
:disabled="!isEditable"
|
</a-upload>
|
||||||
name="avatar"
|
|
||||||
list-type="picture-card"
|
<a-upload
|
||||||
class="avatar-uploader"
|
:disabled="!isEditable"
|
||||||
:show-upload-list="false"
|
name="avatar"
|
||||||
:customRequest="customRequest"
|
list-type="picture-card"
|
||||||
:before-upload="beforeUpload"
|
class="avatar-uploader"
|
||||||
:style="{ width: '82px', height: '82px' }"
|
:show-upload-list="false"
|
||||||
accept=".png,.jpg,.jpeg"
|
:customRequest="customRequest"
|
||||||
>
|
:before-upload="beforeUpload"
|
||||||
<div
|
:style="{ width: '82px', height: '82px' }"
|
||||||
class="ops-setting-companyinfo-upload-show"
|
accept=".png,.jpg,.jpeg"
|
||||||
v-if="infoData.smallLogoName"
|
>
|
||||||
:style="{ width: '82px', height: '82px' }"
|
<div
|
||||||
@click="eidtImageOption.type = 'SmallLogo'"
|
class="ops-setting-companyinfo-upload-show"
|
||||||
>
|
v-if="infoData.smallLogoName"
|
||||||
<img :src="`/api/common-setting/v1/file/${infoData.smallLogoName}`" alt="avatar" />
|
:style="{ width: '82px', height: '82px' }"
|
||||||
<a-icon
|
@click="eidtImageOption.type = 'SmallLogo'"
|
||||||
v-if="isEditable"
|
>
|
||||||
type="minus-circle"
|
<img :src="`/api/common-setting/v1/file/${infoData.smallLogoName}`" alt="avatar" />
|
||||||
theme="filled"
|
<a-icon
|
||||||
class="delete-icon"
|
v-if="isEditable"
|
||||||
@click.stop="deleteSmallLogo"
|
type="minus-circle"
|
||||||
/>
|
theme="filled"
|
||||||
</div>
|
class="delete-icon"
|
||||||
<div v-else @click="eidtImageOption.type = 'SmallLogo'">
|
@click.stop="deleteSmallLogo"
|
||||||
<a-icon type="plus" />
|
/>
|
||||||
<div class="ant-upload-text">上传</div>
|
</div>
|
||||||
</div>
|
<div v-else @click="eidtImageOption.type = 'SmallLogo'">
|
||||||
</a-upload>
|
<a-icon type="plus" />
|
||||||
</a-space>
|
<div class="ant-upload-text">上传</div>
|
||||||
</a-form-model-item>
|
</div>
|
||||||
<a-form-model-item :wrapper-col="{ span: 14, offset: 3 }" v-if="isEditable">
|
</a-upload>
|
||||||
<a-button type="primary" @click="onSubmit"> 保存 </a-button>
|
</a-space>
|
||||||
<a-button ghost type="primary" style="margin-left: 28px" @click="resetForm"> 重置 </a-button>
|
</a-form-model-item>
|
||||||
</a-form-model-item>
|
<a-form-model-item :wrapper-col="{ span: 14, offset: 3 }" v-if="isEditable">
|
||||||
</a-form-model>
|
<a-button type="primary" @click="onSubmit"> 保存 </a-button>
|
||||||
<edit-image
|
<a-button ghost type="primary" style="margin-left: 28px" @click="resetForm"> 重置 </a-button>
|
||||||
v-if="showEditImage"
|
</a-form-model-item>
|
||||||
:show="showEditImage"
|
</a-form-model>
|
||||||
:image="editImage"
|
<edit-image
|
||||||
:title="eidtImageOption.title"
|
v-if="showEditImage"
|
||||||
:eidtImageOption="eidtImageOption"
|
:show="showEditImage"
|
||||||
@save="submitImage"
|
:image="editImage"
|
||||||
@close="showEditImage = false"
|
:title="eidtImageOption.title"
|
||||||
/>
|
:eidtImageOption="eidtImageOption"
|
||||||
</div>
|
@save="submitImage"
|
||||||
</template>
|
@close="showEditImage = false"
|
||||||
|
/>
|
||||||
<script>
|
</div>
|
||||||
import { getCompanyInfo, postCompanyInfo, putCompanyInfo } from '@/api/company'
|
</template>
|
||||||
import { postImageFile } from '@/api/file'
|
|
||||||
import { mapMutations, mapState } from 'vuex'
|
<script>
|
||||||
import SpanTitle from '../components/spanTitle.vue'
|
import { getCompanyInfo, postCompanyInfo, putCompanyInfo } from '@/api/company'
|
||||||
import EditImage from '../components/EditImage.vue'
|
import { postImageFile } from '@/api/file'
|
||||||
import { mixinPermissions } from '@/utils/mixin'
|
import { mapMutations, mapState } from 'vuex'
|
||||||
export default {
|
import SpanTitle from '../components/spanTitle.vue'
|
||||||
name: 'CompanyInfo',
|
import EditImage from '../components/EditImage.vue'
|
||||||
mixins: [mixinPermissions],
|
import { mixinPermissions } from '@/utils/mixin'
|
||||||
components: { SpanTitle, EditImage },
|
export default {
|
||||||
data() {
|
name: 'CompanyInfo',
|
||||||
return {
|
mixins: [mixinPermissions],
|
||||||
labelCol: { span: 3 },
|
components: { SpanTitle, EditImage },
|
||||||
wrapperCol: { span: 10 },
|
data() {
|
||||||
infoData: {
|
return {
|
||||||
name: '',
|
labelCol: { span: 3 },
|
||||||
description: '',
|
wrapperCol: { span: 10 },
|
||||||
address: '',
|
infoData: {
|
||||||
city: '',
|
name: '',
|
||||||
postCode: '',
|
description: '',
|
||||||
country: '',
|
address: '',
|
||||||
website: '',
|
city: '',
|
||||||
phone: '',
|
postCode: '',
|
||||||
faxCode: '',
|
country: '',
|
||||||
email: '',
|
website: '',
|
||||||
logoName: '',
|
phone: '',
|
||||||
smallLogoName: '',
|
faxCode: '',
|
||||||
},
|
email: '',
|
||||||
rule: {
|
logoName: '',
|
||||||
name: [{ required: true, whitespace: true, message: '请输入名称', trigger: 'blur' }],
|
smallLogoName: '',
|
||||||
phone: [
|
messenger: '',
|
||||||
{
|
domainName: '',
|
||||||
required: false,
|
},
|
||||||
whitespace: true,
|
rule: {
|
||||||
pattern: new RegExp('^([0-9]|-)+$', 'g'),
|
name: [{ required: true, whitespace: true, message: '请输入名称', trigger: 'blur' }],
|
||||||
message: '请输入正确的电话号码',
|
phone: [
|
||||||
trigger: 'blur',
|
{
|
||||||
},
|
required: false,
|
||||||
],
|
whitespace: true,
|
||||||
faxCode: [
|
pattern: new RegExp('^([0-9]|-)+$', 'g'),
|
||||||
{
|
message: '请输入正确的电话号码',
|
||||||
required: false,
|
trigger: 'blur',
|
||||||
whitespace: true,
|
},
|
||||||
pattern: new RegExp('^([0-9]|-)+$', 'g'),
|
],
|
||||||
message: '请输入正确的传真号码',
|
faxCode: [
|
||||||
trigger: 'blur',
|
{
|
||||||
},
|
required: false,
|
||||||
],
|
whitespace: true,
|
||||||
email: [
|
pattern: new RegExp('^([0-9]|-)+$', 'g'),
|
||||||
{
|
message: '请输入正确的传真号码',
|
||||||
required: false,
|
trigger: 'blur',
|
||||||
whitespace: true,
|
},
|
||||||
pattern: new RegExp('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(.[a-zA-Z0-9-]+)*.[a-zA-Z0-9]{2,6}$', 'g'),
|
],
|
||||||
message: '请输入正确的邮箱地址',
|
email: [
|
||||||
trigger: 'blur',
|
{
|
||||||
},
|
required: false,
|
||||||
],
|
whitespace: true,
|
||||||
},
|
pattern: new RegExp('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(.[a-zA-Z0-9-]+)*.[a-zA-Z0-9]{2,6}$', 'g'),
|
||||||
getId: -1,
|
message: '请输入正确的邮箱地址',
|
||||||
showEditImage: false,
|
trigger: 'blur',
|
||||||
editImage: null,
|
},
|
||||||
eidtImageOption: {
|
],
|
||||||
type: 'Logo',
|
},
|
||||||
fixedNumber: [15, 4],
|
getId: -1,
|
||||||
title: '编辑企业logo',
|
showEditImage: false,
|
||||||
previewWidth: '200px',
|
editImage: null,
|
||||||
previewHeight: '40px',
|
eidtImageOption: {
|
||||||
autoCropWidth: 200,
|
type: 'Logo',
|
||||||
autoCropHeight: 40,
|
fixedNumber: [15, 4],
|
||||||
},
|
title: '编辑企业logo',
|
||||||
}
|
previewWidth: '200px',
|
||||||
},
|
previewHeight: '40px',
|
||||||
async mounted() {
|
autoCropWidth: 200,
|
||||||
const res = await getCompanyInfo()
|
autoCropHeight: 40,
|
||||||
if (!res.id) {
|
},
|
||||||
this.getId = -1
|
}
|
||||||
} else {
|
},
|
||||||
this.infoData = res.info
|
async mounted() {
|
||||||
this.getId = res.id
|
const res = await getCompanyInfo()
|
||||||
}
|
if (!res.id) {
|
||||||
},
|
this.getId = -1
|
||||||
computed: {
|
} else {
|
||||||
...mapState({
|
this.infoData = res.info
|
||||||
windowHeight: (state) => state.windowHeight,
|
this.getId = res.id
|
||||||
}),
|
}
|
||||||
isEditable() {
|
},
|
||||||
return this.hasDetailPermission('backend', '公司信息', ['update'])
|
computed: {
|
||||||
},
|
...mapState({
|
||||||
},
|
windowHeight: (state) => state.windowHeight,
|
||||||
methods: {
|
}),
|
||||||
...mapMutations(['SET_FILENAME', 'SET_SMALL_FILENAME']),
|
isEditable() {
|
||||||
deleteLogo() {
|
return this.hasDetailPermission('backend', '公司信息', ['update'])
|
||||||
this.infoData.logoName = ''
|
},
|
||||||
},
|
},
|
||||||
deleteSmallLogo() {
|
methods: {
|
||||||
this.infoData.smallLogoName = ''
|
...mapMutations(['SET_FILENAME', 'SET_SMALL_FILENAME']),
|
||||||
},
|
deleteLogo() {
|
||||||
async onSubmit() {
|
this.infoData.logoName = ''
|
||||||
this.$refs.infoData.validate(async (valid) => {
|
},
|
||||||
if (valid) {
|
deleteSmallLogo() {
|
||||||
if (this.getId === -1) {
|
this.infoData.smallLogoName = ''
|
||||||
await postCompanyInfo(this.infoData)
|
},
|
||||||
} else {
|
async onSubmit() {
|
||||||
await putCompanyInfo(this.getId, this.infoData)
|
this.$refs.infoData.validate(async (valid) => {
|
||||||
}
|
if (valid) {
|
||||||
this.SET_FILENAME(this.infoData.logoName)
|
if (this.getId === -1) {
|
||||||
this.SET_SMALL_FILENAME(this.infoData.smallFileName)
|
await postCompanyInfo(this.infoData)
|
||||||
this.$message.success('保存成功')
|
} else {
|
||||||
} else {
|
await putCompanyInfo(this.getId, this.infoData)
|
||||||
this.$message.warning('检查您的输入是否正确!')
|
}
|
||||||
return false
|
this.SET_FILENAME(this.infoData.logoName)
|
||||||
}
|
this.SET_SMALL_FILENAME(this.infoData.smallFileName)
|
||||||
})
|
this.$message.success('保存成功')
|
||||||
},
|
} else {
|
||||||
resetForm() {
|
this.$message.warning('检查您的输入是否正确!')
|
||||||
this.infoData = {
|
return false
|
||||||
name: '',
|
}
|
||||||
description: '',
|
})
|
||||||
address: '',
|
},
|
||||||
city: '',
|
resetForm() {
|
||||||
postCode: '',
|
this.infoData = {
|
||||||
country: '',
|
name: '',
|
||||||
website: '',
|
description: '',
|
||||||
phone: '',
|
address: '',
|
||||||
faxCode: '',
|
city: '',
|
||||||
email: '',
|
postCode: '',
|
||||||
logoName: '',
|
country: '',
|
||||||
smallLogoName: '',
|
website: '',
|
||||||
}
|
phone: '',
|
||||||
},
|
faxCode: '',
|
||||||
customRequest(file) {
|
email: '',
|
||||||
const reader = new FileReader()
|
logoName: '',
|
||||||
var self = this
|
smallLogoName: '',
|
||||||
if (this.eidtImageOption.type === 'Logo') {
|
messenger: '',
|
||||||
this.eidtImageOption = {
|
domainName: '',
|
||||||
type: 'Logo',
|
}
|
||||||
fixedNumber: [20, 4],
|
},
|
||||||
title: '编辑企业logo',
|
customRequest(file) {
|
||||||
previewWidth: '200px',
|
const reader = new FileReader()
|
||||||
previewHeight: '40px',
|
var self = this
|
||||||
autoCropWidth: 200,
|
if (this.eidtImageOption.type === 'Logo') {
|
||||||
autoCropHeight: 40,
|
this.eidtImageOption = {
|
||||||
}
|
type: 'Logo',
|
||||||
} else if (this.eidtImageOption.type === 'SmallLogo') {
|
fixedNumber: [20, 4],
|
||||||
this.eidtImageOption = {
|
title: '编辑企业logo',
|
||||||
type: 'SmallLogo',
|
previewWidth: '200px',
|
||||||
fixedNumber: [4, 4],
|
previewHeight: '40px',
|
||||||
title: '编辑企业logo缩略图',
|
autoCropWidth: 200,
|
||||||
previewWidth: '80px',
|
autoCropHeight: 40,
|
||||||
previewHeight: '80px',
|
}
|
||||||
autoCropWidth: 250,
|
} else if (this.eidtImageOption.type === 'SmallLogo') {
|
||||||
autoCropHeight: 250,
|
this.eidtImageOption = {
|
||||||
}
|
type: 'SmallLogo',
|
||||||
}
|
fixedNumber: [4, 4],
|
||||||
reader.onload = function(e) {
|
title: '编辑企业logo缩略图',
|
||||||
let result
|
previewWidth: '80px',
|
||||||
if (typeof e.target.result === 'object') {
|
previewHeight: '80px',
|
||||||
// 把Array Buffer转化为blob 如果是base64不需要
|
autoCropWidth: 250,
|
||||||
result = window.URL.createObjectURL(new Blob([e.target.result]))
|
autoCropHeight: 250,
|
||||||
} else {
|
}
|
||||||
result = e.target.result
|
}
|
||||||
}
|
reader.onload = function(e) {
|
||||||
|
let result
|
||||||
self.editImage = result
|
if (typeof e.target.result === 'object') {
|
||||||
self.showEditImage = true
|
// 把Array Buffer转化为blob 如果是base64不需要
|
||||||
}
|
result = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||||
reader.readAsDataURL(file.file)
|
} else {
|
||||||
},
|
result = e.target.result
|
||||||
submitImage(file) {
|
}
|
||||||
postImageFile(file).then((res) => {
|
|
||||||
if (res.file_name) {
|
self.editImage = result
|
||||||
if (this.eidtImageOption.type === 'Logo') {
|
self.showEditImage = true
|
||||||
this.infoData.logoName = res.file_name
|
}
|
||||||
} else if (this.eidtImageOption.type === 'SmallLogo') {
|
reader.readAsDataURL(file.file)
|
||||||
this.infoData.smallLogoName = res.file_name
|
},
|
||||||
}
|
submitImage(file) {
|
||||||
} else {
|
postImageFile(file).then((res) => {
|
||||||
}
|
if (res.file_name) {
|
||||||
})
|
if (this.eidtImageOption.type === 'Logo') {
|
||||||
},
|
this.infoData.logoName = res.file_name
|
||||||
|
} else if (this.eidtImageOption.type === 'SmallLogo') {
|
||||||
beforeUpload(file) {
|
this.infoData.smallLogoName = res.file_name
|
||||||
const isLt2M = file.size / 1024 / 1024 < 2
|
}
|
||||||
if (!isLt2M) {
|
} else {
|
||||||
this.$message.error('图片大小不可超过2MB!')
|
}
|
||||||
}
|
})
|
||||||
return isLt2M
|
},
|
||||||
},
|
|
||||||
},
|
beforeUpload(file) {
|
||||||
}
|
const isLt2M = file.size / 1024 / 1024 < 2
|
||||||
</script>
|
if (!isLt2M) {
|
||||||
|
this.$message.error('图片大小不可超过2MB!')
|
||||||
<style lang="less">
|
}
|
||||||
.ops-setting-companyinfo {
|
return isLt2M
|
||||||
padding-top: 15px;
|
},
|
||||||
background-color: #fff;
|
},
|
||||||
border-radius: 15px;
|
}
|
||||||
overflow: auto;
|
</script>
|
||||||
margin-bottom: -24px;
|
|
||||||
.ops-setting-companyinfo-upload-show {
|
<style lang="less">
|
||||||
position: relative;
|
.ops-setting-companyinfo {
|
||||||
width: 290px;
|
padding-top: 15px;
|
||||||
height: 100px;
|
background-color: #fff;
|
||||||
max-height: 100px;
|
border-radius: 15px;
|
||||||
img {
|
overflow: auto;
|
||||||
width: 100%;
|
margin-bottom: -24px;
|
||||||
height: 100%;
|
.ops-setting-companyinfo-upload-show {
|
||||||
}
|
position: relative;
|
||||||
|
width: 290px;
|
||||||
.delete-icon {
|
height: 100px;
|
||||||
display: none;
|
max-height: 100px;
|
||||||
}
|
img {
|
||||||
}
|
width: 100%;
|
||||||
.ant-upload:hover .delete-icon {
|
height: 100%;
|
||||||
display: block;
|
}
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
.delete-icon {
|
||||||
right: 5px;
|
display: none;
|
||||||
color: rgb(247, 85, 85);
|
}
|
||||||
}
|
}
|
||||||
.ant-form-item {
|
.ant-upload:hover .delete-icon {
|
||||||
margin-bottom: 10px;
|
display: block;
|
||||||
}
|
position: absolute;
|
||||||
.ant-form-item label {
|
top: 5px;
|
||||||
padding-right: 10px;
|
right: 5px;
|
||||||
}
|
color: rgb(247, 85, 85);
|
||||||
.avatar-uploader > .ant-upload {
|
}
|
||||||
// max-width: 100px;
|
.ant-form-item {
|
||||||
max-height: 100px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
// .ant-upload.ant-upload-select-picture-card {
|
.ant-form-item label {
|
||||||
// width: 100%;
|
padding-right: 10px;
|
||||||
// > .ant-upload {
|
}
|
||||||
// padding: 0px;
|
.avatar-uploader > .ant-upload {
|
||||||
.ant-upload-picture-card-wrapper {
|
// max-width: 100px;
|
||||||
height: 100px;
|
max-height: 100px;
|
||||||
.ant-upload.ant-upload-select-picture-card {
|
}
|
||||||
width: 100%;
|
// .ant-upload.ant-upload-select-picture-card {
|
||||||
height: 100%;
|
// width: 100%;
|
||||||
margin: 0;
|
// > .ant-upload {
|
||||||
> .ant-upload {
|
// padding: 0px;
|
||||||
padding: 0px;
|
.ant-upload-picture-card-wrapper {
|
||||||
}
|
height: 100px;
|
||||||
}
|
.ant-upload.ant-upload-select-picture-card {
|
||||||
}
|
width: 100%;
|
||||||
}
|
height: 100%;
|
||||||
</style>
|
margin: 0;
|
||||||
|
> .ant-upload {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
122
cmdb-ui/src/views/setting/notice/bot.vue
Normal file
122
cmdb-ui/src/views/setting/notice/bot.vue
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<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="操作" 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">请完整填写机器人配置</div>
|
||||||
|
<a-button
|
||||||
|
v-if="!disabled"
|
||||||
|
icon="plus-circle"
|
||||||
|
class="ops-button-primary"
|
||||||
|
type="primary"
|
||||||
|
@click="insertEvent"
|
||||||
|
>添加</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Bot',
|
||||||
|
props: {
|
||||||
|
columns: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '名称',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'url',
|
||||||
|
title: 'Webhook地址',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
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>
|
151
cmdb-ui/src/views/setting/notice/dingding.vue
Normal file
151
cmdb-ui/src/views/setting/notice/dingding.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<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>基础设置</SpanTitle>
|
||||||
|
<a-form-model-item label="应用Key">
|
||||||
|
<a-input v-model="dingdingData.appKey" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="应用密码">
|
||||||
|
<a-input v-model="dingdingData.appSecret" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="机器人码">
|
||||||
|
<a-input v-model="dingdingData.robotCode" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="机器人">
|
||||||
|
<Bot
|
||||||
|
ref="bot"
|
||||||
|
:disabled="!isEditable"
|
||||||
|
:columns="[
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '名称',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'url',
|
||||||
|
title: 'Webhook地址',
|
||||||
|
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"> 保存 </a-button>
|
||||||
|
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </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: '钉钉' } })
|
||||||
|
} else {
|
||||||
|
await postNoticeConfigByPlatform({
|
||||||
|
platform: 'dingdingApp',
|
||||||
|
info: { ...this.dingdingData, bot, label: '钉钉' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
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: 15px;
|
||||||
|
.notice-dingding-wrapper-tips {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #ffdfdf;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 12px;
|
||||||
|
width: 300px;
|
||||||
|
color: #000000;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
17
cmdb-ui/src/views/setting/notice/email/index.less
Normal file
17
cmdb-ui/src/views/setting/notice/email/index.less
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
.notice-email-wrapper {
|
||||||
|
background-color: #fff;
|
||||||
|
padding-top: 24px;
|
||||||
|
overflow: auto;
|
||||||
|
.notice-email-error-tips {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #ffdfdf;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 12px;
|
||||||
|
width: 300px;
|
||||||
|
color: #000000;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
33
cmdb-ui/src/views/setting/notice/email/index.vue
Normal file
33
cmdb-ui/src/views/setting/notice/email/index.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div :style="{ marginBottom: '-24px' }">
|
||||||
|
<a-tabs :activeKey="activeKey" @change="changeTab" class="ops-tab" type="card">
|
||||||
|
<!-- <a-tab-pane key="1" tab="接收服务器">
|
||||||
|
<Receive />
|
||||||
|
</a-tab-pane> -->
|
||||||
|
<a-tab-pane key="2" tab="发送服务器">
|
||||||
|
<Send />
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Receive from './receive.vue'
|
||||||
|
import Send from './send.vue'
|
||||||
|
export default {
|
||||||
|
name: 'NoticeEmail',
|
||||||
|
components: { Receive, Send },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeKey: '2',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeTab(activeKey) {
|
||||||
|
this.activeKey = activeKey
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
196
cmdb-ui/src/views/setting/notice/email/receive.vue
Normal file
196
cmdb-ui/src/views/setting/notice/email/receive.vue
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<div class="notice-email-wrapper" :style="{ height: `${windowHeight - 104}px` }">
|
||||||
|
<a-form-model :model="settingData" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
<SpanTitle>基础设置</SpanTitle>
|
||||||
|
<a-form-model-item label="连接协议">
|
||||||
|
<a-radio-group v-model="settingData.connectProtocol" :default-value="1" @change="changeConnectProtocol">
|
||||||
|
<a-radio :value="1" :default-checked="true"> POP/IMAP/POPS/IMAPS </a-radio>
|
||||||
|
<a-radio :value="2"> EWS(Exchange Web服务) </a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="认证类型">
|
||||||
|
<a-select v-model="settingData.authentication">
|
||||||
|
<a-select-option value="Base"> 基本 </a-select-option>
|
||||||
|
<a-select-option value="OAuth"> OAuth </a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="服务器名/IP地址" prop="IP">
|
||||||
|
<a-input v-model="settingData.IP" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="用户名">
|
||||||
|
<a-input v-model="settingData.username" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="密码">
|
||||||
|
<a-input v-model="settingData.password" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="邮件地址">
|
||||||
|
<a-input v-model="settingData.email" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<template v-if="settingData.connectProtocol === 1">
|
||||||
|
<a-form-model-item label="邮件类型">
|
||||||
|
<a-select v-model="settingData.emailType">
|
||||||
|
<a-select-option value="POP"> POP </a-select-option>
|
||||||
|
<a-select-option value="IMAP"> IMAP </a-select-option>
|
||||||
|
<a-select-option value="POPS"> POPS </a-select-option>
|
||||||
|
<a-select-option value="IMAPS"> IMAPS </a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="端口">
|
||||||
|
<a-input v-model="settingData.port" />
|
||||||
|
</a-form-model-item>
|
||||||
|
</template>
|
||||||
|
<a-form-model-item label="测试邮件设置">
|
||||||
|
<a-button type="primary" ghost>测试回收箱</a-button>
|
||||||
|
<br />
|
||||||
|
<span class="notice-email-error-tips">
|
||||||
|
<ops-icon type="icon-shidi-quxiao" :style="{ color: '#D81E06' }" />
|
||||||
|
邮件接收失败
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<span
|
||||||
|
>邮箱服务器未配置,请配置一个邮箱服务器 <a-divider type="vertical" :style="{ backgroundColor: '#2F54EB' }" />
|
||||||
|
<a>故障诊断</a></span
|
||||||
|
>
|
||||||
|
</a-form-model-item>
|
||||||
|
<SpanTitle>邮件设置</SpanTitle>
|
||||||
|
<a-form-model-item label="获取邮件间隔" :wrapperCol="{ span: 4 }">
|
||||||
|
<a-input class="ant-input-after" v-model="settingData.getEmailTimeout" />
|
||||||
|
<span :style="{ position: 'absolute', marginLeft: '8px' }">分</span>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="16" :offset="3">
|
||||||
|
<a-checkbox :default-checked="false" disabled>启动代理服务器</a-checkbox>
|
||||||
|
<a-icon type="info-circle" :style="{ color: '#FF9E58', fontSize: '16px' }" />
|
||||||
|
<a-divider type="vertical" :style="{ backgroundColor: '#2F54EB' }" />
|
||||||
|
<a @click="configProxySetting">配置代理设置</a>
|
||||||
|
<br />
|
||||||
|
<a-checkbox :default-checked="false">启动邮件测试</a-checkbox>
|
||||||
|
<br /><br />
|
||||||
|
<a-checkbox :default-checked="false" @change="changeCreateReqByEmail">禁用通过邮件创建请求</a-checkbox>
|
||||||
|
<br />
|
||||||
|
<template v-if="settingData.banReqByEmail">
|
||||||
|
<strong>指定允许的邮件/域名,逗号分隔多个值</strong>
|
||||||
|
<a-input type="textarea" :style="{ borderRadius: '8px', borderColor: '#2F54EB' }" />
|
||||||
|
<p :style="{ fontSize: '12px' }">例如:user@domain.com,*@domain.com</p>
|
||||||
|
<p :style="{ fontSize: '12px' }">限制不能适用于已在会话中的请求,它将聚集到它的上级工单中</p>
|
||||||
|
</template>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<SpanTitle>消息设置</SpanTitle>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="16" :offset="3">
|
||||||
|
<a-checkbox :default-checked="false">将消息移动到错误的文件夹</a-checkbox>
|
||||||
|
<a-icon type="info-circle" :style="{ color: '#FF9E58', fontSize: '16px' }" />
|
||||||
|
<a-divider type="vertical" :style="{ backgroundColor: '#2F54EB' }" />
|
||||||
|
<a href="#">了解更多</a>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<br /><br />
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="16" :offset="3">
|
||||||
|
<a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
<a-button type="primary" @click="onSubmit"> 保存 </a-button>
|
||||||
|
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </a-button>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-model>
|
||||||
|
<a-modal dialogClass="ops-modal" width="500px" v-model="visible" title="配置代理设置">
|
||||||
|
<a-form-model v-model="proxySetting" :label-col="{ span: 4 }" :wrapper-col="{ span: 19 }">
|
||||||
|
<a-form-model-item label="主机">
|
||||||
|
<a-input v-model="proxySetting.host" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="端口">
|
||||||
|
<a-input v-model="proxySetting.port" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="用户名">
|
||||||
|
<a-input v-model="proxySetting.username" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="密码">
|
||||||
|
<a-input v-model="proxySetting.password" />
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import SpanTitle from '../../components/spanTitle.vue'
|
||||||
|
export default {
|
||||||
|
name: 'Receive',
|
||||||
|
components: { SpanTitle },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
labelCol: { span: 3 },
|
||||||
|
wrapperCol: { span: 10 },
|
||||||
|
settingData: {
|
||||||
|
connectProtocol: 1,
|
||||||
|
authentication: 'Base',
|
||||||
|
IP: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
email: '',
|
||||||
|
emailType: '',
|
||||||
|
port: '',
|
||||||
|
|
||||||
|
getEmailTimeout: '',
|
||||||
|
activeProxy: false,
|
||||||
|
activeEmailDebug: false,
|
||||||
|
banReqByEmail: false,
|
||||||
|
|
||||||
|
transfromMessage: false,
|
||||||
|
},
|
||||||
|
visible: false,
|
||||||
|
proxySetting: {
|
||||||
|
host: '',
|
||||||
|
post: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeConnectProtocol(e) {
|
||||||
|
console.log(e.target.value)
|
||||||
|
},
|
||||||
|
changeCreateReqByEmail(e) {
|
||||||
|
this.settingData.banReqByEmail = e.target.checked
|
||||||
|
},
|
||||||
|
configProxySetting() {
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
onSubmit() {
|
||||||
|
console.log(this.settingData)
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.settingData = {
|
||||||
|
connectProtocol: 1,
|
||||||
|
authentication: '',
|
||||||
|
IP: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
email: '',
|
||||||
|
emailType: '',
|
||||||
|
port: '',
|
||||||
|
|
||||||
|
getEmailTimeout: '',
|
||||||
|
activeProxy: false,
|
||||||
|
activeEmailDebug: false,
|
||||||
|
banReqByEmail: false,
|
||||||
|
|
||||||
|
transfromMessage: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './index.less';
|
||||||
|
</style>
|
169
cmdb-ui/src/views/setting/notice/email/send.vue
Normal file
169
cmdb-ui/src/views/setting/notice/email/send.vue
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<template>
|
||||||
|
<div class="notice-email-wrapper" :style="{ height: `${windowHeight - 104}px` }">
|
||||||
|
<a-form-model ref="sendForm" :model="settingData" :label-col="labelCol" :rules="rules" :wrapper-col="wrapperCol">
|
||||||
|
<SpanTitle>基础设置</SpanTitle>
|
||||||
|
<a-form-model-item label="是否加密">
|
||||||
|
<a-radio-group v-model="settingData.tls" :disabled="!isEditable">
|
||||||
|
<a-radio :value="true">
|
||||||
|
是
|
||||||
|
</a-radio>
|
||||||
|
<a-radio :value="false">
|
||||||
|
否
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="端口" prop="port">
|
||||||
|
<a-input v-model="settingData.port" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="邮件服务器" prop="host">
|
||||||
|
<a-input v-model="settingData.host" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="用户名" prop="account">
|
||||||
|
<a-input v-model="settingData.account" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="密码" prop="password">
|
||||||
|
<a-input-password v-model="settingData.password" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<SpanTitle>邮件测试</SpanTitle>
|
||||||
|
<a-form-model-item label="测试发送邮件地址" prop="receive_address">
|
||||||
|
<a-input v-model="settingData.receive_address" :disabled="!isEditable">
|
||||||
|
<span
|
||||||
|
v-if="isEditable"
|
||||||
|
:style="{ cursor: 'pointer' }"
|
||||||
|
@click="testSendEmail"
|
||||||
|
slot="addonAfter"
|
||||||
|
>测试邮件发送</span
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</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"> 保存 </a-button>
|
||||||
|
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </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,
|
||||||
|
sendTestEmail,
|
||||||
|
} from '@/api/noticeSetting'
|
||||||
|
import { mixinPermissions } from '@/utils/mixin'
|
||||||
|
export default {
|
||||||
|
name: 'Send',
|
||||||
|
mixins: [mixinPermissions],
|
||||||
|
components: { SpanTitle },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
labelCol: { lg: 3, md: 5, sm: 8 },
|
||||||
|
wrapperCol: { lg: 10, md: 12, sm: 12 },
|
||||||
|
id: null,
|
||||||
|
settingData: {
|
||||||
|
tls: true,
|
||||||
|
host: '',
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
port: '',
|
||||||
|
receive_address: '',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
port: [{ required: true, message: '请输入端口', trigger: 'blur' }],
|
||||||
|
host: [{ required: true, whitespace: true, message: '请输入服务器', trigger: 'blur' }],
|
||||||
|
account: [
|
||||||
|
{ required: true, whitespace: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
|
||||||
|
message: '邮箱格式错误',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
password: [{ required: false, whitespace: true, message: '请输入密码', trigger: 'blur' }],
|
||||||
|
receive_address: [
|
||||||
|
{ required: false, whitespace: true, message: '请输入测试发送邮件地址', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
|
||||||
|
message: '邮箱格式错误',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
isEditable() {
|
||||||
|
return this.hasDetailPermission('backend', '通知设置', ['update'])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'settingData.tls': {
|
||||||
|
handler(newV, oldV) {
|
||||||
|
if (newV === false) {
|
||||||
|
this.settingData.port = 25
|
||||||
|
}
|
||||||
|
if (newV === true) {
|
||||||
|
this.settingData.port = 465
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getData() {
|
||||||
|
getNoticeConfigByPlatform({ platform: 'email' }).then((res) => {
|
||||||
|
this.id = res?.id ?? null
|
||||||
|
if (this.id) {
|
||||||
|
this.settingData = res.info
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async testSendEmail() {
|
||||||
|
await sendTestEmail(this.settingData.receive_address, {
|
||||||
|
info: { ...this.settingData, receive_address: undefined },
|
||||||
|
})
|
||||||
|
this.$message.success('已发送邮件,请查收')
|
||||||
|
},
|
||||||
|
onSubmit() {
|
||||||
|
this.$refs.sendForm.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (this.id) {
|
||||||
|
await putNoticeConfigByPlatform(this.id, { info: { ...this.settingData, label: '邮箱' } })
|
||||||
|
} else {
|
||||||
|
await postNoticeConfigByPlatform({ platform: 'email', info: { ...this.settingData, label: '邮箱' } })
|
||||||
|
}
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
this.getData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.settingData = {
|
||||||
|
tls: true,
|
||||||
|
host: '',
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
port: 25,
|
||||||
|
receive_address: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './index.less';
|
||||||
|
</style>
|
131
cmdb-ui/src/views/setting/notice/feishu.vue
Normal file
131
cmdb-ui/src/views/setting/notice/feishu.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<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>基础设置</SpanTitle>
|
||||||
|
<a-form-model-item label="应用ID">
|
||||||
|
<a-input v-model="feishuData.id" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="应用密码">
|
||||||
|
<a-input v-model="feishuData.password" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="机器人">
|
||||||
|
<Bot
|
||||||
|
ref="bot"
|
||||||
|
:disabled="!isEditable"
|
||||||
|
:columns="[
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '名称',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'url',
|
||||||
|
title: 'Webhook地址',
|
||||||
|
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"> 保存 </a-button>
|
||||||
|
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </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: '飞书' } })
|
||||||
|
} else {
|
||||||
|
await postNoticeConfigByPlatform({
|
||||||
|
platform: 'feishuApp',
|
||||||
|
info: { ...this.feishuData, bot, label: '飞书' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
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: 15px;
|
||||||
|
.notice-feishu-wrapper-tips {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #ffdfdf;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 12px;
|
||||||
|
width: 300px;
|
||||||
|
color: #000000;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
135
cmdb-ui/src/views/setting/notice/wx.vue
Normal file
135
cmdb-ui/src/views/setting/notice/wx.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<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>基础设置</SpanTitle>
|
||||||
|
<a-form-model-item label="企业ID">
|
||||||
|
<a-input v-model="wxData.corpid" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="自建应用ID">
|
||||||
|
<a-input v-model="wxData.agentid" :disabled="!isEditable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="自建应用密码">
|
||||||
|
<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="机器人">
|
||||||
|
<Bot ref="bot" :disabled="!isEditable" />
|
||||||
|
</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"> 保存 </a-button>
|
||||||
|
<a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </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: '企业微信' } })
|
||||||
|
} else {
|
||||||
|
await postNoticeConfigByPlatform({
|
||||||
|
platform: 'wechatApp',
|
||||||
|
info: { ...this.wxData, bot, label: '企业微信' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
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: 15px;
|
||||||
|
.notice-wx-wrapper-tips {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #ffdfdf;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 12px;
|
||||||
|
width: 300px;
|
||||||
|
color: #000000;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,368 +1,405 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="setting-person">
|
<div class="setting-person">
|
||||||
<div class="setting-person-left">
|
<div class="setting-person-left">
|
||||||
<div
|
<div
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
$refs.personForm.clearValidate()
|
$refs.personForm.clearValidate()
|
||||||
$nextTick(() => {
|
$nextTick(() => {
|
||||||
current = '1'
|
current = '1'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
:class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '1' }"
|
:class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '1' }"
|
||||||
>
|
>
|
||||||
<ops-icon type="icon-shidi-yonghu" />个人信息
|
<ops-icon type="icon-shidi-yonghu" />个人信息
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
$refs.personForm.clearValidate()
|
$refs.personForm.clearValidate()
|
||||||
$nextTick(() => {
|
$nextTick(() => {
|
||||||
current = '2'
|
current = '2'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
:class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '2' }"
|
:class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '2' }"
|
||||||
>
|
>
|
||||||
<a-icon type="unlock" theme="filled" />账号密码
|
<a-icon type="unlock" theme="filled" />账号密码
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-person-right">
|
<div class="setting-person-right">
|
||||||
<a-form-model
|
<a-form-model
|
||||||
ref="personForm"
|
ref="personForm"
|
||||||
:model="form"
|
:model="form"
|
||||||
:rules="current === '1' ? rules1 : rules2"
|
:rules="current === '1' ? rules1 : rules2"
|
||||||
:colon="false"
|
:colon="false"
|
||||||
labelAlign="left"
|
labelAlign="left"
|
||||||
:labelCol="{ span: 4 }"
|
:labelCol="{ span: 4 }"
|
||||||
:wrapperCol="{ span: 10 }"
|
:wrapperCol="{ span: 10 }"
|
||||||
>
|
>
|
||||||
<div v-show="current === '1'">
|
<div v-show="current === '1'">
|
||||||
<a-form-model-item label="头像" :style="{ display: 'flex', alignItems: 'center' }">
|
<a-form-model-item label="头像" :style="{ display: 'flex', alignItems: 'center' }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-avatar v-if="form.avatar" :src="`/api/common-setting/v1/file/${form.avatar}`" :size="64"> </a-avatar>
|
<a-avatar v-if="form.avatar" :src="`/api/common-setting/v1/file/${form.avatar}`" :size="64"> </a-avatar>
|
||||||
<a-avatar v-else style="backgroundColor:#F0F5FF" :size="64">
|
<a-avatar v-else style="backgroundColor:#F0F5FF" :size="64">
|
||||||
<ops-icon type="icon-shidi-yonghu" :style="{ color: '#2F54EB' }" />
|
<ops-icon type="icon-shidi-yonghu" :style="{ color: '#2F54EB' }" />
|
||||||
</a-avatar>
|
</a-avatar>
|
||||||
<a-upload
|
<a-upload
|
||||||
name="avatar"
|
name="avatar"
|
||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:customRequest="customRequest"
|
:customRequest="customRequest"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="beforeUpload"
|
||||||
:style="{ width: '310px', height: '100px' }"
|
:style="{ width: '310px', height: '100px' }"
|
||||||
accept=".svg,.png,.jpg,.jpeg"
|
accept=".svg,.png,.jpg,.jpeg"
|
||||||
>
|
>
|
||||||
<a-button type="primary" ghost size="small">更换头像</a-button>
|
<a-button type="primary" ghost size="small">更换头像</a-button>
|
||||||
</a-upload>
|
</a-upload>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="姓名" prop="nickname">
|
<a-form-model-item label="姓名" prop="nickname">
|
||||||
<a-input v-model="form.nickname" />
|
<a-input v-model="form.nickname" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="用户名">
|
<a-form-model-item label="用户名">
|
||||||
<div class="setting-person-right-disabled">{{ form.username }}</div>
|
<div class="setting-person-right-disabled">{{ form.username }}</div>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="邮箱">
|
<a-form-model-item label="邮箱">
|
||||||
<div class="setting-person-right-disabled">{{ form.email }}</div>
|
<div class="setting-person-right-disabled">{{ form.email }}</div>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="直属上级">
|
<a-form-model-item label="直属上级">
|
||||||
<div class="setting-person-right-disabled">
|
<div class="setting-person-right-disabled">
|
||||||
{{ getDirectorName(allFlatEmployees, form.direct_supervisor_id) }}
|
{{ getDirectorName(allFlatEmployees, form.direct_supervisor_id) }}
|
||||||
</div>
|
</div>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="性别">
|
<a-form-model-item label="性别">
|
||||||
<a-select v-model="form.sex">
|
<a-select v-model="form.sex">
|
||||||
<a-select-option value="男">男</a-select-option>
|
<a-select-option value="男">男</a-select-option>
|
||||||
<a-select-option value="女">女</a-select-option>
|
<a-select-option value="女">女</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="手机号" prop="mobile">
|
<a-form-model-item label="手机号" prop="mobile">
|
||||||
<a-input v-model="form.mobile" />
|
<a-input v-model="form.mobile" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="部门">
|
<a-form-model-item label="部门">
|
||||||
<div class="setting-person-right-disabled">
|
<div class="setting-person-right-disabled">
|
||||||
{{ getDepartmentName(allFlatDepartments, form.department_id) }}
|
{{ getDepartmentName(allFlatDepartments, form.department_id) }}
|
||||||
</div>
|
</div>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="岗位">
|
<a-form-model-item label="岗位">
|
||||||
<div class="setting-person-right-disabled">{{ form.position_name }}</div>
|
<div class="setting-person-right-disabled">{{ form.position_name }}</div>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item label="绑定信息">
|
<a-form-model-item label="绑定信息">
|
||||||
<a-space>
|
<a-space>
|
||||||
<div :class="{ 'setting-person-bind': true, 'setting-person-bind-existed': form.wx_id }">
|
<a-tooltip title="企业微信">
|
||||||
<ops-icon type="ops-setting-notice-wx" />
|
<div
|
||||||
</div>
|
@click="handleBind('wechatApp', form.notice_info && form.notice_info.wechatApp)"
|
||||||
<div @click="handleBindWx" class="setting-person-bind-button">
|
:class="{
|
||||||
{{ form.notice_info && form.notice_info.wechatApp ? '重新绑定' : '绑定' }}
|
'setting-person-bind': true,
|
||||||
</div>
|
'setting-person-bind-existed': form.notice_info && form.notice_info.wechatApp,
|
||||||
</a-space>
|
}"
|
||||||
</a-form-model-item>
|
>
|
||||||
</div>
|
<ops-icon type="ops-setting-notice-wx" />
|
||||||
<div v-show="current === '2'">
|
</div>
|
||||||
<a-form-model-item label="新密码" prop="password1">
|
</a-tooltip>
|
||||||
<a-input v-model="form.password1" />
|
<a-tooltip title="飞书">
|
||||||
</a-form-model-item>
|
<div
|
||||||
<a-form-model-item label="确认密码" prop="password2">
|
@click="handleBind('feishuApp', form.notice_info && form.notice_info.feishuApp)"
|
||||||
<a-input v-model="form.password2" />
|
:class="{
|
||||||
</a-form-model-item>
|
'setting-person-bind': true,
|
||||||
</div>
|
'setting-person-bind-existed': form.notice_info && form.notice_info.feishuApp,
|
||||||
<div style="margin-right: 120px">
|
}"
|
||||||
<a-form-model-item label=" ">
|
>
|
||||||
<a-button type="primary" @click="handleSave" :style="{ width: '100%' }">保存</a-button>
|
<ops-icon type="ops-setting-notice-feishu" />
|
||||||
</a-form-model-item>
|
</div>
|
||||||
</div>
|
</a-tooltip>
|
||||||
</a-form-model>
|
<a-tooltip title="钉钉">
|
||||||
</div>
|
<div
|
||||||
<EditImage
|
@click="handleBind('dingdingApp', form.notice_info && form.notice_info.dingdingApp)"
|
||||||
v-if="showEditImage"
|
:class="{
|
||||||
:fixed-number="eidtImageOption.fixedNumber"
|
'setting-person-bind': true,
|
||||||
:show="showEditImage"
|
'setting-person-bind-existed': form.notice_info && form.notice_info.dingdingApp,
|
||||||
:image="editImage"
|
}"
|
||||||
:title="eidtImageOption.title"
|
>
|
||||||
:preview-width="eidtImageOption.previewWidth"
|
<ops-icon type="ops-setting-notice-dingding" />
|
||||||
:preview-height="eidtImageOption.previewHeight"
|
</div>
|
||||||
preview-radius="0"
|
</a-tooltip>
|
||||||
width="550px"
|
</a-space>
|
||||||
save-button-title="确定"
|
</a-form-model-item>
|
||||||
@save="submitImage"
|
</div>
|
||||||
@close="showEditImage = false"
|
<div v-show="current === '2'">
|
||||||
/>
|
<a-form-model-item label="新密码" prop="password1">
|
||||||
</div>
|
<a-input v-model="form.password1" />
|
||||||
</template>
|
</a-form-model-item>
|
||||||
|
<a-form-model-item label="确认密码" prop="password2">
|
||||||
<script>
|
<a-input v-model="form.password2" />
|
||||||
import { mapActions, mapGetters } from 'vuex'
|
</a-form-model-item>
|
||||||
import { getAllDepartmentList } from '@/api/company'
|
</div>
|
||||||
import { postImageFile } from '@/api/file'
|
<div style="margin-right: 120px">
|
||||||
import {
|
<a-form-model-item label=" ">
|
||||||
getEmployeeList,
|
<a-button type="primary" @click="handleSave" :style="{ width: '100%' }">保存</a-button>
|
||||||
getEmployeeByUid,
|
</a-form-model-item>
|
||||||
updateEmployeeByUid,
|
</div>
|
||||||
updatePasswordByUid,
|
</a-form-model>
|
||||||
bindWxByUid,
|
</div>
|
||||||
} from '@/api/employee'
|
<EditImage
|
||||||
import { getDepartmentName, getDirectorName } from '@/utils/util'
|
v-if="showEditImage"
|
||||||
import EditImage from '../components/EditImage.vue'
|
:show="showEditImage"
|
||||||
export default {
|
:image="editImage"
|
||||||
name: 'Person',
|
:title="eidtImageOption.title"
|
||||||
components: { EditImage },
|
:preview-width="eidtImageOption.previewWidth"
|
||||||
data() {
|
:preview-height="eidtImageOption.previewHeight"
|
||||||
const validatePassword = (rule, value, callback) => {
|
preview-radius="0"
|
||||||
if (!value) {
|
width="550px"
|
||||||
callback(new Error('请二次确认新密码'))
|
save-button-title="确定"
|
||||||
}
|
@save="submitImage"
|
||||||
if (value !== this.form.password1) {
|
@close="showEditImage = false"
|
||||||
callback(new Error('两次输入密码不一致'))
|
/>
|
||||||
}
|
</div>
|
||||||
callback()
|
</template>
|
||||||
}
|
|
||||||
return {
|
<script>
|
||||||
current: '1',
|
import { mapActions, mapGetters } from 'vuex'
|
||||||
form: {},
|
import { getAllDepartmentList } from '@/api/company'
|
||||||
rules1: {
|
import { postImageFile } from '@/api/file'
|
||||||
nickname: [
|
import {
|
||||||
{ required: true, whitespace: true, message: '请输入姓名', trigger: 'blur' },
|
getEmployeeList,
|
||||||
{ max: 20, message: '字符数须小于20' },
|
getEmployeeByUid,
|
||||||
],
|
updateEmployeeByUid,
|
||||||
mobile: [
|
updatePasswordByUid,
|
||||||
{
|
bindPlatformByUid,
|
||||||
pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
unbindPlatformByUid,
|
||||||
message: '请输入正确的手机号',
|
} from '@/api/employee'
|
||||||
trigger: 'blur',
|
import { getDepartmentName, getDirectorName } from '@/utils/util'
|
||||||
},
|
import EditImage from '../components/EditImage.vue'
|
||||||
],
|
export default {
|
||||||
},
|
name: 'Person',
|
||||||
rules2: {
|
components: { EditImage },
|
||||||
password1: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
|
data() {
|
||||||
password2: [{ required: true, message: '两次输入密码不一致', trigger: 'blur', validator: validatePassword }],
|
const validatePassword = (rule, value, callback) => {
|
||||||
},
|
if (!value) {
|
||||||
allFlatEmployees: [],
|
callback(new Error('请二次确认新密码'))
|
||||||
allFlatDepartments: [],
|
}
|
||||||
showEditImage: false,
|
if (value !== this.form.password1) {
|
||||||
eidtImageOption: {
|
callback(new Error('两次输入密码不一致'))
|
||||||
type: 'avatar',
|
}
|
||||||
fixedNumber: [4, 4],
|
callback()
|
||||||
title: '编辑头像',
|
}
|
||||||
previewWidth: '60px',
|
return {
|
||||||
previewHeight: '60px',
|
current: '1',
|
||||||
},
|
form: {},
|
||||||
editImage: null,
|
rules1: {
|
||||||
}
|
nickname: [
|
||||||
},
|
{ required: true, whitespace: true, message: '请输入姓名', trigger: 'blur' },
|
||||||
computed: {
|
{ max: 20, message: '字符数须小于20' },
|
||||||
...mapGetters(['uid']),
|
],
|
||||||
},
|
mobile: [
|
||||||
mounted() {
|
{
|
||||||
this.getAllFlatEmployees()
|
pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||||
this.getAllFlatDepartment()
|
message: '请输入正确的手机号',
|
||||||
this.getEmployeeByUid()
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
methods: {
|
],
|
||||||
...mapActions(['GetInfo']),
|
},
|
||||||
getDepartmentName,
|
rules2: {
|
||||||
getDirectorName,
|
password1: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
|
||||||
getEmployeeByUid() {
|
password2: [{ required: true, message: '两次输入密码不一致', trigger: 'blur', validator: validatePassword }],
|
||||||
getEmployeeByUid(this.uid).then((res) => {
|
},
|
||||||
this.form = { ...res }
|
allFlatEmployees: [],
|
||||||
})
|
allFlatDepartments: [],
|
||||||
},
|
showEditImage: false,
|
||||||
getAllFlatEmployees() {
|
eidtImageOption: {
|
||||||
getEmployeeList({ block_status: 0, page_size: 99999 }).then((res) => {
|
type: 'avatar',
|
||||||
this.allFlatEmployees = res.data_list
|
fixedNumber: [4, 4],
|
||||||
})
|
title: '编辑头像',
|
||||||
},
|
previewWidth: '60px',
|
||||||
getAllFlatDepartment() {
|
previewHeight: '60px',
|
||||||
getAllDepartmentList({ is_tree: 0 }).then((res) => {
|
},
|
||||||
this.allFlatDepartments = res
|
editImage: null,
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
async handleSave() {
|
computed: {
|
||||||
await this.$refs.personForm.validate(async (valid) => {
|
...mapGetters(['uid']),
|
||||||
if (valid) {
|
},
|
||||||
const { nickname, mobile, sex, avatar, password1 } = this.form
|
mounted() {
|
||||||
const params = { nickname, mobile, sex, avatar }
|
this.getAllFlatEmployees()
|
||||||
if (this.current === '1') {
|
this.getAllFlatDepartment()
|
||||||
await updateEmployeeByUid(this.uid, params).then((res) => {
|
this.getEmployeeByUid()
|
||||||
this.$message.success('保存成功!')
|
},
|
||||||
this.getEmployeeByUid()
|
methods: {
|
||||||
this.GetInfo()
|
...mapActions(['GetInfo']),
|
||||||
})
|
getDepartmentName,
|
||||||
} else {
|
getDirectorName,
|
||||||
await updatePasswordByUid(this.uid, { password: password1 }).then((res) => {
|
getEmployeeByUid() {
|
||||||
this.$message.success('保存成功!')
|
getEmployeeByUid(this.uid).then((res) => {
|
||||||
})
|
this.form = { ...res }
|
||||||
}
|
})
|
||||||
}
|
},
|
||||||
})
|
getAllFlatEmployees() {
|
||||||
},
|
getEmployeeList({ block_status: 0, page_size: 99999 }).then((res) => {
|
||||||
customRequest(file) {
|
this.allFlatEmployees = res.data_list
|
||||||
const reader = new FileReader()
|
})
|
||||||
var self = this
|
},
|
||||||
reader.onload = function(e) {
|
getAllFlatDepartment() {
|
||||||
let result
|
getAllDepartmentList({ is_tree: 0 }).then((res) => {
|
||||||
if (typeof e.target.result === 'object') {
|
this.allFlatDepartments = res
|
||||||
// 把Array Buffer转化为blob 如果是base64不需要
|
})
|
||||||
result = window.URL.createObjectURL(new Blob([e.target.result]))
|
},
|
||||||
} else {
|
async handleSave() {
|
||||||
result = e.target.result
|
await this.$refs.personForm.validate(async (valid) => {
|
||||||
}
|
if (valid) {
|
||||||
|
const { nickname, mobile, sex, avatar, password1 } = this.form
|
||||||
self.editImage = result
|
const params = { nickname, mobile, sex, avatar }
|
||||||
self.showEditImage = true
|
if (this.current === '1') {
|
||||||
}
|
await updateEmployeeByUid(this.uid, params).then((res) => {
|
||||||
reader.readAsDataURL(file.file)
|
this.$message.success('保存成功!')
|
||||||
},
|
this.getEmployeeByUid()
|
||||||
beforeUpload(file) {
|
this.GetInfo()
|
||||||
const isLt2M = file.size / 1024 / 1024 < 2
|
})
|
||||||
if (!isLt2M) {
|
} else {
|
||||||
this.$message.error('图片大小不可超过2MB!')
|
await updatePasswordByUid(this.uid, { password: password1 }).then((res) => {
|
||||||
}
|
this.$message.success('保存成功!')
|
||||||
return isLt2M
|
})
|
||||||
},
|
}
|
||||||
submitImage(file) {
|
}
|
||||||
postImageFile(file).then((res) => {
|
})
|
||||||
if (res.file_name) {
|
},
|
||||||
this.form.avatar = res.file_name
|
customRequest(file) {
|
||||||
}
|
const reader = new FileReader()
|
||||||
})
|
var self = this
|
||||||
},
|
reader.onload = function(e) {
|
||||||
async handleBindWx() {
|
let result
|
||||||
await this.$refs.personForm.validate(async (valid) => {
|
if (typeof e.target.result === 'object') {
|
||||||
if (valid) {
|
// 把Array Buffer转化为blob 如果是base64不需要
|
||||||
const { nickname, mobile, sex, avatar } = this.form
|
result = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||||
const params = { nickname, mobile, sex, avatar }
|
} else {
|
||||||
await updateEmployeeByUid(this.uid, params)
|
result = e.target.result
|
||||||
bindWxByUid(this.uid)
|
}
|
||||||
.then(() => {
|
|
||||||
this.$message.success('绑定成功!')
|
self.editImage = result
|
||||||
})
|
self.showEditImage = true
|
||||||
.finally(() => {
|
}
|
||||||
this.getEmployeeByUid()
|
reader.readAsDataURL(file.file)
|
||||||
this.GetInfo()
|
},
|
||||||
})
|
beforeUpload(file) {
|
||||||
}
|
const isLt2M = file.size / 1024 / 1024 < 2
|
||||||
})
|
if (!isLt2M) {
|
||||||
},
|
this.$message.error('图片大小不可超过2MB!')
|
||||||
},
|
}
|
||||||
}
|
return isLt2M
|
||||||
</script>
|
},
|
||||||
|
submitImage(file) {
|
||||||
<style lang="less" scoped>
|
postImageFile(file).then((res) => {
|
||||||
@import '~@/style/static.less';
|
if (res.file_name) {
|
||||||
.setting-person {
|
this.form.avatar = res.file_name
|
||||||
display: flex;
|
}
|
||||||
flex-direction: row;
|
})
|
||||||
.setting-person-left {
|
},
|
||||||
width: 200px;
|
async handleBind(platform, isBind) {
|
||||||
height: 400px;
|
if (isBind) {
|
||||||
margin-right: 24px;
|
const that = this
|
||||||
background-color: #fff;
|
this.$confirm({
|
||||||
border-radius: 15px;
|
title: '警告',
|
||||||
padding-top: 15px;
|
content: `确认解绑?`,
|
||||||
.setting-person-left-item {
|
onOk() {
|
||||||
cursor: pointer;
|
unbindPlatformByUid(platform, that.uid)
|
||||||
padding: 10px 20px;
|
.then(() => {
|
||||||
color: #a5a9bc;
|
that.$message.success('解绑成功!')
|
||||||
border-left: 4px solid #fff;
|
})
|
||||||
margin-bottom: 5px;
|
.finally(() => {
|
||||||
&:hover {
|
that.getEmployeeByUid()
|
||||||
.ops_popover_item_selected();
|
that.GetInfo()
|
||||||
border-color: #custom_colors[color_1];
|
})
|
||||||
}
|
},
|
||||||
> i {
|
})
|
||||||
margin-right: 10px;
|
} else {
|
||||||
}
|
await this.$refs.personForm.validate(async (valid) => {
|
||||||
}
|
if (valid) {
|
||||||
.setting-person-left-item-selected {
|
const { nickname, mobile, sex, avatar } = this.form
|
||||||
.ops_popover_item_selected();
|
const params = { nickname, mobile, sex, avatar }
|
||||||
border-color: #custom_colors[color_1];
|
await updateEmployeeByUid(this.uid, params)
|
||||||
}
|
bindPlatformByUid(platform, this.uid)
|
||||||
}
|
.then(() => {
|
||||||
.setting-person-right {
|
this.$message.success('绑定成功!')
|
||||||
width: 800px;
|
})
|
||||||
height: 700px;
|
.finally(() => {
|
||||||
background-color: #fff;
|
this.getEmployeeByUid()
|
||||||
border-radius: 15px;
|
this.GetInfo()
|
||||||
padding: 24px 48px;
|
})
|
||||||
.setting-person-right-disabled {
|
}
|
||||||
background-color: #custom_colors[color_2];
|
})
|
||||||
border-radius: 4px;
|
}
|
||||||
height: 30px;
|
},
|
||||||
line-height: 30px;
|
},
|
||||||
margin-top: 4px;
|
}
|
||||||
padding: 0 10px;
|
</script>
|
||||||
color: #a5a9bc;
|
|
||||||
}
|
<style lang="less" scoped>
|
||||||
.setting-person-bind {
|
@import '~@/style/static.less';
|
||||||
width: 40px;
|
.setting-person {
|
||||||
height: 40px;
|
display: flex;
|
||||||
background: #a5a9bc;
|
flex-direction: row;
|
||||||
border-radius: 4px;
|
.setting-person-left {
|
||||||
color: #fff;
|
width: 200px;
|
||||||
font-size: 30px;
|
height: 400px;
|
||||||
text-align: center;
|
margin-right: 24px;
|
||||||
}
|
background-color: #fff;
|
||||||
.setting-person-bind-existed {
|
border-radius: 15px;
|
||||||
background: #008cee;
|
padding-top: 15px;
|
||||||
}
|
.setting-person-left-item {
|
||||||
.setting-person-bind-button {
|
cursor: pointer;
|
||||||
height: 40px;
|
padding: 10px 20px;
|
||||||
width: 72px;
|
color: #a5a9bc;
|
||||||
background: #f0f5ff;
|
border-left: 4px solid #fff;
|
||||||
border-radius: 4px;
|
margin-bottom: 5px;
|
||||||
padding: 0 8px;
|
&:hover {
|
||||||
text-align: center;
|
.ops_popover_item_selected();
|
||||||
cursor: pointer;
|
border-color: #custom_colors[color_1];
|
||||||
}
|
}
|
||||||
}
|
> i {
|
||||||
}
|
margin-right: 10px;
|
||||||
</style>
|
}
|
||||||
<style lang="less">
|
}
|
||||||
.setting-person-right .ant-form-item {
|
.setting-person-left-item-selected {
|
||||||
margin-bottom: 12px;
|
.ops_popover_item_selected();
|
||||||
display: flex;
|
border-color: #custom_colors[color_1];
|
||||||
justify-content: center;
|
}
|
||||||
align-items: center;
|
}
|
||||||
}
|
.setting-person-right {
|
||||||
</style>
|
width: 800px;
|
||||||
|
height: 700px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 24px 48px;
|
||||||
|
.setting-person-right-disabled {
|
||||||
|
background-color: #custom_colors[color_2];
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: #a5a9bc;
|
||||||
|
}
|
||||||
|
.setting-person-bind {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: #a5a9bc;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 30px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.setting-person-bind-existed {
|
||||||
|
background: #008cee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less">
|
||||||
|
.setting-person-right .ant-form-item {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -30,7 +30,7 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
|
|
||||||
cmdb-api:
|
cmdb-api:
|
||||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.3.4
|
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.3.5
|
||||||
# build:
|
# build:
|
||||||
# context: .
|
# context: .
|
||||||
# target: cmdb-api
|
# target: cmdb-api
|
||||||
@@ -45,6 +45,7 @@ services:
|
|||||||
sed -i "s#USE_ACL = False#USE_ACL = True#g" settings.py
|
sed -i "s#USE_ACL = False#USE_ACL = True#g" settings.py
|
||||||
/wait
|
/wait
|
||||||
flask db-setup
|
flask db-setup
|
||||||
|
flask common-check-new-columns
|
||||||
gunicorn --workers=3 autoapp:app -b 0.0.0.0:5000 -D
|
gunicorn --workers=3 autoapp:app -b 0.0.0.0:5000 -D
|
||||||
flask cmdb-init-cache
|
flask cmdb-init-cache
|
||||||
flask cmdb-init-acl
|
flask cmdb-init-acl
|
||||||
@@ -62,7 +63,7 @@ services:
|
|||||||
- cmdb-api
|
- cmdb-api
|
||||||
|
|
||||||
cmdb-ui:
|
cmdb-ui:
|
||||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.3.4
|
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.3.5
|
||||||
# build:
|
# build:
|
||||||
# context: .
|
# context: .
|
||||||
# target: cmdb-ui
|
# target: cmdb-ui
|
||||||
|
Reference in New Issue
Block a user