primate: use a-table with pagination instead of a-list (#151)

This converts many components to use a-table with a-pagination than a-list:
* Convert MigrateWizard
* Convert Firewalls
* Convert Port Forwarding
* Convert Load Balancing
* Convert Egress Configure
* Convert IngressEgress Configure
* Convert Dedicate VLAN
* Convert VPC Tiers tab

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
Co-authored-by: Ritchie Vincent <ritchie@ritchievincent.co.uk>
Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Ritchie Vincent 2020-03-21 20:10:40 +00:00 committed by Rohit Yadav
parent 063c865111
commit 0836e45b94
9 changed files with 649 additions and 357 deletions

View File

@ -40,7 +40,7 @@ export default {
component: () => import('@/components/view/DetailsTab.vue')
}, {
name: 'Egress Rules',
component: () => import('@/views/network/EgressConfigure.vue'),
component: () => import('@/views/network/EgressRulesTab.vue'),
show: (record) => { return record.type === 'Isolated' && 'listEgressFirewallRules' in store.getters.apis }
}, {
name: 'Public IP Addresses',

View File

@ -917,6 +917,7 @@
"snmpPort": "SNMP Port",
"sockettimeout": "Socket Timeout",
"sourcecidr": "Source CIDR",
"destcidr": "Destination CIDR",
"sourceNat": "Source NAT",
"sourceipaddress": "Source IP Address",
"sourceport": "Source Port",

View File

@ -16,60 +16,64 @@
// under the License.
<template>
<a-list :dataSource="hosts" itemLayout="vertical" class="list" :loading="loading">
<div slot="header" class="list__header">
<a-input-search
placeholder="Search"
v-model="searchQuery"
@search="fetchData" />
</div>
<a-list-item
slot="renderItem"
slot-scope="host, index"
class="host-item"
:class="{ 'host-item--selected' : selectedIndex === index }"
>
<div class="host-item__row">
<div class="host-item__value">
<span class="host-item__title">{{ $t('name') }}</span>
{{ host.name }}
</div>
<div class="host-item__value host-item__value--small">
<span class="host-item__title">Suitability</span>
<a-icon
class="host-item__suitability-icon"
type="check-circle"
theme="twoTone"
twoToneColor="#52c41a"
v-if="host.suitableformigration" />
<a-icon
class="host-item__suitability-icon"
type="close-circle"
theme="twoTone"
twoToneColor="#f5222d"
v-else />
</div>
<div class="host-item__value host-item__value--full">
<span class="host-item__title">{{ $t('cpuused') }}</span>
{{ host.cpuused }}
</div>
<div class="host-item__value">
<span class="host-item__title">{{ $t('memused') }}</span>
{{ host.memoryused | byteToGigabyte }} GB
</div>
<div class="form">
<a-input-search
placeholder="Search"
v-model="searchQuery"
style="margin-bottom: 10px;"
@search="fetchData" />
<a-table
size="small"
style="overflow-y: auto"
:loading="loading"
:columns="columns"
:dataSource="hosts"
:pagination="false"
:rowKey="record => record.id">
<div slot="suitability" slot-scope="record">
<a-icon
class="host-item__suitability-icon"
type="check-circle"
theme="twoTone"
twoToneColor="#52c41a"
v-if="record.suitableformigration" />
<a-icon
class="host-item__suitability-icon"
type="close-circle"
theme="twoTone"
twoToneColor="#f5222d"
v-else />
</div>
<div slot="memused" slot-scope="record">
{{ record.memoryused | byteToGigabyte }} GB
</div>
<template slot="select" slot-scope="record">
<a-radio
class="host-item__radio"
@click="selectedIndex = index"
:checked="selectedIndex === index"
:disabled="!host.suitableformigration"></a-radio>
</div>
</a-list-item>
<div slot="footer" class="list__footer">
<a-button type="primary" :disabled="selectedIndex === null" @click="submitForm">
{{ $t('OK') }}
@click="selectedHost = record"
:checked="record.id === selectedHost.id"
:disabled="!record.suitableformigration"></a-radio>
</template>
</a-table>
<a-pagination
class="pagination"
size="small"
:current="page"
:pageSize="pageSize"
:total="totalCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="handleChangePage"
@showSizeChange="handleChangePageSize"
showSizeChanger/>
<div style="margin-top: 20px; display: flex; justify-content:flex-end;">
<a-button type="primary" :disabled="!selectedHost.id" @click="submitForm">
{{ $t('ok') }}
</a-button>
</div>
</a-list>
</div>
</template>
<script>
@ -87,8 +91,33 @@ export default {
return {
loading: true,
hosts: [],
selectedIndex: null,
searchQuery: ''
selectedHost: {},
searchQuery: '',
totalCount: 0,
page: 1,
pageSize: 10,
columns: [
{
title: this.$t('name'),
dataIndex: 'name'
},
{
title: this.$t('Suitability'),
scopedSlots: { customRender: 'suitability' }
},
{
title: this.$t('cpuused'),
dataIndex: 'cpuused'
},
{
title: this.$t('memused'),
scopedSlots: { customRender: 'memused' }
},
{
title: this.$t('select'),
scopedSlots: { customRender: 'select' }
}
]
}
},
mounted () {
@ -100,20 +129,24 @@ export default {
api('findHostsForMigration', {
virtualmachineid: this.resource.id,
keyword: this.searchQuery,
page: 1,
pagesize: 500
page: this.page,
pagesize: this.pageSize
}).then(response => {
this.hosts = response.findhostsformigrationresponse.host
this.loading = false
this.totalCount = response.findhostsformigrationresponse.count
if (this.totalCount > 0) {
this.totalCount -= 1
}
}).catch(error => {
this.$message.error('Failed to load hosts: ' + error)
}).finally(() => {
this.loading = false
})
},
submitForm () {
this.loading = true
const host = this.hosts[this.selectedIndex]
api(host.requiresStorageMotion ? 'migrateVirtualMachineWithVolume' : 'migrateVirtualMachine', {
hostid: host.id,
api(this.selectedHost.requiresStorageMotion ? 'migrateVirtualMachineWithVolume' : 'migrateVirtualMachine', {
hostid: this.selectedHost.id,
virtualmachineid: this.resource.id
}).then(response => {
this.$store.dispatch('AddAsyncJob', {
@ -141,15 +174,23 @@ export default {
this.$parent.$parent.close()
}).catch(error => {
console.error(error)
this.$message.error('Failed to migrate host.')
this.$message.error(`Failed to migrate VM to host ${this.selectedHost.name}`)
})
},
handleChangePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
this.fetchData()
},
handleChangePageSize (currentPage, pageSize) {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
}
},
filters: {
byteToGigabyte: value => {
if (!value) return ''
value = value / Math.pow(10, 9)
return value.toFixed(2)
return (value / Math.pow(10, 9)).toFixed(2)
}
}
}
@ -157,26 +198,10 @@ export default {
<style scoped lang="scss">
.list {
max-height: 95vh;
width: 95vw;
overflow-y: scroll;
margin: -24px;
@media (min-width: 1000px) {
max-height: 70vh;
width: 60vw;
}
&__header,
&__footer {
padding-left: 20px;
padding-right: 20px;
}
&__footer {
display: flex;
justify-content: flex-end;
.form {
width: 85vw;
@media (min-width: 800px) {
width: 750px;
}
}
@ -230,4 +255,8 @@ export default {
}
}
.pagination {
margin-top: 20px;
}
</style>

View File

@ -18,41 +18,37 @@
<template>
<a-spin :spinning="fetchLoading">
<a-button type="dashed" icon="plus" style="width: 100%" @click="handleOpenModal">{{ $t('label.dedicate.vlan.vni.range') }}</a-button>
<a-list class="list">
<a-list-item v-for="item in items" :key="item.id" class="list__item">
<div class="list__item-outer-container">
<div class="list__item-container">
<div class="list__col">
<div class="list__label">{{ $t('vlanrange') }}</div>
<div>{{ item.guestvlanrange }}</div>
</div>
<div class="list__col">
<div class="list__label">{{ $t('domain') }}</div>
<div>{{ item.domain }}</div>
</div>
<div class="list__col">
<div class="list__label">{{ $t('account') }}</div>
<div>{{ item.account }}</div>
</div>
</div>
<div class="list__col">
<div class="list__label">{{ $t('id') }}</div>
<div>{{ item.id }}</div>
</div>
</div>
<div slot="actions">
<a-popconfirm
:title="`${$t('label.delete')}?`"
@confirm="handleDelete(item)"
okText="Yes"
cancelText="No"
placement="top"
>
<a-button icon="delete" type="danger" shape="round"></a-button>
</a-popconfirm>
</div>
</a-list-item>
</a-list>
<a-table
size="small"
style="overflow-y: auto; margin-top: 20px;"
:loading="fetchLoading"
:columns="columns"
:dataSource="items"
:pagination="false"
:rowKey="record => record.id">
<template slot="actions" slot-scope="record">
<a-popconfirm
:title="`${$t('label.delete')}?`"
@confirm="handleDelete(record)"
okText="Yes"
cancelText="No"
placement="top"
>
<a-button icon="delete" type="danger" shape="round"></a-button>
</a-popconfirm>
</template>
</a-table>
<a-pagination
class="pagination"
size="small"
:current="page"
:pageSize="pageSize"
:total="totalCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="handleChangePage"
@showSizeChange="handleChangePageSize"
showSizeChanger/>
<a-modal v-model="modal" :title="$t('label.dedicate.vlan.vni.range')" @ok="handleSubmit">
<a-spin :spinning="formLoading">
@ -147,7 +143,28 @@ export default {
accounts: [],
projects: [],
modal: false,
selectedScope: 'account'
selectedScope: 'account',
totalCount: 0,
page: 1,
pageSize: 10,
columns: [
{
title: this.$t('vlanrange'),
dataIndex: 'guestvlanrange'
},
{
title: this.$t('domain'),
dataIndex: 'domain'
},
{
title: this.$t('account'),
dataIndex: 'account'
},
{
title: this.$t('action'),
scopedSlots: { customRender: 'actions' }
}
]
}
},
beforeCreate () {
@ -166,20 +183,23 @@ export default {
methods: {
fetchData () {
this.form.resetFields()
this.formLoading = true
api('listDedicatedGuestVlanRanges', {
physicalnetworkid: this.resource.id
physicalnetworkid: this.resource.id,
page: this.page,
pageSize: this.pageSize
}).then(response => {
this.items = response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange
? response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange : []
this.parentFinishLoading()
this.formLoading = false
this.items = response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange || []
this.totalCount = response.listdedicatedguestvlanrangesresponse.count || 0
}).catch(error => {
this.$notification.error({
message: `Error ${error.response.status}`,
description: error.response.data.errorresponse.errortext
description: error.response.data.errorresponse.errortext,
duration: 0
})
this.parentFinishLoading()
}).finally(() => {
this.formLoading = false
this.parentFinishLoading()
})
},
fetchDomains () {
@ -187,8 +207,7 @@ export default {
details: 'min',
listAll: true
}).then(response => {
this.domains = response.listdomainsresponse.domain
? response.listdomainsresponse.domain : []
this.domains = response.listdomainsresponse.domain || []
if (this.domains.length > 0) {
this.form.setFieldsValue({
domain: this.domains[0].id
@ -340,6 +359,16 @@ export default {
this.modal = true
this.formLoading = true
this.fetchDomains()
},
handleChangePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
this.fetchData()
},
handleChangePageSize (currentPage, pageSize) {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
}
}
}
@ -381,4 +410,8 @@ export default {
}
}
}
.pagination {
margin-top: 20px;
}
</style>

View File

@ -20,15 +20,15 @@
<div>
<div class="form">
<div class="form__item">
<div class="form__label">Source CIDR</div>
<div class="form__label">{{ $t('sourcecidr') }}</div>
<a-input v-model="newRule.cidrlist"></a-input>
</div>
<div class="form__item">
<div class="form__label">Destination CIDR</div>
<div class="form__label">{{ $t('destcidr') }}</div>
<a-input v-model="newRule.destcidrlist"></a-input>
</div>
<div class="form__item">
<div class="form__label">Protocol</div>
<div class="form__label">{{ $t('protocol') }}</div>
<a-select v-model="newRule.protocol" style="width: 100%;" @change="resetRulePorts">
<a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="udp">UDP</a-select-option>
@ -37,19 +37,19 @@
</a-select>
</div>
<div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
<div class="form__label">Start Port</div>
<div class="form__label">{{ $t('startport') }}</div>
<a-input v-model="newRule.startport"></a-input>
</div>
<div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
<div class="form__label">End Port</div>
<div class="form__label">{{ $t('endport') }}</div>
<a-input v-model="newRule.endport"></a-input>
</div>
<div v-show="newRule.protocol === 'icmp'" class="form__item">
<div class="form__label">ICMP Type</div>
<div class="form__label">{{ $t('icmptype') }}</div>
<a-input v-model="newRule.icmptype"></a-input>
</div>
<div v-show="newRule.protocol === 'icmp'" class="form__item">
<div class="form__label">ICMP Code</div>
<div class="form__label">{{ $t('icmpcode') }}</div>
<a-input v-model="newRule.icmpcode"></a-input>
</div>
<div class="form__item">
@ -60,35 +60,39 @@
<a-divider/>
<a-list :loading="loading" style="min-height: 25px;">
<a-list-item v-for="rule in egressRules" :key="rule.id" class="rule">
<div class="rule-container">
<div class="rule__item">
<div class="rule__title">Source CIDR</div>
<div>{{ rule.cidrlist }}</div>
</div>
<div class="rule__item">
<div class="rule__title">Destination CIDR</div>
<div>{{ rule.destcidrlist }}</div>
</div>
<div class="rule__item">
<div class="rule__title">Protocol</div>
<div>{{ rule.protocol | capitalise }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ rule.protocol === 'icmp' ? 'ICMP Type' : 'Start Port' }}</div>
<div>{{ rule.icmptype || rule.startport >= 0 ? rule.icmptype || rule.startport : 'All' }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ rule.protocol === 'icmp' ? 'ICMP Code' : 'End Port' }}</div>
<div>{{ rule.icmpcode || rule.endport >= 0 ? rule.icmpcode || rule.endport : 'All' }}</div>
</div>
<div slot="actions">
<a-button shape="round" type="danger" icon="delete" @click="deleteRule(rule)" />
</div>
</div>
</a-list-item>
</a-list>
<a-table
size="small"
style="overflow-y: auto"
:loading="loading"
:columns="columns"
:dataSource="egressRules"
:pagination="false"
:rowKey="record => record.id">
<template slot="protocol" slot-scope="record">
{{ record.protocol | capitalise }}
</template>
<template slot="startport" slot-scope="record">
{{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : 'All' }}
</template>
<template slot="endport" slot-scope="record">
{{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : 'All' }}
</template>
<template slot="actions" slot-scope="record">
<a-button shape="round" type="danger" icon="delete" @click="deleteRule(record)" />
</template>
</a-table>
<a-pagination
class="pagination"
size="small"
:current="page"
:pageSize="pageSize"
:total="totalCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="handleChangePage"
@showSizeChange="handleChangePageSize"
showSizeChanger/>
</div>
</template>
@ -96,6 +100,7 @@
import { api } from '@/api'
export default {
name: 'EgressRulesTab',
props: {
resource: {
type: Object,
@ -115,7 +120,36 @@ export default {
icmpcode: null,
startport: null,
endport: null
}
},
totalCount: 0,
page: 1,
pageSize: 10,
columns: [
{
title: this.$t('sourcecidr'),
dataIndex: 'cidrlist'
},
{
title: this.$t('destcidr'),
dataIndex: 'destcidrlist'
},
{
title: this.$t('protocol'),
scopedSlots: { customRender: 'protocol' }
},
{
title: `ICMP Type / Start Port`,
scopedSlots: { customRender: 'startport' }
},
{
title: `ICMP Code / End Port`,
scopedSlots: { customRender: 'endport' }
},
{
title: this.$t('action'),
scopedSlots: { customRender: 'actions' }
}
]
}
},
mounted () {
@ -141,9 +175,13 @@ export default {
this.loading = true
api('listEgressFirewallRules', {
listAll: true,
networkid: this.resource.id
networkid: this.resource.id,
page: this.page,
pageSize: this.pageSize
}).then(response => {
this.egressRules = response.listegressfirewallrulesresponse.firewallrule
this.egressRules = response.listegressfirewallrulesresponse.firewallrule || []
this.totalCount = response.listegressfirewallrulesresponse.count || 0
}).finally(() => {
this.loading = false
})
},
@ -211,6 +249,16 @@ export default {
this.newRule.icmpcode = null
this.newRule.startport = null
this.newRule.endport = null
},
handleChangePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
this.fetchData()
},
handleChangePageSize (currentPage, pageSize) {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
}
}
}
@ -300,4 +348,7 @@ export default {
}
}
.pagination {
margin-top: 20px;
}
</style>

View File

@ -55,36 +55,41 @@
<a-divider/>
<a-list :loading="loading" style="min-height: 25px;">
<a-list-item v-for="rule in firewallRules" :key="rule.id" class="rule">
<div class="rule-container">
<div class="rule__item">
<div class="rule__title">{{ $t('sourcecidr') }}</div>
<div>{{ rule.cidrlist }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('protocol') }}</div>
<div>{{ rule.protocol | capitalise }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ rule.protocol === 'icmp' ? $t('icmptype') : $t('startport') }}</div>
<div>{{ rule.icmptype || rule.startport >= 0 ? rule.icmptype || rule.startport : $t('all') }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ rule.protocol === 'icmp' ? 'ICMP Code' : 'End Port' }}</div>
<div>{{ rule.icmpcode || rule.endport >= 0 ? rule.icmpcode || rule.endport : $t('all') }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('state') }}</div>
<div>{{ rule.state }}</div>
</div>
<div slot="actions">
<a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(rule.id)" />
<a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(rule)" />
</div>
<a-table
size="small"
style="overflow-y: auto"
:loading="loading"
:columns="columns"
:dataSource="firewallRules"
:pagination="false"
:rowKey="record => record.id">
<template slot="protocol" slot-scope="record">
{{ record.protocol | capitalise }}
</template>
<template slot="startport" slot-scope="record">
{{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('all') }}
</template>
<template slot="endport" slot-scope="record">
{{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('all') }}
</template>
<template slot="actions" slot-scope="record">
<div class="actions">
<a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(record.id)" />
<a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(record)" />
</div>
</a-list-item>
</a-list>
</template>
</a-table>
<a-pagination
class="pagination"
size="small"
:current="page"
:pageSize="pageSize"
:total="totalCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="handleChangePage"
@showSizeChange="handleChangePageSize"
showSizeChanger/>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
<div class="add-tags">
@ -96,7 +101,7 @@
<p class="add-tags__label">{{ $t('value') }}</p>
<a-input v-model="newTag.value"></a-input>
</div>
<a-button type="primary" @click="() => handleAddTag()">{{ $t('add') }}</a-button>
<a-button type="primary" @click="() => handleAddTag()" :loading="addTagLoading">{{ $t('add') }}</a-button>
</div>
<a-divider></a-divider>
@ -129,6 +134,7 @@ export default {
data () {
return {
loading: true,
addTagLoading: false,
firewallRules: [],
newRule: {
protocol: 'tcp',
@ -145,7 +151,36 @@ export default {
newTag: {
key: null,
value: null
}
},
totalCount: 0,
page: 1,
pageSize: 10,
columns: [
{
title: this.$t('sourcecidr'),
dataIndex: 'cidrlist'
},
{
title: this.$t('protocol'),
scopedSlots: { customRender: 'protocol' }
},
{
title: `${this.$t('startport')}/${this.$t('icmptype')}`,
scopedSlots: { customRender: 'startport' }
},
{
title: `${this.$t('endport')}/${this.$t('icmpcode')}`,
scopedSlots: { customRender: 'endport' }
},
{
title: this.$t('state'),
dataIndex: 'state'
},
{
title: this.$t('action'),
scopedSlots: { customRender: 'actions' }
}
]
}
},
mounted () {
@ -171,9 +206,12 @@ export default {
this.loading = true
api('listFirewallRules', {
listAll: true,
ipaddressid: this.resource.id
ipaddressid: this.resource.id,
page: this.page,
pageSize: this.pageSize
}).then(response => {
this.firewallRules = response.listfirewallrulesresponse.firewallrule
this.firewallRules = response.listfirewallrulesresponse.firewallrule || []
this.totalCount = response.listfirewallrulesresponse.count || 0
}).catch(error => {
this.$notification.error({
message: `Error ${error.response.status}`,
@ -271,6 +309,7 @@ export default {
})
},
handleAddTag () {
this.addTagLoading = true
api('createTags', {
'tags[0].key': this.newTag.key,
'tags[0].value': this.newTag.value,
@ -305,6 +344,8 @@ export default {
description: error.response.data.createtagsresponse.errortext
})
this.closeModal()
}).finally(() => {
this.addTagLoading = false
})
},
handleDeleteTag (tag) {
@ -343,6 +384,16 @@ export default {
})
this.closeModal()
})
},
handleChangePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
this.fetchData()
},
handleChangePageSize (currentPage, pageSize) {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
}
}
}
@ -434,7 +485,6 @@ export default {
}
.rule-action {
margin-bottom: 20px;
&:not(:last-of-type) {
margin-right: 10px;
@ -472,5 +522,8 @@ export default {
display: block;
margin-left: auto;
}
.pagination {
margin-top: 20px;
}
</style>

View File

@ -69,49 +69,33 @@
</div>
</div>
<a-list>
<a-list-item v-for="(rule, index) in rules" :key="index" class="list">
<div class="list__col">
<div class="list__title">Protocol</div>
<div>{{ rule.protocol | capitalise }}</div>
<a-table
size="small"
style="overflow-y: auto"
:columns="columns"
:dataSource="rules"
:pagination="false"
:rowKey="record => record.id">
<template slot="protocol" slot-scope="record">
{{ record.protocol | capitalise }}
</template>
<template slot="account" slot-scope="record">
<div v-if="record.account && record.securitygroupname">
{{ record.account }} - {{ record.securitygroupname }}
</div>
<div v-if="rule.startport" class="list__col">
<div class="list__title">Start Port</div>
<div>{{ rule.startport }}</div>
</div>
<div v-if="rule.endport" class="list__col">
<div class="list__title">End Port</div>
<div>{{ rule.endport }}</div>
</div>
<div v-if="rule.icmptype" class="list__col">
<div class="list__title">ICMP Type</div>
<div>{{ rule.icmptype }}</div>
</div>
<div v-if="rule.icmpcode" class="list__col">
<div class="list__title">ICMP Code</div>
<div>{{ rule.icmpcode }}</div>
</div>
<div v-if="rule.cidr" class="list__col">
<div class="list__title">CIDR</div>
<div>{{ rule.cidr }}</div>
</div>
<div class="list__col" v-if="rule.account && rule.securitygroupname">
<div class="list__title">Account, Security Group</div>
<div>{{ rule.account }} - {{ rule.securitygroupname }}</div>
</div>
<div slot="actions" class="actions">
<a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(rule)" />
<a-popconfirm
:title="$t('label.delete') + '?'"
@confirm="handleDeleteRule(rule)"
okText="Yes"
cancelText="No"
>
<a-button shape="round" type="danger" icon="delete" class="rule-action" />
</a-popconfirm>
</div>
</a-list-item>
</a-list>
</template>
<template slot="actions" slot-scope="record">
<a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(record)" />
<a-popconfirm
:title="$t('label.delete') + '?'"
@confirm="handleDeleteRule(record)"
okText="Yes"
cancelText="No"
>
<a-button shape="round" type="danger" icon="delete" class="rule-action" />
</a-popconfirm>
</template>
</a-table>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
<a-spin v-if="tagsLoading"></a-spin>
@ -187,7 +171,41 @@ export default {
selectedRule: null,
tagsLoading: false,
addType: 'cidr',
tabType: null
tabType: null,
columns: [
{
title: this.$t('protocol'),
scopedSlots: { customRender: 'protocol' }
},
{
title: this.$t('startport'),
dataIndex: 'startport'
},
{
title: this.$t('endport'),
dataIndex: 'endport'
},
{
title: 'ICMP Type',
dataIndex: 'icmptype'
},
{
title: 'ICMP Code',
dataIndex: 'icmpcode'
},
{
title: 'CIDR',
dataIndex: 'cidr'
},
{
title: 'Account, Security Group',
scopedSlots: { customRender: 'account' }
},
{
title: this.$t('action'),
scopedSlots: { customRender: 'actions' }
}
]
}
},
watch: {

View File

@ -59,90 +59,78 @@
<a-divider />
<a-list :loading="loading" style="min-height: 25px;">
<a-list-item v-for="rule in lbRules" :key="rule.id" class="rule custom-ant-list">
<div class="rule-container">
<div class="rule__row">
<div class="rule__item">
<div class="rule__title">{{ $t('name') }}</div>
<div>{{ rule.name }}</div>
<a-table
size="small"
style="overflow-y: auto"
:loading="loading"
:columns="columns"
:dataSource="lbRules"
:pagination="false"
:rowKey="record => record.id">
<template slot="algorithm" slot-scope="record">
{{ returnAlgorithmName(record.algorithm) }}
</template>
<template slot="protocol" slot-scope="record">
{{ record.protocol | capitalise }}
</template>
<template slot="stickiness" slot-scope="record">
<a-button @click="() => openStickinessModal(record.id)">
{{ returnStickinessLabel(record.id) }}
</a-button>
</template>
<template slot="add" slot-scope="record">
<a-button type="primary" icon="plus" @click="() => { selectedRule = record; handleOpenAddVMModal() }">
{{ $t('add') }}
</a-button>
</template>
<template slot="expandedRowRender" slot-scope="record">
<div class="rule-instance-list">
<div v-for="instance in record.ruleInstances" :key="instance.loadbalancerruleinstance.id">
<div v-for="ip in instance.lbvmipaddresses" :key="ip" class="rule-instance-list__item">
<div>
<a-icon type="desktop" />
<router-link :to="{ path: '/vm/' + record.virtualmachineid }">
{{ instance.loadbalancerruleinstance.displayname }}
</router-link>
</div>
<div>{{ ip }}</div>
<div><status :text="instance.loadbalancerruleinstance.state" displayText /></div>
<a-button
size="small"
shape="round"
type="danger"
icon="delete"
@click="() => handleDeleteInstanceFromRule(instance, record, ip)" />
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('publicport') }}</div>
<div>{{ rule.publicport }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('privateport') }}</div>
<div>{{ rule.privateport }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('algorithm') }}</div>
<div>{{ returnAlgorithmName(rule.algorithm) }}</div>
</div>
</div>
<div class="rule__row">
<div class="rule__item">
<div class="rule__title">{{ $t('protocol') }}</div>
<div>{{ rule.protocol | capitalise }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('state') }}</div>
<div>{{ rule.state }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('label.action.configure.stickiness') }}</div>
<a-button @click="() => openStickinessModal(rule.id)">
{{ returnStickinessLabel(rule.id) }}
</a-button>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('label.add.VMs') }}</div>
<a-button type="primary" icon="plus" @click="() => { selectedRule = rule; handleOpenAddVMModal() }">
{{ $t('add') }}
</a-button>
</div>
</div>
<div class="rule__row" v-if="rule.ruleInstances">
<a-collapse :bordered="false" class="rule-instance-collapse">
<template v-slot:expandIcon="props">
<a-icon type="caret-right" :rotate="props.isActive ? 90 : 0" />
</template>
<a-collapse-panel header="View Instances">
<div class="rule-instance-list">
<div v-for="instance in rule.ruleInstances" :key="instance.loadbalancerruleinstance.id">
<div v-for="ip in instance.lbvmipaddresses" :key="ip" class="rule-instance-list__item">
<div>
<a-icon type="desktop" />
<router-link :to="{ path: '/vm/' + rule.virtualmachineid }"> {{ instance.loadbalancerruleinstance.displayname }}</router-link>
</div>
<div>{{ ip }}</div>
<div><status :text="instance.loadbalancerruleinstance.state" displayText /></div>
<a-button
shape="round"
type="danger"
icon="delete"
@click="() => handleDeleteInstanceFromRule(instance, rule, ip)" />
</div>
</div>
</div>
</a-collapse-panel>
</a-collapse>
</div>
</div>
<div class="rule__item">
<a-button shape="circle" icon="edit" class="rule-action" @click="() => openEditRuleModal(rule)"></a-button>
<a-button shape="circle" icon="tag" class="rule-action" @click="() => openTagsModal(rule.id)" />
</template>
<template slot="actions" slot-scope="record">
<div class="actions">
<a-button size="small" shape="circle" icon="edit" @click="() => openEditRuleModal(record)"></a-button>
<a-button size="small" shape="circle" icon="tag" @click="() => openTagsModal(record.id)" />
<a-popconfirm
:title="$t('label.delete') + '?'"
@confirm="handleDeleteRule(rule)"
@confirm="handleDeleteRule(record)"
okText="Yes"
cancelText="No"
>
<a-button shape="circle" type="danger" icon="delete" class="rule-action" />
<a-button size="small" shape="circle" type="danger" icon="delete" />
</a-popconfirm>
</div>
</a-list-item>
</a-list>
</template>
</a-table>
<a-pagination
class="pagination"
size="small"
:current="page"
:pageSize="pageSize"
:total="totalCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="handleChangePage"
@showSizeChange="handleChangePageSize"
showSizeChanger/>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal" class="tags-modal">
<span v-show="tagsModalLoading" class="modal-loading">
@ -393,7 +381,48 @@ export default {
addVmModalLoading: false,
addVmModalNicLoading: false,
vms: [],
nics: []
nics: [],
totalCount: 0,
page: 1,
pageSize: 10,
columns: [
{
title: this.$t('name'),
dataIndex: 'name'
},
{
title: this.$t('publicport'),
dataIndex: 'publicport'
},
{
title: this.$t('privateport'),
dataIndex: 'privateport'
},
{
title: this.$t('algorithm'),
scopedSlots: { customRender: 'algorithm' }
},
{
title: this.$t('protocol'),
scopedSlots: { customRender: 'protocol' }
},
{
title: this.$t('state'),
dataIndex: 'state'
},
{
title: this.$t('label.action.configure.stickiness'),
scopedSlots: { customRender: 'stickiness' }
},
{
title: this.$t('label.add.VMs'),
scopedSlots: { customRender: 'add' }
},
{
title: this.$t('action'),
scopedSlots: { customRender: 'actions' }
}
]
}
},
mounted () {
@ -421,11 +450,12 @@ export default {
this.stickinessPolicies = []
api('listLoadBalancerRules', {
listAll: true,
publicipid: this.resource.id
publicipid: this.resource.id,
page: this.page,
pageSize: this.pageSize
}).then(response => {
this.lbRules = response.listloadbalancerrulesresponse.loadbalancerrule
? response.listloadbalancerrulesresponse.loadbalancerrule
: []
this.lbRules = response.listloadbalancerrulesresponse.loadbalancerrule || []
this.totalCount = response.listloadbalancerrulesresponse.count || 0
}).then(() => {
if (this.lbRules.length > 0) {
setTimeout(() => {
@ -1041,6 +1071,16 @@ export default {
this.newRule.virtualmachineid = []
this.newTagsForm.resetFields()
this.stickinessPolicyForm.resetFields()
},
handleChangePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
this.fetchData()
},
handleChangePageSize (currentPage, pageSize) {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
}
}
}
@ -1370,4 +1410,16 @@ export default {
}
}
.pagination {
margin-top: 20px;
}
.actions {
button {
&:not(:last-child) {
margin-right: 10px;
}
}
}
</style>

View File

@ -71,37 +71,47 @@
<a-divider/>
<a-list :loading="loading" style="min-height: 25px;">
<a-list-item v-for="rule in portForwardRules" :key="rule.id" class="rule">
<div class="rule-container">
<div class="rule__item">
<div class="rule__title">{{ $t('privateport') }}</div>
<div>{{ rule.privateport }} - {{ rule.privateendport }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('publicport') }}</div>
<div>{{ rule.publicport }} - {{ rule.publicendport }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('protocol') }}</div>
<div>{{ rule.protocol | capitalise }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('state') }}</div>
<div>{{ rule.state }}</div>
</div>
<div class="rule__item">
<div class="rule__title">{{ $t('vm') }}</div>
<div class="rule__title"></div>
<div><a-icon type="desktop"/> <router-link :to="{ path: '/vm/' + rule.virtualmachineid }">{{ rule.virtualmachinename }}</router-link> ({{ rule.vmguestip }})</div>
</div>
<div slot="actions">
<a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(rule.id)" />
<a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(rule)" />
</div>
<a-table
size="small"
style="overflow-y: auto"
:loading="loading"
:columns="columns"
:dataSource="portForwardRules"
:pagination="false"
:rowKey="record => record.id">
<template slot="privateport" slot-scope="record">
{{ record.privateport }} - {{ record.privateendport }}
</template>
<template slot="publicport" slot-scope="record">
{{ record.publicport }} - {{ record.publicendport }}
</template>
<template slot="protocol" slot-scope="record">
{{ record.protocol | capitalise }}
</template>
<template slot="vm" slot-scope="record">
<div><a-icon type="desktop"/>
<router-link
:to="{ path: '/vm/' + record.virtualmachineid }">
{{ record.virtualmachinename }}</router-link> ({{ record.vmguestip }})</div>
</template>
<template slot="actions" slot-scope="record">
<div class="actions">
<a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(record.id)" />
<a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(record)" />
</div>
</a-list-item>
</a-list>
</template>
</a-table>
<a-pagination
class="pagination"
size="small"
:current="page"
:pageSize="pageSize"
:total="totalCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="handleChangePage"
@showSizeChange="handleChangePageSize"
showSizeChanger/>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
<span v-show="tagsModalLoading" class="tags-modal-loading">
@ -227,7 +237,36 @@ export default {
addVmModalLoading: false,
addVmModalNicLoading: false,
vms: [],
nics: []
nics: [],
totalCount: 0,
page: 1,
pageSize: 10,
columns: [
{
title: this.$t('privateport'),
scopedSlots: { customRender: 'privateport' }
},
{
title: this.$t('publicport'),
scopedSlots: { customRender: 'publicport' }
},
{
title: this.$t('protocol'),
scopedSlots: { customRender: 'protocol' }
},
{
title: this.$t('state'),
dataIndex: 'state'
},
{
title: this.$t('vm'),
scopedSlots: { customRender: 'vm' }
},
{
title: this.$t('action'),
scopedSlots: { customRender: 'actions' }
}
]
}
},
mounted () {
@ -253,9 +292,12 @@ export default {
this.loading = true
api('listPortForwardingRules', {
listAll: true,
ipaddressid: this.resource.id
ipaddressid: this.resource.id,
page: this.page,
pageSize: this.pageSize
}).then(response => {
this.portForwardRules = response.listportforwardingrulesresponse.portforwardingrule
this.portForwardRules = response.listportforwardingrulesresponse.portforwardingrule || []
this.totalCount = response.listportforwardingrulesresponse.count || 0
}).catch(error => {
this.$notification.error({
message: `Error ${error.response.status}`,
@ -487,6 +529,16 @@ export default {
})
this.closeModal()
})
},
handleChangePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
this.fetchData()
},
handleChangePageSize (currentPage, pageSize) {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
}
}
}
@ -590,7 +642,6 @@ export default {
}
.rule-action {
margin-bottom: 20px;
&:not(:last-of-type) {
margin-right: 10px;
@ -672,4 +723,8 @@ export default {
}
.pagination {
margin-top: 20px;
}
</style>