| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628 |
- <template>
- <div class="network-config">
- <!-- 适配大、小屏幕 -->
- <div class="mb-2" :class="{ 'd-flex align-items-start' : isBigScreen && !isDialog }" v-for="(item, i) in networkList" :key="item.key">
- <div class="d-flex">
- <a-tag color="blue" class="mr-1" style="height: 20px; margin-top: 10px;">{{ isBonding ? 'bond' : $t('compute.text_193')}}{{i + count}}</a-tag>
- <a-form-item
- v-show="showVpc"
- :wrapperCol="{ span: 24 }"
- class="mb-0 mr-1">
- <oc-select
- v-if="i === 0"
- v-decorator="decorator.vpcs(item.key)"
- show-status
- show-group
- :status-desc="$t('compute.vpc_status_desc')"
- :resource="vpcResource"
- :formatter="vpcFormatter"
- :params="vpcParams"
- :mapper="vpcResourceMapper"
- :sort="(arr) => arr.sort((a, b) => a.network_count > b.network_count ? -1 : 1)"
- :placeholder="$t('compute.text_194')"
- @selectChange="(curObjArr) => vpcSelectChange(curObjArr, i, item)"
- @fetchSuccess="(data) => fetchVpcSuccessHandle(data, item)" />
- <a-tag v-else color="blue" class="w-100 mr-1">{{ getVpcTag(networkList[0].vpc) }}</a-tag>
- </a-form-item>
- <a-form-item
- :wrapperCol="{ span: 24 }"
- :style="isDialog ? { flex: 1 } : ''"
- class="mb-0 mr-1 network-item">
- <base-select
- class="w-100"
- v-decorator="decorator.networks(item.key)"
- resource="networks"
- remote
- show-sync
- :item.sync="item.network"
- :isDefaultSelect="canDefaultSelect && i === 0"
- :need-params="true"
- :params="{ ...networkParamsC, $t: item.key }"
- :mapper="networkResourceMapper"
- :remote-fn="q => ({ search: q })"
- :beforeDefaultSelectCallBack="beforeDefaultSelectCallBack"
- @change="v => networkChange(v, item, i)"
- :select-props="{ allowClear: true, placeholder: $t('compute.text_195') }"
- :min-width="isDialog ? '200px' : '500px'" />
- <div slot="extra" v-if="i === 0">{{$t('compute.text_196')}}<help-link href="/network2">{{$t('compute.perform_create')}}</help-link>
- </div>
- </a-form-item>
- </div>
- <div :class="{ 'd-flex ml-1' : isBigScreen && !isDialog }">
- <!-- 高级 -->
- <template v-if="showAdvanced">
- <!-- ip -->
- <template v-if="isSupportIPv4(item) && !(isSupportIPv6(item) && item.ipv6Mode === 'only' && item.requireIpv6)">
- <template v-if="item.ipShow">
- <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
- <ip-select v-decorator="decorator.ips(item.key, item.network)" :value="item.ip" :network="item.network" @change="e => ipChange(e, i)" />
- </a-form-item>
- <a-button type="link" class="mt-1" @click="triggerShowIp(item)">{{$t('compute.text_135')}}</a-button>
- </template>
- <a-tooltip v-else :title="ipBtnTooltip">
- <a-button type="link" class="mr-1 mt-1" :disabled="ipsDisabled" @click="triggerShowIp(item)">{{$t('compute.text_198')}}</a-button>
- </a-tooltip>
- </template>
- <!-- mac -->
- <template v-if="showMacConfig">
- <template v-if="item.macShow">
- <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
- <a-input
- style="width: 164px"
- :placeholder="$t('compute.text_806')"
- @change="e => macChange(e, i)"
- v-decorator="decorator.macs(item.key, item.network)" />
- </a-form-item>
- <a-button type="link" class="mt-1" @click="triggerShowMac(item)">{{$t('compute.text_135')}}</a-button>
- </template>
- <a-tooltip v-else :title="ipBtnTooltip">
- <a-button type="link" class="mr-1 mt-1" :disabled="ipsDisabled" @click="triggerShowMac(item)">{{$t('compute.mac_config')}}</a-button>
- </a-tooltip>
- </template>
- <!-- 透传设备 -->
- <template v-if="showDeviceConfig">
- <template v-if="item.deviceShow">
- <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
- <oc-select
- style="width: 164px"
- v-decorator="decorator.devices(item.key)"
- :data="gpuOptions"
- :placeholder="$t('compute.sriov_device_tips')" />
- </a-form-item>
- <a-button type="link" class="mt-1" @click="triggerShowDevice(item)">{{$t('compute.text_135')}}</a-button>
- </template>
- <a-button v-else type="link" class="mr-1 mt-1" @click="triggerShowDevice(item)">{{ $t('compute.config_transparent_net') }}</a-button>
- </template>
- <!-- 安全组 -->
- <template v-if="showSecgroupConfig">
- <template v-if="item.secgroupShow">
- <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }">
- <base-select
- v-decorator="decorator.secgroups(item.key)"
- resource="secgroups"
- :params="secgroupParams"
- :select-props="{ allowClear: true, placeholder: $t('compute.secgroup_tips'), mode: 'multiple' }" />
- </a-form-item>
- </template>
- <a-button v-else type="link" class="mr-1 mt-1" @click="triggerShowSecgroup(item)">{{ $t('compute.config_secgroup') }}</a-button>
- </template>
- <!-- ipv6 -->
- <template>
- <a-form-item class="mb-0" style="display:inline-block" :wrapperCol="{ span: 24 }" v-if="isSupportIPv6(item) && isSupportIPv4(item)">
- <div class="d-flex align-items-center">
- <a-checkbox style="width: max-content" v-decorator="decorator.ipv6s(item.key, item.network)" @change="(e) => triggerRequireIpv6(item, e)" />
- <a-dropdown>
- <a-menu slot="overlay" @click="(e) => triggerIpv6Mode(item, e, i)" v-decorator="decorator.ipv6_mode(item.key, item.network)">
- <a-menu-item key="all">{{ $t('compute.server_create.require_ipv6_all') }}</a-menu-item>
- <a-menu-item key="only">{{ $t('compute.server_create.require_ipv6_only') }}</a-menu-item>
- </a-menu>
- <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>
- </a-dropdown>
- </div>
- </a-form-item>
- <template v-if="(isSupportIPv6(item) && item.requireIpv6) || (!isSupportIPv4(item) && isSupportIPv6(item))">
- <template v-if="item.ipv6Show">
- <a-form-item class="mb-0 ml-1" style="width: 350px;display:inline-block" :wrapperCol="{ span: 24 }">
- <span class="mr-1">{{ getIpv6Prefix(item.network?.guest_ip6_start) }}</span>
- <a-form-item class="mb-0" style="display:inline-block">
- <a-input
- style="width: 164px"
- :placeholder="$t('compute.complete_ipv6_address')"
- @change="e => ipv6Change(e, i)"
- v-decorator="decorator.ips6(item.key, item.network)" />
- </a-form-item>
- <a-button type="link" class="mt-1" @click="triggerShowIpv6(item)">{{$t('compute.text_135')}}</a-button>
- </a-form-item>
- </template>
- <a-button v-else type="link" class="mt-1" @click="triggerShowIpv6(item)">{{$t('compute.ipv6_config')}}</a-button>
- </template>
- </template>
- </template>
- <a-button class="mt-1" type="link" @click="() => showAdvanced = !showAdvanced">{{ showAdvanced ? $t('compute.hide_advanced') : $t('compute.advanced') }}</a-button>
- <a-button shape="circle" icon="minus" size="small" v-if="i !== 0" @click="decrease(item.key, i)" class="mt-2" />
- </div>
- </div>
- <div class="d-flex align-items-center" v-if="networkCountRemaining > 0">
- <a-button type="primary" shape="circle" icon="plus" size="small" @click="add" />
- <a-button type="link" @click="add">{{$t('compute.text_199')}}</a-button>
- <span class="network-count-tips">{{$t('compute.text_130')}}<span class="remain-num">{{ networkCountRemaining }}</span>{{$t('compute.text_200')}}</span>
- </div>
- </div>
- </template>
- <script>
- import * as R from 'ramda'
- import ipaddr from 'ipaddr.js'
- import { uuid } from '@/utils/utils'
- import IpSelect from './IpSelect.vue'
- export default {
- name: 'NetworkConfig',
- components: {
- IpSelect,
- },
- props: {
- count: {
- type: Number,
- default: () => 0,
- },
- networkParams: {
- type: Object,
- required: true,
- },
- limit: {
- type: Number,
- default: 8, // 默认支持最多 8 个ip子网
- },
- form: {
- type: Object,
- required: true,
- },
- decorator: {
- type: Object,
- required: true,
- 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),
- },
- isBonding: {
- type: Boolean,
- default: false,
- },
- networkResourceMapper: {
- type: Function,
- default: (data) => { return data },
- },
- vpcParams: {
- type: Object,
- required: true,
- },
- vpcResource: {
- type: String,
- default: 'vpcs', // 还可能是这样的resource cloudregions/{region_id}/vpcs
- },
- vpcResourceMapper: {
- type: Function,
- default: data => { return data },
- },
- vpcObj: {
- type: Object,
- validator: val => val.id && val.name,
- },
- ipsDisable: {
- type: Boolean,
- default: false,
- },
- showVpc: {
- type: Boolean,
- default: true,
- },
- isDialog: {
- type: Boolean,
- },
- showMacConfig: {
- type: Boolean,
- default: false,
- },
- showDeviceConfig: {
- type: Boolean,
- default: false,
- },
- showSecgroupConfig: {
- type: Boolean,
- default: false,
- },
- secgroupParams: {
- type: Object,
- default: () => ({}),
- },
- hiddenAdd: {
- type: Boolean,
- default: false,
- },
- },
- data () {
- return {
- networkList: [],
- ipsDisabled: this.ipsDisable,
- networkLoading: false,
- networkOpts: [],
- screenWidth: document.body.clientWidth,
- showAdvanced: false,
- canDefaultSelect: true,
- }
- },
- computed: {
- networkCountRemaining () {
- if (this.hiddenAdd) return 0
- return this.limit - this.networkList.length
- },
- networkParamsC () {
- if (!this.networkList[0]?.vpc?.id) return {}
- return {
- limit: 20,
- vpc: this.networkList[0].vpc?.id,
- ...this.networkParams,
- }
- },
- ipBtnTooltip () {
- return this.ipsDisabled ? this.$t('common_718') : null
- },
- gpuOptions () {
- const specs = this.form.fi.capability.specs || {}
- const data = specs.isolated_devices || {}
- const ret = []
- for (const key in data) {
- if (data.hasOwnProperty(key)) {
- const item = data[key]
- if (item.dev_type.startsWith('NIC')) {
- ret.push({
- ...item,
- key: `${item.model}`,
- label: `${item.model}`,
- })
- }
- }
- }
- return ret
- },
- isBigScreen () {
- return this.screenWidth > 1650
- },
- },
- watch: {
- vpcObj (val) {
- if (val && val.id && val.name) {
- this.$set(this.networkList[0], 'vpc', val)
- this.form.fc.setFieldsValue({
- [this.decorator.vpcs(this.networkList[0].key)[0]]: this.vpcObj.id,
- })
- }
- },
- networkList (val) {
- this.$set(this.form.fi, 'networkList', val)
- },
- },
- created () {
- this.add()
- },
- mounted () {
- window.addEventListener('resize', this.onResize)
- },
- beforeDestroy () {
- window.removeEventListener('resize', this.onResize)
- },
- methods: {
- initData (data) {
- this.canDefaultSelect = false
- this.networkList = data.map(item => {
- const obj = {
- ...item,
- key: uuid(),
- network: { id: item.network },
- vpc: { id: item.vpc },
- ipShow: !!item.address,
- ipv6Show: false,
- requireIpv6: false,
- ipv6Mode: 'all',
- macShow: false,
- deviceShow: false,
- secgroupShow: false,
- ip: item.address,
- }
- if (item.address) {
- obj.ipShow = true
- this.showAdvanced = true
- }
- if (item.mac) {
- obj.macShow = true
- this.showAdvanced = true
- }
- if (item.require_ipv6) {
- obj.requireIpv6 = true
- obj.ipv6Mode = 'all'
- this.showAdvanced = true
- }
- if (item.strict_ipv6 && item.require_ipv6) {
- obj.ipv6Mode = 'only'
- }
- if (item.address6) {
- obj.ipv6Show = true
- this.showAdvanced = true
- }
- if (item.sriov_device && item.sriov_device.model) {
- obj.deviceShow = true
- this.showAdvanced = true
- }
- if (item.secgroups && item.secgroups.length > 0) {
- obj.secgroupShow = true
- this.showAdvanced = true
- }
- return obj
- })
- this.$nextTick(() => {
- this.form.fc.setFieldsValue({
- [this.decorator.vpcs(this.networkList[0].key)[0]]: this.networkList[0].vpc.id,
- })
- for (const item of this.networkList) {
- const value = {}
- value[this.decorator.networks(item.key)[0]] = item.network.id
- if (item.address) {
- value[this.decorator.ips(item.key, item.network.id)[0]] = item.address
- }
- if (item.mac) {
- value[this.decorator.macs(item.key, item.network.id)[0]] = item.mac
- }
- value[this.decorator.ipv6s(item.key, item.network.id)[0]] = item.requireIpv6
- value[this.decorator.ipv6_mode(item.key, item.network.id)[0]] = item.ipv6Mode
- if (item.address6) {
- value[this.decorator.ips6(item.key, item.network.id)[0]] = item.address6.replace(this.getIpv6Prefix(item.address6), '')
- }
- if (item.sriov_device && item.sriov_device.model) {
- value[this.decorator.devices(item.key)[0]] = item.sriov_device.model
- }
- if (item.secgroups && item.secgroups.length > 0) {
- value[this.decorator.secgroups(item.key)[0]] = item.secgroups
- }
- setTimeout(() => {
- this.form.fc.setFieldsValue(value)
- }, 2000)
- setTimeout(() => {
- this.form.fc.setFieldsValue(value)
- }, 4000)
- }
- })
- },
- getVpcTag (data) {
- if (!data.cidr_block) return data.name
- return `${data.name}(${data.cidr_block})`
- },
- vpcLabelFormat (item) {
- if (!item.cidr_block) return item.name
- return `${item.name}(${item.account ? item.account + ', ' : ''}${item.cidr_block})`
- },
- vpcFormatter (v) {
- return { key: v.id, label: this.vpcLabelFormat(v), disabled: v.network_count === 0, ...v }
- },
- networkFormatter (v) {
- let addrLabel = `${v.guest_ip_start} - ${v.guest_ip_end}/${v.guest_ip_mask}`
- if (v.guest_ip6_start && v.guest_ip6_end) {
- addrLabel += `,${v.guest_ip6_start} - ${v.guest_ip6_end}/${v.guest_ip6_mask}`
- }
- return {
- key: v.id,
- label: `${v.name}(${addrLabel}, vlan=${v.vlan_id})`,
- rightLabel: `${this.$t('common.text00001')}: ${v.ports - v.ports_used}`,
- disabled: v.ports <= v.ports_used,
- ...v,
- }
- },
- ipChange (e, i) {
- this.networkList[i].ip = e
- },
- macChange (e, i) {
- this.networkList[i].mac = e.target.value
- },
- ipv6Change (e, i) {
- this.networkList[i].ipv6 = e.target.value
- },
- getIpv6Prefix (ipv6 = '') {
- if (ipv6) {
- const list = ipaddr.parse(ipv6).toNormalizedString().split(':')
- return list.slice(0, 4).join(':') + ':'
- }
- return ''
- },
- add () {
- const uid = uuid()
- const data = {
- network: {},
- vpc: {},
- ipShow: false,
- ipv6Show: false,
- requireIpv6: false,
- ipv6Mode: 'all',
- macShow: false,
- deviceShow: false,
- key: uid,
- ip: '',
- secgroupShow: false,
- }
- if (this.vpcObj) {
- data.vpc = this.vpcObj
- }
- this.networkList.push(data)
- this.$nextTick(() => {
- if (this.vpcObj) {
- this.form.fc.setFieldsValue({
- [this.decorator.vpcs(uid)[0]]: this.vpcObj.id,
- })
- }
- })
- },
- triggerShowIp (item, i) {
- item.ipShow = !item.ipShow
- },
- triggerShowIpv6 (item, i) {
- item.ipv6Show = !item.ipv6Show
- },
- triggerIpv6Mode (item, e, i) {
- item.ipv6Mode = e.key
- this.$nextTick(() => {
- this.form.fc.setFieldsValue({
- [`networkIPv6Modes[${item.key}]`]: e.key,
- })
- })
- },
- triggerRequireIpv6 (item, e) {
- item.requireIpv6 = e.target.checked
- },
- triggerShowMac (item, i) {
- item.macShow = !item.macShow
- },
- triggerShowDevice (item, i) {
- item.deviceShow = !item.deviceShow
- },
- triggerShowSecgroup (item, i) {
- item.secgroupShow = !item.secgroupShow
- },
- decrease (uid, index) {
- this.networkList.splice(index, 1)
- },
- reset (ipsDisabled) { // 重置成不可手动输入IP,并且仅保留1条数据
- if (this.networkList.length > 1) {
- const firstItem = this.networkList[0]
- this.networkList = [firstItem]
- }
- this.$set(this.networkList[0], 'ipShow', false)
- this.$set(this.networkList[0], 'ipv6Show', false)
- this.$set(this.networkList[0], 'requireIpv6', false)
- this.$set(this.networkList[0], 'ipv6Mode', 'all')
- this.$set(this.networkList[0], 'macShow', false)
- this.$set(this.networkList[0], 'deviceShow', false)
- this.$set(this.networkList[0], 'secgroupShow', false)
- this.ipsDisabled = ipsDisabled
- },
- networkChange (val, item, i) {
- this.$nextTick(() => {
- const fieldKey = `networkExits[${item.key}]`
- this.form.fc.getFieldDecorator(fieldKey, {
- preserve: true,
- })
- this.form.fc.setFieldsValue({
- [fieldKey]: item.network.exit,
- })
- if (item.network.guest_ip_start && item.network.guest_ip_end) {
- this.form.fc.setFieldsValue({
- [`networkIPv6Modes[${item.key}]`]: 'all',
- [`networkIPv6s[${item.key}]`]: false,
- })
- this.$set(this.networkList[i], 'ipv6Mode', 'all')
- this.$set(this.networkList[i], 'requireIpv6', false)
- }
- })
- },
- networkSelectChange (curObjArr, item) {
- item.network = curObjArr[0]
- this.$nextTick(() => {
- const fieldKey = `networkExits[${item.key}]`
- this.form.fc.getFieldDecorator(fieldKey, {
- preserve: true,
- })
- this.form.fc.setFieldsValue({
- [fieldKey]: item.network?.exit,
- })
- })
- },
- vpcChange (v, i) {
- this.$nextTick(() => {
- if (this.form.fi) {
- const networkVpcObj = this.networkList[i].vpc
- if (R.is(Object, networkVpcObj)) {
- this.$set(this.form.fi, 'networkVpcObj', networkVpcObj)
- }
- }
- })
- },
- vpcSelectChange (curObjArr, i, item) {
- item.vpc = curObjArr[0]
- this.$nextTick(() => {
- if (this.form.fi) {
- const networkVpcObj = item.vpc || {}
- if (R.is(Object, networkVpcObj)) {
- this.$set(this.form.fi, 'networkVpcObj', networkVpcObj)
- }
- }
- })
- },
- beforeDefaultSelectCallBack (data = []) {
- const cur = data[0]
- if (cur) {
- return cur.ports > cur.ports_used
- }
- return false
- },
- fetchVpcSuccessHandle (data = [], item) {
- let target = data[0] || {}
- if (item.vpc?.id && data.some(i => i.id === item.vpc?.id)) {
- target = data.filter(i => i.id === item.vpc?.id)[0]
- }
- item.vpc = target
- this.$nextTick(() => {
- this.form.fc.setFieldsValue({ [`vpcs[${item.key}]`]: target?.key, vpcs: { [item.key]: target?.key } })
- })
- this.vpcSelectChange([target], 0, item)
- // this.fetchNetworkOpts(this.networkParamsC, item)
- },
- fetchNetworkSuccessHandle (data, item) {
- item.network = data[0]
- this.$nextTick(() => {
- this.form.fc.setFieldsValue({ [`networks[${item.key}]`]: data?.[0]?.key })
- })
- },
- fetchNetworkOpts (params, item) {
- this.networkLoading = true
- // 未获取vpc时,network也不展示
- if (!params.vpc) {
- this.networkOpts = []
- item.network = {}
- this.form.fc.setFieldsValue({ [`networks[${item.key}]`]: '' })
- this.networkLoading = false
- } else {
- this.networkOpts = []
- new this.$Manager('networks').list({ params }).then((res) => {
- this.networkOpts = res.data.data || []
- this.$nextTick(() => {
- this.form.fc.setFieldsValue({ [`networks[${item.key}]`]: this.networkOpts?.[0]?.id })
- })
- item.network = this.networkOpts[0]
- this.networkLoading = false
- }).catch((err) => {
- this.networkLoading = false
- console.log(err)
- throw err
- })
- }
- },
- onResize () {
- this.screenWidth = document.body.clientWidth
- },
- isSupportIPv6 (item) {
- return !!item.network.guest_ip6_start && !!item.network.guest_ip6_end
- },
- isSupportIPv4 (item) {
- return !!item.network.guest_ip_start && !!item.network.guest_ip_end
- },
- },
- }
- </script>
- <style lang="less" scoped>
- @import '../../../../src/styles/less/theme';
- .network-config {
- .network-count-tips {
- .remain-num {
- color: @primary-color;
- }
- }
- }
- </style>
|