Merge branch 'develop' into i18n

This commit is contained in:
pycook 2020-02-11 09:50:11 +08:00 committed by GitHub
commit cd0797e860
13 changed files with 633 additions and 97 deletions

1
.gitignore vendored
View File

@ -38,6 +38,7 @@ pip-log.txt
.tox .tox
nosetests.xml nosetests.xml
.pytest_cache .pytest_cache
cmdb-api/test-output
# Translations # Translations
*.mo *.mo

View File

@ -23,6 +23,7 @@ from api.models.cmdb import CITypeGroupItem
from api.models.cmdb import CITypeRelation from api.models.cmdb import CITypeRelation
from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView from api.models.cmdb import PreferenceTreeView
from api.tasks.cmdb import ci_type_attribute_order_rebuild
class CITypeManager(object): class CITypeManager(object):
@ -39,7 +40,9 @@ class CITypeManager(object):
@staticmethod @staticmethod
def check_is_existed(key): def check_is_existed(key):
return CITypeCache.get(key) or abort(404, "CIType <{0}> is not existed".format(key)) ci_type = CITypeCache.get(key) or abort(404, "CIType <{0}> is not existed".format(key))
return CIType.get_by_id(ci_type.id)
@staticmethod @staticmethod
def get_ci_types(type_name=None): def get_ci_types(type_name=None):
@ -55,16 +58,35 @@ class CITypeManager(object):
ci_type = CITypeCache.get(_type) or abort(404, "CIType <{0}> is not found".format(_type)) ci_type = CITypeCache.get(_type) or abort(404, "CIType <{0}> is not found".format(_type))
return ci_type.to_dict() return ci_type.to_dict()
@staticmethod
def _validate_unique(type_id=None, name=None, alias=None):
if name is not None:
ci_type = CIType.get_by(name=name, first=True, to_dict=False)
elif alias is not None:
ci_type = CIType.get_by(alias=alias, first=True, to_dict=False)
else:
return
if not ci_type:
return
if type_id is not None and ci_type.id != type_id:
return abort(400, "CIType <{0}> is already existed".format(name or alias))
if type_id is None and ci_type is not None:
return abort(400, "CIType <{0}> is already existed".format(name or alias))
@classmethod @classmethod
@kwargs_required("name") @kwargs_required("name")
def add(cls, **kwargs): def add(cls, **kwargs):
unique_key = kwargs.pop("unique_key", None) unique_key = kwargs.pop("unique_key", None)
unique_key = AttributeCache.get(unique_key) or abort(404, "Unique key is not defined") unique_key = AttributeCache.get(unique_key) or abort(404, "Unique key is not defined")
CIType.get_by(name=kwargs['name']) and abort(404, "CIType <{0}> is already existed".format(kwargs.get("name")))
kwargs["alias"] = kwargs["name"] if not kwargs.get("alias") else kwargs["alias"] kwargs["alias"] = kwargs["name"] if not kwargs.get("alias") else kwargs["alias"]
cls._validate_unique(name=kwargs['name'])
cls._validate_unique(alias=kwargs['alias'])
kwargs["unique_id"] = unique_key.id kwargs["unique_id"] = unique_key.id
ci_type = CIType.create(**kwargs) ci_type = CIType.create(**kwargs)
@ -88,6 +110,9 @@ class CITypeManager(object):
ci_type = cls.check_is_existed(type_id) ci_type = cls.check_is_existed(type_id)
cls._validate_unique(type_id=type_id, name=kwargs.get('name'))
cls._validate_unique(type_id=type_id, alias=kwargs.get('alias'))
unique_key = kwargs.pop("unique_key", None) unique_key = kwargs.pop("unique_key", None)
unique_key = AttributeCache.get(unique_key) unique_key = AttributeCache.get(unique_key)
if unique_key is not None: if unique_key is not None:
@ -305,6 +330,31 @@ class CITypeAttributeManager(object):
CITypeAttributesCache.clean(type_id) CITypeAttributesCache.clean(type_id)
@classmethod
def transfer(cls, type_id, _from, _to):
current_app.logger.info("[{0}] {1} -> {2}".format(type_id, _from, _to))
attr_id = _from.get('attr_id')
from_group_id = _from.get('group_id')
to_group_id = _to.get('group_id')
order = _to.get('order')
if from_group_id != to_group_id:
if from_group_id is not None:
CITypeAttributeGroupManager.delete_item(from_group_id, attr_id)
if to_group_id is not None:
CITypeAttributeGroupManager.add_item(to_group_id, attr_id, order)
elif from_group_id:
CITypeAttributeGroupManager.update_item(from_group_id, attr_id, order)
else: # other attribute transfer
return abort(400, "invalid operation!!!")
CITypeAttributesCache.clean(type_id)
ci_type_attribute_order_rebuild.apply_async(args=(type_id,), queue=CMDB_QUEUE)
class CITypeRelationManager(object): class CITypeRelationManager(object):
""" """
@ -441,6 +491,8 @@ class CITypeAttributeGroupManager(object):
return abort(400, "Group <{0}> duplicate".format(name)) return abort(400, "Group <{0}> duplicate".format(name))
if name is not None: if name is not None:
group.update(name=name) group.update(name=name)
else:
name = group.name
cls.create_or_update(group.type_id, name, attr_order, group_order) cls.create_or_update(group.type_id, name, attr_order, group_order)
@ -455,3 +507,86 @@ class CITypeAttributeGroupManager(object):
item.soft_delete() item.soft_delete()
return group_id return group_id
@classmethod
def add_item(cls, group_id, attr_id, order):
db.session.remove()
existed = CITypeAttributeGroupItem.get_by(group_id=group_id,
attr_id=attr_id,
first=True,
to_dict=False)
if existed is not None:
existed.update(order=order)
else:
CITypeAttributeGroupItem.create(group_id=group_id, attr_id=attr_id, order=order)
gt_items = db.session.query(CITypeAttributeGroupItem).filter(
CITypeAttributeGroupItem.deleted.is_(False)).filter(CITypeAttributeGroupItem.order > order)
for _item in gt_items:
_order = _item.order
_item.update(order=_order + 1)
@classmethod
def update_item(cls, group_id, attr_id, order):
db.session.remove()
existed = CITypeAttributeGroupItem.get_by(group_id=group_id,
attr_id=attr_id,
first=True,
to_dict=False)
existed or abort(404, "Group<{0}> - Attribute<{1}> is not found".format(group_id, attr_id))
if existed.order > order: # forward, +1
items = db.session.query(CITypeAttributeGroupItem).filter(
CITypeAttributeGroupItem.deleted.is_(False)).filter(
CITypeAttributeGroupItem.order >= order).filter(
CITypeAttributeGroupItem.order < existed.order)
for item in items:
item.update(order=item.order + 1)
elif existed.order < order: # backward, -1
items = db.session.query(CITypeAttributeGroupItem).filter(
CITypeAttributeGroupItem.deleted.is_(False)).filter(
CITypeAttributeGroupItem.order > existed.order).filter(
CITypeAttributeGroupItem.order <= order)
for item in items:
item.update(order=item.order - 1)
existed.update(order=order)
@classmethod
def delete_item(cls, group_id, attr_id):
db.session.remove()
item = CITypeAttributeGroupItem.get_by(group_id=group_id,
attr_id=attr_id,
first=True,
to_dict=False)
if item is not None:
item.soft_delete()
order = item.order
gt_items = db.session.query(CITypeAttributeGroupItem).filter(
CITypeAttributeGroupItem.deleted.is_(False)).filter(CITypeAttributeGroupItem.order > order)
for _item in gt_items:
_order = _item.order
_item.update(order=_order - 1)
@classmethod
def transfer(cls, type_id, _from, _to):
current_app.logger.info("CIType[{0}] {1} -> {2}".format(type_id, _from, _to))
from_group = CITypeAttributeGroup.get_by_id(_from)
from_group or abort(404, "Group <{0}> is not found".format(_from))
to_group = CITypeAttributeGroup.get_by_id(_to)
to_group or abort(404, "Group <{0}> is not found".format(_to))
from_order, to_order = from_group.order, to_group.order
from_group.update(order=to_order)
to_group.update(order=from_order)
CITypeAttributesCache.clean(type_id)
ci_type_attribute_order_rebuild.apply_async(args=(type_id,), queue=CMDB_QUEUE)

View File

@ -25,6 +25,9 @@ class FormatMixin(object):
class CRUDMixin(FormatMixin): class CRUDMixin(FormatMixin):
def __init__(self, **kwargs):
super(CRUDMixin, self).__init__(**kwargs)
@classmethod @classmethod
def create(cls, flush=False, **kwargs): def create(cls, flush=False, **kwargs):
return cls(**kwargs).save(flush=flush) return cls(**kwargs).save(flush=flush)

View File

@ -47,7 +47,7 @@ def _auth_with_token():
try: try:
token = auth_headers token = auth_headers
data = jwt.decode(token, current_app.config['SECRET_KEY']) data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
user = User.query.filter_by(email=data['sub']).first() user = User.query.filter_by(email=data['sub']).first()
if not user: if not user:
return False return False

View File

@ -15,13 +15,13 @@ from api.lib.database import Model
class RelationType(Model): class RelationType(Model):
__tablename__ = "c_relation_types" __tablename__ = "c_relation_types"
name = db.Column(db.String(16), index=True) name = db.Column(db.String(16), index=True, nullable=False)
class CITypeGroup(Model): class CITypeGroup(Model):
__tablename__ = "c_ci_type_groups" __tablename__ = "c_ci_type_groups"
name = db.Column(db.String(32)) name = db.Column(db.String(32), nullable=False)
class CITypeGroupItem(Model): class CITypeGroupItem(Model):
@ -35,12 +35,12 @@ class CITypeGroupItem(Model):
class CIType(Model): class CIType(Model):
__tablename__ = "c_ci_types" __tablename__ = "c_ci_types"
name = db.Column(db.String(32)) name = db.Column(db.String(32), nullable=False)
alias = db.Column(db.String(32)) alias = db.Column(db.String(32), nullable=False)
unique_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"), nullable=False) unique_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"), nullable=False)
enabled = db.Column(db.Boolean, default=True, nullable=False) enabled = db.Column(db.Boolean, default=True, nullable=False)
is_attached = db.Column(db.Boolean, default=False, nullable=False) is_attached = db.Column(db.Boolean, default=False, nullable=False)
icon_url = db.Column(db.String(256)) icon_url = db.Column(db.String(256), default='', nullable=False)
order = db.Column(db.SmallInteger, default=0, nullable=False) order = db.Column(db.SmallInteger, default=0, nullable=False)
unique_key = db.relationship("Attribute", backref="c_ci_types.unique_id") unique_key = db.relationship("Attribute", backref="c_ci_types.unique_id")
@ -89,7 +89,7 @@ class CITypeAttribute(Model):
class CITypeAttributeGroup(Model): class CITypeAttributeGroup(Model):
__tablename__ = "c_ci_type_attribute_groups" __tablename__ = "c_ci_type_attribute_groups"
name = db.Column(db.String(64)) name = db.Column(db.String(64), nullable=False)
type_id = db.Column(db.Integer, db.ForeignKey("c_ci_types.id"), nullable=False) type_id = db.Column(db.Integer, db.ForeignKey("c_ci_types.id"), nullable=False)
order = db.Column(db.SmallInteger, default=0) order = db.Column(db.SmallInteger, default=0)
@ -266,8 +266,8 @@ class OperationRecord(Model):
__tablename__ = "c_records" __tablename__ = "c_records"
uid = db.Column(db.Integer, index=True, nullable=False) uid = db.Column(db.Integer, index=True, nullable=False)
origin = db.Column(db.String(32)) origin = db.Column(db.String(32), nullable=False)
ticket_id = db.Column(db.String(32)) ticket_id = db.Column(db.String(32), nullable=False)
reason = db.Column(db.Text) reason = db.Column(db.Text)

