mirror of https://github.com/veops/cmdb.git
add batch module
This commit is contained in:
parent
13476128d5
commit
61f77cf311
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<div>
|
||||
<div id="title">
|
||||
<ci-type-choice @getCiTypeAttr="showCiType">
|
||||
</ci-type-choice>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="18">
|
||||
<a-card style="height: 605px">
|
||||
<a-button class="ant-btn-primary" style="margin-left: 10px;" :disabled="uploadFlag" id="upload-button" @click="uploadData">上传</a-button>
|
||||
<upload-file-form v-if="displayUpload" ref="fileEditor"></upload-file-form>
|
||||
<ci-table v-if="editorOnline" :ciTypeAttrs="ciTypeAttrs" ref="onlineEditor"></ci-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div style="min-height: 604px; background: white">
|
||||
<a-card title="上传结果">
|
||||
<upload-result v-if="beginLoad" :upLoadData="needDataList" :ciType="ciType" :unique-field="uniqueField"></upload-result>
|
||||
</a-card>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CiTypeChoice from './modules/CiTypeChoice'
|
||||
import CiTable from './modules/CiTable'
|
||||
import UploadFileForm from './modules/UploadFileForm'
|
||||
import UploadResult from './modules/UploadResult'
|
||||
import { filterNull } from '@/api/cmdb/batch'
|
||||
|
||||
export default {
|
||||
name: 'Batch',
|
||||
components: {
|
||||
CiTypeChoice,
|
||||
CiTable,
|
||||
UploadFileForm,
|
||||
UploadResult
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
editorOnline: false,
|
||||
uploadFlag: true,
|
||||
ciTypeAttrs: [],
|
||||
needDataList: [],
|
||||
ciType: -1,
|
||||
uniqueField: '',
|
||||
uniqueId: 0,
|
||||
beginLoad: false,
|
||||
displayUpload: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCiType (message) {
|
||||
this.ciTypeAttrs = message
|
||||
this.ciType = message.type_id
|
||||
this.uniqueField = message.unique
|
||||
this.uniqueId = message.unique_id
|
||||
this.editorOnline = false
|
||||
this.$nextTick(() => {
|
||||
this.editorOnline = true
|
||||
})
|
||||
},
|
||||
uploadData () {
|
||||
if (this.ciType < 0) {
|
||||
alert('尚未选择模板类型!')
|
||||
return
|
||||
}
|
||||
this.beginLoad = false
|
||||
const fileData = this.$refs.fileEditor.dataList
|
||||
if (fileData.length > 0) {
|
||||
this.needDataList = filterNull(fileData)
|
||||
} else {
|
||||
this.needDataList = filterNull(this.$refs.onlineEditor.getDataList())
|
||||
}
|
||||
this.displayUpload = false
|
||||
this.$nextTick(() => {
|
||||
this.beginLoad = true
|
||||
this.displayUpload = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<div>
|
||||
<div id="hotTable" class="hotTable" style="overflow: hidden; height:275px">
|
||||
<HotTable :root="root" ref="HTable" :settings="hotSettings"></HotTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { HotTable } from '@handsontable-pro/vue'
|
||||
export default {
|
||||
name: 'Editor',
|
||||
components: {
|
||||
HotTable
|
||||
},
|
||||
props: { ciTypeAttrs: { type: Object, required: true } },
|
||||
data: function () {
|
||||
return {
|
||||
root: 'test-hot',
|
||||
dataTitle: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hotSettings () {
|
||||
const whiteColumn = []
|
||||
const aliasList = []
|
||||
const dataTitle = []
|
||||
this.$props.ciTypeAttrs.attributes.forEach(item => {
|
||||
dataTitle.push(item.name)
|
||||
aliasList.push(item.alias)
|
||||
whiteColumn.push('')
|
||||
})
|
||||
this.dataTitle = dataTitle
|
||||
const dt = {
|
||||
data: [whiteColumn],
|
||||
startRows: 11,
|
||||
startCols: 6,
|
||||
minRows: 5,
|
||||
minCols: 4,
|
||||
maxRows: 90,
|
||||
maxCols: 90,
|
||||
rowHeaders: true,
|
||||
// minSpareCols: 2, //列留白
|
||||
colHeaders: aliasList,
|
||||
minSpareRows: 2, // 行留白
|
||||
// autoWrapRow: true, // 自动换行
|
||||
// 自定义右键菜单,可汉化,默认布尔值
|
||||
contextMenu: {
|
||||
items: {
|
||||
row_above: {
|
||||
name: '上方插入一行'
|
||||
},
|
||||
row_below: {
|
||||
name: '下方插入一行'
|
||||
},
|
||||
moverow: {
|
||||
name: '删除行'
|
||||
},
|
||||
unfreeze_column: {
|
||||
name: '取消列固定'
|
||||
},
|
||||
hsep1: '---------', // 提供分隔线
|
||||
hsep2: '---------'
|
||||
}
|
||||
},
|
||||
// width: '100%',
|
||||
// fillHandle: true, // 选中拖拽复制 possible values: true, false, "horizontal", "vertical"
|
||||
fixedColumnsLeft: 0, // 固定左边列数
|
||||
fixedRowsTop: 0, // 固定上边列数
|
||||
manualColumnFreeze: true, // 手动固定列
|
||||
// manualColumnMove: true, // 手动移动列
|
||||
// manualRowMove: true, // 手动移动行
|
||||
// manualColumnResize: true, // 手工更改列距
|
||||
// manualRowResize: true, // 手动更改行距
|
||||
comments: true, // 添加注释
|
||||
customBorders: [], // 添加边框
|
||||
columnSorting: true, // 排序
|
||||
stretchH: 'all', // 根据宽度横向扩展,last:只扩展最后一列,none:默认不扩展
|
||||
afterChange: function (changes, source) {
|
||||
if (changes !== null) {
|
||||
document.getElementById('upload-button').disabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return dt
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDataList () {
|
||||
const data = this.$refs.HTable.$data.hotInstance.getData()
|
||||
data.unshift(this.dataTitle)
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import '~handsontable/dist/handsontable.full.css';
|
||||
</style>
|
|
@ -0,0 +1,113 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-form :form="form" style="max-width: 500px; margin: 30px auto 0;">
|
||||
<a-row>
|
||||
<a-col :span="18">
|
||||
<a-form-item label="模板类型" :labelCol="labelCol" :wrapperCol="wrapperCol">
|
||||
<a-select
|
||||
placeholder="--请选择模板类型--"
|
||||
v-decorator="['ciTypes', { rules: [{required: true, message: '模板类型必须选择'}] }]"
|
||||
@change="selectCiType"
|
||||
>
|
||||
<a-select-option v-for="ciType in ciTypeList" :key="ciType.name" :value="ciType.id">{{ ciType.alias }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item>
|
||||
<a-button
|
||||
style="margin-left: 20px"
|
||||
:disabled="downLoadButtonDis"
|
||||
@click="downLoadExcel"
|
||||
>下载模板</a-button>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<a-divider />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCITypes } from '@/api/cmdb/CIType'
|
||||
import { getCITypeAttributesById } from '@/api/cmdb/CITypeAttr'
|
||||
import { writeExcel } from '@/api/cmdb/batch'
|
||||
|
||||
export default {
|
||||
name: 'CiTypeChoice',
|
||||
data () {
|
||||
return {
|
||||
labelCol: { lg: { span: 5 }, sm: { span: 5 } },
|
||||
wrapperCol: { lg: { span: 19 }, sm: { span: 19 } },
|
||||
form: this.$form.createForm(this),
|
||||
ciTypeList: [],
|
||||
ciTypeName: '',
|
||||
downLoadButtonDis: true,
|
||||
selectNum: 0,
|
||||
selectCiTypeAttrList: []
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
getCITypes().then(res => {
|
||||
this.ciTypeList = res.ci_types
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
selectCiType (el) {
|
||||
// 当选择好模板类型时的回调函数
|
||||
this.downLoadButtonDis = false
|
||||
this.selectNum = el
|
||||
getCITypeAttributesById(el).then(res => {
|
||||
this.$emit('getCiTypeAttr', res)
|
||||
this.selectCiTypeAttrList = res
|
||||
})
|
||||
|
||||
this.ciTypeList.forEach(item => {
|
||||
if (this.selectNum === item.id) {
|
||||
this.ciTypeName = item.alias || item.name
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
downLoadExcel () {
|
||||
const columns = []
|
||||
this.selectCiTypeAttrList.attributes.forEach(item => {
|
||||
columns.push(item.alias)
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-form-style-desc {
|
||||
padding: 0 56px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
h3 {
|
||||
margin: 0 0 12px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
}
|
||||
h4 {
|
||||
margin: 0 0 4px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-form :form="form" style="max-width: 500px; margin: 40px auto 0;">
|
||||
<a-upload-dragger ref="upload" :multiple="true" :customRequest="customRequest" accept=".xls">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<a-icon type="inbox" />
|
||||
</p>
|
||||
<p class="ant-upload-text">点击或拖拽文件至此上传!</p>
|
||||
<p class="ant-upload-hint">支持文件类型:xls</p>
|
||||
</a-upload-dragger>
|
||||
</a-form>
|
||||
<a-divider>or</a-divider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { processFile } from '@/api/cmdb/batch'
|
||||
|
||||
export default {
|
||||
name: 'Step2',
|
||||
data () {
|
||||
return {
|
||||
labelCol: { lg: { span: 5 }, sm: { span: 5 } },
|
||||
wrapperCol: { lg: { span: 19 }, sm: { span: 19 } },
|
||||
form: this.$form.createForm(this),
|
||||
loading: false,
|
||||
timer: 0,
|
||||
ciItemNum: 0,
|
||||
dataList: []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
customRequest (data) {
|
||||
processFile(data.file).then(res => {
|
||||
this.ciItemNum = res.length - 1
|
||||
document.getElementById('upload-button').disabled = false
|
||||
this.dataList = res
|
||||
})
|
||||
},
|
||||
|
||||
handleChange (info) {
|
||||
document.getElementById('load-button').disabled = false
|
||||
console.log(info)
|
||||
},
|
||||
clear () {
|
||||
console.log(this.$refs.upload.$children[0].onSuccess('', ''))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.stepFormText {
|
||||
margin-bottom: 24px;
|
||||
.ant-form-item-label,
|
||||
.ant-form-item-control {
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<div>
|
||||
<h4>共 <span style="color: blue">{{ total }}</span> 条,已完成 <span style="color: lightgreen">{{ complete }}</span> 条
|
||||
,失败 <span style="color: red">{{ errorNum }} </span>条</h4>
|
||||
|
||||
<a-progress :percent="mPercent"/>
|
||||
<div class="my-box">
|
||||
<span>错误信息:</span>
|
||||
<ol>
|
||||
<li :key="item" v-for="item in errorItems">{{ item }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { uploadData } from '@/api/cmdb/batch'
|
||||
|
||||
export default {
|
||||
name: 'Result',
|
||||
props: {
|
||||
upLoadData: {
|
||||
required: true,
|
||||
type: Array
|
||||
},
|
||||
ciType: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
uniqueField: {
|
||||
required: true,
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
total: 0,
|
||||
complete: 0,
|
||||
errorNum: 0,
|
||||
errorItems: []
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
document.getElementById('upload-button').disabled = true
|
||||
this.upload2Server()
|
||||
},
|
||||
computed: {
|
||||
mpercent () {
|
||||
return Math.round(this.complete / this.total * 10000) / 100
|
||||
},
|
||||
progressStatus () {
|
||||
if (this.complete === this.total) {
|
||||
return null
|
||||
} else {
|
||||
return 'active'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
upload2Server () {
|
||||
this.total = this.$props.upLoadData.length - 1
|
||||
for (let i = 0; i < this.total; i++) {
|
||||
const item = {}
|
||||
let itemUniqueName = 'unknown'
|
||||
for (let j = 0; j < this.$props.upLoadData[0].length; j++) {
|
||||
item[this.$props.upLoadData[0][j]] = this.$props.upLoadData[i + 1][j]
|
||||
if (this.$props.upLoadData[0][j] === this.$props.uniqueField) {
|
||||
itemUniqueName = this.$props.upLoadData[i + 1][j] || 'unknown'
|
||||
}
|
||||
}
|
||||
uploadData(this.$props.ciType, item).then(res => {
|
||||
console.log(res)
|
||||
}).catch(err => {
|
||||
this.errorNum += 1
|
||||
console.log(err)
|
||||
this.errorItems.push(itemUniqueName + ': ' + (((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'))
|
||||
})
|
||||
this.complete += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.my-box {
|
||||
margin-top: 20px;
|
||||
color: red;
|
||||
border: 1px red dashed;
|
||||
padding: 8px;
|
||||
border-radius:5px;
|
||||
height: 429px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue