764 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			764 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Vue
		
	
	
<template>
 | 
						||
  <basic-form :model="formData" ref="addForm" label-width="0" :disabled="disabled">
 | 
						||
    <basic-table :data="showParamList" ref="addTable">
 | 
						||
      <el-table-column label="序号" show-overflow-tooltip width="60px">
 | 
						||
        <template slot-scope="{ $index }">
 | 
						||
          <basic-form-item label="">
 | 
						||
            {{ $index + 1 }}
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="命名规则" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" :prop="`showParamList.${$index}.configs.nameRuleId`">
 | 
						||
            <el-select v-model="row.configs.nameRuleId" placeholder="请选择命名规则" clearable filterable>
 | 
						||
              <el-option v-for="(item, index) in nameruleList" :key="index" :label="item.name" :value="item.id"> </el-option>
 | 
						||
            </el-select>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="主机名" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required,vmHostName" :prop="`showParamList.${$index}.configs.vmHostName`">
 | 
						||
            <el-input v-model="row.configs.vmHostName"> </el-input>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <!-- <el-table-column label="云平台类型" show-overflow-tooltip>
 | 
						||
        <template slot-scope="{ row,$index }">
 | 
						||
          <basic-form-item label="">
 | 
						||
            <el-select v-model="row.subLocation.vendorType">
 | 
						||
              <el-option :label="item | vendorName" :value="item" v-for="item in vendorTypelist" :key="item"></el-option>
 | 
						||
            </el-select>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column> -->
 | 
						||
      <el-table-column label="区域" show-overflow-tooltip width="180px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.subLocation.region`">
 | 
						||
            <el-select v-model="row.subLocation.region" @change="changeRegion(row, true)">
 | 
						||
              <el-option :label="item.regionName" :value="item.region" v-for="item in regionList" :key="item.region"></el-option>
 | 
						||
            </el-select>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="可用区" show-overflow-tooltip width="180px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.subLocation.az`">
 | 
						||
            <el-select v-model="row.subLocation.az" @change="changeAz(row, true)">
 | 
						||
              <el-option :label="item.azName" :value="item.availablitiyZone" v-for="item in getZoneListByRegion(row)" :key="item.availablitiyZone"></el-option>
 | 
						||
            </el-select>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="镜像类型" show-overflow-tooltip width="180px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.configs.osCategory`">
 | 
						||
            <el-select v-model="row.configs.osCategory" placeholder="请选择" @change="getVersionList(row, true)">
 | 
						||
              <el-option v-for="(item, index) in row.osList" :key="index" :label="item" :value="item"> </el-option>
 | 
						||
            </el-select>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="镜像版本" show-overflow-tooltip width="180px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.configs.osVersion`">
 | 
						||
            <el-select v-model="row.configs.osVersion" placeholder="请选择" @change="getImage(row, true)">
 | 
						||
              <el-option v-for="(item, index) in row.versionList" :key="index" :label="item" :value="item"> </el-option>
 | 
						||
            </el-select>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="CPU(核)" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.configs.cpu`">
 | 
						||
            <el-input-number :min="1" :max="500" v-model="row.configs.cpu" @change="(val) => changeCpu(val, row)"> </el-input-number>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="内存(GB)" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.configs.memory`">
 | 
						||
            <el-input-number :min="row.configs.templateRam" :max="500" v-model="row.configs.memory" @change="(val) => changeMemory(val, row)"> </el-input-number>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="磁盘(GB)" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.configs.sysDisk.disk`">
 | 
						||
            <span class="detail-href" @click="handleOpenDiskDialog(row, $index)">
 | 
						||
              <template v-if="getDiskTotalSize(row) > 0">{{ getDiskTotalSize(row) }}</template>
 | 
						||
              <template v-else>配置</template>
 | 
						||
            </span>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="网络" show-overflow-tooltip width="180px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" :prop="`showParamList.${$index}`" :rules="rules.networkCardConfigsRule">
 | 
						||
            <span class="detail-href" @click="handleOpenNetworkDialog(row, $index)">
 | 
						||
              <template v-if="!row.configs.networkCardConfigs.length || row.configs.networkCardConfigs[0].ipPoolId">
 | 
						||
                <div v-for="(item, index) in row.configs.networkCardConfigs" :key="index">
 | 
						||
                  {{ item.ipPoolName }} <span v-if="item.address.length">-{{ item.address.join('、') }}</span>
 | 
						||
                </div>
 | 
						||
              </template>
 | 
						||
              <template v-else>配置</template>
 | 
						||
            </span>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="密码" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required,vmPassword" :prop="`showParamList.${$index}.configs.password`">
 | 
						||
            <el-input v-model="row.configs.password" placeholder="请输入密码" show-password></el-input>
 | 
						||
          </basic-form-item>
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.configs.confirm_password`">
 | 
						||
            <el-input v-model="row.configs.confirm_password" placeholder="请再次输入密码" show-password></el-input>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="用途" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.subLocation.purpose`">
 | 
						||
            <el-input v-model="row.subLocation.purpose"> </el-input>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="软件配置" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="">
 | 
						||
            <span class="detail-href" @click="openGraph(row, $index)"> {{ getGraphConfigLabel(row) }} </span>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="数量" show-overflow-tooltip width="160px">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="" validate="required" :prop="`showParamList.${$index}.emption.count`">
 | 
						||
            <el-input-number v-model="row.emption.count"> </el-input-number>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <el-table-column label="操作" width="160px" fixed="right">
 | 
						||
        <template slot-scope="{ row, $index }">
 | 
						||
          <basic-form-item label="">
 | 
						||
            <el-button type="text" @click="handleClone(row)">复制</el-button>
 | 
						||
            <el-button type="text" @click="handleSub($index)">删除</el-button>
 | 
						||
          </basic-form-item>
 | 
						||
        </template>
 | 
						||
      </el-table-column>
 | 
						||
      <span slot="pagination"></span>
 | 
						||
    </basic-table>
 | 
						||
    <DataDisk v-if="addDiskDialog.visible" :dialog="addDiskDialog" @success="changeDisk"></DataDisk>
 | 
						||
    <Ippool v-if="addNetworkDialog.visible" :dialog="addNetworkDialog" :disabled="disabled"></Ippool>
 | 
						||
    <el-dialog :visible.sync="graphDialog.visible" title="作业编排" width="1200px" append-to-body top="5vh" :closeOnClickModal="false">
 | 
						||
      <Graph v-if="graphDialog.visible" :addData="addData" :graphDialog="graphDialog" @back="graphDialog.visible = false" :disabled="disabled"></Graph>
 | 
						||
    </el-dialog>
 | 
						||
  </basic-form>
 | 
						||
</template>
 | 
						||
 | 
						||
<script>
 | 
						||
import { conditionService, conditionImage, getNameRule } from 'services/platform/index'
 | 
						||
import { generateSpec, getSpecValue } from 'views/resource-apply/utils/index'
 | 
						||
import { getPoolCondition } from 'services/platform/pool'
 | 
						||
import { uniqBy, cloneDeep } from 'lodash-es'
 | 
						||
import { transformNetworkConfig } from './ImageItem'
 | 
						||
import { subApplicationParam, GEN_UUID, ServiceCodeMap, defaultTask } from '../data/EFCInit'
 | 
						||
import Ippool, { CECSTACK_DEFAULT_CONFIG, CLOUDTOWER_DEFAULT_CONFIG } from './EFCIppool'
 | 
						||
import DataDisk from './EFCDataDisk'
 | 
						||
import sku from '../mixins/EFCsku'
 | 
						||
import Graph from '../graph/graph.vue'
 | 
						||
import { getVolumeTpl } from 'services/platform/smart.js'
 | 
						||
export default {
 | 
						||
  name: 'VmList',
 | 
						||
  mixins: [sku],
 | 
						||
  components: {
 | 
						||
    Ippool,
 | 
						||
    DataDisk,
 | 
						||
    Graph
 | 
						||
  },
 | 
						||
  props: {
 | 
						||
    itemData: {
 | 
						||
      type: [Object, Boolean]
 | 
						||
    },
 | 
						||
    showParamList: {
 | 
						||
      type: Array,
 | 
						||
      required: true,
 | 
						||
      default: () => []
 | 
						||
    },
 | 
						||
    addData: {
 | 
						||
      type: Object,
 | 
						||
      required: true,
 | 
						||
      default: () => {}
 | 
						||
    },
 | 
						||
    disabled: {
 | 
						||
      type: Boolean
 | 
						||
    }
 | 
						||
  },
 | 
						||
  provide() {
 | 
						||
    // 使用函数的形式,可以访问到 `this`
 | 
						||
    return {
 | 
						||
      getSubApplicationParamsForTaskServer: () => this.addData.subApplicationParams.filter((item) => item.taskGroupUuid === this.graphDialog.task.taskGroupUuid)
 | 
						||
    }
 | 
						||
  },
 | 
						||
  computed: {
 | 
						||
    formData() {
 | 
						||
      return { showParamList: this.showParamList }
 | 
						||
    },
 | 
						||
    regionList() {
 | 
						||
      return this.allPoolList.reduce((pre, cur) => {
 | 
						||
        if (!pre.find((item) => item.region == cur.region)) {
 | 
						||
          pre.push({
 | 
						||
            regionName: cur.regionName,
 | 
						||
            region: cur.region
 | 
						||
          })
 | 
						||
        }
 | 
						||
        return pre
 | 
						||
      }, [])
 | 
						||
    }
 | 
						||
  },
 | 
						||
  data() {
 | 
						||
    return {
 | 
						||
      nameruleList: [],
 | 
						||
      vendorTypelist: [],
 | 
						||
      allPoolList: [],
 | 
						||
      addDiskDialog: {
 | 
						||
        visible: false,
 | 
						||
        index: '',
 | 
						||
        row: {}
 | 
						||
      },
 | 
						||
      addNetworkDialog: {
 | 
						||
        visible: false,
 | 
						||
        row: {},
 | 
						||
        cb: () => {}
 | 
						||
      },
 | 
						||
      rules: {
 | 
						||
        networkCardConfigsRule: [
 | 
						||
          {
 | 
						||
            required: true,
 | 
						||
            validator: (rule, value, callback) => {
 | 
						||
              const validateNetworkCard = this.validateNetworkCard(value)
 | 
						||
              if (validateNetworkCard.valid) {
 | 
						||
                callback()
 | 
						||
              } else {
 | 
						||
                callback(new Error(validateNetworkCard.message))
 | 
						||
              }
 | 
						||
            },
 | 
						||
            trigger: null
 | 
						||
          }
 | 
						||
        ]
 | 
						||
      },
 | 
						||
      graphDialog: {
 | 
						||
        visible: false,
 | 
						||
        task: {},
 | 
						||
        cb: () => {}
 | 
						||
      },
 | 
						||
      CTDataDisktemplateData: []
 | 
						||
    }
 | 
						||
  },
 | 
						||
  created() {
 | 
						||
    getNameRule({
 | 
						||
      page: 1,
 | 
						||
      rows: 9999
 | 
						||
    }).then((data) => {
 | 
						||
      if (data.success) {
 | 
						||
        this.nameruleList = data.data.rows
 | 
						||
      }
 | 
						||
    })
 | 
						||
    this.getAllPool()
 | 
						||
    // 获取云平台类型
 | 
						||
    conditionService('server').then((data) => {
 | 
						||
      if (data.success) {
 | 
						||
        this.vendorTypelist = data.data
 | 
						||
      }
 | 
						||
    })
 | 
						||
  },
 | 
						||
  methods: {
 | 
						||
    getGraphConfigLabel(row) {
 | 
						||
      const task = this.addData.tasks.find(({ taskGroupUuid }) => taskGroupUuid === row.taskGroupUuid)
 | 
						||
      if (!task) return '配置'
 | 
						||
      return `${task.taskName}`
 | 
						||
    },
 | 
						||
    openGraph(row) {
 | 
						||
      if (!this.addData.location.name) return this.$message.error('请填写业务信息')
 | 
						||
      if (!row.configs.vmHostName) return this.$message.error('请填写主机名')
 | 
						||
      if (!row.configs.password) return this.$message.error('请填写密码')
 | 
						||
      this.$emit('handleUpdateParamList')
 | 
						||
      this.graphDialog.visible = true
 | 
						||
      this.graphDialog.task = cloneDeep(
 | 
						||
        this.addData.tasks.find((item) => item.taskGroupUuid === row.taskGroupUuid) || {
 | 
						||
          ...defaultTask,
 | 
						||
          taskGroupUuid: row.taskGroupUuid
 | 
						||
        }
 | 
						||
      )
 | 
						||
      this.graphDialog.cb = () => {
 | 
						||
        this.graphDialog.task.taskName = `${this.addData.location.name}_${this.graphDialog.task.name}`
 | 
						||
        const findIndex = this.addData.tasks.findIndex((item) => item.taskGroupUuid === row.taskGroupUuid)
 | 
						||
        if (findIndex > -1) {
 | 
						||
          this.addData.tasks[findIndex] = this.graphDialog.task
 | 
						||
        } else {
 | 
						||
          this.addData.tasks.push(this.graphDialog.task)
 | 
						||
        }
 | 
						||
      }
 | 
						||
    },
 | 
						||
    changeCpu(val, row) {
 | 
						||
      row.elements[0].specs[0].cpu = val
 | 
						||
    },
 | 
						||
    changeMemory(val, row) {
 | 
						||
      row.elements[0].specs[1].memory = val
 | 
						||
    },
 | 
						||
    // 将磁盘配置保存到 elements 中
 | 
						||
    async changeDisk(index = -1, row) {
 | 
						||
      row = index > -1 ? this.showParamList[index] : row
 | 
						||
      // 系统盘
 | 
						||
      row.elements[1].specs = [{ disk: row.configs.sysDisk.disk }]
 | 
						||
      row.configs.sysDisk.categoryId = row.elements[1].categoryId
 | 
						||
      // 数据盘
 | 
						||
      row.elements.splice(2)
 | 
						||
      if (row.configs.addDiskList.length) {
 | 
						||
        const dataDiskElementClone = cloneDeep(row.elements[1])
 | 
						||
        row.configs.addDiskList.map((item) => {
 | 
						||
          row.elements.push({ ...dataDiskElementClone, specs: [{ disk: item.disk }] })
 | 
						||
        })
 | 
						||
      }
 | 
						||
      // 设置各个云平台的默认磁盘配置
 | 
						||
      const vendorType = row.subLocation.vendorType
 | 
						||
      const vendorId = row.subLocation.vendorId
 | 
						||
      switch (vendorType) {
 | 
						||
        case 'VMWARE':
 | 
						||
          row.configs.sysDisk.diskType = 'thin'
 | 
						||
          // 设置磁盘默认数据
 | 
						||
          row.configs.addDiskList.forEach((item) => {
 | 
						||
            item.createLvm = false
 | 
						||
            item.diskType = 'thin'
 | 
						||
            item.fileSystem = 'ext3'
 | 
						||
            item.forceMount = false
 | 
						||
          })
 | 
						||
          break
 | 
						||
        case 'INSPURRAIL':
 | 
						||
          row.configs.sysDisk.diskType = 'thin'
 | 
						||
          // 设置磁盘默认数据
 | 
						||
          row.configs.addDiskList.forEach((item) => {
 | 
						||
            item.createLvm = false
 | 
						||
            item.diskType = 'thin'
 | 
						||
            item.fileSystem = 'ext3'
 | 
						||
            item.forceMount = false
 | 
						||
          })
 | 
						||
          break
 | 
						||
        case 'CLOUDTOWER':
 | 
						||
          const busData = [
 | 
						||
            { name: 'VIRTIO', value: 'VIRTIO' },
 | 
						||
            { name: 'SCSI', value: 'SCSI' },
 | 
						||
            { name: 'IDE', value: 'IDE' }
 | 
						||
          ]
 | 
						||
          // 设置磁盘默认数据
 | 
						||
          if (!this.CTDataDisktemplateData.length || this.CTDataDisktemplateData[0].vendorId !== vendorId) {
 | 
						||
            await getVolumeTpl({
 | 
						||
              simple: true,
 | 
						||
              params: this.$tools.handleSearchParam({ vendorId })
 | 
						||
            }).then((data) => {
 | 
						||
              if (data.success) {
 | 
						||
                if (!data.data.rows.length) {
 | 
						||
                  row.configs.addDiskList = []
 | 
						||
                  return this.$message.error('获取CLOUDTOWER存储策略数据为空,请联系管理员')
 | 
						||
                }
 | 
						||
                this.CTDataDisktemplateData = data.data.rows
 | 
						||
              }
 | 
						||
            })
 | 
						||
          }
 | 
						||
          row.configs.addDiskList.forEach((item) => {
 | 
						||
            item.type = 'newDisk'
 | 
						||
            item.bus = busData[0].value
 | 
						||
            item.volumeTemplateId = this.CTDataDisktemplateData[0].id // 存储策略
 | 
						||
            item.volumeTemplateName = this.CTDataDisktemplateData[0].name
 | 
						||
          })
 | 
						||
          break
 | 
						||
        case 'CECSTACK':
 | 
						||
          row.configs.addDiskList.forEach((item) => {
 | 
						||
            item.deleteWithInstance = true
 | 
						||
            item.bootVolume = false
 | 
						||
            // item.size = ''
 | 
						||
            // item.volumeType = ''
 | 
						||
            item.volumeUnit = 'GiB'
 | 
						||
            // item.bootIndex = ''
 | 
						||
          })
 | 
						||
          break
 | 
						||
        default:
 | 
						||
          break
 | 
						||
      }
 | 
						||
    },
 | 
						||
    handleClone(row = subApplicationParam) {
 | 
						||
      const cloneData = cloneDeep(row)
 | 
						||
      cloneData.taskGroupUuid = GEN_UUID()
 | 
						||
      cloneData.taskTargetUuid = GEN_UUID()
 | 
						||
      cloneData.configs.name = ''
 | 
						||
      this.showParamList.push(cloneData)
 | 
						||
    },
 | 
						||
    handleSub(index) {
 | 
						||
      this.showParamList.splice(index, 1)
 | 
						||
    },
 | 
						||
    handleOpenNetworkDialog(row, index) {
 | 
						||
      this.addNetworkDialog.visible = true
 | 
						||
      this.addNetworkDialog.row = row
 | 
						||
      this.addNetworkDialog.cb = () => {
 | 
						||
        this.$refs.addForm && this.$refs.addForm.$refs.formRef.validateField(`showParamList.${index}`)
 | 
						||
      }
 | 
						||
    },
 | 
						||
    getDiskTotalSize(row) {
 | 
						||
      return row.configs.sysDisk.disk + row.configs.addDiskList.reduce((pre, cur) => pre + cur.disk, 0)
 | 
						||
    },
 | 
						||
    handleOpenDiskDialog(row, index) {
 | 
						||
      this.addDiskDialog.visible = true
 | 
						||
      this.addDiskDialog.row = row
 | 
						||
      this.addDiskDialog.index = index
 | 
						||
    },
 | 
						||
    getOsList(row) {
 | 
						||
      if (!row.subLocation.vendorId) return
 | 
						||
      let params
 | 
						||
      if (row.subLocation.vendorType === 'CECSTACK') {
 | 
						||
        params = {
 | 
						||
          condition: 'listByImageType',
 | 
						||
          vendorId: row.subLocation.vendorId,
 | 
						||
          status: 'ACTIVE',
 | 
						||
          tenantId: 0,
 | 
						||
          regionId: row.subLocation.region,
 | 
						||
          imageType: 'PUBLIC'
 | 
						||
        }
 | 
						||
      } else {
 | 
						||
        params = {
 | 
						||
          condition: 'listTenantImages',
 | 
						||
          vendorId: row.subLocation.vendorId
 | 
						||
        }
 | 
						||
        if (row.subLocation.vendorType === 'SANGFOR') {
 | 
						||
          if (!row.subLocation.azUuid) return
 | 
						||
          params.azUuid = row.subLocation.azUuid
 | 
						||
        }
 | 
						||
      }
 | 
						||
      conditionImage(params).then((data) => {
 | 
						||
        if (data.success) {
 | 
						||
          row.imageData = data.data
 | 
						||
          this.$set(row, 'osList', Object.keys(row.imageData))
 | 
						||
          if (!this.itemData) {
 | 
						||
            row.configs.osCategory = row.osList[0]
 | 
						||
            row.configs.osVersion = ''
 | 
						||
          }
 | 
						||
          this.$set(row, 'versionList', [])
 | 
						||
          this.getVersionList(row, !row.configs.osVersion)
 | 
						||
        }
 | 
						||
      })
 | 
						||
      return row.osList
 | 
						||
    },
 | 
						||
 | 
						||
    getVersionList(row, flag) {
 | 
						||
      if (row.subLocation.vendorType === 'CECSTACK') {
 | 
						||
        row.versionList = row.imageData[row.configs.osCategory].map(({ name }) => name)
 | 
						||
      } else {
 | 
						||
        row.versionList = Object.keys(row.imageData[row.configs.osCategory])
 | 
						||
      }
 | 
						||
      if (flag) {
 | 
						||
        row.configs.osVersion = row.versionList[0]
 | 
						||
        this.getImage(row, true)
 | 
						||
      } else {
 | 
						||
        this.getImage(row, false)
 | 
						||
      }
 | 
						||
    },
 | 
						||
    getImage(row, flag) {
 | 
						||
      let obj
 | 
						||
      if (row.subLocation.vendorType === 'CECSTACK') {
 | 
						||
        obj = row.imageData[row.configs.osCategory].find(({ name }) => row.configs.osVersion === name)
 | 
						||
      } else {
 | 
						||
        obj = row.imageData[row.configs.osCategory][row.configs.osVersion]?.[0]
 | 
						||
      }
 | 
						||
      if (!obj) return
 | 
						||
      if (flag) {
 | 
						||
        row.configs.imageId = obj.id
 | 
						||
        // 电子云为 minDisk
 | 
						||
        // cloudtower 镜像中可能没系统盘大小
 | 
						||
        row.configs.templateDisk = obj.templateDisk || obj.minDisk || 50
 | 
						||
        row.configs.templateRam = obj.minRam / 1024 || 1
 | 
						||
        if (row.configs.memory < row.configs.templateRam) row.configs.memory = row.configs.templateRam
 | 
						||
        row.configs.sysDisk.disk = row.configs.templateDisk
 | 
						||
      }
 | 
						||
      if (row.subLocation.vendorType == 'CECSTACK') {
 | 
						||
        if (flag) {
 | 
						||
          this.$set(row.configs, 'networkCardConfigs', [
 | 
						||
            {
 | 
						||
              networkId: obj.networkId,
 | 
						||
              ipPolicy: 'Auto',
 | 
						||
              ipPoolId: '',
 | 
						||
              ipPoolName: '',
 | 
						||
              address: [],
 | 
						||
              portGroupId: '',
 | 
						||
              loading: false,
 | 
						||
              // 电子云特殊参数
 | 
						||
              ...CECSTACK_DEFAULT_CONFIG
 | 
						||
            }
 | 
						||
          ])
 | 
						||
        }
 | 
						||
      } else if (row.subLocation.vendorType == 'SANGFOR') {
 | 
						||
        row.configs.vmUuid = obj.imageUuid
 | 
						||
        if (flag && row.configs.networkCardConfigs.length == 0) {
 | 
						||
          const hardware_status = JSON.parse(obj.detail || '{}')?.hardware_status
 | 
						||
          if (!hardware_status) return this.$message.error('镜像缺少网卡,请检查镜像是否正确')
 | 
						||
          const configs = transformNetworkConfig(hardware_status).map((item) => ({
 | 
						||
            ipPolicy: 'Auto',
 | 
						||
            ipPoolId: '',
 | 
						||
            ipPoolName: '',
 | 
						||
            address: [],
 | 
						||
            portGroupId: '',
 | 
						||
            loading: false
 | 
						||
          }))
 | 
						||
          this.$set(row.configs, 'networkCardConfigs', configs)
 | 
						||
        }
 | 
						||
      } else if (row.subLocation.vendorType == 'VMWARE') {
 | 
						||
        if (flag && (row.configs.networkCardConfigs.length == 0 || row.configs.networkCardConfigs[0].networkCardId != obj.networkCards?.[0]?.id)) {
 | 
						||
          if (!obj.networkCards?.length) this.$message.error('镜像缺少网卡,请检查镜像是否正确')
 | 
						||
          this.$set(
 | 
						||
            row.configs,
 | 
						||
            'networkCardConfigs',
 | 
						||
            obj.networkCards.map((item) => {
 | 
						||
              return {
 | 
						||
                networkCardId: item.id,
 | 
						||
                ipPolicy: 'Auto',
 | 
						||
                ipPoolId: '',
 | 
						||
                ipPoolName: '',
 | 
						||
                address: [],
 | 
						||
                portGroupId: '',
 | 
						||
                loading: false
 | 
						||
              }
 | 
						||
            })
 | 
						||
          )
 | 
						||
        }
 | 
						||
      } else if (row.subLocation.vendorType == 'INSPURRAIL') {
 | 
						||
        if (flag) {
 | 
						||
          if (row.configs.networkCardConfigs.length == 0 || row.configs.networkCardConfigs[0].networkCardId != obj.networkCards?.[0]?.id) {
 | 
						||
            if (!obj.networkCards?.length) this.$message.error('镜像缺少网卡,请检查镜像是否正确')
 | 
						||
            this.$set(
 | 
						||
              row.configs,
 | 
						||
              'networkCardConfigs',
 | 
						||
              obj.networkCards.map((item) => {
 | 
						||
                return {
 | 
						||
                  networkCardId: item.id,
 | 
						||
                  ipPolicy: 'Auto',
 | 
						||
                  ipPoolId: '',
 | 
						||
                  ipPoolName: '',
 | 
						||
                  address: [],
 | 
						||
                  portGroupId: '',
 | 
						||
                  loading: false
 | 
						||
                }
 | 
						||
              })
 | 
						||
            )
 | 
						||
          }
 | 
						||
          // 模板里面的磁盘配置
 | 
						||
          const volumes = obj.volumes || []
 | 
						||
          volumes.map(({ bootVolume, size }) => {
 | 
						||
            if (bootVolume) {
 | 
						||
              row.configs.templateDisk = size
 | 
						||
              row.configs.sysDisk.disk = size
 | 
						||
            } else {
 | 
						||
              row.configs.addDiskList.push({ disk: size, templateDisk: size })
 | 
						||
            }
 | 
						||
          })
 | 
						||
        }
 | 
						||
      } else if (row.subLocation.vendorType == 'CLOUDTOWER') {
 | 
						||
        if (flag) {
 | 
						||
          this.$set(row.configs, 'networkCardConfigs', [
 | 
						||
            {
 | 
						||
              networkId: obj.networkId,
 | 
						||
              ipPolicy: 'Auto',
 | 
						||
              ipPoolId: '',
 | 
						||
              ipPoolName: '',
 | 
						||
              address: [],
 | 
						||
              portGroupId: '',
 | 
						||
              loading: false,
 | 
						||
              // CLOUDTOWER特殊参数
 | 
						||
              ...CLOUDTOWER_DEFAULT_CONFIG
 | 
						||
            }
 | 
						||
          ])
 | 
						||
        }
 | 
						||
      } else if (row.subLocation.vendorType === 'CNWARE') {
 | 
						||
        const res = JSON.parse(obj.configuration).devices.map((d) => {
 | 
						||
          if (d.capacity) {
 | 
						||
            const size = Number(d.capacity) / 1024 / 1024 / 1024
 | 
						||
            // 系统盘大小
 | 
						||
            row.configs.templateDisk = size
 | 
						||
          }
 | 
						||
          return {
 | 
						||
            ...d
 | 
						||
          }
 | 
						||
        })
 | 
						||
        if (flag) {
 | 
						||
          row.configs.sysDiskList = res.flat()
 | 
						||
        }
 | 
						||
      }
 | 
						||
      // 设置硬盘参数
 | 
						||
      if (flag) this.changeDisk(-1, row)
 | 
						||
    },
 | 
						||
    setVendor(row, manual = true) {
 | 
						||
      const findVendorList = this.getVendorListByRegionZone(row)
 | 
						||
      const pool = findVendorList.filter((item) => item.vendorId == row.subLocation.vendorId)[0]
 | 
						||
      if (!pool) return this.$message.error('当前可用区下不存在资源池')
 | 
						||
      console.log('当前资源池:', pool.vendorType, pool.name)
 | 
						||
      const { vendorType, id, azUuid } = pool
 | 
						||
      row.subLocation.poolGroupId = id
 | 
						||
      row.subLocation.azUuid = azUuid
 | 
						||
      row.subLocation.vendorType = vendorType
 | 
						||
      row.networkRelations = pool.networkRelations || []
 | 
						||
      row.service = ServiceCodeMap.server[vendorType]
 | 
						||
      row.elements.map((item, index) => {
 | 
						||
        item.serviceCode = index === 0 ? ServiceCodeMap.server[vendorType] : ServiceCodeMap.disk[vendorType]
 | 
						||
      })
 | 
						||
      switch (vendorType) {
 | 
						||
        case 'VMWARE':
 | 
						||
          row.configs.isAddShterm = false
 | 
						||
          break
 | 
						||
        case 'INSPURRAIL':
 | 
						||
          row.configs.isAddShterm = false
 | 
						||
          break
 | 
						||
        case 'CLOUDTOWER':
 | 
						||
          row.configs.serverId = 0
 | 
						||
          row.configs.ha = true
 | 
						||
          row.configs.firmware = 'BIOS'
 | 
						||
          row.configs.status = 'running'
 | 
						||
          row.configs.fullClone = false
 | 
						||
          break
 | 
						||
        default:
 | 
						||
          break
 | 
						||
      }
 | 
						||
      // 根据云平台类型设置默认磁盘配置
 | 
						||
      this.changeDisk(-1, row)
 | 
						||
      this.getData(row, manual)
 | 
						||
    },
 | 
						||
    getData(row, manual = true) {
 | 
						||
      // 加载服务目录
 | 
						||
      if (!this.itemData || !this.disabled) this.loadSku(row)
 | 
						||
      this.getOsList(row)
 | 
						||
    },
 | 
						||
 | 
						||
    getZoneListByRegion(row) {
 | 
						||
      const filterPools = this.allPoolList.filter((item) => item.region == row.subLocation.region)
 | 
						||
      if (!filterPools.length) return []
 | 
						||
      // 同一地域下可能会存在重复可用区,去重处理
 | 
						||
      if (filterPools[0].vendorType === 'CECSTACK') {
 | 
						||
        const formatPools = []
 | 
						||
        // 将多个可用区扁平化放在资源池中
 | 
						||
        filterPools.map((pool) => {
 | 
						||
          pool.regionRelations.map((zone) => {
 | 
						||
            formatPools.push({ ...pool, availablitiyZone: zone.zoneId, azName: zone.zoneName })
 | 
						||
          })
 | 
						||
        })
 | 
						||
        return uniqBy(formatPools, 'availablitiyZone')
 | 
						||
      } else {
 | 
						||
        return uniqBy(filterPools, 'availablitiyZone')
 | 
						||
      }
 | 
						||
    },
 | 
						||
    getVendorListByRegionZone(row) {
 | 
						||
      const zoneList = this.getZoneListByRegion(row)
 | 
						||
      return zoneList.filter((item) => item.availablitiyZone == row.subLocation.az)
 | 
						||
    },
 | 
						||
    // 地域变化
 | 
						||
    // manual 手动切换
 | 
						||
    changeRegion(row, manual = true) {
 | 
						||
      this.setZone(row, manual)
 | 
						||
    },
 | 
						||
    // 设置可用区
 | 
						||
    setZone(row, manual = true) {
 | 
						||
      const zoneList = this.getZoneListByRegion(row)
 | 
						||
      // 手动切换才需要置空
 | 
						||
      if (manual && !zoneList.length) return (row.subLocation.az = '')
 | 
						||
      if (!zoneList.find((item) => item.availablitiyZone === row.subLocation.az)) {
 | 
						||
        row.subLocation.az = zoneList[0].availablitiyZone
 | 
						||
      }
 | 
						||
      this.changeAz(row, manual)
 | 
						||
    },
 | 
						||
    // 可用区改变
 | 
						||
    changeAz(row, manual = true) {
 | 
						||
      const findVendorList = this.getVendorListByRegionZone(row)
 | 
						||
      if (manual && !findVendorList.length) return (row.subLocation.vendorId = '')
 | 
						||
      if (!findVendorList.find((item) => item.vendorId === row.subLocation.vendorId)) {
 | 
						||
        row.subLocation.vendorId = findVendorList[0].vendorId
 | 
						||
      }
 | 
						||
      this.setVendor(row, manual)
 | 
						||
    },
 | 
						||
    // 所有资源池
 | 
						||
    async getAllPool() {
 | 
						||
      const res = await getPoolCondition({
 | 
						||
        condition: 'listPoolGroups',
 | 
						||
        tenantId: this.addData.location.tenantId
 | 
						||
      })
 | 
						||
      if (!res.success) return
 | 
						||
      this.allPoolList = res.data
 | 
						||
      if (!this.regionList.length) return
 | 
						||
      const [{ region }] = this.regionList
 | 
						||
      if (!this.showParamList[0].subLocation.region) {
 | 
						||
        this.showParamList[0].subLocation.region = region
 | 
						||
      }
 | 
						||
      this.changeRegion(this.showParamList[0], false)
 | 
						||
    },
 | 
						||
    validateNetworkCard(param) {
 | 
						||
      const res = {
 | 
						||
        valid: true,
 | 
						||
        message: ''
 | 
						||
      }
 | 
						||
      let flag = false
 | 
						||
      let addressLenFlag = false
 | 
						||
      let cecstackFlag = false
 | 
						||
      param.configs.networkCardConfigs.forEach((item) => {
 | 
						||
        if (!item.ipPoolId || (item.ipPolicy == 'Manual' && item.address.length == 0) || (item.checkIpv6 && (!item.ipv6PoolId || (item.ipv6Policy == 'Manual' && item.ipv6Address.length == 0)))) flag = true
 | 
						||
        if (item.ipPolicy == 'Manual' && item.address.length !== param.emption.count) {
 | 
						||
          addressLenFlag = true
 | 
						||
        }
 | 
						||
        if (param.subLocation.vendorType === 'CECSTACK' && !(item.vpcUuid || item.subnetUuid)) {
 | 
						||
          cecstackFlag = true
 | 
						||
        }
 | 
						||
      })
 | 
						||
      if (flag) {
 | 
						||
        res.valid = false
 | 
						||
        res.message = '网卡信息配置不完善'
 | 
						||
        return res
 | 
						||
      }
 | 
						||
      if (addressLenFlag) {
 | 
						||
        res.valid = false
 | 
						||
        res.message = 'IP数量与云主机订购数量不一致'
 | 
						||
        return res
 | 
						||
      }
 | 
						||
      if (cecstackFlag) {
 | 
						||
        res.valid = false
 | 
						||
        res.message = '缺少 VPC 和子网信息'
 | 
						||
      }
 | 
						||
      return res
 | 
						||
    },
 | 
						||
    getParams() {
 | 
						||
      let data = false
 | 
						||
      this.$refs.addForm.validate((valid) => {
 | 
						||
        if (valid) {
 | 
						||
          const result = []
 | 
						||
          this.showParamList.forEach((param) => {
 | 
						||
            const validateNetworkCard = this.validateNetworkCard(param)
 | 
						||
            if (!validateNetworkCard.valid) {
 | 
						||
              data = false
 | 
						||
              return
 | 
						||
            }
 | 
						||
            const { password, confirm_password, ...other } = param.configs
 | 
						||
            if (password !== confirm_password) {
 | 
						||
              data = false
 | 
						||
              this.$message.error('两次密码输入不一致')
 | 
						||
              return
 | 
						||
            }
 | 
						||
            result.push({
 | 
						||
              ...other,
 | 
						||
              categoryId: param.elements[0].categoryId
 | 
						||
            })
 | 
						||
          })
 | 
						||
          if (result.length === this.showParamList.length) data = result
 | 
						||
        } else {
 | 
						||
          this.$message.error('虚拟机信息未填写完整')
 | 
						||
        }
 | 
						||
      })
 | 
						||
      return data
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
</script>
 | 
						||
 | 
						||
<style scoped lang="scss"></style>
 |