mirror of https://github.com/veops/cmdb.git
commit
8f6b54cd12
|
@ -1,5 +1,7 @@
|
|||
/* eslint-disable */
|
||||
import _ from 'lodash'
|
||||
import XLSX from 'xlsx'
|
||||
import XLSXS from 'xlsx-js-style'
|
||||
export function sum(arr) {
|
||||
if (!arr.length) {
|
||||
return 0
|
||||
|
@ -150,3 +152,25 @@ export const toThousands = (num = 0) => {
|
|||
return n.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
|
||||
})
|
||||
}
|
||||
|
||||
export const downloadExcel = (data, fileName = `${moment().format('YYYY-MM-DD HH:mm:ss')}.xls`) => {
|
||||
// STEP 1: Create a new workbook
|
||||
const wb = XLSXS.utils.book_new()
|
||||
// STEP 2: Create data rows and styles
|
||||
const rowArray = data
|
||||
// STEP 3: Create worksheet with rows; Add worksheet to workbook
|
||||
const ws = XLSXS.utils.aoa_to_sheet(rowArray)
|
||||
XLSXS.utils.book_append_sheet(wb, ws, fileName)
|
||||
|
||||
let maxColumnNumber = 1 // 默认最大列数
|
||||
rowArray.forEach(item => { if (item.length > maxColumnNumber) { maxColumnNumber = item.length } })
|
||||
|
||||
// 添加列宽
|
||||
ws['!cols'] = (rowArray[0].map(item => {
|
||||
return { width: 22 }
|
||||
}))
|
||||
// // 添加行高
|
||||
// ws['!rows'] = [{ 'hpt': 80 }]
|
||||
// STEP 4: Write Excel file to browser #导出
|
||||
XLSXS.writeFile(wb, fileName + '.xlsx')
|
||||
}
|
||||
|
|
|
@ -14,20 +14,83 @@
|
|||
}}</a-select-option>
|
||||
</a-select>
|
||||
<a-button
|
||||
@click="downLoadExcel"
|
||||
@click="openModal"
|
||||
:disabled="!selectNum"
|
||||
type="primary"
|
||||
class="ops-button-primary"
|
||||
icon="download"
|
||||
>下载模板</a-button
|
||||
>
|
||||
<a-modal
|
||||
:bodyStyle="{ paddingTop: 0 }"
|
||||
width="800px"
|
||||
:title="`${ciTypeName}`"
|
||||
:visible="visible"
|
||||
@cancel="handleCancel"
|
||||
@ok="handleOk"
|
||||
wrapClassName="ci-type-choice-modal"
|
||||
>
|
||||
<a-divider orientation="left">模型属性</a-divider>
|
||||
<a-checkbox
|
||||
@change="changeCheckAll"
|
||||
:style="{ marginBottom: '20px' }"
|
||||
:indeterminate="indeterminate"
|
||||
:checked="checkAll"
|
||||
>
|
||||
全选
|
||||
</a-checkbox>
|
||||
<br />
|
||||
<a-checkbox-group v-model="checkedAttrs">
|
||||
<a-row>
|
||||
<a-col :span="6" v-for="item in selectCiTypeAttrList.attributes" :key="item.alias || item.name">
|
||||
<a-checkbox :disabled="item.name === selectCiTypeAttrList.unique" :value="item.alias || item.name">
|
||||
{{ item.alias || item.name }}
|
||||
<span style="color: red" v-if="item.name === selectCiTypeAttrList.unique">*</span>
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-checkbox-group>
|
||||
<template v-if="parentsType && parentsType.length">
|
||||
<a-divider orientation="left">模型关联</a-divider>
|
||||
<a-row :gutter="[24, 24]" align="top" type="flex">
|
||||
<a-col :style="{ display: 'inline-flex' }" :span="12" v-for="item in parentsType" :key="item.id">
|
||||
<a-checkbox @click="clickParent(item)" :checked="checkedParents.includes(item.alias || item.name)">
|
||||
</a-checkbox>
|
||||
<span
|
||||
:style="{
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
width: '80px',
|
||||
margin: '0 5px',
|
||||
textAlign: 'right',
|
||||
}"
|
||||
:title="item.alias || item.name"
|
||||
>{{ item.alias || item.name }}</span
|
||||
>
|
||||
<a-select :style="{ flex: 1 }" size="small" v-model="parentsForm[item.alias || item.name].attr">
|
||||
<a-select-option
|
||||
:title="attr.alias || attr.name"
|
||||
v-for="attr in item.attributes"
|
||||
:key="attr.alias || attr.name"
|
||||
:value="attr.alias || attr.name"
|
||||
>
|
||||
{{ attr.alias || attr.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</a-modal>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { downloadExcel } from '../../../utils/helper'
|
||||
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||
import { writeExcel } from '@/modules/cmdb/api/batch'
|
||||
import { getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
|
||||
|
||||
export default {
|
||||
name: 'CiTypeChoice',
|
||||
|
@ -37,6 +100,13 @@ export default {
|
|||
ciTypeName: '',
|
||||
selectNum: 0,
|
||||
selectCiTypeAttrList: [],
|
||||
visible: false,
|
||||
checkedAttrs: [],
|
||||
indeterminate: false,
|
||||
checkAll: true,
|
||||
parentsType: [],
|
||||
parentsForm: {},
|
||||
checkedParents: [],
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
|
@ -44,6 +114,18 @@ export default {
|
|||
this.ciTypeList = res.ci_types
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
checkedAttrs() {
|
||||
if (this.checkedAttrs.length < this.selectCiTypeAttrList.attributes.length) {
|
||||
this.indeterminate = true
|
||||
this.checkAll = false
|
||||
}
|
||||
if (this.checkedAttrs.length === this.selectCiTypeAttrList.attributes.length) {
|
||||
this.indeterminate = false
|
||||
this.checkAll = true
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selectCiType(el) {
|
||||
// 当选择好模板类型时的回调函数
|
||||
|
@ -60,24 +142,70 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
downLoadExcel() {
|
||||
const columns = []
|
||||
this.selectCiTypeAttrList.attributes.forEach((item) => {
|
||||
columns.push(item.alias)
|
||||
openModal() {
|
||||
getCITypeParent(this.selectNum).then((res) => {
|
||||
this.parentsType = res.parents
|
||||
const _parentsForm = {}
|
||||
res.parents.forEach((item) => {
|
||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||
_parentsForm[item.alias || item.name] = { attr: _find?.alias || _find?.name, value: '' }
|
||||
})
|
||||
this.parentsForm = _parentsForm
|
||||
this.checkedParents = []
|
||||
this.visible = true
|
||||
this.checkedAttrs = this.selectCiTypeAttrList.attributes.map((item) => item.alias || item.name)
|
||||
})
|
||||
const excel = writeExcel(columns, this.ciTypeName)
|
||||
const tempLink = document.createElement('a')
|
||||
tempLink.download = this.ciTypeName + '.xls'
|
||||
tempLink.style.display = 'none'
|
||||
const blob = new Blob([excel])
|
||||
tempLink.href = URL.createObjectURL(blob)
|
||||
document.body.appendChild(tempLink)
|
||||
tempLink.click()
|
||||
document.body.removeChild(tempLink)
|
||||
},
|
||||
filterOption(input, option) {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
},
|
||||
handleOk() {
|
||||
const columns1 = this.checkedAttrs.map((item) => {
|
||||
return {
|
||||
v: item,
|
||||
t: 's',
|
||||
s: {
|
||||
numFmt: 'string',
|
||||
},
|
||||
}
|
||||
})
|
||||
const columns2 = this.checkedParents.map((p) => {
|
||||
return {
|
||||
v: `$${p}.${this.parentsForm[p].attr}`,
|
||||
t: 's',
|
||||
s: {
|
||||
font: {
|
||||
color: {
|
||||
rgb: 'FF0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
downloadExcel([[...columns1, ...columns2]], this.ciTypeName)
|
||||
this.handleCancel()
|
||||
},
|
||||
changeCheckAll(e) {
|
||||
if (e.target.checked) {
|
||||
this.checkedAttrs = this.selectCiTypeAttrList.attributes.map((item) => item.alias || item.name)
|
||||
} else {
|
||||
const _find = this.selectCiTypeAttrList.attributes.find(
|
||||
(item) => item.name === this.selectCiTypeAttrList.unique
|
||||
)
|
||||
this.checkedAttrs = [_find?.alias || _find?.name]
|
||||
}
|
||||
},
|
||||
clickParent(item) {
|
||||
const _idx = this.checkedParents.findIndex((p) => p === (item.alias || item.name))
|
||||
if (_idx > -1) {
|
||||
this.checkedParents.splice(_idx, 1)
|
||||
} else {
|
||||
this.checkedParents.push(item.alias || item.name)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -105,3 +233,15 @@ export default {
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.ci-type-choice-modal {
|
||||
.ant-checkbox-disabled .ant-checkbox-inner {
|
||||
border-color: #2f54eb !important;
|
||||
background-color: #2f54eb;
|
||||
}
|
||||
.ant-checkbox-disabled.ant-checkbox-checked .ant-checkbox-inner::after {
|
||||
border-color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -40,15 +40,25 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
columns() {
|
||||
const _columns = []
|
||||
if (this.ciTypeAttrs.attributes) {
|
||||
return this.ciTypeAttrs.attributes.map((item) => {
|
||||
_columns.push(
|
||||
...this.ciTypeAttrs.attributes.map((item) => {
|
||||
return {
|
||||
title: item.alias || item.name,
|
||||
field: item.alias || item.name,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
return []
|
||||
if (this.uploadData && this.uploadData.length) {
|
||||
Object.keys(this.uploadData[0]).forEach((key) => {
|
||||
if (key.startsWith('$')) {
|
||||
_columns.push({ title: key, field: key })
|
||||
}
|
||||
})
|
||||
}
|
||||
return _columns
|
||||
},
|
||||
dataSource() {
|
||||
return _.cloneDeep(this.uploadData)
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
ref="upload"
|
||||
:multiple="false"
|
||||
:customRequest="customRequest"
|
||||
accept=".xls"
|
||||
accept=".xls,.xlsx"
|
||||
:showUploadList="false"
|
||||
:fileList="fileList"
|
||||
>
|
||||
<img :style="{ width: '80px', height: '80px' }" src="@/assets/file_upload.png" />
|
||||
<p class="ant-upload-text">点击或拖拽文件至此上传!</p>
|
||||
<p class="ant-upload-hint">支持文件类型:xls</p>
|
||||
<p class="ant-upload-hint">支持文件类型:xls,xlsx</p>
|
||||
</a-upload-dragger>
|
||||
<div v-for="item in fileList" :key="item.name" class="cmdb-batch-upload-dragger-file">
|
||||
<span><a-icon type="file" :style="{ color: '#2F54EB', marginRight: '5px' }" />{{ item.name }}</span>
|
||||
|
|
|
@ -209,13 +209,19 @@
|
|||
<EditAttrsPopover :typeId="typeId" class="operation-icon" @refresh="refreshAfterEditAttrs" />
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<a-space>
|
||||
<a @click="$refs.detail.create(row.ci_id || row._id)">
|
||||
<a-icon type="unordered-list" />
|
||||
</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-tooltip title="添加关系">
|
||||
<a @click="$refs.detail.create(row.ci_id || row._id, 'tab_2', '2')">
|
||||
<a-icon type="retweet" />
|
||||
</a>
|
||||
</a-tooltip>
|
||||
<a @click="deleteCI(row)" :style="{ color: 'red' }">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<template #empty>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<a-tab-pane key="tab_2">
|
||||
<span slot="tab"><a-icon type="branches" />关系</span>
|
||||
<div :style="{ padding: '24px' }">
|
||||
<CiDetailRelation :ciId="ciId" :typeId="typeId" :ci="ci" />
|
||||
<CiDetailRelation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_3">
|
||||
|
@ -147,8 +147,14 @@ export default {
|
|||
},
|
||||
inject: ['reload', 'handleSearch', 'attrList'],
|
||||
methods: {
|
||||
create(ciId) {
|
||||
create(ciId, activeTabKey = 'tab_1', ciDetailRelationKey = '1') {
|
||||
this.visible = true
|
||||
this.activeTabKey = activeTabKey
|
||||
if (activeTabKey === 'tab_2') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ciDetailRelation.activeKey = ciDetailRelationKey
|
||||
})
|
||||
}
|
||||
this.ciId = ciId
|
||||
this.getAttributes()
|
||||
this.getCI()
|
||||
|
|
|
@ -23,6 +23,30 @@
|
|||
:attributeList="attributeList"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="parentsType && parentsType.length">
|
||||
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">模型关系</a-divider>
|
||||
<a-form>
|
||||
<a-row :gutter="24" align="top" type="flex">
|
||||
<a-col :span="12" v-for="item in parentsType" :key="item.id">
|
||||
<a-form-item :label="item.alias || item.name" :colon="false">
|
||||
<a-input-group compact style="width: 100%">
|
||||
<a-select v-model="parentsForm[item.name].attr">
|
||||
<a-select-option
|
||||
:title="attr.alias || attr.name"
|
||||
v-for="attr in item.attributes"
|
||||
:key="attr.name"
|
||||
:value="attr.name"
|
||||
>
|
||||
{{ attr.alias || attr.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-input placeholder="多个值使用,分割" v-model="parentsForm[item.name].value" style="width: 50%" />
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="action === 'update'">
|
||||
<a-form :form="form">
|
||||
|
@ -110,6 +134,7 @@ import { addCI } from '@/modules/cmdb/api/ci'
|
|||
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
||||
import { valueTypeMap } from '../../../utils/const'
|
||||
import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue'
|
||||
import { getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
|
||||
|
||||
export default {
|
||||
name: 'CreateInstanceForm',
|
||||
|
@ -138,6 +163,8 @@ export default {
|
|||
batchUpdateLists: [],
|
||||
editAttr: null,
|
||||
attributesByGroup: [],
|
||||
parentsType: [],
|
||||
parentsForm: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -231,6 +258,11 @@ export default {
|
|||
}
|
||||
})
|
||||
values.ci_type = _this.typeId
|
||||
Object.keys(this.parentsForm).forEach((type) => {
|
||||
if (this.parentsForm[type].value) {
|
||||
values[`$${type}.${this.parentsForm[type].attr}`] = this.parentsForm[type].value
|
||||
}
|
||||
})
|
||||
addCI(values).then((res) => {
|
||||
_this.$message.success('新增成功!')
|
||||
_this.visible = false
|
||||
|
@ -249,6 +281,17 @@ export default {
|
|||
Promise.all([this.getCIType(), this.getAttributeList()]).then(() => {
|
||||
this.batchUpdateLists = [{ name: this.attributeList[0].name }]
|
||||
})
|
||||
if (action === 'create') {
|
||||
getCITypeParent(this.typeId).then((res) => {
|
||||
this.parentsType = res.parents
|
||||
const _parentsForm = {}
|
||||
res.parents.forEach((item) => {
|
||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||
_parentsForm[item.name] = { attr: _find.name, value: '' }
|
||||
})
|
||||
this.parentsForm = _parentsForm
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getFieldType(name) {
|
||||
|
|
|
@ -244,14 +244,13 @@ export default {
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.ci)
|
||||
this.init(true)
|
||||
},
|
||||
methods: {
|
||||
async init(isFirst) {
|
||||
await Promise.all([this.getParentCITypes(), this.getChildCITypes()])
|
||||
Promise.all([this.getFirstCIs(), this.getSecondCIs()]).then(() => {
|
||||
if (isFirst) {
|
||||
if (isFirst && this.$refs.ciDetailRelationTopo) {
|
||||
this.$refs.ciDetailRelationTopo.setTopoData(this.topoData)
|
||||
}
|
||||
})
|
||||
|
@ -395,12 +394,6 @@ export default {
|
|||
margin-top: 20px;
|
||||
margin-bottom: 5px;
|
||||
color: #303133;
|
||||
> a {
|
||||
display: none;
|
||||
}
|
||||
&:hover > a {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -243,17 +243,23 @@
|
|||
/>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<a-space>
|
||||
<a @click="$refs.detail.create(row.ci_id || row._id)">
|
||||
<a-icon type="unordered-list" />
|
||||
</a>
|
||||
<a-tooltip title="添加关系">
|
||||
<a @click="$refs.detail.create(row.ci_id || row._id, 'tab_2', '2')">
|
||||
<a-icon type="retweet" />
|
||||
</a>
|
||||
</a-tooltip>
|
||||
<template v-if="isLeaf">
|
||||
<a-divider type="vertical" />
|
||||
<a-tooltip title="删除实例">
|
||||
<a @click="deleteCI(row)" :style="{ color: 'red' }">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<template #empty>
|
||||
|
|
|
@ -297,17 +297,23 @@
|
|||
<EditAttrsPopover :typeId="Number(typeId)" class="operation-icon" @refresh="refreshAfterEditAttrs" />
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<a-space>
|
||||
<a @click="$refs.detail.create(row.ci_id || row._id)">
|
||||
<a-icon type="unordered-list" />
|
||||
</a>
|
||||
<a-tooltip title="添加关系">
|
||||
<a @click="$refs.detail.create(row.ci_id || row._id, 'tab_2', '2')">
|
||||
<a-icon type="retweet" />
|
||||
</a>
|
||||
</a-tooltip>
|
||||
<template>
|
||||
<a-divider type="vertical" />
|
||||
<a-tooltip title="删除实例">
|
||||
<a @click="deleteCI(row)" :style="{ color: 'red' }">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<template #empty>
|
||||
|
|
Loading…
Reference in New Issue