attributes paginate and fix update value

This commit is contained in:
pycook 2019-10-21 21:53:46 +08:00 committed by pycook
parent d54b404eb6
commit a0fcbd220e
7 changed files with 98 additions and 49 deletions

View File

@ -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)

View File

@ -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

View File

@ -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
@ -127,7 +129,7 @@ class Search(object):
if self.only_type_query: if self.only_type_query:
return ret_sql.format(query_sql, "ORDER BY B.ci_id {1} LIMIT {0:d}, {2};".format( return ret_sql.format(query_sql, "ORDER BY B.ci_id {1} LIMIT {0:d}, {2};".format(
(self.page - 1) * self.count, sort_type, self.count)) (self.page - 1) * self.count, sort_type, self.count))
elif self.type_id_list: elif self.type_id_list:
self.query_sql = "SELECT B.ci_id FROM ({0}) AS B {1}".format( self.query_sql = "SELECT B.ci_id FROM ({0}) AS B {1}".format(
@ -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, []

View File

@ -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:
value_table.create(ci_id=ci_id, attr_id=attr.id, value=v) if v is not None:
self._write_change(ci_id, attr.id, operate_type, None, 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)
elif existed_attr.value != v: elif existed_attr.value != v:
existed_attr.update(value=v) if v is not None:
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)

View File

@ -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):

View File

@ -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

View File

@ -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')