mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
ui: assorted improvements (#7833)
This PR aims to polish the UI with following tweaks and changes: - Increase resource and os-logo icons both in list view, user-menu bar and VM deployment form - Fix css issues in VM deployment form when resource icons are on some of the templates/isos but not all - Replace edit icon in the resource icon editting button on the infocard, in resource view - Fix css marging/padding issue for nav bar and left-branding/logo - Introduce a new Limits option in the user menu, to allow users to see their own limits when they log in - Rename resource tab to limits tab for accounts, project and domains - Introduce a new copy-label component, that can be clicked to copy strings; use in info-card and list view for entites such as IP addresses and UUIDs - Add router-link to /zones/ in case of user-accounts (when /zone isn't routable in the UI) - Show better list of nics and ssh keys pairs in infocard for VM resource view - Standardise most resources to show state/status columns right after resource name (wherever applicable) - Remove displayname column in VM list view, add cpu number and memory by default - Add k8s version column in k8s list view - Add size and phy size columns in case of template and ISOs list view, only for root/domain admins - Add phy network router-link in case of guest VLAN list view; rearrange columns list for consistency - Add snapshot phy size column in the snapshot list view; and router-link for volume in the snapshot list view; and missing/useful details in the volume snapshot details view - Add a create and add data disk feature in Instances tab, just like we've add nic feature in the same Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
29c7b3167e
commit
feb9509547
@ -61,6 +61,7 @@
|
||||
"label.action.create.snapshot.from.vmsnapshot": "Create snapshot from VM snapshot",
|
||||
"label.action.create.template.from.volume": "Create template from volume",
|
||||
"label.action.create.volume": "Create volume",
|
||||
"label.action.create.volume.add": "Create and Add Volume",
|
||||
"label.action.delete.account": "Delete account",
|
||||
"label.action.delete.backup.offering": "Delete backup offering",
|
||||
"label.action.delete.cluster": "Delete cluster",
|
||||
@ -1135,7 +1136,8 @@
|
||||
"label.license.agreements": "License agreements",
|
||||
"label.limit": "Limit",
|
||||
"label.limitcpuuse": "CPU cap",
|
||||
"label.limits": "Configure limits",
|
||||
"label.limits": "Limits",
|
||||
"label.limits.configure": "Configure limits",
|
||||
"label.link.domain.to.ldap": "Link domain to LDAP",
|
||||
"label.linklocalip": "Link-local/Control IP address",
|
||||
"label.linux": "Linux",
|
||||
@ -1785,6 +1787,7 @@
|
||||
"label.snapshotlimit": "Snapshot limits",
|
||||
"label.snapshotmemory": "Snapshot memory",
|
||||
"label.snapshots": "Snapshots",
|
||||
"label.snapshottype": "Snapshot Type",
|
||||
"label.sockettimeout": "Socket timeout",
|
||||
"label.softwareversion": "Software version",
|
||||
"label.source.based": "SourceBased",
|
||||
@ -2140,6 +2143,7 @@
|
||||
"label.volumename": "Volume name",
|
||||
"label.volumes": "Volumes",
|
||||
"label.volumetotal": "Volume",
|
||||
"label.volumetype": "Volume Type",
|
||||
"label.vpc": "VPC",
|
||||
"label.vpc.id": "VPC ID",
|
||||
"label.vpc.offerings": "VPC offerings",
|
||||
@ -2347,6 +2351,7 @@
|
||||
"message.attach.volume": "Please fill in the following data to attach a new volume. If you are attaching a disk volume to a Windows based virtual machine, you will need to reboot the instance to see the attached disk.",
|
||||
"message.attach.volume.failed": "Failed to attach volume.",
|
||||
"message.attach.volume.progress": "Attaching volume",
|
||||
"message.attach.volume.success": "Successfully attached the volume to the instance",
|
||||
"message.authorization.failed": "Session expired, authorization verification failed.",
|
||||
"message.autoscale.loadbalancer.update": "The load balancer rule can be updated only when autoscale VM group is DISABLED.",
|
||||
"message.autoscale.policies.update": "The scale up/down policies can be updated only when autoscale VM group is DISABLED.",
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
<a-dropdown>
|
||||
<span class="user-menu-dropdown action">
|
||||
<span v-if="image">
|
||||
<resource-icon :image="image" size="2x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="image" size="4x" style="margin-right: 5px; margin-top: -3px"/>
|
||||
</span>
|
||||
<a-avatar v-else-if="userInitials" class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: '#1890ff', color: 'white' }">
|
||||
{{ userInitials }}
|
||||
@ -45,6 +45,12 @@
|
||||
<span class="user-menu-item-name">{{ $t('label.profilename') }}</span>
|
||||
</a-menu-item>
|
||||
</router-link>
|
||||
<router-link :to="{ path: '/account/' + $store.getters.userInfo.accountid, query: { tab: 'limits' } }">
|
||||
<a-menu-item class="user-menu-item" key="0">
|
||||
<ControlOutlined class="user-menu-item-icon" />
|
||||
<span class="user-menu-item-name">{{ $t('label.limits') }}</span>
|
||||
</a-menu-item>
|
||||
</router-link>
|
||||
<a @click="toggleUseBrowserTimezone">
|
||||
<a-menu-item class="user-menu-item" key="1">
|
||||
<ClockCircleOutlined class="user-menu-item-icon" />
|
||||
|
||||
@ -119,14 +119,12 @@ export default {
|
||||
|
||||
.ant-menu-light {
|
||||
border-right-color: transparent;
|
||||
padding: 14px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
.ant-menu-dark {
|
||||
border-right-color: transparent;
|
||||
padding: 14px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +51,21 @@
|
||||
{{ dataResource.rootdisksize }} GB
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="['template', 'iso'].includes($route.meta.name) && item === 'size'">
|
||||
<div>
|
||||
{{ parseFloat(dataResource.size / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GB
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="['volume', 'snapshot', 'template', 'iso'].includes($route.meta.name) && item === 'physicalsize'">
|
||||
<div>
|
||||
{{ parseFloat(dataResource.physicalsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GB
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="['volume', 'snapshot', 'template', 'iso'].includes($route.meta.name) && item === 'virtualsize'">
|
||||
<div>
|
||||
{{ parseFloat(dataResource.virtualsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GB
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="['name', 'type'].includes(item)">
|
||||
<span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(dataResource[item])">{{ $t(dataResource[item].toLowerCase()) }}</span>
|
||||
<span v-else>{{ dataResource[item] }}</span>
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
v-clipboard:copy="name" >
|
||||
<upload-resource-icon v-if="'uploadResourceIcon' in $store.getters.apis" :visible="showUpload" :resource="resource" @handle-close="showUpload(false)"/>
|
||||
<div class="ant-upload-preview" v-if="$showIcon() && !$route.path.includes('zones')">
|
||||
<camera-outlined class="upload-icon"/>
|
||||
<edit-outlined class="upload-icon"/>
|
||||
</div>
|
||||
<slot name="avatar">
|
||||
<span v-if="(resource.icon && resource.icon.base64image || images.template || images.iso || resourceIcon) && !['router', 'systemvm', 'volume'].includes($route.path.split('/')[1])">
|
||||
@ -119,14 +119,14 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.id') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<tooltip-button
|
||||
tooltipPlacement="right"
|
||||
tooltipPlacement="top"
|
||||
:tooltip="$t('label.copyid')"
|
||||
icon="barcode-outlined"
|
||||
type="dashed"
|
||||
size="small"
|
||||
:copyResource="resource.id"
|
||||
@onClick="$message.success($t('label.copied.clipboard'))" />
|
||||
<span style="margin-left: 10px;">{{ resource.id }}</span>
|
||||
<span style="margin-left: 10px;"><copy-label :label="resource.id" /></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.ostypename && resource.ostypeid">
|
||||
@ -139,6 +139,29 @@
|
||||
<span style="margin-left: 8px">{{ resource.ostypename }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.ipaddress">
|
||||
<div class="resource-detail-item__label">{{ $t('label.ip') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<environment-outlined
|
||||
@click="$message.success(`${$t('label.copied.clipboard')} : ${ ipaddress }`)"
|
||||
v-clipboard:copy="ipaddress" />
|
||||
<router-link v-if="!isStatic && resource.ipaddressid" :to="{ path: '/publicip/' + resource.ipaddressid }">
|
||||
<copy-label :label="ipaddress" />
|
||||
</router-link>
|
||||
<span v-else>
|
||||
<span v-if="ipaddress.includes(',')">
|
||||
<span
|
||||
v-for="(value, index) in ipaddress.split(',')"
|
||||
:key="index">
|
||||
<copy-label :label="value" /><br/>
|
||||
</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<copy-label :label="ipaddress" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="('cpunumber' in resource && 'cpuspeed' in resource) || resource.cputotal">
|
||||
<div class="resource-detail-item__label">{{ $t('label.cpu') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
@ -292,11 +315,19 @@
|
||||
v-for="(eth, index) in resource.nic"
|
||||
:key="eth.id"
|
||||
style="margin-left: -24px; margin-top: 5px;">
|
||||
<api-outlined /><strong>eth{{ index }}</strong> {{ eth.ip6address ? eth.ipaddress + ', ' + eth.ip6address : eth.ipaddress }}
|
||||
<router-link v-if="!isStatic && eth.networkname && eth.networkid" :to="{ path: '/guestnetwork/' + eth.networkid }">({{ eth.networkname }})</router-link>
|
||||
<api-outlined />
|
||||
<strong>eth{{ index }}</strong>
|
||||
<copy-label :label="eth.ip6address ? eth.ipaddress + ', ' + eth.ip6address : eth.ipaddress" />
|
||||
<a-tag v-if="eth.isdefault">
|
||||
{{ $t('label.default') }}
|
||||
</a-tag >
|
||||
</a-tag ><br/>
|
||||
<span v-if="!isStatic && eth.networkname && eth.networkid">
|
||||
|
||||
<apartment-outlined/>
|
||||
<router-link :to="{ path: '/guestnetwork/' + eth.networkid }">
|
||||
{{ eth.networkname }}
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -324,16 +355,6 @@
|
||||
<span>{{ resource.loadbalancer.name }} ( {{ resource.loadbalancer.publicip }}:{{ resource.loadbalancer.publicport }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.ipaddress">
|
||||
<div class="resource-detail-item__label">{{ $t('label.ip') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<environment-outlined
|
||||
@click="$message.success(`${$t('label.copied.clipboard')} : ${ ipaddress }`)"
|
||||
v-clipboard:copy="ipaddress" />
|
||||
<router-link v-if="!isStatic && resource.ipaddressid" :to="{ path: '/publicip/' + resource.ipaddressid }">{{ ipaddress }}</router-link>
|
||||
<span v-else>{{ ipaddress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.projectid || resource.projectname">
|
||||
<div class="resource-detail-item__label">{{ $t('label.project') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
@ -368,10 +389,15 @@
|
||||
<div class="resource-detail-item" v-if="resource.keypairs && resource.keypairs.length > 0">
|
||||
<div class="resource-detail-item__label">{{ $t('label.keypairs') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<div>
|
||||
<div
|
||||
v-for="keypair in keypairs"
|
||||
:key="keypair"
|
||||
style="margin-top: 5px;">
|
||||
<key-outlined />
|
||||
<li v-for="keypair in keypairs" :key="keypair">
|
||||
<router-link :to="{ path: '/ssh/' + keypair }" style="margin-right: 5px">{{ keypair }}</router-link>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.resourcetype && resource.resourceid && routeFromResourceType">
|
||||
@ -420,7 +446,8 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.publicip') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<gateway-outlined />
|
||||
<router-link :to="{ path: '/publicip/' + resource.publicipid }">{{ resource.publicip }} </router-link>
|
||||
<router-link v-if="resource.publicipid" :to="{ path: '/publicip/' + resource.publicipid }">{{ resource.publicip }} </router-link>
|
||||
<copy-label :label="resource.publicip"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.vpcid">
|
||||
@ -554,6 +581,7 @@
|
||||
</span>
|
||||
<global-outlined v-else />
|
||||
<router-link v-if="!isStatic && $router.resolve('/zone/' + resource.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + resource.zoneid }">{{ resource.zone || resource.zonename || resource.zoneid }}</router-link>
|
||||
<router-link v-else-if="$router.resolve('/zones/' + resource.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zones/' + resource.zoneid }">{{ resource.zone || resource.zonename || resource.zoneid }}</router-link>
|
||||
<span v-else>{{ resource.zone || resource.zonename || resource.zoneid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -733,6 +761,7 @@ import { createPathBasedOnVmType } from '@/utils/plugins'
|
||||
import Console from '@/components/widgets/Console'
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import CopyLabel from '@/components/widgets/CopyLabel'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import UploadResourceIcon from '@/components/view/UploadResourceIcon'
|
||||
import eventBus from '@/config/eventBus'
|
||||
@ -745,6 +774,7 @@ export default {
|
||||
Console,
|
||||
OsLogo,
|
||||
Status,
|
||||
CopyLabel,
|
||||
TooltipButton,
|
||||
UploadResourceIcon,
|
||||
ResourceIcon,
|
||||
|
||||
@ -73,9 +73,9 @@
|
||||
<template #name="{text, record}">
|
||||
<span v-if="['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
|
||||
<span v-if="record.icon && record.icon.base64image">
|
||||
<resource-icon :image="record.icon.base64image" size="1x"/>
|
||||
<resource-icon :image="record.icon.base64image" size="2x"/>
|
||||
</span>
|
||||
<os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="lg" />
|
||||
<os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="2x" />
|
||||
</span>
|
||||
<span style="min-width: 120px" >
|
||||
<QuickView
|
||||
@ -88,8 +88,8 @@
|
||||
<tooltip-button type="dashed" size="small" icon="LoginOutlined" @onClick="changeProject(record)" />
|
||||
</span>
|
||||
<span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
|
||||
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="1x"/>
|
||||
<os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="1x" />
|
||||
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/>
|
||||
<os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="2x" />
|
||||
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/>
|
||||
<render-icon v-else style="font-size: 16px;" :svgIcon="$route.meta.icon" />
|
||||
</span>
|
||||
@ -143,7 +143,7 @@
|
||||
</template>
|
||||
<template #username="{text, record}">
|
||||
<span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
|
||||
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="1x"/>
|
||||
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/>
|
||||
<user-outlined v-else style="font-size: 16px;" />
|
||||
</span>
|
||||
<router-link :to="{ path: $route.path + '/' + record.id }" v-if="['/accountuser', '/vpnuser'].includes($route.path)">{{ text }}</router-link>
|
||||
@ -162,7 +162,9 @@
|
||||
</template>
|
||||
<template #ipaddress="{ text, record }" href="javascript:;">
|
||||
<router-link v-if="['/publicip', '/privategw'].includes($route.path)" :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
||||
<span v-else>{{ text }}</span>
|
||||
<span v-else>
|
||||
<copy-label :label="text" />
|
||||
</span>
|
||||
<span v-if="record.issourcenat">
|
||||
|
||||
<a-tag>source-nat</a-tag>
|
||||
@ -188,6 +190,25 @@
|
||||
<template #virtualmachinename="{ text, record }">
|
||||
<router-link :to="{ path: '/vm/' + record.virtualmachineid }">{{ text }}</router-link>
|
||||
</template>
|
||||
<template #volumename="{ text, record }">
|
||||
<router-link :to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link>
|
||||
</template>
|
||||
<template #size="{ text }">
|
||||
<span v-if="text">
|
||||
{{ parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) }} GiB
|
||||
</span>
|
||||
</template>
|
||||
<template #physicalsize="{ text }">
|
||||
<span v-if="text">
|
||||
{{ parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) }} GiB
|
||||
</span>
|
||||
</template>
|
||||
<template #physicalnetworkname="{ text, record }">
|
||||
<router-link :to="{ path: '/physicalnetwork/' + record.physicalnetworkid }">{{ text }}</router-link>
|
||||
</template>
|
||||
<template #serviceofferingname="{ text, record }">
|
||||
<router-link :to="{ path: '/computeoffering/' + record.serviceofferingid }">{{ text }}</router-link>
|
||||
</template>
|
||||
<template #hypervisor="{ text, record }">
|
||||
<span v-if="$route.name === 'hypervisorcapability'">
|
||||
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
||||
@ -198,6 +219,9 @@
|
||||
<status v-if="$route.path.startsWith('/host')" :text="getHostState(record)" displayText />
|
||||
<status v-else :text="text ? text : ''" displayText :styles="{ 'min-width': '80px' }" />
|
||||
</template>
|
||||
<template #status="{ text }">
|
||||
<status :text="text ? text : ''" displayText />
|
||||
</template>
|
||||
<template #allocationstate="{ text }">
|
||||
<status :text="text ? text : ''" displayText />
|
||||
</template>
|
||||
@ -303,6 +327,7 @@
|
||||
</template>
|
||||
<template #zonename="{ text, record }">
|
||||
<router-link v-if="$router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link>
|
||||
<router-link v-else-if="$router.resolve('/zones/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zones/' + record.zoneid }">{{ text }}</router-link>
|
||||
<span v-else>{{ text }}</span>
|
||||
</template>
|
||||
<template #rolename="{ text, record }">
|
||||
@ -431,6 +456,7 @@ import { api } from '@/api'
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import QuickView from '@/components/view/QuickView'
|
||||
import CopyLabel from '@/components/widgets/CopyLabel'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import ResourceLabel from '@/components/widgets/ResourceLabel'
|
||||
@ -442,6 +468,7 @@ export default {
|
||||
OsLogo,
|
||||
Status,
|
||||
QuickView,
|
||||
CopyLabel,
|
||||
TooltipButton,
|
||||
ResourceIcon,
|
||||
ResourceLabel
|
||||
|
||||
51
ui/src/components/widgets/CopyLabel.vue
Normal file
51
ui/src/components/widgets/CopyLabel.vue
Normal file
@ -0,0 +1,51 @@
|
||||
// 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>
|
||||
<span @click="$message.success($t('label.copied.clipboard') + ': ' + label)">
|
||||
<a-tooltip :title="tooltip ? tooltip : $t('label.copy')" :placement="tooltipPlacement">
|
||||
<a href="javascript:;" v-clipboard:copy="label">{{ label }}</a>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CopyLabel',
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tooltip: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tooltipPlacement: {
|
||||
type: String,
|
||||
default: 'top'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tooltip-icon {
|
||||
color: rgba(0,0,0,.45);
|
||||
}
|
||||
</style>
|
||||
@ -41,11 +41,11 @@ export default {
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||
},
|
||||
{
|
||||
name: 'resources',
|
||||
name: 'limits',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceCountUsage.vue')))
|
||||
},
|
||||
{
|
||||
name: 'limits',
|
||||
name: 'limits.configure',
|
||||
show: (record, route, user) => { return ['Admin', 'DomainAdmin'].includes(user.roletype) },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue')))
|
||||
},
|
||||
|
||||
@ -46,8 +46,8 @@ export default {
|
||||
return filters
|
||||
},
|
||||
columns: () => {
|
||||
const fields = ['name', 'displayname', 'state', 'ipaddress']
|
||||
const metricsFields = ['cpunumber', 'cpuused', 'cputotal',
|
||||
const fields = ['name', 'state', 'ipaddress']
|
||||
const metricsFields = ['cpunumber', 'cputotal', 'cpuused', 'memorytotal',
|
||||
{
|
||||
memoryused: (record) => {
|
||||
if (record.memoryintfreekbs <= 0 || record.memorykbs <= 0) {
|
||||
@ -56,7 +56,7 @@ export default {
|
||||
return parseFloat(100.0 * (record.memorykbs - record.memoryintfreekbs) / record.memorykbs).toFixed(2) + '%'
|
||||
}
|
||||
},
|
||||
'memorytotal', 'networkread', 'networkwrite', 'diskread', 'diskwrite', 'diskiopstotal']
|
||||
'networkread', 'networkwrite', 'diskread', 'diskwrite', 'diskiopstotal']
|
||||
|
||||
if (store.getters.metrics) {
|
||||
fields.push(...metricsFields)
|
||||
@ -66,18 +66,17 @@ export default {
|
||||
fields.splice(2, 0, 'instancename')
|
||||
fields.push('account')
|
||||
fields.push('hostname')
|
||||
fields.push('zonename')
|
||||
} else if (store.getters.userInfo.roletype === 'DomainAdmin') {
|
||||
fields.push('account')
|
||||
fields.push('zonename')
|
||||
} else {
|
||||
fields.push('zonename')
|
||||
fields.push('serviceofferingname')
|
||||
}
|
||||
fields.push('zonename')
|
||||
return fields
|
||||
},
|
||||
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'tags'],
|
||||
details: () => {
|
||||
var fields = ['displayname', 'name', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename',
|
||||
var fields = ['name', 'displayname', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename',
|
||||
'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account',
|
||||
'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy', 'hostcontrolstate']
|
||||
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)
|
||||
@ -455,7 +454,7 @@ export default {
|
||||
docHelp: 'plugins/cloudstack-kubernetes-service.html',
|
||||
permission: ['listKubernetesClusters'],
|
||||
columns: (store) => {
|
||||
var fields = ['name', 'state', 'size', 'cpunumber', 'memory']
|
||||
var fields = ['name', 'state', 'size', 'cpunumber', 'memory', 'kubernetesversionname']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) {
|
||||
fields.push('account')
|
||||
}
|
||||
@ -548,7 +547,7 @@ export default {
|
||||
docHelp: 'adminguide/autoscale_without_netscaler.html',
|
||||
resourceType: 'AutoScaleVmGroup',
|
||||
permission: ['listAutoScaleVmGroups'],
|
||||
columns: ['name', 'account', 'associatednetworkname', 'publicip', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'state'],
|
||||
columns: ['name', 'state', 'associatednetworkname', 'publicip', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'account'],
|
||||
details: ['name', 'id', 'account', 'domain', 'associatednetworkname', 'associatednetworkid', 'lbruleid', 'lbprovider', 'publicip', 'publicipid', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'interval', 'state', 'created'],
|
||||
related: [{
|
||||
name: 'vm',
|
||||
@ -652,7 +651,7 @@ export default {
|
||||
docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group',
|
||||
resourceType: 'VMInstanceGroup',
|
||||
permission: ['listInstanceGroups'],
|
||||
columns: ['name', 'account'],
|
||||
columns: ['name', 'account', 'domain'],
|
||||
details: ['name', 'id', 'account', 'domain', 'created'],
|
||||
related: [{
|
||||
name: 'vm',
|
||||
@ -706,6 +705,7 @@ export default {
|
||||
var fields = ['name', 'fingerprint']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('account')
|
||||
fields.push('domain')
|
||||
}
|
||||
return fields
|
||||
},
|
||||
@ -783,6 +783,7 @@ export default {
|
||||
var fields = ['name', 'id']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('account')
|
||||
fields.push('domain')
|
||||
}
|
||||
return fields
|
||||
},
|
||||
@ -854,6 +855,7 @@ export default {
|
||||
var fields = ['name', 'type', 'description']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('account')
|
||||
fields.push('domain')
|
||||
}
|
||||
return fields
|
||||
},
|
||||
|
||||
@ -44,12 +44,12 @@ export default {
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||
},
|
||||
{
|
||||
name: 'resources',
|
||||
name: 'limits',
|
||||
show: (record, route, user) => { return ['Admin', 'DomainAdmin'].includes(user.roletype) },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceCountUsage.vue')))
|
||||
},
|
||||
{
|
||||
name: 'limits',
|
||||
name: 'limits.configure',
|
||||
show: (record, route, user) => { return ['Admin'].includes(user.roletype) || (['DomainAdmin'].includes(user.roletype) && record.id !== user.domainid) },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue')))
|
||||
},
|
||||
|
||||
@ -35,8 +35,17 @@ export default {
|
||||
resourceType: 'Template',
|
||||
filters: ['self', 'shared', 'featured', 'community'],
|
||||
columns: () => {
|
||||
var fields = ['name', 'hypervisor', 'ostypename']
|
||||
var fields = ['name',
|
||||
{
|
||||
state: (record) => {
|
||||
if (record.isready) {
|
||||
return 'Ready'
|
||||
}
|
||||
return 'Not Ready'
|
||||
}
|
||||
}, 'ostypename', 'hypervisor']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('size')
|
||||
fields.push('account')
|
||||
}
|
||||
if (['Admin'].includes(store.getters.userInfo.roletype)) {
|
||||
@ -45,8 +54,8 @@ export default {
|
||||
return fields
|
||||
},
|
||||
details: () => {
|
||||
var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'isready', 'passwordenabled',
|
||||
'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type',
|
||||
var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled',
|
||||
'crossZones', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type',
|
||||
'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy']
|
||||
if (['Admin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('templatetype', 'url')
|
||||
@ -186,8 +195,17 @@ export default {
|
||||
resourceType: 'ISO',
|
||||
filters: ['self', 'shared', 'featured', 'community'],
|
||||
columns: () => {
|
||||
var fields = ['name', 'ostypename']
|
||||
var fields = ['name',
|
||||
{
|
||||
state: (record) => {
|
||||
if (record.isready) {
|
||||
return 'Ready'
|
||||
}
|
||||
return 'Not Ready'
|
||||
}
|
||||
}, 'ostypename']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('size')
|
||||
fields.push('account')
|
||||
}
|
||||
if (['Admin'].includes(store.getters.userInfo.roletype)) {
|
||||
|
||||
@ -33,7 +33,7 @@ export default {
|
||||
permission: ['listNetworks'],
|
||||
resourceType: 'Network',
|
||||
columns: () => {
|
||||
var fields = ['name', 'state', 'type', 'vpcname', 'cidr', 'ip6cidr', 'broadcasturi', 'domain', 'account', 'zonename']
|
||||
var fields = ['name', 'state', 'type', 'vpcname', 'cidr', 'ip6cidr', 'broadcasturi', 'account', 'domain', 'zonename']
|
||||
if (!isAdmin()) {
|
||||
fields = fields.filter(function (e) { return e !== 'broadcasturi' })
|
||||
}
|
||||
@ -189,7 +189,7 @@ export default {
|
||||
docHelp: 'adminguide/networking_and_traffic.html#configuring-a-virtual-private-cloud',
|
||||
permission: ['listVPCs'],
|
||||
resourceType: 'Vpc',
|
||||
columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'zonename'],
|
||||
columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'domain', 'zonename'],
|
||||
details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'],
|
||||
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
|
||||
related: [{
|
||||
@ -319,8 +319,8 @@ export default {
|
||||
docHelp: 'adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts',
|
||||
permission: ['listPublicIpAddresses'],
|
||||
resourceType: 'PublicIpAddress',
|
||||
columns: ['ipaddress', 'state', 'associatednetworkname', 'virtualmachinename', 'allocated', 'account', 'zonename'],
|
||||
details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'zonename'],
|
||||
columns: ['ipaddress', 'state', 'associatednetworkname', 'virtualmachinename', 'allocated', 'account', 'domain', 'zonename'],
|
||||
details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'domain', 'zonename'],
|
||||
filters: ['allocated', 'reserved', 'free'],
|
||||
component: shallowRef(() => import('@/views/network/PublicIpResource.vue')),
|
||||
tabs: [{
|
||||
@ -423,7 +423,7 @@ export default {
|
||||
icon: 'gateway-outlined',
|
||||
hidden: true,
|
||||
permission: ['listPrivateGateways'],
|
||||
columns: ['ipaddress', 'state', 'gateway', 'netmask', 'account'],
|
||||
columns: ['ipaddress', 'state', 'gateway', 'netmask', 'account', 'domain'],
|
||||
details: ['ipaddress', 'gateway', 'netmask', 'vlan', 'sourcenatsupported', 'aclname', 'account', 'domain', 'zone', 'associatednetwork', 'associatednetworkid'],
|
||||
tabs: [{
|
||||
name: 'details',
|
||||
@ -709,7 +709,7 @@ export default {
|
||||
title: 'label.vpncustomergatewayid',
|
||||
icon: 'lock-outlined',
|
||||
permission: ['listVpnCustomerGateways'],
|
||||
columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account'],
|
||||
columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account', 'domain'],
|
||||
details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'account', 'domain'],
|
||||
searchFilters: ['keyword', 'domainid', 'account'],
|
||||
resourceType: 'VPNCustomerGateway',
|
||||
@ -908,8 +908,8 @@ export default {
|
||||
permission: ['listGuestVlans'],
|
||||
resourceType: 'GuestVlan',
|
||||
filters: ['allocatedonly', 'all'],
|
||||
columns: ['vlan', 'zonename', 'physicalnetworkname', 'allocationstate', 'taken', 'domain', 'account', 'project'],
|
||||
details: ['vlan', 'zonename', 'physicalnetworkname', 'allocationstate', 'taken', 'domain', 'account', 'project', 'isdedicated'],
|
||||
columns: ['vlan', 'allocationstate', 'physicalnetworkname', 'taken', 'account', 'project', 'domain', 'zonename'],
|
||||
details: ['vlan', 'allocationstate', 'physicalnetworkname', 'taken', 'account', 'project', 'domain', 'isdedicated', 'zonename'],
|
||||
searchFilters: ['zoneid'],
|
||||
tabs: [{
|
||||
name: 'details',
|
||||
|
||||
@ -47,11 +47,11 @@ export default {
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'resources',
|
||||
name: 'limits',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceCountUsage.vue')))
|
||||
},
|
||||
{
|
||||
name: 'limits',
|
||||
name: 'limits.configure',
|
||||
show: (record, route, user) => { return ['Admin', 'DomainAdmin'].includes(user.roletype) },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue')))
|
||||
},
|
||||
|
||||
@ -38,7 +38,7 @@ export default {
|
||||
}
|
||||
},
|
||||
columns: () => {
|
||||
const fields = ['name', 'state', 'type', 'vmname', 'sizegb']
|
||||
const fields = ['name', 'state', 'sizegb', 'type', 'vmname']
|
||||
const metricsFields = ['diskkbsread', 'diskkbswrite', 'diskiopstotal']
|
||||
|
||||
if (store.getters.userInfo.roletype === 'Admin') {
|
||||
@ -307,14 +307,15 @@ export default {
|
||||
permission: ['listSnapshots'],
|
||||
resourceType: 'Snapshot',
|
||||
columns: () => {
|
||||
var fields = ['name', 'state', 'volumename', 'intervaltype', 'created']
|
||||
var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize', 'created']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('domain')
|
||||
fields.push('account')
|
||||
fields.push('domain')
|
||||
}
|
||||
fields.push('zonename')
|
||||
return fields
|
||||
},
|
||||
details: ['name', 'id', 'volumename', 'intervaltype', 'account', 'domain', 'created'],
|
||||
details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'account', 'domain', 'created'],
|
||||
tabs: [
|
||||
{
|
||||
name: 'details',
|
||||
@ -380,8 +381,8 @@ export default {
|
||||
columns: () => {
|
||||
const fields = ['displayname', 'state', 'name', 'type', 'current', 'parentName', 'created']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('domain')
|
||||
fields.push('account')
|
||||
fields.push('domain')
|
||||
}
|
||||
return fields
|
||||
},
|
||||
@ -446,7 +447,7 @@ export default {
|
||||
title: 'label.backup',
|
||||
icon: 'cloud-upload-outlined',
|
||||
permission: ['listBackups'],
|
||||
columns: [{ name: (record) => { return record.virtualmachinename } }, 'virtualmachinename', 'status', 'type', 'created', 'account', 'zone'],
|
||||
columns: [{ name: (record) => { return record.virtualmachinename } }, 'status', 'virtualmachinename', 'type', 'created', 'account', 'domain', 'zone'],
|
||||
details: ['virtualmachinename', 'id', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'],
|
||||
actions: [
|
||||
{
|
||||
|
||||
@ -109,6 +109,8 @@ const err = (error) => {
|
||||
if (originalPath !== '/user/login') {
|
||||
router.push({ path: '/user/login', query: { redirect: originalPath } })
|
||||
}
|
||||
store.commit('SET_COUNT_NOTIFY', 0)
|
||||
notification.destroy()
|
||||
})
|
||||
}
|
||||
if (response.status === 404) {
|
||||
|
||||
@ -34,27 +34,28 @@
|
||||
<div style="margin-top: 15px">
|
||||
<span>{{ $t('message.select.a.zone') }}</span><br/>
|
||||
<a-form-item :label="$t('label.zoneid')" name="zoneid" ref="zoneid">
|
||||
<div v-if="zones.length <= 8">
|
||||
<a-row type="flex" :gutter="5" justify="start">
|
||||
<div v-if="zones.length <= 9">
|
||||
<a-row type="flex" :gutter="[16,18]" justify="start">
|
||||
<div v-for="(zoneItem, idx) in zones" :key="idx">
|
||||
<a-radio-group
|
||||
:key="idx"
|
||||
:size="large"
|
||||
v-model:value="form.zoneid"
|
||||
@change="onSelectZoneId(zoneItem.id)">
|
||||
<a-col :span="8">
|
||||
<a-card-grid style="width:200px;" :title="zoneItem.name" :hoverable="false">
|
||||
<a-radio :value="zoneItem.id">
|
||||
<div>
|
||||
<a-col :span="6">
|
||||
<a-radio-button
|
||||
:value="zoneItem.id"
|
||||
style="border-width: 2px"
|
||||
class="zone-radio-button">
|
||||
<span>
|
||||
<resource-icon
|
||||
v-if="zoneItem && zoneItem.icon && zoneItem.icon.base64image"
|
||||
:image="zoneItem.icon.base64image"
|
||||
size="36"
|
||||
style="marginTop: -30px; marginLeft: 60px" />
|
||||
<global-outlined v-else :style="{fontSize: '36px', marginLeft: '60px', marginTop: '-40px'}"/>
|
||||
</div>
|
||||
</a-radio>
|
||||
<a-card-meta title="" :description="zoneItem.name" style="text-align:center; paddingTop: 10px;" />
|
||||
</a-card-grid>
|
||||
size="2x" />
|
||||
<global-outlined size="2x" v-else />
|
||||
{{ zoneItem.name }}
|
||||
</span>
|
||||
</a-radio-button>
|
||||
</a-col>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
@ -2938,6 +2939,15 @@ export default {
|
||||
margin: 0 0 1.2rem;
|
||||
}
|
||||
|
||||
.zone-radio-button {
|
||||
width:100%;
|
||||
min-width: 225px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
padding-left: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vm-info-card {
|
||||
.ant-card-body {
|
||||
min-height: 250px;
|
||||
|
||||
@ -34,27 +34,28 @@
|
||||
<div style="margin-top: 15px">
|
||||
<span>{{ $t('message.select.a.zone') }}</span><br/>
|
||||
<a-form-item :label="$t('label.zoneid')" name="zoneid" ref="zoneid">
|
||||
<div v-if="zones.length <= 8">
|
||||
<a-row type="flex" :gutter="5" justify="start">
|
||||
<div v-if="zones.length <= 9">
|
||||
<a-row type="flex" :gutter="[16, 18]" justify="start">
|
||||
<div v-for="(zoneItem, idx) in zones" :key="idx">
|
||||
<a-radio-group
|
||||
:key="idx"
|
||||
:size="large"
|
||||
v-model:value="form.zoneid"
|
||||
@change="onSelectZoneId(zoneItem.id)">
|
||||
<a-col :span="8">
|
||||
<a-card-grid style="width:200px;" :title="zoneItem.name" :hoverable="false">
|
||||
<a-radio :value="zoneItem.id">
|
||||
<div>
|
||||
<a-col :span="6">
|
||||
<a-radio-button
|
||||
:value="zoneItem.id"
|
||||
style="border-width: 2px"
|
||||
class="zone-radio-button">
|
||||
<span>
|
||||
<resource-icon
|
||||
v-if="zoneItem && zoneItem.icon && zoneItem.icon.base64image"
|
||||
:image="zoneItem.icon.base64image"
|
||||
size="36"
|
||||
style="marginTop: -30px; marginLeft: 60px" />
|
||||
<global-outlined v-else :style="{fontSize: '36px', marginLeft: '60px', marginTop: '-40px'}"/>
|
||||
</div>
|
||||
</a-radio>
|
||||
<a-card-meta title="" :description="zoneItem.name" style="text-align:center; paddingTop: 10px;" />
|
||||
</a-card-grid>
|
||||
size="2x" />
|
||||
<global-outlined size="2x" v-else />
|
||||
{{ zoneItem.name }}
|
||||
</span>
|
||||
</a-radio-button>
|
||||
</a-col>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
@ -74,7 +75,7 @@
|
||||
>
|
||||
<a-select-option v-for="zone1 in zones" :key="zone1.id" :label="zone1.name">
|
||||
<span>
|
||||
<resource-icon v-if="zone1.icon && zone1.icon.base64image" :image="zone1.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon v-if="zone1.icon && zone1.icon.base64image" :image="zone1.icon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zone1.name }}
|
||||
</span>
|
||||
@ -2695,6 +2696,15 @@ export default {
|
||||
margin: 0 0 1.2rem;
|
||||
}
|
||||
|
||||
.zone-radio-button {
|
||||
width:100%;
|
||||
min-width: 225px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
padding-left: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vm-info-card {
|
||||
.ant-card-body {
|
||||
min-height: 250px;
|
||||
|
||||
@ -34,13 +34,21 @@
|
||||
<barcode-outlined /> {{ vm.isoid }}
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.volumes')" key="volumes">
|
||||
<volumes-tab :resource="vm" :items="volumes" :loading="loading" />
|
||||
<a-button
|
||||
type="primary"
|
||||
style="width: 100%; margin-bottom: 10px"
|
||||
@click="showAddVolModal"
|
||||
:loading="loading"
|
||||
:disabled="!('createVolume' in $store.getters.apis)">
|
||||
<template #icon><plus-outlined /></template> {{ $t('label.action.create.volume.add') }}
|
||||
</a-button>
|
||||
<volumes-tab :resource="vm" :loading="loading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.nics')" key="nics" v-if="'listNics' in $store.getters.apis">
|
||||
<a-button
|
||||
type="dashed"
|
||||
type="primary"
|
||||
style="width: 100%; margin-bottom: 10px"
|
||||
@click="showAddModal"
|
||||
@click="showAddNicModal"
|
||||
:loading="loadingNic"
|
||||
:disabled="!('addNicToVirtualMachine' in $store.getters.apis)">
|
||||
<template #icon><plus-outlined /></template> {{ $t('label.network.addvm') }}
|
||||
@ -130,6 +138,16 @@
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<a-modal
|
||||
:visible="showAddVolumeModal"
|
||||
:title="$t('label.action.create.volume.add')"
|
||||
:maskClosable="false"
|
||||
:closable="true"
|
||||
:footer="null"
|
||||
@cancel="closeModals">
|
||||
<CreateVolume :resource="resource" @close-action="closeModals" />
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
:visible="showAddNetworkModal"
|
||||
:title="$t('label.network.addvm')"
|
||||
@ -289,6 +307,7 @@ import DetailsTab from '@/components/view/DetailsTab'
|
||||
import StatsTab from '@/components/view/StatsTab'
|
||||
import EventsTab from '@/components/view/EventsTab'
|
||||
import DetailSettings from '@/components/view/DetailSettings'
|
||||
import CreateVolume from '@/views/storage/CreateVolume'
|
||||
import NicsTable from '@/views/network/NicsTable'
|
||||
import ListResourceTable from '@/components/view/ListResourceTable'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
@ -304,6 +323,7 @@ export default {
|
||||
StatsTab,
|
||||
EventsTab,
|
||||
DetailSettings,
|
||||
CreateVolume,
|
||||
NicsTable,
|
||||
ListResourceTable,
|
||||
TooltipButton,
|
||||
@ -328,9 +348,11 @@ export default {
|
||||
vm: {},
|
||||
totalStorage: 0,
|
||||
currentTab: 'details',
|
||||
showAddVolumeModal: false,
|
||||
showAddNetworkModal: false,
|
||||
showUpdateIpModal: false,
|
||||
showSecondaryIpModal: false,
|
||||
diskOfferings: [],
|
||||
addNetworkData: {
|
||||
allNetworks: [],
|
||||
network: '',
|
||||
@ -408,6 +430,14 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
listDiskOfferings () {
|
||||
api('listDiskOfferings', {
|
||||
listAll: 'true',
|
||||
zoneid: this.vm.zoneid
|
||||
}).then(response => {
|
||||
this.diskOfferings = response.listdiskofferingsresponse.diskoffering
|
||||
})
|
||||
},
|
||||
listNetworks () {
|
||||
api('listNetworks', {
|
||||
listAll: 'true',
|
||||
@ -456,11 +486,16 @@ export default {
|
||||
this.listIps.loading = false
|
||||
})
|
||||
},
|
||||
showAddModal () {
|
||||
showAddVolModal () {
|
||||
this.showAddVolumeModal = true
|
||||
this.listDiskOfferings()
|
||||
},
|
||||
showAddNicModal () {
|
||||
this.showAddNetworkModal = true
|
||||
this.listNetworks()
|
||||
},
|
||||
closeModals () {
|
||||
this.showAddVolumeModal = false
|
||||
this.showAddNetworkModal = false
|
||||
this.showUpdateIpModal = false
|
||||
this.showSecondaryIpModal = false
|
||||
|
||||
@ -37,13 +37,15 @@
|
||||
v-if="item.icon && item.icon.base64image"
|
||||
class="radio-group__os-logo"
|
||||
:image="item.icon.base64image"
|
||||
size="1x" />
|
||||
size="2x" />
|
||||
<os-logo
|
||||
v-else
|
||||
class="radio-group__os-logo"
|
||||
size="2x"
|
||||
:osId="item.ostypeid"
|
||||
:os-name="item.osName" />
|
||||
{{ item.displaytext }}
|
||||
|
||||
{{ item.displaytext }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-list-item>
|
||||
@ -156,15 +158,11 @@ export default {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
:deep(.ant-radio) {
|
||||
margin-right: 20px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
&__os-logo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin-top: 2px;
|
||||
margin-left: 23px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
}" >
|
||||
<a-select-option v-for="(zone, index) in zones" :key="index" :label="zone.name">
|
||||
<span>
|
||||
<resource-icon v-if="zone.icon && zone.icon.base64image" :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon v-if="zone.icon && zone.icon.base64image" :image="zone.icon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zone.name }}
|
||||
</span>
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
:rowKey="record => record.zoneid">
|
||||
<template #zonename="{record}">
|
||||
<span v-if="fetchZoneIcon(record.zoneid)">
|
||||
<resource-icon :image="zoneIcon" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="zoneIcon" size="2x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
<span> {{ record.zonename }} </span>
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
v-model:value="form.name"
|
||||
:placeholder="apiParams.name.description" />
|
||||
</a-form-item>
|
||||
<a-form-item ref="zoneid" name="zoneid" v-if="!createVolumeFromSnapshot">
|
||||
<a-form-item ref="zoneid" name="zoneid" v-if="!createVolumeFromVM && !createVolumeFromSnapshot">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/>
|
||||
</template>
|
||||
@ -150,6 +150,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
createVolumeFromVM () {
|
||||
return this.$route.path.startsWith('/vm/')
|
||||
},
|
||||
createVolumeFromSnapshot () {
|
||||
return this.$route.path.startsWith('/snapshot')
|
||||
}
|
||||
@ -192,7 +195,11 @@ export default {
|
||||
},
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
api('listZones', { showicon: true }).then(json => {
|
||||
const params = { showicon: true }
|
||||
if (this.createVolumeFromVM) {
|
||||
params.id = this.resource.zoneid
|
||||
}
|
||||
api('listZones', params).then(json => {
|
||||
this.zones = json.listzonesresponse.zone || []
|
||||
this.form.zoneid = this.zones[0].id || ''
|
||||
this.fetchDiskOfferings(this.form.zoneid)
|
||||
@ -221,6 +228,12 @@ export default {
|
||||
this.formRef.value.validate().then(() => {
|
||||
const formRaw = toRaw(this.form)
|
||||
const values = this.handleRemoveFields(formRaw)
|
||||
if (this.createVolumeFromVM) {
|
||||
values.account = this.resource.account
|
||||
values.domainid = this.resource.domainid
|
||||
values.virtualmachineid = this.resource.id
|
||||
values.zoneid = this.resource.zoneid
|
||||
}
|
||||
if (this.createVolumeFromSnapshot) {
|
||||
values.snapshotid = this.resource.id
|
||||
}
|
||||
@ -231,6 +244,25 @@ export default {
|
||||
title: this.$t('message.success.create.volume'),
|
||||
description: values.name,
|
||||
successMessage: this.$t('message.success.create.volume'),
|
||||
successMethod: (result) => {
|
||||
this.closeModal()
|
||||
if (this.createVolumeFromVM) {
|
||||
const params = {}
|
||||
params.id = result.jobresult.volume.id
|
||||
params.virtualmachineid = this.resource.id
|
||||
api('attachVolume', params).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.attachvolumeresponse.jobid,
|
||||
title: this.$t('message.success.attach.volume'),
|
||||
description: values.name,
|
||||
successMessage: this.$t('message.attach.volume.success'),
|
||||
errorMessage: this.$t('message.attach.volume.failed'),
|
||||
loadingMessage: this.$t('message.attach.volume.progress'),
|
||||
catchMessage: this.$t('error.fetching.async.job.result')
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
errorMessage: this.$t('message.create.volume.failed'),
|
||||
loadingMessage: this.$t('message.create.volume.processing'),
|
||||
catchMessage: this.$t('error.fetching.async.job.result')
|
||||
@ -262,7 +294,8 @@ export default {
|
||||
width: 80vw;
|
||||
|
||||
@media (min-width: 500px) {
|
||||
width: 400px;
|
||||
min-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user