mirror of https://github.com/veops/cmdb.git
Merge pull request #639 from veops/dev_ui_ipam
feat(ui): ipam - add batch assign
This commit is contained in:
commit
c70153c3a7
|
@ -837,7 +837,13 @@ if __name__ == "__main__":
|
||||||
onlineRatio: 'Online Ratio',
|
onlineRatio: 'Online Ratio',
|
||||||
scanEnable: 'Scan Enable',
|
scanEnable: 'Scan Enable',
|
||||||
lastScanTime: 'Last Scan Time',
|
lastScanTime: 'Last Scan Time',
|
||||||
isSuccess: 'Is Success'
|
isSuccess: 'Is Success',
|
||||||
|
batchAssign: 'Batch Assign',
|
||||||
|
batchAssignInProgress: 'Assign in batches, {total} in total, {successNum} successful, {errorNum} failed',
|
||||||
|
batchAssignCompleted: 'Batch Assign Completed',
|
||||||
|
batchRecycle: 'Batch Recycle',
|
||||||
|
batchRecycleInProgress: 'Recycle in batches, {total} in total, {successNum} successful, {errorNum} failed',
|
||||||
|
batchRecycleCompleted: 'Batch Recycle Completed',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default cmdb_en
|
export default cmdb_en
|
||||||
|
|
|
@ -836,7 +836,13 @@ if __name__ == "__main__":
|
||||||
onlineRatio: '在线率',
|
onlineRatio: '在线率',
|
||||||
scanEnable: '是否扫描',
|
scanEnable: '是否扫描',
|
||||||
lastScanTime: '最后扫描时间',
|
lastScanTime: '最后扫描时间',
|
||||||
isSuccess: '是否成功'
|
isSuccess: '是否成功',
|
||||||
|
batchAssign: '批量分配',
|
||||||
|
batchAssignInProgress: '正在批量分配,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||||
|
batchAssignCompleted: '批量分配已完成',
|
||||||
|
batchRecycle: '批量回收',
|
||||||
|
batchRecycleInProgress: '正在批量回收,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||||
|
batchRecycleCompleted: '批量回收已完成',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default cmdb_zh
|
export default cmdb_zh
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="700"
|
:width="700"
|
||||||
:title="$t('cmdb.ipam.addressAssign')"
|
:title="$t('cmdb.ipam.addressAssign')"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
@ok="handleOk"
|
@ok="handleOk"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
>
|
>
|
||||||
|
@ -17,7 +18,7 @@
|
||||||
<a-form-model-item
|
<a-form-model-item
|
||||||
label="IP"
|
label="IP"
|
||||||
>
|
>
|
||||||
{{ ipData.ip }}
|
<span class="assign-form-ip" >{{ ipList.join(', ') }}</span>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item
|
<a-form-model-item
|
||||||
v-for="(item) in formList"
|
v-for="(item) in formList"
|
||||||
|
@ -80,37 +81,29 @@ export default {
|
||||||
attrList: {
|
attrList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
|
||||||
subnetData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
ipData: {},
|
ipData: {},
|
||||||
|
ipList: [],
|
||||||
nodeId: -1,
|
nodeId: -1,
|
||||||
formList: [],
|
formList: [],
|
||||||
form: {},
|
form: {},
|
||||||
formRules: {},
|
formRules: {},
|
||||||
statusSelectOption: [
|
confirmLoading: false,
|
||||||
{
|
isBatch: false
|
||||||
value: 0,
|
|
||||||
label: 'cmdb.ipam.assigned'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: 'cmdb.ipam.reserved'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async open({
|
async open({
|
||||||
ipData,
|
ipList = [],
|
||||||
|
ipData = null,
|
||||||
nodeId,
|
nodeId,
|
||||||
}) {
|
}) {
|
||||||
|
this.isBatch = ipList.length !== 0
|
||||||
|
this.ipList = ipList.length ? _.cloneDeep(ipList) : [ipData?.ip ?? '']
|
||||||
this.ipData = ipData || {}
|
this.ipData = ipData || {}
|
||||||
this.nodeId = nodeId || -1
|
this.nodeId = nodeId || -1
|
||||||
this.visible = true
|
this.visible = true
|
||||||
|
@ -237,7 +230,8 @@ export default {
|
||||||
this.form = {}
|
this.form = {}
|
||||||
this.formRules = {}
|
this.formRules = {}
|
||||||
this.formList = []
|
this.formList = []
|
||||||
this.visible = false
|
this.confirmLoading = false
|
||||||
|
this.isBatch = false
|
||||||
|
|
||||||
this.$refs.assignFormRef.clearValidate()
|
this.$refs.assignFormRef.clearValidate()
|
||||||
},
|
},
|
||||||
|
@ -248,16 +242,35 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await postIPAMAddress({
|
this.confirmLoading = true
|
||||||
ips: [this.ipData.ip],
|
|
||||||
parent_id: this.nodeId,
|
if (!this.isBatch) {
|
||||||
...this.form,
|
await postIPAMAddress({
|
||||||
subnet_mask: this?.ipData?.subnet_mask ?? undefined,
|
ips: this.ipList,
|
||||||
gateway: this?.ipData?.gateway ?? undefined
|
parent_id: this.nodeId,
|
||||||
})
|
...this.form,
|
||||||
|
subnet_mask: this?.ipData?.subnet_mask ?? undefined,
|
||||||
|
gateway: this?.ipData?.gateway ?? undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit('ok')
|
||||||
|
} else {
|
||||||
|
const ipChunk = _.chunk(this.ipList, 5)
|
||||||
|
const paramsList = ipChunk.map((ips) => ({
|
||||||
|
ips,
|
||||||
|
parent_id: this.nodeId,
|
||||||
|
...this.form,
|
||||||
|
subnet_mask: this?.ipData?.subnet_mask ?? undefined,
|
||||||
|
gateway: this?.ipData?.gateway ?? undefined
|
||||||
|
}))
|
||||||
|
this.$emit('batchAssign', {
|
||||||
|
paramsList,
|
||||||
|
ipList: this.ipList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.$emit('ok')
|
|
||||||
this.handleCancel()
|
this.handleCancel()
|
||||||
|
this.confirmLoading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -280,5 +293,12 @@ export default {
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&-ip {
|
||||||
|
max-height: 100px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -9,12 +9,11 @@
|
||||||
<div class="address-null-tip2">{{ $t(addressNullTip) }}</div>
|
<div class="address-null-tip2">{{ $t(addressNullTip) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="loading" class="address-loading">
|
<a-spin
|
||||||
<a-icon type="loading" class="address-loading-icon" />
|
v-else
|
||||||
<span class="address-loading-text">{{ $t('loading') }}</span>
|
:tip="loadTip"
|
||||||
</div>
|
:spinning="loading"
|
||||||
|
>
|
||||||
<template v-else>
|
|
||||||
<div class="address-header">
|
<div class="address-header">
|
||||||
<div class="address-header-left">
|
<div class="address-header-left">
|
||||||
<a-input-search
|
<a-input-search
|
||||||
|
@ -53,6 +52,15 @@
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
|
|
||||||
|
<div v-if="selectedIPList.length" class="ops-list-batch-action">
|
||||||
|
<span @click="clickBatchAssign">{{ $t('cmdb.ipam.batchAssign') }}</span>
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<span @click="clickBatchRecycle">{{ $t('cmdb.ipam.batchRecycle') }}</span>
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<span @click="handleExport">{{ $t('export') }}</span>
|
||||||
|
<span>{{ $t('cmdb.ci.selectRows', { rows: selectedIPList.length }) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="currentLayout === 'grid'"
|
v-if="currentLayout === 'grid'"
|
||||||
class="address-header-status"
|
class="address-header-status"
|
||||||
|
@ -85,22 +93,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="address-header-right">
|
<div class="address-header-right">
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
class="ops-button-ghost"
|
|
||||||
ghost
|
|
||||||
@click="handleExport"
|
|
||||||
>
|
|
||||||
<ops-icon type="veops-export" />
|
|
||||||
{{ $t('export') }}
|
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<div class="address-header-layout">
|
<div class="address-header-layout">
|
||||||
<div
|
<div
|
||||||
v-for="(item) in layoutList"
|
v-for="(item) in layoutList"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:class="['address-header-layout-item', currentLayout === item.value ?'address-header-layout-item-active' : '']"
|
:class="['address-header-layout-item', currentLayout === item.value ?'address-header-layout-item-active' : '']"
|
||||||
@click="currentLayout = item.value"
|
@click="handleChangeLayout(item.value)"
|
||||||
>
|
>
|
||||||
<ops-icon :type="item.icon" />
|
<ops-icon :type="item.icon" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,6 +117,7 @@
|
||||||
:columnWidth="columnWidth"
|
:columnWidth="columnWidth"
|
||||||
@openAssign="openAssign"
|
@openAssign="openAssign"
|
||||||
@recycle="handleRecycle"
|
@recycle="handleRecycle"
|
||||||
|
@selectChange="handleTableSelectChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<GridIP
|
<GridIP
|
||||||
|
@ -131,13 +130,13 @@
|
||||||
@recycle="handleRecycle"
|
@recycle="handleRecycle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</a-spin>
|
||||||
|
|
||||||
<AssignForm
|
<AssignForm
|
||||||
ref="assignFormRef"
|
ref="assignFormRef"
|
||||||
:attrList="attrList"
|
:attrList="attrList"
|
||||||
:subnetData="subnetData"
|
|
||||||
@ok="getIPList"
|
@ok="getIPList"
|
||||||
|
@batchAssign="batchAssign"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -188,6 +187,8 @@ export default {
|
||||||
referenceCIIdMap: {},
|
referenceCIIdMap: {},
|
||||||
columnWidth: {},
|
columnWidth: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
|
selectedIPList: [],
|
||||||
|
loadTip: this.$t('loading'),
|
||||||
|
|
||||||
currentStatus: 'all',
|
currentStatus: 'all',
|
||||||
filterOption: [
|
filterOption: [
|
||||||
|
@ -298,6 +299,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async initData() {
|
async initData() {
|
||||||
|
this.loadTip = this.$t('loading')
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
await this.getColumns()
|
await this.getColumns()
|
||||||
|
@ -497,6 +499,7 @@ export default {
|
||||||
let tableData = []
|
let tableData = []
|
||||||
if (this.currentLayout === 'table') {
|
if (this.currentLayout === 'table') {
|
||||||
tableData = this.$refs.tableIPRef.getCheckedTableData()
|
tableData = this.$refs.tableIPRef.getCheckedTableData()
|
||||||
|
this.selectedIPList = []
|
||||||
} else {
|
} else {
|
||||||
tableData = this.filterIPList
|
tableData = this.filterIPList
|
||||||
}
|
}
|
||||||
|
@ -561,6 +564,143 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChangeLayout(value) {
|
||||||
|
if (this.currentLayout !== value) {
|
||||||
|
if (value === 'grid') {
|
||||||
|
this.selectedIPList = []
|
||||||
|
}
|
||||||
|
this.currentLayout = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTableSelectChange(ips) {
|
||||||
|
this.selectedIPList = ips
|
||||||
|
},
|
||||||
|
|
||||||
|
clickBatchAssign() {
|
||||||
|
this.$refs.assignFormRef.open({
|
||||||
|
nodeId: this?.nodeData?._id,
|
||||||
|
ipData: {
|
||||||
|
subnet_mask: this?.subnetData?.subnet_mask ?? undefined,
|
||||||
|
gateway: this?.subnetData?.gateway ?? undefined
|
||||||
|
},
|
||||||
|
ipList: this.selectedIPList
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async batchAssign({
|
||||||
|
paramsList,
|
||||||
|
ipList
|
||||||
|
}) {
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
this.loadTip = this.$t('cmdb.ipam.batchAssignInProgress', {
|
||||||
|
total: ipList.length,
|
||||||
|
successNum: successNum,
|
||||||
|
errorNum: errorNum,
|
||||||
|
})
|
||||||
|
|
||||||
|
await _.reduce(
|
||||||
|
paramsList,
|
||||||
|
(promiseChain, params) => {
|
||||||
|
const ipCount = params?.ips?.length ?? 0
|
||||||
|
|
||||||
|
return promiseChain.then(() => {
|
||||||
|
return postIPAMAddress(params).then(() => {
|
||||||
|
successNum += ipCount
|
||||||
|
}).catch(() => {
|
||||||
|
errorNum += ipCount
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadTip = this.$t('cmdb.ipam.batchAssignInProgress', {
|
||||||
|
total: ipList.length,
|
||||||
|
successNum: successNum,
|
||||||
|
errorNum: errorNum,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Promise.resolve()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (this.$refs.tableIPRef) {
|
||||||
|
this.$refs.tableIPRef.clearCheckbox()
|
||||||
|
this.selectedIPList = []
|
||||||
|
}
|
||||||
|
this.$message.success(this.$t('cmdb.ipam.batchAssignCompleted'))
|
||||||
|
this.loading = false
|
||||||
|
this.getIPList()
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clickBatchRecycle() {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('cmdb.ipam.recycleTip'),
|
||||||
|
onOk: () => {
|
||||||
|
this.handleBatchRecycle()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleBatchRecycle() {
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
this.loadTip = this.$t('cmdb.ipam.batchRecycleInProgress', {
|
||||||
|
total: this.selectedIPList.length,
|
||||||
|
successNum: successNum,
|
||||||
|
errorNum: errorNum,
|
||||||
|
})
|
||||||
|
|
||||||
|
const ipChunk = _.chunk(this.selectedIPList, 5)
|
||||||
|
|
||||||
|
await _.reduce(
|
||||||
|
ipChunk,
|
||||||
|
(promiseChain, ips) => {
|
||||||
|
const ipCount = ips.length
|
||||||
|
console.log('ipCount', ipCount, successNum, errorNum)
|
||||||
|
return promiseChain.then(() => {
|
||||||
|
return postIPAMAddress({
|
||||||
|
ips,
|
||||||
|
parent_id: this.nodeData._id,
|
||||||
|
assign_status: 1
|
||||||
|
}).then(() => {
|
||||||
|
successNum += ipCount
|
||||||
|
}).catch(() => {
|
||||||
|
errorNum += ipCount
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadTip = this.$t('cmdb.ipam.batchRecycleInProgress', {
|
||||||
|
total: this.selectedIPList.length,
|
||||||
|
successNum: successNum,
|
||||||
|
errorNum: errorNum,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Promise.resolve()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (this.$refs.tableIPRef) {
|
||||||
|
this.$refs.tableIPRef.clearCheckbox()
|
||||||
|
this.selectedIPList = []
|
||||||
|
}
|
||||||
|
this.$message.success(this.$t('cmdb.ipam.batchRecycleCompleted'))
|
||||||
|
this.loading = false
|
||||||
|
this.getIPList()
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -570,7 +710,6 @@ export default {
|
||||||
.address {
|
.address {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&-header {
|
&-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -695,27 +834,5 @@ export default {
|
||||||
color: #2F54EB;
|
color: #2F54EB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-loading {
|
|
||||||
width: 100%;
|
|
||||||
height: 300px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
color: #000000;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
&-icon {
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-text {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
class="ops-unstripe-table checkbox-hover-table"
|
class="ops-unstripe-table checkbox-hover-table"
|
||||||
@checkbox-change="onSelectChange"
|
@checkbox-change="onSelectChange"
|
||||||
@checkbox-all="onSelectChange"
|
@checkbox-all="onSelectChange"
|
||||||
@checkbox-range-end="onSelectChange"
|
@checkbox-range-end="onSelectRangeEnd"
|
||||||
>
|
>
|
||||||
<vxe-table-column
|
<vxe-table-column
|
||||||
align="center"
|
align="center"
|
||||||
|
@ -241,13 +241,20 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clearCheckbox) {
|
if (clearCheckbox) {
|
||||||
tableRef.clearCheckboxRow()
|
this.clearCheckbox()
|
||||||
tableRef.clearCheckboxReserve()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tableData
|
return tableData
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearCheckbox() {
|
||||||
|
const tableRef = this.$refs?.xTable?.getVxetableRef?.()
|
||||||
|
if (tableRef) {
|
||||||
|
tableRef.clearCheckboxRow()
|
||||||
|
tableRef.clearCheckboxReserve()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getReferenceAttrValue(id, col) {
|
getReferenceAttrValue(id, col) {
|
||||||
const ci = this?.referenceCIIdMap?.[col?.reference_type_id]?.[id]
|
const ci = this?.referenceCIIdMap?.[col?.reference_type_id]?.[id]
|
||||||
if (!ci) {
|
if (!ci) {
|
||||||
|
@ -267,7 +274,15 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onSelectChange() {
|
onSelectChange() {
|
||||||
console.log('onSelectChange')
|
const xTable = this.$refs.xTable.getVxetableRef()
|
||||||
|
const records = [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()]
|
||||||
|
const ips = records.map((item) => item.ip)
|
||||||
|
this.$emit('selectChange', ips)
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelectRangeEnd({ records }) {
|
||||||
|
const ips = records?.map?.((item) => item.ip) || []
|
||||||
|
this.$emit('selectChange', ips)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue