UI: batch update relation

This commit is contained in:
pycook 2020-04-01 11:09:41 +08:00
parent cf5e4966c0
commit 486fcac138
12 changed files with 204 additions and 15 deletions

View File

@ -485,7 +485,7 @@ class CIRelationManager(object):
return numfound, len(first_ci_ids), result
@classmethod
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None):
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None, many_to_one=False):
first_ci = CIManager.confirm_ci_existed(first_ci_id)
second_ci = CIManager.confirm_ci_existed(second_ci_id)
@ -495,7 +495,7 @@ class CIRelationManager(object):
to_dict=False,
first=True)
if existed is not None:
if existed.relation_type_id != relation_type_id:
if existed.relation_type_id != relation_type_id and relation_type_id is not None:
existed.update(relation_type_id=relation_type_id)
CIRelationHistoryManager().add(existed, OperateType.UPDATE)
@ -509,6 +509,16 @@ class CIRelationManager(object):
relation_type_id or abort(404, "Relation {0} <-> {1} is not found".format(
first_ci.ci_type.name, second_ci.ci_type.name))
if many_to_one:
for item in CIRelation.get_by(second_ci_id=second_ci_id,
relation_type_id=relation_type_id,
to_dict=False):
item.soft_delete()
his_manager = CIRelationHistoryManager()
his_manager.add(item, operate_type=OperateType.DELETE)
ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
existed = CIRelation.create(first_ci_id=first_ci_id,
second_ci_id=second_ci_id,
relation_type_id=relation_type_id)
@ -544,3 +554,25 @@ class CIRelationManager(object):
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
return cls.delete(cr.id)
@classmethod
def batch_update(cls, ci_ids, parents):
"""
only for many to one
:param ci_ids:
:param parents:
:return:
"""
from api.lib.cmdb.utils import TableMap
if parents is not None and isinstance(parents, dict):
for attr_name in parents:
if parents[attr_name]:
attr = AttributeCache.get(attr_name)
value_table = TableMap(attr_name=attr.name).table
parent = value_table.get_by(attr_id=attr.id, value=parents[attr_name], first=True, to_dict=False)
if not parent:
return abort(404, "{0}: {1} is not found".format(attr_name, parents[attr_name]))
parent_id = parent.ci_id
for ci_id in ci_ids:
cls.add(parent_id, ci_id, many_to_one=True)

View File

@ -11,7 +11,7 @@ from api.extensions import celery
from api.extensions import db
from api.extensions import es
from api.extensions import rd
from api.lib.cmdb.cache import CITypeAttributeCache
from api.lib.cmdb.cache import CITypeAttributesCache
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_RELATION
@ -81,7 +81,7 @@ def ci_type_attribute_order_rebuild(type_id):
from api.lib.cmdb.ci_type import CITypeAttributeGroupManager
attrs = CITypeAttributeCache.get(type_id)
attrs = CITypeAttributesCache.get(type_id)
id2attr = {attr.attr_id: attr for attr in attrs}
res = CITypeAttributeGroupManager.get_by_type_id(type_id, True)

View File

@ -11,6 +11,7 @@ from api.lib.cmdb.cache import RelationTypeCache
from api.lib.cmdb.ci import CIRelationManager
from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci_relation.search import Search
from api.lib.decorator import args_required
from api.lib.perm.auth import auth_abandoned
from api.lib.utils import get_page
from api.lib.utils import get_page_size
@ -122,11 +123,13 @@ class CIRelationView(APIView):
def post(self, first_ci_id, second_ci_id):
manager = CIRelationManager()
res = manager.add(first_ci_id, second_ci_id)
return self.jsonify(cr_id=res)
def delete(self, first_ci_id, second_ci_id):
manager = CIRelationManager()
manager.delete_2(first_ci_id, second_ci_id)
return self.jsonify(message="CIType Relation is deleted")
@ -136,4 +139,24 @@ class DeleteCIRelationView(APIView):
def delete(self, cr_id):
manager = CIRelationManager()
manager.delete(cr_id)
return self.jsonify(message="CIType Relation is deleted")
class BatchCreateOrUpdateCIRelationView(APIView):
url_prefix = "/ci_relations/batch"
@args_required('ci_ids')
@args_required('parents')
def post(self):
ci_ids = request.values.get('ci_ids')
parents = request.values.get('parents')
CIRelationManager.batch_update(ci_ids, parents)
return self.jsonify(code=200)
@args_required('ci_ids')
@args_required('parents')
def put(self):
return self.post()

View File

@ -15,7 +15,7 @@
"@antv/data-set": "^0.10.2",
"@handsontable-pro/vue": "^3.1.1",
"@handsontable/vue": "^4.1.1",
"ant-design-vue": "1.5.0-beta.1",
"ant-design-vue": "1.5.0",
"axios": "^0.19.0",
"core-js": "^3.1.2",
"enquire.js": "^2.1.6",

View File

@ -1,9 +1,9 @@
<template>
<a-locale-provider :locale="locale">
<a-config-provider :locale="locale">
<div id="app">
<router-view v-if="alive" />
</div>
</a-locale-provider>
</a-config-provider>
</template>
<script>

View File

@ -28,3 +28,11 @@ export function statisticsCIRelation (params) {
params: params
})
}
export function batchUpdateCIRelation (ciIds, parents) {
return axios({
url: '/v0.1/ci_relations/batch',
method: 'POST',
data: { ci_ids: ciIds, parents: parents }
})
}

View File

@ -8,7 +8,7 @@
*/
import Vue from 'vue'
import {
LocaleProvider,
ConfigProvider,
Layout,
Input,
InputNumber,
@ -50,7 +50,7 @@ import {
} from 'ant-design-vue'
// import VueCropper from 'vue-cropper'
Vue.use(LocaleProvider)
Vue.use(ConfigProvider)
Vue.use(Layout)
Vue.use(Input)
Vue.use(InputNumber)

View File

@ -74,6 +74,7 @@ export default {
batchOperate: 'Batch operation',
confirmBatchUpdate: 'Confirm batch modification?',
batchUpdate: 'Batch update',
batchUpdateRelation: 'Create or update relation',
batchUpdateSuccess: 'Batch update successfully',
confirmDelete: 'Confirm deleting ?',
attribute: 'Attributes',

View File

@ -74,6 +74,7 @@ export default {
batchOperate: '批量操作',
confirmBatchUpdate: '确认要批量修改吗 ?',
batchUpdate: '批量修改',
batchUpdateRelation: '创建|修改 关系',
batchUpdateSuccess: '批量修改成功',
confirmDelete: '真的要删除吗 ?',
attribute: '属性',

View File

@ -20,12 +20,20 @@
@click="$refs.create.visible = true; $refs.create.action='update'"
>
<span @click="$refs.create.visible = true">
<a-icon type="edit" />&nbsp;{{ $t('button.update') }}
<a-icon type="edit" />{{ $t('button.update') }}
</span>
</a-menu-item>
<a-menu-item
key="batchUpdateRelation"
@click="$refs.batchUpdateRelation.visible = true"
>
<span @click="$refs.batchUpdateRelation.visible = true">
<a-icon type="link" />{{ $t('ci.batchUpdateRelation') }}
</span>
</a-menu-item>
<a-menu-item key="batchDownload" @click="batchDownload">
<json-excel :fetch="batchDownload" name="cmdb.xls">
<a-icon type="download" />&nbsp;{{ $t('button.download') }}
<a-icon type="download" />&nbsp;&nbsp;&nbsp;&nbsp;{{ $t('button.download') }}
</json-excel>
</a-menu-item>
<a-menu-item key="batchDelete" @click="batchDelete">
@ -73,6 +81,7 @@
</s-table>
<create-instance-form @refresh="refreshTable" ref="create" @submit="batchUpdate" />
<batch-update-relation :typeId="typeId" ref="batchUpdateRelation" @submit="batchUpdateRelation" />
</a-spin>
</a-card>
@ -130,9 +139,11 @@ import JsonExcel from 'vue-json-excel'
import SearchForm from './modules/SearchForm'
import CreateInstanceForm from './modules/CreateInstanceForm'
import BatchUpdateRelation from './modules/BatchUpdateRelation'
import EditableCell from './modules/EditableCell'
import CiDetail from './modules/CiDetail'
import { searchCI, updateCI, deleteCI } from '@/api/cmdb/ci'
import { batchUpdateCIRelation } from '@/api/cmdb/CIRelation'
import { getSubscribeAttributes, subscribeCIType } from '@/api/cmdb/preference'
import { notification } from 'ant-design-vue'
import { getCITypeAttributesByName } from '@/api/cmdb/CITypeAttr'
@ -155,6 +166,7 @@ export default {
JsonExcel,
SearchForm,
CreateInstanceForm,
BatchUpdateRelation,
CiDetail
},
data () {
@ -486,6 +498,38 @@ export default {
}
})
},
batchUpdateRelation (values) {
const that = this
this.$confirm({
title: that.$t('tip.warning'),
content: that.$t('ci.confirmBatchUpdate'),
onOk () {
that.loading = true
that.loadTip = that.$t('ci.batchUpdate')
const payload = {}
Object.keys(values).forEach(key => {
if (values[key] || values[key] === 0) {
payload[key] = values[key]
}
})
batchUpdateCIRelation(that.selectedRowKeys, payload).then(() => {
that.loading = false
notification.success({
message: that.$t('ci.batchUpdateSuccess')
})
that.$refs.create.visible = false
that.$refs.table.clearSelected()
}).catch(e => {
console.log(e)
that.loading = false
notification.error({
message: e.response.data.message
})
})
}
})
},
batchDelete () {
const that = this
this.$confirm({

View File

@ -0,0 +1,80 @@
<template>
<a-drawer
:title="$t('ci.batchUpdateRelation')"
width="50%"
@close="() => { visible = false; $emit('refresh', true) }"
:visible="visible"
:wrapStyle="{ overflow: 'auto' }"
>
<a-form :form="form" :layout="formLayout" @submit="commitUpdateRelation">
<a-button type="primary" @click="commitUpdateRelation">Submit</a-button>
<a-form-item
v-bind="formItemLayout"
:label="item.alias || item.name"
v-for="item in parentCITypes"
:key="item.id"
>
<template v-for="_item in item.attributes">
<a-input
v-decorator="[_item.name, {validateTrigger: ['submit'], rules: []}]"
style="width: 100%"
v-if="_item.id == item.unique_id"
:key="_item.id"
:placeholder="_item.alias || _item.name"
/>
</template>
</a-form-item>
</a-form>
</a-drawer>
</template>
<script>
import { getCITypeParent } from '@/api/cmdb/CITypeRelation'
export default {
props: {
typeId: {
type: Number,
required: true
}
},
data () {
return {
action: '',
form: this.$form.createForm(this),
parentCITypes: [],
visible: false,
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 8 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
}
},
formLayout: 'horizontal'
}
},
created () {
this.getParentCITypes()
},
methods: {
getParentCITypes () {
getCITypeParent(this.typeId).then(res => {
this.parentCITypes = res.parents
})
},
commitUpdateRelation (e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
this.$emit('submit', values)
}
})
}
}
}
</script>

View File

@ -2017,10 +2017,10 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
ant-design-vue@1.5.0-beta.1:
version "1.5.0-beta.1"
resolved "https://registry.yarnpkg.com/ant-design-vue/-/ant-design-vue-1.5.0-beta.1.tgz#9396e1bb4435c9bc5dc224e2bd888a000144a8f9"
integrity sha512-Fv5vxO+qHakbjsswgZ70/bjiZK4FphdFxv31VjBJKfhA5Ud7K6sNqC0BVpYS8nL0BwxGCVG8I30efNdU3VifnA==
ant-design-vue@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/ant-design-vue/-/ant-design-vue-1.5.0.tgz#2e2c5658cf1211be06fbee95a18eee02965e089f"
integrity sha512-12+mTowYNZZhsXFR848BZRWGtZrWGayJx9j8Dv3gpgPBU4Abi86tz0hUSbnp8RXL6wb7xNcE9JoawNeE9Y83+Q==
dependencies:
"@ant-design/icons" "^2.1.1"
"@ant-design/icons-vue" "^2.0.0"