feat(cmdb-ui):ci type import&export,pref(cmdb-ui):download ci xlsx name, pref(cmdb-ui):ci detail history merge row method (#331)

* pref(cmdb-ui):download ci xlsx name

* pref(cmdb-ui):ci detail history merge row method

* feat(cmdb-ui):ci type import&export
This commit is contained in:
wang-liang0615 2023-12-22 15:42:20 +08:00 committed by GitHub
parent ffae57642c
commit 855cb91b31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 102 additions and 39 deletions

View File

@ -111,12 +111,14 @@ export default {
}, },
methods: { methods: {
...mapMutations('cmdbStore', ['SET_IS_TABLE_LOADING']), ...mapMutations('cmdbStore', ['SET_IS_TABLE_LOADING']),
open({ preferenceAttrList }) { open({ preferenceAttrList, ciTypeName = undefined }) {
this.preferenceAttrList = preferenceAttrList this.preferenceAttrList = preferenceAttrList
this.visible = true this.visible = true
this.$nextTick((res) => { this.$nextTick((res) => {
this.form.setFieldsValue({ this.form.setFieldsValue({
filename: `cmdb-${moment().format('YYYYMMDDHHmmss')}`, filename: ciTypeName
? `cmdb-${ciTypeName}-${moment().format('YYYYMMDDHHmmss')}`
: `cmdb-${moment().format('YYYYMMDDHHmmss')}`,
}) })
if (this.treeType === 'tree') { if (this.treeType === 'tree') {
const _check = ['ci_type_alias'] const _check = ['ci_type_alias']

View File

@ -106,7 +106,6 @@ export default {
this.uploadData = _uploadData.slice(1) this.uploadData = _uploadData.slice(1)
this.hasError = false this.hasError = false
this.isUploading = false this.isUploading = false
this.$refs.uploadResult.visible = false
}, },
handleUpload() { handleUpload() {
if (!this.ciType) { if (!this.ciType) {

View File

@ -576,7 +576,10 @@ export default {
}, },
async openBatchDownload() { async openBatchDownload() {
this.$refs.batchDownload.open({ preferenceAttrList: this.preferenceAttrList }) this.$refs.batchDownload.open({
preferenceAttrList: this.preferenceAttrList,
ciTypeName: this.$route.meta.title || this.$route.meta.name,
})
}, },
batchDownload({ filename, type, checkedKeys }) { batchDownload({ filename, type, checkedKeys }) {
const jsonAttrList = [] const jsonAttrList = []

View File

@ -276,15 +276,16 @@ export default {
}, },
mergeRowMethod({ row, _rowIndex, column, visibleData }) { mergeRowMethod({ row, _rowIndex, column, visibleData }) {
const fields = ['created_at', 'username'] const fields = ['created_at', 'username']
const cellValue = row[column.property] const cellValue1 = row['created_at']
if (cellValue && fields.includes(column.property)) { const cellValue2 = row['username']
if (cellValue1 && cellValue2 && fields.includes(column.property)) {
const prevRow = visibleData[_rowIndex - 1] const prevRow = visibleData[_rowIndex - 1]
let nextRow = visibleData[_rowIndex + 1] let nextRow = visibleData[_rowIndex + 1]
if (prevRow && prevRow[column.property] === cellValue) { if (prevRow && prevRow['created_at'] === cellValue1 && prevRow['username'] === cellValue2) {
return { rowspan: 0, colspan: 0 } return { rowspan: 0, colspan: 0 }
} else { } else {
let countRowspan = 1 let countRowspan = 1
while (nextRow && nextRow[column.property] === cellValue) { while (nextRow && nextRow['created_at'] === cellValue1 && nextRow['username'] === cellValue2) {
nextRow = visibleData[++countRowspan + _rowIndex] nextRow = visibleData[++countRowspan + _rowIndex]
} }
if (countRowspan > 1) { if (countRowspan > 1) {

View File

@ -136,8 +136,8 @@ export default {
async getCITypeDiscovery(currentTab) { async getCITypeDiscovery(currentTab) {
await getCITypeDiscovery(this.CITypeId).then((res) => { await getCITypeDiscovery(this.CITypeId).then((res) => {
this.adCITypeList = res.filter((item) => item.adr_id) this.adCITypeList = res.filter((item) => item.adr_id)
if (res && res.length && !this.currentTab) { if (this.adCITypeList && this.adCITypeList.length && !this.currentTab) {
this.currentTab = res[0].id this.currentTab = this.adCITypeList[0].id
} }
if (currentTab) { if (currentTab) {
this.currentTab = currentTab this.currentTab = currentTab

View File

@ -40,20 +40,18 @@
<a-menu-item key="0"> <a-menu-item key="0">
<a-upload <a-upload
name="file" name="file"
accept="json" accept=".json"
:showUploadList="false" :showUploadList="false"
style="display: inline-block" style="display: inline-block"
action="/api/v0.1/ci_types/template/import/file " action="/api/v0.1/ci_types/template/import/file"
@change="changeUploadFile"
> >
<a-space <a><a-icon type="upload"/></a><a> 导入</a>
><a><a-icon type="upload"/></a><a>导入</a></a-space
>
</a-upload> </a-upload>
</a-menu-item> </a-menu-item>
<a-menu-item key="1"> <a-menu-item key="1">
<a-space> <a-space>
<a><a-icon type="download"/></a> <a href="/api/v0.1/ci_types/template/export/file"><a-icon type="download" /> 导出</a>
<a href="/api/v0.1/ci_types/template/export/file">导出</a>
</a-space> </a-space>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@ -134,6 +132,12 @@
<a-space class="ci-types-left-detail-action"> <a-space class="ci-types-left-detail-action">
<a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a> <a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a>
<a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a> <a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a>
<a
v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"
@click="(e) => handleDownloadCiType(e, ci)"
><a-icon
type="download"
/></a>
<a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a> <a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a>
</a-space> </a-space>
</div> </div>
@ -309,6 +313,7 @@ export default {
OpsMoveIcon, OpsMoveIcon,
AttributeStore, AttributeStore,
}, },
inject: ['reload'],
data() { data() {
return { return {
emptyImage, emptyImage,
@ -716,6 +721,21 @@ export default {
}, },
}) })
}, },
handleDownloadCiType(e, ci) {
e.preventDefault()
e.stopPropagation()
const x = new XMLHttpRequest()
x.open('GET', `/api/v0.1/ci_types/${ci.id}/template/export`, true)
x.responseType = 'blob'
x.onload = function(e) {
const url = window.URL.createObjectURL(x.response)
const a = document.createElement('a')
a.href = url
a.download = `${ci.alias || ci.name}.json`
a.click()
}
x.send()
},
resetRoute() { resetRoute() {
resetRouter() resetRouter()
const roles = store.getters.roles const roles = store.getters.roles
@ -780,6 +800,19 @@ export default {
} }
}) })
}, },
changeUploadFile({ file, fileList, event }) {
const key = 'upload'
if (file.status === 'uploading') {
this.$message.loading({ content: '正在导入中', key, duration: 0 })
}
if (file.status === 'done') {
this.$message.success({ content: '导入成功', key, duration: 2 })
this.reload()
}
if (file.status === 'error') {
this.$message.error({ content: '导入失败,请稍后重试', key, duration: 2 })
}
},
}, },
} }
</script> </script>

View File

@ -58,10 +58,10 @@
/> />
<div class="relation-views-right-bar"> <div class="relation-views-right-bar">
<a-space> <a-space>
<a-button <a-button
v-if="isLeaf" v-if="isLeaf"
type="primary" type="primary"
size="small" size="small"
@click="$refs.create.handleOpen(true, 'create')" @click="$refs.create.handleOpen(true, 'create')"
>新建</a-button >新建</a-button
> >
@ -322,6 +322,7 @@
v-else-if="relationViews.name2id && !relationViews.name2id.length" v-else-if="relationViews.name2id && !relationViews.name2id.length"
></a-alert> ></a-alert>
<AddTableModal ref="addTableModal" @reload="reload" /> <AddTableModal ref="addTableModal" @reload="reload" />
<!-- <GrantDrawer ref="grantDrawer" resourceTypeName="RelationView" app_id="cmdb" /> -->
<CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" /> <CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" />
<ci-detail ref="detail" :typeId="Number(currentTypeId[0])" /> <ci-detail ref="detail" :typeId="Number(currentTypeId[0])" />
@ -378,6 +379,7 @@ export default {
SearchForm, SearchForm,
AddTableModal, AddTableModal,
ContextMenu, ContextMenu,
// GrantDrawer,
CMDBGrant, CMDBGrant,
SplitPane, SplitPane,
ElTree: Tree, ElTree: Tree,
@ -651,6 +653,9 @@ export default {
} else { } else {
q = `q=_type:${this.currentTypeId[0]},` + q q = `q=_type:${this.currentTypeId[0]},` + q
} }
if (Object.values(this.level2constraint).includes('2')) {
q = q + `&&has_m2m=1`
}
if (this.currentTypeId[0]) { if (this.currentTypeId[0]) {
const res = await searchCIRelation(q) const res = await searchCIRelation(q)
@ -689,6 +694,7 @@ export default {
root_ids: key.split('%')[0], root_ids: key.split('%')[0],
level: this.treeKeys.length - index, level: this.treeKeys.length - index,
type_ids: this.showTypes.map((type) => type.id).join(','), type_ids: this.showTypes.map((type) => type.id).join(','),
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
}).then((res) => { }).then((res) => {
let result let result
const getTreeItem = (data, id) => { const getTreeItem = (data, id) => {
@ -742,7 +748,10 @@ export default {
_showTypes = JSON.parse(JSON.stringify(this.origShowTypes)) _showTypes = JSON.parse(JSON.stringify(this.origShowTypes))
} }
const promises = _showTypeIds.map((typeId) => { const promises = _showTypeIds.map((typeId) => {
const _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1') let _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1')
if (Object.values(this.level2constraint).includes('2')) {
_q = _q + `&&has_m2m=1`
}
console.log(_q) console.log(_q)
if (this.treeKeys.length === 0) { if (this.treeKeys.length === 0) {
return searchCI2(_q).then((res) => { return searchCI2(_q).then((res) => {
@ -798,6 +807,7 @@ export default {
root_ids: ciIds.join(','), root_ids: ciIds.join(','),
level: level, level: level,
type_ids: this.showTypes.map((type) => type.id).join(','), type_ids: this.showTypes.map((type) => type.id).join(','),
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
}).then((num) => { }).then((num) => {
facet.forEach((item, idx) => { facet.forEach((item, idx) => {
item[1] += num[ciIds[idx] + ''] item[1] += num[ciIds[idx] + '']
@ -827,6 +837,9 @@ export default {
.map((item) => item.split('%')[0]) .map((item) => item.split('%')[0])
.join(',')}` .join(',')}`
} }
if (Object.values(this.level2constraint).includes('2')) {
q = q + `&&has_m2m=1`
}
searchCIRelation(q).then(async (res) => { searchCIRelation(q).then(async (res) => {
const facet = [] const facet = []
const ciIds = [] const ciIds = []
@ -849,6 +862,7 @@ export default {
root_ids: ciIds.join(','), root_ids: ciIds.join(','),
level: _level - 1, level: _level - 1,
type_ids: this.showTypes.map((type) => type.id).join(','), type_ids: this.showTypes.map((type) => type.id).join(','),
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
}).then((num) => { }).then((num) => {
facet.forEach((item, idx) => { facet.forEach((item, idx) => {
item[1] += num[ciIds[idx] + ''] item[1] += num[ciIds[idx] + '']
@ -1322,7 +1336,10 @@ export default {
}) })
}, },
async openBatchDownload() { async openBatchDownload() {
this.$refs.batchDownload.open({ preferenceAttrList: this.preferenceAttrList }) this.$refs.batchDownload.open({
preferenceAttrList: this.preferenceAttrList,
ciTypeName: this.$route.meta.name,
})
}, },
batchDownload({ filename, type, checkedKeys }) { batchDownload({ filename, type, checkedKeys }) {
const jsonAttrList = [] const jsonAttrList = []

View File

@ -130,22 +130,27 @@ export default {
exp = expression.match(regQ) ? expression.match(regQ)[0] : null exp = expression.match(regQ) ? expression.match(regQ)[0] : null
} }
const res = await searchCI({ await searchCI({
q: `_type:${this.addTypeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`, q: `_type:${this.addTypeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
count: 50, count: 50,
page: this.currentPage, page: this.currentPage,
sort, sort,
}) })
this.tableData = res.result .then((res) => {
this.totalNumber = res.numfound this.tableData = res.result
this.columns = this.getColumns(res.result, this.preferenceAttrList) this.totalNumber = res.numfound
this.$nextTick(() => { this.columns = this.getColumns(res.result, this.preferenceAttrList)
const _table = this.$refs.xTable this.$nextTick(() => {
if (_table) { const _table = this.$refs.xTable
_table.refreshColumn() if (_table) {
} _table.refreshColumn()
this.loading = false }
}) this.loading = false
})
})
.catch(() => {
this.loading = false
})
}, },
getColumns(data, attrList) { getColumns(data, attrList) {
const modalDom = document.getElementById('add-table-modal') const modalDom = document.getElementById('add-table-modal')

View File

@ -18,10 +18,10 @@
属性说明 属性说明
</span> </span>
</span> </span>
<a-button <a-button
size="small" size="small"
icon="plus" icon="plus"
type="primary" type="primary"
@click="$refs.create.handleOpen(true, 'create')" @click="$refs.create.handleOpen(true, 'create')"
>新建</a-button >新建</a-button
> >
@ -1054,7 +1054,10 @@ export default {
this.$refs.jsonEditor.open(column, row) this.$refs.jsonEditor.open(column, row)
}, },
async openBatchDownload() { async openBatchDownload() {
this.$refs.batchDownload.open({ preferenceAttrList: this.currentAttrList }) this.$refs.batchDownload.open({
preferenceAttrList: this.currentAttrList,
ciTypeName: this.$route.meta.title || this.$route.meta.name,
})
}, },
batchDownload({ filename, type, checkedKeys }) { batchDownload({ filename, type, checkedKeys }) {
console.log(filename, type) console.log(filename, type)