| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| <template> |
| <div> |
| <a-form-item v-if="imageTypeSelectionAllowed" :label="$t('label.type')" name="imagetype" ref="imagetype"> |
| <a-radio-group |
| v-model:value="localSelectedImageType" |
| button-style="solid" |
| :disabled="imagePreSelected" |
| @change="emitChangeImageType()"> |
| <a-radio-button value="templateid">{{ $t('label.template') }}</a-radio-button> |
| <a-radio-button value="isoid">{{ $t('label.iso') }}</a-radio-button> |
| <a-radio-button value="volumeid">{{ $t('label.volume') }}</a-radio-button> |
| <a-radio-button value="snapshotid">{{ $t('label.snapshot') }}</a-radio-button> |
| </a-radio-group> |
| <div style="margin-top: 5px; margin-bottom: 5px;"> |
| {{ $t('message.' + localSelectedImageType.replace('id', '') + '.desc') }} |
| </div> |
| </a-form-item> |
| <a-form-item |
| :label="$t('label.os')" |
| name="guestoscategoryid" |
| ref="guestoscategoryid" |
| v-if="!guestOsCategoriesSelectionDisallowed"> |
| <block-radio-group-select |
| :maxBlocks="16" |
| :items="guestOsCategories" |
| :selectedValue="localSelectedGuestOsCategoryId" |
| :horizontalGutter="6" |
| :verticalGutter="6" |
| blockSize="square" |
| @change="handleGuestOsCategoryChange"> |
| <template #radio-option="{ item }"> |
| <div class="radio-option"> |
| <div class="radio-option__icon"> |
| <resource-icon v-if="item.icon && item.icon.base64image" :image="item.icon.base64image" size="os" style="margin-bottom: 2px; margin-left: 1px" /> |
| <font-awesome-icon v-else-if="['-1', '0'].includes(item.id)" :icon="['fas', item.id === '0' ? 'user' : 'images']" size="2x" :style="categoryFontAwesomeIconStyle" /> |
| <os-logo v-else size="2x" :os-name="item.name" /> |
| </div> |
| <a-tooltip placement="top" :title="item.name"> |
| <div class="ellipsis">{{ item.name }}</div> |
| </a-tooltip> |
| </div> |
| </template> |
| <template #select-option="{ item }"> |
| <span> |
| <resource-icon v-if="item.icon && item.icon.base64image" :image="item.icon.base64image" size="2x" style="margin-right: 5px"/> |
| <font-awesome-icon |
| v-else-if="item.id === '0'" |
| :icon="['fas', 'user']" |
| size="2x" |
| :style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#666' }]" |
| /> |
| <os-logo v-else :os-name="item.name" style="margin-right: 5px" /> |
| {{ item.name }} |
| </span> |
| </template> |
| </block-radio-group-select> |
| </a-form-item> |
| <a-card> |
| <os-based-image-selection-search-view |
| v-if="!imagePreSelected" |
| class="search-input" |
| :filtersDisabled="searchFiltersDisabled" |
| @search="handleImageSearch"> |
| </os-based-image-selection-search-view> |
| <a-spin :spinning="imagesLoading"> |
| <os-based-image-radio-group |
| :imagesList="imagesList" |
| :itemCount="imagesCount" |
| :input-decorator="localSelectedImageType" |
| :selected="selectedImageId" |
| :preFillContent="preFillContent" |
| @emit-update-image="updateImage" |
| @handle-search-filter="($event) => eventPagination($event)" |
| /> |
| </a-spin> |
| <div v-if="diskSizeSelectionAllowed"> |
| <div> |
| {{ $t('label.override.rootdisk.size') }} |
| <a-switch |
| v-model:checked="localRootDiskOverrideChecked" |
| :disabled="rootDiskOverrideDisabled" |
| @change="handleRootDiskOverrideCheckedChange" |
| style="margin-left: 10px;"/> |
| <div v-if="diskSizeSelectionDeployAsIsMessageVisible"> {{ $t('message.deployasis') }} </div> |
| </div> |
| <disk-size-selection |
| v-if="showRootDiskSizeChanger" |
| :input-decorator="diskSizeSelectionInputDecorator" |
| :preFillContent="preFillContent" |
| :isCustomized="true" |
| :minDiskSize="preFillContent.minrootdisksize" |
| @update-disk-size="emitUpdateDiskSize" |
| style="margin-top: 10px;"/> |
| </div> |
| <a-form-item :label="$t('label.hypervisor')" v-if="localSelectedImageType === 'isoid'"> |
| <a-select |
| v-model:value="localSelectedIsoHypervisor" |
| :preFillContent="preFillContent" |
| :options="isoHypervisorItems" |
| @change="handleIsoHypervisorChange" |
| showSearch |
| optionFilterProp="label" |
| :filterOption="filterOption" /> |
| </a-form-item> |
| </a-card> |
| </div> |
| </template> |
| |
| <script> |
| import BlockRadioGroupSelect from '@/components/widgets/BlockRadioGroupSelect.vue' |
| import ResourceIcon from '@/components/view/ResourceIcon' |
| import OsLogo from '@/components/widgets/OsLogo' |
| import OsBasedImageSelectionSearchView from '@views/compute/wizard/OsBasedImageSelectionSearchView' |
| import OsBasedImageRadioGroup from '@views/compute/wizard/OsBasedImageRadioGroup' |
| import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection' |
| |
| export default { |
| name: 'OsBasedImageSelection', |
| components: { |
| BlockRadioGroupSelect, |
| ResourceIcon, |
| OsLogo, |
| OsBasedImageSelectionSearchView, |
| OsBasedImageRadioGroup, |
| DiskSizeSelection |
| }, |
| props: { |
| imageTypeSelectionAllowed: { |
| type: Boolean, |
| default: true |
| }, |
| selectedImageType: { |
| type: String, |
| default: 'templateid' |
| }, |
| imagePreSelected: { |
| type: Boolean, |
| default: false |
| }, |
| guestOsCategoriesSelectionDisallowed: { |
| type: Boolean, |
| default: false |
| }, |
| guestOsCategories: { |
| type: Array, |
| default: () => [] |
| }, |
| guestOsCategoriesLoading: { |
| type: Boolean, |
| default: false |
| }, |
| selectedGuestOsCategoryId: { |
| type: String, |
| default: undefined |
| }, |
| imageItems: { |
| type: Object, |
| default: () => {} |
| }, |
| imagesLoading: { |
| type: Boolean, |
| default: false |
| }, |
| diskSizeSelectionAllowed: { |
| type: Boolean, |
| default: true |
| }, |
| diskSizeSelectionDeployAsIsMessageVisible: { |
| type: Boolean, |
| default: false |
| }, |
| diskSizeSelectionInputDecorator: { |
| type: String, |
| default: 'rootdisksize' |
| }, |
| rootDiskOverrideDisabled: { |
| type: Boolean, |
| default: false |
| }, |
| rootDiskOverrideChecked: { |
| type: Boolean, |
| default: false |
| }, |
| isoHypervisorItems: { |
| type: Array, |
| default: () => [] |
| }, |
| selectedIsoHypervisor: { |
| type: String, |
| default: undefined |
| }, |
| filterOption: { |
| type: Function, |
| required: true |
| }, |
| preFillContent: { |
| type: Object, |
| default: () => {} |
| } |
| }, |
| data () { |
| return { |
| filterType: 'executable', |
| selectedImageId: '', |
| imageSearchFilters: {}, |
| showRootDiskSizeChanger: false, |
| // Local data properties to mirror props |
| localSelectedImageType: this.selectedImageType, |
| localSelectedGuestOsCategoryId: this.selectedGuestOsCategoryId, |
| localRootDiskOverrideChecked: this.rootDiskOverrideChecked, |
| localSelectedIsoHypervisor: this.selectedIsoHypervisor |
| } |
| }, |
| mounted () { |
| this.filterType = this.defaultImageFilter |
| }, |
| watch: { |
| selectedImageType (newValue) { |
| this.localSelectedImageType = newValue |
| }, |
| guestOsCategories (newValue) { |
| this.imageSearchFilters = {} |
| }, |
| selectedGuestOsCategoryId (newValue) { |
| this.localSelectedGuestOsCategoryId = newValue |
| this.updateImageFilterType() |
| }, |
| rootDiskOverrideChecked (newValue) { |
| this.localRootDiskOverrideChecked = newValue |
| }, |
| selectedIsoHypervisor (newValue) { |
| this.localSelectedIsoHypervisor = newValue |
| } |
| }, |
| computed: { |
| defaultImageFilter () { |
| return ['DomainAdmin', 'User'].includes(this.$store.getters.userInfo.roletype) ? 'executable' : 'all' |
| }, |
| imagesList () { |
| if (!this.localSelectedImageType || !this.imageItems || !this.imageItems[this.filterType]) { |
| return [] |
| } |
| const imageTypeKey = this.localSelectedImageType.slice(0, -2) |
| return this.imageItems[this.filterType][imageTypeKey] || [] |
| }, |
| imagesCount () { |
| return this.imageItems[this.filterType] ? this.imageItems[this.filterType].count || 0 : 0 |
| }, |
| selectedCategory () { |
| if (this.localSelectedGuestOsCategoryId && this.guestOsCategories) { |
| return this.guestOsCategories.find(option => option.id === this.localSelectedGuestOsCategoryId) |
| } |
| return null |
| }, |
| categoryFontAwesomeIconStyle () { |
| return [this.$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#666' }] |
| }, |
| searchFiltersDisabled () { |
| return this.selectedCategory?.disableimagefilters |
| } |
| }, |
| emits: ['change-image-type', 'change-guest-os-category', 'update-image', 'handle-image-search-filter', 'change-root-disk-override-checked', 'update-disk-size', 'change-iso-hypervisor'], |
| methods: { |
| emitChangeImageType () { |
| this.$emit('change-image-type', this.localSelectedImageType) |
| }, |
| handleGuestOsCategoryChange (value) { |
| this.localSelectedGuestOsCategoryId = value |
| this.$emit('change-guest-os-category', this.localSelectedGuestOsCategoryId) |
| }, |
| updateImage (decorator, id) { |
| this.selectedImageId = id |
| this.$emit('update-image', decorator, id) |
| }, |
| handleImageSearch (searchFilters) { |
| this.imageSearchFilters = { |
| page: 1, |
| pageSize: 10 |
| } |
| Object.assign(this.imageSearchFilters, searchFilters) |
| this.updateImageFilterType() |
| this.emitSearchFilter() |
| }, |
| updateImageFilterType () { |
| this.filterType = this.defaultImageFilter |
| if (this.localSelectedGuestOsCategoryId === '0') { |
| this.filterType = 'self' |
| } else { |
| if (this.imageSearchFilters?.featured) { |
| this.filterType = 'featured' |
| } else if (this.imageSearchFilters?.public) { |
| this.filterType = 'community' |
| } |
| } |
| }, |
| eventPagination (pagination) { |
| Object.assign(this.imageSearchFilters, pagination) |
| this.emitSearchFilter() |
| }, |
| emitSearchFilter () { |
| this.$emit('handle-image-search-filter', this.imageSearchFilters) |
| }, |
| changeFilterType (value) { |
| this.filterType = value |
| }, |
| handleRootDiskOverrideCheckedChange (value) { |
| this.showRootDiskSizeChanger = value |
| this.$emit('change-root-disk-override-checked', value) |
| }, |
| emitUpdateDiskSize (decorator, value) { |
| this.$emit('update-disk-size', decorator, value) |
| }, |
| handleIsoHypervisorChange (hypervisor) { |
| this.$emit('change-iso-hypervisor', hypervisor) |
| } |
| } |
| } |
| </script> |
| |
| <style lang="less" scoped> |
| .search-input { |
| z-index: 8; |
| |
| @media (max-width: 600px) { |
| position: relative; |
| width: 100%; |
| top: 0; |
| right: 0; |
| } |
| } |
| |
| :deep(.ant-tabs-nav-scroll) { |
| min-height: 45px; |
| } |
| |
| .radio-option { |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| text-align: center; |
| padding-top: 8px; |
| } |
| |
| .ellipsis { |
| max-width: 80px; |
| flex-grow: 1; |
| white-space: nowrap; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| } |
| |
| .radio-option__icon { |
| width: 30px; |
| height: 30px; |
| object-fit: contain; |
| } |
| </style> |