mirror of https://github.com/apache/cloudstack.git
compute: VM deployment wizard (#146)
A mostly functional vm deployment wizard. Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
ba6dec532b
commit
373fc23c45
|
|
@ -1103,7 +1103,7 @@
|
|||
"sshKeyPairs": "SSH keypairs",
|
||||
"wednesday": "Wednesday",
|
||||
"noselect": "No thanks",
|
||||
"groupname": "Add to group",
|
||||
"group": "Group",
|
||||
"keyboard": "Keyboard language",
|
||||
"userdata": "Userdata",
|
||||
"label.back": "Back",
|
||||
|
|
|
|||
|
|
@ -42,9 +42,8 @@ export const deviceEnquire = function (callback) {
|
|||
}
|
||||
}
|
||||
|
||||
// screen and (max-width: 1087.99px)
|
||||
enquireJs
|
||||
.register('screen and (max-width: 576px)', matchMobile)
|
||||
.register('screen and (min-width: 576px) and (max-width: 1280px)', matchTablet)
|
||||
.register('screen and (min-width: 1281px)', matchDesktop)
|
||||
.register('screen and (max-width: 800px)', matchMobile)
|
||||
.register('screen and (min-width: 800px) and (max-width: 1366px)', matchTablet)
|
||||
.register('screen and (min-width: 1367px)', matchDesktop)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@
|
|||
:pageSize="pageSize"
|
||||
:total="itemCount"
|
||||
:showTotal="total => `Total ${total} items`"
|
||||
:pageSizeOptions="['10', '20', '40', '80', '100']"
|
||||
:pageSizeOptions="['10', '20', '40', '80', '100', '500']"
|
||||
@change="changePage"
|
||||
@showSizeChange="changePageSize"
|
||||
showSizeChanger
|
||||
|
|
|
|||
|
|
@ -25,131 +25,216 @@
|
|||
@submit="handleSubmit"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-collapse
|
||||
:accordion="false"
|
||||
:bordered="false"
|
||||
defaultActiveKey="basic"
|
||||
>
|
||||
<a-collapse-panel :header="this.$t('BasicSetup')" key="basic">
|
||||
<a-form-item :label="this.$t('name')">
|
||||
<a-input
|
||||
v-decorator="['name']"
|
||||
:placeholder="this.$t('vm.name.description')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="this.$t('zoneid')">
|
||||
<a-select
|
||||
v-decorator="['zoneid', {
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
:placeholder="this.$t('vm.zone.description')"
|
||||
:options="zoneSelectOptions"
|
||||
@change="onSelectZoneId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('templateIso')" key="templates-isos">
|
||||
<a-collapse
|
||||
:accordion="true"
|
||||
defaultActiveKey="templates"
|
||||
@change="onTemplatesIsosCollapseChange"
|
||||
>
|
||||
<a-collapse-panel :header="this.$t('Templates')" key="templates">
|
||||
<template-iso-selection
|
||||
input-decorator="templateid"
|
||||
:items="options.templates"
|
||||
></template-iso-selection>
|
||||
<a-steps direction="vertical" size="small">
|
||||
<a-step :title="this.$t('details')" status="process">
|
||||
<template slot="description">
|
||||
<div style="margin-top: 15px">
|
||||
<a-form-item :label="this.$t('name')">
|
||||
<a-input
|
||||
v-decorator="['name']"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('zoneid')">
|
||||
<a-select
|
||||
v-decorator="['zoneid', {
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
:options="zoneSelectOptions"
|
||||
@change="onSelectZoneId"
|
||||
:loading="loading.zones"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :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-select
|
||||
v-decorator="['clusterid']"
|
||||
:options="clusterSelectOptions"
|
||||
:loading="loading.clusters"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('hostId')">
|
||||
<a-select
|
||||
v-decorator="['hostid']"
|
||||
:options="hostSelectOptions"
|
||||
:loading="loading.hosts"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('group')">
|
||||
<a-select
|
||||
v-decorator="['group']"
|
||||
:options="groupsSelectOptions"
|
||||
:loading="loading.groups"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('keyboard')">
|
||||
<a-select
|
||||
v-decorator="['keyboard']"
|
||||
:options="keyboardSelectOptions"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('userdata')">
|
||||
<a-textarea
|
||||
v-decorator="['userdata']">
|
||||
</a-textarea>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="this.$t('templateIso')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected" style="margin-top: 15px">
|
||||
<a-card
|
||||
:tabList="tabList"
|
||||
:activeTabKey="tabKey"
|
||||
@tabChange="key => onTabChange(key, 'tabKey')">
|
||||
<p v-if="tabKey === 'templateid'">
|
||||
<template-iso-selection
|
||||
input-decorator="templateid"
|
||||
:items="options.templates"
|
||||
:selected="tabKey"
|
||||
:loading="loading.templates"
|
||||
@update-template-iso="updateFieldValue"
|
||||
></template-iso-selection>
|
||||
<disk-size-selection
|
||||
input-decorator="rootdisksize"
|
||||
@update-disk-size="updateFieldValue"/>
|
||||
</p>
|
||||
<p v-else>
|
||||
<template-iso-selection
|
||||
input-decorator="isoid"
|
||||
:items="options.isos"
|
||||
:selected="tabKey"
|
||||
:loading="loading.isos"
|
||||
@update-template-iso="updateFieldValue"
|
||||
></template-iso-selection>
|
||||
</p>
|
||||
</a-card>
|
||||
<a-form-item class="form-item-hidden">
|
||||
<a-input v-decorator="['templateid']"/>
|
||||
</a-form-item>
|
||||
<a-form-item class="form-item-hidden">
|
||||
<a-input v-decorator="['isoid']"/>
|
||||
</a-form-item>
|
||||
<a-form-item class="form-item-hidden">
|
||||
<a-input v-decorator="['rootdisksize']"/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="this.$t('serviceOfferingId')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected">
|
||||
<compute-selection
|
||||
:compute-items="options.serviceOfferings"
|
||||
:value="serviceOffering ? serviceOffering.id : ''"
|
||||
:loading="loading.serviceOfferings"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('serviceOfferings', $event)"
|
||||
></compute-selection>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="this.$t('diskofferingid')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected">
|
||||
<disk-offering-selection
|
||||
:items="options.diskOfferings"
|
||||
:value="diskOffering ? diskOffering.id : ''"
|
||||
:loading="loading.diskOfferings"
|
||||
@select-disk-offering-item="($event) => updateDiskOffering($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('diskOfferings', $event)"
|
||||
></disk-offering-selection>
|
||||
<disk-size-selection
|
||||
input-decorator="rootdisksize"
|
||||
></disk-size-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('ISOs')" key="isos">
|
||||
<template-iso-selection
|
||||
input-decorator="isoid"
|
||||
:items="options.isos"
|
||||
></template-iso-selection>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('serviceOfferingId')" key="compute">
|
||||
<compute-selection
|
||||
:compute-items="options.serviceOfferings"
|
||||
:value="serviceOffering ? serviceOffering.id : ''"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
></compute-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('diskOfferingId')" key="disk">
|
||||
<disk-offering-selection
|
||||
:items="options.diskOfferings"
|
||||
:value="diskOffering ? diskOffering.id : ''"
|
||||
@select-disk-offering-item="($event) => updateDiskOffering($event)"
|
||||
></disk-offering-selection>
|
||||
<disk-size-selection
|
||||
v-if="diskOffering && diskOffering.iscustomized"
|
||||
input-decorator="size"
|
||||
></disk-size-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('Affinity Groups')" key="affinity">
|
||||
<affinity-group-selection
|
||||
:items="options.affinityGroups"
|
||||
:value="affinityGroupIds"
|
||||
@select-affinity-group-item="($event) => updateAffinityGroups($event)"
|
||||
></affinity-group-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('networks')" key="networks">
|
||||
<a-collapse
|
||||
:accordion="false"
|
||||
>
|
||||
<a-collapse-panel
|
||||
:header="$t('existingNetworks')"
|
||||
>
|
||||
v-if="diskOffering && diskOffering.iscustomized"
|
||||
input-decorator="size"
|
||||
@update-disk-size="updateFieldValue" />
|
||||
<a-form-item class="form-item-hidden">
|
||||
<a-input v-decorator="['size']"/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="this.$t('Affinity Groups')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected">
|
||||
<affinity-group-selection
|
||||
:items="options.affinityGroups"
|
||||
:value="affinityGroupIds"
|
||||
:loading="loading.affinityGroups"
|
||||
@select-affinity-group-item="($event) => updateAffinityGroups($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"
|
||||
></affinity-group-selection>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="this.$t('networks')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected">
|
||||
<network-selection
|
||||
:items="options.networks"
|
||||
:value="networkOfferingIds"
|
||||
:loading="loading.networks"
|
||||
:zoneId="zoneId"
|
||||
@select-network-item="($event) => updateNetworks($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('networks', $event)"
|
||||
></network-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel
|
||||
:header="$t('addNewNetworks')"
|
||||
>
|
||||
<network-creation></network-creation>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
||||
<network-configuration
|
||||
v-if="networks.length > 0"
|
||||
:items="networks"
|
||||
></network-configuration>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('sshKeyPairs')" key="sshKeyPairs">
|
||||
<ssh-key-pair-selection
|
||||
:items="options.sshKeyPairs"
|
||||
:value="sshKeyPair ? sshKeyPair.name : ''"
|
||||
@select-ssh-key-pair-item="($event) => updateSshKeyPairs($event)"
|
||||
></ssh-key-pair-selection>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
||||
<network-configuration
|
||||
v-if="networks.length > 0"
|
||||
:items="networks"
|
||||
@update-network-config="($event) => updateNetworkConfig($event)"
|
||||
@select-default-network-item="($event) => updateDefaultNetworks($event)"
|
||||
></network-configuration>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="this.$t('sshKeyPairs')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected">
|
||||
<ssh-key-pair-selection
|
||||
:items="options.sshKeyPairs"
|
||||
:value="sshKeyPair ? sshKeyPair.name : ''"
|
||||
:loading="loading.sshKeyPairs"
|
||||
@select-ssh-key-pair-item="($event) => updateSshKeyPairs($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('sshKeyPairs', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
</a-steps>
|
||||
<div class="card-footer">
|
||||
<!-- ToDo extract as component -->
|
||||
<a-button @click="() => this.$router.back()">{{ this.$t('cancel') }}</a-button>
|
||||
<a-button type="primary" @click="handleSubmit">{{ this.$t('submit') }}</a-button>
|
||||
<a-button @click="() => this.$router.back()" :loading="loading.deploy">
|
||||
{{ this.$t('cancel') }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleSubmit" :loading="loading.deploy">
|
||||
<a-icon type="rocket" />
|
||||
{{ this.$t('Launch VM') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="7" v-if="!isMobile()">
|
||||
<a-affix :offsetTop="75">
|
||||
<info-card :resource="vm" :title="this.$t('yourInstance')">
|
||||
<info-card class="vm-info-card" :resource="vm" :title="this.$t('yourInstance')">
|
||||
<!-- ToDo: Refactor this, maybe move everything to the info-card component -->
|
||||
<div slot="details" v-if="diskSize" style="margin-bottom: 12px;">
|
||||
<a-icon type="hdd"></a-icon>
|
||||
|
|
@ -183,14 +268,12 @@ import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
|
|||
import AffinityGroupSelection from '@views/compute/wizard/AffinityGroupSelection'
|
||||
import NetworkSelection from '@views/compute/wizard/NetworkSelection'
|
||||
import NetworkConfiguration from '@views/compute/wizard/NetworkConfiguration'
|
||||
import NetworkCreation from '@views/compute/wizard/NetworksCreation'
|
||||
import SshKeyPairSelection from '@views/compute/wizard/SshKeyPairSelection'
|
||||
|
||||
export default {
|
||||
name: 'Wizard',
|
||||
components: {
|
||||
SshKeyPairSelection,
|
||||
NetworkCreation,
|
||||
NetworkConfiguration,
|
||||
NetworkSelection,
|
||||
AffinityGroupSelection,
|
||||
|
|
@ -208,6 +291,8 @@ export default {
|
|||
mixins: [mixin, mixinDevice],
|
||||
data () {
|
||||
return {
|
||||
zoneId: '',
|
||||
zoneSelected: false,
|
||||
vm: {},
|
||||
options: {
|
||||
templates: [],
|
||||
|
|
@ -217,7 +302,27 @@ export default {
|
|||
zones: [],
|
||||
affinityGroups: [],
|
||||
networks: [],
|
||||
sshKeyPairs: []
|
||||
sshKeyPairs: [],
|
||||
pods: [],
|
||||
clusters: [],
|
||||
hosts: [],
|
||||
groups: [],
|
||||
keyboards: []
|
||||
},
|
||||
loading: {
|
||||
deploy: false,
|
||||
templates: false,
|
||||
isos: false,
|
||||
serviceOfferings: false,
|
||||
diskOfferings: false,
|
||||
affinityGroups: false,
|
||||
networks: false,
|
||||
sshKeyPairs: false,
|
||||
zones: false,
|
||||
pods: false,
|
||||
clusters: false,
|
||||
hosts: false,
|
||||
groups: false
|
||||
},
|
||||
instanceConfig: [],
|
||||
template: {},
|
||||
|
|
@ -226,13 +331,44 @@ export default {
|
|||
diskOffering: {},
|
||||
affinityGroups: [],
|
||||
networks: [],
|
||||
networksAdd: [],
|
||||
zone: {},
|
||||
sshKeyPair: {},
|
||||
isoFilter: [
|
||||
'executable',
|
||||
templateFilter: [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
]
|
||||
],
|
||||
isoFilter: [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
],
|
||||
steps: {
|
||||
BASIC: 0,
|
||||
TEMPLATE_ISO: 1,
|
||||
COMPUTE: 2,
|
||||
DISK_OFFERING: 3,
|
||||
AFFINITY_GROUP: 4,
|
||||
NETWORK: 5,
|
||||
SSH_KEY_PAIR: 6
|
||||
},
|
||||
initDataConfig: {},
|
||||
defaultNetwork: '',
|
||||
networkConfig: [],
|
||||
tabList: [
|
||||
{
|
||||
key: 'templateid',
|
||||
tab: this.$t('Templates')
|
||||
},
|
||||
{
|
||||
key: 'isoid',
|
||||
tab: this.$t('ISOs')
|
||||
}
|
||||
],
|
||||
tabKey: 'templateid'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -255,35 +391,78 @@ export default {
|
|||
},
|
||||
params () {
|
||||
return {
|
||||
templates: {
|
||||
list: 'listTemplates',
|
||||
serviceOfferings: {
|
||||
list: 'listServiceOfferings',
|
||||
options: {
|
||||
templatefilter: 'executable',
|
||||
zoneid: _.get(this.zone, 'id')
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
issystem: false,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: undefined
|
||||
}
|
||||
},
|
||||
serviceOfferings: {
|
||||
list: 'listServiceOfferings'
|
||||
},
|
||||
diskOfferings: {
|
||||
list: 'listDiskOfferings'
|
||||
list: 'listDiskOfferings',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: undefined
|
||||
}
|
||||
},
|
||||
zones: {
|
||||
list: 'listZones'
|
||||
},
|
||||
affinityGroups: {
|
||||
list: 'listAffinityGroups'
|
||||
list: 'listAffinityGroups',
|
||||
options: {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: undefined
|
||||
}
|
||||
},
|
||||
sshKeyPairs: {
|
||||
list: 'listSSHKeyPairs'
|
||||
list: 'listSSHKeyPairs',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: undefined
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
list: 'listNetworks',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
canusefordeploy: true,
|
||||
projectid: store.getters.project.id
|
||||
projectid: store.getters.project.id,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: undefined
|
||||
}
|
||||
},
|
||||
pods: {
|
||||
list: 'listPods',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id')
|
||||
}
|
||||
},
|
||||
clusters: {
|
||||
list: 'listClusters',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id')
|
||||
}
|
||||
},
|
||||
hosts: {
|
||||
list: 'listHosts',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
state: 'Up',
|
||||
type: 'Routing'
|
||||
}
|
||||
},
|
||||
groups: {
|
||||
list: 'listInstanceGroups'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -297,9 +476,54 @@ export default {
|
|||
value: zone.id
|
||||
}
|
||||
})
|
||||
},
|
||||
podSelectOptions () {
|
||||
return this.options.pods.map((pod) => {
|
||||
return {
|
||||
label: pod.name,
|
||||
value: pod.id
|
||||
}
|
||||
})
|
||||
},
|
||||
clusterSelectOptions () {
|
||||
return this.options.clusters.map((cluster) => {
|
||||
return {
|
||||
label: cluster.name,
|
||||
value: cluster.id
|
||||
}
|
||||
})
|
||||
},
|
||||
hostSelectOptions () {
|
||||
return this.options.hosts.map((host) => {
|
||||
return {
|
||||
label: host.name,
|
||||
value: host.id
|
||||
}
|
||||
})
|
||||
},
|
||||
keyboardSelectOptions () {
|
||||
return this.options.keyboards.map((keyboard) => {
|
||||
return {
|
||||
label: this.$t(keyboard.description),
|
||||
value: keyboard.id
|
||||
}
|
||||
})
|
||||
},
|
||||
groupsSelectOptions () {
|
||||
return this.options.groups.map((group) => {
|
||||
return {
|
||||
label: group.name,
|
||||
value: group.id
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route' (to, from) {
|
||||
if (to.name === 'deployVirtualMachine') {
|
||||
this.resetData()
|
||||
}
|
||||
},
|
||||
instanceConfig (instanceConfig) {
|
||||
this.template = _.find(this.options.templates, (option) => option.id === instanceConfig.templateid)
|
||||
this.iso = _.find(this.options.isos, (option) => option.id === instanceConfig.isoid)
|
||||
|
|
@ -363,26 +587,99 @@ export default {
|
|||
this.vm = this.instanceConfig
|
||||
}
|
||||
})
|
||||
this.form.getFieldDecorator('computeofferingid', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('diskofferingid', { initialValue: [], preserve: true })
|
||||
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: [], preserve: true })
|
||||
this.form.getFieldDecorator('isoid', { initialValue: undefined, preserve: true })
|
||||
this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('keypair', { initialValue: [], preserve: true })
|
||||
},
|
||||
created () {
|
||||
_.each(this.params, this.fetchOptions)
|
||||
Vue.nextTick().then(() => {
|
||||
this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
|
||||
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
|
||||
})
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
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')
|
||||
this.fetchKeyboard()
|
||||
Vue.nextTick().then(() => {
|
||||
this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
|
||||
})
|
||||
},
|
||||
fetchKeyboard () {
|
||||
const keyboardType = []
|
||||
keyboardType.push({
|
||||
id: '',
|
||||
description: ''
|
||||
})
|
||||
keyboardType.push({
|
||||
id: 'us',
|
||||
description: 'label.standard.us.keyboard'
|
||||
})
|
||||
keyboardType.push({
|
||||
id: 'uk',
|
||||
description: 'label.uk.keyboard'
|
||||
})
|
||||
keyboardType.push({
|
||||
id: 'fr',
|
||||
description: 'label.french.azerty.keyboard'
|
||||
})
|
||||
keyboardType.push({
|
||||
id: 'jp',
|
||||
description: 'label.japanese.keyboard'
|
||||
})
|
||||
keyboardType.push({
|
||||
id: 'sc',
|
||||
description: 'label.simplified.chinese.keyboard'
|
||||
})
|
||||
|
||||
this.$set(this.options, 'keyboards', keyboardType)
|
||||
},
|
||||
resetData () {
|
||||
this.vm = {}
|
||||
this.zoneSelected = false
|
||||
this.form.resetFields()
|
||||
this.fetchData()
|
||||
},
|
||||
updateFieldValue (name, value) {
|
||||
if (name === 'templateid') {
|
||||
this.tabKey = 'templateid'
|
||||
this.form.setFieldsValue({
|
||||
templateid: value,
|
||||
isoid: undefined
|
||||
})
|
||||
} else if (name === 'isoid') {
|
||||
this.tabKey = 'isoid'
|
||||
this.form.setFieldsValue({
|
||||
isoid: value,
|
||||
templateid: undefined
|
||||
})
|
||||
} else {
|
||||
this.form.setFieldsValue({
|
||||
[name]: value
|
||||
})
|
||||
}
|
||||
},
|
||||
updateComputeOffering (id) {
|
||||
this.form.setFieldsValue({
|
||||
computeofferingid: id
|
||||
})
|
||||
},
|
||||
updateDiskOffering (id) {
|
||||
if (id === '0') {
|
||||
this.form.setFieldsValue({
|
||||
diskofferingid: undefined
|
||||
})
|
||||
return
|
||||
}
|
||||
this.form.setFieldsValue({
|
||||
diskofferingid: id
|
||||
})
|
||||
|
|
@ -397,7 +694,19 @@ export default {
|
|||
networkids: ids
|
||||
})
|
||||
},
|
||||
updateDefaultNetworks (id) {
|
||||
this.defaultNetwork = id
|
||||
},
|
||||
updateNetworkConfig (networks) {
|
||||
this.networkConfig = networks
|
||||
},
|
||||
updateSshKeyPairs (name) {
|
||||
if (name === this.$t('noselect')) {
|
||||
this.form.setFieldsValue({
|
||||
keypair: undefined
|
||||
})
|
||||
return
|
||||
}
|
||||
this.form.setFieldsValue({
|
||||
keypair: name
|
||||
})
|
||||
|
|
@ -405,10 +714,103 @@ export default {
|
|||
getText (option) {
|
||||
return _.get(option, 'displaytext', _.get(option, 'name'))
|
||||
},
|
||||
handleSubmit () {
|
||||
handleSubmit (e) {
|
||||
console.log('wizard submit')
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
const deployVmData = {}
|
||||
// step 1 : select zone
|
||||
deployVmData.zoneid = values.zoneid
|
||||
deployVmData.podid = values.podid
|
||||
deployVmData.clusterid = values.clusterid
|
||||
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)))
|
||||
}
|
||||
// step 2: select template/iso
|
||||
if (this.tabKey === 'templateid') {
|
||||
deployVmData.templateid = values.templateid
|
||||
} else {
|
||||
deployVmData.templateid = values.isoid
|
||||
}
|
||||
if (values.rootdisksize && values.rootdisksize > 0) {
|
||||
deployVmData.rootdisksize = values.rootdisksize
|
||||
}
|
||||
// step 3: select service offering
|
||||
deployVmData.serviceofferingid = values.computeofferingid
|
||||
// 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(',')
|
||||
// step 6: select network
|
||||
if (values.networkids && values.networkids.length > 0) {
|
||||
for (let i = 0; i < values.networkids.length; i++) {
|
||||
deployVmData['iptonetworklist[' + i + '].networkid'] = values.networkids[i]
|
||||
if (this.networkConfig.length > 0) {
|
||||
const networkConfig = this.networkConfig.filter((item) => item.key === values.networkids[i])
|
||||
if (networkConfig && networkConfig.length > 0) {
|
||||
deployVmData['iptonetworklist[' + i + '].ip'] = networkConfig[0].ipAddress ? networkConfig[0].ipAddress : undefined
|
||||
deployVmData['iptonetworklist[' + i + '].mac'] = networkConfig[0].macAddress ? networkConfig[0].macAddress : undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// step 7: select ssh key pair
|
||||
deployVmData.keypair = values.keypair
|
||||
deployVmData.name = values.name
|
||||
deployVmData.displayname = values.name
|
||||
const title = this.$t('Launch Virtual Machine')
|
||||
const description = deployVmData.name ? deployVmData.name : values.zoneid
|
||||
this.loading.deploy = true
|
||||
api('deployVirtualMachine', deployVmData).then(response => {
|
||||
const jobId = response.deployvirtualmachineresponse.jobid
|
||||
if (jobId) {
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
successMethod: result => {
|
||||
let successDescription = ''
|
||||
if (result.jobresult.virtualmachine.name) {
|
||||
successDescription = result.jobresult.virtualmachine.name
|
||||
} else {
|
||||
successDescription = result.jobresult.virtualmachine.id
|
||||
}
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: title,
|
||||
jobid: jobId,
|
||||
description: successDescription,
|
||||
status: 'progress'
|
||||
})
|
||||
},
|
||||
loadingMessage: `${title} in progress for ${description}`,
|
||||
catchMessage: 'Error encountered while fetching async job result'
|
||||
})
|
||||
}
|
||||
this.$router.back()
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
|
||||
})
|
||||
}).finally(() => {
|
||||
this.loading.deploy = false
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchOptions (param, name) {
|
||||
fetchOptions (param, name, exclude) {
|
||||
if (exclude && exclude.length > 0) {
|
||||
if (exclude.includes(name)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.loading[name] = true
|
||||
param.loading = true
|
||||
param.opts = []
|
||||
const options = param.options || {}
|
||||
|
|
@ -416,6 +818,11 @@ export default {
|
|||
api(param.list, options).then((response) => {
|
||||
param.loading = false
|
||||
_.map(response, (responseItem, responseKey) => {
|
||||
if (Object.keys(responseItem).length === 0) {
|
||||
this.options[name] = []
|
||||
this.$forceUpdate()
|
||||
return
|
||||
}
|
||||
if (!responseKey.includes('response')) {
|
||||
return
|
||||
}
|
||||
|
|
@ -431,41 +838,108 @@ export default {
|
|||
}).catch(function (error) {
|
||||
console.log(error.stack)
|
||||
param.loading = false
|
||||
}).finally(() => {
|
||||
this.loading[name] = false
|
||||
})
|
||||
},
|
||||
fetchTemplates (templateFilter) {
|
||||
return new Promise((resolve, reject) => {
|
||||
api('listTemplates', {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
templatefilter: templateFilter
|
||||
}).then((response) => {
|
||||
resolve(response)
|
||||
}).catch((reason) => {
|
||||
// ToDo: Handle errors
|
||||
reject(reason)
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchIsos (isoFilter) {
|
||||
api('listIsos', {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
isofilter: isoFilter,
|
||||
bootable: true
|
||||
}).then((response) => {
|
||||
const concatedIsos = _.concat(this.options.isos, _.get(response, 'listisosresponse.iso', []))
|
||||
this.options.isos = _.uniqWith(concatedIsos, _.isEqual)
|
||||
this.$forceUpdate()
|
||||
return new Promise((resolve, reject) => {
|
||||
api('listIsos', {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
isofilter: isoFilter,
|
||||
bootable: true
|
||||
}).then((response) => {
|
||||
resolve(response)
|
||||
}).catch((reason) => {
|
||||
// ToDo: Handle errors
|
||||
reject(reason)
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchAllTemplates () {
|
||||
const promises = []
|
||||
this.options.templates = []
|
||||
this.loading.templates = true
|
||||
this.templateFilter.forEach((filter) => {
|
||||
promises.push(this.fetchTemplates(filter))
|
||||
})
|
||||
Promise.all(promises).then(response => {
|
||||
response.forEach((resItem) => {
|
||||
const concatTemplates = _.concat(this.options.templates, _.get(resItem, 'listtemplatesresponse.template', []))
|
||||
this.options.templates = _.uniqWith(concatTemplates, _.isEqual)
|
||||
this.$forceUpdate()
|
||||
})
|
||||
}).catch((reason) => {
|
||||
// ToDo: Handle errors
|
||||
console.log(reason)
|
||||
}).finally(() => {
|
||||
this.loading.templates = false
|
||||
})
|
||||
},
|
||||
fetchAllIsos () {
|
||||
const promises = []
|
||||
this.options.isos = []
|
||||
this.loading.isos = true
|
||||
this.isoFilter.forEach((filter) => {
|
||||
this.fetchIsos(filter)
|
||||
promises.push(this.fetchIsos(filter))
|
||||
})
|
||||
Promise.all(promises).then(response => {
|
||||
response.forEach((resItem) => {
|
||||
const concatedIsos = _.concat(this.options.isos, _.get(resItem, 'listisosresponse.iso', []))
|
||||
this.options.isos = _.uniqWith(concatedIsos, _.isEqual)
|
||||
this.$forceUpdate()
|
||||
})
|
||||
}).catch((reason) => {
|
||||
console.log(reason)
|
||||
}).finally(() => {
|
||||
this.loading.isos = false
|
||||
})
|
||||
},
|
||||
onTemplatesIsosCollapseChange (key) {
|
||||
if (key === 'isos' && this.options.isos.length === 0) {
|
||||
onSelectZoneId (value) {
|
||||
this.zoneId = value
|
||||
this.zoneSelected = true
|
||||
this.form.setFieldsValue({
|
||||
clusterid: undefined,
|
||||
podid: undefined,
|
||||
hostid: undefined,
|
||||
templateid: undefined,
|
||||
isoid: undefined
|
||||
})
|
||||
this.tabKey = 'templateid'
|
||||
_.each(this.params, (param, name) => {
|
||||
this.fetchOptions(param, name, ['zones', 'groups'])
|
||||
})
|
||||
this.fetchAllTemplates()
|
||||
},
|
||||
handleSearchFilter (name, options) {
|
||||
this.params[name].options = { ...this.params[name].options, ...options }
|
||||
this.fetchOptions(this.params[name], name)
|
||||
},
|
||||
onTabChange (key, type) {
|
||||
this[type] = key
|
||||
if (key === 'isoid') {
|
||||
this.fetchAllIsos()
|
||||
}
|
||||
},
|
||||
onSelectZoneId () {
|
||||
this.$nextTick(() => {
|
||||
if (this.options.isos.length !== 0) {
|
||||
this.fetchAllIsos()
|
||||
}
|
||||
this.fetchOptions(this.params.templates, 'templates')
|
||||
this.fetchOptions(this.params.networks, 'networks')
|
||||
})
|
||||
sanitizeReverse (value) {
|
||||
const reversedValue = value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
||||
return reversedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -505,4 +979,22 @@ export default {
|
|||
border-radius: @border-radius-base !important;
|
||||
margin: 0 0 1.2rem;
|
||||
}
|
||||
|
||||
.vm-info-card {
|
||||
.resource-detail-item__label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.resource-detail-item__details, .resource-detail-item {
|
||||
a {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-item-hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<a-spin :spinning="networkLoading">
|
||||
<a-tabs
|
||||
:activeKey="currentTab"
|
||||
:tabPosition="device === 'tablet' || device === 'mobile' ? 'top' : 'left'"
|
||||
:tabPosition="device === 'mobile' ? 'top' : 'left'"
|
||||
:animated="false"
|
||||
@change="handleChangeTab">
|
||||
<a-tab-pane :tab="$t('details')" key="details">
|
||||
|
|
|
|||
|
|
@ -16,15 +16,24 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="items"
|
||||
:rowKey="record => record.id"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
</a-table>
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="items"
|
||||
:rowKey="record => record.id"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -40,10 +49,15 @@ export default {
|
|||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
|
|
@ -60,6 +74,13 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
|
|
@ -76,6 +97,18 @@ export default {
|
|||
this.selectedRowKeys = newValue
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -16,16 +16,26 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
<span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
|
||||
<span slot="ramTitle"><a-icon type="bulb" /> {{ $t('memory') }}</span>
|
||||
</a-table>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -39,10 +49,15 @@ export default {
|
|||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
|
|
@ -64,6 +79,13 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
tableSource () {
|
||||
return this.computeItems.map((item) => {
|
||||
return {
|
||||
|
|
@ -78,9 +100,7 @@ export default {
|
|||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-compute-item', row.key)
|
||||
}
|
||||
onChange: this.onSelectRow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -90,6 +110,22 @@ export default {
|
|||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-compute-item', value[0])
|
||||
},
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -16,20 +16,37 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
<span slot="diskSizeTitle"><a-icon type="hdd" /> {{ $t('disksize') }}</span>
|
||||
<span slot="iopsTitle"><a-icon type="rocket" /> {{ $t('minMaxIops') }}</span>
|
||||
<template slot="diskSize" slot-scope="text, record">
|
||||
<div v-if="record.isCustomized">{{ $t('isCustomized') }}</div>
|
||||
<div v-else>{{ record.diskSize }} GB</div>
|
||||
</template>
|
||||
</a-table>
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
<span slot="diskSizeTitle"><a-icon type="hdd" /> {{ $t('disksize') }}</span>
|
||||
<span slot="iopsTitle"><a-icon type="rocket" /> {{ $t('minMaxIops') }}</span>
|
||||
<template slot="diskSize" slot-scope="text, record">
|
||||
<div v-if="record.isCustomized">{{ $t('isCustomized') }}</div>
|
||||
<div v-else-if="record.diskSize">{{ record.diskSize }} GB</div>
|
||||
<div v-else>-</div>
|
||||
</template>
|
||||
<template slot="iops" slot-scope="text, record">
|
||||
<span v-if="record.miniops && record.maxiops">{{ record.miniops }} - {{ record.maxiops }}</span>
|
||||
<span v-else-if="record.miniops && !record.maxiops">{{ record.miniops }}</span>
|
||||
<span v-else-if="!record.miniops && record.maxiops">{{ record.maxiops }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -43,18 +60,23 @@ export default {
|
|||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('diskOffering'),
|
||||
title: this.$t('diskoffering'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'diskSize',
|
||||
dataIndex: 'disksize',
|
||||
slots: { title: 'diskSizeTitle' },
|
||||
width: '30%',
|
||||
scopedSlots: { customRender: 'diskSize' }
|
||||
|
|
@ -62,20 +84,41 @@ export default {
|
|||
{
|
||||
dataIndex: 'iops',
|
||||
slots: { title: 'iopsTitle' },
|
||||
width: '30%'
|
||||
width: '30%',
|
||||
scopedSlots: { customRender: 'iops' }
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
selectedRowKeys: ['0'],
|
||||
dataItems: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.dataItems = []
|
||||
this.dataItems.push({
|
||||
id: '0',
|
||||
name: this.$t('noselect'),
|
||||
diskSize: undefined,
|
||||
miniops: undefined,
|
||||
maxiops: undefined,
|
||||
isCustomized: undefined
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
tableSource () {
|
||||
return this.items.map((item) => {
|
||||
return this.dataItems.map((item) => {
|
||||
return {
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
diskSize: item.disksize,
|
||||
iops: `${item.miniops} – ${item.maxiops}`,
|
||||
miniops: item.miniops,
|
||||
maxiops: item.maxiops,
|
||||
isCustomized: item.iscustomized
|
||||
}
|
||||
})
|
||||
|
|
@ -84,9 +127,7 @@ export default {
|
|||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-disk-offering-item', row.key)
|
||||
}
|
||||
onChange: this.onSelectRow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -95,6 +136,27 @@ export default {
|
|||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
},
|
||||
items (newData, oldData) {
|
||||
if (newData && newData.length > 0) {
|
||||
this.dataItems = this.dataItems.concat(newData)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-disk-offering-item', value[0])
|
||||
},
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,13 @@
|
|||
<a-slider
|
||||
:min="0"
|
||||
:max="1024"
|
||||
v-decorator="[inputDecorator]"
|
||||
v-model="inputValue"
|
||||
@change="($event) => updateDickSize($event)"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-input-number
|
||||
v-decorator="[inputDecorator, {
|
||||
rules: [{ required: false, message: 'Please enter a number' }]
|
||||
}]"
|
||||
v-model="inputValue"
|
||||
:formatter="value => `${value} GB`"
|
||||
:parser="value => value.replace(' GB', '')"
|
||||
/>
|
||||
|
|
@ -46,6 +45,16 @@ export default {
|
|||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
inputValue: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateDickSize (value) {
|
||||
this.$emit('update-disk-size', this.inputDecorator, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,21 +18,24 @@
|
|||
<template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="items"
|
||||
:dataSource="dataItems"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
:rowKey="record => record.id"
|
||||
size="middle"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
<template v-slot:ipAddress="text">
|
||||
<template slot="ipAddress" slot-scope="text, record">
|
||||
<a-input
|
||||
:value="text"
|
||||
></a-input>
|
||||
style="width: 150px;"
|
||||
:placeholder="$t('ipAddress')"
|
||||
@change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)" />
|
||||
</template>
|
||||
<template v-slot:macAddress="text">
|
||||
<template slot="macAddress" slot-scope="text, record">
|
||||
<a-input
|
||||
:value="text"
|
||||
></a-input>
|
||||
style="width: 150px;"
|
||||
:placeholder="$t('macAddress')"
|
||||
@change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)" />
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
|
@ -52,6 +55,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
networks: [],
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
|
|
@ -71,17 +75,24 @@ export default {
|
|||
scopedSlots: { customRender: 'macAddress' }
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
selectedRowKeys: [],
|
||||
dataItems: []
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.dataItems = []
|
||||
},
|
||||
created () {
|
||||
this.dataItems = this.items
|
||||
this.selectedRowKeys = [this.dataItems[0].id]
|
||||
this.$emit('select-default-network-item', this.selectedRowKeys)
|
||||
},
|
||||
computed: {
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-default-network-item', row.key)
|
||||
}
|
||||
onChange: this.onSelectRow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -90,6 +101,38 @@ export default {
|
|||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
},
|
||||
items (newData, oldData) {
|
||||
if (newData && newData.length > 0) {
|
||||
this.dataItems = newData
|
||||
const keyEx = this.dataItems.filter((item) => this.selectedRowKeys.includes(item.id))
|
||||
if (!keyEx || keyEx.length === 0) {
|
||||
this.selectedRowKeys = [this.dataItems[0].id]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-default-network-item', value[0])
|
||||
},
|
||||
updateNetworkData (name, key, value) {
|
||||
if (this.networks.length === 0) {
|
||||
const networkItem = {}
|
||||
networkItem.key = key
|
||||
networkItem[name] = value
|
||||
this.networks.push(networkItem)
|
||||
this.$emit('update-network-config', this.networks)
|
||||
return
|
||||
}
|
||||
|
||||
this.networks.filter((item, index) => {
|
||||
if (item.key === key) {
|
||||
this.$set(this.networks[index], name, value)
|
||||
}
|
||||
})
|
||||
this.$emit('update-network-config', this.networks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,29 +16,39 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="networkItems"
|
||||
:rowKey="record => record.id"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
>
|
||||
<a-list
|
||||
slot="expandedRowRender"
|
||||
slot-scope="record"
|
||||
:key="record.id"
|
||||
:dataSource="getDetails(record)"
|
||||
size="small"
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="networkItems"
|
||||
:rowKey="record => record.id"
|
||||
:pagination="{showSizeChanger: true, size: 'small'}"
|
||||
:rowSelection="rowSelection"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
<a-list-item slot="renderItem" slot-scope="item" :key="item.id">
|
||||
<a-list-item-meta
|
||||
:description="item.description"
|
||||
>
|
||||
<template v-slot:title>{{ item.title }}</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-table>
|
||||
<a-list
|
||||
slot="expandedRowRender"
|
||||
slot-scope="record"
|
||||
:key="record.id"
|
||||
:dataSource="getDetails(record)"
|
||||
size="small"
|
||||
>
|
||||
<a-list-item slot="renderItem" slot-scope="item" :key="item.id">
|
||||
<a-list-item-meta
|
||||
:description="item.description"
|
||||
>
|
||||
<template v-slot:title>{{ item.title }}</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -56,16 +66,28 @@ export default {
|
|||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
selectedRowKeys: [],
|
||||
vpcs: [],
|
||||
filteredInfo: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
columns () {
|
||||
let vpcFilter = []
|
||||
if (this.vpcs) {
|
||||
|
|
@ -146,6 +168,16 @@ export default {
|
|||
description: network.networkofferingdisplaytext
|
||||
}
|
||||
]
|
||||
},
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-table
|
||||
v-if="networkItems.length > 0"
|
||||
:columns="columns"
|
||||
:dataSource="networkItems"
|
||||
:pagination="false"
|
||||
>
|
||||
<template v-slot:name="text">
|
||||
<a-input
|
||||
:value="text"
|
||||
></a-input>
|
||||
</template>
|
||||
<template v-slot:operation>
|
||||
<a-popconfirm
|
||||
v-if="networkItems.length"
|
||||
title="Sure to delete?"
|
||||
@confirm="removeItem()"
|
||||
>
|
||||
<a-button type="link">Delete</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
<template v-slot:networkOffering>
|
||||
<a-select
|
||||
:placeholder="$t('networkOfferingId')"
|
||||
:options="networkOfferingOptions"
|
||||
></a-select>
|
||||
</template>
|
||||
<template v-slot:vpc>
|
||||
<a-select
|
||||
:placeholder="$t('vpc')"
|
||||
:options="vpcOptions"
|
||||
></a-select>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<div style="text-align: right; margin-top: 1rem;">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="addNewItem"
|
||||
>{{ $t('addAnotherNetwork') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import store from '@/store'
|
||||
import _ from 'lodash'
|
||||
|
||||
/*
|
||||
* ToDo: Implement real functionality
|
||||
*/
|
||||
export default {
|
||||
name: 'NetworkCreation',
|
||||
data () {
|
||||
return {
|
||||
networkItems: [{}],
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('networks'),
|
||||
scopedSlots: { customRender: 'name' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'offering',
|
||||
title: this.$t('networkOfferingId'),
|
||||
scopedSlots: { customRender: 'networkOffering' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'vpcName',
|
||||
title: this.$t('VPC'),
|
||||
scopedSlots: { customRender: 'vpc' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'action',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: '10%'
|
||||
}
|
||||
],
|
||||
networkOfferings: [],
|
||||
vpcs: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
networkOfferingOptions () {
|
||||
return this.networkOfferings.map((offering) => {
|
||||
return {
|
||||
label: offering.name,
|
||||
value: offering.id
|
||||
}
|
||||
})
|
||||
},
|
||||
vpcOptions () {
|
||||
return this.vpcs.map((vpc) => {
|
||||
return {
|
||||
label: vpc.name,
|
||||
value: vpc.id
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created () {
|
||||
api('listNetworkOfferings', {
|
||||
// ToDo: Add the zoneId
|
||||
}).then((response) => {
|
||||
this.networkOfferings = _.get(response, 'listnetworkofferingsresponse.networkoffering')
|
||||
})
|
||||
// ToDo: Remove this redundant api call – see the NetworkSelection component
|
||||
api('listVPCs', {
|
||||
projectid: store.getters.project.id
|
||||
}).then((response) => {
|
||||
this.vpcs = _.get(response, 'listvpcsresponse.vpc')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
addNewItem () {
|
||||
this.networkItems.push({})
|
||||
},
|
||||
removeItem () {
|
||||
this.networkItems.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -16,16 +16,26 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
<template v-slot:account><a-icon type="user" /> {{ $t('account') }}</template>
|
||||
<template v-slot:domain><a-icon type="block" /> {{ $t('domain') }}</template>
|
||||
</a-table>
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
<template v-slot:account><a-icon type="user" /> {{ $t('account') }}</template>
|
||||
<template v-slot:domain><a-icon type="block" /> {{ $t('domain') }}</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -39,10 +49,15 @@ export default {
|
|||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
|
|
@ -60,12 +75,28 @@ export default {
|
|||
width: '30%'
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
selectedRowKeys: [this.$t('noselect')],
|
||||
dataItems: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.dataItems = []
|
||||
this.dataItems.push({
|
||||
name: this.$t('noselect'),
|
||||
account: '-',
|
||||
domain: '-'
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
tableSource () {
|
||||
return this.items.map((item) => {
|
||||
return this.dataItems.map((item) => {
|
||||
return {
|
||||
key: item.name,
|
||||
name: item.name,
|
||||
|
|
@ -78,9 +109,7 @@ export default {
|
|||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-ssh-key-pair-item', row.key)
|
||||
}
|
||||
onChange: this.onSelectRow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -89,6 +118,27 @@ export default {
|
|||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
},
|
||||
items (newData, oldData) {
|
||||
if (newData && newData.length > 0) {
|
||||
this.dataItems = this.dataItems.concat(newData)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-ssh-key-pair-item', value[0])
|
||||
},
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,37 +17,46 @@
|
|||
|
||||
<template>
|
||||
<a-form-item>
|
||||
<a-radio-group
|
||||
v-for="(os, osIndex) in osList"
|
||||
:key="osIndex"
|
||||
class="radio-group"
|
||||
v-decorator="[inputDecorator, {
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
>
|
||||
<a-radio
|
||||
class="radio-group__radio"
|
||||
:value="os.id"
|
||||
>
|
||||
{{ os.displaytext }}
|
||||
<a-tag
|
||||
:visible="os.ispublic && !os.isfeatured"
|
||||
color="blue"
|
||||
>{{ $t('isPublic') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="os.isfeatured"
|
||||
color="green"
|
||||
>{{ $t('isFeatured') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="isSelf(os)"
|
||||
color="orange"
|
||||
>{{ $t('isSelf') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="isShared(os)"
|
||||
color="cyan"
|
||||
>{{ $t('isShared') }}</a-tag>
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
<a-list
|
||||
class="form-item-scroll"
|
||||
itemLayout="vertical"
|
||||
size="small"
|
||||
:dataSource="osList"
|
||||
:pagination="pagination">
|
||||
<a-list-item slot="renderItem" slot-scope="os, osIndex" key="os.id">
|
||||
<a-radio-group
|
||||
class="radio-group"
|
||||
:key="osIndex"
|
||||
v-model="value"
|
||||
@change="($event) => updateSelectionTemplateIso($event.target.value)">
|
||||
<a-radio
|
||||
class="radio-group__radio"
|
||||
:value="os.id">
|
||||
{{ os.displaytext }}
|
||||
<a-tag
|
||||
:visible="os.ispublic && !os.isfeatured"
|
||||
color="blue"
|
||||
@click="onFilterTag('is: public')"
|
||||
>{{ $t('isPublic') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="os.isfeatured"
|
||||
color="green"
|
||||
@click="onFilterTag('is: featured')"
|
||||
>{{ $t('isFeatured') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="isSelf(os)"
|
||||
color="orange"
|
||||
@click="onFilterTag('is: self')"
|
||||
>{{ $t('isSelf') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="isShared(os)"
|
||||
color="cyan"
|
||||
@click="onFilterTag('is: shared')"
|
||||
>{{ $t('isShared') }}</a-tag>
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
|
|
@ -64,6 +73,45 @@ export default {
|
|||
inputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selected: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
itemCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
value: '',
|
||||
page: 1,
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pagination () {
|
||||
return {
|
||||
size: 'small',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: this.itemCount,
|
||||
showSizeChanger: true,
|
||||
onChange: this.onChangePage,
|
||||
onShowSizeChange: this.onChangePageSize
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -72,6 +120,22 @@ export default {
|
|||
},
|
||||
isSelf (item) {
|
||||
return !item.ispublic && (item.account === store.getters.userInfo.account)
|
||||
},
|
||||
updateSelectionTemplateIso (id) {
|
||||
this.$emit('emit-update-template-iso', this.inputDecorator, id)
|
||||
},
|
||||
onChangePage (page, pageSize) {
|
||||
this.pagination.page = page
|
||||
this.pagination.pageSize = pageSize
|
||||
this.$forceUpdate()
|
||||
},
|
||||
onChangePageSize (page, pageSize) {
|
||||
this.pagination.page = page
|
||||
this.pagination.pageSize = pageSize
|
||||
this.$forceUpdate()
|
||||
},
|
||||
onFilterTag (tag) {
|
||||
this.$emit('handle-filter-tag', tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,4 +153,14 @@ export default {
|
|||
.ant-tag {
|
||||
margin-left: 0.4rem;
|
||||
}
|
||||
|
||||
/deep/.ant-spin-container {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -16,27 +16,33 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-tabs :defaultActiveKey="Object.keys(osTypes)[0]" v-if="view === TAB_VIEW">
|
||||
<a-button icon="search" slot="tabBarExtraContent" @click="() => toggleView(FILTER_VIEW)"/>
|
||||
<a-tab-pane v-for="(osList, osName) in osTypes" :key="osName">
|
||||
<span slot="tab">
|
||||
<os-logo :os-name="osName"></os-logo>
|
||||
</span>
|
||||
<TemplateIsoRadioGroup
|
||||
:osList="osList"
|
||||
:input-decorator="inputDecorator"
|
||||
></TemplateIsoRadioGroup>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else>
|
||||
<a-input class="search-input" v-model="filter">
|
||||
<a-icon slot="prefix" type="search"/>
|
||||
<a-icon slot="addonAfter" type="close" @click="toggleView(TAB_VIEW)"/>
|
||||
</a-input>
|
||||
<TemplateIsoRadioGroup
|
||||
:osList="filteredItems"
|
||||
:input-decorator="inputDecorator"
|
||||
></TemplateIsoRadioGroup>
|
||||
<div>
|
||||
<a-input-search
|
||||
class="search-input"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="filterDataSource"/>
|
||||
<a-spin :spinning="loading">
|
||||
<a-tabs
|
||||
tabPosition="top"
|
||||
:animated="false"
|
||||
:defaultActiveKey="Object.keys(dataSource)[0]">
|
||||
<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"
|
||||
:osList="dataSource[osName]"
|
||||
:input-decorator="inputDecorator"
|
||||
:selected="checkedValue"
|
||||
:itemCount="itemCount[osName]"
|
||||
@handle-filter-tag="filterDataSource"
|
||||
@emit-update-template-iso="updateTemplateIso"
|
||||
></TemplateIsoRadioGroup>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -45,9 +51,7 @@ import OsLogo from '@/components/widgets/OsLogo'
|
|||
import { getNormalizedOsName } from '@/utils/icons'
|
||||
import _ from 'lodash'
|
||||
import TemplateIsoRadioGroup from '@views/compute/wizard/TemplateIsoRadioGroup'
|
||||
|
||||
export const TAB_VIEW = 1
|
||||
export const FILTER_VIEW = 2
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
name: 'TemplateIsoSelection',
|
||||
|
|
@ -60,27 +64,53 @@ export default {
|
|||
inputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selected: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
TAB_VIEW: TAB_VIEW,
|
||||
FILTER_VIEW: FILTER_VIEW,
|
||||
visible: false,
|
||||
filter: '',
|
||||
filteredItems: this.items,
|
||||
view: TAB_VIEW
|
||||
checkedValue: '',
|
||||
dataSource: {},
|
||||
itemCount: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
osTypes () {
|
||||
watch: {
|
||||
items (items) {
|
||||
this.filteredItems = []
|
||||
this.checkedValue = ''
|
||||
if (items && items.length > 0) {
|
||||
this.filteredItems = items
|
||||
this.checkedValue = items[0].id
|
||||
}
|
||||
this.dataSource = this.mappingDataSource()
|
||||
},
|
||||
inputDecorator (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
this.filter = ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
mappingDataSource () {
|
||||
let mappedItems = {}
|
||||
this.items.forEach((os) => {
|
||||
const itemCount = {}
|
||||
this.filteredItems.forEach((os) => {
|
||||
const osName = getNormalizedOsName(os.ostypename)
|
||||
if (Array.isArray(mappedItems[osName])) {
|
||||
mappedItems[osName].push(os)
|
||||
itemCount[osName] = itemCount[osName] + 1
|
||||
} else {
|
||||
mappedItems[osName] = [os]
|
||||
itemCount[osName] = 1
|
||||
}
|
||||
})
|
||||
mappedItems = _.mapValues(mappedItems, (list) => {
|
||||
|
|
@ -90,24 +120,50 @@ export default {
|
|||
nonFeaturedItems = _.sortBy(nonFeaturedItems, (item) => item.displaytext.toLowerCase())
|
||||
return featuredItems.concat(nonFeaturedItems) // pin featured isos/templates at the top
|
||||
})
|
||||
this.itemCount = itemCount
|
||||
return mappedItems
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
items (items) {
|
||||
this.filteredItems = items
|
||||
},
|
||||
filter (filterString) {
|
||||
if (filterString !== '') {
|
||||
this.filteredItems = this.filteredItems.filter((item) => item.displaytext.toLowerCase().includes(filterString))
|
||||
updateTemplateIso (name, id) {
|
||||
this.$emit('update-template-iso', name, id)
|
||||
},
|
||||
filterDataSource (strQuery) {
|
||||
if (strQuery !== '' && strQuery.includes('is:')) {
|
||||
this.filteredItems = []
|
||||
this.filter = strQuery
|
||||
const filters = strQuery.split(';')
|
||||
filters.forEach((filter) => {
|
||||
const query = filter.replace(/ /g, '')
|
||||
const data = this.filterDataSourceByTag(query)
|
||||
this.filteredItems = this.filteredItems.concat(data)
|
||||
})
|
||||
} else if (strQuery !== '') {
|
||||
this.filteredItems = this.items.filter((item) => item.displaytext.toLowerCase().includes(strQuery.toLowerCase()))
|
||||
} else {
|
||||
this.filteredItems = this.items
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleView (view) {
|
||||
this.view = view
|
||||
this.dataSource = this.mappingDataSource()
|
||||
},
|
||||
filterDataSourceByTag (tag) {
|
||||
let arrResult = []
|
||||
if (tag.includes('public')) {
|
||||
arrResult = this.items.filter((item) => {
|
||||
return item.ispublic && item.isfeatured
|
||||
})
|
||||
} else if (tag.includes('featured')) {
|
||||
arrResult = this.items.filter((item) => {
|
||||
return item.isfeatured
|
||||
})
|
||||
} else if (tag.includes('self')) {
|
||||
arrResult = this.items.filter((item) => {
|
||||
return !item.ispublic && (item.account === store.getters.userInfo.account)
|
||||
})
|
||||
} else if (tag.includes('shared')) {
|
||||
arrResult = this.items.filter((item) => {
|
||||
return !item.ispublic && (item.account !== store.getters.userInfo.account)
|
||||
})
|
||||
}
|
||||
|
||||
return arrResult
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -115,6 +171,21 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.search-input {
|
||||
margin: 0.5rem 0 1rem;
|
||||
width: 25vw;
|
||||
z-index: 8;
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 10px;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/.ant-tabs-nav-scroll {
|
||||
min-height: 45px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export default {
|
|||
detailColumn: [],
|
||||
detail: [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
pageSize: 10,
|
||||
quickview: false,
|
||||
loading: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export default {
|
|||
detailColumn: [],
|
||||
detail: [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
fetchLoading: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export default {
|
|||
columns: [],
|
||||
dataSource: [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
fetchLoading: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<a-spin :spinning="fetchLoading">
|
||||
<a-tabs
|
||||
:activeKey="currentTab"
|
||||
:tabPosition="device === 'tablet' || device === 'mobile' ? 'top' : 'left'"
|
||||
:tabPosition="device === 'mobile' ? 'top' : 'left'"
|
||||
:animated="false"
|
||||
@change="handleChangeTab">
|
||||
<a-tab-pane :tab="$t('details')" key="details">
|
||||
|
|
|
|||
Loading…
Reference in New Issue