Use infinite scroll select

This commit is contained in:
vishesh92 2025-11-05 22:12:11 +05:30
parent d53b6dbda4
commit 10b60c27a0
No known key found for this signature in database
GPG Key ID: 4E395186CBFA790B
10 changed files with 587 additions and 886 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,49 @@ 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()
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
},
handleChangeAccount (e) {
this.$emit('accountChange', e)
handleChangeAccount (accountName) {
this.selectedAccount = accountName
this.$emit('accountChange', accountName)
}
}
}

View File

@ -58,6 +58,7 @@
:filter-option="false"
:loading="loading"
show-search
allowClear
placeholder="Select"
@search="onSearchTimed"
@popupScroll="onScroll"

View File

@ -19,7 +19,7 @@
<a-form layout="vertical" >
<a-form-item :label="$t('label.owner.type')">
<a-select
@change="changeDomain"
@change="changeAccountType"
v-model:value="selectedAccountType"
defaultValue="account"
autoFocus
@ -36,114 +36,62 @@
</a-select>
</a-form-item>
<a-form-item :label="$t('label.domain')" required>
<a-select
@change="changeDomain"
<infinite-scroll-select
v-model:value="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 />
{{ 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"
@change-option-value="handleDomainChange" />
</a-form-item>
<template v-if="selectedAccountType === 'Account'">
<a-form-item :label="$t('label.account')" required>
<a-select
@change="emitChangeEvent"
<infinite-scroll-select
v-model:value="selectedAccount"
showSearch
optionFilterProp="label"
:filterOption="
(input, option) => {
return option.label.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 />
{{ account.name }}
</span>
</a-select-option>
</a-select>
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
defaultIcon="team-outlined"
@change-option-value="handleAccountChange" />
</a-form-item>
</template>
<template v-else>
<a-form-item :label="$t('label.project')" required>
<a-select
@change="emitChangeEvent"
<infinite-scroll-select
v-model:value="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 />
{{ project.name }}
</span>
</a-select-option>
</a-select>
api="listProjects"
:apiParams="projectsApiParams"
resourceType="project"
optionValueKey="id"
optionLabelKey="name"
defaultIcon="project-outlined"
@change-option-value="handleProjectChange" />
</a-form-item>
</template>
</a-form>
</template>
<script>
import { api } from '@/api'
import ResourceIcon from '@/components/view/ResourceIcon.vue'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect.vue'
export default {
name: 'OwnershipSelection',
components: { ResourceIcon },
components: { InfiniteScrollSelect },
data () {
return {
domains: [],
accounts: [],
projects: [],
selectedAccountType: this.$store.getters.project?.id ? 'Project' : 'Account',
selectedDomain: null,
selectedAccount: null,
selectedProject: null,
loading: false
selectedDomainOption: null,
selectedAccountOption: null,
selectedProjectOption: null
}
},
props: {
@ -151,103 +99,98 @@ export default {
type: Object
}
},
created () {
this.fetchData()
},
methods: {
fetchData () {
this.loading = true
api('listDomains', {
response: 'json',
computed: {
domainsApiParams () {
return {
listAll: true,
showicon: true,
details: 'min'
})
.then((response) => {
this.domains = response.listdomainsresponse.domain
if (this.override) {
this.domains = this.domains.filter(item => this.override.domains.has(item.id))
}
if (this.domains.length === 0) {
this.selectedDomain = null
this.selectedProject = null
this.selectedAccount = null
return
}
const domainIds = this.domains?.map(domain => domain.id)
const ownerDomainId = this.$store.getters.project?.domainid || this.$store.getters.userInfo.domainid
this.selectedDomain = domainIds?.includes(ownerDomainId) ? ownerDomainId : this.domains?.[0]?.id
this.changeDomain()
})
.catch((error) => {
this.$notifyError(error)
})
.finally(() => {
this.loading = false
})
}
},
fetchAccounts () {
this.loading = true
api('listAccounts', {
response: 'json',
domainId: this.selectedDomain,
accountsApiParams () {
if (!this.selectedDomain) {
return null
}
return {
domainid: this.selectedDomain,
showicon: true,
state: 'Enabled',
isrecursive: false
})
.then((response) => {
this.accounts = response.listaccountsresponse.account || []
if (this.override?.accounts && this.accounts) {
this.accounts = this.accounts.filter(item => this.override.accounts.has(item.name))
}
const accountNames = this.accounts.map(account => account.name)
if (this.selectedDomain === this.$store.getters.userInfo.domainid && accountNames.includes(this.$store.getters.userInfo.account)) {
this.selectedAccount = this.$store.getters.userInfo.account
} else {
this.selectedAccount = this.accounts?.[0]?.name
}
this.selectedProject = null
this.emitChangeEvent()
})
.catch((error) => {
this.$notifyError(error)
})
.finally(() => {
this.loading = false
})
}
},
fetchProjects () {
this.loading = true
api('listProjects', {
response: 'json',
projectsApiParams () {
if (!this.selectedDomain) {
return null
}
return {
domainId: this.selectedDomain,
state: 'Active',
showicon: true,
details: 'min',
isrecursive: false
})
.then((response) => {
this.projects = response.listprojectsresponse.project
if (this.override?.projects && this.projects) {
this.projects = this.projects.filter(item => this.override.projects.has(item.id))
}
this.selectedProject = this.projects?.map(project => project.id)?.includes(this.$store.getters.project?.id) ? this.$store.getters.project?.id : this.projects?.[0]?.id
this.selectedAccount = null
this.emitChangeEvent()
})
.catch((error) => {
this.$notifyError(error)
})
.finally(() => {
this.loading = false
})
},
changeDomain () {
if (this.selectedAccountType === 'Account') {
this.fetchAccounts()
} else {
this.fetchProjects()
}
}
},
created () {
// Set initial domain selection
const ownerDomainId = this.$store.getters.project?.domainid || this.$store.getters.userInfo.domainid
if (ownerDomainId) {
this.selectedDomain = ownerDomainId
}
},
methods: {
changeAccountType () {
// Reset account/project selection when switching types
this.selectedAccount = null
this.selectedProject = null
// Trigger initial selection based on type
this.handleDomainChange(this.selectedDomain)
},
handleDomainChange (domainId) {
this.selectedDomain = domainId
// Reset child selections
this.selectedAccount = null
this.selectedProject = null
// Pre-select account if it's the user's domain
if (this.selectedAccountType === 'Account' &&
this.selectedDomain === this.$store.getters.userInfo.domainid) {
this.selectedAccount = this.$store.getters.userInfo.account
} else if (this.selectedAccountType === 'Project' &&
this.$store.getters.project?.id &&
this.selectedDomain === this.$store.getters.project?.domainid) {
// Pre-select project if applicable
this.selectedProject = this.$store.getters.project?.id
}
this.emitChangeEvent()
},
handleDomainOptionChange (option) {
this.selectedDomainOption = option
// Note: Override filtering is not implemented in InfiniteScrollSelect migration
// If override.domains filtering is needed, it should be handled at a higher level
// or by using a custom filtering mechanism
},
handleAccountChange (accountName) {
this.selectedAccount = accountName
this.selectedProject = null
this.emitChangeEvent()
},
handleAccountOptionChange (option) {
this.selectedAccountOption = option
// Note: Override filtering is not implemented in InfiniteScrollSelect migration
// If override.accounts filtering is needed, it should be handled at a higher level
},
handleProjectChange (projectId) {
this.selectedProject = projectId
this.selectedAccount = null
this.emitChangeEvent()
},
handleProjectOptionChange (option) {
this.selectedProjectOption = option
// Note: Override filtering is not implemented in InfiniteScrollSelect migration
// If override.projects filtering is needed, it should be handled at a higher level
},
emitChangeEvent () {
this.$emit('fetch-owner', this)

View File

@ -90,45 +90,30 @@
<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"
: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 +170,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 +185,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 +200,25 @@ export default {
computed: {
samlAllowed () {
return 'authorizeSamlSso' in this.$store.getters.apis
},
domainsApiParams () {
return {
listall: true,
showicon: true,
details: 'min'
}
},
accountsApiParams () {
if (!this.form.domainid) {
return {
showicon: true,
listall: true
}
}
return {
showicon: true,
domainid: this.form.domainid
}
}
},
methods: {
@ -241,53 +242,19 @@ 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
})
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
},
fetchTimeZone (value) {
this.timeZoneMap = []
@ -328,12 +295,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,18 @@
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"
style="width: 100%;"
@select="getAccounts"
:dropdownMatchSelectWidth="false"
/>
@change-option-value="handleDomainChange"
@change-option="handleDomainOptionChange" />
</a-form-item>
</a-col>
</a-row>&nbsp;
@ -150,15 +153,18 @@
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"
/>
@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 +367,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 +381,8 @@ export default {
ListView,
Status,
TooltipLabel,
TooltipButton
TooltipButton,
InfiniteScrollSelect
},
props: {
resource: {
@ -402,8 +410,6 @@ export default {
page: 1,
pageSize: 20,
usageTypes: [],
domains: [],
accounts: [],
account: null,
domain: null,
usageType: null,
@ -436,6 +442,24 @@ export default {
this.fetchData()
this.updateColumns()
},
computed: {
domainsApiParams () {
return {
listall: true
}
},
accountsApiParams () {
if (!this.form.domain) {
return {
listall: true
}
}
return {
listall: true,
domainid: this.form.domain
}
}
},
methods: {
clearFilters () {
this.formRef.value.resetFields()
@ -445,8 +469,6 @@ export default {
this.usageType = null
this.page = 1
this.pageSize = 20
this.getAccounts()
},
disabledDate (current) {
return current && current > dayjs().endOf('day')
@ -473,8 +495,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 +548,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 +558,13 @@ 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
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
},
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 +572,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,30 @@
<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"
: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"
:placeholder="apiParams.account.description" />
</a-form-item>
<a-form-item
@ -199,13 +187,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 +209,6 @@ export default {
zones: [],
osTypes: {},
loading: false,
domains: [],
accounts: [],
domainLoading: false,
domainid: null,
account: null,
architectureTypes: {}
@ -230,6 +217,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 +258,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 +308,17 @@ 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()
}
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
},
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,30 @@
<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"
@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"
: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 +111,31 @@
<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>
@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>
@change-option-value="handleAccountChange" />
</a-form-item>
<div :span="24" class="action-button">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@ -173,27 +153,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 +179,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 +228,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 +253,15 @@ 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
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
},
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,15 @@
<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"
@change-option-value="handleZoneChange" />
</a-form-item>
<a-form-item name="format" ref="format">
<template #label>
@ -83,23 +77,15 @@
<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"
@change-option="onChangeDiskOffering" />
</a-form-item>
<a-form-item name="checksum" ref="checksum">
<template #label>
@ -114,38 +100,31 @@
<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>
@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>
@change-option-value="handleAccountChange" />
</a-form-item>
<div :span="24" class="action-button">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@ -162,27 +141,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 +168,42 @@ 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 () {
if (!this.form.domainid) {
return {
listall: true,
showicon: true
}
}
return {
domainid: this.form.domainid,
showicon: true
}
}
},
created () {
this.initForm()
this.fetchData()
},
methods: {
initForm () {
@ -207,78 +218,29 @@ 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
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
},
handleAccountChange (accountName) {
this.form.account = accountName
this.account = accountName
},
handleSubmit (e) {
e.preventDefault()
if (this.loading) return

View File

@ -67,39 +67,31 @@
<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"
: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 +148,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 +174,6 @@ export default {
},
created () {
this.initForm()
if (['Domain', 'Local'].includes(this.form.scope)) {
this.fetchDomainData()
}
},
computed: {
isAdminOrDomainAdmin () {
@ -201,6 +187,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 +229,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 +261,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 +290,14 @@ export default {
}, 1)
},
handleScopeChange (e) {
if (['Domain', 'Local'].includes(this.form.scope)) {
this.fetchDomainData()
}
// Reset domain and account when scope changes
this.form.domainid = null
this.form.account = null
},
handleDomainChanged (domainid) {
if (domainid) {
this.fetchAccountData()
}
// Reset account when domain changes
this.form.account = null
// InfiniteScrollSelect will auto-reload accounts when apiParams changes
}
}
}

View File

@ -372,22 +372,15 @@
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"
@change-option-value="changeDomain" />
</a-form-item>
<a-form-item
@ -395,22 +388,15 @@
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"
@change-option-value="changeAccount" />
<span v-if="importForm.accountError" class="required">{{ $t('label.required') }}</span>
</a-form-item>
@ -419,22 +405,15 @@
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"
@change-option-value="changeProject" />
<span v-if="importForm.projectError" class="required">{{ $t('label.required') }}</span>
</a-form-item>
@ -480,6 +459,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 +467,8 @@ export default {
Breadcrumb,
Status,
SearchView,
ResourceIcon
ResourceIcon,
InfiniteScrollSelect
},
name: 'ManageVolumes',
data () {
@ -607,7 +588,6 @@ export default {
this.page.managed = parseInt(this.$route.query.managedpage || 1)
this.initForm()
this.fetchData()
this.fetchDomains()
},
computed: {
isPageAllowed () {
@ -629,6 +609,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 +980,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 +992,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