View File

@ -11,6 +11,7 @@ from api.extensions import celery
from api.extensions import db from api.extensions import db
from api.extensions import es from api.extensions import es
from api.extensions import rd from api.extensions import rd
from api.lib.cmdb.cache import CITypeAttributeCache
from api.lib.cmdb.const import CMDB_QUEUE from api.lib.cmdb.const import CMDB_QUEUE
from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
@ -71,3 +72,23 @@ def ci_relation_delete(parent_id, child_id):
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION) rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id)) current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))
@celery.task(name="cmdb.ci_type_attribute_order_rebuild", queue=CMDB_QUEUE)
def ci_type_attribute_order_rebuild(type_id):
current_app.logger.info('rebuild attribute order')
db.session.remove()
from api.lib.cmdb.ci_type import CITypeAttributeGroupManager
attrs = CITypeAttributeCache.get(type_id)
id2attr = {attr.attr_id: attr for attr in attrs}
res = CITypeAttributeGroupManager.get_by_type_id(type_id, True)
order = 0
for group in res:
for _attr in group.get('attributes'):
if order != id2attr.get(_attr['id']) and id2attr.get(_attr['id']):
id2attr.get(_attr['id']).update(order=order)
order += 1

View File

@ -164,6 +164,34 @@ class CITypeAttributeView(APIView):
return self.jsonify(attributes=attr_id_list) return self.jsonify(attributes=attr_id_list)
class CITypeAttributeTransferView(APIView):
url_prefix = "/ci_types/<int:type_id>/attributes/transfer"
@args_required('from')
@args_required('to')
def post(self, type_id):
_from = request.values.get('from') # {'attr_id': xx, 'group_id': xx}
_to = request.values.get('to') # {'group_id': xx, 'order': xxx}
CITypeAttributeManager.transfer(type_id, _from, _to)
return self.jsonify(code=200)
class CITypeAttributeGroupTransferView(APIView):
url_prefix = "/ci_types/<int:type_id>/attribute_groups/transfer"
@args_required('from')
@args_required('to')
def post(self, type_id):
_from = request.values.get('from') # group_id
_to = request.values.get('to') # group_id
CITypeAttributeGroupManager.transfer(type_id, _from, _to)
return self.jsonify(code=200)
class CITypeAttributeGroupView(APIView): class CITypeAttributeGroupView(APIView):
url_prefix = ("/ci_types/<int:type_id>/attribute_groups", url_prefix = ("/ci_types/<int:type_id>/attribute_groups",
"/ci_types/attribute_groups/<int:group_id>") "/ci_types/attribute_groups/<int:group_id>")

