mirror of https://github.com/apache/cloudstack.git
Add service offering category selection and management functionality
This commit is contained in:
parent
e1e902db45
commit
6519904c53
|
|
@ -511,6 +511,7 @@
|
|||
"label.capacitybytes": "Capacity bytes",
|
||||
"label.capacityiops": "IOPS total",
|
||||
"label.category": "Category",
|
||||
"label.categoryname": "Category name",
|
||||
"label.certchain": "Chain",
|
||||
"label.certificate": "Certificate",
|
||||
"label.certificate.chain": "Certificate chain",
|
||||
|
|
@ -1165,6 +1166,10 @@
|
|||
"label.guest.os.category": "Guest OS Category",
|
||||
"label.guest.os.categories": "Guest OS Categories",
|
||||
"label.guest.os.hypervisor.mappings": "Guest OS mappings",
|
||||
"label.service.offering.categories": "Service Offering Categories",
|
||||
"label.add.service.offering.category": "Add Service Offering Category",
|
||||
"label.action.delete.service.offering.category": "Delete Service Offering Category",
|
||||
"message.action.delete.service.offering.category": "Please confirm that you want to delete this service offering category",
|
||||
"label.guest.start.ip": "Guest start IP",
|
||||
"label.guest.traffic": "Guest traffic",
|
||||
"label.guestcidraddress": "Guest CIDR",
|
||||
|
|
|
|||
|
|
@ -1027,6 +1027,9 @@
|
|||
<template v-if="column.key === 'vgpuActions'">
|
||||
<slot name="actionButtons" :record="record" :actions="actions"></slot>
|
||||
</template>
|
||||
<template v-if="column.key === 'category' && $route.path.split('/')[1] === 'computeoffering'">
|
||||
<router-link :to="{ path: '/serviceofferingcategory/' + record.categoryid }">{{ text }}</router-link>
|
||||
</template>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span v-if="hasSelected">
|
||||
|
|
@ -1309,6 +1312,9 @@ export default {
|
|||
case 'guestoscategory':
|
||||
apiCommand = 'updateOsCategory'
|
||||
break
|
||||
case 'serviceofferingcategory':
|
||||
apiCommand = 'updateServiceOfferingCategory'
|
||||
break
|
||||
}
|
||||
return apiCommand
|
||||
},
|
||||
|
|
@ -1322,7 +1328,7 @@ export default {
|
|||
return new Promise((resolve, reject) => {
|
||||
postAPI(apiCommand, {
|
||||
id,
|
||||
sortKey: index
|
||||
sortkey: index
|
||||
}).then((response) => {
|
||||
resolve(response)
|
||||
}).catch((reason) => {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,40 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'serviceofferingcategory',
|
||||
title: 'label.service.offering.categories',
|
||||
icon: 'shopping-outlined',
|
||||
permission: ['listServiceOfferingCategories'],
|
||||
columns: ['name', 'order'],
|
||||
details: ['name', 'id'],
|
||||
actions: [
|
||||
{
|
||||
api: 'createServiceOfferingCategory',
|
||||
icon: 'plus-outlined',
|
||||
label: 'label.add.service.offering.category',
|
||||
listView: true,
|
||||
dataView: false,
|
||||
args: ['name']
|
||||
},
|
||||
{
|
||||
api: 'updateServiceOfferingCategory',
|
||||
icon: 'edit-outlined',
|
||||
label: 'label.edit',
|
||||
dataView: true,
|
||||
popup: true,
|
||||
args: ['name']
|
||||
},
|
||||
{
|
||||
api: 'deleteServiceOfferingCategory',
|
||||
icon: 'delete-outlined',
|
||||
label: 'label.action.delete.service.offering.category',
|
||||
message: 'message.action.delete.service.offering.category',
|
||||
dataView: true,
|
||||
popup: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'guestos',
|
||||
title: 'label.guest.os',
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export default {
|
|||
docHelp: 'adminguide/service_offerings.html#compute-and-disk-service-offerings',
|
||||
icon: 'cloud-outlined',
|
||||
permission: ['listServiceOfferings'],
|
||||
searchFilters: ['name', 'gpuenabled', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory'],
|
||||
searchFilters: ['name', 'gpuenabled', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory', 'category'],
|
||||
params: () => {
|
||||
var params = {}
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
|
|
@ -39,9 +39,9 @@ export default {
|
|||
return params
|
||||
},
|
||||
filters: ['active', 'inactive'],
|
||||
columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'gpu', 'domain', 'zone', 'order'],
|
||||
columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'gpu', 'category', 'domain', 'zone', 'order'],
|
||||
details: () => {
|
||||
var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storageaccessgroups', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot', 'purgeresources', 'leaseduration', 'gpucardid', 'gpucardname', 'vgpuprofileid', 'vgpuprofilename', 'gpucount', 'gpudisplay', 'leaseexpiryaction', 'externaldetails']
|
||||
var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storageaccessgroups', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot', 'purgeresources', 'leaseduration', 'gpucardid', 'gpucardname', 'vgpuprofileid', 'vgpuprofilename', 'gpucount', 'gpudisplay', 'leaseexpiryaction', 'externaldetails', 'categoryid', 'category']
|
||||
if (store.getters.apis.createServiceOffering &&
|
||||
store.getters.apis.createServiceOffering.params.filter(x => x.name === 'storagepolicy').length > 0) {
|
||||
fields.splice(6, 0, 'vspherestoragepolicy')
|
||||
|
|
@ -96,8 +96,11 @@ export default {
|
|||
label: 'label.edit',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true,
|
||||
args: ['name', 'displaytext', 'storagetags', 'hosttags', 'externaldetails'],
|
||||
args: ['name', 'displaytext', 'categoryid', 'storagetags', 'hosttags', 'externaldetails'],
|
||||
mapping: {
|
||||
categoryid: {
|
||||
api: 'listServiceOfferingCategories'
|
||||
},
|
||||
externaldetails: {
|
||||
transformedvalue: (record) => { return getFilteredExternalDetails(record.serviceofferingdetails) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,8 +261,11 @@
|
|||
:minimum-cpunumber="templateConfigurationExists && selectedTemplateConfiguration && selectedTemplateConfiguration.cpunumber ? selectedTemplateConfiguration.cpunumber : 0"
|
||||
:minimum-cpuspeed="templateConfigurationExists && selectedTemplateConfiguration && selectedTemplateConfiguration.cpuspeed ? selectedTemplateConfiguration.cpuspeed : 0"
|
||||
:minimum-memory="templateConfigurationExists && selectedTemplateConfiguration && selectedTemplateConfiguration.memory ? selectedTemplateConfiguration.memory : 0"
|
||||
:service-offering-categories="options.serviceOfferingCategories"
|
||||
:selected-service-offering-category-id="selectedServiceOfferingCategoryId"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('serviceOfferings', $event)"
|
||||
@service-offering-category-change="($event) => handleServiceOfferingCategoryChange($event)"
|
||||
></compute-offering-selection>
|
||||
<compute-selection
|
||||
v-if="serviceOffering && (serviceOffering.iscustomized || serviceOffering.iscustomizediops)"
|
||||
|
|
@ -2271,6 +2274,46 @@ export default {
|
|||
console.error('Error fetching guestOsCategories:', e)
|
||||
})
|
||||
},
|
||||
fetchServiceOfferingCategories () {
|
||||
this.loading.serviceOfferingCategories = true
|
||||
return new Promise((resolve, reject) => {
|
||||
getAPI('listServiceOfferingCategories').then(json => {
|
||||
const categories = json.listserviceofferingcategoriesresponse.serviceofferingcategory || []
|
||||
this.options.serviceOfferingCategories = [
|
||||
{
|
||||
id: '-1',
|
||||
name: this.$t('label.all')
|
||||
},
|
||||
...categories
|
||||
]
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
console.error('Error fetching service offering categories:', error)
|
||||
this.options.serviceOfferingCategories = [
|
||||
{
|
||||
id: '-1',
|
||||
name: this.$t('label.all')
|
||||
}
|
||||
]
|
||||
reject(error)
|
||||
}).finally(() => {
|
||||
this.loading.serviceOfferingCategories = false
|
||||
})
|
||||
})
|
||||
},
|
||||
handleServiceOfferingCategoryChange (categoryId) {
|
||||
this.selectedServiceOfferingCategoryId = categoryId
|
||||
const params = {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
if (categoryId && categoryId !== '-1') {
|
||||
params.serviceofferingcategoryid = categoryId
|
||||
} else {
|
||||
params.serviceofferingcategoryid = null
|
||||
}
|
||||
this.handleSearchFilter('serviceOfferings', params)
|
||||
},
|
||||
changeArchitecture (arch) {
|
||||
this.selectedArchitecture = arch
|
||||
this.updateImages()
|
||||
|
|
@ -3027,6 +3070,8 @@ export default {
|
|||
if (this.isModernImageSelection && guestOsFetch) {
|
||||
await guestOsFetch
|
||||
}
|
||||
// Load service offering categories
|
||||
this.fetchServiceOfferingCategories()
|
||||
this.fetchImages()
|
||||
this.updateTemplateKey()
|
||||
this.formModel = toRaw(this.form)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,25 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<a-form-item
|
||||
:label="$t('label.category')"
|
||||
name="serviceofferingcategoryid"
|
||||
ref="serviceofferingcategoryid"
|
||||
v-if="serviceOfferingCategories && serviceOfferingCategories.length > 0"
|
||||
style="margin-bottom: 16px;">
|
||||
<block-radio-group-select
|
||||
:maxBlocks="16"
|
||||
:items="serviceOfferingCategories"
|
||||
:selectedValue="selectedServiceOfferingCategoryId"
|
||||
:horizontalGutter="6"
|
||||
:verticalGutter="6"
|
||||
blockSize="medium"
|
||||
@change="handleServiceOfferingCategoryChange">
|
||||
<template #radio-option="{ item }">
|
||||
{{ item.name }}
|
||||
</template>
|
||||
</block-radio-group-select>
|
||||
</a-form-item>
|
||||
<div style="margin-bottom: 16px; display: flex; justify-content: right; align-items: center;">
|
||||
<div v-if="showGpuFilter" style="display: flex; align-items: center; margin-right: 16px;">
|
||||
<span>{{ $t('label.show.only.gpu.enabled.offerings') }}</span>
|
||||
|
|
@ -94,8 +113,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import BlockRadioGroupSelect from '@/components/widgets/BlockRadioGroupSelect.vue'
|
||||
|
||||
export default {
|
||||
name: 'ComputeOfferingSelection',
|
||||
components: {
|
||||
BlockRadioGroupSelect
|
||||
},
|
||||
props: {
|
||||
computeItems: {
|
||||
type: Array,
|
||||
|
|
@ -150,6 +174,14 @@ export default {
|
|||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
serviceOfferingCategories: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedServiceOfferingCategoryId: {
|
||||
type: String,
|
||||
default: '-1'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
|
@ -314,6 +346,9 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
handleServiceOfferingCategoryChange (categoryId) {
|
||||
this.$emit('service-offering-category-change', categoryId)
|
||||
},
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-compute-item', value[0])
|
||||
|
|
@ -381,4 +416,26 @@ export default {
|
|||
:deep(.ant-table-tbody) > tr > td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.radio-opion__icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,25 @@
|
|||
v-model:value="form.displaytext"
|
||||
:placeholder="$t('label.displaytext')"/>
|
||||
</a-form-item>
|
||||
<a-form-item name="categoryid" ref="categoryid" v-if="!isSystem">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.category')" :tooltip="apiParams.categoryid ? apiParams.categoryid.description : $t('label.category')"/>
|
||||
</template>
|
||||
<a-select
|
||||
:getPopupContainer="(trigger) => trigger.parentNode"
|
||||
v-model:value="form.categoryid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="categoryLoading"
|
||||
:placeholder="$t('label.category')">
|
||||
<a-select-option v-for="category in categories" :key="category.id" :label="category.name">
|
||||
{{ category.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="systemvmtype" ref="systemvmtype" v-if="isSystem">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.systemvmtype')" :tooltip="apiParams.systemvmtype.description"/>
|
||||
|
|
@ -745,7 +764,9 @@ export default {
|
|||
leaseexpiryaction: undefined,
|
||||
vgpuProfiles: [],
|
||||
vgpuProfileLoading: false,
|
||||
externalDetailsEnabled: false
|
||||
externalDetailsEnabled: false,
|
||||
categories: [],
|
||||
categoryLoading: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -857,6 +878,7 @@ export default {
|
|||
this.fetchDomainData()
|
||||
this.fetchZoneData()
|
||||
this.fetchGPUCards()
|
||||
this.fetchCategories()
|
||||
if (isAdmin()) {
|
||||
this.fetchStorageTagData()
|
||||
this.fetchDeploymentPlannerData()
|
||||
|
|
@ -882,6 +904,15 @@ export default {
|
|||
this.gpuCardLoading = false
|
||||
})
|
||||
},
|
||||
fetchCategories () {
|
||||
this.categoryLoading = true
|
||||
getAPI('listServiceOfferingCategories', {
|
||||
}).then(json => {
|
||||
this.categories = json.listserviceofferingcategoriesresponse.serviceofferingcategory || []
|
||||
}).finally(() => {
|
||||
this.categoryLoading = false
|
||||
})
|
||||
},
|
||||
addDiskOffering () {
|
||||
this.showDiskOfferingModal = true
|
||||
},
|
||||
|
|
@ -1089,6 +1120,10 @@ export default {
|
|||
params.diskofferingid = values.diskofferingid
|
||||
}
|
||||
|
||||
if (values.categoryid) {
|
||||
params.categoryid = values.categoryid
|
||||
}
|
||||
|
||||
// Add GPU parameters
|
||||
if (values.vgpuprofile) {
|
||||
params.vgpuprofileid = values.vgpuprofile
|
||||
|
|
|
|||
Loading…
Reference in New Issue