mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
views: custom search framework for list views (#235)
This adds a new search view component that will allow users to do custom search using a popover component for vm, storage, network, image, event, project and routers. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
1b548b38dc
commit
77bbfc3292
481
ui/src/components/view/SearchView.vue
Normal file
481
ui/src/components/view/SearchView.vue
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
// 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>
|
||||||
|
<span :style="styleSearch">
|
||||||
|
<span v-if="!searchFilters || searchFilters.length === 0" style="display: flex;">
|
||||||
|
<a-input-search
|
||||||
|
style="width: 100%; display: table-cell"
|
||||||
|
:placeholder="$t('label.search')"
|
||||||
|
v-model="searchQuery"
|
||||||
|
allowClear
|
||||||
|
@search="onSearch" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="filter-group">
|
||||||
|
<a-input-search
|
||||||
|
allowClear
|
||||||
|
class="input-search"
|
||||||
|
placeholder="Search"
|
||||||
|
v-model="searchQuery"
|
||||||
|
@search="onSearch">
|
||||||
|
<a-popover
|
||||||
|
placement="bottomRight"
|
||||||
|
slot="addonBefore"
|
||||||
|
trigger="click"
|
||||||
|
v-model="visibleFilter">
|
||||||
|
<template slot="content">
|
||||||
|
<a-form
|
||||||
|
style="min-width: 170px"
|
||||||
|
:form="form"
|
||||||
|
layout="vertical"
|
||||||
|
@submit="handleSubmit">
|
||||||
|
<a-form-item
|
||||||
|
v-for="(field, index) in fields"
|
||||||
|
:key="index"
|
||||||
|
:label="field.name==='keyword' ? $t('label.name') : $t('label.' + field.name)">
|
||||||
|
<a-select
|
||||||
|
allowClear
|
||||||
|
v-if="field.type==='list'"
|
||||||
|
v-decorator="[field.name]"
|
||||||
|
:loading="field.loading">
|
||||||
|
<a-select-option
|
||||||
|
v-for="(opt, idx) in field.opts"
|
||||||
|
:key="idx"
|
||||||
|
:value="opt.id">{{ $t(opt.name) }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-input
|
||||||
|
v-else-if="field.type==='input'"
|
||||||
|
v-decorator="[field.name]" />
|
||||||
|
<div v-else-if="field.type==='tag'">
|
||||||
|
<div>
|
||||||
|
<a-input-group
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
compact>
|
||||||
|
<a-input ref="input" :value="inputKey" @change="e => inputKey = e.target.value" style="width: 50px; text-align: center" :placeholder="$t('label.key')" />
|
||||||
|
<a-input style=" width: 20px; border-left: 0; pointer-events: none; backgroundColor: #fff" placeholder="=" disabled />
|
||||||
|
<a-input :value="inputValue" @change="handleValueChange" style="width: 50px; text-align: center; border-left: 0" :placeholder="$t('label.value')" />
|
||||||
|
<a-button shape="circle" size="small" @click="inputKey = inputValue = ''">
|
||||||
|
<a-icon type="close"/>
|
||||||
|
</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<div class="filter-group-button">
|
||||||
|
<a-button
|
||||||
|
class="filter-group-button-clear"
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
icon="stop"
|
||||||
|
@click="onClear">{{ $t('label.reset') }}</a-button>
|
||||||
|
<a-button
|
||||||
|
class="filter-group-button-search"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="search"
|
||||||
|
@click="handleSubmit">{{ $t('label.search') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
class="filter-button"
|
||||||
|
size="small"
|
||||||
|
@click="() => { searchQuery = null }">
|
||||||
|
<a-icon type="filter" :theme="Object.keys(selectedFilters).length > 0 ? 'twoTone' : 'outlined'" />
|
||||||
|
</a-button>
|
||||||
|
</a-popover>
|
||||||
|
</a-input-search>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SearchView',
|
||||||
|
props: {
|
||||||
|
searchFilters: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
apiName: {
|
||||||
|
type: String,
|
||||||
|
default: () => ''
|
||||||
|
},
|
||||||
|
selectedFilters: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['parentSearch', 'parentFilter', 'parentChangeFilter'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
searchQuery: null,
|
||||||
|
paramsFilter: {},
|
||||||
|
visibleFilter: false,
|
||||||
|
fields: [],
|
||||||
|
inputKey: null,
|
||||||
|
inputValue: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.form = this.$form.createForm(this)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visibleFilter (newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.initFormFieldData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
styleSearch () {
|
||||||
|
if (!this.searchFilters || this.searchFilters.length === 0) {
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
display: 'table-cell'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
display: 'table-cell',
|
||||||
|
lineHeight: '31px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initFormFieldData () {
|
||||||
|
const arrayField = []
|
||||||
|
this.fields = []
|
||||||
|
this.searchFilters.forEach(item => {
|
||||||
|
let type = 'input'
|
||||||
|
|
||||||
|
if (item === 'domainid' && !('listDomains' in this.$store.getters.apis)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (item === 'account' && !('addAccountToProject' in this.$store.getters.apis || 'createAccount' in this.$store.getters.apis)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (item === 'podid' && !('listPods' in this.$store.getters.apis)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (item === 'clusterid' && !('listClusters' in this.$store.getters.apis)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (['zoneid', 'domainid', 'state', 'level', 'clusterid', 'podid'].includes(item)) {
|
||||||
|
type = 'list'
|
||||||
|
} else if (item === 'tags') {
|
||||||
|
type = 'tag'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fields.push({
|
||||||
|
type: type,
|
||||||
|
name: item,
|
||||||
|
opts: [],
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
arrayField.push(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
const promises = []
|
||||||
|
let zoneIndex = -1
|
||||||
|
let domainIndex = -1
|
||||||
|
let podIndex = -1
|
||||||
|
let clusterIndex = -1
|
||||||
|
|
||||||
|
if (arrayField.includes('state')) {
|
||||||
|
const stateIndex = this.fields.findIndex(item => item.name === 'state')
|
||||||
|
this.fields[stateIndex].loading = true
|
||||||
|
this.fields[stateIndex].opts = this.fetchState()
|
||||||
|
this.fields[stateIndex].loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('level')) {
|
||||||
|
const levelIndex = this.fields.findIndex(item => item.name === 'level')
|
||||||
|
this.fields[levelIndex].loading = true
|
||||||
|
this.fields[levelIndex].opts = this.fetchLevel()
|
||||||
|
this.fields[levelIndex].loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('zoneid')) {
|
||||||
|
zoneIndex = this.fields.findIndex(item => item.name === 'zoneid')
|
||||||
|
this.fields[zoneIndex].loading = true
|
||||||
|
promises.push(await this.fetchZones())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('domainid')) {
|
||||||
|
domainIndex = this.fields.findIndex(item => item.name === 'domainid')
|
||||||
|
this.fields[domainIndex].loading = true
|
||||||
|
promises.push(await this.fetchDomains())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('podid')) {
|
||||||
|
podIndex = this.fields.findIndex(item => item.name === 'podid')
|
||||||
|
this.fields[podIndex].loading = true
|
||||||
|
promises.push(await this.fetchPods())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('clusterid')) {
|
||||||
|
clusterIndex = this.fields.findIndex(item => item.name === 'clusterid')
|
||||||
|
this.fields[clusterIndex].loading = true
|
||||||
|
promises.push(await this.fetchClusters())
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(promises).then(response => {
|
||||||
|
if (zoneIndex > -1) {
|
||||||
|
const zones = response.filter(item => item.type === 'zoneid')
|
||||||
|
if (zones && zones.length > 0) {
|
||||||
|
this.fields[zoneIndex].opts = zones[0].data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (domainIndex > -1) {
|
||||||
|
const domain = response.filter(item => item.type === 'domainid')
|
||||||
|
if (domain && domain.length > 0) {
|
||||||
|
this.fields[domainIndex].opts = domain[0].data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (podIndex > -1) {
|
||||||
|
const pod = response.filter(item => item.type === 'podid')
|
||||||
|
if (pod && pod.length > 0) {
|
||||||
|
this.fields[podIndex].opts = pod[0].data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clusterIndex > -1) {
|
||||||
|
const cluster = response.filter(item => item.type === 'clusterid')
|
||||||
|
console.log(cluster)
|
||||||
|
if (cluster && cluster.length > 0) {
|
||||||
|
this.fields[clusterIndex].opts = cluster[0].data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$forceUpdate()
|
||||||
|
}).finally(() => {
|
||||||
|
if (zoneIndex > -1) {
|
||||||
|
this.fields[zoneIndex].loading = false
|
||||||
|
}
|
||||||
|
if (domainIndex > -1) {
|
||||||
|
this.fields[domainIndex].loading = false
|
||||||
|
}
|
||||||
|
if (podIndex > -1) {
|
||||||
|
this.fields[podIndex].loading = false
|
||||||
|
}
|
||||||
|
if (clusterIndex > -1) {
|
||||||
|
this.fields[clusterIndex].loading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchZones () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listZones', { listAll: true }).then(json => {
|
||||||
|
const zones = json.listzonesresponse.zone
|
||||||
|
resolve({
|
||||||
|
type: 'zoneid',
|
||||||
|
data: zones
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error.response.headers['x-description'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchDomains () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listDomains', { listAll: true }).then(json => {
|
||||||
|
const domain = json.listdomainsresponse.domain
|
||||||
|
resolve({
|
||||||
|
type: 'domainid',
|
||||||
|
data: domain
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error.response.headers['x-description'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchPods () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listPods', { listAll: true }).then(json => {
|
||||||
|
const pods = json.listpodsresponse.pod
|
||||||
|
resolve({
|
||||||
|
type: 'podid',
|
||||||
|
data: pods
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error.response.headers['x-description'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchClusters () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listClusters', { listAll: true }).then(json => {
|
||||||
|
const clusters = json.listclustersresponse.cluster
|
||||||
|
resolve({
|
||||||
|
type: 'clusterid',
|
||||||
|
data: clusters
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error.response.headers['x-description'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchState () {
|
||||||
|
const state = []
|
||||||
|
if (this.apiName.indexOf('listVolumes') > -1) {
|
||||||
|
state.push({
|
||||||
|
id: 'Allocated',
|
||||||
|
name: 'label.allocated'
|
||||||
|
})
|
||||||
|
state.push({
|
||||||
|
id: 'Ready',
|
||||||
|
name: 'label.isready'
|
||||||
|
})
|
||||||
|
state.push({
|
||||||
|
id: 'Destroy',
|
||||||
|
name: 'label.destroy'
|
||||||
|
})
|
||||||
|
state.push({
|
||||||
|
id: 'Expunging',
|
||||||
|
name: 'label.expunging'
|
||||||
|
})
|
||||||
|
state.push({
|
||||||
|
id: 'Expunged',
|
||||||
|
name: 'label.expunged'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
},
|
||||||
|
fetchLevel () {
|
||||||
|
const levels = []
|
||||||
|
levels.push({
|
||||||
|
id: 'INFO',
|
||||||
|
name: 'label.info.upper'
|
||||||
|
})
|
||||||
|
levels.push({
|
||||||
|
id: 'WARN',
|
||||||
|
name: 'label.warn.upper'
|
||||||
|
})
|
||||||
|
levels.push({
|
||||||
|
id: 'ERROR',
|
||||||
|
name: 'label.error.upper'
|
||||||
|
})
|
||||||
|
return levels
|
||||||
|
},
|
||||||
|
onSearch (value) {
|
||||||
|
this.paramsFilter = {}
|
||||||
|
this.searchQuery = value
|
||||||
|
this.parentSearch(this.searchQuery)
|
||||||
|
},
|
||||||
|
onClear () {
|
||||||
|
this.searchFilters.map(item => {
|
||||||
|
const field = {}
|
||||||
|
field[item] = undefined
|
||||||
|
this.form.setFieldsValue(field)
|
||||||
|
})
|
||||||
|
this.inputKey = null
|
||||||
|
this.inputValue = null
|
||||||
|
this.searchQuery = null
|
||||||
|
this.paramsFilter = {}
|
||||||
|
this.parentFilter(this.paramsFilter)
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.paramsFilter = {}
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const key in values) {
|
||||||
|
const input = values[key]
|
||||||
|
if (input === '' || input === null || input === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
this.paramsFilter[key] = input
|
||||||
|
}
|
||||||
|
if (this.searchFilters.includes('tags')) {
|
||||||
|
if (this.inputKey) {
|
||||||
|
this.paramsFilter['tags[0].key'] = this.inputKey
|
||||||
|
this.paramsFilter['tags[0].value'] = this.inputValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.parentFilter(this.paramsFilter)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleKeyChange (e) {
|
||||||
|
this.inputKey = e.target.value
|
||||||
|
},
|
||||||
|
handleValueChange (e) {
|
||||||
|
this.inputValue = e.target.value
|
||||||
|
},
|
||||||
|
changeFilter (filter) {
|
||||||
|
this.parentChangeFilter(filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.input-search {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/.ant-input-group {
|
||||||
|
.ant-input-affix-wrapper {
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-button {
|
||||||
|
background: inherit;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
min-height: 25px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -42,7 +42,7 @@ function generateRouterMap (section) {
|
|||||||
name: section.name,
|
name: section.name,
|
||||||
path: '/' + section.name,
|
path: '/' + section.name,
|
||||||
hidden: section.hidden,
|
hidden: section.hidden,
|
||||||
meta: { title: section.title, icon: section.icon, docHelp: section.docHelp },
|
meta: { title: section.title, icon: section.icon, docHelp: section.docHelp, searchFilters: section.searchFilters },
|
||||||
component: RouteView
|
component: RouteView
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +67,7 @@ function generateRouterMap (section) {
|
|||||||
params: child.params ? child.params : {},
|
params: child.params ? child.params : {},
|
||||||
columns: child.columns,
|
columns: child.columns,
|
||||||
details: child.details,
|
details: child.details,
|
||||||
|
searchFilters: child.searchFilters,
|
||||||
related: child.related,
|
related: child.related,
|
||||||
actions: child.actions,
|
actions: child.actions,
|
||||||
tabs: child.tabs
|
tabs: child.tabs
|
||||||
@ -86,6 +87,7 @@ function generateRouterMap (section) {
|
|||||||
resourceType: child.resourceType,
|
resourceType: child.resourceType,
|
||||||
params: child.params ? child.params : {},
|
params: child.params ? child.params : {},
|
||||||
details: child.details,
|
details: child.details,
|
||||||
|
searchFilters: child.searchFilters,
|
||||||
related: child.related,
|
related: child.related,
|
||||||
tabs: child.tabs,
|
tabs: child.tabs,
|
||||||
actions: child.actions ? child.actions : []
|
actions: child.actions ? child.actions : []
|
||||||
@ -142,6 +144,7 @@ function generateRouterMap (section) {
|
|||||||
params: section.params ? section.params : {},
|
params: section.params ? section.params : {},
|
||||||
details: section.details,
|
details: section.details,
|
||||||
related: section.related,
|
related: section.related,
|
||||||
|
searchFilters: section.searchFilters,
|
||||||
tabs: section.tabs,
|
tabs: section.tabs,
|
||||||
actions: section.actions ? section.actions : []
|
actions: section.actions ? section.actions : []
|
||||||
},
|
},
|
||||||
|
|||||||
@ -61,6 +61,7 @@ export default {
|
|||||||
}
|
}
|
||||||
return fields
|
return fields
|
||||||
},
|
},
|
||||||
|
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
|
||||||
details: ['displayname', 'name', 'id', 'state', 'ipaddress', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename'],
|
details: ['displayname', 'name', 'id', 'state', 'ipaddress', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'vmsnapshot',
|
name: 'vmsnapshot',
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export default {
|
|||||||
permission: ['listEvents'],
|
permission: ['listEvents'],
|
||||||
columns: ['username', 'description', 'state', 'level', 'type', 'account', 'domain', 'created'],
|
columns: ['username', 'description', 'state', 'level', 'type', 'account', 'domain', 'created'],
|
||||||
details: ['username', 'id', 'description', 'state', 'level', 'type', 'account', 'domain', 'created'],
|
details: ['username', 'id', 'description', 'state', 'level', 'type', 'account', 'domain', 'created'],
|
||||||
|
searchFilters: ['level', 'domainid', 'account', 'keyword'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'event',
|
name: 'event',
|
||||||
title: 'label.event.timeline',
|
title: 'label.event.timeline',
|
||||||
|
|||||||
@ -40,6 +40,7 @@ export default {
|
|||||||
return fields
|
return fields
|
||||||
},
|
},
|
||||||
details: ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 'isdynamicallyscalable', 'ispublic', 'isfeatured', 'crosszones', 'type', 'account', 'domain', 'created'],
|
details: ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 'isdynamicallyscalable', 'ispublic', 'isfeatured', 'crosszones', 'type', 'account', 'domain', 'created'],
|
||||||
|
searchFilters: ['name', 'zoneid', 'tags'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'vm',
|
name: 'vm',
|
||||||
title: 'label.instances',
|
title: 'label.instances',
|
||||||
@ -130,6 +131,7 @@ export default {
|
|||||||
filters: ['self', 'shared', 'featured', 'community'],
|
filters: ['self', 'shared', 'featured', 'community'],
|
||||||
columns: ['name', 'ostypename', 'account', 'domain'],
|
columns: ['name', 'ostypename', 'account', 'domain'],
|
||||||
details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'bootable', 'isready', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'crosszones', 'account', 'domain', 'created'],
|
details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'bootable', 'isready', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'crosszones', 'account', 'domain', 'created'],
|
||||||
|
searchFilters: ['name', 'zoneid', 'tags'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'vm',
|
name: 'vm',
|
||||||
title: 'label.instances',
|
title: 'label.instances',
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export default {
|
|||||||
permission: ['listRouters'],
|
permission: ['listRouters'],
|
||||||
params: { projectid: '-1' },
|
params: { projectid: '-1' },
|
||||||
columns: ['name', 'state', 'publicip', 'guestnetworkname', 'vpcname', 'redundantstate', 'version', 'hostname', 'account', 'zonename', 'requiresupgrade'],
|
columns: ['name', 'state', 'publicip', 'guestnetworkname', 'vpcname', 'redundantstate', 'version', 'hostname', 'account', 'zonename', 'requiresupgrade'],
|
||||||
|
searchFilters: ['name', 'zoneid', 'podid', 'clusterid'],
|
||||||
details: ['name', 'id', 'version', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created'],
|
details: ['name', 'id', 'version', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created'],
|
||||||
tabs: [{
|
tabs: [{
|
||||||
name: 'details',
|
name: 'details',
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export default {
|
|||||||
resourceType: 'Network',
|
resourceType: 'Network',
|
||||||
columns: ['name', 'state', 'type', 'cidr', 'ip6cidr', 'broadcasturi', 'account', 'zonename'],
|
columns: ['name', 'state', 'type', 'cidr', 'ip6cidr', 'broadcasturi', 'account', 'zonename'],
|
||||||
details: ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'zonename', 'account', 'domain'],
|
details: ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'zonename', 'account', 'domain'],
|
||||||
|
searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'tags'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'vm',
|
name: 'vm',
|
||||||
title: 'label.instances',
|
title: 'label.instances',
|
||||||
@ -113,6 +114,7 @@ export default {
|
|||||||
resourceType: 'Vpc',
|
resourceType: 'Vpc',
|
||||||
columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'zonename'],
|
columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'zonename'],
|
||||||
details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain'],
|
details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain'],
|
||||||
|
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'vm',
|
name: 'vm',
|
||||||
title: 'label.instances',
|
title: 'label.instances',
|
||||||
@ -554,6 +556,7 @@ export default {
|
|||||||
permission: ['listVpnCustomerGateways'],
|
permission: ['listVpnCustomerGateways'],
|
||||||
columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account', 'domain'],
|
columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account', 'domain'],
|
||||||
details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap', 'account', 'domain'],
|
details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap', 'account', 'domain'],
|
||||||
|
searchFilters: ['keyword', 'domainid', 'account'],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
api: 'createVpnCustomerGateway',
|
api: 'createVpnCustomerGateway',
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export default {
|
|||||||
permission: ['listProjects'],
|
permission: ['listProjects'],
|
||||||
resourceType: 'Project',
|
resourceType: 'Project',
|
||||||
columns: ['name', 'state', 'displaytext', 'account', 'domain'],
|
columns: ['name', 'state', 'displaytext', 'account', 'domain'],
|
||||||
|
searchFilters: ['name', 'displaytext', 'domainid', 'account'],
|
||||||
details: ['name', 'id', 'displaytext', 'projectaccountname', 'account', 'domain'],
|
details: ['name', 'id', 'displaytext', 'projectaccountname', 'account', 'domain'],
|
||||||
tabs: [
|
tabs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -64,6 +64,7 @@ export default {
|
|||||||
title: 'label.snapshots',
|
title: 'label.snapshots',
|
||||||
param: 'volumeid'
|
param: 'volumeid'
|
||||||
}],
|
}],
|
||||||
|
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'state', 'tags'],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
api: 'createVolume',
|
api: 'createVolume',
|
||||||
@ -231,6 +232,7 @@ export default {
|
|||||||
resourceType: 'Snapshot',
|
resourceType: 'Snapshot',
|
||||||
columns: ['name', 'state', 'volumename', 'intervaltype', 'created', 'account'],
|
columns: ['name', 'state', 'volumename', 'intervaltype', 'created', 'account'],
|
||||||
details: ['name', 'id', 'volumename', 'intervaltype', 'account', 'domain', 'created'],
|
details: ['name', 'id', 'volumename', 'intervaltype', 'account', 'domain', 'created'],
|
||||||
|
searchFilters: ['name', 'domainid', 'account', 'tags'],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
api: 'createTemplate',
|
api: 'createTemplate',
|
||||||
@ -283,6 +285,7 @@ export default {
|
|||||||
resourceType: 'VMSnapshot',
|
resourceType: 'VMSnapshot',
|
||||||
columns: ['displayname', 'state', 'type', 'current', 'parentName', 'created', 'account'],
|
columns: ['displayname', 'state', 'type', 'current', 'parentName', 'created', 'account'],
|
||||||
details: ['name', 'id', 'displayname', 'description', 'type', 'current', 'parentName', 'virtualmachineid', 'account', 'domain', 'created'],
|
details: ['name', 'id', 'displayname', 'description', 'type', 'current', 'parentName', 'virtualmachineid', 'account', 'domain', 'created'],
|
||||||
|
searchFilters: ['name', 'domainid', 'account', 'tags'],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
api: 'revertToVMSnapshot',
|
api: 'revertToVMSnapshot',
|
||||||
|
|||||||
@ -514,6 +514,7 @@
|
|||||||
"label.ciscovnmc.resource.details": "CiscoVNMC resource details",
|
"label.ciscovnmc.resource.details": "CiscoVNMC resource details",
|
||||||
"label.cks.cluster.size": "Cluster size (Worker nodes)",
|
"label.cks.cluster.size": "Cluster size (Worker nodes)",
|
||||||
"label.cleanup": "Clean up",
|
"label.cleanup": "Clean up",
|
||||||
|
"label.clear": "Clear",
|
||||||
"label.clear.list": "Clear list",
|
"label.clear.list": "Clear list",
|
||||||
"label.close": "Close",
|
"label.close": "Close",
|
||||||
"label.cloud.console": "Cloud Management Console",
|
"label.cloud.console": "Cloud Management Console",
|
||||||
@ -723,7 +724,7 @@
|
|||||||
"label.domain.name": "Domain Name",
|
"label.domain.name": "Domain Name",
|
||||||
"label.domain.router": "Domain router",
|
"label.domain.router": "Domain router",
|
||||||
"label.domain.suffix": "DNS Domain Suffix (i.e., xyz.com)",
|
"label.domain.suffix": "DNS Domain Suffix (i.e., xyz.com)",
|
||||||
"label.domainid": "Domain ID",
|
"label.domainid": "Domain",
|
||||||
"label.domainname": "Domain",
|
"label.domainname": "Domain",
|
||||||
"label.domainpath": "Domain",
|
"label.domainpath": "Domain",
|
||||||
"label.domains": "Domains",
|
"label.domains": "Domains",
|
||||||
@ -801,6 +802,8 @@
|
|||||||
"label.example": "Example",
|
"label.example": "Example",
|
||||||
"label.existingnetworks": "Existing networks",
|
"label.existingnetworks": "Existing networks",
|
||||||
"label.expunge": "Expunge",
|
"label.expunge": "Expunge",
|
||||||
|
"label.expunged": "Expunged",
|
||||||
|
"label.expunging": "Expunging",
|
||||||
"label.external.link": "External link",
|
"label.external.link": "External link",
|
||||||
"label.externalid": "External Id",
|
"label.externalid": "External Id",
|
||||||
"label.externalloadbalanceripaddress": "External load balancer IP address",
|
"label.externalloadbalanceripaddress": "External load balancer IP address",
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import { api } from '@/api'
|
|||||||
import { message, notification } from 'ant-design-vue'
|
import { message, notification } from 'ant-design-vue'
|
||||||
|
|
||||||
export const pollJobPlugin = {
|
export const pollJobPlugin = {
|
||||||
|
|
||||||
install (Vue) {
|
install (Vue) {
|
||||||
Vue.prototype.$pollJob = function (options) {
|
Vue.prototype.$pollJob = function (options) {
|
||||||
/**
|
/**
|
||||||
@ -111,7 +110,6 @@ export const pollJobPlugin = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const notifierPlugin = {
|
export const notifierPlugin = {
|
||||||
|
|
||||||
install (Vue) {
|
install (Vue) {
|
||||||
Vue.prototype.$notifyError = function (error) {
|
Vue.prototype.$notifyError = function (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
|||||||
@ -68,13 +68,11 @@
|
|||||||
:dataView="dataView"
|
:dataView="dataView"
|
||||||
:resource="resource"
|
:resource="resource"
|
||||||
@exec-action="execAction"/>
|
@exec-action="execAction"/>
|
||||||
<a-input-search
|
<search-view
|
||||||
v-if="!dataView"
|
v-if="!dataView"
|
||||||
style="width: 100%; display: table-cell"
|
:searchFilters="searchFilters"
|
||||||
:placeholder="$t('label.search')"
|
:selectedFilters="paramsFilters"
|
||||||
v-model="searchQuery"
|
:apiName="apiName"/>
|
||||||
allowClear
|
|
||||||
@search="onSearch" />
|
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
@ -304,7 +302,7 @@
|
|||||||
:current="page"
|
:current="page"
|
||||||
:pageSize="pageSize"
|
:pageSize="pageSize"
|
||||||
:total="itemCount"
|
:total="itemCount"
|
||||||
:showTotal="total => `Showing ${1+((page-1)*pageSize)}-${Math.min(page*pageSize, total)} of ${total} items`"
|
:showTotal="total => `Showing ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} of ${total} items`"
|
||||||
:pageSizeOptions="device === 'desktop' ? ['20', '50', '100', '500'] : ['10', '20', '50', '100', '500']"
|
:pageSizeOptions="device === 'desktop' ? ['20', '50', '100', '500'] : ['10', '20', '50', '100', '500']"
|
||||||
@change="changePage"
|
@change="changePage"
|
||||||
@showSizeChange="changePageSize"
|
@showSizeChange="changePageSize"
|
||||||
@ -326,6 +324,7 @@ import Status from '@/components/widgets/Status'
|
|||||||
import ListView from '@/components/view/ListView'
|
import ListView from '@/components/view/ListView'
|
||||||
import ResourceView from '@/components/view/ResourceView'
|
import ResourceView from '@/components/view/ResourceView'
|
||||||
import ActionButton from '@/components/view/ActionButton'
|
import ActionButton from '@/components/view/ActionButton'
|
||||||
|
import SearchView from '@/components/view/SearchView'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Resource',
|
name: 'Resource',
|
||||||
@ -335,7 +334,8 @@ export default {
|
|||||||
ResourceView,
|
ResourceView,
|
||||||
ListView,
|
ListView,
|
||||||
Status,
|
Status,
|
||||||
ActionButton
|
ActionButton,
|
||||||
|
SearchView
|
||||||
},
|
},
|
||||||
mixins: [mixinDevice],
|
mixins: [mixinDevice],
|
||||||
provide: function () {
|
provide: function () {
|
||||||
@ -344,6 +344,9 @@ export default {
|
|||||||
parentToggleLoading: this.toggleLoading,
|
parentToggleLoading: this.toggleLoading,
|
||||||
parentStartLoading: this.startLoading,
|
parentStartLoading: this.startLoading,
|
||||||
parentFinishLoading: this.finishLoading,
|
parentFinishLoading: this.finishLoading,
|
||||||
|
parentSearch: this.onSearch,
|
||||||
|
parentChangeFilter: this.changeFilter,
|
||||||
|
parentFilter: this.onFilter,
|
||||||
parentChangeResource: this.changeResource,
|
parentChangeResource: this.changeResource,
|
||||||
parentPollActionCompletion: this.pollActionCompletion,
|
parentPollActionCompletion: this.pollActionCompletion,
|
||||||
parentEditTariffAction: () => {}
|
parentEditTariffAction: () => {}
|
||||||
@ -367,6 +370,8 @@ export default {
|
|||||||
dataView: false,
|
dataView: false,
|
||||||
selectedFilter: '',
|
selectedFilter: '',
|
||||||
filters: [],
|
filters: [],
|
||||||
|
searchFilters: [],
|
||||||
|
paramsFilters: {},
|
||||||
actions: [],
|
actions: [],
|
||||||
formModel: {},
|
formModel: {},
|
||||||
confirmDirty: false
|
confirmDirty: false
|
||||||
@ -397,6 +402,7 @@ export default {
|
|||||||
'$route' (to, from) {
|
'$route' (to, from) {
|
||||||
if (to.fullPath !== from.fullPath && !to.fullPath.includes('action/')) {
|
if (to.fullPath !== from.fullPath && !to.fullPath.includes('action/')) {
|
||||||
this.searchQuery = ''
|
this.searchQuery = ''
|
||||||
|
this.paramsFilters = {}
|
||||||
this.page = 1
|
this.page = 1
|
||||||
this.itemCount = 0
|
this.itemCount = 0
|
||||||
this.selectedFilter = ''
|
this.selectedFilter = ''
|
||||||
@ -449,6 +455,11 @@ export default {
|
|||||||
} else if (this.$route.meta.params) {
|
} else if (this.$route.meta.params) {
|
||||||
Object.assign(params, this.$route.meta.params)
|
Object.assign(params, this.$route.meta.params)
|
||||||
}
|
}
|
||||||
|
if (Object.keys(this.paramsFilters).length > 0) {
|
||||||
|
Object.assign(params, this.paramsFilters)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchFilters = this.$route && this.$route.meta && this.$route.meta.searchFilters
|
||||||
|
|
||||||
if (this.$route && this.$route.params && this.$route.params.id) {
|
if (this.$route && this.$route.params && this.$route.params.id) {
|
||||||
this.dataView = true
|
this.dataView = true
|
||||||
@ -616,6 +627,16 @@ export default {
|
|||||||
this.$emit('change-resource', this.resource)
|
this.$emit('change-resource', this.resource)
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
if (Object.keys(this.paramsFilters).length > 0) {
|
||||||
|
this.itemCount = 0
|
||||||
|
this.items = []
|
||||||
|
this.$message.error({
|
||||||
|
content: error.response.headers['x-description'],
|
||||||
|
duration: 5
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.$notifyError(error)
|
this.$notifyError(error)
|
||||||
|
|
||||||
if ([401, 405].includes(error.response.status)) {
|
if ([401, 405].includes(error.response.status)) {
|
||||||
@ -634,10 +655,19 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onSearch (value) {
|
onSearch (value) {
|
||||||
|
this.paramsFilters = {}
|
||||||
this.searchQuery = value
|
this.searchQuery = value
|
||||||
this.page = 1
|
this.page = 1
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
|
onFilter (filters) {
|
||||||
|
this.paramsFilters = {}
|
||||||
|
if (filters && Object.keys(filters).length > 0) {
|
||||||
|
this.paramsFilters = filters
|
||||||
|
}
|
||||||
|
this.page = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
closeAction () {
|
closeAction () {
|
||||||
this.actionLoading = false
|
this.actionLoading = false
|
||||||
this.showAction = false
|
this.showAction = false
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user