| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| <template> |
| <div> |
| <a-card |
| class="ant-form-text" |
| style="text-align: justify; margin: 10px 0; padding: 24px;" |
| v-html="$t(description)"> |
| </a-card> |
| <div v-ctrl-enter="handleSubmit"> |
| <a-table |
| bordered |
| :scroll="{ x: 500 }" |
| :dataSource="ipRanges" |
| :columns="columns" |
| :pagination="false" |
| style="margin-bottom: 24px; width: 100%" > |
| <template #bodyCell="{ column, record }"> |
| <template v-if="column.key === 'gateway'"> |
| <div> {{ record.gateway }}</div> |
| <div v-if="record.fornsx"> <a-tag color="processing"> {{ $t('label.tag.nsx') }} </a-tag> </div> |
| <div v-else-if="isNsxZone"> <a-tag color="processing"> {{ $t('label.tag.systemvm') }} </a-tag> </div> |
| </template> |
| <template v-if="column.key === 'actions'"> |
| <tooltip-button |
| :tooltip="$t('label.delete')" |
| :disabled="(record.fornsx && !forNsx) || (!record.fornsx && forNsx)" |
| type="primary" |
| :danger="true" |
| icon="delete-outlined" |
| @onClick="onDelete(record.key)" /> |
| </template> |
| </template> |
| <template #footer> |
| <a-form |
| :layout="isMobile() ? 'horizontal': 'inline'" |
| :ref="formRef" |
| :model="form" |
| :rules="rules" |
| @finish="handleAddRange" |
| > |
| <div class="form-row"> |
| <div class="form-col"> |
| <a-form-item name="gateway" ref="gateway"> |
| <a-input |
| v-model:value="form.gateway" |
| :placeholder="$t('label.gateway')" |
| v-focus="true" |
| /> |
| </a-form-item> |
| </div> |
| <div class="form-col"> |
| <a-form-item name="netmask" ref="netmask"> |
| <a-input |
| v-model:value="form.netmask" |
| :placeholder="$t('label.netmask')" |
| /> |
| </a-form-item> |
| </div> |
| <div class="form-col"> |
| <a-form-item name="vlan" ref="vlan"> |
| <a-input |
| v-model:value="form.vlan" |
| :disabled="forNsx" |
| :placeholder="$t('label.vlan')" |
| /> |
| </a-form-item> |
| </div> |
| <div class="form-col"> |
| <a-form-item name="startIp" ref="startIp"> |
| <a-input |
| v-model:value="form.startIp" |
| :placeholder="$t('label.start.ip')" |
| /> |
| </a-form-item> |
| </div> |
| <div class="form-col"> |
| <a-form-item name="endIp" ref="endIp"> |
| <a-input |
| v-model:value="form.endIp" |
| :placeholder="$t('label.end.ip')" |
| /> |
| </a-form-item> |
| </div> |
| <div class="form-col"> |
| <a-form-item :style="{ display: 'inline-block', float: 'right', marginRight: 0 }"> |
| <a-button type="primary" html-type="submit">{{ $t('label.add') }}</a-button> |
| </a-form-item> |
| </div> |
| </div> |
| </a-form> |
| </template> |
| </a-table> |
| <div class="form-action"> |
| <a-button |
| v-if="!isFixError" |
| class="button-prev" |
| @click="handleBack"> |
| {{ $t('label.previous') }} |
| </a-button> |
| <a-button class="button-next" ref="submit" type="primary" @click="handleSubmit"> |
| {{ $t('label.next') }} |
| </a-button> |
| </div> |
| </div> |
| <a-modal |
| v-if="showError" |
| :visible="showError" |
| :closable="true" |
| :maskClosable="false" |
| :title="`${$t('label.error')}!`" |
| :footer="null" |
| @cancel="showError = false" |
| centered |
| > |
| <div v-ctrl-enter="() => showError = false"> |
| <span>{{ $t('message.required.add.least.ip') }}</span> |
| <div :span="24" class="action-button"> |
| <a-button @click="showError = false">{{ $t('label.cancel') }}</a-button> |
| <a-button type="primary" ref="submit" @click="showError = false">{{ $t('label.ok') }}</a-button> |
| </div> |
| </div> |
| </a-modal> |
| </div> |
| </template> |
| <script> |
| |
| import { ref, reactive, toRaw } from 'vue' |
| import TooltipButton from '@/components/widgets/TooltipButton' |
| import { mixinDevice } from '@/utils/mixin.js' |
| |
| export default { |
| components: { |
| TooltipButton |
| }, |
| mixins: [mixinDevice], |
| props: { |
| traffic: { |
| type: String, |
| default: '0' |
| }, |
| description: { |
| type: String, |
| default: 'label.creating.iprange' |
| }, |
| prefillContent: { |
| type: Object, |
| default: function () { |
| return {} |
| } |
| }, |
| isFixError: { |
| type: Boolean, |
| default: false |
| }, |
| forNsx: { |
| type: Boolean, |
| default: false |
| }, |
| isNsxZone: { |
| type: Boolean, |
| default: false |
| } |
| }, |
| data () { |
| return { |
| formItemLayout: { |
| wrapperCol: { span: 0 } |
| }, |
| ipRanges: [], |
| columns: [ |
| { |
| key: 'gateway', |
| title: this.$t('label.gateway'), |
| dataIndex: 'gateway', |
| width: 140 |
| }, |
| { |
| title: this.$t('label.netmask'), |
| dataIndex: 'netmask', |
| width: 140 |
| }, |
| { |
| title: this.$t('label.vlan'), |
| dataIndex: 'vlan', |
| width: 120 |
| }, |
| { |
| title: this.$t('label.start.ip'), |
| dataIndex: 'startIp', |
| width: 140 |
| }, |
| { |
| title: this.$t('label.end.ip'), |
| dataIndex: 'endIp', |
| width: 140 |
| }, |
| { |
| key: 'actions', |
| title: '', |
| dataIndex: 'actions', |
| width: 70 |
| } |
| ], |
| showError: false, |
| ipV4Regex: /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i, |
| ipV6Regex: /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i |
| } |
| }, |
| mounted () { |
| const prefilledIpRangesKey = this.traffic + '-ipranges' |
| if (this.prefillContent[prefilledIpRangesKey]) { |
| this.ipRanges = this.prefillContent[prefilledIpRangesKey] |
| } |
| }, |
| created () { |
| this.initForm() |
| }, |
| methods: { |
| initForm () { |
| this.formRef = ref() |
| this.form = reactive({}) |
| this.rules = reactive({ |
| gateway: [{ required: true, message: this.$t('message.error.gateway') }], |
| netmask: [{ required: true, message: this.$t('message.error.netmask') }], |
| startIp: [{ |
| required: true, |
| message: this.$t('message.error.startip') |
| }, |
| { |
| validator: this.checkIpFormat, |
| ipV4: true, |
| message: this.$t('message.error.ipv4.address') |
| }], |
| endIp: [{ |
| required: true, |
| message: this.$t('message.error.startip') |
| }, |
| { |
| validator: this.checkIpFormat, |
| ipV4: true, |
| message: this.$t('message.error.ipv4.address') |
| }] |
| }) |
| }, |
| handleAddRange () { |
| this.formRef.value.validate().then(() => { |
| const values = toRaw(this.form) |
| const len = this.isValidSetup() ? this.ipRanges.length - 1 : 0 |
| const key = this.isValidSetup() ? this.ipRanges[len].key : 0 |
| this.ipRanges.push({ |
| key: key + 1, |
| gateway: values.gateway, |
| netmask: values.netmask, |
| vlan: values.vlan, |
| startIp: values.startIp, |
| endIp: values.endIp, |
| fornsx: this.forNsx, |
| forsystemvms: this.isNsxZone && !this.forNsx |
| }) |
| this.formRef.value.resetFields() |
| }).catch(error => { |
| this.formRef.value.scrollToField(error.errorFields[0].name) |
| }) |
| this.emitIpRanges() |
| }, |
| isValidSetup () { |
| return this.ipRanges && this.ipRanges.length > 0 |
| }, |
| handleSubmit () { |
| if (this.isValidSetup()) { |
| this.showError = false |
| if (this.isFixError) { |
| this.$emit('submitLaunchZone') |
| return |
| } |
| |
| this.$emit('nextPressed', this.ipRanges) |
| } else { |
| this.showError = true |
| } |
| }, |
| handleBack (e) { |
| this.$emit('backPressed') |
| }, |
| onDelete (key) { |
| const ipRanges = [...this.ipRanges] |
| this.ipRanges = ipRanges.filter(item => item.key !== key) |
| this.emitIpRanges() |
| }, |
| emitIpRanges () { |
| const trafficRanges = {} |
| trafficRanges[this.traffic + '-ipranges'] = this.ipRanges |
| this.$emit('fieldsChanged', trafficRanges) |
| }, |
| async checkIpFormat (rule, value) { |
| if (!value || value === '') { |
| return Promise.resolve() |
| } else if (rule.ipV4 && !this.ipV4Regex.test(value)) { |
| return Promise.reject(rule.message) |
| } else if (rule.ipV6 && !this.ipV6Regex.test(value)) { |
| return Promise.reject(rule.message) |
| } else { |
| return Promise.resolve() |
| } |
| } |
| } |
| } |
| </script> |
| |
| <style scoped lang="less"> |
| .form-row { |
| display: grid; |
| grid-template-columns: 145px 145px 130px 145px 145px 70px; |
| justify-content: center; |
| |
| @media (max-width: 768px) { |
| display: flex; |
| flex-direction: column; |
| } |
| } |
| </style> |