mirror of https://github.com/apache/cloudstack.git
UI: bulk action support for various resources (#5034)
* ui: support bulk action for various resources * ui: support bulk action for various resources * Bulk actions support - progress review * Extract common code + suppress error notification with bulk actions * cleanup + suppress notification * add progress view * Add routes to notification + add async jobs + refactor progress view * minor tweaks * fix group action for vpn users * Refactor code * Unique row key * remove redundant cols * address comments * Added the following: 1. Make Cancel as default button for bulk actions 2. Add Filter Filter on the Operation status Column - Progress View 3. For Stop and delete bulk operations - add An alert message(in Red) to inform users that it is a destructive operation * Add dynamism to column filtering
This commit is contained in:
parent
ff07fee286
commit
15d3d39cb2
|
|
@ -71,6 +71,13 @@
|
|||
"label.action.attach.disk.processing": "Attaching Disk....",
|
||||
"label.action.attach.iso": "Attach ISO",
|
||||
"label.action.attach.iso.processing": "Attaching ISO....",
|
||||
"label.action.bulk.delete.egress.firewall.rules": "Bulk delete egress firewall rules",
|
||||
"label.action.bulk.delete.firewall.rules": "Bulk delete firewall rules",
|
||||
"label.action.bulk.delete.load.balancer.rules": "Bulk delete load balancer rules",
|
||||
"label.action.bulk.delete.templates": "Bulk delete templates",
|
||||
"label.action.bulk.delete.isos": "Bulk delete ISOs",
|
||||
"label.action.bulk.delete.portforward.rules": "Bulk delete Port Forward rules",
|
||||
"label.action.bulk.release.public.ip.address": "Bulk release Public IP Addresses",
|
||||
"label.action.cancel.maintenance.mode": "Cancel Maintenance Mode",
|
||||
"label.action.cancel.maintenance.mode.processing": "Cancelling Maintenance Mode....",
|
||||
"label.action.change.password": "Change Password",
|
||||
|
|
@ -99,6 +106,7 @@
|
|||
"label.action.delete.disk.offering.processing": "Deleting Disk Offering....",
|
||||
"label.action.delete.domain": "Delete Domain",
|
||||
"label.action.delete.domain.processing": "Deleting Domain....",
|
||||
"label.action.delete.egress.firewall": "Delete egress firewall rule",
|
||||
"label.action.delete.firewall": "Delete firewall rule",
|
||||
"label.action.delete.firewall.processing": "Deleting Firewall....",
|
||||
"label.action.delete.ingress.rule": "Delete Ingress Rule",
|
||||
|
|
@ -586,6 +594,13 @@
|
|||
"label.confirmdeclineinvitation": "Are you sure you want to decline this project invitation?",
|
||||
"label.confirmpassword": "Confirm Password",
|
||||
"label.confirmpassword.description": "Please type the same password again",
|
||||
"label.confirm.delete.egress.firewall.rules": "Please confirm you wish to delete the selected egress firewall rules",
|
||||
"label.confirm.delete.firewall.rules": "Please confirm you wish to delete the selected firewall rules",
|
||||
"label.confirm.delete.loadbalancer.rules": "Please confirm you wish to delete the selected load balancing rules",
|
||||
"label.confirm.delete.portforward.rules": "Please confirm you wish to delete the selected port-forward rules",
|
||||
"label.confirm.delete.templates": "Please confirm you wish to delete the selected templates",
|
||||
"label.confirm.delete.isos": "Please confirm you wish to delete the selected isos",
|
||||
"label.confirm.release.public.ip.addresses": "Please confirm you wish to release the selected public IP addresses",
|
||||
"label.congratulations": "Congratulations!",
|
||||
"label.connectiontimeout": "Connection Timeout",
|
||||
"label.conservemode": "Conserve mode",
|
||||
|
|
@ -693,6 +708,7 @@
|
|||
"label.delete.opendaylight.device": "Delete OpenDaylight Controller",
|
||||
"label.delete.pa": "Delete Palo Alto",
|
||||
"label.delete.portable.ip.range": "Delete Portable IP Range",
|
||||
"label.delete.portforward.rules": "Delete Port Forward Rules",
|
||||
"label.delete.project": "Delete project",
|
||||
"label.delete.project.role": "Delete Project Role",
|
||||
"label.delete.role": "Delete Role",
|
||||
|
|
@ -902,6 +918,7 @@
|
|||
"label.filterby": "Filter by",
|
||||
"label.fingerprint": "FingerPrint",
|
||||
"label.firewall": "Firewall",
|
||||
"label.firewallrule": "Firewall Rule",
|
||||
"label.firstname": "First Name",
|
||||
"label.firstname.lower": "firstname",
|
||||
"label.fix.errors": "Fix errors",
|
||||
|
|
@ -1169,6 +1186,7 @@
|
|||
"label.isvolatile": "Volatile",
|
||||
"label.item.listing": "Item listing",
|
||||
"label.items": "items",
|
||||
"label.items.selected": "item(s) selected",
|
||||
"label.japanese.keyboard": "Japanese keyboard",
|
||||
"label.keep": "Keep",
|
||||
"label.keep.colon": "Keep:",
|
||||
|
|
@ -1520,6 +1538,7 @@
|
|||
"label.opendaylight.controllerdetail": "OpenDaylight Controller Details",
|
||||
"label.opendaylight.controllers": "OpenDaylight Controllers",
|
||||
"label.operation": "Operation",
|
||||
"label.operation.status": "Operation Status",
|
||||
"label.optional": "Optional",
|
||||
"label.order": "Order",
|
||||
"label.oscategoryid": "OS Preference",
|
||||
|
|
@ -1605,6 +1624,7 @@
|
|||
"label.portable.ip.ranges": "Portable IP Ranges",
|
||||
"label.portableipaddress": "Portable IPs",
|
||||
"label.portforwarding": "Port Forwarding",
|
||||
"label.portforwarding.rule": "Port Forwarding Rule",
|
||||
"label.powerflex.gateway": "Gateway",
|
||||
"label.powerflex.gateway.username": "Gateway Username",
|
||||
"label.powerflex.gateway.password": "Gateway Password",
|
||||
|
|
@ -2399,6 +2419,7 @@
|
|||
"message.action.delete.external.firewall": "Please confirm that you would like to remove this external firewall. Warning: If you are planning to add back the same external firewall, you must reset usage data on the device.",
|
||||
"message.action.delete.external.load.balancer": "Please confirm that you would like to remove this external load balancer. Warning: If you are planning to add back the same external load balancer, you must reset usage data on the device.",
|
||||
"message.action.delete.ingress.rule": "Please confirm that you want to delete this ingress rule.",
|
||||
"message.action.delete.instance.group": "Please confirm that you want to delete the instance group",
|
||||
"message.action.delete.iso": "Please confirm that you want to delete this ISO.",
|
||||
"message.action.delete.iso.for.all.zones": "The ISO is used by all zones. Please confirm that you want to delete it from all zones.",
|
||||
"message.action.delete.network": "Please confirm that you want to delete this network.",
|
||||
|
|
@ -3323,6 +3344,8 @@
|
|||
"state.error": "Error",
|
||||
"state.expired": "Expired",
|
||||
"state.expunging": "Expunging",
|
||||
"state.failed": "Failed",
|
||||
"state.inprogress": "In Progress",
|
||||
"state.migrating": "Migrating",
|
||||
"state.pending": "Pending",
|
||||
"state.readonly": "Read-Only",
|
||||
|
|
|
|||
|
|
@ -33,8 +33,11 @@
|
|||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item v-for="(job, index) in jobs" :key="index">
|
||||
<a-list-item-meta :title="job.title" :description="job.description">
|
||||
<a-avatar :style="notificationAvatar[job.status].style" :icon="notificationAvatar[job.status].icon" slot="avatar"/>
|
||||
<a-list-item-meta :title="job.title">
|
||||
<a-avatar :style="notificationAvatar[job.status].style" :icon="notificationAvatar[job.status].icon" slot="avatar"/><br/>
|
||||
<span v-if="getResourceName(job.description, 'name') && job.path" slot="description"><router-link :to="{ path: job.path}"> {{ getResourceName(job.description, "name") + ' - ' }}</router-link></span>
|
||||
<span v-if="getResourceName(job.description, 'name') && job.path" slot="description"> {{ getResourceName(job.description, "msg") }}</span>
|
||||
<span v-else slot="description"> {{ job.description }} </span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
|
|
@ -80,6 +83,16 @@ export default {
|
|||
this.pollJobs()
|
||||
}, 4000)
|
||||
},
|
||||
getResourceName (description, data) {
|
||||
if (description) {
|
||||
if (data === 'name') {
|
||||
const name = description.match(/\(([^)]+)\)/)
|
||||
return name ? name[1] : null
|
||||
}
|
||||
const msg = description.substring(description.indexOf(')') + 1)
|
||||
return msg
|
||||
}
|
||||
},
|
||||
async pollJobs () {
|
||||
var hasUpdated = false
|
||||
for (var i in this.jobs) {
|
||||
|
|
@ -102,12 +115,14 @@ export default {
|
|||
if (result.jobresult.errortext !== null) {
|
||||
this.jobs[i].description = '(' + this.jobs[i].description + ') ' + result.jobresult.errortext
|
||||
}
|
||||
this.$notification.error({
|
||||
message: this.jobs[i].title,
|
||||
description: this.jobs[i].description,
|
||||
key: this.jobs[i].jobid,
|
||||
duration: 0
|
||||
})
|
||||
if (!this.jobs[i].bulkAction) {
|
||||
this.$notification.error({
|
||||
message: this.jobs[i].title,
|
||||
description: this.jobs[i].description,
|
||||
key: this.jobs[i].jobid,
|
||||
duration: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}).catch(function (e) {
|
||||
console.log(this.$t('error.fetching.async.job.result') + e)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,191 @@
|
|||
// 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>
|
||||
<a-modal
|
||||
:visible="showGroupActionModal"
|
||||
:closable="true"
|
||||
:maskClosable="false"
|
||||
:cancelText="$t('label.cancel')"
|
||||
@cancel="handleCancel"
|
||||
width="50vw"
|
||||
style="top: 20px;overflow-y: auto"
|
||||
centered
|
||||
>
|
||||
<span slot="title"> {{ $t(message.title) }}
|
||||
<a
|
||||
v-if="message.docHelp || $route.meta.docHelp"
|
||||
style="margin-left: 5px"
|
||||
:href="$config.docBase + '/' + (message.docHelp || $route.meta.docHelp)"
|
||||
target="_blank">
|
||||
<a-icon type="question-circle-o"></a-icon>
|
||||
</a>
|
||||
</span>
|
||||
<template slot="footer">
|
||||
<a-button key="back" @click="handleCancel"> {{ $t('label.close') }} </a-button>
|
||||
</template>
|
||||
<a-card :bordered="false" style="background:#f1f1f1">
|
||||
<div><a-icon type="check-circle-o" style="color: #52c41a; margin-right: 8px"/> {{ $t('label.success') + ': ' + succeededCount }}</div>
|
||||
<div><a-icon type="close-circle-o" style="color: #f5222d; margin-right: 8px"/> {{ $t('state.failed') + ': ' + failedCount }}</div>
|
||||
<div><a-icon type="sync-o" style="color: #1890ff; margin-right: 8px"/> {{ $t('state.inprogress') + ': ' + selectedItems.filter(item => item.status === 'InProgress').length || 0 }}</div>
|
||||
</a-card>
|
||||
<a-divider />
|
||||
<div v-if="showGroupActionModal">
|
||||
<a-table
|
||||
v-if="selectedItems.length > 0"
|
||||
size="middle"
|
||||
:columns="selectedColumns"
|
||||
:dataSource="tableChanged ? filteredItems : selectedItems"
|
||||
:rowKey="(record, idx) => (this.$route.path.includes('/template') || this.$route.path.includes('/iso')) ? record.zoneid: record.id"
|
||||
:pagination="true"
|
||||
@change="handleTableChange"
|
||||
style="overflow-y: auto">
|
||||
<div slot="status" slot-scope="text">
|
||||
<status :text=" text ? text : $t('state.inprogress') " displayText></status>
|
||||
</div>
|
||||
<template slot="algorithm" slot-scope="record">
|
||||
{{ returnAlgorithmName(record.algorithm) }}
|
||||
</template>
|
||||
<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="startport" slot-scope="record">
|
||||
{{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
|
||||
</template>
|
||||
<template slot="endport" slot-scope="record">
|
||||
{{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
|
||||
</template>
|
||||
<template slot="vm" slot-scope="record">
|
||||
<div><a-icon type="desktop"/> {{ record.virtualmachinename }} ({{ record.vmguestip }})</div>
|
||||
</template>
|
||||
</a-table>
|
||||
<br/>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import Status from '@/components/widgets/Status'
|
||||
|
||||
export default {
|
||||
name: 'BulkActionProgress',
|
||||
components: {
|
||||
Status
|
||||
},
|
||||
filters: {
|
||||
capitalise: val => {
|
||||
if (val === 'all') return 'All'
|
||||
return val.toUpperCase()
|
||||
}
|
||||
},
|
||||
props: {
|
||||
showGroupActionModal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
message: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.filteredItems = this.selectedItems
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
appliedFilterStatus: {},
|
||||
filteredItems: [],
|
||||
filterItemsTimer: null,
|
||||
tableChanged: false
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
watch: {
|
||||
succeededCount (count) {
|
||||
if (count > 0) {
|
||||
this.filterItemsDelayed()
|
||||
}
|
||||
},
|
||||
failedCount (count) {
|
||||
if (count > 0) {
|
||||
this.filterItemsDelayed()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
succeededCount () {
|
||||
return this.selectedItems.filter(item => item.status === 'success').length || 0
|
||||
},
|
||||
failedCount () {
|
||||
return this.selectedItems.filter(item => item.status === 'failed').length || 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTableChange (pagination, filters, sorter) {
|
||||
this.filteredItems = this.selectedItems
|
||||
this.appliedFilterStatus = filters.status
|
||||
this.filterItems()
|
||||
this.tableChanged = true
|
||||
},
|
||||
filterItems () {
|
||||
if (this.appliedFilterStatus && this.appliedFilterStatus.length > 0) {
|
||||
this.filteredItems = this.selectedItems.filter(item => {
|
||||
if (this.appliedFilterStatus.includes(item.status)) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
filterItemsDelayed () {
|
||||
clearTimeout(this.filterItemsTimer)
|
||||
this.filterItemsTimer = setTimeout(() => {
|
||||
this.filterItems()
|
||||
}, 50)
|
||||
},
|
||||
handleCancel () {
|
||||
this.filteredItems = []
|
||||
this.tableChanged = false
|
||||
this.$emit('handle-cancel')
|
||||
},
|
||||
returnAlgorithmName (name) {
|
||||
switch (name) {
|
||||
case 'leastconn':
|
||||
return 'Least connections'
|
||||
case 'roundrobin' :
|
||||
return 'Round-robin'
|
||||
case 'source':
|
||||
return 'Source'
|
||||
default :
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
// 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-modal
|
||||
v-if="showConfirmationAction"
|
||||
:visible="showConfirmationAction"
|
||||
:closable="true"
|
||||
:maskClosable="false"
|
||||
:okText="$t('label.ok')"
|
||||
:cancelText="$t('label.cancel')"
|
||||
style="top: 20px;"
|
||||
width="50vw"
|
||||
@ok="groupAction"
|
||||
@cancel="closeModal"
|
||||
:ok-button-props="{props: { type: 'default' } }"
|
||||
:cancel-button-props="{props: { type: 'primary' } }"
|
||||
centered>
|
||||
<span slot="title">
|
||||
{{ $t(message.title) }}
|
||||
</span>
|
||||
<span>
|
||||
<a-alert
|
||||
v-if="isDestructiveAction()"
|
||||
type="error">
|
||||
<a-icon slot="message" type="exclamation-circle" style="color: red; fontSize: 30px; display: inline-flex" />
|
||||
<span style="padding-left: 5px" slot="message" v-html="`<b>${selectedRowKeys.length} ` + $t('label.items.selected') + `. </b>`" />
|
||||
<span slot="message" v-html="$t(message.confirmMessage)" />
|
||||
</a-alert>
|
||||
<a-alert v-else type="warning">
|
||||
<span v-if="selectedRowKeys.length > 0" slot="message" v-html="`<b>${selectedRowKeys.length} ` + $t('label.items.selected') + `. </b>`" />
|
||||
<span slot="message" v-html="$t(message.confirmMessage)" />
|
||||
</a-alert>
|
||||
<a-divider />
|
||||
<a-table
|
||||
size="middle"
|
||||
:columns="selectedColumns"
|
||||
:dataSource="selectedItems"
|
||||
:rowKey="(record, idx) => this.$route.path.includes('/iso/') ? record.zoneid : record.id"
|
||||
:pagination="true"
|
||||
style="overflow-y: auto">
|
||||
<template slot="algorithm" slot-scope="record">
|
||||
{{ returnAlgorithmName(record.algorithm) }}
|
||||
</template>
|
||||
<template v-for="(column, index) in selectedColumns" :slot="column" slot-scope="text" >
|
||||
<span :key="index"> {{ text }} ==== {{ column }} </span>
|
||||
</template>
|
||||
<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"/> {{ record.virtualmachinename }} ({{ record.vmguestip }})</div>
|
||||
</template>
|
||||
<template slot="startport" slot-scope="record">
|
||||
{{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
|
||||
</template>
|
||||
<template slot="endport" slot-scope="record">
|
||||
{{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
|
||||
</template>
|
||||
</a-table>
|
||||
<a-divider />
|
||||
<br/>
|
||||
</span>
|
||||
</a-modal>
|
||||
|
||||
<bulk-action-progress
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:selectedItems="selectedItems"
|
||||
:selectedColumns="selectedColumns"
|
||||
:message="message"
|
||||
@handle-cancel="handleCancel" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Status from '@/components/widgets/Status'
|
||||
import BulkActionProgress from '@/components/view/BulkActionProgress'
|
||||
|
||||
export default {
|
||||
name: 'BulkActionView',
|
||||
components: {
|
||||
Status,
|
||||
BulkActionProgress
|
||||
},
|
||||
props: {
|
||||
showConfirmationAction: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showGroupActionModal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedRowKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
message: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
capitalise: val => {
|
||||
if (val === 'all') return 'All'
|
||||
return val.toUpperCase()
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
data () {
|
||||
return {
|
||||
totalCount: 0,
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCancel () {
|
||||
this.$emit('handle-cancel')
|
||||
},
|
||||
groupAction () {
|
||||
this.$emit('group-action')
|
||||
},
|
||||
isDestructiveAction () {
|
||||
if (new RegExp(['remove', 'delete', 'destroy', 'stop', 'release', 'disassociate'].join('|')).test(this.action)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
returnAlgorithmName (name) {
|
||||
switch (name) {
|
||||
case 'leastconn':
|
||||
return 'Least connections'
|
||||
case 'roundrobin' :
|
||||
return 'Round-robin'
|
||||
case 'source':
|
||||
return 'Source'
|
||||
default :
|
||||
return ''
|
||||
}
|
||||
},
|
||||
closeModal () {
|
||||
this.$emit('close-modal')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -23,8 +23,7 @@
|
|||
:dataSource="items"
|
||||
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
||||
:pagination="false"
|
||||
:rowSelection="['vm', 'alert'].includes($route.name) || $route.name === 'event' && $store.getters.userInfo.roletype === 'Admin'
|
||||
? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
|
||||
:rowSelection=" enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
|
||||
:rowClassName="getRowClassName"
|
||||
style="overflow-y: auto"
|
||||
>
|
||||
|
|
@ -422,6 +421,13 @@ export default {
|
|||
'/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering'].join('|'))
|
||||
.test(this.$route.path)
|
||||
},
|
||||
enableGroupAction () {
|
||||
return ['vm', 'alert', 'vmgroup', 'ssh', 'affinitygroup', 'volume', 'snapshot',
|
||||
'vmsnapshot', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway',
|
||||
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
|
||||
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes'
|
||||
].includes(this.$route.name)
|
||||
},
|
||||
fetchColumns () {
|
||||
if (this.isOrderUpdatable()) {
|
||||
return this.columns
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ export default {
|
|||
case 'ReadWrite':
|
||||
state = this.$t('state.readwrite')
|
||||
break
|
||||
case 'InProgress':
|
||||
state = this.$t('state.inprogress')
|
||||
break
|
||||
}
|
||||
return state.charAt(0).toUpperCase() + state.slice(1)
|
||||
}
|
||||
|
|
@ -102,6 +105,7 @@ export default {
|
|||
case 'True':
|
||||
case 'Up':
|
||||
case 'enabled':
|
||||
case 'success':
|
||||
status = 'success'
|
||||
break
|
||||
case 'Alert':
|
||||
|
|
@ -112,6 +116,7 @@ export default {
|
|||
case 'Error':
|
||||
case 'False':
|
||||
case 'Stopped':
|
||||
case 'failed':
|
||||
status = 'error'
|
||||
break
|
||||
case 'Migrating':
|
||||
|
|
@ -119,6 +124,7 @@ export default {
|
|||
case 'Starting':
|
||||
case 'Stopping':
|
||||
case 'Upgrading':
|
||||
case 'InProgress':
|
||||
status = 'processing'
|
||||
break
|
||||
case 'Allocated':
|
||||
|
|
|
|||
|
|
@ -116,7 +116,10 @@ export default {
|
|||
!(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1) &&
|
||||
(record.state === 'disabled' || record.state === 'locked')
|
||||
},
|
||||
params: { lock: 'false' }
|
||||
params: { lock: 'false' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'disableAccount',
|
||||
|
|
@ -134,7 +137,10 @@ export default {
|
|||
lock: {
|
||||
value: (record) => { return false }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x, lock: false } }) }
|
||||
},
|
||||
{
|
||||
api: 'disableAccount',
|
||||
|
|
@ -152,7 +158,10 @@ export default {
|
|||
lock: {
|
||||
value: (record) => { return true }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x, lock: true } }) }
|
||||
},
|
||||
{
|
||||
api: 'uploadSslCert',
|
||||
|
|
@ -180,7 +189,10 @@ export default {
|
|||
show: (record, store) => {
|
||||
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||
!(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1)
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,10 @@ export default {
|
|||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
|
||||
},
|
||||
{
|
||||
api: 'restoreVirtualMachine',
|
||||
|
|
@ -452,7 +455,10 @@ export default {
|
|||
message: 'message.kubernetes.cluster.start',
|
||||
docHelp: 'plugins/cloudstack-kubernetes-service.html#starting-a-stopped-kubernetes-cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Stopped'].includes(record.state) }
|
||||
show: (record) => { return ['Stopped'].includes(record.state) },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'stopKubernetesCluster',
|
||||
|
|
@ -461,7 +467,10 @@ export default {
|
|||
message: 'message.kubernetes.cluster.stop',
|
||||
docHelp: 'plugins/cloudstack-kubernetes-service.html#stopping-kubernetes-cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return !['Stopped', 'Destroyed', 'Destroying'].includes(record.state) }
|
||||
show: (record) => { return !['Stopped', 'Destroyed', 'Destroying'].includes(record.state) },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'scaleKubernetesCluster',
|
||||
|
|
@ -492,7 +501,10 @@ export default {
|
|||
message: 'message.kubernetes.cluster.delete',
|
||||
docHelp: 'plugins/cloudstack-kubernetes-service.html#deleting-kubernetes-cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return !['Destroyed', 'Destroying'].includes(record.state) }
|
||||
show: (record) => { return !['Destroyed', 'Destroying'].includes(record.state) },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -528,7 +540,11 @@ export default {
|
|||
api: 'deleteInstanceGroup',
|
||||
icon: 'delete',
|
||||
label: 'label.delete.instance.group',
|
||||
dataView: true
|
||||
message: 'message.action.delete.instance.group',
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -578,6 +594,16 @@ export default {
|
|||
domainid: {
|
||||
value: (record, params) => { return record.domainid }
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values, record) => {
|
||||
return selection.map(x => {
|
||||
const data = record.filter(y => { return y.name === x })
|
||||
return {
|
||||
name: x, account: data[0].account, domainid: data[0].domainid
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -621,7 +647,10 @@ export default {
|
|||
label: 'label.delete.affinity.group',
|
||||
docHelp: 'adminguide/virtual_machines.html#delete-an-affinity-group',
|
||||
message: 'message.delete.affinity.group',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ export default {
|
|||
label: 'label.action.start.router',
|
||||
message: 'message.confirm.start.lb.vm',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state === 'Stopped' }
|
||||
show: (record) => { return record.state === 'Stopped' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'stopInternalLoadBalancerVM',
|
||||
|
|
@ -38,7 +41,10 @@ export default {
|
|||
label: 'label.action.stop.router',
|
||||
dataView: true,
|
||||
args: ['forced'],
|
||||
show: (record) => { return record.state === 'Running' }
|
||||
show: (record) => { return record.state === 'Running' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
|
||||
},
|
||||
{
|
||||
api: 'migrateSystemVm',
|
||||
|
|
|
|||
|
|
@ -49,7 +49,10 @@ export default {
|
|||
label: 'label.action.start.router',
|
||||
message: 'message.action.start.router',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state === 'Stopped' }
|
||||
show: (record) => { return record.state === 'Stopped' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'stopRouter',
|
||||
|
|
@ -58,7 +61,10 @@ export default {
|
|||
message: 'message.action.stop.router',
|
||||
dataView: true,
|
||||
args: ['forced'],
|
||||
show: (record) => { return record.state === 'Running' }
|
||||
show: (record) => { return record.state === 'Running' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
|
||||
},
|
||||
{
|
||||
api: 'rebootRouter',
|
||||
|
|
@ -67,7 +73,10 @@ export default {
|
|||
message: 'message.action.reboot.router',
|
||||
dataView: true,
|
||||
args: ['forced'],
|
||||
hidden: (record) => { return record.state === 'Running' }
|
||||
hidden: (record) => { return record.state === 'Running' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
|
||||
},
|
||||
{
|
||||
api: 'scaleSystemVm',
|
||||
|
|
@ -156,7 +165,10 @@ export default {
|
|||
label: 'label.destroy.router',
|
||||
message: 'message.confirm.destroy.router',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Running', 'Error', 'Stopped'].includes(record.state) }
|
||||
show: (record) => { return ['Running', 'Error', 'Stopped'].includes(record.state) },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ export default {
|
|||
label: 'label.action.start.systemvm',
|
||||
message: 'message.action.start.systemvm',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state === 'Stopped' }
|
||||
show: (record) => { return record.state === 'Stopped' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'stopSystemVm',
|
||||
|
|
@ -39,7 +42,10 @@ export default {
|
|||
message: 'message.action.stop.systemvm',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state === 'Running' },
|
||||
args: ['forced']
|
||||
args: ['forced'],
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
|
||||
},
|
||||
{
|
||||
api: 'rebootSystemVm',
|
||||
|
|
@ -48,7 +54,10 @@ export default {
|
|||
message: 'message.action.reboot.systemvm',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state === 'Running' },
|
||||
args: ['forced']
|
||||
args: ['forced'],
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
|
||||
},
|
||||
{
|
||||
api: 'scaleSystemVm',
|
||||
|
|
@ -121,7 +130,10 @@ export default {
|
|||
label: 'label.action.destroy.systemvm',
|
||||
message: 'message.action.destroy.systemvm',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Running', 'Error', 'Stopped'].includes(record.state) }
|
||||
show: (record) => { return ['Running', 'Error', 'Stopped'].includes(record.state) },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@ export default {
|
|||
message: 'message.restart.network',
|
||||
dataView: true,
|
||||
args: ['cleanup'],
|
||||
show: (record) => record.type !== 'L2'
|
||||
show: (record) => record.type !== 'L2',
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, cleanup: values.cleanup } }) }
|
||||
},
|
||||
{
|
||||
api: 'replaceNetworkACLList',
|
||||
|
|
@ -114,7 +117,10 @@ export default {
|
|||
icon: 'delete',
|
||||
label: 'label.action.delete.network',
|
||||
message: 'message.action.delete.network',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -174,14 +180,20 @@ export default {
|
|||
fields.push('makeredundant')
|
||||
}
|
||||
return fields
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, cleanup: values.cleanup, makeredundant: values.makeredundant } }) }
|
||||
},
|
||||
{
|
||||
api: 'deleteVPC',
|
||||
icon: 'delete',
|
||||
label: 'label.remove.vpc',
|
||||
message: 'message.remove.vpc',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -257,7 +269,10 @@ export default {
|
|||
}, {
|
||||
name: 'firewall',
|
||||
component: () => import('@/views/network/FirewallRules.vue'),
|
||||
networkServiceFilter: networkService => networkService.filter(x => x.name === 'Firewall').length > 0
|
||||
networkServiceFilter: networkService => networkService.filter(x => x.name === 'Firewall').length > 0,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
name: 'portforwarding',
|
||||
|
|
@ -305,7 +320,10 @@ export default {
|
|||
message: 'message.action.release.ip',
|
||||
docHelp: 'adminguide/networking_and_traffic.html#releasing-an-ip-address-alloted-to-a-vpc',
|
||||
dataView: true,
|
||||
show: (record) => { return !record.issourcenat }
|
||||
show: (record) => { return !record.issourcenat },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -583,6 +601,16 @@ export default {
|
|||
account: {
|
||||
value: (record) => { return record.account }
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values, record) => {
|
||||
return selection.map(x => {
|
||||
const data = record.filter(y => { return y.id === x })
|
||||
return {
|
||||
username: data[0].username, account: data[0].account, domainid: data[0].domainid
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -624,7 +652,10 @@ export default {
|
|||
label: 'label.delete.vpn.customer.gateway',
|
||||
message: 'message.delete.vpn.customer.gateway',
|
||||
docHelp: 'adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,10 @@ export default {
|
|||
label: 'label.action.delete.service.offering',
|
||||
message: 'message.action.delete.service.offering',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
@ -112,7 +115,10 @@ export default {
|
|||
message: 'message.action.delete.system.service.offering',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true,
|
||||
params: { issystem: 'true' }
|
||||
params: { issystem: 'true' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
@ -165,7 +171,10 @@ export default {
|
|||
label: 'label.action.delete.disk.offering',
|
||||
message: 'message.action.delete.disk.offering',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
@ -190,7 +199,10 @@ export default {
|
|||
label: 'label.action.delete.backup.offering',
|
||||
message: 'message.action.delete.backup.offering',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
@ -234,7 +246,10 @@ export default {
|
|||
state: {
|
||||
value: (record) => { return 'Enabled' }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Enabled' } }) }
|
||||
}, {
|
||||
api: 'updateNetworkOffering',
|
||||
icon: 'pause-circle',
|
||||
|
|
@ -247,7 +262,10 @@ export default {
|
|||
state: {
|
||||
value: (record) => { return 'Disabled' }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Disabled' } }) }
|
||||
}, {
|
||||
api: 'updateNetworkOffering',
|
||||
icon: 'lock',
|
||||
|
|
@ -262,7 +280,10 @@ export default {
|
|||
label: 'label.remove.network.offering',
|
||||
message: 'message.confirm.remove.network.offering',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
@ -306,7 +327,10 @@ export default {
|
|||
state: {
|
||||
value: (record) => { return 'Enabled' }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Enabled' } }) }
|
||||
}, {
|
||||
api: 'updateVPCOffering',
|
||||
icon: 'pause-circle',
|
||||
|
|
@ -319,7 +343,10 @@ export default {
|
|||
state: {
|
||||
value: (record) => { return 'Disabled' }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Disabled' } }) }
|
||||
}, {
|
||||
api: 'updateVPCOffering',
|
||||
icon: 'lock',
|
||||
|
|
@ -332,7 +359,10 @@ export default {
|
|||
icon: 'delete',
|
||||
label: 'label.remove.vpc.offering',
|
||||
message: 'message.confirm.remove.vpc.offering',
|
||||
dataView: true
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -104,7 +104,10 @@ export default {
|
|||
dataView: true,
|
||||
show: (record, store) => {
|
||||
return ((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin) && record.state === 'Suspended'
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'suspendProject',
|
||||
|
|
@ -116,7 +119,10 @@ export default {
|
|||
show: (record, store) => {
|
||||
return ((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) ||
|
||||
record.isCurrentUserProjectAdmin) && record.state !== 'Suspended'
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'addAccountToProject',
|
||||
|
|
@ -139,7 +145,10 @@ export default {
|
|||
dataView: true,
|
||||
show: (record, store) => {
|
||||
return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,12 +223,14 @@ export default {
|
|||
label: 'label.action.delete.volume',
|
||||
message: 'message.action.delete.volume',
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
show: (record, store) => {
|
||||
return ['Expunging', 'Expunged', 'UploadError'].includes(record.state) ||
|
||||
['Allocated', 'Uploaded'].includes(record.state) && record.type !== 'ROOT' && !record.virtualmachineid ||
|
||||
((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy')
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
},
|
||||
{
|
||||
api: 'destroyVolume',
|
||||
|
|
@ -311,7 +313,10 @@ export default {
|
|||
label: 'label.action.delete.snapshot',
|
||||
message: 'message.action.delete.snapshot',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state !== 'Destroyed' }
|
||||
show: (record) => { return record.state !== 'Destroyed' },
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -369,7 +374,10 @@ export default {
|
|||
vmsnapshotid: {
|
||||
value: (record) => { return record.id }
|
||||
}
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { vmsnapshotid: x } }) }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export const pollJobPlugin = {
|
|||
* @param {String} [catchMessage=Error caught]
|
||||
* @param {Function} [catchMethod=() => {}]
|
||||
* @param {Object} [action=null]
|
||||
* @param {Object} [bulkAction=false]
|
||||
*/
|
||||
const {
|
||||
jobId,
|
||||
|
|
@ -48,7 +49,8 @@ export const pollJobPlugin = {
|
|||
showLoading = true,
|
||||
catchMessage = i18n.t('label.error.caught'),
|
||||
catchMethod = () => {},
|
||||
action = null
|
||||
action = null,
|
||||
bulkAction = false
|
||||
} = options
|
||||
|
||||
api('queryAsyncJobResult', { jobId }).then(json => {
|
||||
|
|
@ -69,11 +71,13 @@ export const pollJobPlugin = {
|
|||
eventBus.$emit('async-job-complete', action)
|
||||
successMethod(result)
|
||||
} else if (result.jobstatus === 2) {
|
||||
message.error({
|
||||
content: errorMessage,
|
||||
key: jobId,
|
||||
duration: 1
|
||||
})
|
||||
if (!bulkAction) {
|
||||
message.error({
|
||||
content: errorMessage,
|
||||
key: jobId,
|
||||
duration: 1
|
||||
})
|
||||
}
|
||||
var title = errorMessage
|
||||
if (action && action.label) {
|
||||
title = i18n.t(action.label)
|
||||
|
|
@ -82,12 +86,14 @@ export const pollJobPlugin = {
|
|||
if (name) {
|
||||
desc = `(${name}) ${desc}`
|
||||
}
|
||||
notification.error({
|
||||
message: title,
|
||||
description: desc,
|
||||
key: jobId,
|
||||
duration: 0
|
||||
})
|
||||
if (!bulkAction) {
|
||||
notification.error({
|
||||
message: title,
|
||||
description: desc,
|
||||
key: jobId,
|
||||
duration: 0
|
||||
})
|
||||
}
|
||||
eventBus.$emit('async-job-complete', action)
|
||||
errorMethod(result)
|
||||
} else if (result.jobstatus === 0) {
|
||||
|
|
@ -110,6 +116,7 @@ export const pollJobPlugin = {
|
|||
duration: 0
|
||||
})
|
||||
catchMethod && catchMethod()
|
||||
// }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
<slot name="action" v-if="dataView && $route.path.startsWith('/publicip')"></slot>
|
||||
<action-button
|
||||
v-else
|
||||
:style="dataView ? { float: device === 'mobile' ? 'left' : 'right' } : { 'margin-right': '10px', display: 'inline-flex' }"
|
||||
:style="dataView ? { float: device === 'mobile' ? 'left' : 'right' } : { 'margin-right': '10px', display: getStyle(), padding: '5px' }"
|
||||
:loading="loading"
|
||||
:actions="actions"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
|
|
@ -127,8 +127,11 @@
|
|||
:okText="$t('label.ok')"
|
||||
:cancelText="$t('label.cancel')"
|
||||
style="top: 20px;"
|
||||
:width="modalWidth"
|
||||
@ok="handleSubmit"
|
||||
@cancel="closeAction"
|
||||
:ok-button-props="getOkProps()"
|
||||
:cancel-button-props="getCancelProps()"
|
||||
:confirmLoading="actionLoading"
|
||||
centered
|
||||
>
|
||||
|
|
@ -144,9 +147,37 @@
|
|||
</span>
|
||||
<a-spin :spinning="actionLoading">
|
||||
<span v-if="currentAction.message">
|
||||
<a-alert type="warning">
|
||||
<span slot="message" v-html="$t(currentAction.message)" />
|
||||
</a-alert>
|
||||
<div v-if="selectedRowKeys.length > 0">
|
||||
<a-alert
|
||||
v-if="['delete', 'poweroff'].includes(currentAction.icon)"
|
||||
type="error">
|
||||
<a-icon slot="message" type="exclamation-circle" style="color: red; fontSize: 30px; display: inline-flex" />
|
||||
<span style="padding-left: 5px" slot="message" v-html="`<b>${selectedRowKeys.length} ` + $t('label.items.selected') + `. </b>`" />
|
||||
<span slot="message" v-html="$t(currentAction.message)" />
|
||||
</a-alert>
|
||||
<a-alert v-else type="warning">
|
||||
<span v-if="selectedRowKeys.length > 0" slot="message" v-html="`<b>${selectedRowKeys.length} ` + $t('label.items.selected') + `. </b>`" />
|
||||
<span slot="message" v-html="$t(currentAction.message)" />
|
||||
</a-alert>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-alert type="warning">
|
||||
<span slot="message" v-html="$t(currentAction.message)" />
|
||||
</a-alert>
|
||||
</div>
|
||||
<div v-if="selectedRowKeys.length > 0">
|
||||
<a-divider />
|
||||
<a-table
|
||||
v-if="selectedRowKeys.length > 0"
|
||||
size="middle"
|
||||
:columns="chosenColumns"
|
||||
:dataSource="selectedItems"
|
||||
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
||||
:pagination="true"
|
||||
style="overflow-y: auto"
|
||||
>
|
||||
</a-table>
|
||||
</div>
|
||||
<br v-if="currentAction.paramFields.length > 0"/>
|
||||
</span>
|
||||
<a-form
|
||||
|
|
@ -293,6 +324,7 @@
|
|||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
<br />
|
||||
</a-modal>
|
||||
</div>
|
||||
|
||||
|
|
@ -330,6 +362,12 @@
|
|||
</template>
|
||||
</a-pagination>
|
||||
</div>
|
||||
<bulk-action-progress
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:selectedItems="selectedItems"
|
||||
:selectedColumns="selectedColumns"
|
||||
:message="modalInfo"
|
||||
@handle-cancel="handleCancel" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -347,6 +385,7 @@ import ListView from '@/components/view/ListView'
|
|||
import ResourceView from '@/components/view/ResourceView'
|
||||
import ActionButton from '@/components/view/ActionButton'
|
||||
import SearchView from '@/components/view/SearchView'
|
||||
import BulkActionProgress from '@/components/view/BulkActionProgress'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
|
||||
export default {
|
||||
|
|
@ -359,6 +398,7 @@ export default {
|
|||
Status,
|
||||
ActionButton,
|
||||
SearchView,
|
||||
BulkActionProgress,
|
||||
TooltipLabel
|
||||
},
|
||||
mixins: [mixinDevice],
|
||||
|
|
@ -381,7 +421,12 @@ export default {
|
|||
loading: false,
|
||||
actionLoading: false,
|
||||
columns: [],
|
||||
selectedColumns: [],
|
||||
chosenColumns: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
items: [],
|
||||
modalInfo: {},
|
||||
itemCount: 0,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
|
|
@ -398,7 +443,8 @@ export default {
|
|||
actions: [],
|
||||
formModel: {},
|
||||
confirmDirty: false,
|
||||
firstIndex: 0
|
||||
firstIndex: 0,
|
||||
modalWidth: '30vw'
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -416,11 +462,74 @@ export default {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.$route.path.includes('/publicip/') && ['firewall', 'portforwarding', 'loadbalancing'].includes(this.$route.query.tab)) ||
|
||||
(this.$route.path.includes('/guestnetwork/') && (this.$route.query.tab === 'egress.rules' || this.$route.query.tab === 'public.ip.addresses'))) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.$route.path.includes('/template/') || this.$route.path.includes('/iso/')) {
|
||||
return
|
||||
}
|
||||
this.fetchData()
|
||||
})
|
||||
eventBus.$on('exec-action', (action, isGroupAction) => {
|
||||
this.execAction(action, isGroupAction)
|
||||
})
|
||||
eventBus.$on('update-bulk-job-status', (items, action) => {
|
||||
for (const item of items) {
|
||||
this.$store.getters.asyncJobIds.map(function (j) {
|
||||
if (j.jobid === item.jobid) {
|
||||
j.bulkAction = action
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
eventBus.$on('update-job-details', (jobId, resourceId) => {
|
||||
const fullPath = this.$route.fullPath
|
||||
const path = this.$route.path
|
||||
var jobs = this.$store.getters.asyncJobIds.map(job => {
|
||||
if (job.jobid === jobId) {
|
||||
if (resourceId && !path.includes(resourceId)) {
|
||||
job.path = path + '/' + resourceId
|
||||
} else {
|
||||
job.path = fullPath
|
||||
}
|
||||
}
|
||||
return job
|
||||
})
|
||||
|
||||
this.$store.commit('SET_ASYNC_JOB_IDS', jobs)
|
||||
})
|
||||
|
||||
eventBus.$on('update-resource-state', (selectedItems, resource, state, jobid) => {
|
||||
if (selectedItems.length === 0) {
|
||||
return
|
||||
}
|
||||
var tempResource = []
|
||||
if (selectedItems && resource) {
|
||||
if (resource.includes(',')) {
|
||||
resource = resource.split(',')
|
||||
tempResource = resource
|
||||
} else {
|
||||
tempResource.push(resource)
|
||||
}
|
||||
for (var r = 0; r < tempResource.length; r++) {
|
||||
var objIndex = 0
|
||||
if (this.$route.path.includes('/template') || this.$route.path.includes('/iso')) {
|
||||
objIndex = selectedItems.findIndex(obj => (obj.zoneid === tempResource[r]))
|
||||
} else {
|
||||
objIndex = selectedItems.findIndex(obj => (obj.id === tempResource[r] || obj.username === tempResource[r]))
|
||||
}
|
||||
if (state && objIndex !== -1) {
|
||||
selectedItems[objIndex].status = state
|
||||
}
|
||||
if (jobid && objIndex !== -1) {
|
||||
selectedItems[objIndex].jobid = jobid
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (this.device === 'desktop') {
|
||||
this.pageSize = 20
|
||||
|
|
@ -465,7 +574,32 @@ export default {
|
|||
this.fetchData()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected () {
|
||||
return this.selectedRowKeys.length > 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getStyle () {
|
||||
if (['snapshot', 'vmsnapshot', 'publicip'].includes(this.$route.name)) {
|
||||
return 'table-cell'
|
||||
}
|
||||
return 'inline-flex'
|
||||
},
|
||||
getOkProps () {
|
||||
if (this.selectedRowKeys.length > 0 && this.currentAction?.groupAction) {
|
||||
return { props: { type: 'default' } }
|
||||
} else {
|
||||
return { props: { type: 'primary' } }
|
||||
}
|
||||
},
|
||||
getCancelProps () {
|
||||
if (this.selectedRowKeys.length > 0 && this.currentAction?.groupAction) {
|
||||
return { props: { type: 'primary' } }
|
||||
} else {
|
||||
return { props: { type: 'default' } }
|
||||
}
|
||||
},
|
||||
switchProject (projectId) {
|
||||
if (!projectId || !projectId.length || projectId.length !== 36) {
|
||||
return
|
||||
|
|
@ -600,6 +734,12 @@ export default {
|
|||
sorter: function (a, b) { return genericCompare(a[this.dataIndex] || '', b[this.dataIndex] || '') }
|
||||
})
|
||||
}
|
||||
this.chosenColumns = this.columns.filter(column => {
|
||||
return ![this.$t('label.state'), this.$t('label.hostname'), this.$t('label.hostid'), this.$t('label.zonename'),
|
||||
this.$t('label.zone'), this.$t('label.zoneid'), this.$t('label.ip'), this.$t('label.ipaddress'), this.$t('label.privateip'),
|
||||
this.$t('label.linklocalip'), this.$t('label.size'), this.$t('label.sizegb'), this.$t('label.current'),
|
||||
this.$t('label.created'), this.$t('label.order')].includes(column.title)
|
||||
})
|
||||
|
||||
if (['listTemplates', 'listIsos'].includes(this.apiName) && this.dataView) {
|
||||
delete params.showunique
|
||||
|
|
@ -716,6 +856,14 @@ export default {
|
|||
},
|
||||
onRowSelectionChange (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
if (selection?.length > 0) {
|
||||
this.modalWidth = '50vw'
|
||||
this.selectedItems = (this.items.filter(function (item) {
|
||||
return selection.indexOf(item.id) !== -1
|
||||
}))
|
||||
} else {
|
||||
this.modalWidth = '30vw'
|
||||
}
|
||||
},
|
||||
execAction (action, isGroupAction) {
|
||||
const self = this
|
||||
|
|
@ -860,12 +1008,16 @@ export default {
|
|||
}).then(function () {
|
||||
})
|
||||
},
|
||||
pollActionCompletion (jobId, action, resourceName, showLoading = true) {
|
||||
pollActionCompletion (jobId, action, resourceName, resource, showLoading = true) {
|
||||
eventBus.$emit('update-job-details', jobId, resource)
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
name: resourceName,
|
||||
successMethod: result => {
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, resource, 'success')
|
||||
}
|
||||
if (action.response) {
|
||||
const description = action.response(result.jobresult)
|
||||
if (description) {
|
||||
|
|
@ -880,11 +1032,17 @@ export default {
|
|||
action.successMethod(this, result)
|
||||
}
|
||||
},
|
||||
errorMethod: () => this.fetchData(),
|
||||
errorMethod: () => {
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, resource, 'failed')
|
||||
}
|
||||
},
|
||||
loadingMessage: `${this.$t(action.label)} - ${resourceName}`,
|
||||
showLoading: showLoading,
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
action
|
||||
action,
|
||||
bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal
|
||||
})
|
||||
},
|
||||
fillEditFormFieldValues () {
|
||||
|
|
@ -904,8 +1062,33 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.message = {}
|
||||
},
|
||||
handleSubmit (e) {
|
||||
if (!this.dataView && this.currentAction.groupAction && this.selectedRowKeys.length > 0) {
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.selectedColumns = this.chosenColumns
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
this.showGroupActionModal = true
|
||||
this.modalInfo.title = this.currentAction.label
|
||||
this.modalInfo.docHelp = this.currentAction.docHelp
|
||||
}
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.actionLoading = true
|
||||
|
|
@ -913,9 +1096,9 @@ export default {
|
|||
this.items.map(x => {
|
||||
itemsNameMap[x.id] = x.name || x.displaytext || x.id
|
||||
})
|
||||
const paramsList = this.currentAction.groupMap(this.selectedRowKeys, values)
|
||||
const paramsList = this.currentAction.groupMap(this.selectedRowKeys, values, this.items)
|
||||
for (const params of paramsList) {
|
||||
var resourceName = itemsNameMap[params.id]
|
||||
var resourceName = itemsNameMap[params.id || params.vmsnapshotid || params.username || params.name]
|
||||
// Using a method for this since it's an async call and don't want wrong prarms to be passed
|
||||
this.callGroupApi(params, resourceName)
|
||||
}
|
||||
|
|
@ -938,23 +1121,44 @@ export default {
|
|||
callGroupApi (params, resourceName) {
|
||||
const action = this.currentAction
|
||||
api(action.api, params).then(json => {
|
||||
this.handleResponse(json, resourceName, action, false)
|
||||
this.handleResponse(json, resourceName, this.getDataIdentifier(params), action, false)
|
||||
}).catch(error => {
|
||||
if ([401].includes(error.response.status)) {
|
||||
return
|
||||
}
|
||||
this.$notifyError(error)
|
||||
if (this.selectedItems.length !== 0) {
|
||||
this.$notifyError(error)
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, this.getDataIdentifier(params), 'failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
handleResponse (response, resourceName, action, showLoading = true) {
|
||||
getDataIdentifier (params) {
|
||||
var dataIdentifier = ''
|
||||
dataIdentifier = params.id || params.username || params.name || params.vmsnapshotid || params.ids
|
||||
return dataIdentifier
|
||||
},
|
||||
handleResponse (response, resourceName, resource, action, showLoading = true) {
|
||||
for (const obj in response) {
|
||||
if (obj.includes('response')) {
|
||||
if (response[obj].jobid) {
|
||||
const jobid = response[obj].jobid
|
||||
this.$store.dispatch('AddAsyncJob', { title: this.$t(action.label), jobid: jobid, description: resourceName, status: 'progress' })
|
||||
this.pollActionCompletion(jobid, action, resourceName, showLoading)
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: this.$t(action.label),
|
||||
jobid: jobid,
|
||||
description: resourceName,
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, resource, 'InProgress', jobid)
|
||||
this.pollActionCompletion(jobid, action, resourceName, resource, showLoading)
|
||||
return true
|
||||
} else {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, resource, 'success')
|
||||
if (resource) {
|
||||
this.selectedItems.filter(item => item === resource)
|
||||
}
|
||||
}
|
||||
var message = action.successMessage ? this.$t(action.successMessage) : this.$t(action.label) +
|
||||
(resourceName ? ' - ' + resourceName : '')
|
||||
var duration = 2
|
||||
|
|
@ -1049,7 +1253,7 @@ export default {
|
|||
args = [action.api, params]
|
||||
}
|
||||
api(...args).then(json => {
|
||||
hasJobId = this.handleResponse(json, resourceName, action)
|
||||
hasJobId = this.handleResponse(json, resourceName, this.getDataIdentifier(params), action)
|
||||
if ((action.icon === 'delete' || ['archiveEvents', 'archiveAlerts', 'unmanageVirtualMachine'].includes(action.api)) && this.dataView) {
|
||||
this.$router.go(-1)
|
||||
} else {
|
||||
|
|
@ -1064,6 +1268,7 @@ export default {
|
|||
}
|
||||
|
||||
console.log(error)
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, this.getDataIdentifier(params), 'failed')
|
||||
this.$notifyError(error)
|
||||
}).finally(f => {
|
||||
this.actionLoading = false
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@
|
|||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import eventBus from '@/config/eventBus'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
|
||||
export default {
|
||||
|
|
@ -238,6 +239,8 @@ export default {
|
|||
},
|
||||
response: (result) => { return result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${result.virtualmachine.displayname}</b> is <b>${result.virtualmachine.password}</b>` : null }
|
||||
})
|
||||
const resourceId = this.resource.id
|
||||
eventBus.$emit('update-job-details', jobId, resourceId)
|
||||
this.closeAction()
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,14 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<a-button
|
||||
v-if="(('deleteIso' in $store.getters.apis) && this.selectedItems.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t(message.title) }}
|
||||
</a-button>
|
||||
<a-table
|
||||
size="small"
|
||||
style="overflow-y: auto"
|
||||
|
|
@ -24,6 +32,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowKey="record => record.zoneid">
|
||||
<div slot="isready" slot-scope="text, record">
|
||||
<span v-if="record.isready">{{ $t('label.yes') }}</span>
|
||||
|
|
@ -118,17 +127,35 @@
|
|||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
<bulk-action-view
|
||||
v-if="showConfirmationAction || showGroupActionModal"
|
||||
:showConfirmationAction="showConfirmationAction"
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:items="dataSource"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:columns="columns"
|
||||
:selectedColumns="selectedColumns"
|
||||
action="deleteIso"
|
||||
:loading="loading"
|
||||
:message="message"
|
||||
@group-action="deleteIsos"
|
||||
@handle-cancel="handleCancel"
|
||||
@close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionView from '@/components/view/BulkActionView'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
name: 'IsoZones',
|
||||
components: {
|
||||
TooltipButton
|
||||
TooltipButton,
|
||||
BulkActionView
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -153,7 +180,18 @@ export default {
|
|||
zones: [],
|
||||
zoneLoading: false,
|
||||
copyLoading: false,
|
||||
deleteLoading: false
|
||||
deleteLoading: false,
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['Status', 'Ready'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.delete.isos'),
|
||||
confirmMessage: this.$t('label.confirm.delete.isos')
|
||||
},
|
||||
modalWidth: '30vw'
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -238,6 +276,56 @@ export default {
|
|||
(this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Iso is ready or downloaded
|
||||
this.resource.account !== 'system'
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.dataSource.filter(function (item) {
|
||||
return selection.indexOf(item.zoneid) !== -1
|
||||
}))
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.fetchData()
|
||||
if (this.dataSource.length === 0) {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
},
|
||||
deleteIsos (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const iso of this.selectedItems) {
|
||||
this.deleteIso(iso)
|
||||
}
|
||||
},
|
||||
deleteIso (record) {
|
||||
const params = {
|
||||
id: record.id,
|
||||
|
|
@ -250,21 +338,39 @@ export default {
|
|||
title: this.$t('label.action.delete.iso'),
|
||||
jobid: jobId,
|
||||
description: this.resource.name,
|
||||
status: 'progress'
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
const singleZone = (this.dataSource.length === 1)
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
successMethod: result => {
|
||||
if (singleZone) {
|
||||
this.$router.go(-1)
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
} else {
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, record.zoneid, 'success')
|
||||
}
|
||||
},
|
||||
errorMethod: () => this.fetchData(),
|
||||
errorMethod: () => {
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.fetchData()
|
||||
}
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, record.zoneid, 'failed')
|
||||
}
|
||||
},
|
||||
showLoading: !(this.selectedItems.length > 0 && this.showGroupActionModal),
|
||||
loadingMessage: `${this.$t('label.deleting.iso')} ${this.resource.name} ${this.$t('label.in.progress')}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result')
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
|
|
@ -315,6 +421,7 @@ export default {
|
|||
description: this.resource.name,
|
||||
status: 'progress'
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
successMethod: result => {
|
||||
|
|
@ -336,6 +443,9 @@ export default {
|
|||
this.fetchData()
|
||||
})
|
||||
})
|
||||
},
|
||||
closeModal () {
|
||||
this.showConfirmationAction = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,14 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<a-button
|
||||
v-if="(('deleteTemplate' in $store.getters.apis) && this.selectedRowKeys.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t('label.action.bulk.delete.templates') }}
|
||||
</a-button>
|
||||
<a-table
|
||||
size="small"
|
||||
style="overflow-y: auto"
|
||||
|
|
@ -24,6 +32,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowKey="record => record.zoneid">
|
||||
<div slot="isready" slot-scope="text, record">
|
||||
<span v-if="record.isready">{{ $t('label.yes') }}</span>
|
||||
|
|
@ -110,34 +119,65 @@
|
|||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
:title="$t('label.action.delete.template')"
|
||||
:title="selectedItems.length > 0 && showTable ? $t(message.title) : $t('label.action.delete.template')"
|
||||
:visible="showDeleteTemplate"
|
||||
:closable="true"
|
||||
:maskClosable="false"
|
||||
:okText="$t('label.ok')"
|
||||
:cancelText="$t('label.cancel')"
|
||||
@ok="deleteTemplate"
|
||||
:width="showTable ? modalWidth : '30vw'"
|
||||
@ok="selectedItems.length > 0 ? deleteTemplates() : deleteTemplate(currentRecord)"
|
||||
@cancel="onCloseModal"
|
||||
:ok-button-props="getOkProps()"
|
||||
:cancel-button-props="getCancelProps()"
|
||||
:confirmLoading="deleteLoading"
|
||||
centered>
|
||||
<div v-if="selectedRowKeys.length > 0">
|
||||
<a-alert type="error">
|
||||
<a-icon slot="message" type="exclamation-circle" style="color: red; fontSize: 30px; display: inline-flex" />
|
||||
<span style="padding-left: 5px" slot="message" v-html="`<b>${selectedRowKeys.length} ` + $t('label.items.selected') + `. </b>`" />
|
||||
<span slot="message" v-html="$t(message.confirmMessage)" />
|
||||
</a-alert>
|
||||
</div>
|
||||
<a-alert v-else :message="$t('message.action.delete.template')" type="warning" />
|
||||
<br />
|
||||
<a-table
|
||||
v-if="selectedRowKeys.length > 0 && showTable"
|
||||
size="middle"
|
||||
:columns="selectedColumns"
|
||||
:dataSource="selectedItems"
|
||||
:rowKey="(record, idx) => record.zoneid || record.name"
|
||||
:pagination="true"
|
||||
style="overflow-y: auto">
|
||||
</a-table>
|
||||
<a-spin :spinning="deleteLoading">
|
||||
<a-alert :message="$t('message.action.delete.template')" type="warning" />
|
||||
<a-form-item :label="$t('label.isforced')" style="margin-bottom: 0;">
|
||||
<a-switch v-model="forcedDelete"></a-switch>
|
||||
</a-form-item>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
<bulk-action-progress
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:selectedItems="selectedItems"
|
||||
:selectedColumns="selectedColumns"
|
||||
:message="message"
|
||||
@handle-cancel="handleCancel" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionProgress from '@/components/view/BulkActionProgress'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
name: 'TemplateZones',
|
||||
components: {
|
||||
TooltipButton
|
||||
TooltipButton,
|
||||
BulkActionProgress,
|
||||
Status
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -164,7 +204,19 @@ export default {
|
|||
copyLoading: false,
|
||||
deleteLoading: false,
|
||||
showDeleteTemplate: false,
|
||||
forcedDelete: false
|
||||
forcedDelete: false,
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['Status', 'Ready'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.delete.templates'),
|
||||
confirmMessage: this.$t('label.confirm.delete.templates')
|
||||
},
|
||||
modalWidth: '30vw',
|
||||
showTable: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -206,7 +258,7 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
loading (newData, oldData) {
|
||||
if (!newData) {
|
||||
if (!newData && !this.showGroupActionModal) {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
|
|
@ -249,11 +301,83 @@ export default {
|
|||
(this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Template is ready or downloaded
|
||||
this.resource.templatetype !== 'SYSTEM'
|
||||
},
|
||||
deleteTemplate () {
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
if (selection?.length > 0) {
|
||||
this.modalWidth = '50vw'
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.dataSource.filter(function (item) {
|
||||
return selection.indexOf(item.zoneid) !== -1
|
||||
}))
|
||||
} else {
|
||||
this.modalWidth = '30vw'
|
||||
this.selectedItems = []
|
||||
}
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
this.onShowDeleteModal(this.selectedItems[0])
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.showTable = false
|
||||
this.fetchData()
|
||||
if (this.dataSource.length === 0) {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
},
|
||||
getOkProps () {
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
return { props: { type: 'default' } }
|
||||
} else {
|
||||
return { props: { type: 'primary' } }
|
||||
}
|
||||
},
|
||||
getCancelProps () {
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
return { props: { type: 'primary' } }
|
||||
} else {
|
||||
return { props: { type: 'default' } }
|
||||
}
|
||||
},
|
||||
deleteTemplates (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0 && this.showTable) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const template of this.selectedItems) {
|
||||
this.deleteTemplate(template)
|
||||
}
|
||||
},
|
||||
deleteTemplate (template) {
|
||||
const params = {
|
||||
id: this.currentRecord.id,
|
||||
id: template.id,
|
||||
forced: this.forcedDelete,
|
||||
zoneid: this.currentRecord.zoneid
|
||||
zoneid: template.zoneid
|
||||
}
|
||||
this.deleteLoading = true
|
||||
api('deleteTemplate', params).then(json => {
|
||||
|
|
@ -262,8 +386,10 @@ export default {
|
|||
title: this.$t('label.action.delete.template'),
|
||||
jobid: jobId,
|
||||
description: this.resource.name,
|
||||
status: 'progress'
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
const singleZone = (this.dataSource.length === 1)
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
|
|
@ -271,18 +397,36 @@ export default {
|
|||
if (singleZone) {
|
||||
const isResourcePage = (this.$route.params && this.$route.params.id)
|
||||
if (isResourcePage) {
|
||||
this.$router.go(-1)
|
||||
if (this.selectedItems.length === 0 && !this.showGroupActionModal) {
|
||||
this.$router.push({ path: '/template' })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, template.zoneid, 'success')
|
||||
}
|
||||
},
|
||||
errorMethod: () => this.fetchData(),
|
||||
errorMethod: () => {
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.fetchData()
|
||||
}
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, template.zoneid, 'failed')
|
||||
}
|
||||
},
|
||||
showLoading: !(this.selectedItems.length > 0 && this.showGroupActionModal),
|
||||
loadingMessage: `${this.$t('label.deleting.template')} ${this.resource.name} ${this.$t('label.in.progress')}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result')
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
this.onCloseModal()
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.fetchData()
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
|
|
@ -311,11 +455,19 @@ export default {
|
|||
this.forcedDelete = false
|
||||
this.currentRecord = record
|
||||
this.showDeleteTemplate = true
|
||||
if (this.showConfirmationAction) {
|
||||
this.showTable = true
|
||||
} else {
|
||||
this.selectedItems = []
|
||||
}
|
||||
},
|
||||
onCloseModal () {
|
||||
this.currentRecord = {}
|
||||
this.showCopyActionForm = false
|
||||
this.showDeleteTemplate = false
|
||||
this.showConfirmationAction = false
|
||||
this.showTable = false
|
||||
this.selectedRowKeys = []
|
||||
},
|
||||
handleCopyTemplateSubmit (e) {
|
||||
e.preventDefault()
|
||||
|
|
@ -337,6 +489,7 @@ export default {
|
|||
description: this.resource.name,
|
||||
status: 'progress'
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
successMethod: result => {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,14 @@
|
|||
</div>
|
||||
|
||||
<a-divider/>
|
||||
|
||||
<a-button
|
||||
v-if="(('deleteEgressFirewallRule' in $store.getters.apis) && this.selectedRowKeys.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t('label.action.bulk.delete.egress.firewall.rules') }}
|
||||
</a-button>
|
||||
<a-table
|
||||
size="small"
|
||||
style="overflow-y: auto"
|
||||
|
|
@ -67,6 +74,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="egressRules"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowKey="record => record.id">
|
||||
<template slot="protocol" slot-scope="record">
|
||||
{{ record.protocol | capitalise }}
|
||||
|
|
@ -97,17 +105,37 @@
|
|||
</template>
|
||||
</a-pagination>
|
||||
|
||||
<bulk-action-view
|
||||
v-if="showConfirmationAction || showGroupActionModal"
|
||||
:showConfirmationAction="showConfirmationAction"
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:items="egressRules"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:columns="columns"
|
||||
:selectedColumns="selectedColumns"
|
||||
action="deleteEgressFirewallRule"
|
||||
:loading="loading"
|
||||
:message="message"
|
||||
@group-action="deleteRules"
|
||||
@handle-cancel="handleCancel"
|
||||
@close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionView from '@/components/view/BulkActionView'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
name: 'EgressRulesTab',
|
||||
components: {
|
||||
TooltipButton
|
||||
Status,
|
||||
TooltipButton,
|
||||
BulkActionView
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -117,6 +145,16 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['Action'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.delete.egress.firewall.rules'),
|
||||
confirmMessage: this.$t('label.confirm.delete.egress.firewall.rules')
|
||||
},
|
||||
loading: true,
|
||||
egressRules: [],
|
||||
newRule: {
|
||||
|
|
@ -160,6 +198,11 @@ export default {
|
|||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected () {
|
||||
return this.selectedRowKeys.length > 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
|
|
@ -178,6 +221,7 @@ export default {
|
|||
this.fetchData()
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
|
|
@ -193,22 +237,91 @@ export default {
|
|||
this.loading = false
|
||||
})
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.egressRules.filter(function (item) {
|
||||
return selection.indexOf(item.id) !== -1
|
||||
}))
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.parentFetchData()
|
||||
},
|
||||
deleteRules (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const rule of this.selectedItems) {
|
||||
this.deleteRule(rule)
|
||||
}
|
||||
},
|
||||
deleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deleteEgressFirewallRule', { id: rule.id }).then(response => {
|
||||
const jobId = response.deleteegressfirewallruleresponse.jobid
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: this.$t('label.action.delete.egress.firewall'),
|
||||
jobid: jobId,
|
||||
description: rule.id,
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId: response.deleteegressfirewallruleresponse.jobid,
|
||||
jobId: jobId,
|
||||
successMessage: this.$t('message.success.remove.egress.rule'),
|
||||
successMethod: () => this.fetchData(),
|
||||
successMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'success')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: this.$t('message.remove.egress.rule.failed'),
|
||||
errorMethod: () => this.fetchData(),
|
||||
errorMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'failed')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: this.$t('message.remove.egress.rule.processing'),
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
catchMethod: () => this.fetchData()
|
||||
catchMethod: () => this.fetchData(),
|
||||
bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
this.fetchData()
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
addRule () {
|
||||
|
|
@ -252,6 +365,9 @@ export default {
|
|||
this.newRule.startport = null
|
||||
this.newRule.endport = null
|
||||
},
|
||||
closeModal () {
|
||||
this.showConfirmationAction = false
|
||||
},
|
||||
handleChangePage (page, pageSize) {
|
||||
this.page = page
|
||||
this.pageSize = pageSize
|
||||
|
|
|
|||
|
|
@ -54,7 +54,14 @@
|
|||
</div>
|
||||
|
||||
<a-divider/>
|
||||
|
||||
<a-button
|
||||
v-if="(('deleteFirewallRule' in $store.getters.apis) && this.selectedItems.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t('label.action.bulk.delete.firewall.rules') }}
|
||||
</a-button>
|
||||
<a-table
|
||||
size="small"
|
||||
style="overflow-y: auto"
|
||||
|
|
@ -62,6 +69,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="firewallRules"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowKey="record => record.id">
|
||||
<template slot="protocol" slot-scope="record">
|
||||
{{ record.protocol | capitalise }}
|
||||
|
|
@ -132,16 +140,36 @@
|
|||
<a-button class="add-tags-done" @click="tagsModalVisible = false" type="primary">{{ $t('label.done') }}</a-button>
|
||||
</a-modal>
|
||||
|
||||
<bulk-action-view
|
||||
v-if="showConfirmationAction || showGroupActionModal"
|
||||
:showConfirmationAction="showConfirmationAction"
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:items="firewallRules"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:columns="columns"
|
||||
:selectedColumns="selectedColumns"
|
||||
action="deleteFirewallRule"
|
||||
:loading="loading"
|
||||
:message="message"
|
||||
@group-action="deleteRules"
|
||||
@handle-cancel="handleCancel"
|
||||
@close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionView from '@/components/view/BulkActionView'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TooltipButton
|
||||
Status,
|
||||
TooltipButton,
|
||||
BulkActionView
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -152,6 +180,16 @@ export default {
|
|||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
data () {
|
||||
return {
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['State', 'Action'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.delete.firewall.rules'),
|
||||
confirmMessage: this.$t('label.confirm.delete.firewall.rules')
|
||||
},
|
||||
loading: true,
|
||||
addTagLoading: false,
|
||||
firewallRules: [],
|
||||
|
|
@ -202,6 +240,11 @@ export default {
|
|||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected () {
|
||||
return this.selectedRowKeys.length > 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
|
|
@ -237,18 +280,85 @@ export default {
|
|||
this.loading = false
|
||||
})
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.firewallRules.filter(function (item) {
|
||||
return selection.indexOf(item.id) !== -1
|
||||
}))
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.parentFetchData()
|
||||
},
|
||||
deleteRules (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const rule of this.selectedItems) {
|
||||
this.deleteRule(rule)
|
||||
}
|
||||
},
|
||||
deleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deleteFirewallRule', { id: rule.id }).then(response => {
|
||||
const jobId = response.deletefirewallruleresponse.jobid
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: this.$t('label.action.delete.firewall'),
|
||||
jobid: jobId,
|
||||
description: rule.id,
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId: response.deletefirewallruleresponse.jobid,
|
||||
jobId: jobId,
|
||||
successMessage: this.$t('message.success.remove.firewall.rule'),
|
||||
successMethod: () => this.fetchData(),
|
||||
successMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'success')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: this.$t('message.remove.firewall.rule.failed'),
|
||||
errorMethod: () => this.fetchData(),
|
||||
errorMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'failed')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: this.$t('message.remove.firewall.rule.processing'),
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
catchMethod: () => this.fetchData()
|
||||
catchMethod: () => this.fetchData(),
|
||||
bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
|
|
@ -300,6 +410,7 @@ export default {
|
|||
this.tagsModalVisible = false
|
||||
this.newTag.key = null
|
||||
this.newTag.value = null
|
||||
this.showConfirmationAction = false
|
||||
},
|
||||
openTagsModal (id) {
|
||||
this.selectedRule = id
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@
|
|||
@click="onShowAcquireIp">
|
||||
{{ $t('label.acquire.new.ip') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="(('disassociateIpAddress' in $store.getters.apis) && this.selectedRowKeys.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t('label.action.bulk.release.public.ip.address') }}
|
||||
</a-button>
|
||||
<div v-if="$route.path.startsWith('/vpc')">
|
||||
Select Tier:
|
||||
<a-select
|
||||
|
|
@ -50,6 +58,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="ips"
|
||||
:rowKey="item => item.id"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:pagination="false" >
|
||||
<template slot="ipaddress" slot-scope="text, record">
|
||||
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }} </router-link>
|
||||
|
|
@ -124,18 +133,36 @@
|
|||
</a-form-item>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
<bulk-action-view
|
||||
v-if="showConfirmationAction || showGroupActionModal"
|
||||
:showConfirmationAction="showConfirmationAction"
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:items="ips"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:columns="columns"
|
||||
:selectedColumns="selectedColumns"
|
||||
action="disassociateIpAddress"
|
||||
:loading="loading"
|
||||
:message="message"
|
||||
@group-action="releaseIpAddresses"
|
||||
@handle-cancel="handleCancel"
|
||||
@close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionView from '@/components/view/BulkActionView'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
name: 'IpAddressesTab',
|
||||
components: {
|
||||
Status,
|
||||
TooltipButton
|
||||
TooltipButton,
|
||||
BulkActionView
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -159,6 +186,16 @@ export default {
|
|||
pageSize: 10,
|
||||
totalIps: 0,
|
||||
tiersSelect: false,
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['Action'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.release.public.ip.address'),
|
||||
confirmMessage: this.$t('label.confirm.release.public.ip.addresses')
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: this.$t('label.ipaddress'),
|
||||
|
|
@ -202,6 +239,7 @@ export default {
|
|||
this.fetchData()
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
methods: {
|
||||
fetchData () {
|
||||
const params = {
|
||||
|
|
@ -250,6 +288,19 @@ export default {
|
|||
this.vpcTier = tier
|
||||
this.fetchData()
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.ips.filter(function (item) {
|
||||
return selection.indexOf(item.id) !== -1
|
||||
}))
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
changePage (page, pageSize) {
|
||||
this.page = page
|
||||
this.pageSize = pageSize
|
||||
|
|
@ -260,6 +311,13 @@ export default {
|
|||
this.pageSize = pageSize
|
||||
this.fetchData()
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
},
|
||||
acquireIpAddress () {
|
||||
const params = {}
|
||||
if (this.$route.path.startsWith('/vpc')) {
|
||||
|
|
@ -298,23 +356,66 @@ export default {
|
|||
this.acquireLoading = false
|
||||
})
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.parentFetchData()
|
||||
},
|
||||
releaseIpAddresses (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const ip of this.selectedItems) {
|
||||
this.releaseIpAddress(ip)
|
||||
}
|
||||
},
|
||||
releaseIpAddress (ip) {
|
||||
this.fetchLoading = true
|
||||
api('disassociateIpAddress', {
|
||||
id: ip.id
|
||||
}).then(response => {
|
||||
const jobId = response.disassociateipaddressresponse.jobid
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: this.$t('label.action.release.ip'),
|
||||
jobid: jobId,
|
||||
description: ip.id,
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId: response.disassociateipaddressresponse.jobid,
|
||||
jobId: jobId,
|
||||
successMessage: this.$t('message.success.release.ip'),
|
||||
successMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, ip.id, 'success')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: this.$t('message.release.ip.failed'),
|
||||
errorMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, ip.id, 'failed')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: `${this.$t('label.releasing.ip')} ${this.$t('label.for')} ${this.resource.name} ${this.$t('label.is.in.progress')}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result')
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal
|
||||
})
|
||||
}).catch(error => {
|
||||
this.fetchLoading = false
|
||||
|
|
@ -353,6 +454,9 @@ export default {
|
|||
},
|
||||
onCloseModal () {
|
||||
this.showAcquireIp = false
|
||||
},
|
||||
closeModal () {
|
||||
this.showConfirmationAction = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,14 @@
|
|||
</div>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<a-button
|
||||
v-if="(('deleteLoadBalancerRule' in $store.getters.apis) && this.selectedItems.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t('label.action.bulk.delete.load.balancer.rules') }}
|
||||
</a-button>
|
||||
<a-table
|
||||
size="small"
|
||||
class="list-view"
|
||||
|
|
@ -70,6 +77,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="lbRules"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowKey="record => record.id">
|
||||
<template slot="algorithm" slot-scope="record">
|
||||
{{ returnAlgorithmName(record.algorithm) }}
|
||||
|
|
@ -375,9 +383,24 @@
|
|||
</template>
|
||||
</a-pagination>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
|
||||
<bulk-action-view
|
||||
v-if="showConfirmationAction || showGroupActionModal"
|
||||
:showConfirmationAction="showConfirmationAction"
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:items="lbRules"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:columns="columns"
|
||||
:selectedColumns="selectedColumns"
|
||||
:filterColumns="filterColumns"
|
||||
action="deleteLoadBalancerRule"
|
||||
:loading="loading"
|
||||
:message="message"
|
||||
@group-action="deleteRules"
|
||||
@handle-cancel="handleCancel"
|
||||
@close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -385,12 +408,15 @@
|
|||
import { api } from '@/api'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionView from '@/components/view/BulkActionView'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
name: 'LoadBalancing',
|
||||
components: {
|
||||
Status,
|
||||
TooltipButton
|
||||
TooltipButton,
|
||||
BulkActionView
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -401,6 +427,16 @@ export default {
|
|||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
data () {
|
||||
return {
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['State', 'Action', 'Add VMs', 'Stickiness'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.delete.load.balancer.rules'),
|
||||
confirmMessage: this.$t('label.confirm.delete.loadbalancer.rules')
|
||||
},
|
||||
loading: true,
|
||||
lbRules: [],
|
||||
newTagsForm: this.$form.createForm(this),
|
||||
|
|
@ -523,6 +559,11 @@ export default {
|
|||
searchQuery: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected () {
|
||||
return this.selectedRowKeys.length > 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
|
|
@ -939,37 +980,104 @@ export default {
|
|||
this.loading = false
|
||||
})
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.lbRules.filter(function (item) {
|
||||
return selection.indexOf(item.id) !== -1
|
||||
}))
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.parentFetchData()
|
||||
},
|
||||
deleteRules (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const rule of this.selectedItems) {
|
||||
this.handleDeleteRule(rule)
|
||||
}
|
||||
},
|
||||
handleDeleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deleteLoadBalancerRule', {
|
||||
id: rule.id
|
||||
}).then(response => {
|
||||
const jobId = response.deleteloadbalancerruleresponse.jobid
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: this.$t('label.action.delete.load.balancer'),
|
||||
jobid: jobId,
|
||||
description: rule.id,
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId: response.deleteloadbalancerruleresponse.jobid,
|
||||
jobId: jobId,
|
||||
successMessage: this.$t('message.success.remove.rule'),
|
||||
successMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'success')
|
||||
}
|
||||
if (this.selectedRowKeys.length === 0) {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
}
|
||||
this.closeModal()
|
||||
},
|
||||
errorMessage: this.$t('message.remove.rule.failed'),
|
||||
errorMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.fetchData()
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'failed')
|
||||
}
|
||||
if (this.selectedRowKeys.length === 0) {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
}
|
||||
this.closeModal()
|
||||
},
|
||||
loadingMessage: this.$t('message.delete.rule.processing'),
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
catchMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.fetchData()
|
||||
if (this.selectedRowKeys.length === 0) {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
}
|
||||
this.closeModal()
|
||||
}
|
||||
},
|
||||
bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
this.$notifyError(error)
|
||||
this.loading = false
|
||||
})
|
||||
|
|
@ -1149,6 +1257,7 @@ export default {
|
|||
this.editRuleModalLoading = false
|
||||
this.addVmModalLoading = false
|
||||
this.addVmModalNicLoading = false
|
||||
this.showConfirmationAction = false
|
||||
this.vms = []
|
||||
this.nics = []
|
||||
this.addVmModalVisible = false
|
||||
|
|
|
|||
|
|
@ -71,7 +71,14 @@
|
|||
</div>
|
||||
|
||||
<a-divider/>
|
||||
|
||||
<a-button
|
||||
v-if="(('deletePortForwardingRule' in $store.getters.apis) && this.selectedItems.length > 0)"
|
||||
type="danger"
|
||||
icon="plus"
|
||||
style="width: 100%; margin-bottom: 15px"
|
||||
@click="bulkActionConfirmation()">
|
||||
{{ $t('label.action.bulk.delete.portforward.rules') }}
|
||||
</a-button>
|
||||
<a-table
|
||||
size="small"
|
||||
style="overflow-y: auto"
|
||||
|
|
@ -79,6 +86,7 @@
|
|||
:columns="columns"
|
||||
:dataSource="portForwardRules"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowKey="record => record.id">
|
||||
<template slot="privateport" slot-scope="record">
|
||||
{{ record.privateport }} - {{ record.privateendport }}
|
||||
|
|
@ -246,6 +254,23 @@
|
|||
</a-pagination>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<bulk-action-view
|
||||
v-if="showConfirmationAction || showGroupActionModal"
|
||||
:showConfirmationAction="showConfirmationAction"
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:items="portForwardRules"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:columns="columns"
|
||||
:selectedColumns="selectedColumns"
|
||||
:filterColumns="filterColumns"
|
||||
action="deletePortForwardingRule"
|
||||
:loading="loading"
|
||||
:message="message"
|
||||
@group-action="deleteRules"
|
||||
@handle-cancel="handleCancel"
|
||||
@close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -253,11 +278,14 @@
|
|||
import { api } from '@/api'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import BulkActionView from '@/components/view/BulkActionView'
|
||||
import eventBus from '@/config/eventBus'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Status,
|
||||
TooltipButton
|
||||
TooltipButton,
|
||||
BulkActionView
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -268,6 +296,16 @@ export default {
|
|||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
data () {
|
||||
return {
|
||||
selectedRowKeys: [],
|
||||
showGroupActionModal: false,
|
||||
selectedItems: [],
|
||||
selectedColumns: [],
|
||||
filterColumns: ['State', 'Action'],
|
||||
showConfirmationAction: false,
|
||||
message: {
|
||||
title: this.$t('label.action.bulk.delete.portforward.rules'),
|
||||
confirmMessage: this.$t('label.confirm.delete.portforward.rules')
|
||||
},
|
||||
loading: true,
|
||||
portForwardRules: [],
|
||||
newRule: {
|
||||
|
|
@ -369,6 +407,11 @@ export default {
|
|||
searchQuery: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected () {
|
||||
return this.selectedRowKeys.length > 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
|
|
@ -429,18 +472,85 @@ export default {
|
|||
this.loading = false
|
||||
})
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
this.selectedItems = (this.portForwardRules.filter(function (item) {
|
||||
return selection.indexOf(item.id) !== -1
|
||||
}))
|
||||
},
|
||||
resetSelection () {
|
||||
this.setSelection([])
|
||||
},
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.setSelection(selectedRowKeys)
|
||||
},
|
||||
bulkActionConfirmation () {
|
||||
this.showConfirmationAction = true
|
||||
this.selectedColumns = this.columns.filter(column => {
|
||||
return !this.filterColumns.includes(column.title)
|
||||
})
|
||||
this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
|
||||
},
|
||||
handleCancel () {
|
||||
eventBus.$emit('update-bulk-job-status', this.selectedItems, false)
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItems = []
|
||||
this.selectedColumns = []
|
||||
this.selectedRowKeys = []
|
||||
this.parentFetchData()
|
||||
},
|
||||
deleteRules (e) {
|
||||
this.showConfirmationAction = false
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.showGroupActionModal = true
|
||||
}
|
||||
for (const rule of this.selectedItems) {
|
||||
this.deleteRule(rule)
|
||||
}
|
||||
},
|
||||
deleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deletePortForwardingRule', { id: rule.id }).then(response => {
|
||||
const jobId = response.deleteportforwardingruleresponse.jobid
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: this.$t('label.portforwarding.rule'),
|
||||
jobid: jobId,
|
||||
description: rule.id,
|
||||
status: 'progress',
|
||||
bulkAction: this.selectedItems.length > 0 && this.showGroupActionModal
|
||||
})
|
||||
eventBus.$emit('update-job-details', jobId, null)
|
||||
this.$pollJob({
|
||||
jobId: response.deleteportforwardingruleresponse.jobid,
|
||||
jobId: jobId,
|
||||
successMessage: this.$t('message.success.remove.port.forward'),
|
||||
successMethod: () => this.fetchData(),
|
||||
successMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'success')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: this.$t('message.remove.port.forward.failed'),
|
||||
errorMethod: () => this.fetchData(),
|
||||
errorMethod: () => {
|
||||
if (this.selectedItems.length > 0) {
|
||||
eventBus.$emit('update-resource-state', this.selectedItems, rule.id, 'failed')
|
||||
}
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: this.$t('message.delete.port.forward.processing'),
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
catchMethod: () => this.fetchData()
|
||||
catchMethod: () => this.fetchData(),
|
||||
bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
|
|
@ -503,6 +613,7 @@ export default {
|
|||
this.newRule.virtualmachineid = null
|
||||
this.addVmModalLoading = false
|
||||
this.addVmModalNicLoading = false
|
||||
this.showConfirmationAction = false
|
||||
this.nics = []
|
||||
this.resetTagInputs()
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue