This commit is contained in:
Vishesh 2026-01-22 15:09:18 +01:00 committed by GitHub
commit e544fcac8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 559 additions and 729 deletions

View File

@ -18,52 +18,44 @@
<template>
<div class="form">
<div class="form__item" :class="{'error': domainError}">
<a-spin :spinning="domainsLoading">
<p class="form__label">{{ $t('label.domain') }}<span class="required">*</span></p>
<p class="required required-label">{{ $t('label.required') }}</p>
<a-select
style="width: 100%"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
@change="handleChangeDomain"
v-focus="true"
v-model:value="domainId">
<a-select-option
v-for="(domain, index) in domainsList"
:value="domain.id"
:key="index"
:label="domain.path || domain.name || domain.description">
{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</a-spin>
</div>
<div class="form__item" v-if="accountsList">
<p class="form__label">{{ $t('label.account') }}</p>
<a-select
<p class="form__label">{{ $t('label.domain') }}<span class="required">*</span></p>
<p class="required required-label">{{ $t('label.required') }}</p>
<infinite-scroll-select
style="width: 100%"
@change="handleChangeAccount"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="(account, index) in accountsList" :value="account.name" :key="index">
{{ account.name }}
</a-select-option>
</a-select>
v-model:value="domainId"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
v-focus="true"
@change-option-value="handleChangeDomain" />
</div>
<div class="form__item">
<p class="form__label">{{ $t('label.account') }}</p>
<infinite-scroll-select
style="width: 100%"
v-model:value="selectedAccount"
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
@change-option-value="handleChangeAccount" />
</div>
</div>
</template>
<script>
import { api } from '@/api'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'DedicateDomain',
components: {
InfiniteScrollSelect
},
props: {
error: {
type: Boolean,
@ -72,59 +64,48 @@ export default {
},
data () {
return {
domainsLoading: false,
domainId: null,
accountsList: null,
domainsList: null,
selectedAccount: null,
domainError: false
}
},
computed: {
domainsApiParams () {
return {
listall: true,
details: 'min'
}
},
accountsApiParams () {
if (!this.domainId) {
return {
listall: true,
showicon: true
}
}
return {
showicon: true,
domainid: this.domainId
}
}
},
watch: {
error () {
this.domainError = this.error
}
},
created () {
this.fetchData()
},
methods: {
fetchData () {
this.domainsLoading = true
api('listDomains', {
listAll: true,
details: 'min'
}).then(response => {
this.domainsList = response.listdomainsresponse.domain
if (this.domainsList[0]) {
this.domainId = this.domainsList[0].id
this.handleChangeDomain(this.domainId)
}
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.domainsLoading = false
})
},
fetchAccounts () {
api('listAccounts', {
domainid: this.domainId
}).then(response => {
this.accountsList = response.listaccountsresponse.account || []
if (this.accountsList && this.accountsList.length === 0) {
this.handleChangeAccount(null)
}
}).catch(error => {
this.$notifyError(error)
})
},
handleChangeDomain (e) {
this.$emit('domainChange', e)
handleChangeDomain (domainId) {
this.domainId = domainId
this.selectedAccount = null
this.$emit('domainChange', domainId)
this.domainError = false
this.fetchAccounts()
},
handleChangeAccount (e) {
this.$emit('accountChange', e)
handleChangeAccount (accountName) {
this.selectedAccount = accountName
this.$emit('accountChange', accountName)
}
}
}

View File

@ -41,8 +41,10 @@
- optionValueKey (String, optional): Property to use as the value for options (e.g., 'name'). Default is 'id'
- optionLabelKey (String, optional): Property to use as the label for options (e.g., 'name'). Default is 'name'
- defaultOption (Object, optional): Preselected object to include initially
- allowClear (Boolean, optional): Whether to allow clearing the selection. Default is false
- showIcon (Boolean, optional): Whether to show icon for the options. Default is true
- defaultIcon (String, optional): Icon to be shown when there is no resource icon for the option. Default is 'cloud-outlined'
- selectFirstOption (Boolean, optional): Whether to automatically select the first option when options are loaded. Default is false
Events:
- @change-option-value (Function): Emits the selected option value(s) when value(s) changes. Do not use @change as it will give warnings and may not work
@ -58,6 +60,7 @@
:filter-option="false"
:loading="loading"
show-search
:allowClear="allowClear"
placeholder="Select"
@search="onSearchTimed"
@popupScroll="onScroll"
@ -75,9 +78,9 @@
</div>
</div>
</template>
<a-select-option v-for="option in options" :key="option.id" :value="option[optionValueKey]">
<a-select-option v-for="option in selectableOptions" :key="option.id" :value="option[optionValueKey]">
<span>
<span v-if="showIcon">
<span v-if="showIcon && option.id !== null && option.id !== undefined">
<resource-icon v-if="option.icon && option.icon.base64image" :image="option.icon.base64image" size="1x" style="margin-right: 5px"/>
<render-icon v-else :icon="defaultIcon" style="margin-right: 5px" />
</span>
@ -124,6 +127,10 @@ export default {
type: Object,
default: null
},
allowClear: {
type: Boolean,
default: false
},
showIcon: {
type: Boolean,
default: true
@ -135,6 +142,10 @@ export default {
pageSize: {
type: Number,
default: null
},
selectFirstOption: {
type: Boolean,
default: false
}
},
data () {
@ -147,7 +158,8 @@ export default {
searchTimer: null,
scrollHandlerAttached: false,
preselectedOptionValue: null,
successiveFetches: 0
successiveFetches: 0,
hasAutoSelectedFirst: false
}
},
created () {
@ -166,6 +178,36 @@ export default {
},
formattedSearchFooterMessage () {
return `${this.$t('label.showing.results.for').replace('%x', this.searchQuery)}`
},
selectableOptions () {
const currentValue = this.$attrs.value
// Only filter out null/empty options when the current value is also null/undefined/empty
// This prevents such options from being selected and allows the placeholder to show instead
if (currentValue === null || currentValue === undefined || currentValue === '') {
return this.options.filter(option => {
const optionValue = option[this.optionValueKey]
return optionValue !== null && optionValue !== undefined && optionValue !== ''
})
}
// When a valid value is selected, show all options
return this.options
},
apiOptionsCount () {
if (this.defaultOption) {
const defaultOptionValue = this.defaultOption[this.optionValueKey]
return this.options.filter(option => option[this.optionValueKey] !== defaultOptionValue).length
}
return this.options.length
},
preselectedMatchValue () {
// Extract the first value from preselectedOptionValue if it's an array, otherwise return the value itself
if (!this.preselectedOptionValue) return null
return Array.isArray(this.preselectedOptionValue) ? this.preselectedOptionValue[0] : this.preselectedOptionValue
},
preselectedMatch () {
// Find the matching option for the preselected value
if (!this.preselectedMatchValue) return null
return this.options.find(entry => entry[this.optionValueKey] === this.preselectedMatchValue) || null
}
},
watch: {
@ -210,6 +252,7 @@ export default {
}).finally(() => {
if (this.successiveFetches === 0) {
this.loading = false
this.autoSelectFirstOptionIfNeeded()
}
})
},
@ -220,11 +263,10 @@ export default {
this.resetPreselectedOptionValue()
return
}
const matchValue = Array.isArray(this.preselectedOptionValue) ? this.preselectedOptionValue[0] : this.preselectedOptionValue
const match = this.options.find(entry => entry[this.optionValueKey] === matchValue)
if (!match) {
if (!this.preselectedMatch) {
this.successiveFetches++
if (this.options.length < this.totalCount) {
// Exclude defaultOption from count when comparing with totalCount
if (this.apiOptionsCount < this.totalCount) {
this.fetchItems()
} else {
this.resetPreselectedOptionValue()
@ -246,6 +288,36 @@ export default {
this.preselectedOptionValue = null
this.successiveFetches = 0
},
autoSelectFirstOptionIfNeeded () {
if (!this.selectFirstOption || this.hasAutoSelectedFirst) {
return
}
// Don't auto-select if there's a preselected value being fetched
if (this.preselectedOptionValue) {
return
}
const currentValue = this.$attrs.value
if (currentValue !== undefined && currentValue !== null && currentValue !== '') {
return
}
if (this.options.length === 0) {
return
}
if (this.searchQuery && this.searchQuery.length > 0) {
return
}
// Only auto-select after initial load is complete (no more successive fetches)
if (this.successiveFetches > 0) {
return
}
const firstOption = this.options[0]
if (firstOption) {
const firstValue = firstOption[this.optionValueKey]
this.hasAutoSelectedFirst = true
this.$emit('change-option-value', firstValue)
this.$emit('change-option', firstOption)
}
},
onSearchTimed (value) {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
@ -264,7 +336,8 @@ export default {
},
onScroll (e) {
const nearBottom = e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 10
const hasMore = this.options.length < this.totalCount
// Exclude defaultOption from count when comparing with totalCount
const hasMore = this.apiOptionsCount < this.totalCount
if (nearBottom && hasMore && !this.loading) {
this.fetchItems()
}

View File

@ -90,45 +90,31 @@
<template #label>
<tooltip-label :title="$t('label.domainid')" :tooltip="apiParams.domainid.description"/>
</template>
<a-select
:loading="domainLoading"
<infinite-scroll-select
v-model:value="form.domainid"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
:selectFirstOption="true"
:placeholder="apiParams.domainid.description"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="domain in domainsList" :key="domain.id" :label="domain.path || domain.name || domain.description">
<span>
<resource-icon v-if="domain && domain.icon" :image="domain.icon.base64image" size="1x" style="margin-right: 5px"/>
<block-outlined v-else style="margin-right: 5px" />
{{ domain.path || domain.name || domain.description }}
</span>
</a-select-option>
</a-select>
@change-option-value="handleDomainChange" />
</a-form-item>
<a-form-item name="account" ref="account" v-if="!account">
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.account"
:loading="loadingAccount"
:placeholder="apiParams.account.description"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="(item, idx) in accountList" :key="idx" :label="item.name">
<span>
<resource-icon v-if="item && item.icon" :image="item.icon.base64image" size="1x" style="margin-right: 5px"/>
<team-outlined v-else style="margin-right: 5px" />
{{ item.name }}
</span>
</a-select-option>
</a-select>
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
:placeholder="apiParams.account.description" />
</a-form-item>
<a-form-item name="timezone" ref="timezone">
<template #label>
@ -185,12 +171,14 @@ import { timeZone } from '@/utils/timezone'
import debounce from 'lodash/debounce'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'AddUser',
components: {
TooltipLabel,
ResourceIcon
ResourceIcon,
InfiniteScrollSelect
},
data () {
this.fetchTimeZone = debounce(this.fetchTimeZone, 800)
@ -198,14 +186,9 @@ export default {
loading: false,
timeZoneLoading: false,
timeZoneMap: [],
domainLoading: false,
domainsList: [],
selectedDomain: '',
samlEnable: false,
idpLoading: false,
idps: [],
loadingAccount: false,
accountList: [],
account: null,
domainid: null
}
@ -218,6 +201,19 @@ export default {
computed: {
samlAllowed () {
return 'authorizeSamlSso' in this.$store.getters.apis
},
domainsApiParams () {
return {
listall: true,
showicon: true,
details: 'min'
}
},
accountsApiParams () {
return {
showicon: true,
domainid: this.form?.domainid || null
}
}
},
methods: {
@ -241,53 +237,18 @@ export default {
fetchData () {
this.account = this.$route.query && this.$route.query.account ? this.$route.query.account : null
this.domainid = this.$route.query && this.$route.query.domainid ? this.$route.query.domainid : null
if (!this.domianid) {
this.fetchDomains()
// Set initial domain if provided from route
if (this.domainid) {
this.form.domainid = this.domainid
}
this.fetchTimeZone()
if (this.samlAllowed) {
this.fetchIdps()
}
},
fetchDomains () {
this.domainLoading = true
var params = {
listAll: true,
showicon: true,
details: 'min'
}
api('listDomains', params).then(response => {
this.domainsList = response.listdomainsresponse.domain || []
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext
})
}).finally(() => {
const domainid = this.domainsList[0]?.id || ''
this.form.domainid = domainid
this.fetchAccount(domainid)
this.domainLoading = false
})
},
fetchAccount (domainid) {
this.accountList = []
handleDomainChange (domainId) {
this.form.domainid = domainId
this.form.account = null
this.loadingAccount = true
var params = { listAll: true, showicon: true }
if (domainid) {
params.domainid = domainid
}
api('listAccounts', params).then(response => {
this.accountList = response.listaccountsresponse.account || []
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext
})
}).finally(() => {
this.loadingAccount = false
})
},
fetchTimeZone (value) {
this.timeZoneMap = []
@ -328,12 +289,14 @@ export default {
accounttype: 0
}
// Account: use route query account if available, otherwise use form value (which is the account name)
if (this.account) {
params.account = this.account
} else if (this.accountList[values.account]) {
params.account = this.accountList[values.account].name
} else if (values.account) {
params.account = values.account
}
// Domain: use route query domainid if available, otherwise use form value
if (this.domainid) {
params.domainid = this.domainid
} else if (values.domainid) {

View File

@ -121,15 +121,20 @@
ref="domain"
name="domain"
>
<a-auto-complete
<infinite-scroll-select
v-model:value="form.domain"
:options="domains"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
:placeholder="$t('label.domain')"
:filter-option="filterOption"
:defaultOption="{ id: null, path: ''}"
:allowClear="true"
style="width: 100%;"
@select="getAccounts"
:dropdownMatchSelectWidth="false"
/>
@change-option-value="handleDomainChange"
@change-option="handleDomainOptionChange" />
</a-form-item>
</a-col>
</a-row>&nbsp;
@ -150,15 +155,20 @@
ref="account"
name="account"
>
<a-auto-complete
<infinite-scroll-select
v-model:value="form.account"
:options="accounts"
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="id"
optionLabelKey="name"
defaultIcon="team-outlined"
:placeholder="$t('label.account')"
:filter-option="filterOption"
:disabled="form.isRecursive"
:dropdownMatchSelectWidth="false"
@select="selectAccount"
/>
:defaultOption="{ id: null, name: ''}"
allowClear="true"
@change-option-value="selectAccount"
@change-option="selectAccountOption" />
</a-form-item>
</a-col>
<a-col :span="3" v-if="'listUsageTypes' in $store.getters.apis">
@ -361,6 +371,7 @@ import ListView from '@/components/view/ListView'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import TooltipButton from '@/components/widgets/TooltipButton'
import Status from '@/components/widgets/Status'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
dayjs.extend(relativeTime)
dayjs.extend(utc)
@ -374,7 +385,8 @@ export default {
ListView,
Status,
TooltipLabel,
TooltipButton
TooltipButton,
InfiniteScrollSelect
},
props: {
resource: {
@ -402,8 +414,6 @@ export default {
page: 1,
pageSize: 20,
usageTypes: [],
domains: [],
accounts: [],
account: null,
domain: null,
usageType: null,
@ -436,6 +446,23 @@ export default {
this.fetchData()
this.updateColumns()
},
computed: {
domainsApiParams () {
return {
listall: true
}
},
accountsApiParams () {
if (!this.form.domain) {
return {
listall: true
}
}
return {
domainid: this.form.domain
}
}
},
methods: {
clearFilters () {
this.formRef.value.resetFields()
@ -445,8 +472,6 @@ export default {
this.usageType = null
this.page = 1
this.pageSize = 20
this.getAccounts()
},
disabledDate (current) {
return current && current > dayjs().endOf('day')
@ -473,8 +498,6 @@ export default {
this.listUsageServerMetrics()
this.getUsageTypes()
this.getAllUsageRecordColumns()
this.getDomains()
this.getAccounts()
if (!this.$store.getters.customColumns[this.$store.getters.userInfo.id]) {
this.$store.getters.customColumns[this.$store.getters.userInfo.id] = {}
this.$store.getters.customColumns[this.$store.getters.userInfo.id][this.$route.path] = this.selectedColumnKeys
@ -528,16 +551,6 @@ export default {
this.formRef.value.scrollToField(error.errorFields[0].name)
})
},
selectAccount (value, option) {
if (option && option.id) {
this.account = option
} else {
this.account = null
if (this.formRef?.value) {
this.formRef.value.resetFields('account')
}
}
},
selectUsageType (value, option) {
if (option && option.id) {
this.usageType = option
@ -548,24 +561,12 @@ export default {
}
}
},
getDomains () {
api('listDomains', { listAll: true }).then(json => {
if (json && json.listdomainsresponse && json.listdomainsresponse.domain) {
this.domains = [{ id: null, value: '' }, ...json.listdomainsresponse.domain.map(x => {
return {
id: x.id,
value: x.path
}
})]
}
})
handleDomainChange (domainId) {
this.form.domain = domainId
this.form.account = null
},
getAccounts (value, option) {
var params = {
listAll: true
}
handleDomainOptionChange (option) {
if (option && option.id) {
params.domainid = option.id
this.domain = option
} else {
this.domain = null
@ -573,16 +574,19 @@ export default {
this.formRef.value.resetFields('domain')
}
}
api('listAccounts', params).then(json => {
if (json && json.listaccountsresponse && json.listaccountsresponse.account) {
this.accounts = [{ id: null, value: '' }, ...json.listaccountsresponse.account.map(x => {
return {
id: x.id,
value: x.name
}
})]
},
selectAccount (accountId) {
this.form.account = accountId
},
selectAccountOption (option) {
if (option && option.id) {
this.account = option
} else {
this.account = null
if (this.formRef?.value) {
this.formRef.value.resetFields('account')
}
})
}
},
getParams (page, pageSize) {
const formRaw = toRaw(this.form)

View File

@ -73,42 +73,32 @@
<template #label>
<tooltip-label :title="$t('label.domainid')" :tooltip="apiParams.domainid.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.domainid"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:loading="domainLoading"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
allowClear="true"
:placeholder="apiParams.domainid.description"
@change="val => { handleDomainChange(val) }">
<a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex" :label="opt.path || opt.name || opt.description" :value="opt.id">
<span>
<resource-icon v-if="opt && opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
<block-outlined v-else style="margin-right: 5px" />
{{ opt.path || opt.name || opt.description }}
</span>
</a-select-option>
</a-select>
@change-option-value="handleDomainChange" />
</a-form-item>
<a-form-item name="account" ref="account" v-if="domainid">
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.account"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:placeholder="apiParams.account.description"
@change="val => { handleAccountChange(val) }">
<a-select-option v-for="(acc, index) in accounts" :value="acc.name" :key="index">
{{ acc.name }}
</a-select-option>
</a-select>
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
allowClear="true"
:placeholder="apiParams.account.description" />
</a-form-item>
<a-form-item
@ -199,13 +189,15 @@ import { api } from '@/api'
import { mixinForm } from '@/utils/mixin'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'CreateTemplate',
mixins: [mixinForm],
components: {
ResourceIcon,
TooltipLabel
TooltipLabel,
InfiniteScrollSelect
},
props: {
resource: {
@ -219,9 +211,6 @@ export default {
zones: [],
osTypes: {},
loading: false,
domains: [],
accounts: [],
domainLoading: false,
domainid: null,
account: null,
architectureTypes: {}
@ -230,6 +219,21 @@ export default {
computed: {
isAdminRole () {
return this.$store.getters.userInfo.roletype === 'Admin'
},
domainsApiParams () {
return {
listall: true,
showicon: true,
details: 'min'
}
},
accountsApiParams () {
if (!this.domainid) {
return null
}
return {
domainid: this.domainid
}
}
},
beforeCreate () {
@ -256,9 +260,6 @@ export default {
if (this.resource.intervaltype) {
this.fetchSnapshotZones()
}
if ('listDomains' in this.$store.getters.apis) {
this.fetchDomains()
}
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
},
fetchOsTypes () {
@ -309,44 +310,16 @@ export default {
}
})
},
fetchDomains () {
const params = {}
params.listAll = true
params.showicon = true
params.details = 'min'
this.domainLoading = true
api('listDomains', params).then(json => {
this.domains = json.listdomainsresponse.domain
}).finally(() => {
this.domainLoading = false
this.handleDomainChange(null)
})
},
async handleDomainChange (domain) {
this.domainid = domain
handleDomainChange (domainId) {
this.domainid = domainId
this.form.account = null
this.account = null
if ('listAccounts' in this.$store.getters.apis) {
await this.fetchAccounts()
}
},
fetchAccounts () {
return new Promise((resolve, reject) => {
api('listAccounts', {
domainid: this.domainid
}).then(response => {
this.accounts = response?.listaccountsresponse?.account || []
resolve(this.accounts)
}).catch(error => {
this.$notifyError(error)
})
})
},
handleAccountChange (acc) {
if (acc) {
this.account = acc.name
handleAccountChange (accountName) {
if (accountName) {
this.account = accountName
} else {
this.account = acc
this.account = null
}
},
handleSubmit (e) {

View File

@ -57,43 +57,33 @@
<template #label>
<tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.zoneId"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="zone.id" v-for="zone in zones" :key="zone.id" :label="zone.name || zone.description">
<span>
<resource-icon v-if="zone.icon" :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
<global-outlined v-else style="margin-right: 5px"/>
{{ zone.name || zone.description }}
</span>
</a-select-option>
</a-select>
api="listZones"
:apiParams="zonesApiParams"
resourceType="zone"
optionValueKey="id"
optionLabelKey="name"
defaultIcon="global-outlined"
selectFirstOption="true"
@change-option-value="handleZoneChange" />
</a-form-item>
<a-form-item name="diskofferingid" ref="diskofferingid">
<template #label>
<tooltip-label :title="$t('label.diskofferingid')" :tooltip="apiParams.diskofferingid.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.diskofferingid"
:loading="offeringLoading"
api="listDiskOfferings"
:apiParams="diskOfferingsApiParams"
resourceType="diskoffering"
optionValueKey="id"
optionLabelKey="displaytext"
defaultIcon="hdd-outlined"
:defaultOption="{ id: null, displaytext: ''}"
allowClear="true"
:placeholder="apiParams.diskofferingid.description"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
v-for="(offering, index) in offerings"
:value="offering.id"
:key="index"
:label="offering.displaytext || offering.name">
{{ offering.displaytext || offering.name }}
</a-select-option>
</a-select>
@change-option="onChangeDiskOffering" />
</a-form-item>
<a-form-item ref="format" name="format">
<template #label>
@ -124,38 +114,33 @@
<template #label>
<tooltip-label :title="$t('label.domain')" :tooltip="apiParams.domainid.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.domainid"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:loading="domainLoading"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
:placeholder="$t('label.domainid')"
@change="val => { handleDomainChange(domainList[val].id) }">
<a-select-option v-for="(opt, optIndex) in domainList" :key="optIndex" :label="opt.path || opt.name || opt.description">
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
allowClear="true"
@change-option-value="handleDomainChange" />
</a-form-item>
<a-form-item name="account" ref="account" v-if="'listDomains' in $store.getters.apis">
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.account"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
allowClear="true"
:placeholder="$t('label.account')"
@change="val => { handleAccountChange(val) }">
<a-select-option v-for="(acc, index) in accountList" :value="acc.name" :key="index">
{{ acc.name }}
</a-select-option>
</a-select>
@change-option-value="handleAccountChange" />
</a-form-item>
<div :span="24" class="action-button">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@ -173,27 +158,25 @@ import { axios } from '../../utils/request'
import { mixinForm } from '@/utils/mixin'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'UploadLocalVolume',
mixins: [mixinForm],
components: {
ResourceIcon,
TooltipLabel
TooltipLabel,
InfiniteScrollSelect
},
data () {
return {
fileList: [],
zones: [],
domainList: [],
accountList: [],
offerings: [],
offeringLoading: false,
formats: ['RAW', 'VHD', 'VHDX', 'OVA', 'QCOW2'],
domainId: null,
account: null,
uploadParams: null,
domainLoading: false,
customDiskOffering: false,
isCustomizedDiskIOps: false,
loading: false,
uploadPercentage: 0
}
@ -201,9 +184,38 @@ export default {
beforeCreate () {
this.apiParams = this.$getApiParams('getUploadParamsForVolume')
},
computed: {
zonesApiParams () {
return {
showicon: true
}
},
diskOfferingsApiParams () {
if (!this.form.zoneId) {
return null
}
return {
zoneid: this.form.zoneId,
listall: true
}
},
domainsApiParams () {
return {
listall: true,
details: 'min'
}
},
accountsApiParams () {
if (!this.form.domainid) {
return null
}
return {
domainid: this.form.domainid
}
}
},
created () {
this.initForm()
this.fetchData()
},
methods: {
initForm () {
@ -221,38 +233,18 @@ export default {
zoneId: [{ required: true, message: this.$t('message.error.select') }]
})
},
listZones () {
api('listZones', { showicon: true }).then(json => {
if (json && json.listzonesresponse && json.listzonesresponse.zone) {
this.zones = json.listzonesresponse.zone
this.zones = this.zones.filter(zone => zone.type !== 'Edge')
if (this.zones.length > 0) {
this.onZoneChange(this.zones[0].id)
}
}
})
},
onZoneChange (zoneId) {
handleZoneChange (zoneId) {
this.form.zoneId = zoneId
this.zoneId = zoneId
this.fetchDiskOfferings(zoneId)
// InfiniteScrollSelect will auto-reload disk offerings when apiParams changes
},
fetchDiskOfferings (zoneId) {
this.offeringLoading = true
this.offerings = [{ id: -1, name: '' }]
this.form.diskofferingid = undefined
api('listDiskOfferings', {
zoneid: zoneId,
listall: true
}).then(json => {
for (var offering of json.listdiskofferingsresponse.diskoffering) {
if (offering.iscustomized) {
this.offerings.push(offering)
}
}
}).finally(() => {
this.offeringLoading = false
})
onChangeDiskOffering (offering) {
if (offering) {
this.customDiskOffering = offering.iscustomized || false
this.isCustomizedDiskIOps = offering.iscustomizediops || false
} else {
this.customDiskOffering = false
this.isCustomizedDiskIOps = false
}
},
handleRemove (file) {
const index = this.fileList.indexOf(file)
@ -266,53 +258,14 @@ export default {
this.form.file = file
return false
},
handleDomainChange (domain) {
this.domainId = domain
if ('listAccounts' in this.$store.getters.apis) {
this.fetchAccounts()
}
handleDomainChange (domainId) {
this.form.domainid = domainId
this.domainId = domainId
this.form.account = null
},
handleAccountChange (acc) {
if (acc) {
this.account = acc.name
} else {
this.account = acc
}
},
fetchData () {
this.listZones()
if ('listDomains' in this.$store.getters.apis) {
this.fetchDomains()
}
},
fetchDomains () {
this.domainLoading = true
api('listDomains', {
listAll: true,
details: 'min'
}).then(response => {
this.domainList = response.listdomainsresponse.domain
if (this.domainList[0]) {
this.handleDomainChange(null)
}
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.domainLoading = false
})
},
fetchAccounts () {
api('listAccounts', {
domainid: this.domainId
}).then(response => {
this.accountList = response.listaccountsresponse.account || []
if (this.accountList && this.accountList.length === 0) {
this.handleAccountChange(null)
}
}).catch(error => {
this.$notifyError(error)
})
handleAccountChange (accountName) {
this.form.account = accountName
this.account = accountName
},
handleSubmit (e) {
e.preventDefault()

View File

@ -47,21 +47,16 @@
<template #label>
<tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.zoneId"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="zone.id" v-for="zone in zones" :key="zone.id" :label="zone.name || zone.description">
<span>
<resource-icon v-if="zone.icon" :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
<global-outlined v-else style="margin-right: 5px"/>
{{ zone.name || zone.description }}
</span>
</a-select-option>
</a-select>
api="listZones"
:apiParams="zonesApiParams"
resourceType="zone"
optionValueKey="id"
optionLabelKey="name"
defaultIcon="global-outlined"
selectFirstOption="true"
@change-option-value="handleZoneChange" />
</a-form-item>
<a-form-item name="format" ref="format">
<template #label>
@ -83,23 +78,17 @@
<template #label>
<tooltip-label :title="$t('label.diskofferingid')" :tooltip="apiParams.diskofferingid.description || $t('label.diskoffering')"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.diskofferingid"
:loading="loading"
@change="id => onChangeDiskOffering(id)"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
v-for="(offering, index) in offerings"
:value="offering.id"
:key="index"
:label="offering.displaytext || offering.name">
{{ offering.displaytext || offering.name }}
</a-select-option>
</a-select>
api="listDiskOfferings"
:apiParams="diskOfferingsApiParams"
resourceType="diskoffering"
optionValueKey="id"
optionLabelKey="displaytext"
defaultIcon="hdd-outlined"
:defaultOption="{ id: null, displaytext: ''}"
allowClear="true"
@change-option="onChangeDiskOffering" />
</a-form-item>
<a-form-item name="checksum" ref="checksum">
<template #label>
@ -114,38 +103,33 @@
<template #label>
<tooltip-label :title="$t('label.domain')" :tooltip="apiParams.domainid.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.domainid"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:loading="domainLoading"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
allowClear="true"
:placeholder="$t('label.domainid')"
@change="val => { handleDomainChange(domainList[val].id) }">
<a-select-option v-for="(opt, optIndex) in domainList" :key="optIndex" :label="opt.path || opt.name || opt.description">
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
@change-option-value="handleDomainChange" />
</a-form-item>
<a-form-item name="account" ref="account" v-if="'listDomains' in $store.getters.apis">
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.account"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
:placeholder="$t('label.account')"
@change="val => { handleAccountChange(val) }">
<a-select-option v-for="(acc, index) in accountList" :value="acc.name" :key="index">
{{ acc.name }}
</a-select-option>
</a-select>
allowClear="true"
@change-option-value="handleAccountChange" />
</a-form-item>
<div :span="24" class="action-button">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@ -162,27 +146,26 @@ import { api } from '@/api'
import { mixinForm } from '@/utils/mixin'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'UploadVolume',
mixins: [mixinForm],
components: {
ResourceIcon,
TooltipLabel
TooltipLabel,
InfiniteScrollSelect
},
data () {
return {
zones: [],
domainList: [],
accountList: [],
formats: ['RAW', 'VHD', 'VHDX', 'OVA', 'QCOW2'],
offerings: [],
zoneSelected: '',
selectedDiskOfferingId: null,
domainId: null,
account: null,
uploadParams: null,
domainLoading: false,
customDiskOffering: false,
isCustomizedDiskIOps: false,
loading: false,
uploadPercentage: 0
}
@ -190,9 +173,36 @@ export default {
beforeCreate () {
this.apiParams = this.$getApiParams('uploadVolume')
},
computed: {
zonesApiParams () {
return {
showicon: true
}
},
diskOfferingsApiParams () {
if (!this.form.zoneId) {
return null
}
return {
zoneid: this.form.zoneId,
listall: true
}
},
domainsApiParams () {
return {
listall: true,
details: 'min'
}
},
accountsApiParams () {
return {
domainid: this.form?.domainid || null,
showicon: true
}
}
},
created () {
this.initForm()
this.fetchData()
},
methods: {
initForm () {
@ -207,78 +217,28 @@ export default {
format: [{ required: true, message: this.$t('message.error.select') }]
})
},
fetchData () {
this.loading = true
api('listZones', { showicon: true }).then(json => {
this.zones = json.listzonesresponse.zone || []
this.zones = this.zones.filter(zone => zone.type !== 'Edge')
this.form.zoneId = this.zones[0].id || ''
this.fetchDiskOfferings(this.form.zoneId)
}).finally(() => {
this.loading = false
})
if ('listDomains' in this.$store.getters.apis) {
this.fetchDomains()
}
handleZoneChange (zoneId) {
this.form.zoneId = zoneId
// InfiniteScrollSelect will auto-reload disk offerings when apiParams changes
},
fetchDiskOfferings (zoneId) {
this.loading = true
api('listDiskOfferings', {
zoneid: zoneId,
listall: true
}).then(json => {
this.offerings = json.listdiskofferingsresponse.diskoffering || []
}).finally(() => {
this.loading = false
})
},
fetchDomains () {
this.domainLoading = true
api('listDomains', {
listAll: true,
details: 'min'
}).then(response => {
this.domainList = response.listdomainsresponse.domain
if (this.domainList[0]) {
this.handleDomainChange(null)
}
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.domainLoading = false
})
},
fetchAccounts () {
api('listAccounts', {
domainid: this.domainId
}).then(response => {
this.accountList = response.listaccountsresponse.account || []
if (this.accountList && this.accountList.length === 0) {
this.handleAccountChange(null)
}
}).catch(error => {
this.$notifyError(error)
})
},
onChangeDiskOffering (id) {
const offering = this.offerings.filter(x => x.id === id)
this.customDiskOffering = offering[0]?.iscustomized || false
this.isCustomizedDiskIOps = offering[0]?.iscustomizediops || false
},
handleDomainChange (domain) {
this.domainId = domain
if ('listAccounts' in this.$store.getters.apis) {
this.fetchAccounts()
}
},
handleAccountChange (acc) {
if (acc) {
this.account = acc.name
onChangeDiskOffering (offering) {
if (offering) {
this.customDiskOffering = offering.iscustomized || false
this.isCustomizedDiskIOps = offering.iscustomizediops || false
} else {
this.account = acc
this.customDiskOffering = false
this.isCustomizedDiskIOps = false
}
},
handleDomainChange (domainId) {
this.form.domainid = domainId
this.domainId = domainId
this.form.account = null
},
handleAccountChange (accountName) {
this.form.account = accountName
this.account = accountName
},
handleSubmit (e) {
e.preventDefault()
if (this.loading) return

View File

@ -67,39 +67,33 @@
<info-circle-outlined style="color: rgba(0,0,0,.45)" />
</a-tooltip>
</template>
<a-select
<infinite-scroll-select
id="domain-selection"
v-model:value="form.domainid"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:loading="domainLoading"
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
:defaultOption="{ id: null, path: ''}"
allowClear="true"
:placeholder="apiParams.domainid.description"
@change="val => { handleDomainChanged(val) }">
<a-select-option v-for="opt in domains" :key="opt.id" :label="opt.path || opt.name || opt.description || ''">
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
@change-option-value="handleDomainChanged" />
</a-form-item>
<a-form-item name="account" ref="account" v-if="isAdminOrDomainAdmin && ['Local'].includes(form.scope) && form.domainid">
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.account"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:loading="accountLoading"
:placeholder="apiParams.account.description">
<a-select-option v-for="opt in accounts" :key="opt.id" :label="opt.name">
{{ opt.name }}
</a-select-option>
</a-select>
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
:placeholder="apiParams.account.description" />
</a-form-item>
<a-form-item name="payloadurl" ref="payloadurl">
<template #label>
@ -156,25 +150,22 @@
<script>
import { ref, reactive, toRaw } from 'vue'
import { api } from '@/api'
import _ from 'lodash'
import { mixinForm } from '@/utils/mixin'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import TestWebhookDeliveryView from '@/components/view/TestWebhookDeliveryView'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'CreateWebhook',
mixins: [mixinForm],
components: {
TooltipLabel,
TestWebhookDeliveryView
TestWebhookDeliveryView,
InfiniteScrollSelect
},
props: {},
data () {
return {
domains: [],
domainLoading: false,
accounts: [],
accountLoading: false,
loading: false,
testDeliveryAllowed: false,
testDeliveryLoading: false
@ -185,9 +176,6 @@ export default {
},
created () {
this.initForm()
if (['Domain', 'Local'].includes(this.form.scope)) {
this.fetchDomainData()
}
},
computed: {
isAdminOrDomainAdmin () {
@ -201,6 +189,21 @@ export default {
return this.form.payloadurl.toLowerCase().startsWith('https://')
}
return false
},
domainsApiParams () {
return {
listAll: true,
showicon: true,
details: 'min'
}
},
accountsApiParams () {
if (!this.form.domainid) {
return null
}
return {
domainid: this.form.domainid
}
}
},
methods: {
@ -228,46 +231,6 @@ export default {
updateTestDeliveryLoading (value) {
this.testDeliveryLoading = value
},
fetchDomainData () {
this.domainLoading = true
this.domains = [
{
id: null,
name: ''
}
]
this.form.domainid = null
this.form.account = null
api('listDomains', {}).then(json => {
const listdomains = json.listdomainsresponse.domain
this.domains = this.domains.concat(listdomains)
}).finally(() => {
this.domainLoading = false
if (this.arrayHasItems(this.domains)) {
this.form.domainid = null
}
})
},
fetchAccountData () {
this.accounts = []
this.form.account = null
if (!this.form.domainid) {
return
}
this.accountLoading = true
var params = {
domainid: this.form.domainid
}
api('listAccounts', params).then(json => {
const listAccounts = json.listaccountsresponse.account || []
this.accounts = listAccounts
}).finally(() => {
this.accountLoading = false
if (this.arrayHasItems(this.accounts)) {
this.form.account = this.accounts[0].id
}
})
},
handleSubmit (e) {
e.preventDefault()
if (this.loading) return
@ -300,10 +263,8 @@ export default {
return
}
if (values.account) {
const accountItem = _.find(this.accounts, (option) => option.id === values.account)
if (accountItem) {
params.account = accountItem.name
}
// values.account is the account name (optionValueKey="name")
params.account = values.account
}
this.loading = true
api('createWebhook', params).then(json => {
@ -331,14 +292,11 @@ export default {
}, 1)
},
handleScopeChange (e) {
if (['Domain', 'Local'].includes(this.form.scope)) {
this.fetchDomainData()
}
this.form.domainid = null
this.form.account = null
},
handleDomainChanged (domainid) {
if (domainid) {
this.fetchAccountData()
}
this.form.account = null
}
}
}

View File

@ -372,22 +372,16 @@
name="domain"
ref="domain"
:label="$t('label.domain')">
<a-select
@change="changeDomain"
<infinite-scroll-select
v-model:value="importForm.selectedDomain"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="domain in domains" :key="domain.name" :value="domain.id" :label="domain.path || domain.name || domain.description">
<span>
<resource-icon v-if="domain && domain.icon" :image="domain.icon.base64image" size="1x" style="margin-right: 5px"/>
<block-outlined v-else style="margin-right: 5px" />
{{ domain.path || domain.name || domain.description }}
</span>
</a-select-option>
</a-select>
api="listDomains"
:apiParams="domainsApiParams"
resourceType="domain"
optionValueKey="id"
optionLabelKey="path"
defaultIcon="block-outlined"
allowClear="true"
@change-option-value="changeDomain" />
</a-form-item>
<a-form-item
@ -395,22 +389,16 @@
name="account"
ref="account"
:label="$t('label.account')">
<a-select
@change="changeAccount"
<infinite-scroll-select
v-model:value="importForm.selectedAccount"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="account in accounts" :key="account.name" :value="account.name">
<span>
<resource-icon v-if="account && account.icon" :image="account.icon.base64image" size="1x" style="margin-right: 5px"/>
<team-outlined v-else style="margin-right: 5px" />
{{ account.name }}
</span>
</a-select-option>
</a-select>
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
allowClear="true"
@change-option-value="changeAccount" />
<span v-if="importForm.accountError" class="required">{{ $t('label.required') }}</span>
</a-form-item>
@ -419,22 +407,16 @@
name="project"
ref="project"
:label="$t('label.project')">
<a-select
@change="changeProject"
<infinite-scroll-select
v-model:value="importForm.selectedProject"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="project in projects" :key="project.id" :value="project.id" :label="project.name">
<span>
<resource-icon v-if="project && project.icon" :image="project.icon.base64image" size="1x" style="margin-right: 5px"/>
<project-outlined v-else style="margin-right: 5px" />
{{ project.name }}
</span>
</a-select-option>
</a-select>
api="listProjects"
:apiParams="projectsApiParams"
resourceType="project"
optionValueKey="id"
optionLabelKey="name"
defaultIcon="project-outlined"
allowClear="true"
@change-option-value="changeProject" />
<span v-if="importForm.projectError" class="required">{{ $t('label.required') }}</span>
</a-form-item>
@ -480,6 +462,7 @@ import Status from '@/components/widgets/Status'
import SearchView from '@/components/view/SearchView'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel.vue'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
components: {
@ -487,7 +470,8 @@ export default {
Breadcrumb,
Status,
SearchView,
ResourceIcon
ResourceIcon,
InfiniteScrollSelect
},
name: 'ManageVolumes',
data () {
@ -607,7 +591,6 @@ export default {
this.page.managed = parseInt(this.$route.query.managedpage || 1)
this.initForm()
this.fetchData()
this.fetchDomains()
},
computed: {
isPageAllowed () {
@ -629,6 +612,36 @@ export default {
showCluster () {
return this.poolscope !== 'zone'
},
domainsApiParams () {
return {
listall: true,
details: 'min',
showicon: true
}
},
accountsApiParams () {
if (!this.importForm.selectedDomain) {
return null
}
return {
domainid: this.importForm.selectedDomain,
showicon: true,
state: 'Enabled',
isrecursive: false
}
},
projectsApiParams () {
if (!this.importForm.selectedDomain) {
return null
}
return {
domainid: this.importForm.selectedDomain,
state: 'Active',
showicon: true,
details: 'min',
isrecursive: false
}
},
showHost () {
return this.poolscope === 'host'
},
@ -970,53 +983,6 @@ export default {
this.updateQuery('scope', value)
this.fetchOptions(this.params.zones, 'zones', value)
},
fetchDomains () {
api('listDomains', {
response: 'json',
listAll: true,
showicon: true,
details: 'min'
}).then(response => {
this.domains = response.listdomainsresponse.domain || []
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.loading = false
})
},
fetchAccounts () {
this.loading = true
api('listAccounts', {
response: 'json',
domainId: this.importForm.selectedDomain,
showicon: true,
state: 'Enabled',
isrecursive: false
}).then(response => {
this.accounts = response.listaccountsresponse.account || []
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.loading = false
})
},
fetchProjects () {
this.loading = true
api('listProjects', {
response: 'json',
domainId: this.importForm.selectedDomain,
state: 'Active',
showicon: true,
details: 'min',
isrecursive: false
}).then(response => {
this.projects = response.listprojectsresponse.project || []
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.loading = false
})
},
changeAccountType () {
this.importForm.selectedDomain = null
this.importForm.selectedAccount = null
@ -1029,8 +995,7 @@ export default {
this.importForm.selectedProject = null
this.importForm.selectedDiskoffering = null
this.diskOfferings = {}
this.fetchAccounts()
this.fetchProjects()
// InfiniteScrollSelect will auto-reload when apiParams changes
},
changeAccount () {
this.importForm.selectedProject = null