This is an automated email from the ASF dual-hosted git repository. rohit pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 62feb24de657772a01ed145e0a6476306a1d7374 Merge: 540dcc1040a feb9509547a Author: Rohit Yadav <rohit.ya...@shapeblue.com> AuthorDate: Fri Aug 11 15:15:27 2023 +0530 Merge remote-tracking branch 'origin/4.18' Signed-off-by: Rohit Yadav <rohit.ya...@shapeblue.com> ui/public/locales/en.json | 7 ++- ui/src/components/header/UserMenu.vue | 9 ++- ui/src/components/menu/SideMenu.vue | 2 - ui/src/components/view/DetailsTab.vue | 15 +++++ ui/src/components/view/InfoCard.vue | 72 +++++++++++++++------- ui/src/components/view/ListView.vue | 39 ++++++++++-- ui/src/components/widgets/CopyLabel.vue | 51 +++++++++++++++ ui/src/config/section/account.js | 4 +- ui/src/config/section/compute.js | 22 ++++--- ui/src/config/section/domain.js | 4 +- ui/src/config/section/image.js | 26 ++++++-- ui/src/config/section/network.js | 16 ++--- ui/src/config/section/project.js | 4 +- ui/src/config/section/storage.js | 13 ++-- ui/src/views/compute/CreateAutoScaleVmGroup.vue | 38 ++++++++---- ui/src/views/compute/DeployVM.vue | 40 +++++++----- ui/src/views/compute/InstanceTab.vue | 43 +++++++++++-- .../views/compute/wizard/TemplateIsoRadioGroup.vue | 14 ++--- ui/src/views/dashboard/CapacityDashboard.vue | 2 +- ui/src/views/image/TemplateZones.vue | 4 +- ui/src/views/storage/CreateVolume.vue | 39 +++++++++++- 21 files changed, 354 insertions(+), 110 deletions(-) diff --cc ui/src/components/header/UserMenu.vue index 42df0446463,0c7434594be..dfa84c77b65 --- a/ui/src/components/header/UserMenu.vue +++ b/ui/src/components/header/UserMenu.vue @@@ -27,36 -27,50 +27,40 @@@ <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' }"> + <a-avatar v-else-if="userInitials" class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: $config.theme['@primary-color'], color: 'white' }"> {{ userInitials }} </a-avatar> - <a-avatar v-else class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: '#1890ff', color: 'white' }"> + <a-avatar v-else class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: $config.theme['@primary-color'], color: 'white' }"> <template #icon><user-outlined /></template> </a-avatar> <span>{{ nickname() }}</span> </span> <template #overlay> - <a-menu class="user-menu-wrapper"> - <router-link :to="{ path: '/accountuser/' + $store.getters.userInfo.id }"> - <a-menu-item class="user-menu-item" key="0"> - <UserOutlined class="user-menu-item-icon" /> - <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" /> - <span class="user-menu-item-name" style="margin-right: 5px">{{ $t('label.use.local.timezone') }}</span> - <a-switch :checked="$store.getters.usebrowsertimezone" /> - </a-menu-item> - </a> - <a :href="$config.docBase" target="_blank"> - <a-menu-item class="user-menu-item" key="2"> - <QuestionCircleOutlined class="user-menu-item-icon" /> - <span class="user-menu-item-name">{{ $t('label.help') }}</span> - </a-menu-item> - </a> + <a-menu class="user-menu-wrapper" @click="handleClickMenu"> + <a-menu-item class="user-menu-item" key="profile"> + <UserOutlined class="user-menu-item-icon" /> + <span class="user-menu-item-name">{{ $t('label.profilename') }}</span> + </a-menu-item> ++ <a-menu-item class="user-menu-item" key="limits"> ++ <ControlOutlined class="user-menu-item-icon" /> ++ <span class="user-menu-item-name">{{ $t('label.limits') }}</span> ++ </a-menu-item> + <a-menu-item class="user-menu-item" key="timezone"> + <ClockCircleOutlined class="user-menu-item-icon" /> + <span class="user-menu-item-name" style="margin-right: 5px">{{ $t('label.use.local.timezone') }}</span> + <a-switch :checked="$store.getters.usebrowsertimezone" /> + </a-menu-item> + <a-menu-item class="user-menu-item" key="document"> + <QuestionCircleOutlined class="user-menu-item-icon" /> + <span class="user-menu-item-name">{{ $t('label.help') }}</span> + </a-menu-item> <a-menu-divider/> - <a href="javascript:;" @click="handleLogout"> - <a-menu-item class="user-menu-item" key="3"> - <LogoutOutlined class="user-menu-item-icon" /> - <span class="user-menu-item-name">{{ $t('label.logout') }}</span> - </a-menu-item> - </a> + <a-menu-item class="user-menu-item" key="logout"> + <LogoutOutlined class="user-menu-item-icon" /> + <span class="user-menu-item-name">{{ $t('label.logout') }}</span> + </a-menu-item> </a-menu> </template> </a-dropdown> @@@ -137,22 -151,6 +141,25 @@@ export default }) }) }, + handleClickMenu (item) { + switch (item.key) { + case 'profile': + this.$router.push(`/accountuser/${this.$store.getters.userInfo.id}`) + break ++ case 'limits': ++ this.$router.push(`/account/${this.$store.getters.userInfo.accountid}?tab=limits`) ++ break + case 'timezone': + this.toggleUseBrowserTimezone() + break + case 'document': + window.open(this.$config.docBase, '_blank') + break + case 'logout': + this.handleLogout() + break + } + }, handleLogout () { return this.Logout({}).then(() => { this.$router.push('/user/login') diff --cc ui/src/components/view/DetailsTab.vue index e276e05bcb0,e359f14d86b..301686c3433 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@@ -51,6 -51,21 +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 ++ {{ parseFloat(dataResource.size / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + </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 ++ {{ parseFloat(dataResource.physicalsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + </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 ++ {{ parseFloat(dataResource.virtualsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + </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> diff --cc ui/src/components/view/InfoCard.vue index df1411ad5fe,72df182d80e..550bba74409 --- a/ui/src/components/view/InfoCard.vue +++ b/ui/src/components/view/InfoCard.vue @@@ -144,9 -124,9 +144,9 @@@ icon="barcode-outlined" type="dashed" size="small" - :copyResource="resource.id" + :copyResource="String(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"> diff --cc ui/src/components/view/ListView.vue index 69dc3a1c559,38b8b519138..8352a179afe --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@@ -37,150 -37,180 +37,171 @@@ </a-menu> </div> </template> - <template #footer> - <span v-if="hasSelected"> - {{ `Selected ${selectedRowKeys.length} items` }} - </span> - </template> - - <!-- - <div #expandedRowRender="{ resource }"> - <info-card :resource="resource style="margin-left: 0px; width: 50%"> - <div #actions style="padding-top: 12px"> - <a-tooltip - v-for="(action, actionIndex) in $route.meta.actions" - :key="actionIndex" - placement="bottom"> - <template #title> - {{ $t(action.label) }} - </template> - <a-button - v-if="action.api in $store.getters.apis && action.dataView && - ('show' in action ? action.show(resource, $store.getters.userInfo) : true)" - :icon="action.icon" - :type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')" - shape="circle" - style="margin-right: 5px; margin-top: 12px" - @click="$parent.execAction(action)" - > - </a-button> - </a-tooltip> - </div> - </info-card> - </div> - --> + <template #bodyCell="{ column, text, record }"> + <template v-if="column.key === 'name'"> + <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 + style="margin-left: 5px" + :actions="actions" + :resource="record" + :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'name' " + @exec-action="$parent.execAction"/> + <span v-if="$route.path.startsWith('/project')" style="margin-right: 5px"> + <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> + <span v-else :style="{ 'margin-right': record.ostypename ? '5px' : '0' }"> + <os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" /> + </span> - <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="2x"/> + <span v-if="record.hasannotations"> + <span v-if="record.id"> + <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.id, query: { tab: 'comments' } }"><message-filled style="padding-left: 10px" size="small"/></router-link> + </span> + <router-link v-else :to="{ path: $route.path + '/' + record.name }" >{{ text }}</router-link> + </span> + <span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span> + <span v-else-if="$route.path.startsWith('/alert')"> + <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + </span> + <span v-else-if="$route.path.startsWith('/tungstenfabric')"> + <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + </span> + <span v-else-if="isTungstenPath()"> + <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" v-if="record.uuid && record.zoneid">{{ $t(text.toLowerCase()) }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link> + <router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + </span> + <span v-else> + <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link> + </span> </span> - <os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="2x" /> - </span> - <span style="min-width: 120px" > + </template> + <template v-if="record.clustertype === 'ExternalManaged' && $route.path.split('/')[1] === 'kubernetes' && ['cpunumber', 'memory', 'size'].includes(column.key)"> + <span>{{ text <= 0 ? 'N/A' : text }}</span> + </template> + <template v-if="column.key === 'templatetype'"> + <router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link> + </template> + <template v-if="column.key === 'type'"> + <span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)">{{ $t(text.toLowerCase()) }}</span> + <span v-else>{{ text }}</span> + </template> + + <template v-if="column.key === 'schedule'"> + {{ text }} + <br/> + ({{ generateHumanReadableSchedule(text) }}) + </template> + <template v-if="column.key === 'displayname'"> <QuickView style="margin-left: 5px" :actions="actions" :resource="record" - :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'name' " + :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'displayname' " @exec-action="$parent.execAction"/> - <span v-if="$route.path.startsWith('/project')" style="margin-right: 5px"> - <tooltip-button type="dashed" size="small" icon="LoginOutlined" @onClick="changeProject(record)" /> - </span> + <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> + </template> + <template v-if="column.key === 'username'"> <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"/> - <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> - <span v-else :style="{ 'margin-right': record.ostypename ? '5px' : '0' }"> - <os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" /> + <user-outlined v-else style="font-size: 16px;" /> </span> - - <span v-if="record.hasannotations"> - <span v-if="record.id"> - <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.id, query: { tab: 'comments' } }"><message-filled style="padding-left: 10px" size="small"/></router-link> - </span> - <router-link v-else :to="{ path: $route.path + '/' + record.name }" >{{ text }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.id }" v-if="['/accountuser', '/vpnuser'].includes($route.path)">{{ text }}</router-link> + <router-link :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link> + <span v-else>{{ text }}</span> + </template> + <template v-if="column.key === 'entityid'"> + <router-link :to="{ path: generateCommentsPath(record), query: { tab: 'comments' } }">{{ record.entityname }}</router-link> + </template> + <template v-if="column.key === 'entitytype'"> + {{ generateHumanReadableEntityType(record) }} + </template> + <template v-if="column.key === 'adminsonly' && ['Admin'].includes($store.getters.userInfo.roletype)"> + <a-checkbox :checked="record.adminsonly" :value="record.id" v-if="record.userid === $store.getters.userInfo.id" @change="e => updateAdminsOnly(e)" /> + <a-checkbox :checked="record.adminsonly" disabled v-else /> + </template> + <template v-if="column.key === 'ipaddress'" 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-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span> - <span v-else-if="$route.path.startsWith('/preferences')">{{ text }}</span> - <span v-else-if="$route.path.startsWith('/alert')"> - <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + <span v-if="record.issourcenat"> + + <a-tag>source-nat</a-tag> </span> - <span v-else-if="$route.path.startsWith('/tungstenfabric')"> - <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + <span v-if="record.isstaticnat"> + + <a-tag>static-nat</a-tag> </span> - <span v-else-if="isTungstenPath()"> - <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" v-if="record.uuid && record.zoneid">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + </template> + <template v-if="column.key === 'ip6address'" href="javascript:;"> + <span>{{ ipV6Address(text, record) }}</span> + </template> + <template v-if="column.key === 'publicip'"> + <router-link v-if="['/autoscalevmgroup'].includes($route.path)" :to="{ path: '/publicip' + '/' + record.publicipid }">{{ text }}</router-link> + <router-link v-else :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> + </template> + <template v-if="column.key === 'traffictype'"> + {{ text }} + </template> + <template v-if="column.key === 'vmname'"> + <router-link :to="{ path: createPathBasedOnVmType(record.vmtype, record.virtualmachineid) }">{{ text }}</router-link> + </template> + <template v-if="column.key === 'virtualmachinename'"> + <router-link :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }">{{ text }}</router-link> + </template> ++ <template v-if="column.key === 'volumename'"> ++ <router-link :to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link> ++ </template> ++ <template v-if="column.key === 'size'"> ++ <span v-if="text"> ++ {{ parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) }} GiB + </span> - <span v-else> - <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link> ++ </template> ++ <template v-if="column.key === 'physicalsize'"> ++ <span v-if="text"> ++ {{ parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) }} GiB + </span> ++ </template> ++ <template v-if="column.key === 'physicalnetworkname'"> ++ <router-link :to="{ path: '/physicalnetwork/' + record.physicalnetworkid }">{{ text }}</router-link> ++ </template> ++ <template v-if="column.key === 'serviceofferingname'"> ++ <router-link :to="{ path: '/computeoffering/' + record.serviceofferingid }">{{ text }}</router-link> ++ </template> + <template v-if="column.key === 'hypervisor'"> + <span v-if="$route.name === 'hypervisorcapability'"> + <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> </span> - </template> - <template #templatetype="{ text, record }"> - <router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link> - </template> - <template #type="{ text }"> - <span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)">{{ $t(text.toLowerCase()) }}</span> - <span v-else>{{ text }}</span> - </template> - <template #displayname="{text, record}"> - <QuickView - style="margin-left: 5px" - :actions="actions" - :resource="record" - :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'displayname' " - @exec-action="$parent.execAction"/> - <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> - </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="2x"/> - <user-outlined v-else style="font-size: 16px;" /> + <span v-else-if="$route.name === 'guestoshypervisormapping'"> + <QuickView + style="margin-left: 5px" + :actions="actions" + :resource="record" + :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'hypervisor' " + @exec-action="$parent.execAction"/> + <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> </span> - <router-link :to="{ path: $route.path + '/' + record.id }" v-if="['/accountuser', '/vpnuser'].includes($route.path)">{{ text }}</router-link> - <router-link :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link> <span v-else>{{ text }}</span> </template> - <template #entityid="{ record }" href="javascript:;"> - <router-link :to="{ path: generateCommentsPath(record), query: { tab: 'comments' } }">{{ record.entityname }}</router-link> - </template> - <template #entitytype="{ record }" href="javascript:;"> - {{ generateHumanReadableEntityType(record) }} - </template> - <template #adminsonly="{ record }" v-if="['Admin'].includes($store.getters.userInfo.roletype)" href="javascript:;"> - <a-checkbox :checked="record.adminsonly" :value="record.id" v-if="record.userid === $store.getters.userInfo.id" @change="e => updateAdminsOnly(e)" /> - <a-checkbox :checked="record.adminsonly" disabled v-else /> - </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> - <copy-label :label="text" /> - </span> - <span v-if="record.issourcenat"> - - <a-tag>source-nat</a-tag> - </span> - <span v-if="record.isstaticnat"> - - <a-tag>static-nat</a-tag> - </span> - </template> - <template #ip6address="{ text, record }" href="javascript:;"> - <span>{{ ipV6Address(text, record) }}</span> - </template> - <template #publicip="{ text, record }"> - <router-link v-if="['/autoscalevmgroup'].includes($route.path)" :to="{ path: '/publicip' + '/' + record.publicipid }">{{ text }}</router-link> - <router-link v-else :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> - </template> - <template #traffictype="{ text }" href="javascript:;"> - {{ text }} - </template> - <template #vmname="{ text, record }"> - <router-link :to="{ path: createPathBasedOnVmType(record.vmtype, record.virtualmachineid) }">{{ text }}</router-link> - </template> - <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'"> + <template v-if="column.key === 'osname'"> + <span v-if="$route.name === 'guestos'"> <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> </span> <span v-else>{{ text }}</span> @@@ -189,22 -219,25 +210,25 @@@ <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 }"> ++ <template v-if="column.key === 'status'"> + <status :text="text ? text : ''" displayText /> + </template> - <template #allocationstate="{ text }"> + <template v-if="column.key === 'allocationstate'"> <status :text="text ? text : ''" displayText /> </template> - <template #resourcestate="{ text }"> + <template v-if="column.key === 'resourcestate'"> <status :text="text ? text : ''" displayText /> </template> - <template #powerstate="{ text }"> + <template v-if="column.key === 'powerstate'"> <status :text="text ? text : ''" displayText /> </template> - <template #agentstate="{ text }"> + <template v-if="column.key === 'agentstate'"> <status :text="text ? text : ''" displayText /> </template> - <template #quotastate="{ text }"> + <template v-if="column.key === 'quotastate'"> <status :text="text ? text : ''" displayText /> </template> - <template #vlan="{ text, record }"> + <template v-if="column.key === 'vlan'"> <a href="javascript:;"> <router-link v-if="$route.path === '/guestvlans'" :to="{ path: '/guestvlans/' + record.id }">{{ text }}</router-link> </a> @@@ -230,213 -260,193 +254,214 @@@ <router-link v-if="record.storageid" :to="{ path: '/storagepool/' + record.storageid }">{{ text }}</router-link> <span v-else>{{ text }}</span> </template> - - <template - v-for="(value, name) in thresholdMapping" - :key="name" - #[name]="{ text, record }" - href="javascript:;"> - <span> - <span v-if="record[value.disable]" class="alert-disable-threshold"> - {{ text }} - </span> - <span v-else-if="record[value.notification]" class="alert-notification-threshold"> - {{ text }} - </span> - <span style="padding: 10%;" v-else> - {{ text }} + <template v-for="(value, name) in thresholdMapping" :key="name"> + <template v-if="column.key === name"> + <span> + <span v-if="record[value.disable]" class="alert-disable-threshold"> + {{ text }} + </span> + <span v-else-if="record[value.notification]" class="alert-notification-threshold"> + {{ text }} + </span> + <span style="padding: 10%;" v-else> + {{ text }} + </span> </span> - </span> + </template> </template> - <template #level="{ text, record }"> - <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link> - </template> + <template v-if="column.key === 'level'"> + <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link> + </template> - <template #clustername="{ text, record }"> - <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link> - </template> - <template #podname="{ text, record }"> - <router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link> - </template> - <template #account="{ text, record }"> - <template v-if="record.owner"> - <template v-for="(item, idx) in record.owner" :key="idx"> - <span style="margin-right:5px"> - <span v-if="$store.getters.userInfo.roletype !== 'User'"> - <router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: record.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link> - <router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: record.domainid, dataView: true } }">{{ item.account }}</router-link> + <template v-if="column.key === 'clustername'"> + <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link> + </template> + <template v-if="column.key === 'podname'"> + <router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link> + </template> + <template v-if="column.key === 'account'"> + <template v-if="record.owner"> + <template v-for="(item, idx) in record.owner" :key="idx"> + <span style="margin-right:5px"> + <span v-if="$store.getters.userInfo.roletype !== 'User'"> + <router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: record.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link> + <router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: record.domainid, dataView: true } }">{{ item.account }}</router-link> + </span> + <span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span> </span> - <span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span> - </span> + </template> + </template> + <template v-if="text && !text.startsWith('PrjAcct-')"> + <router-link + v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`).matched[0].redirect !== '/exception/404'" + :to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }">{{ text }}</router-link> + <router-link :to="{ path: '/account/' + record.accountid }" v-else-if="record.accountid">{{ text }}</router-link> + <router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid, dataView: true } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link> + <span v-else>{{ text }}</span> </template> </template> - <template v-if="text && !text.startsWith('PrjAcct-')"> - <router-link - v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`).matched[0].redirect !== '/exception/404'" - :to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }">{{ text }}</router-link> - <router-link :to="{ path: '/account/' + record.accountid }" v-else-if="record.accountid">{{ text }}</router-link> - <router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid, dataView: true } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link> + <template v-if="column.key === 'resource'"> + <resource-label :resourceType="record.resourcetype" :resourceId="record.resourceid" :resourceName="record.resourcename" /> + </template> + <template v-if="column.key === 'domain'"> + <router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link> <span v-else>{{ text }}</span> </template> - </template> - <template #resource="{ record }"> - <resource-label :resourceType="record.resourcetype" :resourceId="record.resourceid" :resourceName="record.resourcename" /> - </template> - <template #domain="{ text, record }"> - <router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link> - <span v-else>{{ text }}</span> - </template> - <template #domainpath="{ text, record }"> - <router-link v-if="record.domainid && !record.domainid.includes(',') && $router.resolve('/domain/' + record.domainid).matched[0].redirect !== '/exception/404'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link> - <span v-else>{{ text }}</span> - </template> - <template #zone="{ text, record }"> - <router-link v-if="record.zoneid && !record.zoneid.includes(',') && $router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link> - <span v-else>{{ text }}</span> - </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 }"> - <router-link v-if="record.roleid && $router.resolve('/role/' + record.roleid).matched[0].redirect !== '/exception/404'" :to="{ path: '/role/' + record.roleid }">{{ text }}</router-link> - <span v-else>{{ text }}</span> - </template> - <template #templateversion="{ record }"> - <span> {{ record.version }} </span> - </template> - <template #softwareversion="{ record }"> - <span> {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span> - </template> - <template #access="{ record }"> - <status :text="record.readonly ? 'ReadOnly' : 'ReadWrite'" displayText /> - </template> - <template #requiresupgrade="{ record }"> - <status :text="record.requiresupgrade ? 'warning' : ''" /> - {{ record.requiresupgrade ? 'Yes' : 'No' }} - </template> - <template #loadbalancerrule="{ record }"> - <span> {{ record.loadbalancerrule }} </span> - </template> - <template #autoscalingenabled="{ record }"> - <status :text="record.autoscalingenabled ? 'Enabled' : 'Disabled'" /> - {{ record.autoscalingenabled ? 'Enabled' : 'Disabled' }} - </template> - <template #current="{record}"> - <status :text="record.current ? record.current.toString() : 'false'" /> - </template> - <template #created="{ text }"> - {{ $toLocaleDate(text) }} - </template> - <template #sent="{ text }"> - {{ $toLocaleDate(text) }} - </template> - <template #order="{ text, record }"> - <div class="shift-btns"> - <a-tooltip :name="text" placement="top"> - <template #title>{{ $t('label.move.to.top') }}</template> - <a-button - shape="round" - @click="moveItemTop(record)" - class="shift-btn"> - <DoubleLeftOutlined class="shift-btn shift-btn--rotated" /> - </a-button> - </a-tooltip> - <a-tooltip placement="top"> - <template #title>{{ $t('label.move.to.bottom') }}</template> - <a-button - shape="round" - @click="moveItemBottom(record)" - class="shift-btn"> - <DoubleRightOutlined class="shift-btn shift-btn--rotated" /> - </a-button> - </a-tooltip> - <a-tooltip placement="top"> - <template #title>{{ $t('label.move.up.row') }}</template> - <a-button shape="round" @click="moveItemUp(record)" class="shift-btn"> - <CaretUpOutlined class="shift-btn" /> - </a-button> - </a-tooltip> - <a-tooltip placement="top"> - <template #title>{{ $t('label.move.down.row') }}</template> - <a-button shape="round" @click="moveItemDown(record)" class="shift-btn"> - <CaretDownOutlined class="shift-btn" /> - </a-button> - </a-tooltip> - </div> - </template> + <template v-if="column.key === 'domainpath'"> + <router-link v-if="record.domainid && !record.domainid.includes(',') && $router.resolve('/domain/' + record.domainid).matched[0].redirect !== '/exception/404'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link> + <span v-else>{{ text }}</span> + </template> + <template v-if="column.key === 'zone'"> + <router-link v-if="record.zoneid && !record.zoneid.includes(',') && $router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link> + <span v-else>{{ text }}</span> + </template> + <template v-if="column.key === 'zonename'"> + <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 v-if="column.key === 'rolename'"> + <router-link v-if="record.roleid && $router.resolve('/role/' + record.roleid).matched[0].redirect !== '/exception/404'" :to="{ path: '/role/' + record.roleid }">{{ text }}</router-link> + <span v-else>{{ text }}</span> + </template> + <template v-if="column.key === 'templateversion'"> + <span> {{ record.version }} </span> + </template> + <template v-if="column.key === 'softwareversion'"> + <span> {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span> + </template> + <template v-if="column.key === 'access'"> + <status :text="record.readonly ? 'ReadOnly' : 'ReadWrite'" displayText /> + </template> + <template v-if="column.key === 'requiresupgrade'"> + <status :text="record.requiresupgrade ? 'warning' : ''" /> + {{ record.requiresupgrade ? 'Yes' : 'No' }} + </template> + <template v-if="column.key === 'loadbalancerrule'"> + <span> {{ record.loadbalancerrule }} </span> + </template> + <template v-if="column.key === 'autoscalingenabled'"> + <status :text="record.autoscalingenabled ? 'Enabled' : 'Disabled'" /> + {{ record.autoscalingenabled ? 'Enabled' : 'Disabled' }} + </template> + <template v-if="column.key === 'current'"> + <status :text="record.current ? record.current.toString() : 'false'" /> + </template> + <template v-if="column.key === 'enabled'"> + <status :text="record.enabled ? record.enabled.toString() : 'false'" /> + {{ record.enabled ? 'Enabled' : 'Disabled' }} + </template> + <template v-if="['created', 'sent'].includes(column.key)"> + {{ $toLocaleDate(text) }} + </template> + <template v-if="['startdate', 'enddate'].includes(column.key) && ['vm'].includes($route.path.split('/')[1])"> + {{ getDateAtTimeZone(text, record.timezone) }} + </template> + <template v-if="column.key === 'order'"> + <div class="shift-btns"> + <a-tooltip :name="text" placement="top"> + <template #title>{{ $t('label.move.to.top') }}</template> + <a-button + shape="round" + @click="moveItemTop(record)" + class="shift-btn"> + <DoubleLeftOutlined class="shift-btn shift-btn--rotated" /> + </a-button> + </a-tooltip> + <a-tooltip placement="top"> + <template #title>{{ $t('label.move.to.bottom') }}</template> + <a-button + shape="round" + @click="moveItemBottom(record)" + class="shift-btn"> + <DoubleRightOutlined class="shift-btn shift-btn--rotated" /> + </a-button> + </a-tooltip> + <a-tooltip placement="top"> + <template #title>{{ $t('label.move.up.row') }}</template> + <a-button shape="round" @click="moveItemUp(record)" class="shift-btn"> + <CaretUpOutlined class="shift-btn" /> + </a-button> + </a-tooltip> + <a-tooltip placement="top"> + <template #title>{{ $t('label.move.down.row') }}</template> + <a-button shape="round" @click="moveItemDown(record)" class="shift-btn"> + <CaretDownOutlined class="shift-btn" /> + </a-button> + </a-tooltip> + </div> + </template> - <template #value="{ text, record }"> - <a-input - v-if="editableValueKey === record.key" - v-focus="true" - :defaultValue="record.value" - :disabled="!('updateConfiguration' in $store.getters.apis)" - v-model:value="editableValue" - @keydown.esc="editableValueKey = null" - @pressEnter="saveValue(record)"> - </a-input> - <div v-else style="width: 200px; word-break: break-all"> - {{ text }} - </div> - </template> - <template #actions="{ record }"> - <tooltip-button - :tooltip="$t('label.edit')" - :disabled="!('updateConfiguration' in $store.getters.apis)" - v-if="editableValueKey !== record.key" - icon="edit-outlined" - @onClick="editValue(record)" /> - <tooltip-button - :tooltip="$t('label.cancel')" - @onClick="editableValueKey = null" - v-if="editableValueKey === record.key" - iconType="CloseCircleTwoTone" - iconTwoToneColor="#f5222d" /> - <tooltip-button - :tooltip="$t('label.ok')" - :disabled="!('updateConfiguration' in $store.getters.apis)" - @onClick="saveValue(record)" - v-if="editableValueKey === record.key" - iconType="CheckCircleTwoTone" - iconTwoToneColor="#52c41a" /> - <tooltip-button - :tooltip="$t('label.reset.config.value')" - @onClick="resetConfig(record)" - v-if="editableValueKey !== record.key" - icon="reload-outlined" - :disabled="!('updateConfiguration' in $store.getters.apis)" /> + <template v-if="column.key === 'value'"> + <a-input + v-if="editableValueKey === record.key" + v-focus="true" + :defaultValue="record.value" + :disabled="!('updateConfiguration' in $store.getters.apis)" + v-model:value="editableValue" + @keydown.esc="editableValueKey = null" + @pressEnter="saveValue(record)"> + </a-input> + <div v-else style="width: 200px; word-break: break-all"> + {{ text }} + </div> + </template> + <template v-if="column.key === 'actions'"> + <tooltip-button + :tooltip="$t('label.edit')" + :disabled="!('updateConfiguration' in $store.getters.apis)" + v-if="editableValueKey !== record.key" + icon="edit-outlined" + @onClick="editValue(record)" /> + <tooltip-button + :tooltip="$t('label.cancel')" + @onClick="editableValueKey = null" + v-if="editableValueKey === record.key" + iconType="CloseCircleTwoTone" + iconTwoToneColor="#f5222d" /> + <tooltip-button + :tooltip="$t('label.ok')" + :disabled="!('updateConfiguration' in $store.getters.apis)" + @onClick="saveValue(record)" + v-if="editableValueKey === record.key" + iconType="CheckCircleTwoTone" + iconTwoToneColor="#52c41a" /> + <tooltip-button + :tooltip="$t('label.reset.config.value')" + @onClick="resetConfig(record)" + v-if="editableValueKey !== record.key" + icon="reload-outlined" + :disabled="!('updateConfiguration' in $store.getters.apis)" /> + </template> + <template v-if="column.key === 'tariffActions'"> + <tooltip-button + :tooltip="$t('label.edit')" + v-if="editableValueKey !== record.key" + :disabled="!('quotaTariffUpdate' in $store.getters.apis)" + icon="edit-outlined" + @onClick="editTariffValue(record)" /> + <slot></slot> + </template> + <template v-if="column.key === 'vmScheduleActions'"> + <tooltip-button + :tooltip="$t('label.edit')" + :disabled="!('updateVMSchedule' in $store.getters.apis)" + icon="edit-outlined" + @onClick="updateVMSchedule(record)" /> + <tooltip-button + :tooltip="$t('label.remove')" + :disabled="!('deleteVMSchedule' in $store.getters.apis)" + icon="delete-outlined" + :danger="true" + type="primary" + @onClick="removeVMSchedule(record)" /> + </template> </template> - <template #tariffActions="{ record }"> - <tooltip-button - :tooltip="$t('label.edit')" - v-if="editableValueKey !== record.key" - :disabled="!('quotaTariffUpdate' in $store.getters.apis)" - icon="edit-outlined" - @onClick="editTariffValue(record)" /> - <slot></slot> + <template #footer> + <span v-if="hasSelected"> + {{ `Selected ${selectedRowKeys.length} items` }} + </span> </template> </a-table> </template> diff --cc ui/src/config/section/compute.js index 6d2c55b4111,e9a16ff6322..99634e330c2 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@@ -455,7 -454,7 +454,7 @@@ export default docHelp: 'plugins/cloudstack-kubernetes-service.html', permission: ['listKubernetesClusters'], columns: (store) => { - var fields = ['name', 'state', 'clustertype', 'size', 'cpunumber', 'memory'] - var fields = ['name', 'state', 'size', 'cpunumber', 'memory', 'kubernetesversionname'] ++ var fields = ['name', 'state', 'clustertype', 'size', 'cpunumber', 'memory', 'kubernetesversionname'] if (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) { fields.push('account') } diff --cc ui/src/config/section/network.js index b5ebc0f8694,3105798b0a4..153e6c5df5f --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@@ -323,8 -319,8 +323,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', 'vpcname', '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'], ++ columns: ['ipaddress', 'state', 'associatednetworkname', 'vpcname', '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: [{ diff --cc ui/src/views/compute/CreateAutoScaleVmGroup.vue index 3797077ba6d,b764f8185e2..084828ef9c5 --- a/ui/src/views/compute/CreateAutoScaleVmGroup.vue +++ b/ui/src/views/compute/CreateAutoScaleVmGroup.vue @@@ -34,8 -34,8 +34,8 @@@ <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 <= 9"> + <div v-if="zones.length <= 8"> - <a-row type="flex" :gutter="5" justify="start"> + <a-row type="flex" :gutter="[16,18]" justify="start"> <div v-for="(zoneItem, idx) in zones" :key="idx"> <a-radio-group :key="idx" @@@ -2942,6 -2939,15 +2945,15 @@@ export default margin: 0 0 1.2rem; } + .zone-radio-button { + width:100%; - min-width: 225px; ++ min-width: 345px; + height: 60px; + display: flex; + padding-left: 20px; + align-items: center; + } + .vm-info-card { .ant-card-body { min-height: 250px; diff --cc ui/src/views/compute/DeployVM.vue index 507b902e11f,bb94c87db74..fc45f8cf2f8 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@@ -34,8 -34,8 +34,8 @@@ <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 <= 9"> + <div v-if="zones.length <= 8"> - <a-row type="flex" :gutter="5" justify="start"> + <a-row type="flex" :gutter="[16, 18]" justify="start"> <div v-for="(zoneItem, idx) in zones" :key="idx"> <a-radio-group :key="idx" @@@ -2721,6 -2696,15 +2724,15 @@@ export default margin: 0 0 1.2rem; } + .zone-radio-button { + width:100%; - min-width: 225px; ++ min-width: 345px; + height: 60px; + display: flex; + padding-left: 20px; + align-items: center; + } + .vm-info-card { .ant-card-body { min-height: 250px; diff --cc ui/src/views/compute/InstanceTab.vue index 927fa3db7b4,4ab7fb6f2d5..a69d044ea8e --- a/ui/src/views/compute/InstanceTab.vue +++ b/ui/src/views/compute/InstanceTab.vue @@@ -294,8 -307,8 +312,9 @@@ import DetailsTab from '@/components/vi 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 InstanceSchedules from '@/views/compute/InstanceSchedules.vue' import ListResourceTable from '@/components/view/ListResourceTable' import TooltipButton from '@/components/widgets/TooltipButton' import ResourceIcon from '@/components/view/ResourceIcon' @@@ -310,8 -323,8 +329,9 @@@ export default StatsTab, EventsTab, DetailSettings, + CreateVolume, NicsTable, + InstanceSchedules, ListResourceTable, TooltipButton, ResourceIcon, diff --cc ui/src/views/image/TemplateZones.vue index 8d7a3e24b24,a2f8834be6e..2e46a239b77 --- a/ui/src/views/image/TemplateZones.vue +++ b/ui/src/views/image/TemplateZones.vue @@@ -35,35 -35,16 +35,35 @@@ :pagination="false" :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" :rowKey="record => record.zoneid"> - <template #zonename="{record}"> - <span v-if="fetchZoneIcon(record.zoneid)"> - <resource-icon :image="zoneIcon" size="2x" style="margin-right: 5px"/> - </span> - <global-outlined v-else style="margin-right: 5px" /> - <span> {{ record.zonename }} </span> - </template> - <template #isready="{ record }"> - <span v-if="record.isready">{{ $t('label.yes') }}</span> - <span v-else>{{ $t('label.no') }}</span> + <template #bodyCell="{ column, record }"> + <template v-if="column.key === 'zonename'"> + <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> + </template> + <template v-if="column.key === 'isready'"> + <span v-if="record.isready">{{ $t('label.yes') }}</span> + <span v-else>{{ $t('label.no') }}</span> + </template> + <template v-if="column.key === 'actions'"> + <tooltip-button + style="margin-right: 5px" + :disabled="!('copyTemplate' in $store.getters.apis && record.isready)" + :title="$t('label.action.copy.template')" + icon="copy-outlined" + :loading="copyLoading" + @onClick="showCopyTemplate(record)" /> + <tooltip-button + style="margin-right: 5px" + :disabled="!('deleteTemplate' in $store.getters.apis)" + :title="$t('label.action.delete.template')" + type="primary" + :danger="true" + icon="delete-outlined" + @onClick="onShowDeleteModal(record)"/> + </template> </template> <template #expandedRowRender="{ record }"> <a-table @@@ -126,7 -124,7 +126,7 @@@ <a-select-option v-for="zone in zones" :key="zone.id" :label="zone.name"> <div> <span v-if="zone.icon && zone.icon.base64image"> -- <resource-icon :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/> ++ <resource-icon :image="zone.icon.base64image" size="2x" style="margin-right: 5px"/> </span> <global-outlined v-else style="margin-right: 5px" /> {{ zone.name }}