NetworkConfig.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. <template>
  2. <div class="network-config">
  3. <!-- 适配大、小屏幕 -->
  4. <div class="mb-2" :class="{ 'd-flex align-items-start' : isBigScreen && !isDialog }" v-for="(item, i) in networkList" :key="item.key">
  5. <div class="d-flex">
  6. <a-tag color="blue" class="mr-1" style="height: 20px; margin-top: 10px;">{{ isBonding ? 'bond' : $t('compute.text_193')}}{{i + count}}</a-tag>
  7. <a-form-item
  8. v-show="showVpc"
  9. :wrapperCol="{ span: 24 }"
  10. class="mb-0 mr-1">
  11. <oc-select
  12. v-if="i === 0"
  13. v-decorator="decorator.vpcs(item.key)"
  14. show-status
  15. show-group
  16. :status-desc="$t('compute.vpc_status_desc')"
  17. :resource="vpcResource"
  18. :formatter="vpcFormatter"
  19. :params="vpcParams"
  20. :mapper="vpcResourceMapper"
  21. :sort="(arr) => arr.sort((a, b) => a.network_count > b.network_count ? -1 : 1)"
  22. :placeholder="$t('compute.text_194')"
  23. @selectChange="(curObjArr) => vpcSelectChange(curObjArr, i, item)"
  24. @fetchSuccess="(data) => fetchVpcSuccessHandle(data, item)" />
  25. <a-tag v-else color="blue" class="w-100 mr-1">{{ getVpcTag(networkList[0].vpc) }}</a-tag>
  26. </a-form-item>
  27. <a-form-item
  28. :wrapperCol="{ span: 24 }"
  29. :style="isDialog ? { flex: 1 } : ''"
  30. class="mb-0 mr-1 network-item">
  31. <base-select
  32. class="w-100"
  33. v-decorator="decorator.networks(item.key)"
  34. resource="networks"
  35. remote
  36. show-sync
  37. :item.sync="item.network"
  38. :isDefaultSelect="canDefaultSelect && i === 0"
  39. :need-params="true"
  40. :params="{ ...networkParamsC, $t: item.key }"
  41. :mapper="networkResourceMapper"
  42. :remote-fn="q => ({ search: q })"
  43. :beforeDefaultSelectCallBack="beforeDefaultSelectCallBack"
  44. @change="v => networkChange(v, item, i)"
  45. :select-props="{ allowClear: true, placeholder: $t('compute.text_195') }"
  46. :min-width="isDialog ? '200px' : '500px'" />
  47. <div slot="extra" v-if="i === 0">{{$t('compute.text_196')}}<help-link href="/network2">{{$t('compute.perform_create')}}</help-link>
  48. </div>
  49. </a-form-item>
  50. </div>
  51. <div :class="{ 'd-flex ml-1' : isBigScreen && !isDialog }">
  52. <!-- 高级 -->
  53. <template v-if="showAdvanced">
  54. <!-- ip -->
  55. <template v-if="isSupportIPv4(item) && !(isSupportIPv6(item) && item.ipv6Mode === 'only' && item.requireIpv6)">
  56. <template v-if="item.ipShow">
  57. <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
  58. <ip-select v-decorator="decorator.ips(item.key, item.network)" :value="item.ip" :network="item.network" @change="e => ipChange(e, i)" />
  59. </a-form-item>
  60. <a-button type="link" class="mt-1" @click="triggerShowIp(item)">{{$t('compute.text_135')}}</a-button>
  61. </template>
  62. <a-tooltip v-else :title="ipBtnTooltip">
  63. <a-button type="link" class="mr-1 mt-1" :disabled="ipsDisabled" @click="triggerShowIp(item)">{{$t('compute.text_198')}}</a-button>
  64. </a-tooltip>
  65. </template>
  66. <!-- mac -->
  67. <template v-if="showMacConfig">
  68. <template v-if="item.macShow">
  69. <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
  70. <a-input
  71. style="width: 164px"
  72. :placeholder="$t('compute.text_806')"
  73. @change="e => macChange(e, i)"
  74. v-decorator="decorator.macs(item.key, item.network)" />
  75. </a-form-item>
  76. <a-button type="link" class="mt-1" @click="triggerShowMac(item)">{{$t('compute.text_135')}}</a-button>
  77. </template>
  78. <a-tooltip v-else :title="ipBtnTooltip">
  79. <a-button type="link" class="mr-1 mt-1" :disabled="ipsDisabled" @click="triggerShowMac(item)">{{$t('compute.mac_config')}}</a-button>
  80. </a-tooltip>
  81. </template>
  82. <!-- 透传设备 -->
  83. <template v-if="showDeviceConfig">
  84. <template v-if="item.deviceShow">
  85. <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
  86. <oc-select
  87. style="width: 164px"
  88. v-decorator="decorator.devices(item.key)"
  89. :data="gpuOptions"
  90. :placeholder="$t('compute.sriov_device_tips')" />
  91. </a-form-item>
  92. <a-button type="link" class="mt-1" @click="triggerShowDevice(item)">{{$t('compute.text_135')}}</a-button>
  93. </template>
  94. <a-button v-else type="link" class="mr-1 mt-1" @click="triggerShowDevice(item)">{{ $t('compute.config_transparent_net') }}</a-button>
  95. </template>
  96. <!-- 安全组 -->
  97. <template v-if="showSecgroupConfig">
  98. <template v-if="item.secgroupShow">
  99. <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
  100. <base-select
  101. v-decorator="decorator.secgroups(item.key)"
  102. resource="secgroups"
  103. :params="secgroupParams"
  104. :select-props="{ allowClear: true, placeholder: $t('compute.secgroup_tips'), mode: 'multiple' }" />
  105. </a-form-item>
  106. </template>
  107. <a-button v-else type="link" class="mr-1 mt-1" @click="triggerShowSecgroup(item)">{{ $t('compute.config_secgroup') }}</a-button>
  108. </template>
  109. <!-- ipv6 -->
  110. <template>
  111. <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }" v-if="isSupportIPv6(item) && isSupportIPv4(item)">
  112. <div class="d-flex align-items-center">
  113. <a-checkbox style="width: max-content" v-decorator="decorator.ipv6s(item.key, item.network)" @change="(e) => triggerRequireIpv6(item, e)" />
  114. <a-dropdown>
  115. <a-menu slot="overlay" @click="(e) => triggerIpv6Mode(item, e, i)" v-decorator="decorator.ipv6_mode(item.key, item.network)">
  116. <a-menu-item key="all">{{ $t('compute.server_create.require_ipv6_all') }}</a-menu-item>
  117. <a-menu-item key="only">{{ $t('compute.server_create.require_ipv6_only') }}</a-menu-item>
  118. </a-menu>
  119. <a-button type="link" class="pl-1">{{ item.ipv6Mode === 'only' ? $t('compute.server_create.require_ipv6_only') : $t('compute.server_create.require_ipv6_all') }}<a-icon type="down" /> </a-button>
  120. </a-dropdown>
  121. </div>
  122. </a-form-item>
  123. <template v-if="(isSupportIPv6(item) && item.requireIpv6) || (!isSupportIPv4(item) && isSupportIPv6(item))">
  124. <template v-if="item.ipv6Show">
  125. <a-form-item class="mb-0 ml-1" style="width: 350px;display:inline-block" :wrapperCol="{ span: 24 }">
  126. <span class="mr-1">{{ getIpv6Prefix(item.network?.guest_ip6_start) }}</span>
  127. <a-form-item class="mb-0" style="display:inline-block">
  128. <a-input
  129. style="width: 164px"
  130. :placeholder="$t('compute.complete_ipv6_address')"
  131. @change="e => ipv6Change(e, i)"
  132. v-decorator="decorator.ips6(item.key, item.network)" />
  133. </a-form-item>
  134. <a-button type="link" class="mt-1" @click="triggerShowIpv6(item)">{{$t('compute.text_135')}}</a-button>
  135. </a-form-item>
  136. </template>
  137. <a-button v-else type="link" class="mt-1" @click="triggerShowIpv6(item)">{{$t('compute.ipv6_config')}}</a-button>
  138. </template>
  139. </template>
  140. </template>
  141. <a-button class="mt-1" type="link" @click="() => showAdvanced = !showAdvanced">{{ showAdvanced ? $t('compute.hide_advanced') : $t('compute.advanced') }}</a-button>
  142. <a-button shape="circle" icon="minus" size="small" v-if="i !== 0" @click="decrease(item.key, i)" class="mt-2" />
  143. </div>
  144. </div>
  145. <div class="d-flex align-items-center" v-if="networkCountRemaining > 0">
  146. <a-button type="primary" shape="circle" icon="plus" size="small" @click="add" />
  147. <a-button type="link" @click="add">{{$t('compute.text_199')}}</a-button>
  148. <span class="network-count-tips">{{$t('compute.text_130')}}<span class="remain-num">{{ networkCountRemaining }}</span>{{$t('compute.text_200')}}</span>
  149. </div>
  150. </div>
  151. </template>
  152. <script>
  153. import * as R from 'ramda'
  154. import ipaddr from 'ipaddr.js'
  155. import { uuid } from '@/utils/utils'
  156. import IpSelect from './IpSelect.vue'
  157. export default {
  158. name: 'NetworkConfig',
  159. components: {
  160. IpSelect,
  161. },
  162. props: {
  163. count: {
  164. type: Number,
  165. default: () => 0,
  166. },
  167. networkParams: {
  168. type: Object,
  169. required: true,
  170. },
  171. limit: {
  172. type: Number,
  173. default: 8, // 默认支持最多 8 个ip子网
  174. },
  175. form: {
  176. type: Object,
  177. required: true,
  178. },
  179. decorator: {
  180. type: Object,
  181. required: true,
  182. validator: val => R.is(Function, val.vpcs) && R.is(Function, val.networks) && R.is(Function, val.ips) && R.is(Function, val.macs) && R.is(Function, val.ips6) && R.is(Function, val.secgroups),
  183. },
  184. isBonding: {
  185. type: Boolean,
  186. default: false,
  187. },
  188. networkResourceMapper: {
  189. type: Function,
  190. default: (data) => { return data },
  191. },
  192. vpcParams: {
  193. type: Object,
  194. required: true,
  195. },
  196. vpcResource: {
  197. type: String,
  198. default: 'vpcs', // 还可能是这样的resource cloudregions/{region_id}/vpcs
  199. },
  200. vpcResourceMapper: {
  201. type: Function,
  202. default: data => { return data },
  203. },
  204. vpcObj: {
  205. type: Object,
  206. validator: val => val.id && val.name,
  207. },
  208. ipsDisable: {
  209. type: Boolean,
  210. default: false,
  211. },
  212. showVpc: {
  213. type: Boolean,
  214. default: true,
  215. },
  216. isDialog: {
  217. type: Boolean,
  218. },
  219. showMacConfig: {
  220. type: Boolean,
  221. default: false,
  222. },
  223. showDeviceConfig: {
  224. type: Boolean,
  225. default: false,
  226. },
  227. showSecgroupConfig: {
  228. type: Boolean,
  229. default: false,
  230. },
  231. secgroupParams: {
  232. type: Object,
  233. default: () => ({}),
  234. },
  235. hiddenAdd: {
  236. type: Boolean,
  237. default: false,
  238. },
  239. },
  240. data () {
  241. return {
  242. networkList: [],
  243. ipsDisabled: this.ipsDisable,
  244. networkLoading: false,
  245. networkOpts: [],
  246. screenWidth: document.body.clientWidth,
  247. showAdvanced: false,
  248. canDefaultSelect: true,
  249. }
  250. },
  251. computed: {
  252. networkCountRemaining () {
  253. if (this.hiddenAdd) return 0
  254. return this.limit - this.networkList.length
  255. },
  256. networkParamsC () {
  257. if (!this.networkList[0]?.vpc?.id) return {}
  258. return {
  259. limit: 20,
  260. vpc: this.networkList[0].vpc?.id,
  261. ...this.networkParams,
  262. }
  263. },
  264. ipBtnTooltip () {
  265. return this.ipsDisabled ? this.$t('common_718') : null
  266. },
  267. gpuOptions () {
  268. const specs = this.form.fi.capability.specs || {}
  269. const data = specs.isolated_devices || {}
  270. const ret = []
  271. for (const key in data) {
  272. if (data.hasOwnProperty(key)) {
  273. const item = data[key]
  274. if (item.dev_type.startsWith('NIC')) {
  275. ret.push({
  276. ...item,
  277. key: `${item.model}`,
  278. label: `${item.model}`,
  279. })
  280. }
  281. }
  282. }
  283. return ret
  284. },
  285. isBigScreen () {
  286. return this.screenWidth > 1650
  287. },
  288. },
  289. watch: {
  290. vpcObj (val) {
  291. if (val && val.id && val.name) {
  292. this.$set(this.networkList[0], 'vpc', val)
  293. this.form.fc.setFieldsValue({
  294. [this.decorator.vpcs(this.networkList[0].key)[0]]: this.vpcObj.id,
  295. })
  296. }
  297. },
  298. networkList (val) {
  299. this.$set(this.form.fi, 'networkList', val)
  300. },
  301. },
  302. created () {
  303. this.add()
  304. },
  305. mounted () {
  306. window.addEventListener('resize', this.onResize)
  307. },
  308. beforeDestroy () {
  309. window.removeEventListener('resize', this.onResize)
  310. },
  311. methods: {
  312. initData (data) {
  313. this.canDefaultSelect = false
  314. this.networkList = data.map(item => {
  315. const obj = {
  316. ...item,
  317. key: uuid(),
  318. network: { id: item.network },
  319. vpc: { id: item.vpc },
  320. ipShow: !!item.address,
  321. ipv6Show: false,
  322. requireIpv6: false,
  323. ipv6Mode: 'all',
  324. macShow: false,
  325. deviceShow: false,
  326. secgroupShow: false,
  327. ip: item.address,
  328. }
  329. if (item.address) {
  330. obj.ipShow = true
  331. this.showAdvanced = true
  332. }
  333. if (item.mac) {
  334. obj.macShow = true
  335. this.showAdvanced = true
  336. }
  337. if (item.require_ipv6) {
  338. obj.requireIpv6 = true
  339. obj.ipv6Mode = 'all'
  340. this.showAdvanced = true
  341. }
  342. if (item.strict_ipv6 && item.require_ipv6) {
  343. obj.ipv6Mode = 'only'
  344. }
  345. if (item.address6) {
  346. obj.ipv6Show = true
  347. this.showAdvanced = true
  348. }
  349. if (item.sriov_device && item.sriov_device.model) {
  350. obj.deviceShow = true
  351. this.showAdvanced = true
  352. }
  353. if (item.secgroups && item.secgroups.length > 0) {
  354. obj.secgroupShow = true
  355. this.showAdvanced = true
  356. }
  357. return obj
  358. })
  359. this.$nextTick(() => {
  360. this.form.fc.setFieldsValue({
  361. [this.decorator.vpcs(this.networkList[0].key)[0]]: this.networkList[0].vpc.id,
  362. })
  363. for (const item of this.networkList) {
  364. const value = {}
  365. value[this.decorator.networks(item.key)[0]] = item.network.id
  366. if (item.address) {
  367. value[this.decorator.ips(item.key, item.network.id)[0]] = item.address
  368. }
  369. if (item.mac) {
  370. value[this.decorator.macs(item.key, item.network.id)[0]] = item.mac
  371. }
  372. value[this.decorator.ipv6s(item.key, item.network.id)[0]] = item.requireIpv6
  373. value[this.decorator.ipv6_mode(item.key, item.network.id)[0]] = item.ipv6Mode
  374. if (item.address6) {
  375. value[this.decorator.ips6(item.key, item.network.id)[0]] = item.address6.replace(this.getIpv6Prefix(item.address6), '')
  376. }
  377. if (item.sriov_device && item.sriov_device.model) {
  378. value[this.decorator.devices(item.key)[0]] = item.sriov_device.model
  379. }
  380. if (item.secgroups && item.secgroups.length > 0) {
  381. value[this.decorator.secgroups(item.key)[0]] = item.secgroups
  382. }
  383. setTimeout(() => {
  384. this.form.fc.setFieldsValue(value)
  385. }, 2000)
  386. setTimeout(() => {
  387. this.form.fc.setFieldsValue(value)
  388. }, 4000)
  389. }
  390. })
  391. },
  392. getVpcTag (data) {
  393. if (!data.cidr_block) return data.name
  394. return `${data.name}(${data.cidr_block})`
  395. },
  396. vpcLabelFormat (item) {
  397. if (!item.cidr_block) return item.name
  398. return `${item.name}(${item.account ? item.account + ', ' : ''}${item.cidr_block})`
  399. },
  400. vpcFormatter (v) {
  401. return { key: v.id, label: this.vpcLabelFormat(v), disabled: v.network_count === 0, ...v }
  402. },
  403. networkFormatter (v) {
  404. let addrLabel = `${v.guest_ip_start} - ${v.guest_ip_end}/${v.guest_ip_mask}`
  405. if (v.guest_ip6_start && v.guest_ip6_end) {
  406. addrLabel += `,${v.guest_ip6_start} - ${v.guest_ip6_end}/${v.guest_ip6_mask}`
  407. }
  408. return {
  409. key: v.id,
  410. label: `${v.name}(${addrLabel}, vlan=${v.vlan_id})`,
  411. rightLabel: `${this.$t('common.text00001')}: ${v.ports - v.ports_used}`,
  412. disabled: v.ports <= v.ports_used,
  413. ...v,
  414. }
  415. },
  416. ipChange (e, i) {
  417. this.networkList[i].ip = e
  418. },
  419. macChange (e, i) {
  420. this.networkList[i].mac = e.target.value
  421. },
  422. ipv6Change (e, i) {
  423. this.networkList[i].ipv6 = e.target.value
  424. },
  425. getIpv6Prefix (ipv6 = '') {
  426. if (ipv6) {
  427. const list = ipaddr.parse(ipv6).toNormalizedString().split(':')
  428. return list.slice(0, 4).join(':') + ':'
  429. }
  430. return ''
  431. },
  432. add () {
  433. const uid = uuid()
  434. const data = {
  435. network: {},
  436. vpc: {},
  437. ipShow: false,
  438. ipv6Show: false,
  439. requireIpv6: false,
  440. ipv6Mode: 'all',
  441. macShow: false,
  442. deviceShow: false,
  443. key: uid,
  444. ip: '',
  445. secgroupShow: false,
  446. }
  447. if (this.vpcObj) {
  448. data.vpc = this.vpcObj
  449. }
  450. this.networkList.push(data)
  451. this.$nextTick(() => {
  452. if (this.vpcObj) {
  453. this.form.fc.setFieldsValue({
  454. [this.decorator.vpcs(uid)[0]]: this.vpcObj.id,
  455. })
  456. }
  457. })
  458. },
  459. triggerShowIp (item, i) {
  460. item.ipShow = !item.ipShow
  461. },
  462. triggerShowIpv6 (item, i) {
  463. item.ipv6Show = !item.ipv6Show
  464. },
  465. triggerIpv6Mode (item, e, i) {
  466. item.ipv6Mode = e.key
  467. this.$nextTick(() => {
  468. this.form.fc.setFieldsValue({
  469. [`networkIPv6Modes[${item.key}]`]: e.key,
  470. })
  471. })
  472. },
  473. triggerRequireIpv6 (item, e) {
  474. item.requireIpv6 = e.target.checked
  475. },
  476. triggerShowMac (item, i) {
  477. item.macShow = !item.macShow
  478. },
  479. triggerShowDevice (item, i) {
  480. item.deviceShow = !item.deviceShow
  481. },
  482. triggerShowSecgroup (item, i) {
  483. item.secgroupShow = !item.secgroupShow
  484. },
  485. decrease (uid, index) {
  486. this.networkList.splice(index, 1)
  487. },
  488. reset (ipsDisabled) { // 重置成不可手动输入IP,并且仅保留1条数据
  489. if (this.networkList.length > 1) {
  490. const firstItem = this.networkList[0]
  491. this.networkList = [firstItem]
  492. }
  493. this.$set(this.networkList[0], 'ipShow', false)
  494. this.$set(this.networkList[0], 'ipv6Show', false)
  495. this.$set(this.networkList[0], 'requireIpv6', false)
  496. this.$set(this.networkList[0], 'ipv6Mode', 'all')
  497. this.$set(this.networkList[0], 'macShow', false)
  498. this.$set(this.networkList[0], 'deviceShow', false)
  499. this.$set(this.networkList[0], 'secgroupShow', false)
  500. this.ipsDisabled = ipsDisabled
  501. },
  502. networkChange (val, item, i) {
  503. this.$nextTick(() => {
  504. const fieldKey = `networkExits[${item.key}]`
  505. this.form.fc.getFieldDecorator(fieldKey, {
  506. preserve: true,
  507. })
  508. this.form.fc.setFieldsValue({
  509. [fieldKey]: item.network.exit,
  510. })
  511. if (item.network.guest_ip_start && item.network.guest_ip_end) {
  512. this.form.fc.setFieldsValue({
  513. [`networkIPv6Modes[${item.key}]`]: 'all',
  514. [`networkIPv6s[${item.key}]`]: false,
  515. })
  516. this.$set(this.networkList[i], 'ipv6Mode', 'all')
  517. this.$set(this.networkList[i], 'requireIpv6', false)
  518. }
  519. })
  520. },
  521. networkSelectChange (curObjArr, item) {
  522. item.network = curObjArr[0]
  523. this.$nextTick(() => {
  524. const fieldKey = `networkExits[${item.key}]`
  525. this.form.fc.getFieldDecorator(fieldKey, {
  526. preserve: true,
  527. })
  528. this.form.fc.setFieldsValue({
  529. [fieldKey]: item.network?.exit,
  530. })
  531. })
  532. },
  533. vpcChange (v, i) {
  534. this.$nextTick(() => {
  535. if (this.form.fi) {
  536. const networkVpcObj = this.networkList[i].vpc
  537. if (R.is(Object, networkVpcObj)) {
  538. this.$set(this.form.fi, 'networkVpcObj', networkVpcObj)
  539. }
  540. }
  541. })
  542. },
  543. vpcSelectChange (curObjArr, i, item) {
  544. item.vpc = curObjArr[0]
  545. this.$nextTick(() => {
  546. if (this.form.fi) {
  547. const networkVpcObj = item.vpc || {}
  548. if (R.is(Object, networkVpcObj)) {
  549. this.$set(this.form.fi, 'networkVpcObj', networkVpcObj)
  550. }
  551. }
  552. })
  553. },
  554. beforeDefaultSelectCallBack (data = []) {
  555. const cur = data[0]
  556. if (cur) {
  557. return cur.ports > cur.ports_used
  558. }
  559. return false
  560. },
  561. fetchVpcSuccessHandle (data = [], item) {
  562. let target = data[0] || {}
  563. if (item.vpc?.id && data.some(i => i.id === item.vpc?.id)) {
  564. target = data.filter(i => i.id === item.vpc?.id)[0]
  565. }
  566. item.vpc = target
  567. this.$nextTick(() => {
  568. this.form.fc.setFieldsValue({ [`vpcs[${item.key}]`]: target?.key, vpcs: { [item.key]: target?.key } })
  569. })
  570. this.vpcSelectChange([target], 0, item)
  571. // this.fetchNetworkOpts(this.networkParamsC, item)
  572. },
  573. fetchNetworkSuccessHandle (data, item) {
  574. item.network = data[0]
  575. this.$nextTick(() => {
  576. this.form.fc.setFieldsValue({ [`networks[${item.key}]`]: data?.[0]?.key })
  577. })
  578. },
  579. fetchNetworkOpts (params, item) {
  580. this.networkLoading = true
  581. // 未获取vpc时,network也不展示
  582. if (!params.vpc) {
  583. this.networkOpts = []
  584. item.network = {}
  585. this.form.fc.setFieldsValue({ [`networks[${item.key}]`]: '' })
  586. this.networkLoading = false
  587. } else {
  588. this.networkOpts = []
  589. new this.$Manager('networks').list({ params }).then((res) => {
  590. this.networkOpts = res.data.data || []
  591. this.$nextTick(() => {
  592. this.form.fc.setFieldsValue({ [`networks[${item.key}]`]: this.networkOpts?.[0]?.id })
  593. })
  594. item.network = this.networkOpts[0]
  595. this.networkLoading = false
  596. }).catch((err) => {
  597. this.networkLoading = false
  598. console.log(err)
  599. throw err
  600. })
  601. }
  602. },
  603. onResize () {
  604. this.screenWidth = document.body.clientWidth
  605. },
  606. isSupportIPv6 (item) {
  607. return !!item.network.guest_ip6_start && !!item.network.guest_ip6_end
  608. },
  609. isSupportIPv4 (item) {
  610. return !!item.network.guest_ip_start && !!item.network.guest_ip_end
  611. },
  612. },
  613. }
  614. </script>
  615. <style lang="less" scoped>
  616. @import '../../../../src/styles/less/theme';
  617. .network-config {
  618. .network-count-tips {
  619. .remain-num {
  620. color: @primary-color;
  621. }
  622. }
  623. }
  624. </style>