Add input prompt function for all search page (#275)

Add input prompt function for all search page, except tag rule page.
PS: I noticed that the author has already designed a search box component. But it is not fully finished yet. When you are finished, I can help extract the common code to simplify the prompt function code.
diff --git a/dubbo-admin-frontend/src/components/ServiceSearch.vue b/dubbo-admin-frontend/src/components/ServiceSearch.vue
index 1ddb566..2f69ae2 100644
--- a/dubbo-admin-frontend/src/components/ServiceSearch.vue
+++ b/dubbo-admin-frontend/src/components/ServiceSearch.vue
@@ -25,7 +25,7 @@
               <v-layout row wrap>
                 <v-combobox
                   id="serviceSearch"
-                  :loading="loading"
+                  :loading="searchLoading"
                   :items="typeAhead"
                   :search-input.sync="input"
                   v-model="filter"
@@ -147,11 +147,9 @@
         }
       ],
       timerID: null,
-      loading: false,
+      searchLoading: false,
       selected: 0,
-      serviceItem: [],
       input: null,
-      appItem: [],
       typeAhead: [],
       services: [],
       filter: '',
@@ -220,17 +218,13 @@
         // Simulated ajax query
         this.timerID = setTimeout(() => {
           if (v && v.length >= 4) {
-            this.loading = true
+            this.searchLoading = true
             if (this.selected === 0) {
-              this.typeAhead = this.serviceItem.filter(e => {
-                return (e || '').toLowerCase().indexOf((v || '').toLowerCase()) > -1
-              })
+              this.typeAhead = this.$store.getters.getServiceItems(v)
             } else if (this.selected === 2) {
-              this.typeAhead = this.appItem.filter(e => {
-                return (e || '').toLowerCase().indexOf((v || '').toLowerCase()) > -1
-              })
+              this.typeAhead = this.$store.getters.getAppItems(v)
             }
-            this.loading = false
+            this.searchLoading = false
             this.timerID = null
           } else {
             this.typeAhead = []
@@ -286,10 +280,11 @@
     },
     mounted: function () {
       this.setHeaders()
+      this.$store.dispatch('loadServiceItems')
+      this.$store.dispatch('loadAppItems')
       let query = this.$route.query
       let filter = null
       let pattern = null
-      let vm = this
       Object.keys(query).forEach(function (key) {
         if (key === 'filter') {
           filter = query[key]
@@ -315,19 +310,6 @@
         pattern = 'service'
         this.search(this.filter, pattern, true)
       }
-      this.$axios.get('/services')
-        .then(response => {
-          if (response.status === 200) {
-            vm.serviceItem = response.data
-          }
-        })
-
-      this.$axios.get('/applications')
-        .then(response => {
-          if (response.status === 200) {
-            vm.appItem = response.data
-          }
-        })
     }
 
   }
diff --git a/dubbo-admin-frontend/src/components/governance/AccessControl.vue b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
index 0c59e0d..d4480ea 100644
--- a/dubbo-admin-frontend/src/components/governance/AccessControl.vue
+++ b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
@@ -26,6 +26,10 @@
                 <v-combobox
                   id="serviceSearch"
                   v-model="filter"
+                  :loading="searchLoading"
+                  :items="typeAhead"
+                  :search-input.sync="input"
+                  @keyup.enter="search"
                   flat
                   append-icon=""
                   hide-no-data
@@ -240,6 +244,10 @@
     serviceHeaders: [],
     appHeaders: [],
     accesses: [],
+    searchLoading: false,
+    typeAhead: [],
+    input: null,
+    timerID: null,
     modal: {
       enable: false,
       readonly: false,
@@ -303,6 +311,26 @@
         }
       ]
     },
+    querySelections (v) {
+      if (this.timerID) {
+        clearTimeout(this.timerID)
+      }
+      // Simulated ajax query
+      this.timerID = setTimeout(() => {
+        if (v && v.length >= 4) {
+          this.searchLoading = true
+          if (this.selected === 0) {
+            this.typeAhead = this.$store.getters.getServiceItems(v)
+          } else if (this.selected === 1) {
+            this.typeAhead = this.$store.getters.getAppItems(v)
+          }
+          this.searchLoading = false
+          this.timerID = null
+        } else {
+          this.typeAhead = []
+        }
+      }, 500)
+    },
     search () {
       if (!this.filter) {
         this.filter = document.querySelector('#serviceSearch').value.trim()
@@ -468,6 +496,9 @@
     }
   },
   watch: {
+    input (val) {
+      this.querySelections(val)
+    },
     area () {
       this.setAppHeaders()
       this.setServiceHeaders()
@@ -476,6 +507,8 @@
   mounted () {
     this.setAppHeaders()
     this.setServiceHeaders()
+    this.$store.dispatch('loadServiceItems')
+    this.$store.dispatch('loadAppItems')
     let query = this.$route.query
     if ('service' in query) {
       this.filter = query['service']
diff --git a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
index 7db8fad..42771d9 100644
--- a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
+++ b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
@@ -25,6 +25,10 @@
               <v-layout row wrap>
                 <v-combobox
                   id="serviceSearch"
+                  :loading="searchLoading"
+                  :items="typeAhead"
+                  :search-input.sync="input"
+                  @keyup.enter="submit"
                   v-model="filter"
                   flat
                   append-icon=""
@@ -198,6 +202,10 @@
       warnText: '',
       warnStatus: {},
       height: 0,
+      searchLoading: false,
+      typeAhead: [],
+      input: null,
+      timerID: null,
       operations: [
         {id: 0, icon: 'visibility', tooltip: 'view'},
         {id: 1, icon: 'edit', tooltip: 'edit'},
@@ -274,6 +282,26 @@
           }
         ]
       },
+      querySelections (v) {
+        if (this.timerID) {
+          clearTimeout(this.timerID)
+        }
+        // Simulated ajax query
+        this.timerID = setTimeout(() => {
+          if (v && v.length >= 4) {
+            this.searchLoading = true
+            if (this.selected === 0) {
+              this.typeAhead = this.$store.getters.getServiceItems(v)
+            } else if (this.selected === 1) {
+              this.typeAhead = this.$store.getters.getAppItems(v)
+            }
+            this.searchLoading = false
+            this.timerID = null
+          } else {
+            this.typeAhead = []
+          }
+        }, 500)
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -448,6 +476,9 @@
       }
     },
     watch: {
+      input (val) {
+        this.querySelections(val)
+      },
       area () {
         this.setServiceHeaders()
         this.setAppHeaders()
@@ -456,6 +487,8 @@
     mounted: function () {
       this.setServiceHeaders()
       this.setAppHeaders()
+      this.$store.dispatch('loadServiceItems')
+      this.$store.dispatch('loadAppItems')
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/Overrides.vue b/dubbo-admin-frontend/src/components/governance/Overrides.vue
index b2bedc6..d2f48b6 100644
--- a/dubbo-admin-frontend/src/components/governance/Overrides.vue
+++ b/dubbo-admin-frontend/src/components/governance/Overrides.vue
@@ -26,6 +26,10 @@
                 <v-combobox
                   id="serviceSearch"
                   v-model="filter"
+                  :loading="searchLoading"
+                  :items="typeAhead"
+                  :search-input.sync="input"
+                  @keyup.enter="submit"
                   flat
                   append-icon=""
                   hide-no-data
@@ -180,6 +184,10 @@
       warnStatus: {},
       height: 0,
       operations: operations,
+      searchLoading: false,
+      typeAhead: [],
+      input: null,
+      timerID: null,
       serviceConfigs: [
       ],
       appConfigs: [
@@ -229,6 +237,26 @@
           }
         ]
       },
+      querySelections (v) {
+        if (this.timerID) {
+          clearTimeout(this.timerID)
+        }
+        // Simulated ajax query
+        this.timerID = setTimeout(() => {
+          if (v && v.length >= 4) {
+            this.searchLoading = true
+            if (this.selected === 0) {
+              this.typeAhead = this.$store.getters.getServiceItems(v)
+            } else if (this.selected === 1) {
+              this.typeAhead = this.$store.getters.getAppItems(v)
+            }
+            this.searchLoading = false
+            this.timerID = null
+          } else {
+            this.typeAhead = []
+          }
+        }, 500)
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -435,6 +463,9 @@
       }
     },
     watch: {
+      input (val) {
+        this.querySelections(val)
+      },
       area () {
         this.setAppHeaders()
         this.setServiceHeaders()
@@ -443,6 +474,8 @@
     mounted: function () {
       this.setAppHeaders()
       this.setServiceHeaders()
+      this.$store.dispatch('loadServiceItems')
+      this.$store.dispatch('loadAppItems')
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
index 5f95b4e..a033c4b 100644
--- a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
@@ -26,6 +26,10 @@
                 <v-combobox
                   id="serviceSearch"
                   v-model="filter"
+                  :loading="searchLoading"
+                  :items="typeAhead"
+                  :search-input.sync="input"
+                  @keyup.enter="submit"
                   flat
                   append-icon=""
                   hide-no-data
@@ -179,6 +183,10 @@
       warnText: '',
       warnStatus: {},
       height: 0,
+      searchLoading: false,
+      typeAhead: [],
+      input: null,
+      timerID: null,
       operations: operations,
       serviceRoutingRules: [
       ],
@@ -242,6 +250,26 @@
           }
         ]
       },
+      querySelections (v) {
+        if (this.timerID) {
+          clearTimeout(this.timerID)
+        }
+        // Simulated ajax query
+        this.timerID = setTimeout(() => {
+          if (v && v.length >= 4) {
+            this.searchLoading = true
+            if (this.selected === 0) {
+              this.typeAhead = this.$store.getters.getServiceItems(v)
+            } else if (this.selected === 1) {
+              this.typeAhead = this.$store.getters.getAppItems(v)
+            }
+            this.searchLoading = false
+            this.timerID = null
+          } else {
+            this.typeAhead = []
+          }
+        }, 500)
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -443,6 +471,9 @@
       }
     },
     watch: {
+      input (val) {
+        this.querySelections(val)
+      },
       area () {
         this.setAppHeaders()
         this.setServiceHeaders()
@@ -451,6 +482,8 @@
     mounted: function () {
       this.setAppHeaders()
       this.setServiceHeaders()
+      this.$store.dispatch('loadServiceItems')
+      this.$store.dispatch('loadAppItems')
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
index 0bbcf5f..8f266c2 100644
--- a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
+++ b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
@@ -26,6 +26,10 @@
                 <v-combobox
                   id="serviceSearch"
                   v-model="filter"
+                  :items="typeAhead"
+                  :search-input.sync="input"
+                  :loading="searchLoading"
+                  @keyup.enter="submit"
                   flat
                   append-icon=""
                   hide-no-data
@@ -196,6 +200,10 @@
       warnText: '',
       warnStatus: {},
       height: 0,
+      searchLoading: false,
+      typeAhead: [],
+      input: null,
+      timerID: null,
       operations: [
         {id: 0, icon: 'visibility', tooltip: 'view'},
         {id: 1, icon: 'edit', tooltip: 'edit'},
@@ -239,6 +247,26 @@
           }
         ]
       },
+      querySelections (v) {
+        if (this.timerID) {
+          clearTimeout(this.timerID)
+        }
+        // Simulated ajax query
+        this.timerID = setTimeout(() => {
+          if (v && v.length >= 4) {
+            this.searchLoading = true
+            if (this.selected === 0) {
+              this.typeAhead = this.$store.getters.getServiceItems(v)
+            } else if (this.selected === 1) {
+              this.typeAhead = this.$store.getters.getAppItems(v)
+            }
+            this.searchLoading = false
+            this.timerID = null
+          } else {
+            this.typeAhead = []
+          }
+        }, 500)
+      },
       setAppHeaders: function () {
         this.appHeaders = [
           {
@@ -427,11 +455,16 @@
       area () {
         this.setAppHeaders()
         this.setServiceHeaders()
+      },
+      input (val) {
+        this.querySelections(val)
       }
     },
     mounted: function () {
       this.setAppHeaders()
       this.setServiceHeaders()
+      this.$store.dispatch('loadServiceItems')
+      this.$store.dispatch('loadAppItems')
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/store/index.js b/dubbo-admin-frontend/src/store/index.js
index a631f4d..f894271 100644
--- a/dubbo-admin-frontend/src/store/index.js
+++ b/dubbo-admin-frontend/src/store/index.js
@@ -23,17 +23,66 @@
 export const store = new Vuex.Store({
   state: {
     appTitle: 'Dubbo OPS',
-    area: null
+    area: null,
+    serviceItems: null,
+    appItems: null
   },
   mutations: {
     setArea (state, area) {
       state.area = area
+    },
+    setServiceItems (state, serviceItems) {
+      state.serviceItems = serviceItems
+    },
+    setAppItems (state, appItems) {
+      state.appItems = appItems
     }
   },
   actions: {
     changeArea ({commit}, area) {
       commit('setArea', area)
+    },
+    /**
+     * Load service items from server, put results into storage.
+     */
+    loadServiceItems ({commit}) {
+      Vue.prototype.$axios.get('/services')
+        .then(response => {
+          if (response.status === 200) {
+            const serviceItems = response.data
+            commit('setServiceItems', serviceItems)
+          }
+        })
+    },
+    /**
+     * Load application items from server, put results into storage.
+     */
+    loadAppItems ({commit}) {
+      Vue.prototype.$axios.get('/applications')
+        .then(response => {
+          if (response.status === 200) {
+            const appItems = response.data
+            commit('setAppItems', appItems)
+          }
+        })
     }
   },
-  getters: {}
+  getters: {
+    /**
+     * Get service item arrays with filter
+     */
+    getServiceItems: (state) => (filter) => {
+      return state.serviceItems.filter(e => {
+        return (e || '').toLowerCase().indexOf((filter || '').toLowerCase()) > -1
+      })
+    },
+    /**
+     * Get application item arrays with filter
+     */
+    getAppItems: (state) => (filter) => {
+      return state.appItems.filter(e => {
+        return (e || '').toLowerCase().indexOf((filter || '').toLowerCase()) > -1
+      })
+    }
+  }
 })