You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/04/22 09:13:18 UTC
[cloudstack-primate] branch master updated: compute: Fix VM
Deployment Wizard Issues (#273)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new 334be08 compute: Fix VM Deployment Wizard Issues (#273)
334be08 is described below
commit 334be08053df495f5f7534c6f5ebb6f1f02039a7
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Wed Apr 22 16:13:12 2020 +0700
compute: Fix VM Deployment Wizard Issues (#273)
Fixes #254
Pod, cluster, host should be hidden for normal and domain admin user
Listing of all items are not done by passing account/domainid, or zoneid (for example listing of networks, template, isos for zone; keypair, affiinity groups etc for account/domain)
Add a way to filter templates by featured, community, shared, mine?
Multiple listing of templates for the same zone? (multiple radios button)
Add button to add network that can open the add network popup or add a router-link?
User data not properly base64 encoded (check encoding?)
Add support for processing min/max cpu/ram based on type of compute offering selected and custom disk offering
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Co-authored-by: Hoang Nguyen <ho...@unitech.vn>
Co-authored-by: Abhishek Kumar <ab...@gmail.com>
Co-authored-by: Rohit Yadav <ro...@shapeblue.com>
---
src/components/header/ProjectMenu.vue | 4 +-
src/locales/en.json | 9 +-
src/views/AutogenView.vue | 2 +
src/views/compute/DeployVM.vue | 256 ++++++++++++++++---
.../compute/wizard/AffinityGroupSelection.vue | 15 ++
...eSelection.vue => ComputeOfferingSelection.vue} | 38 ++-
src/views/compute/wizard/ComputeSelection.vue | 278 ++++++++++++++-------
src/views/compute/wizard/DiskOfferingSelection.vue | 37 ++-
src/views/compute/wizard/DiskSizeSelection.vue | 47 +++-
src/views/compute/wizard/NetworkConfiguration.vue | 8 +-
src/views/compute/wizard/NetworkSelection.vue | 54 +++-
src/views/compute/wizard/SshKeyPairSelection.vue | 31 ++-
src/views/compute/wizard/TemplateIsoRadioGroup.vue | 24 +-
src/views/compute/wizard/TemplateIsoSelection.vue | 144 ++++++++++-
14 files changed, 768 insertions(+), 179 deletions(-)
diff --git a/src/components/header/ProjectMenu.vue b/src/components/header/ProjectMenu.vue
index 2c922d1..f3ea38b 100644
--- a/src/components/header/ProjectMenu.vue
+++ b/src/components/header/ProjectMenu.vue
@@ -67,9 +67,9 @@ export default {
if (json && json.listprojectsresponse && json.listprojectsresponse.project) {
this.projects.push(...json.listprojectsresponse.project)
}
- const currentProject = Vue.ls.get(CURRENT_PROJECT)
+ const currentProject = Vue.ls.get(CURRENT_PROJECT) || {}
for (var project of this.projects) {
- if (project.id === currentProject.id) {
+ if (project && currentProject && project.id === currentProject.id) {
this.setSelectedProject(project)
break
}
diff --git a/src/locales/en.json b/src/locales/en.json
index fad2146..285f182 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -1151,6 +1151,8 @@
"message.required.traffic.type": "Error in configuration! All required traffic types should be added and with multiple physical networks each network should have a label.",
"message.desc.primary.storage": "Each cluster must contain one or more primary storage servers, and we will add the first one now. Primary storage contains the disk volumes for all the VMs running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.",
"message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server, and we will add the first one now. Secondary storage stores VM templates, ISO images, and VM disk volume snapshots. This server must be available to all hosts in the zone.<br/><br/>Provide the IP address and exported path.",
+"message.error.required.input": "Please enter input",
+"message.error.invalid.range": "Please enter values from {min} to {max}",
"label.name": "Name",
"label.ipv4.dns1": "IPv4 DNS1",
"label.ipv4.dns2": "IPv4 DNS2",
@@ -1266,5 +1268,10 @@
"label.launch.zone": "Launch Zone",
"label.done": "Done",
"label.fix.errors": "Fix errors",
-"error.something.went.wrong.please.correct.the.following": "Something went wrong; please correct the following"
+"error.something.went.wrong.please.correct.the.following": "Something went wrong; please correct the following",
+"filter": "Filter",
+"featured": "Featured",
+"community": "Community",
+"selfexecutable": "Self",
+"sharedexecutable": "Shared"
}
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index 47b10af..07f471f 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -396,6 +396,8 @@ export default {
if (this.searchQuery !== '') {
if (this.apiName === 'listRoles') {
params.name = this.searchQuery
+ } else if (this.apiName === 'quotaEmailTemplateList') {
+ params.templatetype = this.searchQuery
} else {
params.keyword = this.searchQuery
}
diff --git a/src/views/compute/DeployVM.vue b/src/views/compute/DeployVM.vue
index f1a4c9e..d7b7eee 100644
--- a/src/views/compute/DeployVM.vue
+++ b/src/views/compute/DeployVM.vue
@@ -44,21 +44,27 @@
:loading="loading.zones"
></a-select>
</a-form-item>
- <a-form-item :label="this.$t('podId')">
+ <a-form-item
+ v-if="!isNormalAndDomainUser"
+ :label="this.$t('podId')">
<a-select
v-decorator="['podid']"
:options="podSelectOptions"
:loading="loading.pods"
></a-select>
</a-form-item>
- <a-form-item :label="this.$t('clusterid')">
+ <a-form-item
+ v-if="!isNormalAndDomainUser"
+ :label="this.$t('clusterid')">
<a-select
v-decorator="['clusterid']"
:options="clusterSelectOptions"
:loading="loading.clusters"
></a-select>
</a-form-item>
- <a-form-item :label="this.$t('hostId')">
+ <a-form-item
+ v-if="!isNormalAndDomainUser"
+ :label="this.$t('hostId')">
<a-select
v-decorator="['hostid']"
:options="hostSelectOptions"
@@ -101,10 +107,12 @@
:items="options.templates"
:selected="tabKey"
:loading="loading.templates"
+ :preFillContent="dataPreFill"
@update-template-iso="updateFieldValue"
></template-iso-selection>
<disk-size-selection
input-decorator="rootdisksize"
+ :preFillContent="dataPreFill"
@update-disk-size="updateFieldValue"/>
</p>
<p v-else>
@@ -113,8 +121,22 @@
:items="options.isos"
:selected="tabKey"
:loading="loading.isos"
+ :preFillContent="dataPreFill"
@update-template-iso="updateFieldValue"
></template-iso-selection>
+ <a-form-item :label="this.$t('hypervisor')">
+ <a-select
+ v-decorator="['hypervisor', {
+ initialValue: hypervisorSelectOptions && hypervisorSelectOptions.length > 0
+ ? hypervisorSelectOptions[0].value
+ : null,
+ rules: [{ required: true, message: 'Please select option' }]
+ }]"
+ :options="hypervisorSelectOptions"
+ @change="value => this.hypervisor = value"
+ >
+ </a-select>
+ </a-form-item>
</p>
</a-card>
<a-form-item class="form-item-hidden">
@@ -134,13 +156,42 @@
:status="zoneSelected ? 'process' : 'wait'">
<template slot="description">
<div v-if="zoneSelected">
- <compute-selection
+ <compute-offering-selection
:compute-items="options.serviceOfferings"
:value="serviceOffering ? serviceOffering.id : ''"
:loading="loading.serviceOfferings"
+ :preFillContent="dataPreFill"
@select-compute-item="($event) => updateComputeOffering($event)"
@handle-search-filter="($event) => handleSearchFilter('serviceOfferings', $event)"
- ></compute-selection>
+ ></compute-offering-selection>
+ <compute-selection
+ v-if="serviceOffering && serviceOffering.iscustomized"
+ cpunumber-input-decorator="cpunumber"
+ cpuspeed-input-decorator="cpuspeed"
+ memory-input-decorator="memory"
+ :preFillContent="dataPreFill"
+ :computeOfferingId="instanceConfig.computeofferingid"
+ :isConstrained="'serviceofferingdetails' in serviceOffering"
+ :minCpu="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.mincpunumber*1 : 1"
+ :maxCpu="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.maxcpunumber*1 : Number.MAX_SAFE_INTEGER"
+ :minMemory="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.minmemory*1 : 1"
+ :maxMemory="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.maxmemory*1 : Number.MAX_SAFE_INTEGER"
+ @update-compute-cpunumber="updateFieldValue"
+ @update-compute-cpuspeed="updateFieldValue"
+ @update-compute-memory="updateFieldValue" />
+ <span v-if="serviceOffering && serviceOffering.iscustomized">
+ <a-form-item class="form-item-hidden" >
+ <a-input v-decorator="['cpunumber']"/>
+ </a-form-item>
+ <a-form-item
+ class="form-item-hidden"
+ v-if="serviceOffering && !(serviceOffering.cpuspeed > 0)">
+ <a-input v-decorator="['cpuspeed']"/>
+ </a-form-item>
+ <a-form-item class="form-item-hidden">
+ <a-input v-decorator="['memory']"/>
+ </a-form-item>
+ </span>
</div>
</template>
</a-step>
@@ -153,12 +204,14 @@
:items="options.diskOfferings"
:value="diskOffering ? diskOffering.id : ''"
:loading="loading.diskOfferings"
+ :preFillContent="dataPreFill"
@select-disk-offering-item="($event) => updateDiskOffering($event)"
@handle-search-filter="($event) => handleSearchFilter('diskOfferings', $event)"
></disk-offering-selection>
<disk-size-selection
v-if="diskOffering && diskOffering.iscustomized"
input-decorator="size"
+ :preFillContent="dataPreFill"
@update-disk-size="updateFieldValue" />
<a-form-item class="form-item-hidden">
<a-input v-decorator="['size']"/>
@@ -175,6 +228,7 @@
:items="options.affinityGroups"
:value="affinityGroupIds"
:loading="loading.affinityGroups"
+ :preFillContent="dataPreFill"
@select-affinity-group-item="($event) => updateAffinityGroups($event)"
@handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"
></affinity-group-selection>
@@ -191,12 +245,14 @@
:value="networkOfferingIds"
:loading="loading.networks"
:zoneId="zoneId"
+ :preFillContent="dataPreFill"
@select-network-item="($event) => updateNetworks($event)"
@handle-search-filter="($event) => handleSearchFilter('networks', $event)"
></network-selection>
<network-configuration
v-if="networks.length > 0"
:items="networks"
+ :preFillContent="dataPreFill"
@update-network-config="($event) => updateNetworkConfig($event)"
@select-default-network-item="($event) => updateDefaultNetworks($event)"
></network-configuration>
@@ -212,6 +268,7 @@
:items="options.sshKeyPairs"
:value="sshKeyPair ? sshKeyPair.name : ''"
:loading="loading.sshKeyPairs"
+ :preFillContent="dataPreFill"
@select-ssh-key-pair-item="($event) => updateSshKeyPairs($event)"
@handle-search-filter="($event) => handleSearchFilter('sshKeyPairs', $event)"
/>
@@ -261,6 +318,7 @@ import { mixin, mixinDevice } from '@/utils/mixin.js'
import store from '@/store'
import InfoCard from '@/components/view/InfoCard'
+import ComputeOfferingSelection from './wizard/ComputeOfferingSelection'
import ComputeSelection from './wizard/ComputeSelection'
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
@@ -281,11 +339,16 @@ export default {
DiskSizeSelection,
DiskOfferingSelection,
InfoCard,
+ ComputeOfferingSelection,
ComputeSelection
},
props: {
visible: {
type: Boolean
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
mixins: [mixin, mixinDevice],
@@ -297,6 +360,7 @@ export default {
options: {
templates: [],
isos: [],
+ hypervisors: [],
serviceOfferings: [],
diskOfferings: [],
zones: [],
@@ -313,6 +377,7 @@ export default {
deploy: false,
templates: false,
isos: false,
+ hypervisors: false,
serviceOfferings: false,
diskOfferings: false,
affinityGroups: false,
@@ -324,9 +389,10 @@ export default {
hosts: false,
groups: false
},
- instanceConfig: [],
+ instanceConfig: {},
template: {},
iso: {},
+ hypervisor: '',
serviceOffering: {},
diskOffering: {},
affinityGroups: [],
@@ -368,10 +434,14 @@ export default {
tab: this.$t('ISOs')
}
],
- tabKey: 'templateid'
+ tabKey: 'templateid',
+ dataPreFill: {}
}
},
computed: {
+ isNormalAndDomainUser () {
+ return ['DomainAdmin', 'User'].includes(this.$store.getters.userInfo.roletype)
+ },
diskSize () {
const rootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 0)
const customDiskSize = _.get(this.instanceConfig, 'size', 0)
@@ -411,23 +481,33 @@ export default {
}
},
zones: {
- list: 'listZones'
+ list: 'listZones',
+ isLoad: true,
+ field: 'zoneid'
+ },
+ hypervisors: {
+ list: 'listHypervisors',
+ options: {
+ zoneid: _.get(this.zone, 'id')
+ },
+ field: 'hypervisor'
},
affinityGroups: {
list: 'listAffinityGroups',
options: {
page: 1,
pageSize: 10,
- keyword: undefined
+ keyword: undefined,
+ listall: false
}
},
sshKeyPairs: {
list: 'listSSHKeyPairs',
options: {
- zoneid: _.get(this.zone, 'id'),
page: 1,
pageSize: 10,
- keyword: undefined
+ keyword: undefined,
+ listall: false
}
},
networks: {
@@ -436,6 +516,8 @@ export default {
zoneid: _.get(this.zone, 'id'),
canusefordeploy: true,
projectid: store.getters.project.id,
+ domainid: store.getters.project.id ? null : store.getters.userInfo.domainid,
+ account: store.getters.project.id ? null : store.getters.userInfo.account,
page: 1,
pageSize: 10,
keyword: undefined
@@ -443,26 +525,37 @@ export default {
},
pods: {
list: 'listPods',
+ isLoad: !this.isNormalAndDomainUser,
options: {
zoneid: _.get(this.zone, 'id')
- }
+ },
+ field: 'podid'
},
clusters: {
list: 'listClusters',
+ isLoad: !this.isNormalAndDomainUser,
options: {
zoneid: _.get(this.zone, 'id')
- }
+ },
+ field: 'clusterid'
},
hosts: {
list: 'listHosts',
+ isLoad: !this.isNormalAndDomainUser,
options: {
zoneid: _.get(this.zone, 'id'),
state: 'Up',
type: 'Routing'
- }
+ },
+ field: 'hostid'
},
groups: {
- list: 'listInstanceGroups'
+ list: 'listInstanceGroups',
+ options: {
+ listall: false
+ },
+ isLoad: true,
+ field: 'group'
}
}
},
@@ -477,6 +570,14 @@ export default {
}
})
},
+ hypervisorSelectOptions () {
+ return this.options.hypervisors.map((hypervisor) => {
+ return {
+ label: hypervisor.name,
+ value: hypervisor.name
+ }
+ })
+ },
podSelectOptions () {
return this.options.pods.map((pod) => {
return {
@@ -527,6 +628,8 @@ export default {
instanceConfig (instanceConfig) {
this.template = _.find(this.options.templates, (option) => option.id === instanceConfig.templateid)
this.iso = _.find(this.options.isos, (option) => option.id === instanceConfig.isoid)
+ var hypervisorItem = _.find(this.options.hypervisors, (option) => option.name === instanceConfig.hypervisor)
+ this.hypervisor = hypervisorItem ? hypervisorItem.name : null
this.serviceOffering = _.find(this.options.serviceOfferings, (option) => option.id === instanceConfig.computeofferingid)
this.diskOffering = _.find(this.options.diskOfferings, (option) => option.id === instanceConfig.diskofferingid)
this.zone = _.find(this.options.zones, (option) => option.id === instanceConfig.zoneid)
@@ -551,6 +654,9 @@ export default {
this.vm.templatename = this.iso.displaytext
this.vm.ostypeid = this.iso.ostypeid
this.vm.ostypename = this.iso.ostypename
+ if (this.hypervisor) {
+ this.vm.hypervisor = this.hypervisor
+ }
}
if (this.serviceOffering) {
@@ -572,7 +678,7 @@ export default {
}
}
},
- beforeCreate () {
+ created () {
this.form = this.$form.createForm(this, {
onValuesChange: (props, fields) => {
if (fields.isoid) {
@@ -584,36 +690,63 @@ export default {
this.form.setFieldsValue({ isoid: null })
}
this.instanceConfig = { ...this.form.getFieldsValue(), ...fields }
- this.vm = this.instanceConfig
+ this.vm = Object.assign({}, this.instanceConfig)
}
})
this.form.getFieldDecorator('computeofferingid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('diskofferingid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('affinitygroupids', { initialValue: [], preserve: true })
- this.form.getFieldDecorator('isoid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true })
this.form.getFieldDecorator('keypair', { initialValue: undefined, preserve: true })
- this.apiParams = {}
- this.apiDeployVirtualMachine = this.$store.getters.apis.deployVirtualMachine || {}
- this.apiDeployVirtualMachine.params.forEach(param => {
- this.apiParams[param.name] = param
- })
+ this.form.getFieldDecorator('cpunumber', { initialValue: undefined, preserve: true })
+ this.form.getFieldDecorator('cpuSpeed', { initialValue: undefined, preserve: true })
+ this.form.getFieldDecorator('memory', { initialValue: undefined, preserve: true })
},
- created () {
+ mounted () {
+ this.dataPreFill = this.preFillContent && Object.keys(this.preFillContent).length > 0 ? this.preFillContent : {}
this.fetchData()
},
+ provide () {
+ return {
+ vmFetchTemplates: this.fetchAllTemplates,
+ vmFetchIsos: this.fetchAllIsos,
+ vmFetchNetworks: this.fetchNetwork
+ }
+ },
methods: {
+ fillValue (field) {
+ this.form.getFieldDecorator([field], { initialValue: this.dataPreFill[field] })
+ },
fetchData () {
- this.fetchOptions(this.params.zones, 'zones')
- this.fetchOptions(this.params.pods, 'pods')
- this.fetchOptions(this.params.clusters, 'clusters')
- this.fetchOptions(this.params.hosts, 'hosts')
- this.fetchOptions(this.params.groups, 'groups')
+ if (this.dataPreFill.zoneid) {
+ this.fetchDataByZone(this.dataPreFill.zoneid)
+ } else {
+ _.each(this.params, (param, name) => {
+ if (param.isLoad) {
+ this.fetchOptions(param, name)
+ }
+ })
+ }
+
this.fetchKeyboard()
Vue.nextTick().then(() => {
+ ['name', 'keyboard', 'userdata'].forEach(this.fillValue)
this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
})
},
+ async fetchDataByZone (zoneId) {
+ this.fillValue('zoneid')
+ this.options.zones = await this.fetchZones()
+ this.zoneId = zoneId
+ this.zoneSelected = true
+ this.tabKey = 'templateid'
+ await _.each(this.params, (param, name) => {
+ if (!('isLoad' in param) || param.isLoad) {
+ this.fetchOptions(param, name, ['zones'])
+ }
+ })
+ await this.fetchAllTemplates()
+ },
fetchKeyboard () {
const keyboardType = []
keyboardType.push({
@@ -643,6 +776,10 @@ export default {
this.$set(this.options, 'keyboards', keyboardType)
},
+ fetchNetwork () {
+ const param = this.params.networks
+ this.fetchOptions(param, 'networks')
+ },
resetData () {
this.vm = {}
this.zoneSelected = false
@@ -654,13 +791,13 @@ export default {
this.tabKey = 'templateid'
this.form.setFieldsValue({
templateid: value,
- isoid: undefined
+ isoid: null
})
} else if (name === 'isoid') {
this.tabKey = 'isoid'
this.form.setFieldsValue({
isoid: value,
- templateid: undefined
+ templateid: null
})
} else {
this.form.setFieldsValue({
@@ -729,8 +866,8 @@ export default {
deployVmData.hostid = values.hostid
deployVmData.group = values.group
deployVmData.keyboard = values.keyboard
- if (values.keyboard && values.keyboard.length > 0) {
- deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.keyboard)))
+ if (values.userdata && values.userdata.length > 0) {
+ deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.userdata)))
}
// step 2: select template/iso
if (this.tabKey === 'templateid') {
@@ -741,15 +878,29 @@ export default {
if (values.rootdisksize && values.rootdisksize > 0) {
deployVmData.rootdisksize = values.rootdisksize
}
+ if (values.hypervisor && values.hypervisor.length > 0) {
+ deployVmData.hypervisor = values.hypervisor
+ }
// step 3: select service offering
deployVmData.serviceofferingid = values.computeofferingid
+ if (values.cpunumber || values.cpuspeed || values.memory) {
+ if (values.cpunumber) {
+ deployVmData['details[0].cpuNumber'] = values.cpunumber
+ }
+ if (values.cpuspeed) {
+ deployVmData['details[0].cpuSpeed'] = values.cpuspeed
+ }
+ if (values.memory) {
+ deployVmData['details[0].memory'] = values.memory
+ }
+ }
// step 4: select disk offering
deployVmData.diskofferingid = values.diskofferingid
if (values.size) {
deployVmData.size = values.size
}
// step 5: select an affinity group
- deployVmData.affinitygroupids = values.affinitygroupids.join(',')
+ deployVmData.affinitygroupids = (values.affinitygroupids || []).join(',')
// step 6: select network
if (values.networkids && values.networkids.length > 0) {
for (let i = 0; i < values.networkids.length; i++) {
@@ -804,6 +955,20 @@ export default {
})
})
},
+ fetchZones () {
+ return new Promise((resolve) => {
+ this.loading.zones = true
+ const param = this.params.zones
+ api(param.list, { listall: true }).then(json => {
+ const zones = json.listzonesresponse.zone || []
+ resolve(zones)
+ }).catch(function (error) {
+ console.log(error.stack)
+ }).finally(() => {
+ this.loading.zones = false
+ })
+ })
+ },
fetchOptions (param, name, exclude) {
if (exclude && exclude.length > 0) {
if (exclude.includes(name)) {
@@ -814,7 +979,9 @@ export default {
param.loading = true
param.opts = []
const options = param.options || {}
- options.listall = true
+ if (!('listall' in options)) {
+ options.listall = true
+ }
api(param.list, options).then((response) => {
param.loading = false
_.map(response, (responseItem, responseKey) => {
@@ -833,6 +1000,9 @@ export default {
param.opts = response
this.options[name] = response
this.$forceUpdate()
+ if (param.field) {
+ this.fillValue(param.field)
+ }
})
})
}).catch(function (error) {
@@ -869,11 +1039,14 @@ export default {
})
})
},
- fetchAllTemplates () {
+ fetchAllTemplates (filterKey) {
const promises = []
this.options.templates = []
this.loading.templates = true
this.templateFilter.forEach((filter) => {
+ if (filterKey && filterKey !== filter) {
+ return true
+ }
promises.push(this.fetchTemplates(filter))
})
Promise.all(promises).then(response => {
@@ -888,11 +1061,14 @@ export default {
this.loading.templates = false
})
},
- fetchAllIsos () {
+ fetchAllIsos (filterKey) {
const promises = []
this.options.isos = []
this.loading.isos = true
this.isoFilter.forEach((filter) => {
+ if (filterKey && filterKey !== filter) {
+ return true
+ }
promises.push(this.fetchIsos(filter))
})
Promise.all(promises).then(response => {
@@ -908,7 +1084,9 @@ export default {
})
},
onSelectZoneId (value) {
+ this.dataPreFill = {}
this.zoneId = value
+ this.zone = _.find(this.options.zones, (option) => option.id === value)
this.zoneSelected = true
this.form.setFieldsValue({
clusterid: undefined,
@@ -919,7 +1097,9 @@ export default {
})
this.tabKey = 'templateid'
_.each(this.params, (param, name) => {
- this.fetchOptions(param, name, ['zones', 'groups'])
+ if (!('isLoad' in param) || param.isLoad) {
+ this.fetchOptions(param, name, ['zones', 'groups'])
+ }
})
this.fetchAllTemplates()
},
diff --git a/src/views/compute/wizard/AffinityGroupSelection.vue b/src/views/compute/wizard/AffinityGroupSelection.vue
index be14d6f..9c25355 100644
--- a/src/views/compute/wizard/AffinityGroupSelection.vue
+++ b/src/views/compute/wizard/AffinityGroupSelection.vue
@@ -53,6 +53,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -96,6 +100,17 @@ export default {
if (newValue && !_.isEqual(newValue, oldValue)) {
this.selectedRowKeys = newValue
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.affinitygroupids) {
+ this.selectedRowKeys = this.preFillContent.affinitygroupids
+ this.$emit('select-affinity-group-item', this.preFillContent.affinitygroupids)
+ } else {
+ this.selectedRowKeys = []
+ this.$emit('select-affinity-group-item', null)
+ }
+ }
}
},
methods: {
diff --git a/src/views/compute/wizard/ComputeSelection.vue b/src/views/compute/wizard/ComputeOfferingSelection.vue
similarity index 67%
copy from src/views/compute/wizard/ComputeSelection.vue
copy to src/views/compute/wizard/ComputeOfferingSelection.vue
index e44d17a..4403ee4 100644
--- a/src/views/compute/wizard/ComputeSelection.vue
+++ b/src/views/compute/wizard/ComputeOfferingSelection.vue
@@ -40,7 +40,7 @@
<script>
export default {
- name: 'ComputeSelection',
+ name: 'ComputeOfferingSelection',
props: {
computeItems: {
type: Array,
@@ -53,6 +53,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -88,11 +92,28 @@ export default {
},
tableSource () {
return this.computeItems.map((item) => {
+ var cpuNumberValue = item.cpunumber + ''
+ var cpuSpeedValue = (item.cpuspeed !== null && item.cpuspeed !== undefined && item.cpuspeed > 0) ? parseFloat(item.cpuspeed / 1000.0).toFixed(2) + '' : ''
+ var ramValue = item.memory + ''
+ if (item.iscustomized === true) {
+ cpuNumberValue = ''
+ ramValue = ''
+ if ('serviceofferingdetails' in item &&
+ 'mincpunumber' in item.serviceofferingdetails &&
+ 'maxcpunumber' in item.serviceofferingdetails) {
+ cpuNumberValue = item.serviceofferingdetails.mincpunumber + '-' + item.serviceofferingdetails.maxcpunumber
+ }
+ if ('serviceofferingdetails' in item &&
+ 'minmemory' in item.serviceofferingdetails &&
+ 'maxmemory' in item.serviceofferingdetails) {
+ ramValue = item.serviceofferingdetails.minmemory + '-' + item.serviceofferingdetails.maxmemory
+ }
+ }
return {
key: item.id,
name: item.name,
- cpu: `${item.cpunumber} CPU x ${parseFloat(item.cpuspeed / 1000.0).toFixed(2)} Ghz`,
- ram: `${item.memory} MB`
+ cpu: cpuNumberValue.length > 0 ? `${cpuNumberValue} CPU x ${cpuSpeedValue} Ghz` : '',
+ ram: ramValue.length > 0 ? `${ramValue} MB` : ''
}
})
},
@@ -109,6 +130,17 @@ export default {
if (newValue && newValue !== oldValue) {
this.selectedRowKeys = [newValue]
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.computeofferingid) {
+ this.selectedRowKeys = [this.preFillContent.computeofferingid]
+ this.$emit('select-compute-item', this.preFillContent.computeofferingid)
+ } else {
+ this.selectedRowKeys = []
+ this.$emit('select-compute-item', null)
+ }
+ }
}
},
methods: {
diff --git a/src/views/compute/wizard/ComputeSelection.vue b/src/views/compute/wizard/ComputeSelection.vue
index e44d17a..68ae442 100644
--- a/src/views/compute/wizard/ComputeSelection.vue
+++ b/src/views/compute/wizard/ComputeSelection.vue
@@ -16,122 +16,228 @@
// under the License.
<template>
- <div>
- <a-input-search
- style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
- placeholder="Search"
- v-model="filter"
- @search="handleSearch" />
- <a-table
- :columns="columns"
- :dataSource="tableSource"
- :pagination="{showSizeChanger: true}"
- :rowSelection="rowSelection"
- :loading="loading"
- size="middle"
- @change="handleTableChange"
- :scroll="{ y: 225 }"
- >
- <span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
- <span slot="ramTitle"><a-icon type="bulb" /> {{ $t('memory') }}</span>
- </a-table>
- </div>
+ <a-card>
+ <a-col>
+ <a-row>
+ <a-col :md="colContraned" :lg="colContraned">
+ <a-form-item
+ :label="this.$t('cpunumber')"
+ :validate-status="errors.cpu.status"
+ :help="errors.cpu.message">
+ <a-row :gutter="12">
+ <a-col :md="10" :lg="10" v-show="isConstrained">
+ <a-slider
+ :min="minCpu"
+ :max="maxCpu"
+ v-model="cpuNumberInputValue"
+ @change="($event) => updateComputeCpuNumber($event)"
+ />
+ </a-col>
+ <a-col :md="4" :lg="4">
+ <a-input-number
+ v-model="cpuNumberInputValue"
+ :formatter="value => `${value}`"
+ @change="($event) => updateComputeCpuNumber($event)"
+ />
+ </a-col>
+ </a-row>
+ </a-form-item>
+ </a-col>
+ <a-col :md="8" :lg="8" v-show="!isConstrained">
+ <a-form-item
+ :label="this.$t('cpuspeed')"
+ :validate-status="errors.cpuspeed.status"
+ :help="errors.cpuspeed.message">
+ <a-input-number
+ v-model="cpuSpeedInputValue"
+ @change="($event) => updateComputeCpuSpeed($event)"
+ />
+ </a-form-item>
+ </a-col>
+ <a-col :md="colContraned" :lg="colContraned">
+ <a-form-item
+ :label="this.$t('memory')"
+ :validate-status="errors.memory.status"
+ :help="errors.memory.message">
+ <a-row :gutter="12">
+ <a-col :md="10" :lg="10" v-show="isConstrained">
+ <a-slider
+ :min="minMemory"
+ :max="maxMemory"
+ v-model="memoryInputValue"
+ @change="($event) => updateComputeMemory($event)"
+ />
+ </a-col>
+ <a-col :md="4" :lg="4">
+ <a-input-number
+ v-model="memoryInputValue"
+ :formatter="value => `${value} MB`"
+ :parser="value => value.replace(' MB', '')"
+ @change="($event) => updateComputeMemory($event)"
+ />
+ </a-col>
+ </a-row>
+ </a-form-item>
+ </a-col>
+ </a-row>
+ </a-col>
+ </a-card>
</template>
<script>
export default {
name: 'ComputeSelection',
props: {
- computeItems: {
- type: Array,
- default: () => []
+ computeOfferingId: {
+ type: String,
+ default: () => ''
+ },
+ isConstrained: {
+ type: Boolean,
+ default: true
+ },
+ minCpu: {
+ type: Number,
+ default: 1
+ },
+ maxCpu: {
+ type: Number,
+ default: 2
+ },
+ minMemory: {
+ type: Number,
+ default: 1
},
- value: {
+ maxMemory: {
+ type: Number,
+ default: 256
+ },
+ cpunumberInputDecorator: {
type: String,
default: ''
},
- loading: {
- type: Boolean,
- default: false
+ cpuspeedInputDecorator: {
+ type: String,
+ default: ''
+ },
+ memoryInputDecorator: {
+ type: String,
+ default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
return {
- filter: '',
- columns: [
- {
- dataIndex: 'name',
- title: this.$t('serviceOfferingId'),
- width: '40%'
+ cpuNumberInputValue: 1,
+ cpuSpeedInputValue: 1,
+ memoryInputValue: 1,
+ errors: {
+ cpu: {
+ status: '',
+ message: ''
},
- {
- dataIndex: 'cpu',
- slots: { title: 'cpuTitle' },
- width: '30%'
+ cpuspeed: {
+ status: '',
+ message: ''
},
- {
- dataIndex: 'ram',
- slots: { title: 'ramTitle' },
- width: '30%'
+ memory: {
+ status: '',
+ message: ''
}
- ],
- selectedRowKeys: []
+ }
}
},
computed: {
- options () {
- return {
- page: 1,
- pageSize: 10,
- keyword: ''
- }
- },
- tableSource () {
- return this.computeItems.map((item) => {
- return {
- key: item.id,
- name: item.name,
- cpu: `${item.cpunumber} CPU x ${parseFloat(item.cpuspeed / 1000.0).toFixed(2)} Ghz`,
- ram: `${item.memory} MB`
- }
- })
- },
- rowSelection () {
- return {
- type: 'radio',
- selectedRowKeys: this.selectedRowKeys,
- onChange: this.onSelectRow
- }
+ colContraned () {
+ return this.isConstrained ? 12 : 8
}
},
watch: {
- value (newValue, oldValue) {
- if (newValue && newValue !== oldValue) {
- this.selectedRowKeys = [newValue]
+ computeOfferingId (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ this.cpuNumberInputValue = this.minCpu
+ this.memoryInputValue = this.minMemory
}
}
},
+ mounted () {
+ this.cpuNumberInputValue = this.minCpu
+ this.memoryInputValue = this.minMemory
+ this.fillValue()
+ },
methods: {
- onSelectRow (value) {
- this.selectedRowKeys = value
- this.$emit('select-compute-item', value[0])
+ fillValue () {
+ if (this.preFillContent.cpunumber) {
+ this.cpuNumberInputValue = this.preFillContent.cpunumber
+ }
+ if (this.preFillContent.cpuspeed) {
+ this.cpuSpeedInputValue = this.preFillContent.cpuspeed
+ }
+ if (this.preFillContent.memory) {
+ this.memoryInputValue = this.preFillContent.memory
+ }
+ },
+ updateComputeCpuNumber (value) {
+ if (!this.validateInput('cpu', value)) {
+ return
+ }
+ this.$emit('update-compute-cpunumber', this.cpunumberInputDecorator, value)
+ },
+ updateComputeCpuSpeed (value) {
+ this.$emit('update-compute-cpuspeed', this.cpuspeedInputDecorator, value)
},
- handleSearch (value) {
- this.filter = value
- this.options.keyword = this.filter
- this.$emit('handle-search-filter', this.options)
+ updateComputeMemory (value) {
+ if (!this.validateInput('memory', value)) {
+ return
+ }
+ this.$emit('update-compute-memory', this.memoryInputDecorator, value)
},
- handleTableChange (pagination) {
- this.options.page = pagination.current
- this.options.pageSize = pagination.pageSize
- this.$emit('handle-search-filter', this.options)
+ validateInput (input, value) {
+ this.errors[input].status = ''
+ this.errors[input].message = ''
+
+ if (value === null || value === undefined || value.length === 0) {
+ this.errors[input].status = 'error'
+ this.errors[input].message = this.$t('message.error.required.input')
+ return false
+ }
+
+ if (!this.isConstrained) {
+ return true
+ }
+
+ let min
+ let max
+
+ switch (input) {
+ case 'cpu':
+ min = this.minCpu
+ max = this.maxCpu
+ break
+ case 'memory':
+ min = this.minMemory
+ max = this.maxMemory
+ break
+ }
+
+ if (!this.checkValidRange(value, min, max)) {
+ this.errors[input].status = 'error'
+ this.errors[input].message = this.$t('message.error.invalid.range', { min: min, max: max })
+ return false
+ }
+
+ return true
+ },
+ checkValidRange (value, min, max) {
+ if (value < min || value > max) {
+ return false
+ }
+
+ return true
}
}
}
</script>
-
-<style lang="less" scoped>
- .ant-table-wrapper {
- margin: 2rem 0;
- }
-</style>
diff --git a/src/views/compute/wizard/DiskOfferingSelection.vue b/src/views/compute/wizard/DiskOfferingSelection.vue
index 33d94ac..78b06ef 100644
--- a/src/views/compute/wizard/DiskOfferingSelection.vue
+++ b/src/views/compute/wizard/DiskOfferingSelection.vue
@@ -64,6 +64,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -93,15 +97,7 @@ export default {
}
},
created () {
- this.dataItems = []
- this.dataItems.push({
- id: '0',
- name: this.$t('noselect'),
- diskSize: undefined,
- miniops: undefined,
- maxiops: undefined,
- isCustomized: undefined
- })
+ this.initDataItem()
},
computed: {
options () {
@@ -139,11 +135,34 @@ export default {
},
items (newData, oldData) {
if (newData && newData.length > 0) {
+ this.initDataItem()
this.dataItems = this.dataItems.concat(newData)
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.diskofferingid) {
+ this.selectedRowKeys = [this.preFillContent.diskofferingid]
+ this.$emit('select-disk-offering-item', this.preFillContent.diskofferingid)
+ } else {
+ this.selectedRowKeys = ['0']
+ this.$emit('select-disk-offering-item', '0')
+ }
+ }
}
},
methods: {
+ initDataItem () {
+ this.dataItems = []
+ this.dataItems.push({
+ id: '0',
+ name: this.$t('noselect'),
+ diskSize: undefined,
+ miniops: undefined,
+ maxiops: undefined,
+ isCustomized: undefined
+ })
+ },
onSelectRow (value) {
this.selectedRowKeys = value
this.$emit('select-disk-offering-item', value[0])
diff --git a/src/views/compute/wizard/DiskSizeSelection.vue b/src/views/compute/wizard/DiskSizeSelection.vue
index bec025a..684cb18 100644
--- a/src/views/compute/wizard/DiskSizeSelection.vue
+++ b/src/views/compute/wizard/DiskSizeSelection.vue
@@ -16,22 +16,26 @@
// under the License.
<template>
- <a-form-item :label="this.$t('diskSize')">
- <a-row>
- <a-col :span="10">
+ <a-form-item
+ :label="this.$t('diskSize')"
+ class="form-item">
+ <a-row :gutter="12">
+ <a-col :md="10" :lg="10">
<a-slider
:min="0"
:max="1024"
v-model="inputValue"
- @change="($event) => updateDickSize($event)"
+ @change="($event) => updateDiskSize($event)"
/>
</a-col>
- <a-col :span="4">
- <a-input-number
- v-model="inputValue"
- :formatter="value => `${value} GB`"
- :parser="value => value.replace(' GB', '')"
- />
+ <a-col :md="4" :lg="4">
+ <span style="display: inline-flex">
+ <a-input-number
+ v-model="inputValue"
+ @change="($event) => updateDiskSize($event)"
+ />
+ <span style="padding-top: 6px">GB</span>
+ </span>
</a-col>
</a-row>
</a-form-item>
@@ -44,6 +48,10 @@ export default {
inputDecorator: {
type: String,
default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -51,14 +59,27 @@ export default {
inputValue: 0
}
},
+ mounted () {
+ this.fillValue()
+ },
methods: {
- updateDickSize (value) {
+ fillValue () {
+ if (this.inputDecorator === 'rootdisksize') {
+ this.inputValue = this.preFillContent.rootdisksize ? this.preFillContent.rootdisksize : 0
+ } else if (this.inputDecorator === 'size') {
+ this.inputValue = this.preFillContent.size ? this.preFillContent.size : 0
+ }
+ this.$emit('update-disk-size', this.inputDecorator, this.inputValue)
+ },
+ updateDiskSize (value) {
this.$emit('update-disk-size', this.inputDecorator, value)
}
}
}
</script>
-<style scoped>
-
+<style scoped lang="less">
+ .form-item {
+ margin: 0 5px;
+ }
</style>
diff --git a/src/views/compute/wizard/NetworkConfiguration.vue b/src/views/compute/wizard/NetworkConfiguration.vue
index c53ed19..af20568 100644
--- a/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/src/views/compute/wizard/NetworkConfiguration.vue
@@ -28,13 +28,13 @@
<template slot="ipAddress" slot-scope="text, record">
<a-input
style="width: 150px;"
- :placeholder="$t('ipAddress')"
+ :placeholder="$t('ipaddress')"
@change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)" />
</template>
<template slot="macAddress" slot-scope="text, record">
<a-input
style="width: 150px;"
- :placeholder="$t('macAddress')"
+ :placeholder="$t('macaddress')"
@change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)" />
</template>
</a-table>
@@ -51,6 +51,10 @@ export default {
value: {
type: String,
default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
diff --git a/src/views/compute/wizard/NetworkSelection.vue b/src/views/compute/wizard/NetworkSelection.vue
index 21151f2..74678b0 100644
--- a/src/views/compute/wizard/NetworkSelection.vue
+++ b/src/views/compute/wizard/NetworkSelection.vue
@@ -22,6 +22,13 @@
placeholder="Search"
v-model="filter"
@search="handleSearch" />
+ <a-tooltip
+ arrowPointAtCenter
+ placement="bottomRight">
+ <template slot="title">
+ {{ $t('addNewNetworks') }}
+ </template>
+ </a-tooltip>
<a-table
:loading="loading"
:columns="columns"
@@ -70,6 +77,14 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ zoneId: {
+ type: String,
+ default: () => ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -77,7 +92,11 @@ export default {
filter: '',
selectedRowKeys: [],
vpcs: [],
- filteredInfo: null
+ filteredInfo: null,
+ networkOffering: {
+ loading: false,
+ opts: []
+ }
}
},
computed: {
@@ -147,8 +166,22 @@ export default {
if (newValue && !_.isEqual(newValue, oldValue)) {
this.selectedRowKeys = newValue
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.networkids) {
+ this.selectedRowKeys = this.preFillContent.networkids
+ this.$emit('select-network-item', this.preFillContent.networkids)
+ } else {
+ this.selectedRowKeys = []
+ this.$emit('select-network-item', null)
+ }
+ }
}
},
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ },
created () {
api('listVPCs', {
projectid: store.getters.project.id
@@ -156,6 +189,7 @@ export default {
this.vpcs = _.get(response, 'listvpcsresponse.vpc')
})
},
+ inject: ['vmFetchNetworks'],
methods: {
getDetails (network) {
return [
@@ -178,6 +212,24 @@ export default {
this.options.page = pagination.current
this.options.pageSize = pagination.pageSize
this.$emit('handle-search-filter', this.options)
+ },
+ listNetworkOfferings () {
+ return new Promise((resolve, reject) => {
+ const args = {}
+ args.forvpc = false
+ args.zoneid = this.zoneId
+ args.guestiptype = 'Isolated'
+ args.supportedServices = 'SourceNat'
+ args.specifyvlan = false
+ args.state = 'Enabled'
+
+ api('listNetworkOfferings', args).then(json => {
+ const listNetworkOfferings = json.listnetworkofferingsresponse.networkoffering || []
+ resolve(listNetworkOfferings)
+ }).catch(error => {
+ resolve(error)
+ })
+ })
}
}
}
diff --git a/src/views/compute/wizard/SshKeyPairSelection.vue b/src/views/compute/wizard/SshKeyPairSelection.vue
index 420f406..f443cfc 100644
--- a/src/views/compute/wizard/SshKeyPairSelection.vue
+++ b/src/views/compute/wizard/SshKeyPairSelection.vue
@@ -53,6 +53,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -80,12 +84,7 @@ export default {
}
},
created () {
- this.dataItems = []
- this.dataItems.push({
- name: this.$t('noselect'),
- account: '-',
- domain: '-'
- })
+ this.initDataItem()
},
computed: {
options () {
@@ -121,11 +120,31 @@ export default {
},
items (newData, oldData) {
if (newData && newData.length > 0) {
+ this.initDataItem()
this.dataItems = this.dataItems.concat(newData)
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.keypair) {
+ this.selectedRowKeys = [this.preFillContent.keypair]
+ this.$emit('select-ssh-key-pair-item', this.preFillContent.keypair)
+ } else {
+ this.selectedRowKeys = [this.$t('noselect')]
+ this.$emit('select-ssh-key-pair-item', this.$t('noselect'))
+ }
+ }
}
},
methods: {
+ initDataItem () {
+ this.dataItems = []
+ this.dataItems.push({
+ name: this.$t('noselect'),
+ account: '-',
+ domain: '-'
+ })
+ },
onSelectRow (value) {
this.selectedRowKeys = value
this.$emit('select-ssh-key-pair-item', value[0])
diff --git a/src/views/compute/wizard/TemplateIsoRadioGroup.vue b/src/views/compute/wizard/TemplateIsoRadioGroup.vue
index 594b2d0..c01e753 100644
--- a/src/views/compute/wizard/TemplateIsoRadioGroup.vue
+++ b/src/views/compute/wizard/TemplateIsoRadioGroup.vue
@@ -81,6 +81,14 @@ export default {
itemCount: {
type: Number,
default: 0
+ },
+ osType: {
+ type: String,
+ default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -90,16 +98,14 @@ export default {
pageSize: 10
}
},
- created () {
- this.value = this.selected
- this.$emit('emit-update-template-iso', this.inputDecorator, this.value)
- },
- watch: {
- inputDecorator (value) {
- if (value === 'templateid') {
- this.value = this.selected
- }
+ mounted () {
+ if (this.inputDecorator === 'templateid') {
+ this.value = !this.preFillContent.templateid ? this.selected : this.preFillContent.templateid
+ } else {
+ this.value = !this.preFillContent.isoid ? this.selected : this.preFillContent.isoid
}
+
+ this.$emit('emit-update-template-iso', this.inputDecorator, this.value)
},
computed: {
pagination () {
diff --git a/src/views/compute/wizard/TemplateIsoSelection.vue b/src/views/compute/wizard/TemplateIsoSelection.vue
index b26f893..7edef3c 100644
--- a/src/views/compute/wizard/TemplateIsoSelection.vue
+++ b/src/views/compute/wizard/TemplateIsoSelection.vue
@@ -17,26 +17,74 @@
<template>
<div>
- <a-input-search
- class="search-input"
- placeholder="Search"
- v-model="filter"
- @search="filterDataSource"/>
+ <span class="filter-group">
+ <a-input-search
+ class="search-input"
+ placeholder="Search"
+ v-model="filter"
+ @search="filterDataSource">
+ <a-popover
+ placement="bottomRight"
+ slot="addonAfter"
+ trigger="click"
+ v-model="visibleFilter">
+ <template slot="content">
+ <a-form
+ style="width: 170px"
+ :form="form"
+ layout="vertical"
+ @submit="handleSubmit">
+ <a-form-item :label="$t('filter')">
+ <a-select
+ allowClear
+ v-decorator="['filter']">
+ <a-select-option
+ v-for="(opt) in filterOpts"
+ :key="opt.id">{{ $t(opt.name) }}</a-select-option>
+ </a-select>
+ </a-form-item>
+ <div class="filter-group-button">
+ <a-button
+ class="filter-group-button-clear"
+ type="default"
+ size="small"
+ icon="stop"
+ @click="onClear">Clear</a-button>
+ <a-button
+ class="filter-group-button-search"
+ type="primary"
+ size="small"
+ icon="search"
+ @click="handleSubmit">Search</a-button>
+ </div>
+ </a-form>
+ </template>
+ <a-button
+ class="filter-group-button"
+ icon="filter"
+ size="small"/>
+ </a-popover>
+ </a-input-search>
+ </span>
<a-spin :spinning="loading">
<a-tabs
- tabPosition="top"
:animated="false"
- :defaultActiveKey="Object.keys(dataSource)[0]">
+ :defaultActiveKey="Object.keys(dataSource)[0]"
+ tabPosition="top"
+ v-model="osType"
+ @change="changeOsName">
<a-tab-pane v-for="(osList, osName) in dataSource" :key="osName">
<span slot="tab">
<os-logo :os-name="osName"></os-logo>
</span>
<TemplateIsoRadioGroup
- :osType="osName"
+ v-if="osType===osName"
+ :osType="osType"
:osList="dataSource[osName]"
:input-decorator="inputDecorator"
:selected="checkedValue"
:itemCount="itemCount[osName]"
+ :preFillContent="preFillContent"
@handle-filter-tag="filterDataSource"
@emit-update-template-iso="updateTemplateIso"
></TemplateIsoRadioGroup>
@@ -72,6 +120,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -80,7 +132,22 @@ export default {
filteredItems: this.items,
checkedValue: '',
dataSource: {},
- itemCount: {}
+ itemCount: {},
+ visibleFilter: false,
+ filterOpts: [{
+ id: 'featured',
+ name: 'featured'
+ }, {
+ id: 'community',
+ name: 'community'
+ }, {
+ id: 'selfexecutable',
+ name: 'selfexecutable'
+ }, {
+ id: 'sharedexecutable',
+ name: 'sharedexecutable'
+ }],
+ osType: ''
}
},
watch: {
@@ -92,6 +159,7 @@ export default {
this.checkedValue = items[0].id
}
this.dataSource = this.mappingDataSource()
+ this.osType = Object.keys(this.dataSource)[0]
},
inputDecorator (newValue, oldValue) {
if (newValue !== oldValue) {
@@ -99,6 +167,10 @@ export default {
}
}
},
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ },
+ inject: ['vmFetchTemplates', 'vmFetchIsos'],
methods: {
mappingDataSource () {
let mappedItems = {}
@@ -124,6 +196,7 @@ export default {
return mappedItems
},
updateTemplateIso (name, id) {
+ this.checkedValue = id
this.$emit('update-template-iso', name, id)
},
filterDataSource (strQuery) {
@@ -164,6 +237,31 @@ export default {
}
return arrResult
+ },
+ handleSubmit (e) {
+ e.preventDefault()
+ this.form.validateFields((err, values) => {
+ if (err) {
+ return
+ }
+ if (this.inputDecorator === 'template') {
+ this.vmFetchTemplates(values.filter)
+ } else {
+ this.vmFetchIsos(values.filter)
+ }
+ })
+ },
+ onClear () {
+ const field = { filter: undefined }
+ this.form.setFieldsValue(field)
+ if (this.inputDecorator === 'template') {
+ this.vmFetchTemplates()
+ } else {
+ this.vmFetchIsos()
+ }
+ },
+ changeOsName (value) {
+ this.osType = value
}
}
}
@@ -188,4 +286,32 @@ export default {
/deep/.ant-tabs-nav-scroll {
min-height: 45px;
}
+
+ .filter-group {
+ /deep/.ant-input-group-addon {
+ padding: 0 5px;
+ }
+
+ &-button {
+ background: inherit;
+ border: 0;
+ padding: 0;
+ }
+
+ &-button {
+ position: relative;
+ display: block;
+ min-height: 25px;
+
+ &-clear {
+ position: absolute;
+ left: 0;
+ }
+
+ &-search {
+ position: absolute;
+ right: 0;
+ }
+ }
+ }
</style>