You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/07/05 06:19:41 UTC
[cloudstack-primate] branch master updated: views: custom search
framework for list views (#235)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new 6b1c61c views: custom search framework for list views (#235)
6b1c61c is described below
commit 6b1c61c8d2a6a7985ba4b6a74acee3df62d8fa84
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Sun Jul 5 13:19:30 2020 +0700
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 <ro...@shapeblue.com>
Co-authored-by: Rohit Yadav <ro...@shapeblue.com>
---
src/components/view/SearchView.vue | 481 ++++++++++++++++++++++++++++++++++++
src/config/router.js | 5 +-
src/config/section/compute.js | 1 +
src/config/section/event.js | 1 +
src/config/section/image.js | 2 +
src/config/section/infra/routers.js | 1 +
src/config/section/network.js | 3 +
src/config/section/project.js | 1 +
src/config/section/storage.js | 3 +
src/locales/en.json | 5 +-
src/utils/plugins.js | 2 -
src/views/AutogenView.vue | 46 +++-
12 files changed, 539 insertions(+), 12 deletions(-)
diff --git a/src/components/view/SearchView.vue b/src/components/view/SearchView.vue
new file mode 100644
index 0000000..6e5cfb3
--- /dev/null
+++ b/src/components/view/SearchView.vue
@@ -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>
diff --git a/src/config/router.js b/src/config/router.js
index cad2aee..0c0699b 100644
--- a/src/config/router.js
+++ b/src/config/router.js
@@ -42,7 +42,7 @@ function generateRouterMap (section) {
name: section.name,
path: '/' + section.name,
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
}
@@ -67,6 +67,7 @@ function generateRouterMap (section) {
params: child.params ? child.params : {},
columns: child.columns,
details: child.details,
+ searchFilters: child.searchFilters,
related: child.related,
actions: child.actions,
tabs: child.tabs
@@ -86,6 +87,7 @@ function generateRouterMap (section) {
resourceType: child.resourceType,
params: child.params ? child.params : {},
details: child.details,
+ searchFilters: child.searchFilters,
related: child.related,
tabs: child.tabs,
actions: child.actions ? child.actions : []
@@ -142,6 +144,7 @@ function generateRouterMap (section) {
params: section.params ? section.params : {},
details: section.details,
related: section.related,
+ searchFilters: section.searchFilters,
tabs: section.tabs,
actions: section.actions ? section.actions : []
},
diff --git a/src/config/section/compute.js b/src/config/section/compute.js
index c2d0df2..72bcdcf 100644
--- a/src/config/section/compute.js
+++ b/src/config/section/compute.js
@@ -61,6 +61,7 @@ export default {
}
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'],
related: [{
name: 'vmsnapshot',
diff --git a/src/config/section/event.js b/src/config/section/event.js
index 7924286..eee70fa 100644
--- a/src/config/section/event.js
+++ b/src/config/section/event.js
@@ -23,6 +23,7 @@ export default {
permission: ['listEvents'],
columns: ['username', 'description', 'state', 'level', 'type', 'account', 'domain', 'created'],
details: ['username', 'id', 'description', 'state', 'level', 'type', 'account', 'domain', 'created'],
+ searchFilters: ['level', 'domainid', 'account', 'keyword'],
related: [{
name: 'event',
title: 'label.event.timeline',
diff --git a/src/config/section/image.js b/src/config/section/image.js
index 6c2fcec..41a1970 100644
--- a/src/config/section/image.js
+++ b/src/config/section/image.js
@@ -40,6 +40,7 @@ export default {
return fields
},
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: [{
name: 'vm',
title: 'label.instances',
@@ -130,6 +131,7 @@ export default {
filters: ['self', 'shared', 'featured', 'community'],
columns: ['name', 'ostypename', 'account', 'domain'],
details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'bootable', 'isready', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'crosszones', 'account', 'domain', 'created'],
+ searchFilters: ['name', 'zoneid', 'tags'],
related: [{
name: 'vm',
title: 'label.instances',
diff --git a/src/config/section/infra/routers.js b/src/config/section/infra/routers.js
index 0be025d..43679aa 100644
--- a/src/config/section/infra/routers.js
+++ b/src/config/section/infra/routers.js
@@ -23,6 +23,7 @@ export default {
permission: ['listRouters'],
params: { projectid: '-1' },
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'],
tabs: [{
name: 'details',
diff --git a/src/config/section/network.js b/src/config/section/network.js
index 95f9db0..3cf924d 100644
--- a/src/config/section/network.js
+++ b/src/config/section/network.js
@@ -31,6 +31,7 @@ export default {
resourceType: 'Network',
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'],
+ searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'tags'],
related: [{
name: 'vm',
title: 'label.instances',
@@ -113,6 +114,7 @@ export default {
resourceType: 'Vpc',
columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'zonename'],
details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain'],
+ searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
related: [{
name: 'vm',
title: 'label.instances',
@@ -554,6 +556,7 @@ export default {
permission: ['listVpnCustomerGateways'],
columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account', 'domain'],
details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap', 'account', 'domain'],
+ searchFilters: ['keyword', 'domainid', 'account'],
actions: [
{
api: 'createVpnCustomerGateway',
diff --git a/src/config/section/project.js b/src/config/section/project.js
index e5fdb3f..56e0d46 100644
--- a/src/config/section/project.js
+++ b/src/config/section/project.js
@@ -23,6 +23,7 @@ export default {
permission: ['listProjects'],
resourceType: 'Project',
columns: ['name', 'state', 'displaytext', 'account', 'domain'],
+ searchFilters: ['name', 'displaytext', 'domainid', 'account'],
details: ['name', 'id', 'displaytext', 'projectaccountname', 'account', 'domain'],
tabs: [
{
diff --git a/src/config/section/storage.js b/src/config/section/storage.js
index ac41970..970d979 100644
--- a/src/config/section/storage.js
+++ b/src/config/section/storage.js
@@ -64,6 +64,7 @@ export default {
title: 'label.snapshots',
param: 'volumeid'
}],
+ searchFilters: ['name', 'zoneid', 'domainid', 'account', 'state', 'tags'],
actions: [
{
api: 'createVolume',
@@ -231,6 +232,7 @@ export default {
resourceType: 'Snapshot',
columns: ['name', 'state', 'volumename', 'intervaltype', 'created', 'account'],
details: ['name', 'id', 'volumename', 'intervaltype', 'account', 'domain', 'created'],
+ searchFilters: ['name', 'domainid', 'account', 'tags'],
actions: [
{
api: 'createTemplate',
@@ -283,6 +285,7 @@ export default {
resourceType: 'VMSnapshot',
columns: ['displayname', 'state', 'type', 'current', 'parentName', 'created', 'account'],
details: ['name', 'id', 'displayname', 'description', 'type', 'current', 'parentName', 'virtualmachineid', 'account', 'domain', 'created'],
+ searchFilters: ['name', 'domainid', 'account', 'tags'],
actions: [
{
api: 'revertToVMSnapshot',
diff --git a/src/locales/en.json b/src/locales/en.json
index 24f0ede..96d1229 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -514,6 +514,7 @@
"label.ciscovnmc.resource.details": "CiscoVNMC resource details",
"label.cks.cluster.size": "Cluster size (Worker nodes)",
"label.cleanup": "Clean up",
+"label.clear": "Clear",
"label.clear.list": "Clear list",
"label.close": "Close",
"label.cloud.console": "Cloud Management Console",
@@ -723,7 +724,7 @@
"label.domain.name": "Domain Name",
"label.domain.router": "Domain router",
"label.domain.suffix": "DNS Domain Suffix (i.e., xyz.com)",
-"label.domainid": "Domain ID",
+"label.domainid": "Domain",
"label.domainname": "Domain",
"label.domainpath": "Domain",
"label.domains": "Domains",
@@ -801,6 +802,8 @@
"label.example": "Example",
"label.existingnetworks": "Existing networks",
"label.expunge": "Expunge",
+"label.expunged": "Expunged",
+"label.expunging": "Expunging",
"label.external.link": "External link",
"label.externalid": "External Id",
"label.externalloadbalanceripaddress": "External load balancer IP address",
diff --git a/src/utils/plugins.js b/src/utils/plugins.js
index 0e3e7f9..5b9328d 100644
--- a/src/utils/plugins.js
+++ b/src/utils/plugins.js
@@ -21,7 +21,6 @@ import { api } from '@/api'
import { message, notification } from 'ant-design-vue'
export const pollJobPlugin = {
-
install (Vue) {
Vue.prototype.$pollJob = function (options) {
/**
@@ -111,7 +110,6 @@ export const pollJobPlugin = {
}
export const notifierPlugin = {
-
install (Vue) {
Vue.prototype.$notifyError = function (error) {
console.log(error)
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index f1b5f22..c2815a6 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -68,13 +68,11 @@
:dataView="dataView"
:resource="resource"
@exec-action="execAction"/>
- <a-input-search
+ <search-view
v-if="!dataView"
- style="width: 100%; display: table-cell"
- :placeholder="$t('label.search')"
- v-model="searchQuery"
- allowClear
- @search="onSearch" />
+ :searchFilters="searchFilters"
+ :selectedFilters="paramsFilters"
+ :apiName="apiName"/>
</a-col>
</a-row>
</a-card>
@@ -304,7 +302,7 @@
:current="page"
:pageSize="pageSize"
: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']"
@change="changePage"
@showSizeChange="changePageSize"
@@ -326,6 +324,7 @@ import Status from '@/components/widgets/Status'
import ListView from '@/components/view/ListView'
import ResourceView from '@/components/view/ResourceView'
import ActionButton from '@/components/view/ActionButton'
+import SearchView from '@/components/view/SearchView'
export default {
name: 'Resource',
@@ -335,7 +334,8 @@ export default {
ResourceView,
ListView,
Status,
- ActionButton
+ ActionButton,
+ SearchView
},
mixins: [mixinDevice],
provide: function () {
@@ -344,6 +344,9 @@ export default {
parentToggleLoading: this.toggleLoading,
parentStartLoading: this.startLoading,
parentFinishLoading: this.finishLoading,
+ parentSearch: this.onSearch,
+ parentChangeFilter: this.changeFilter,
+ parentFilter: this.onFilter,
parentChangeResource: this.changeResource,
parentPollActionCompletion: this.pollActionCompletion,
parentEditTariffAction: () => {}
@@ -367,6 +370,8 @@ export default {
dataView: false,
selectedFilter: '',
filters: [],
+ searchFilters: [],
+ paramsFilters: {},
actions: [],
formModel: {},
confirmDirty: false
@@ -397,6 +402,7 @@ export default {
'$route' (to, from) {
if (to.fullPath !== from.fullPath && !to.fullPath.includes('action/')) {
this.searchQuery = ''
+ this.paramsFilters = {}
this.page = 1
this.itemCount = 0
this.selectedFilter = ''
@@ -449,6 +455,11 @@ export default {
} else if (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) {
this.dataView = true
@@ -616,6 +627,16 @@ export default {
this.$emit('change-resource', this.resource)
}
}).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)
if ([401, 405].includes(error.response.status)) {
@@ -634,10 +655,19 @@ export default {
})
},
onSearch (value) {
+ this.paramsFilters = {}
this.searchQuery = value
this.page = 1
this.fetchData()
},
+ onFilter (filters) {
+ this.paramsFilters = {}
+ if (filters && Object.keys(filters).length > 0) {
+ this.paramsFilters = filters
+ }
+ this.page = 1
+ this.fetchData()
+ },
closeAction () {
this.actionLoading = false
this.showAction = false