mirror of
				https://github.com/veops/cmdb.git
				synced 2025-11-04 13:46:17 +08:00 
			
		
		
		
	feat(ui): ipam - add batch assign
This commit is contained in:
		@@ -837,7 +837,13 @@ if __name__ == "__main__":
 | 
			
		||||
        onlineRatio: 'Online Ratio',
 | 
			
		||||
        scanEnable: 'Scan Enable',
 | 
			
		||||
        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
 | 
			
		||||
 
 | 
			
		||||
@@ -836,7 +836,13 @@ if __name__ == "__main__":
 | 
			
		||||
        onlineRatio: '在线率',
 | 
			
		||||
        scanEnable: '是否扫描',
 | 
			
		||||
        lastScanTime: '最后扫描时间',
 | 
			
		||||
        isSuccess: '是否成功'
 | 
			
		||||
        isSuccess: '是否成功',
 | 
			
		||||
        batchAssign: '批量分配',
 | 
			
		||||
        batchAssignInProgress: '正在批量分配,共{total}个,成功{successNum}个,失败{errorNum}个',
 | 
			
		||||
        batchAssignCompleted: '批量分配已完成',
 | 
			
		||||
        batchRecycle: '批量回收',
 | 
			
		||||
        batchRecycleInProgress: '正在批量回收,共{total}个,成功{successNum}个,失败{errorNum}个',
 | 
			
		||||
        batchRecycleCompleted: '批量回收已完成',
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
export default cmdb_zh
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
    :visible="visible"
 | 
			
		||||
    :width="700"
 | 
			
		||||
    :title="$t('cmdb.ipam.addressAssign')"
 | 
			
		||||
    :confirmLoading="confirmLoading"
 | 
			
		||||
    @ok="handleOk"
 | 
			
		||||
    @cancel="handleCancel"
 | 
			
		||||
  >
 | 
			
		||||
@@ -17,7 +18,7 @@
 | 
			
		||||
      <a-form-model-item
 | 
			
		||||
        label="IP"
 | 
			
		||||
      >
 | 
			
		||||
        {{ ipData.ip }}
 | 
			
		||||
        <span class="assign-form-ip" >{{ ipList.join(', ') }}</span>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item
 | 
			
		||||
        v-for="(item) in formList"
 | 
			
		||||
@@ -80,37 +81,29 @@ export default {
 | 
			
		||||
    attrList: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default: () => []
 | 
			
		||||
    },
 | 
			
		||||
    subnetData: {
 | 
			
		||||
      type: Object,
 | 
			
		||||
      default: () => {}
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      visible: false,
 | 
			
		||||
      ipData: {},
 | 
			
		||||
      ipList: [],
 | 
			
		||||
      nodeId: -1,
 | 
			
		||||
      formList: [],
 | 
			
		||||
      form: {},
 | 
			
		||||
      formRules: {},
 | 
			
		||||
      statusSelectOption: [
 | 
			
		||||
        {
 | 
			
		||||
          value: 0,
 | 
			
		||||
          label: 'cmdb.ipam.assigned'
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          value: 2,
 | 
			
		||||
          label: 'cmdb.ipam.reserved'
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
      confirmLoading: false,
 | 
			
		||||
      isBatch: false
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    async open({
 | 
			
		||||
      ipData,
 | 
			
		||||
      ipList = [],
 | 
			
		||||
      ipData = null,
 | 
			
		||||
      nodeId,
 | 
			
		||||
    }) {
 | 
			
		||||
      this.isBatch = ipList.length !== 0
 | 
			
		||||
      this.ipList = ipList.length ? _.cloneDeep(ipList) : [ipData?.ip ?? '']
 | 
			
		||||
      this.ipData = ipData || {}
 | 
			
		||||
      this.nodeId = nodeId || -1
 | 
			
		||||
      this.visible = true
 | 
			
		||||
@@ -237,7 +230,8 @@ export default {
 | 
			
		||||
      this.form = {}
 | 
			
		||||
      this.formRules = {}
 | 
			
		||||
      this.formList = []
 | 
			
		||||
      this.visible = false
 | 
			
		||||
      this.confirmLoading = false
 | 
			
		||||
      this.isBatch = false
 | 
			
		||||
 | 
			
		||||
      this.$refs.assignFormRef.clearValidate()
 | 
			
		||||
    },
 | 
			
		||||
@@ -248,16 +242,35 @@ export default {
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await postIPAMAddress({
 | 
			
		||||
          ips: [this.ipData.ip],
 | 
			
		||||
          parent_id: this.nodeId,
 | 
			
		||||
          ...this.form,
 | 
			
		||||
          subnet_mask: this?.ipData?.subnet_mask ?? undefined,
 | 
			
		||||
          gateway: this?.ipData?.gateway ?? undefined
 | 
			
		||||
        })
 | 
			
		||||
        this.confirmLoading = true
 | 
			
		||||
 | 
			
		||||
        if (!this.isBatch) {
 | 
			
		||||
          await postIPAMAddress({
 | 
			
		||||
            ips: this.ipList,
 | 
			
		||||
            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.confirmLoading = false
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -280,5 +293,12 @@ export default {
 | 
			
		||||
  max-height: 400px;
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
  overflow-x: hidden;
 | 
			
		||||
 | 
			
		||||
  &-ip {
 | 
			
		||||
    max-height: 100px;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    display: block;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,11 @@
 | 
			
		||||
      <div class="address-null-tip2">{{ $t(addressNullTip) }}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div v-else-if="loading" class="address-loading">
 | 
			
		||||
      <a-icon type="loading" class="address-loading-icon" />
 | 
			
		||||
      <span class="address-loading-text">{{ $t('loading') }}</span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <template v-else>
 | 
			
		||||
    <a-spin
 | 
			
		||||
      v-else
 | 
			
		||||
      :tip="loadTip"
 | 
			
		||||
      :spinning="loading"
 | 
			
		||||
    >
 | 
			
		||||
      <div class="address-header">
 | 
			
		||||
        <div class="address-header-left">
 | 
			
		||||
          <a-input-search
 | 
			
		||||
@@ -53,6 +52,15 @@
 | 
			
		||||
            </a-select-option>
 | 
			
		||||
          </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
 | 
			
		||||
            v-if="currentLayout === 'grid'"
 | 
			
		||||
            class="address-header-status"
 | 
			
		||||
@@ -85,22 +93,12 @@
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <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
 | 
			
		||||
              v-for="(item) in layoutList"
 | 
			
		||||
              :key="item.value"
 | 
			
		||||
              :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" />
 | 
			
		||||
            </div>
 | 
			
		||||
@@ -119,6 +117,7 @@
 | 
			
		||||
          :columnWidth="columnWidth"
 | 
			
		||||
          @openAssign="openAssign"
 | 
			
		||||
          @recycle="handleRecycle"
 | 
			
		||||
          @selectChange="handleTableSelectChange"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <GridIP
 | 
			
		||||
@@ -131,13 +130,13 @@
 | 
			
		||||
          @recycle="handleRecycle"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
    </a-spin>
 | 
			
		||||
 | 
			
		||||
    <AssignForm
 | 
			
		||||
      ref="assignFormRef"
 | 
			
		||||
      :attrList="attrList"
 | 
			
		||||
      :subnetData="subnetData"
 | 
			
		||||
      @ok="getIPList"
 | 
			
		||||
      @batchAssign="batchAssign"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -188,6 +187,8 @@ export default {
 | 
			
		||||
      referenceCIIdMap: {},
 | 
			
		||||
      columnWidth: {},
 | 
			
		||||
      loading: false,
 | 
			
		||||
      selectedIPList: [],
 | 
			
		||||
      loadTip: this.$t('loading'),
 | 
			
		||||
 | 
			
		||||
      currentStatus: 'all',
 | 
			
		||||
      filterOption: [
 | 
			
		||||
@@ -298,6 +299,7 @@ export default {
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    async initData() {
 | 
			
		||||
      this.loadTip = this.$t('loading')
 | 
			
		||||
      this.loading = true
 | 
			
		||||
      try {
 | 
			
		||||
        await this.getColumns()
 | 
			
		||||
@@ -497,6 +499,7 @@ export default {
 | 
			
		||||
      let tableData = []
 | 
			
		||||
      if (this.currentLayout === 'table') {
 | 
			
		||||
        tableData = this.$refs.tableIPRef.getCheckedTableData()
 | 
			
		||||
        this.selectedIPList = []
 | 
			
		||||
      } else {
 | 
			
		||||
        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 {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: fit-content;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  &-header {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
@@ -695,27 +834,5 @@ export default {
 | 
			
		||||
      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>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
      class="ops-unstripe-table checkbox-hover-table"
 | 
			
		||||
      @checkbox-change="onSelectChange"
 | 
			
		||||
      @checkbox-all="onSelectChange"
 | 
			
		||||
      @checkbox-range-end="onSelectChange"
 | 
			
		||||
      @checkbox-range-end="onSelectRangeEnd"
 | 
			
		||||
    >
 | 
			
		||||
      <vxe-table-column
 | 
			
		||||
        align="center"
 | 
			
		||||
@@ -241,13 +241,20 @@ export default {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (clearCheckbox) {
 | 
			
		||||
        tableRef.clearCheckboxRow()
 | 
			
		||||
        tableRef.clearCheckboxReserve()
 | 
			
		||||
        this.clearCheckbox()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return tableData
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clearCheckbox() {
 | 
			
		||||
      const tableRef = this.$refs?.xTable?.getVxetableRef?.()
 | 
			
		||||
      if (tableRef) {
 | 
			
		||||
        tableRef.clearCheckboxRow()
 | 
			
		||||
        tableRef.clearCheckboxReserve()
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getReferenceAttrValue(id, col) {
 | 
			
		||||
      const ci = this?.referenceCIIdMap?.[col?.reference_type_id]?.[id]
 | 
			
		||||
      if (!ci) {
 | 
			
		||||
@@ -267,7 +274,15 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user