mirror of https://github.com/veops/cmdb.git
UI: batch update relation
This commit is contained in:
parent
cf5e4966c0
commit
486fcac138
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 }
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -74,6 +74,7 @@ export default {
|
|||
batchOperate: '批量操作',
|
||||
confirmBatchUpdate: '确认要批量修改吗 ?',
|
||||
batchUpdate: '批量修改',
|
||||
batchUpdateRelation: '创建|修改 关系',
|
||||
batchUpdateSuccess: '批量修改成功',
|
||||
confirmDelete: '真的要删除吗 ?',
|
||||
attribute: '属性',
|
||||
|
|
|
@ -20,12 +20,20 @@
|
|||
@click="$refs.create.visible = true; $refs.create.action='update'"
|
||||
>
|
||||
<span @click="$refs.create.visible = true">
|
||||
<a-icon type="edit" /> {{ $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" /> {{ $t('button.download') }}
|
||||
<a-icon type="download" /> {{ $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({
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue