mirror of https://github.com/veops/cmdb.git
attributes paginate and fix update value
This commit is contained in:
parent
d54b404eb6
commit
a0fcbd220e
|
@ -38,17 +38,30 @@ class AttributeManager(object):
|
||||||
db.session.add(table)
|
db.session.add(table)
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
||||||
def get_attributes(self, name=None):
|
@classmethod
|
||||||
|
def search_attributes(cls, name=None, alias=None, page=1, page_size=None):
|
||||||
"""
|
"""
|
||||||
:param name:
|
:param name:
|
||||||
|
:param alias:
|
||||||
|
:param page:
|
||||||
|
:param page_size:
|
||||||
:return: attribute, if name is None, then return all attributes
|
:return: attribute, if name is None, then return all attributes
|
||||||
"""
|
"""
|
||||||
attrs = Attribute.get_by_like(name=name) if name is not None else Attribute.get_by()
|
if name is not None:
|
||||||
|
attrs = Attribute.get_by_like(name=name)
|
||||||
|
elif alias is not None:
|
||||||
|
attrs = Attribute.get_by_like(alias=alias)
|
||||||
|
else:
|
||||||
|
attrs = Attribute.get_by()
|
||||||
|
|
||||||
|
numfound = len(attrs)
|
||||||
|
attrs = attrs[(page - 1) * page_size:][:page_size]
|
||||||
res = list()
|
res = list()
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
attr["is_choice"] and attr.update(dict(choice_value=self.get_choice_values(attr["id"], attr["value_type"])))
|
attr["is_choice"] and attr.update(dict(choice_value=cls.get_choice_values(attr["id"], attr["value_type"])))
|
||||||
res.append(attr)
|
res.append(attr)
|
||||||
return res
|
|
||||||
|
return numfound, res
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -34,8 +34,6 @@ from api.models.cmdb import CITypeAttribute
|
||||||
from api.tasks.cmdb import ci_cache
|
from api.tasks.cmdb import ci_cache
|
||||||
from api.tasks.cmdb import ci_delete
|
from api.tasks.cmdb import ci_delete
|
||||||
|
|
||||||
__author__ = 'pycook'
|
|
||||||
|
|
||||||
|
|
||||||
class CIManager(object):
|
class CIManager(object):
|
||||||
""" manage CI interface
|
""" manage CI interface
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
@ -170,13 +172,13 @@ class Search(object):
|
||||||
elif self.type_id_list:
|
elif self.type_id_list:
|
||||||
self.query_sql = """SELECT C.ci_id
|
self.query_sql = """SELECT C.ci_id
|
||||||
FROM ({0}) AS C
|
FROM ({0}) AS C
|
||||||
INNER JOIN cis on c_cis.id=C.ci_id
|
INNER JOIN c_cis on c_cis.id=C.ci_id
|
||||||
WHERE cis.type_id in ({1})""".format(new_table, ",".join(self.type_id_list))
|
WHERE c_cis.type_id in ({1})""".format(new_table, ",".join(self.type_id_list))
|
||||||
|
|
||||||
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT C.ci_id, C.value
|
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT C.ci_id, C.value
|
||||||
FROM ({0}) AS C
|
FROM ({0}) AS C
|
||||||
INNER JOIN cis on c_cis.id=C.ci_id
|
INNER JOIN c_cis on c_cis.id=C.ci_id
|
||||||
WHERE cis.type_id in ({4})
|
WHERE c_cis.type_id in ({4})
|
||||||
ORDER BY C.value {2}
|
ORDER BY C.value {2}
|
||||||
LIMIT {1:d}, {3};""".format(new_table,
|
LIMIT {1:d}, {3};""".format(new_table,
|
||||||
(self.page - 1) * self.count,
|
(self.page - 1) * self.count,
|
||||||
|
@ -299,7 +301,7 @@ class Search(object):
|
||||||
self.query_sql = query_sql
|
self.query_sql = query_sql
|
||||||
current_app.logger.debug(query_sql)
|
current_app.logger.debug(query_sql)
|
||||||
numfound, res = self._execute_sql(query_sql)
|
numfound, res = self._execute_sql(query_sql)
|
||||||
current_app.logger.info("query ci ids is: {0}".format(time.time() - s))
|
current_app.logger.debug("query ci ids is: {0}".format(time.time() - s))
|
||||||
return numfound, [_res[0] for _res in res]
|
return numfound, [_res[0] for _res in res]
|
||||||
|
|
||||||
return 0, []
|
return 0, []
|
||||||
|
|
|
@ -14,6 +14,7 @@ from api.lib.cmdb.const import TableMap
|
||||||
from api.lib.cmdb.const import ExistPolicy
|
from api.lib.cmdb.const import ExistPolicy
|
||||||
from api.lib.cmdb.const import OperateType
|
from api.lib.cmdb.const import OperateType
|
||||||
from api.lib.cmdb.history import AttributeHistoryManger
|
from api.lib.cmdb.history import AttributeHistoryManger
|
||||||
|
from api.models.cmdb import Attribute
|
||||||
|
|
||||||
|
|
||||||
class AttributeValueManager(object):
|
class AttributeValueManager(object):
|
||||||
|
@ -47,6 +48,7 @@ class AttributeValueManager(object):
|
||||||
attr = self._get_attr(field)
|
attr = self._get_attr(field)
|
||||||
if not attr:
|
if not attr:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
value_table = TableMap(attr_name=attr.name).table
|
value_table = TableMap(attr_name=attr.name).table
|
||||||
rs = value_table.get_by(ci_id=ci_id,
|
rs = value_table.get_by(ci_id=ci_id,
|
||||||
attr_id=attr.id,
|
attr_id=attr.id,
|
||||||
|
@ -130,9 +132,16 @@ class AttributeValueManager(object):
|
||||||
|
|
||||||
for v in value_list:
|
for v in value_list:
|
||||||
v = self._validate(attr, v, value_table, ci_id)
|
v = self._validate(attr, v, value_table, ci_id)
|
||||||
|
if not v and attr.value_type != Attribute.TEXT:
|
||||||
|
v = None
|
||||||
|
|
||||||
if operate_type == OperateType.ADD:
|
if operate_type == OperateType.ADD:
|
||||||
|
if v is not None:
|
||||||
value_table.create(ci_id=ci_id, attr_id=attr.id, value=v)
|
value_table.create(ci_id=ci_id, attr_id=attr.id, value=v)
|
||||||
self._write_change(ci_id, attr.id, operate_type, None, v)
|
self._write_change(ci_id, attr.id, operate_type, None, v)
|
||||||
elif existed_attr.value != v:
|
elif existed_attr.value != v:
|
||||||
|
if v is not None:
|
||||||
existed_attr.update(value=v)
|
existed_attr.update(value=v)
|
||||||
|
else:
|
||||||
|
existed_attr.delete()
|
||||||
self._write_change(ci_id, attr.id, operate_type, existed_value, v)
|
self._write_change(ci_id, attr.id, operate_type, existed_value, v)
|
||||||
|
|
|
@ -11,16 +11,26 @@ from api.lib.cmdb.const import RoleEnum
|
||||||
from api.lib.cmdb.attribute import AttributeManager
|
from api.lib.cmdb.attribute import AttributeManager
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.utils import handle_arg_list
|
from api.lib.utils import handle_arg_list
|
||||||
|
from api.lib.utils import get_page
|
||||||
|
from api.lib.utils import get_page_size
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AttributeSearchView(APIView):
|
class AttributeSearchView(APIView):
|
||||||
url_prefix = ("/attributes/s", "/attributes/search")
|
url_prefix = ("/attributes/s", "/attributes/search")
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
q = request.values.get("q")
|
name = request.values.get("name")
|
||||||
attrs = AttributeManager().get_attributes(name=q)
|
alias = request.values.get("alias")
|
||||||
count = len(attrs)
|
page = get_page(request.values.get("page", 1))
|
||||||
return self.jsonify(numfound=count, attributes=attrs)
|
page_size = get_page_size(request.values.get("page_size"))
|
||||||
|
numfound, res = AttributeManager.search_attributes(name=name, alias=alias, page=page, page_size=page_size)
|
||||||
|
|
||||||
|
return self.jsonify(page=page,
|
||||||
|
page_size=page_size,
|
||||||
|
numfound=numfound,
|
||||||
|
total=len(res),
|
||||||
|
attributes=res)
|
||||||
|
|
||||||
|
|
||||||
class AttributeView(APIView):
|
class AttributeView(APIView):
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
:rowKey="record=>record.id"
|
:rowKey="record=>record.id"
|
||||||
:rowSelection="options.rowSelection"
|
:rowSelection="options.rowSelection"
|
||||||
:scroll="scroll"
|
:scroll="scroll"
|
||||||
:showPagination="showPagination"
|
:pagination="{ showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条记录`, pageSizeOptions: pageSizeOptions}"
|
||||||
|
showPagination="auto"
|
||||||
|
:pageSize="25"
|
||||||
ref="table"
|
ref="table"
|
||||||
size="middle"
|
size="middle"
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@
|
||||||
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class="custom-filter-dropdown">
|
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class="custom-filter-dropdown">
|
||||||
<a-input
|
<a-input
|
||||||
v-ant-ref="c => searchInput = c"
|
v-ant-ref="c => searchInput = c"
|
||||||
:placeholder="`Search ${column.dataIndex}`"
|
:placeholder="` ${column.title}`"
|
||||||
:value="selectedKeys[0]"
|
:value="selectedKeys[0]"
|
||||||
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
|
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
|
||||||
@pressEnter="() => handleSearch(selectedKeys, confirm, column)"
|
@pressEnter="() => handleSearch(selectedKeys, confirm, column)"
|
||||||
|
@ -32,12 +34,12 @@
|
||||||
icon="search"
|
icon="search"
|
||||||
size="small"
|
size="small"
|
||||||
style="width: 90px; margin-right: 8px"
|
style="width: 90px; margin-right: 8px"
|
||||||
>Search</a-button>
|
>搜索</a-button>
|
||||||
<a-button
|
<a-button
|
||||||
@click="() => handleReset(clearFilters, column)"
|
@click="() => handleReset(clearFilters, column)"
|
||||||
size="small"
|
size="small"
|
||||||
style="width: 90px"
|
style="width: 90px"
|
||||||
>Reset</a-button>
|
>重置</a-button>
|
||||||
</div>
|
</div>
|
||||||
<a-icon slot="filterIcon" slot-scope="filtered" type="search" :style="{ color: filtered ? '#108ee9' : undefined }" />
|
<a-icon slot="filterIcon" slot-scope="filtered" type="search" :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||||
|
|
||||||
|
@ -69,7 +71,16 @@
|
||||||
<template>
|
<template>
|
||||||
<a @click="handleEdit(record)">编辑</a>
|
<a @click="handleEdit(record)">编辑</a>
|
||||||
<a-divider type="vertical"/>
|
<a-divider type="vertical"/>
|
||||||
<a @click="handleDelete(record)">删除</a>
|
|
||||||
|
<a-popconfirm
|
||||||
|
title="确认删除?"
|
||||||
|
@confirm="handleDelete(record)"
|
||||||
|
@cancel="cancel"
|
||||||
|
okText="是"
|
||||||
|
cancelText="否"
|
||||||
|
>
|
||||||
|
<a>删除</a>
|
||||||
|
</a-popconfirm>
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
@ -93,7 +104,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
scroll: { x: 1400, y: 500 },
|
scroll: { x: 1000, y: 500 },
|
||||||
btnName: '新增属性',
|
btnName: '新增属性',
|
||||||
|
|
||||||
CITypeName: this.$route.params.CITypeName,
|
CITypeName: this.$route.params.CITypeName,
|
||||||
|
@ -107,11 +118,8 @@ export default {
|
||||||
transferTargetKeys: [],
|
transferTargetKeys: [],
|
||||||
transferSelectedKeys: [],
|
transferSelectedKeys: [],
|
||||||
originTargetKeys: [],
|
originTargetKeys: [],
|
||||||
|
pageSizeOptions: ['10', '25', '50', '100'],
|
||||||
|
|
||||||
pagination: {
|
|
||||||
defaultPageSize: 20
|
|
||||||
},
|
|
||||||
showPagination: false,
|
|
||||||
columnSearchText: {
|
columnSearchText: {
|
||||||
alias: '',
|
alias: '',
|
||||||
name: ''
|
name: ''
|
||||||
|
@ -121,7 +129,7 @@ export default {
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'alias',
|
dataIndex: 'alias',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 200,
|
width: 250,
|
||||||
scopedSlots: {
|
scopedSlots: {
|
||||||
customRender: 'aliasSearchRender',
|
customRender: 'aliasSearchRender',
|
||||||
filterDropdown: 'filterDropdown',
|
filterDropdown: 'filterDropdown',
|
||||||
|
@ -140,7 +148,7 @@ export default {
|
||||||
title: '英文名',
|
title: '英文名',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 200,
|
width: 250,
|
||||||
scopedSlots: {
|
scopedSlots: {
|
||||||
customRender: 'nameSearchRender',
|
customRender: 'nameSearchRender',
|
||||||
filterDropdown: 'filterDropdown',
|
filterDropdown: 'filterDropdown',
|
||||||
|
@ -159,7 +167,7 @@ export default {
|
||||||
title: '类型',
|
title: '类型',
|
||||||
dataIndex: 'value_type',
|
dataIndex: 'value_type',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 100,
|
width: 80,
|
||||||
scopedSlots: { customRender: 'value_type' },
|
scopedSlots: { customRender: 'value_type' },
|
||||||
customRender: (text) => valueTypeMap[text]
|
customRender: (text) => valueTypeMap[text]
|
||||||
|
|
||||||
|
@ -167,7 +175,7 @@ export default {
|
||||||
{
|
{
|
||||||
title: '唯一',
|
title: '唯一',
|
||||||
dataIndex: 'is_unique',
|
dataIndex: 'is_unique',
|
||||||
width: 80,
|
width: 50,
|
||||||
sorter: false,
|
sorter: false,
|
||||||
scopedSlots: { customRender: 'is_check' }
|
scopedSlots: { customRender: 'is_check' }
|
||||||
|
|
||||||
|
@ -176,7 +184,7 @@ export default {
|
||||||
title: '索引',
|
title: '索引',
|
||||||
dataIndex: 'is_index',
|
dataIndex: 'is_index',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 80,
|
width: 50,
|
||||||
scopedSlots: { customRender: 'is_check' }
|
scopedSlots: { customRender: 'is_check' }
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -184,7 +192,7 @@ export default {
|
||||||
title: '排序',
|
title: '排序',
|
||||||
dataIndex: 'is_sortable',
|
dataIndex: 'is_sortable',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 80,
|
width: 50,
|
||||||
scopedSlots: { customRender: 'is_check' }
|
scopedSlots: { customRender: 'is_check' }
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -192,7 +200,7 @@ export default {
|
||||||
title: '链接',
|
title: '链接',
|
||||||
dataIndex: 'is_link',
|
dataIndex: 'is_link',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 80,
|
width: 50,
|
||||||
scopedSlots: { customRender: 'is_check' }
|
scopedSlots: { customRender: 'is_check' }
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -200,7 +208,7 @@ export default {
|
||||||
title: '密码',
|
title: '密码',
|
||||||
dataIndex: 'is_password',
|
dataIndex: 'is_password',
|
||||||
sorter: false,
|
sorter: false,
|
||||||
width: 100,
|
width: 50,
|
||||||
scopedSlots: { customRender: 'is_check' }
|
scopedSlots: { customRender: 'is_check' }
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -221,15 +229,22 @@ export default {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
loadData: parameter => {
|
loadData: parameter => {
|
||||||
|
parameter['page_size'] = parameter['pageSize']
|
||||||
|
parameter['page'] = parameter['pageNo']
|
||||||
|
Object.assign(parameter, this.queryParam)
|
||||||
console.log('loadData.parameter', parameter)
|
console.log('loadData.parameter', parameter)
|
||||||
|
|
||||||
return searchAttributes()
|
return searchAttributes(parameter)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.allAttributes = res.attributes
|
res.pageNo = res.page
|
||||||
return {
|
res.pageSize = res.total
|
||||||
data: res.attributes
|
res.totalCount = res.numfound
|
||||||
|
res.totalPage = Math.ceil(res.numfound / parameter.pageSize)
|
||||||
|
res.data = res.attributes
|
||||||
|
|
||||||
}
|
console.log('loadData.res', res)
|
||||||
|
this.allAttributes = res.attributes
|
||||||
|
return res
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -285,17 +300,20 @@ export default {
|
||||||
this.getAttributes()
|
this.getAttributes()
|
||||||
this.setScrollY()
|
this.setScrollY()
|
||||||
},
|
},
|
||||||
|
inject: ['reload'],
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleSearch (selectedKeys, confirm, column) {
|
handleSearch (selectedKeys, confirm, column) {
|
||||||
confirm()
|
confirm()
|
||||||
this.columnSearchText[column.dataIndex] = selectedKeys[0]
|
this.columnSearchText[column.dataIndex] = selectedKeys[0]
|
||||||
|
this.queryParam[column.dataIndex] = selectedKeys[0]
|
||||||
},
|
},
|
||||||
|
|
||||||
handleReset (clearFilters, column) {
|
handleReset (clearFilters, column) {
|
||||||
clearFilters()
|
clearFilters()
|
||||||
this.columnSearchText[column.dataIndex] = ''
|
this.columnSearchText[column.dataIndex] = ''
|
||||||
|
this.queryParam[column.dataIndex] = ''
|
||||||
},
|
},
|
||||||
|
|
||||||
getAttributes () {
|
getAttributes () {
|
||||||
searchAttributes().then(res => {
|
searchAttributes().then(res => {
|
||||||
this.allAttributes = res.attributes
|
this.allAttributes = res.attributes
|
||||||
|
|
|
@ -255,7 +255,6 @@ export default {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.form.validateFields((err, values) => {
|
this.form.validateFields((err, values) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('Received values of form: ', values)
|
console.log('Received values of form: ', values)
|
||||||
if (values.choice_value) {
|
if (values.choice_value) {
|
||||||
values.choice_value = values.choice_value.split('\n')
|
values.choice_value = values.choice_value.split('\n')
|
||||||
|
|
Loading…
Reference in New Issue