View File

@ -21,7 +21,7 @@ class CMDBTestClient(FlaskClient):
headers.setdefault("User-Agent", "py.test") headers.setdefault("User-Agent", "py.test")
kwargs["headers"] = headers kwargs["headers"] = headers
json_data = kwargs.pop("json") json_data = kwargs.pop("json", None)
if json_data is not None: if json_data is not None:
kwargs["data"] = json.dumps(json_data) kwargs["data"] = json.dumps(json_data)
if not kwargs.get("content_type"): if not kwargs.get("content_type"):
@ -49,6 +49,7 @@ def app():
_app.config['SECRET_KEY'] = CMDBTestClient.TEST_APP_SECRET _app.config['SECRET_KEY'] = CMDBTestClient.TEST_APP_SECRET
_app.test_client_class = CMDBTestClient _app.test_client_class = CMDBTestClient
_app.response_class = CMDBTestResponse _app.response_class = CMDBTestResponse
ctx = _app.test_request_context() ctx = _app.test_request_context()
ctx.push() ctx.push()
yield _app yield _app
@ -99,7 +100,6 @@ def clean_db():
db.session.commit() db.session.commit()
if not User.get_by(email="test@xx.com"): if not User.get_by(email="test@xx.com"):
print("hello world xxxxx")
u = User.create( u = User.create(
flush=True, flush=True,
username="test", username="test",

49
cmdb-api/tests/sample.py Normal file
View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
"""provide some sample data in database"""
import uuid
import random
from api.models.cmdb import Attribute, CIType, CITypeAttributeGroup, CITypeAttribute
def init_attributes(num=1):
attrs = []
for i in range(num):
attrs.append(Attribute.create(
name=uuid.uuid4().hex[:8],
alias=uuid.uuid4().hex[:8],
value_type=str(random.randint(0, 100) % 7)
))
return attrs
def init_ci_types(num=1):
attrs = init_attributes(num)
ci_types = []
for i in range(num):
ci_type = CIType.create(
name=uuid.uuid4().hex[:8],
alias=uuid.uuid4().hex[:8],
unique_id=attrs[i].id
)
CITypeAttribute.create(
type_id=ci_type.id,
attr_id=attrs[i].id,
)
ci_types.append(ci_type)
return ci_types
def init_attribute_groups(num=1):
ci_types = init_ci_types(num)
ags = []
for i in range(num):
ags.append(CITypeAttributeGroup.create(
name=uuid.uuid4().hex[:8],
type_id=ci_types[i].id,
order=i
))
return ags

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from api.models.cmdb import Attribute from api.models.cmdb import Attribute
from tests.sample import init_attributes
def test_create_attribute(session, client): def test_create_attribute(session, client):
url = "/api/v0.1/attributes" url = "/api/v0.1/attributes"
@ -16,7 +18,7 @@ def test_create_attribute(session, client):
assert resp.status_code == 200 assert resp.status_code == 200
assert resp.json["attr_id"] assert resp.json["attr_id"]
# check there is a ci_types in database # check there is a attribute in database
attr_id = resp.json["attr_id"] attr_id = resp.json["attr_id"]
attr_ins = Attribute.get_by_id(attr_id) attr_ins = Attribute.get_by_id(attr_id)
assert attr_ins.id == attr_id assert attr_ins.id == attr_id
@ -24,3 +26,34 @@ def test_create_attribute(session, client):
assert attr_ins.alias == "区域" assert attr_ins.alias == "区域"
def test_update_attribute(session, client):
attr_ins = init_attributes(1)[0]
url = "/api/v0.1/attributes/" + str(attr_ins.id)
payload = {
"name": "update",
}
resp = client.put(url, json=payload)
# check resp status code and content
assert resp.status_code == 200
assert resp.json["attr_id"] == attr_ins.id
# check attribute updated in database
attr_ins = Attribute.get_by_id(attr_ins.id)
assert attr_ins.name == "update"
def test_delete_attribute(session, client):
attr_ins = init_attributes(1)[0]
url = "/api/v0.1/attributes/" + str(attr_ins.id)
resp = client.delete(url)
assert resp.status_code == 200
# attr should be soft delete
attr_ins = Attribute.get_by_id(attr_ins.id)
assert attr_ins.deleted is True
assert attr_ins.deleted_at

View File

@ -1,10 +1,3 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
class TestCI:
def test_ci_search_only_type_query(self, app):
with app.test_client() as c:
rv = c.get('/api/v0.1/ci/s?q=_type:server', json={})
json_data = rv.get_json()
assert type(json_data.get("result")) is list

View File

@ -1 +1,167 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from api.models.cmdb import (
CIType, CITypeAttribute,
Attribute, CITypeAttributeGroup,
CITypeAttributeGroupItem)
from tests.sample import (
init_attributes, init_ci_types,
init_attribute_groups)
def test_create_ci_type(session, client):
attr = init_attributes(1)[0]
url = "/api/v0.1/ci_types"
payload = {
"name": "test",
"alias": "测试",
"unique_key": attr.id
}
resp = client.post(url, json=payload)
# check resp status code and content
assert resp.status_code == 200
assert resp.json["type_id"]
# check there is a attribute in database
type_id = resp.json["type_id"]
ci_type_ins = CIType.get_by_id(type_id)
assert ci_type_ins.id == type_id
assert ci_type_ins.name == "test"
assert ci_type_ins.alias == "测试"
assert ci_type_ins.unique_id == attr.id
def test_update_ci_type(session, client):
ci_type_ins = init_ci_types(1)[0]
url = "/api/v0.1/ci_types/" + str(ci_type_ins.id)
payload = {
"name": "update",
}
resp = client.put(url, json=payload)
# check resp status code and content
assert resp.status_code == 200
assert resp.json["type_id"] == ci_type_ins.id
# check ci_type updated in database
ci_type_ins = CIType.get_by_id(ci_type_ins.id)
assert ci_type_ins.name == "update"
def test_delete_ci_type(session, client):
ci_type_ins = init_ci_types(1)[0]
url = "/api/v0.1/ci_types/" + str(ci_type_ins.id)
resp = client.delete(url)
assert resp.status_code == 200
# attr should be soft delete
ci_type_ins = CIType.get_by_id(ci_type_ins.id)
assert ci_type_ins.deleted is True
assert ci_type_ins.deleted_at
def test_bind_attributes_ci_type(session, client):
attrs = init_attributes(3)
ci_type = init_ci_types(1)[0]
url = "/api/v0.1/ci_types/{}/attributes".format(ci_type.id)
payload = {
"attr_id": [str(x.id) for x in attrs]
}
resp = client.post(url, json=payload)
# check resp status code and content
assert resp.status_code == 200
assert len(resp.json["attributes"]) == len(attrs)
# check ci_type has 4 attributes
ci_type_attribute_ids = [x.attr_id for x in CITypeAttribute.query.filter_by(type_id=ci_type.id).all()]
for attr in attrs:
assert attr.id in ci_type_attribute_ids
def test_get_attributes_ci_type(session, client):
ci_type = init_ci_types(1)[0]
url = "/api/v0.1/ci_types/{}/attributes".format(ci_type.name)
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json["attributes"]) == 1
def test_update_attributes_ci_type(session, client):
ci_type = init_ci_types(1)[0]
attr = Attribute.query.first()
url = "/api/v0.1/ci_types/{}/attributes".format(ci_type.id)
payload = {
"attributes": [
{"attr_id": attr.id, "default_show": False, "is_required": True}
]
}
resp = client.put(url, json=payload)
assert resp.status_code == 200
ci_type_attr_ins = CITypeAttribute.query.filter_by(type_id=ci_type.id).first()
assert ci_type_attr_ins
assert ci_type_attr_ins.is_required is True
assert ci_type_attr_ins.default_show is False
def test_create_attribute_group_ci_type(session, client):
ci_type = init_ci_types(1)[0]
url = "/api/v0.1/ci_types/{}/attribute_groups".format(ci_type.id)
payload = {
"name": "A",
"order": 100,
}
resp = client.post(url, json=payload)
# check resp status code and content
assert resp.status_code == 200
assert resp.json["group_id"]
ins = CITypeAttributeGroup.query.filter_by(type_id=ci_type.id).first()
assert ins
assert ins.id == resp.json["group_id"]
assert ins.name == "A"
assert ins.order == 100
def test_update_attribute_group_ci_type(session, client):
attribute_groups = init_attribute_groups(1)[0]
url = "/api/v0.1/ci_types/attribute_groups/{}".format(attribute_groups.id)
payload = {
"attributes": [x.id for x in Attribute.query.all()]
}
resp = client.put(url, json=payload)
assert resp.status_code == 200
assert resp.json["group_id"]
ag_items = CITypeAttributeGroupItem.query.filter_by(group_id=attribute_groups.id).all()
for a in Attribute.query.all():
assert a.id in [x.attr_id for x in ag_items]
def test_delete_attribute_group_ci_type(session, client):
attribute_groups = init_attribute_groups(1)[0]
url = "/api/v0.1/ci_types/attribute_groups/{}".format(attribute_groups.id)
resp = client.delete(url)
assert resp.status_code == 200
attribute_group_ins = CITypeAttributeGroup.query.filter_by(id=attribute_groups.id).first()
assert attribute_group_ins.deleted is True
assert attribute_group_ins.deleted_at

View File

@ -1,4 +1,5 @@
<template> <template>
<div>
<a-card :bordered="false"> <a-card :bordered="false">
<a-spin :tip="loadTip" :spinning="loading"> <a-spin :tip="loadTip" :spinning="loading">
<search-form ref="search" @refresh="refreshTable" :preferenceAttrList="preferenceAttrList" /> <search-form ref="search" @refresh="refreshTable" :preferenceAttrList="preferenceAttrList" />
@ -10,7 +11,8 @@
type="primary" type="primary"
icon="plus" icon="plus"
@click="$refs.create.visible = true; $refs.create.action='create'" @click="$refs.create.visible = true; $refs.create.action='create'"
>{{ $t('button.add') }}</a-button> >新建</a-button>
<a-button class="right" @click="showDrawer(typeId)">显示字段</a-button>
<a-dropdown v-action:edit v-if="selectedRowKeys.length > 0"> <a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item <a-menu-item
@ -27,11 +29,11 @@
</json-excel> </json-excel>
</a-menu-item> </a-menu-item>
<a-menu-item key="batchDelete" @click="batchDelete"> <a-menu-item key="batchDelete" @click="batchDelete">
<a-icon type="delete" />{{ $t('tip.delete') }} <a-icon type="delete" />删除
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<a-button style="margin-left: 8px"> <a-button style="margin-left: 8px">
{{ $t('ci.batchOperate') }} 批量操作
<a-icon type="down" /> <a-icon type="down" />
</a-button> </a-button>
</a-dropdown> </a-dropdown>
@ -62,10 +64,10 @@
<template> <template>
<a <a
@click="$refs.detail.visible = true; $refs.detail.ciId = record.key; $refs.detail.create()" @click="$refs.detail.visible = true; $refs.detail.ciId = record.key; $refs.detail.create()"
>{{ $t('tip.detail') }}</a> >详情</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a @click="deleteCI(record)">{{ $t('tip.delete') }}</a> <a @click="deleteCI(record)">删除</a>
</template> </template>
</span> </span>
</s-table> </s-table>
@ -73,6 +75,52 @@
<create-instance-form @refresh="refreshTable" ref="create" @submit="batchUpdate" /> <create-instance-form @refresh="refreshTable" ref="create" @submit="batchUpdate" />
</a-spin> </a-spin>
</a-card> </a-card>
<template>
<div>
<a-drawer
title="显示字段定义"
:width="600"
@close="onClose"
:visible="visible"
:wrapStyle="{height: 'calc(100% - 108px)', overflow: 'auto', paddingBottom: '108px'}"
>
<template>
<a-transfer
:dataSource="attrList"
:showSearch="true"
:listStyle="{
width: '230px',
height: '500px',
}"
:titles="['未选属性','已选属性']"
:render="item=>item.title"
:targetKeys="selectedAttrList"
@change="handleChange"
@search="handleSearch"
>
<span slot="notFoundContent">没数据</span>
</a-transfer>
</template>
<div
:style="{
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'right',
}"
>
<a-button :style="{marginRight: '8px'}" @click="onClose">取消</a-button>
<a-button @click="subInstanceSubmit" type="primary">提交</a-button>
</div>
</a-drawer>
</div>
</template>
</div>
</template> </template>
<script> <script>
@ -85,8 +133,9 @@ import CreateInstanceForm from './modules/CreateInstanceForm'
import EditableCell from './modules/EditableCell' import EditableCell from './modules/EditableCell'
import CiDetail from './modules/CiDetail' import CiDetail from './modules/CiDetail'
import { searchCI, updateCI, deleteCI } from '@/api/cmdb/ci' import { searchCI, updateCI, deleteCI } from '@/api/cmdb/ci'
import { getSubscribeAttributes } from '@/api/cmdb/preference' import { getSubscribeAttributes, subscribeCIType } from '@/api/cmdb/preference'
import { notification } from 'ant-design-vue' import { notification } from 'ant-design-vue'
import { getCITypeAttributesByName } from '@/api/cmdb/CITypeAttr'
var valueTypeMap = { var valueTypeMap = {
'0': 'int', '0': 'int',
@ -123,6 +172,10 @@ export default {
preferenceAttrList: [], preferenceAttrList: [],
selectedAttrList: [],
attrList: [],
visible: false,
instanceList: [], instanceList: [],
// 表头 // 表头
columns: [], columns: [],
@ -202,6 +255,57 @@ export default {
}, },
inject: ['reload'], inject: ['reload'],
methods: { methods: {
showDrawer () {
this.getAttrList()
},
getAttrList () {
getCITypeAttributesByName(this.typeId).then(res => {
const attributes = res.attributes
getSubscribeAttributes(this.typeId).then(_res => {
const attrList = []
const selectedAttrList = []
const subAttributes = _res.attributes
this.instanceSubscribed = _res.is_subscribed
subAttributes.forEach(item => {
selectedAttrList.push(item.id.toString())
})
attributes.forEach(item => {
const data = {
key: item.id.toString(),
title: item.alias || item.name
}
attrList.push(data)
})
this.attrList = attrList
this.selectedAttrList = selectedAttrList
this.visible = true
})
})
},
onClose () {
this.visible = false
},
subInstanceSubmit () {
subscribeCIType(this.typeId, this.selectedAttrList)
.then(res => {
notification.success({
message: '修改成功'
})
this.reload()
})
.catch(e => {
notification.error({
message: e.response.data.message
})
})
},
handleChange (targetKeys, direction, moveKeys) {
this.selectedAttrList = targetKeys
},
handleSearch (dir, value) {},
setColumnWidth () { setColumnWidth () {
let rows = [] let rows = []
try { try {
@ -445,15 +549,18 @@ export default {
} }
</script> </script>
<style> <style lang='less' scoped>
.ant-table-thead > tr > th, /deep/ .ant-table-thead > tr > th,
.ant-table-tbody > tr > td { /deep/ .ant-table-tbody > tr > td {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }
.spin-content { /deep/ .spin-content {
border: 1px solid #91d5ff; border: 1px solid #91d5ff;
background-color: #e6f7ff; background-color: #e6f7ff;
padding: 30px; padding: 30px;
} }
.right {
float: right;
}
</style> </style>