Merge branch 'apache:master' into master
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..36def7c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,27 @@
+## Contributing to Dubbo
+Dubbo is released under the non-restrictive Apache 2.0 licenses and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. Contributions of all form to this repository is acceptable, as long as it follows the prescribed community guidelines enumerated below.
+
+### Sign the Contributor License Agreement
+Before we accept a non-trivial patch or pull request (PRs), we will need you to sign the Contributor License Agreement. Signing the contributors' agreement does not grant anyone commits rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors may get invited to join the core team that will grant them privileges to merge existing PRs.
+
+### Contact
+
+#### Mailing list
+
+The mailing list is the recommended way of pursuing a discussion on almost anything related to Dubbo. Please refer to this [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) for detailed documentation on how to subscribe.
+
+- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): the developer mailing list where you can ask questions about an issue you may have encountered while working with Dubbo.
+- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): the commit updates will get broadcasted on this mailing list. You can subscribe to it, should you be interested in following Dubbo's development.
+- [notifications@dubbo.apache.org](mailto:notifications-subscribe@dubbo.apache.org): all the Github [issue](https://github.com/apache/dubbo/issues) updates and [pull request](https://github.com/apache/dubbo/pulls) updates will be sent to this mailing list.
+
+### Reporting issue
+
+Please crete an issue here https://github.com/apache/dubbo-kubernetes
+
+
+### Contribution flow
+
+A rough outline of an ideal contributors' workflow is as follows:
+
+
+Thanks for contributing!
\ No newline at end of file
diff --git a/DEVELOPER.md b/DEVELOPER.md
new file mode 100644
index 0000000..1af28cf
--- /dev/null
+++ b/DEVELOPER.md
@@ -0,0 +1,5 @@
+# Developer documentation
+
+The source code guidance of this project. 
+
+With this documentation, developers can learn which part of the project they need to change in order to fix a bug or introduce a new feature. How can they verify their codes work as expected before submit a pull request, by running the project locally or running the test suite.
\ No newline at end of file
diff --git a/README.md b/README.md
index 986b011..1bf13bd 100644
--- a/README.md
+++ b/README.md
@@ -10,76 +10,124 @@
 [![codecov](https://codecov.io/gh/apache/dubbo-kubernetes/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-kubernetes)
 ![license](https://img.shields.io/badge/license-Apache--2.0-green.svg)
 
-This repository contains libraries and tools for creating and deploying Dubbo applications in any Kubernetes environment, i.e. on Kubernetes, Aliyun ACK, etc.
+**The universal Control Plane and Console for managing microservices on any environment - VM and Kubernetes.**
 
-## Prerequisites:
-* Ensure you have Go installed, version 1.20 or higher.
-* Make sure you install kubectl.
-* Ensure you have Dubboctl installed.
+![architecture](./docs/images/homepage/arch1.png)
 
-## Quick Start
-### Create a Dubbo application
-Use `dubboctl create` to create a project template.
-
-```shell
-dubboctl create -l java
-```
-
-This should generate a simple project with a demo service properly configured and is ready to run. 
-
-> For java developers, it's recommended to use [start.dubbo.apache.org]() or [IntelliJ IDEA plugin]() to generate more complicated templates.
-
-### Deploy application to Kubernetes
-Before deploying the application, let's install Nacos, Zookeeper, Prometheus and other components necessary for running a Dubbo application or microservice cluster.
-
-```shell
-dubboctl install --profile=demo # This will install Nacos, Prometheus, Grafana, Admin, etc.
-```
-
-Next, build your application as docker image and deploy it into kubernetes cluster with `dubboctl deploy`, it will do the following two steps:
-
-1. Build your application from source code into docker image and push the image to remote repository.
-2. Generate all the kubernetes configurations (e.g., deployments, services, load balancers) needed to run your application on vanilla Kubernetes.
-
-```shell
-dubboctl deploy --out=deployment.yml
-```
-
-Finally, apply manifests into kubernetes cluster.
-
-```shell
-kubectl apply -f deployment.yml
-```
-
-### Monitor and manage your application
-We already have the application up and running, now it's time to continuously monitor the status or manage the traffics of our applications.
-
-#### Admin
-Run the following command and open `http://localhost:38080/admin/` with your favourite browser.
-
-```shell
-dubboctl dashboard admin
-```
-
-![Admin Console]()
+## Quick Start (under development)
+> NOTICE: As the project has not been officially released yet, the following commands may not run properly. The best way for now is to refer to the [Developer's Guide](./DEVELOPER.md) to learn how to download the source code and build it locally!
 
 
-![Admin Grafana]()
+1. Download `dubbo-control-plane` binary package.
+    ```shell
+    curl -L https://raw.githubusercontent.com/apache/dubbo-kubernetes/master/release/downloadDubbo.sh | sh -
+    
+    cd dubbo-$version
+    export PATH=$PWD/bin:$PATH
+    ```
 
-#### Tracing
-```shell
-dubboctl dashboard zipkin
-```
+2. Install `control-plane` on Kubernetes
+    ```shell
+    dubboctl install --profile=demo
+    ```
 
-#### Traffic Management
-Please refer to our official website to learn the traffic policies in Dubbo with some well-designed tasks.
-* Timeout
-* Accesslog
-* Region-aware traffic split
-* Weight-based traffic split
-* Circuit breaking
-* Canary release
+3. Check installation
+    ```shell
+    kubectl get services -n dubbo-system
+    ```
+
+4. Next, deploy Dubbo applications to Kubernetes as shown below:
+
+    ```yaml
+    apiVersion: v1
+    kind: Service
+    metadata:
+      name: demo-service
+      namespace: dubbo-demo
+    spec:
+      selector:
+        app: dubbo-demo
+      type: ClusterIP
+      ports:
+        - name: port1
+          protocol: http
+          port: 80
+          targetPort: 8080
+    ---
+    apiVersion: apps/v1
+    kind: Deployment
+    metadata:
+      name: example-app
+      namespace: dubbo-demo
+    spec:
+      ...
+      template:
+        metadata:
+          ...
+          labels:
+            app: dubbo-demo
+            dubbo.apache.org/service: dubbo-demo
+        spec:
+          containers:
+            ...
+    ```
+    
+    > If you want to create your own Dubbo application from scratch and deploy it, please use [dubboctl]() we provided below.
 
 
+5. Open the following page to check deployment status on control plane UI:
+    ```shell
+    kubectl port-forward svc/dubbo-control-plane \
+      -n dubbo-system 5681:5681
+    ```
+    
+    visit, 127.0.0.1:5681/admin
 
+   ![ui-demo](./docs/images/homepage/ui-demo.png)
 
+## Architecture
+
+![architecture](./docs/images/homepage/arch2.png)
+
+The microservcice architecture built with Dubbo Control Plane consists of two main components:
+
+- The **`Dubbo Control Plane`** configures the data plane - applications developed with Dubbo SDK, for handling service traffic. Users create [policies]() that the dubbo control plane processes to generate configurations for the data plane.
+- The data plane - the **`Dubbo SDK`**, connets directly to control plane and receives configurations that can work as the sources for service discovery, traffic routing, load balancing, etc.
+
+Dubbo Control Plane supports two deployment modes: **`kubernetes`** and **`universal`**.
+
+- **`kubernetes`** mode is like the classic Service Mesh architecture, with all microservices concepts bonded to kubernetes resources. Unlike classic service mesh solutions like istio, Dubbo favors a proxyless data plane deployment - with no envoy sidecar.
+- **`universal`** is the traditional microservice architecture that all Dubbo users are already familiar with. Unlike the kubernetes mode, it usually needs a dedicated registry like Nacos or Zookeeper for service discovery, etc.
+
+### Kubernetes
+In kubernetes mode, the control plane will interact directly with the Kubernetes API-SERVER, watching the kubernetes resources and transform them as xDS resources for service discovery and traffic management configurations.
+
+![kubernetes-mode](./docs/images/homepage/kubernetes-mode.png)
+
+We all know the `service` definitions of Kubernetes and Dubo are different, `Kubernetes Service` is more like an application concept run on a selected group of pods while `Dubbo Service` can mean a specific RPC service inside the application process. So how does dubbo control plane manages to bridge the `interface-application` gap, check [here]() for more details.
+
+### Universal
+In Universal mode, Dubbo still uses Nacos or Zookeeper as registries for service discovery, control plane then interact with registry directly to work as the console UI,  as the entry point for viewing and managing the cluster.
+
+![universal-mode](./docs/images/homepage/universal-mode.png)
+
+### Multiple clusters
+Dubbo Control Plane supports running your services in multiple zones. It is even possible to run with a mix of Kubernetes and Universal zones. Your microservice environment can include multiple isolated services, and workloads running in different regions, on different clouds, or in different datacenters. A zone can be a Kubernetes cluster, a VPC, or any other deployment you need to include in the same distributed microservice environment. The only condition is that all the data planes running within the zone must be able to connect to the other data planes in this same zone.
+
+Dubbo Control Plane supports a **`global`** deployment mode that can connect different **`zone`** region clusters. The picture below shows how it works.
+
+![multiple-cluster](./docs/images/homepage/multiple-cluster.png)
+
+## Roadmap
+
+- Security
+- Metrics
+- Cross-cluster communication
+- Console
+
+## Refereces
+
+- Dubboctl
+- Console UI Design
+- Dubbo java xDS implementation
+- Dubbo go xDS implementation
diff --git a/docs/dubboctl.md b/docs/dubboctl.md
new file mode 100644
index 0000000..67da43e
--- /dev/null
+++ b/docs/dubboctl.md
@@ -0,0 +1,41 @@
+
+This repository contains libraries and tools for creating and deploying Dubbo applications in any Kubernetes environment, i.e. on Kubernetes, Aliyun ACK, etc.
+
+## Prerequisites:
+* Ensure you have Go installed, version 1.20 or higher.
+* Make sure you install kubectl.
+* Ensure you have Dubboctl installed.
+
+## Quick Start
+### Create a Dubbo application
+Use `dubboctl create` to create a project template.
+
+```shell
+dubboctl create -l java
+```
+
+This should generate a simple project with a demo service properly configured and is ready to run. 
+
+> For java developers, it's recommended to use [start.dubbo.apache.org]() or [IntelliJ IDEA plugin]() to generate more complicated templates.
+
+### Deploy application to Kubernetes
+Before deploying the application, let's install Nacos, Zookeeper, Prometheus and other components necessary for running a Dubbo application or microservice cluster.
+
+```shell
+dubboctl install --profile=demo # This will install Nacos, Prometheus, Grafana, Admin, etc.
+```
+
+Next, build your application as docker image and deploy it into kubernetes cluster with `dubboctl deploy`, it will do the following two steps:
+
+1. Build your application from source code into docker image and push the image to remote repository.
+2. Generate all the kubernetes configurations (e.g., deployments, services, load balancers) needed to run your application on vanilla Kubernetes.
+
+```shell
+dubboctl deploy --out=deployment.yml
+```
+
+Finally, apply manifests into kubernetes cluster.
+
+```shell
+kubectl apply -f deployment.yml
+```
diff --git a/docs/images/homepage/arch1.png b/docs/images/homepage/arch1.png
new file mode 100644
index 0000000..4839b73
--- /dev/null
+++ b/docs/images/homepage/arch1.png
Binary files differ
diff --git a/docs/images/homepage/arch2.png b/docs/images/homepage/arch2.png
new file mode 100644
index 0000000..965ae57
--- /dev/null
+++ b/docs/images/homepage/arch2.png
Binary files differ
diff --git a/docs/images/homepage/kubernetes-mode.png b/docs/images/homepage/kubernetes-mode.png
new file mode 100644
index 0000000..87cf28b
--- /dev/null
+++ b/docs/images/homepage/kubernetes-mode.png
Binary files differ
diff --git a/docs/images/homepage/multiple-cluster.png b/docs/images/homepage/multiple-cluster.png
new file mode 100644
index 0000000..f92def9
--- /dev/null
+++ b/docs/images/homepage/multiple-cluster.png
Binary files differ
diff --git a/docs/images/homepage/ui-demo.png b/docs/images/homepage/ui-demo.png
new file mode 100644
index 0000000..0e62716
--- /dev/null
+++ b/docs/images/homepage/ui-demo.png
Binary files differ
diff --git a/docs/images/homepage/universal-mode.png b/docs/images/homepage/universal-mode.png
new file mode 100644
index 0000000..63b13b0
--- /dev/null
+++ b/docs/images/homepage/universal-mode.png
Binary files differ
diff --git a/ui-vue3/package.json b/ui-vue3/package.json
index 541c376..2a3778d 100644
--- a/ui-vue3/package.json
+++ b/ui-vue3/package.json
@@ -30,6 +30,7 @@
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
     "pinia": "^2.1.7",
+    "pinyin-pro": "^3.19.3",
     "ts-node": "^10.9.2",
     "tslib": "^2.6.2",
     "vue": "^3.3.10",
diff --git a/ui-vue3/src/App.vue b/ui-vue3/src/App.vue
index 5b0f5c3..1387e7e 100644
--- a/ui-vue3/src/App.vue
+++ b/ui-vue3/src/App.vue
@@ -19,13 +19,13 @@
 import { RouterView } from 'vue-router'
 import enUS from 'ant-design-vue/es/locale/en_US'
 import zhCN from 'ant-design-vue/es/locale/zh_CN'
-import { provide, reactive, ref, watch } from 'vue'
+import { computed, provide, reactive, ref, watch } from 'vue'
 import dayjs from 'dayjs'
 import { QuestionCircleOutlined } from '@ant-design/icons-vue'
 import { notification } from 'ant-design-vue'
 import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 import { PRIMARY_COLOR } from '@/base/constants'
-import { localeConfig } from '@/base/i18n'
+import { changeLanguage, i18n, localeConfig } from '@/base/i18n'
 import devTool from '@/utils/DevToolUtil'
 
 dayjs.locale('en')
@@ -44,11 +44,13 @@
 function globalQuestion() {
   devTool.todo('show Q&A tips')
 }
+
+const localeGlobal = reactive(i18n.global.locale)
 </script>
 
 <template>
   <a-config-provider
-    :locale="i18nConfig.locale === 'en' ? enUS : zhCN"
+    :locale="localeGlobal === 'en' ? enUS : zhCN"
     :theme="{
       token: {
         colorPrimary: PRIMARY_COLOR
@@ -69,4 +71,8 @@
 .__global_float_button_question {
   right: 24px;
 }
+
+#nprogress .bar {
+  background: #000000 !important;
+}
 </style>
diff --git a/ui-vue3/src/api/mock/index.ts b/ui-vue3/src/api/mock/index.ts
new file mode 100644
index 0000000..156249f
--- /dev/null
+++ b/ui-vue3/src/api/mock/index.ts
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+const modulesFiles: any = import.meta.glob('./**.ts', { eager: true })
+const fileList = []
+for (const key of Object.keys(modulesFiles)) {
+  fileList.push(modulesFiles[key].default)
+}
+export default fileList
diff --git a/ui-vue3/src/api/mock/mockApp.ts b/ui-vue3/src/api/mock/mockApp.ts
new file mode 100644
index 0000000..0178c21
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockApp.ts
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+import Mock from 'mockjs'
+
+Mock.mock('/mock/application/search', 'get', () => {
+  let total = Mock.mock('@integer(8, 1000)')
+  let list = []
+  for (let i = 0; i < total; i++) {
+    list.push({
+      appName: 'app_' + Mock.mock('@string(2,10)'),
+      instanceNum: Mock.mock('@integer(80, 200)'),
+      deployCluster: 'cluster_' + Mock.mock('@string(5)'),
+      'registerClusters|1-3': ['cluster_' + Mock.mock('@string(5)')]
+    })
+  }
+  return {
+    code: 200,
+    message: 'success',
+    data: Mock.mock({
+      total: total,
+      curPage: 1,
+      pageSize: 10,
+      data: list
+    })
+  }
+})
diff --git a/ui-vue3/src/api/mock/mockGlobalSearch.ts b/ui-vue3/src/api/mock/mockGlobalSearch.ts
new file mode 100644
index 0000000..bf729e2
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockGlobalSearch.ts
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+import Mock from 'mockjs'
+
+Mock.mock(/\/search\?searchType=\w+&keywords=\w*/, 'get', {
+  code: 200,
+  message: '成功',
+  data: {
+    find: true,
+    candidates: ['test1', 'test2', 'tset3']
+  }
+})
diff --git a/ui-vue3/src/api/mock/mockService.ts b/ui-vue3/src/api/mock/mockService.ts
new file mode 100644
index 0000000..f5e975e
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockService.ts
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+import Mock from 'mockjs'
+
+Mock.mock('/mock/service/search', 'get', {
+  code: 200,
+  message: 'success',
+  data: {
+    total: 8,
+    curPage: 1,
+    pageSize: 1,
+    data: [
+      {
+        serviceName: 'org.apache.dubbo.samples.UserService',
+        interfaceNum: 4,
+        avgQPS: 6,
+        avgRT: '194ms',
+        requestTotal: 200
+      },
+      {
+        serviceName: 'org.apache.dubbo.samples.OrderService',
+        interfaceNum: 12,
+        avgQPS: 13,
+        avgRT: '189ms',
+        requestTotal: 164
+      },
+      {
+        serviceName: 'org.apache.dubbo.samples.DetailService',
+        interfaceNum: 14,
+        avgQPS: 0.5,
+        avgRT: '268ms',
+        requestTotal: 1324
+      },
+      {
+        serviceName: 'org.apache.dubbo.samples.PayService',
+        interfaceNum: 8,
+        avgQPS: 9,
+        avgRT: '346ms',
+        requestTotal: 189
+      },
+      {
+        serviceName: 'org.apache.dubbo.samples.CommentService',
+        interfaceNum: 9,
+        avgQPS: 8,
+        avgRT: '936ms',
+        requestTotal: 200
+      },
+      {
+        serviceName: 'org.apache.dubbo.samples.RepayService',
+        interfaceNum: 16,
+        avgQPS: 17,
+        avgRT: '240ms',
+        requestTotal: 146
+      },
+      {
+        serviceName: 'org.apche.dubbo.samples.TransportService',
+        interfaceNum: 5,
+        avgQPS: 43,
+        avgRT: '89ms',
+        requestTotal: 367
+      },
+      {
+        serviceName: 'org.apche.dubbo.samples.DistributionService',
+        interfaceNum: 5,
+        avgQPS: 4,
+        avgRT: '78ms',
+        requestTotal: 145
+      }
+    ]
+  }
+})
diff --git a/ui-vue3/src/api/service/app.ts b/ui-vue3/src/api/service/app.ts
new file mode 100644
index 0000000..079f0e5
--- /dev/null
+++ b/ui-vue3/src/api/service/app.ts
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+import request from '@/base/http/request'
+
+export const searchApplications = (params: any): Promise<any> => {
+  return request({
+    url: '/application/search',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/api/service/globalSearch.ts b/ui-vue3/src/api/service/globalSearch.ts
new file mode 100644
index 0000000..dd2973f
--- /dev/null
+++ b/ui-vue3/src/api/service/globalSearch.ts
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+import request from '@/base/http/request'
+
+export const globalSearch = (params: any): Promise<any> => {
+  return request({
+    url: '/search',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/api/service/service.ts b/ui-vue3/src/api/service/service.ts
new file mode 100644
index 0000000..225f86e
--- /dev/null
+++ b/ui-vue3/src/api/service/service.ts
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+import request from '@/base/http/request'
+
+export const searchService = (params: any): Promise<any> => {
+  return request({
+    url: '/service/search',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/assets/dubbo.png b/ui-vue3/src/assets/dubbo.png
new file mode 100644
index 0000000..c48044b
--- /dev/null
+++ b/ui-vue3/src/assets/dubbo.png
Binary files differ
diff --git a/ui-vue3/src/base/enums/ProvideInject.ts b/ui-vue3/src/base/enums/ProvideInject.ts
index b737f67..af7ee19 100644
--- a/ui-vue3/src/base/enums/ProvideInject.ts
+++ b/ui-vue3/src/base/enums/ProvideInject.ts
@@ -19,5 +19,6 @@
 
 export const PROVIDE_INJECT_KEY = {
   LOCALE: KEY_PREFIX + 'LOCALE',
+  SEARCH_DOMAIN: KEY_PREFIX + 'SEARCH_DOMAIN',
   COLLAPSED: KEY_PREFIX + 'COLLAPSED'
 }
diff --git a/ui-vue3/src/base/i18n/en.ts b/ui-vue3/src/base/i18n/en.ts
index f5e290c..7dd7685 100644
--- a/ui-vue3/src/base/i18n/en.ts
+++ b/ui-vue3/src/base/i18n/en.ts
@@ -87,6 +87,7 @@
   app: 'Application',
   services: 'Services',
   application: 'Application',
+  instance: 'Instance',
   all: 'All',
   ip: 'IP',
   qps: 'qps',
@@ -96,6 +97,9 @@
   timeout: 'timeout(ms)',
   serialization: 'serialization',
   appName: 'Application Name',
+  instanceNum: 'Instance Number',
+  deployCluster: 'Deploy Cluster',
+  registerCluster: 'Register Cluster',
   serviceName: 'Service Name',
   registrySource: 'Registry Source',
   instanceRegistry: 'Instance Registry',
@@ -305,7 +309,32 @@
   serviceManagement: 'Dev & Test',
   resources: 'Resources',
   applications: 'Applications',
-  instances: 'Instances'
+  instances: 'Instances',
+  applicationDomain: {
+    name: 'Application Name',
+    detail: 'Application Detail'
+  },
+  searchDomain: {
+    total: 'Total',
+    unit: 'items'
+  },
+  backHome: 'Back Home',
+  noPageTip: 'Sorry, the page you visited does not exist.',
+  globalSearchTip: 'Search ip, application, instance, service',
+  placeholder: {
+    typeAppName: 'please type appName, support for prefix',
+    typeDefault: 'please type '
+  },
+  none: 'No Select',
+  details: 'Details',
+  debug: 'Debug',
+  distribution: 'Distribution',
+  monitor: 'Monitor',
+  tracing: 'Tracing',
+  event: 'Event',
+
+  provideService: 'Provide Service',
+  dependentService: 'Dependent Service'
 }
 
 export default words
diff --git a/ui-vue3/src/base/i18n/index.ts b/ui-vue3/src/base/i18n/index.ts
index 1d94ba0..66fb5ef 100644
--- a/ui-vue3/src/base/i18n/index.ts
+++ b/ui-vue3/src/base/i18n/index.ts
@@ -18,8 +18,9 @@
 import { createI18n } from 'vue-i18n'
 import { LOCAL_STORAGE_LOCALE } from '@/base/constants'
 import { messages } from '@/base/i18n/messages'
+import { reactive } from 'vue'
 
-export const localeConfig = {
+export const localeConfig = reactive({
   // todo use system's locale
   locale: localStorage.getItem(LOCAL_STORAGE_LOCALE) || 'cn',
   opts: [
@@ -32,9 +33,9 @@
       title: '中文'
     }
   ]
-}
+})
 
-export const i18n = createI18n({
+export const i18n: any = createI18n({
   locale: localeConfig.locale,
   legacy: false,
   globalInjection: true,
diff --git a/ui-vue3/src/base/i18n/zh.ts b/ui-vue3/src/base/i18n/zh.ts
index 537c2ff..90d5c92 100644
--- a/ui-vue3/src/base/i18n/zh.ts
+++ b/ui-vue3/src/base/i18n/zh.ts
@@ -76,6 +76,7 @@
   providers: '提供者',
   consumers: '消费者',
   application: '应用',
+  instance: '实例',
   all: '全部',
   common: '通用',
 
@@ -93,6 +94,9 @@
   timeout: '超时(毫秒)',
   serialization: '序列化',
   appName: '应用名',
+  instanceNum: '实例数量',
+  deployCluster: '部署集群',
+  registerClusters: '注册集群列表',
   serviceName: '服务名',
   registrySource: '注册来源',
   instanceRegistry: '应用级',
@@ -299,7 +303,34 @@
   versionInputPrompt: '请输入服务version(可选)',
   resources: '资源详情',
   applications: '应用',
-  instances: '实例'
+  instances: '实例',
+  applicationDomain: {
+    name: '应用名',
+    detail: '应用详情'
+  },
+  searchDomain: {
+    total: '共计',
+    unit: '条'
+  },
+  backHome: '回到首页',
+  noPageTip: '抱歉,你访问的页面不存在',
+  globalSearchTip: '搜索ip,应用,实例,服务',
+
+  globalSearchTip: '搜索ip,应用,实例,服务',
+  placeholder: {
+    typeAppName: '请输入应用名,支持前缀搜索',
+    typeDefault: '请输入'
+  },
+  none: '无',
+  details: '详情',
+  debug: '调试',
+  distribution: '分布',
+  monitor: '监控',
+  tracing: '链路追踪',
+  event: '事件',
+
+  provideService: '提供服务',
+  dependentService: '依赖服务'
 }
 
 export default words
diff --git a/ui-vue3/src/components/SearchTable.vue b/ui-vue3/src/components/SearchTable.vue
new file mode 100644
index 0000000..0d7a229
--- /dev/null
+++ b/ui-vue3/src/components/SearchTable.vue
@@ -0,0 +1,113 @@
+<!--
+  ~ 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 class="__container_search_table">
+    <a-form>
+      <a-flex wrap="wrap" gap="small">
+        <template v-for="q in searchDomain.params">
+          <a-form-item :label="$t(q.label)">
+            <a-select
+              class="select-type"
+              :style="q.style"
+              v-model:value="searchDomain.queryForm[q.param]"
+              v-if="q.dict && q.dict.length > 0"
+            >
+              <a-select-option
+                :value="item.value"
+                v-for="item in [...q.dict, { label: 'none', value: '' }]"
+              >
+                {{ $t(item.label) }}
+              </a-select-option>
+            </a-select>
+            <a-input
+              v-else
+              :style="q.style"
+              :placeholder="$t('placeholder.' + (q.placeholder || `typeDefault ${q.param}`))"
+              v-model:value="searchDomain.queryForm[q.param]"
+            ></a-input>
+          </a-form-item>
+        </template>
+        <a-form-item :label="''">
+          <a-button type="primary" @click="searchDomain.onSearch()">
+            <Icon
+              style="margin-bottom: -2px; font-size: 1.3rem"
+              icon="ic:outline-manage-search"
+            ></Icon>
+          </a-button>
+        </a-form-item>
+      </a-flex>
+    </a-form>
+
+    <div class="search-table-container">
+      <a-table
+        :pagination="pagination"
+        :scroll="{ y: searchDomain.tableStyle?.scrollY || '55vh' }"
+        :columns="searchDomain?.table.columns"
+        :data-source="searchDomain?.result"
+      >
+        <template #bodyCell="{ text, record, index, column }">
+          <span v-if="column.key === 'idx'">{{ index + 1 }}</span>
+          <slot
+            name="bodyCell"
+            :text="text"
+            :record="record"
+            :index="index"
+            :column="column"
+            v-else
+          >
+          </slot>
+        </template>
+      </a-table>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import type { ComponentInternalInstance } from 'vue'
+import { getCurrentInstance, inject } from 'vue'
+
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import type { SearchDomain } from '@/utils/SearchUtil'
+import { Icon } from '@iconify/vue'
+
+const {
+  appContext: {
+    config: { globalProperties }
+  }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+const searchDomain: SearchDomain | any = inject(PROVIDE_INJECT_KEY.SEARCH_DOMAIN)
+searchDomain.table.columns.forEach((column: any) => {
+  column.title = globalProperties.$t(column.title)
+})
+console.log(searchDomain)
+const pagination = {
+  showTotal: (v: any) =>
+    globalProperties.$t('searchDomain.total') +
+    ': ' +
+    v +
+    ' ' +
+    globalProperties.$t('searchDomain.unit')
+}
+</script>
+<style lang="less" scoped>
+.__container_search_table {
+  .select-type {
+    width: 200px;
+  }
+}
+</style>
diff --git a/ui-vue3/src/layout/breadcrumb/layout_bread.vue b/ui-vue3/src/layout/breadcrumb/layout_bread.vue
index d9dc647..691b5c0 100644
--- a/ui-vue3/src/layout/breadcrumb/layout_bread.vue
+++ b/ui-vue3/src/layout/breadcrumb/layout_bread.vue
@@ -18,7 +18,7 @@
   <div class="__container_layout_bread">
     <a-breadcrumb>
       <a-breadcrumb-item v-for="r in routes">{{ $t(r.name) }}</a-breadcrumb-item>
-      <a-breadcrumb-item>{{ pathId }}</a-breadcrumb-item>
+      <a-breadcrumb-item v-if="pathId">{{ pathId }}</a-breadcrumb-item>
     </a-breadcrumb>
   </div>
 </template>
diff --git a/ui-vue3/src/layout/header/layout_header.vue b/ui-vue3/src/layout/header/layout_header.vue
index 8d53f1d..dc169e3 100644
--- a/ui-vue3/src/layout/header/layout_header.vue
+++ b/ui-vue3/src/layout/header/layout_header.vue
@@ -16,7 +16,7 @@
 -->
 <template>
   <div class="__container_layout_header">
-    <a-layout-header class="header" style="">
+    <a-layout-header class="header">
       <a-row>
         <a-col :span="2">
           <menu-unfold-outlined
@@ -26,7 +26,26 @@
           />
           <menu-fold-outlined v-else class="trigger" @click="() => (collapsed = !collapsed)" />
         </a-col>
-        <a-col :span="16"> </a-col>
+        <a-col :span="3"></a-col>
+        <a-col :span="10" class="search-group">
+          <a-input-group compact>
+            <a-select v-model:value="searchType" class="select-type">
+              <a-select-option v-for="option in searchTypeOptions" :value="option.value">{{
+                option.label
+              }}</a-select-option>
+            </a-select>
+            <a-auto-complete
+              v-model:value="keywords"
+              class="input-keywords"
+              :placeholder="$t('globalSearchTip')"
+              :options="candidates"
+              @select="onSelect"
+              @search="inputChange"
+            />
+            <a-button :icon="h(SearchOutlined)" class="search-icon" @click="inputChange"></a-button>
+          </a-input-group>
+        </a-col>
+        <a-col :span="3"></a-col>
         <a-col :span="2">
           <a-segmented v-model:value="locale" :options="i18nConfig.opts" />
         </a-col>
@@ -38,7 +57,6 @@
             shape="circle"
             useType="pure"
           ></color-picker>
-
           <a-popover>
             <template #content>reset the theme</template>
             <Icon
@@ -63,7 +81,8 @@
 
 <script setup lang="ts">
 import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined } from '@ant-design/icons-vue'
-import { inject, ref, watch } from 'vue'
+import type { ComponentInternalInstance } from 'vue'
+import { inject, ref, reactive, watch, h, getCurrentInstance, computed } from 'vue'
 import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 import { changeLanguage, localeConfig } from '@/base/i18n'
 import {
@@ -74,6 +93,16 @@
 } from '@/base/constants'
 import devTool from '@/utils/DevToolUtil'
 import { Icon } from '@iconify/vue'
+import { SearchOutlined } from '@ant-design/icons-vue'
+import { globalSearch } from '@/api/service/globalSearch'
+import { debounce } from 'lodash'
+import type { SelectOption } from '@/types/common.ts'
+
+const {
+  appContext: {
+    config: { globalProperties }
+  }
+} = <ComponentInternalInstance>getCurrentInstance()
 
 let __null = PRIMARY_COLOR
 const collapsed = inject(PROVIDE_INJECT_KEY.COLLAPSED)
@@ -93,12 +122,70 @@
 watch(locale, (value) => {
   changeLanguage(value)
 })
+
+const searchTypeOptions = reactive([
+  {
+    label: 'IP',
+    value: 'ip'
+  },
+  {
+    label: computed(() => globalProperties.$t('application')),
+    value: 'appName'
+  },
+  {
+    label: computed(() => globalProperties.$t('instance')),
+    value: 'instanceName'
+  },
+  {
+    label: computed(() => globalProperties.$t('service')),
+    value: 'serviceName'
+  }
+])
+const searchType = ref(searchTypeOptions[0].value)
+
+const keywords = ref('')
+
+const onSearch = async () => {
+  let { data } = await globalSearch({
+    searchType: searchType.value,
+    keywords: keywords.value
+  })
+  if (data.find) {
+    for (let i = 0; i < data.candidates.length; i++) {
+      candidates.value[i] = {
+        label: data.candidates[i],
+        value: data.candidates[i]
+      }
+    }
+  } else {
+    candidates.value = []
+  }
+}
+
+const candidates = ref<Array<SelectOption>>([])
+
+const inputChange = debounce(onSearch, 300)
+
+const onSelect = () => {}
 </script>
 <style lang="less" scoped>
 .__container_layout_header {
   .header {
     background: v-bind('PRIMARY_COLOR');
     padding: 0;
+    .search-group {
+      display: flex;
+      align-items: center;
+      .select-type {
+        width: 120px;
+      }
+      .input-keywords {
+        width: calc(100% - 152px);
+      }
+      .search-icon {
+        width: 32px;
+      }
+    }
   }
 
   .trigger {
diff --git a/ui-vue3/src/layout/index.vue b/ui-vue3/src/layout/index.vue
index 523e0bf..33593e0 100644
--- a/ui-vue3/src/layout/index.vue
+++ b/ui-vue3/src/layout/index.vue
@@ -16,7 +16,7 @@
 -->
 <template>
   <div class="__container_layout_index">
-    <a-layout style="min-height: 100vh">
+    <a-layout style="height: 100vh">
       <a-layout-sider
         width="268"
         v-model:collapsed="collapsed"
@@ -33,9 +33,7 @@
       <a-layout>
         <layout_header :collapsed="collapsed"></layout_header>
         <layout_bread></layout_bread>
-        <a-layout-content
-          :style="{ margin: '16px', padding: '16px', background: '#fff', minHeight: '280px' }"
-        >
+        <a-layout-content class="layout-content">
           <router-view v-slot="{ Component }">
             <transition name="slide-fade">
               <component :is="Component" />
@@ -93,6 +91,13 @@
       margin-right: 5px;
     }
   }
+  .layout-content {
+    margin: 16px;
+    padding: 16px;
+    background: #fff;
+    overflow: auto;
+    max-height: 80vh;
+  }
 }
 </style>
 <style>
diff --git a/ui-vue3/src/layout/menu/layout_menu.vue b/ui-vue3/src/layout/menu/layout_menu.vue
index 432c4f7..d37516d 100644
--- a/ui-vue3/src/layout/menu/layout_menu.vue
+++ b/ui-vue3/src/layout/menu/layout_menu.vue
@@ -49,11 +49,10 @@
 const nowRoute = useRoute()
 
 // load active menu
-let selectedKeys = reactive([nowRoute.meta._router_key])
+let selectedKeys = reactive([getLoadSelectedKeys(nowRoute.meta)])
 let openKeys: any = reactive([])
-function loadSelectedKeys() {
-  let nowMeta: RouterMeta = nowRoute.meta
-  selectedKeys[0] = nowRoute.meta.tab ? nowMeta.parent?.meta?._router_key : nowMeta._router_key
+function getLoadSelectedKeys(meta: RouterMeta): string {
+  return meta.tab || meta.hidden ? getLoadSelectedKeys(meta.parent?.meta!) : meta._router_key!
 }
 function loadOpenedKeys() {
   let p: any = nowRoute.meta.parent
@@ -63,7 +62,6 @@
   }
 }
 
-loadSelectedKeys()
 loadOpenedKeys()
 
 function selectMenu(e: any) {
diff --git a/ui-vue3/src/main.ts b/ui-vue3/src/main.ts
index 736014e..7e2e5f8 100644
--- a/ui-vue3/src/main.ts
+++ b/ui-vue3/src/main.ts
@@ -21,9 +21,9 @@
 import App from './App.vue'
 import 'ant-design-vue/dist/reset.css'
 import { i18n } from '@/base/i18n'
-import './api/mock/mockServer'
-import './api/mock/mockCluster'
-import './api/mock/mockVersion'
+import './api/mock/index'
+// import './api/mock/mockCluster'
+// import './api/mock/mockVersion'
 
 import Vue3ColorPicker from 'vue3-colorpicker'
 import 'vue3-colorpicker/style.css'
diff --git a/ui-vue3/src/router/defaultRoutes.ts b/ui-vue3/src/router/defaultRoutes.ts
index e8947b2..5e7f86d 100644
--- a/ui-vue3/src/router/defaultRoutes.ts
+++ b/ui-vue3/src/router/defaultRoutes.ts
@@ -71,10 +71,11 @@
               },
               {
                 path: '/detail/:pathId',
-                name: 'application-tab1',
+                name: 'applicationDomain.detail',
                 component: () => import('../views/resources/applications/tabs/tab1.vue'),
                 meta: {
-                  tab: true
+                  tab: true,
+                  icon: 'material-symbols:view-in-ar'
                 }
               },
               {
@@ -154,8 +155,69 @@
           {
             path: '/services',
             name: 'services',
+            redirect: 'search',
             component: () => import('../views/resources/services/index.vue'),
-            meta: {}
+            meta: {
+              tab_parent: true
+            },
+            children: [
+              {
+                path: '/search',
+                name: 'search',
+                component: () => import('../views/resources/services/search.vue'),
+                meta: {
+                  hidden: true
+                }
+              },
+              {
+                path: '/detail/:serviceName',
+                name: 'detail',
+                component: () => import('../views/resources/services/tabs/detail.vue'),
+                meta: {
+                  tab: true
+                }
+              },
+              {
+                path: '/debug/:serviceName',
+                name: 'debug',
+                component: () => import('../views/resources/services/tabs/debug.vue'),
+                meta: {
+                  tab: true
+                }
+              },
+              {
+                path: '/distribution/:serviceName',
+                name: 'distribution',
+                component: () => import('../views/resources/services/tabs/distribution.vue'),
+                meta: {
+                  tab: true
+                }
+              },
+              {
+                path: '/monitor/:serviceName',
+                name: 'monitor',
+                component: () => import('../views/resources/services/tabs/monitor.vue'),
+                meta: {
+                  tab: true
+                }
+              },
+              {
+                path: '/tracing/:serviceName',
+                name: 'tracing',
+                component: () => import('../views/resources/services/tabs/tracing.vue'),
+                meta: {
+                  tab: true
+                }
+              },
+              {
+                path: '/event/:serviceName',
+                name: 'event',
+                component: () => import('../views/resources/services/tabs/event.vue'),
+                meta: {
+                  tab: true
+                }
+              }
+            ]
           }
         ]
       },
@@ -178,14 +240,14 @@
             children: [
               {
                 path: '/index',
-                name: 'applications_index',
-                component: () => import('../views/resources/applications/index.vue'),
+                name: 'tab_demo_index',
+                component: () => import('../views/common/tab_demo/index.vue'),
                 meta: {
-                  // hidden: true,
+                  hidden: true
                 }
               },
               {
-                path: '/tab1',
+                path: '/tab1/:pathId',
                 name: 'tab1',
                 component: () => import('../views/common/tab_demo/tab1.vue'),
                 meta: {
@@ -194,7 +256,7 @@
                 }
               },
               {
-                path: '/tab2',
+                path: '/tab2/:pathId',
                 name: 'tab2',
                 component: () => import('../views/common/tab_demo/tab2.vue'),
                 meta: {
@@ -207,11 +269,20 @@
           {
             path: '/placeholder',
             name: 'placeholder_demo',
-            component: () => import('../views/common/placeholder_demo/index.vue')
+            component: () => import('../views/common/placeholder_demo/index.vue'),
+            meta: {}
           }
         ]
       }
     ]
+  },
+  {
+    path: '/:catchAll(.*)',
+    name: 'notFound',
+    component: () => import('../views/error/notFound.vue'),
+    meta: {
+      skip: true
+    }
   }
 ]
 
diff --git a/ui-vue3/src/types/common.ts b/ui-vue3/src/types/common.ts
new file mode 100644
index 0000000..38864a5
--- /dev/null
+++ b/ui-vue3/src/types/common.ts
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+export type SelectOption = {
+  label: string
+  value: string
+}
diff --git a/ui-vue3/src/utils/SearchUtil.ts b/ui-vue3/src/utils/SearchUtil.ts
new file mode 100644
index 0000000..0be57b7
--- /dev/null
+++ b/ui-vue3/src/utils/SearchUtil.ts
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+import type { TableColumnsType } from 'ant-design-vue'
+import { reactive } from 'vue'
+
+export class SearchDomain {
+  // form of search
+  queryForm: any
+  params: [
+    {
+      label: string
+      param: string
+      defaultValue: string
+      dict: [
+        {
+          label: string
+          value: string
+        }
+      ]
+    }
+  ]
+  searchApi: Function
+  result: any
+  tableStyle: any
+  table: {
+    columns: TableColumnsType
+  } = { columns: [] }
+  paged = {
+    curPage: 1,
+    total: 0,
+    pageSize: 10
+  }
+
+  constructor(query: any, searchApi: any, columns: TableColumnsType) {
+    this.params = query
+    this.queryForm = reactive({})
+    this.table.columns = columns
+    this.searchApi = searchApi
+    this.onSearch()
+  }
+
+  async onSearch() {
+    let res = (await this.searchApi(this.queryForm || {})).data
+    this.result = res.data
+    this.paged.total = res.total
+    this.paged.curPage = res.curPage
+    this.paged.pageSize = res.pageSize
+  }
+}
+
+export function sortString(a: any, b: any) {
+  if (!isNaN(a - b)) {
+    return a - b
+  }
+  return a.localeCompare(b)
+}
diff --git a/ui-vue3/src/views/common/tab_demo/index.vue b/ui-vue3/src/views/common/tab_demo/index.vue
index 05afadf..0b4f433 100644
--- a/ui-vue3/src/views/common/tab_demo/index.vue
+++ b/ui-vue3/src/views/common/tab_demo/index.vue
@@ -18,13 +18,13 @@
   <div class="__container_tab_index">
     tab page
     <br />
-    <a href="/admin/common/tab/tab1">to tab1 by href</a>
+    <a href="/admin/common/tab/tab1/pathId1">to tab1 by href</a>
     <br />
-    <a href="tab2">to tab2 by href</a>
+    <a href="tab2/pathId1">to tab2 by href</a>
     <br />
-    <a-button @click="router.push('/common/tab/tab1')">to tab1 by href</a-button>
+    <a-button @click="router.push('/common/tab/tab1/pathId1')">to tab1 by href</a-button>
     <br />
-    <a-button @click="router.push('tab2')">to tab2 by href</a-button>
+    <a-button @click="router.push('tab2/pathId1')">to tab2 by href</a-button>
   </div>
 </template>
 
diff --git a/ui-vue3/src/views/error/notFound.vue b/ui-vue3/src/views/error/notFound.vue
new file mode 100644
index 0000000..145ccfc
--- /dev/null
+++ b/ui-vue3/src/views/error/notFound.vue
@@ -0,0 +1,48 @@
+<!--
+  ~ 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 class="__container_error_notFound">
+    <a-result class="result" title="404" :sub-title="$t('noPageTip')">
+      <template #icon>
+        <img :src="dubbo" />
+      </template>
+      <template #extra>
+        <a-button type="primary" @click="backHome">{{ $t('backHome') }}</a-button>
+      </template>
+    </a-result>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useRouter } from 'vue-router'
+import dubbo from '@/assets/dubbo.png'
+
+const router = useRouter()
+
+const backHome = () => {
+  router.push('/home')
+}
+</script>
+<style lang="less" scoped>
+.__container_error_notFound {
+  width: 100vw;
+  height: 100vh;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>
diff --git a/ui-vue3/src/views/home/index.vue b/ui-vue3/src/views/home/index.vue
index 21a8c3e..54d0a23 100644
--- a/ui-vue3/src/views/home/index.vue
+++ b/ui-vue3/src/views/home/index.vue
@@ -58,7 +58,7 @@
         metricsMetadata.info.prometheus
       }}</a-descriptions-item>
       <a-descriptions-item label="Remark">empty</a-descriptions-item>
-      <a-descriptions-item label="rules" :span="4">
+      <a-descriptions-item label="rules">
         <a-tag :color="PRIMARY_COLOR" v-for="v in metricsMetadata.info.rules">{{ v }}</a-tag>
       </a-descriptions-item>
     </a-descriptions>
diff --git a/ui-vue3/src/views/resources/applications/index.vue b/ui-vue3/src/views/resources/applications/index.vue
index a9cda57..d85ae20 100644
--- a/ui-vue3/src/views/resources/applications/index.vue
+++ b/ui-vue3/src/views/resources/applications/index.vue
@@ -16,15 +16,95 @@
 -->
 <template>
   <div class="__container_resources_application_index">
-    应用
-    <br />
-    <a-button @click="router.push('detail/app123')"> to tab</a-button>
+    <search-table :search-domain="searchDomain">
+      <template #bodyCell="{ text, record, index, column }">
+        <template v-if="column.dataIndex === 'registerClusters'">
+          <a-tag v-for="t in text" color="warning">
+            {{ t }}
+          </a-tag>
+        </template>
+        <template v-else-if="column.dataIndex === 'deployCluster'">
+          <a-tag color="success">
+            {{ text }}
+          </a-tag>
+        </template>
+        <template v-else-if="column.dataIndex === 'appName'">
+          <router-link :to="`detail/${record[column.key]}`">{{ text }}</router-link>
+        </template>
+      </template>
+    </search-table>
   </div>
 </template>
 
 <script setup lang="ts">
-import { useRouter } from 'vue-router'
+import { onMounted, provide, reactive } from 'vue'
+import { searchApplications } from '@/api/service/app'
+import SearchTable from '@/components/SearchTable.vue'
+import { SearchDomain, sortString } from '@/utils/SearchUtil'
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 
-const router = useRouter()
+let columns = [
+  {
+    title: 'idx',
+    key: 'idx',
+    dataIndex: 'idx',
+    width: 80
+  },
+  {
+    title: 'appName',
+    key: 'appName',
+    dataIndex: 'appName',
+    sorter: (a: any, b: any) => sortString(a.appName, b.appName),
+    width: 140
+  },
+  {
+    title: 'instanceNum',
+    key: 'instanceNum',
+    dataIndex: 'instanceNum',
+    width: 100,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+
+  {
+    title: 'deployCluster',
+    key: 'deployCluster',
+    dataIndex: 'deployCluster',
+    width: 120
+  },
+  {
+    title: 'registerClusters',
+    key: 'registerClusters',
+    dataIndex: 'registerClusters',
+    width: 200
+  }
+]
+const searchDomain = reactive(
+  new SearchDomain(
+    [
+      {
+        label: 'appName',
+        param: 'appName',
+        placeholder: 'typeAppName',
+        style: {
+          width: '200px'
+        }
+      }
+    ],
+    searchApplications,
+    columns
+  )
+)
+
+onMounted(() => {
+  searchDomain.onSearch()
+  console.log(searchDomain.result)
+})
+
+provide(PROVIDE_INJECT_KEY.SEARCH_DOMAIN, searchDomain)
 </script>
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.search-table-container {
+  min-height: 60vh;
+  //max-height: 70vh; //overflow: auto;
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/index.vue b/ui-vue3/src/views/resources/services/index.vue
index d7a2999..2764e2a 100644
--- a/ui-vue3/src/views/resources/services/index.vue
+++ b/ui-vue3/src/views/resources/services/index.vue
@@ -15,8 +15,33 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_resources_services_index">服务</div>
+  <div class="__container_services">
+    <Icon
+      icon="material-symbols:arrow-back-ios-new-rounded"
+      v-if="route.name !== 'search'"
+      @click="backSearch"
+      class="icon-back"
+    />
+    <LayoutTab></LayoutTab>
+  </div>
 </template>
 
-<script setup lang="ts"></script>
-<style lang="less" scoped></style>
+<script setup lang="ts">
+import LayoutTab from '@/layout/tab/layout_tab.vue'
+import { Icon } from '@iconify/vue'
+import { useRoute, useRouter } from 'vue-router'
+
+const route = useRoute()
+const router = useRouter()
+
+const backSearch = () => {
+  router.push({ name: 'search' })
+}
+</script>
+<style lang="less" scoped>
+.__container_services {
+  .icon-back:hover {
+    cursor: pointer;
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/search.vue b/ui-vue3/src/views/resources/services/search.vue
new file mode 100644
index 0000000..9d36ebf
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/search.vue
@@ -0,0 +1,127 @@
+<!--
+  ~ 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 class="__container_services_index">
+    <a-flex vertical>
+      <a-flex class="service-filter">
+        <a-input-search
+          v-model:value="serviceName"
+          placeholder="请输入"
+          class="service-name-input"
+          @search="debounceSearch"
+          enter-button
+        />
+      </a-flex>
+      <a-table
+        :columns="columns"
+        :data-source="dataSource"
+        :pagination="pagination"
+        :scroll="{ y: '55vh' }"
+      >
+        <template #bodyCell="{ column, text }">
+          <template v-if="column.dataIndex === 'serviceName'">
+            <a-button type="link" @click="viewDetail(text)">{{ text }}</a-button>
+          </template>
+        </template>
+      </a-table>
+    </a-flex>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useRouter } from 'vue-router'
+import type { ComponentInternalInstance } from 'vue'
+import { ref, getCurrentInstance } from 'vue'
+import { searchService } from '@/api/service/service.ts'
+import { debounce } from 'lodash'
+
+const {
+  appContext: {
+    config: { globalProperties }
+  }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+const serviceName = ref('')
+
+const router = useRouter()
+const columns = [
+  {
+    title: '服务',
+    dataIndex: 'serviceName',
+    sorter: true,
+    width: '30%'
+  },
+  {
+    title: '接口数',
+    dataIndex: 'interfaceNum',
+    sorter: true,
+    width: '10%'
+  },
+  {
+    title: '近 1min QPS',
+    dataIndex: 'avgQPS',
+    sorter: true,
+    width: '15%'
+  },
+  {
+    title: '近 1min RT',
+    dataIndex: 'avgRT',
+    sorter: true,
+    width: '15%'
+  },
+  {
+    title: '近 1min 请求总量',
+    dataIndex: 'requestTotal',
+    sorter: true,
+    width: '15%'
+  }
+]
+
+const dataSource = ref([])
+
+const onSearch = async () => {
+  let { data } = await searchService({})
+  dataSource.value = data.data
+}
+
+onSearch()
+
+const debounceSearch = debounce(onSearch, 300)
+
+const viewDetail = (serviceName: string) => {
+  router.push({ name: 'detail', params: { serviceName } })
+}
+
+const pagination = {
+  showTotal: (v: any) =>
+    globalProperties.$t('searchDomain.total') +
+    ': ' +
+    v +
+    ' ' +
+    globalProperties.$t('searchDomain.unit')
+}
+</script>
+<style lang="less" scoped>
+.__container_services_index {
+  .service-filter {
+    margin-bottom: 20px;
+    .service-name-input {
+      width: 500px;
+    }
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/debug.vue b/ui-vue3/src/views/resources/services/tabs/debug.vue
new file mode 100644
index 0000000..5535074
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/tabs/debug.vue
@@ -0,0 +1,169 @@
+<!--
+  ~ 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 class="__container_services_tabs_debug">
+    <div class="tabs-title">方法列表</div>
+    <a-tabs v-model:activeKey="activeKey" tab-position="left" :tabBarStyle="{ width: '200px' }">
+      <a-tab-pane v-for="tabName in methodTabs" :key="tabName" :tab="`${tabName}`">
+        <a-descriptions :column="4" layout="vertical">
+          <a-descriptions-item label="接口" :span="2">
+            <p class="description-item-content">
+              org.apache.dubbo.samples.UserService:getPermissions
+            </p>
+          </a-descriptions-item>
+          <a-descriptions-item label="版本&分组">
+            <a-select
+              v-model:value="versionAndGroup"
+              size="large"
+              :options="versionAndGroupOptions"
+              class="description-item-content"
+            ></a-select>
+          </a-descriptions-item>
+          <a-descriptions-item label="指定生产者">
+            <a-select
+              v-model:value="versionAndGroup"
+              size="large"
+              :options="versionAndGroupOptions"
+              class="description-item-content"
+            ></a-select>
+          </a-descriptions-item>
+          <a-descriptions-item label="入参类型" :span="2">
+            <a-tree block-node :tree-data="enterParamType" class="description-item-content" />
+          </a-descriptions-item>
+          <a-descriptions-item label="出参类型" :span="2">
+            <a-tree block-node :tree-data="outputParamType" class="description-item-content" />
+          </a-descriptions-item>
+          <a-descriptions-item label="请求" :span="2">
+            <a-textarea
+              v-model="requestValue"
+              placeholder="请输入"
+              :rows="4"
+              class="description-item-content"
+            />
+          </a-descriptions-item>
+          <a-descriptions-item label="响应" :span="2">
+            <a-textarea
+              v-model="responseValue"
+              placeholder="请输入"
+              :rows="4"
+              class="description-item-content"
+            />
+          </a-descriptions-item>
+        </a-descriptions>
+        <a-button type="primary">发送请求</a-button>
+      </a-tab-pane>
+    </a-tabs>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+
+const methodTabs = reactive([
+  'login',
+  'register',
+  'logout',
+  'query',
+  'suspend',
+  'updateInfo',
+  'getPermissions',
+  'auth',
+  'comment'
+])
+
+const activeKey = ref(methodTabs[0])
+
+const versionAndGroupOptions = reactive([
+  {
+    value: 'version=1.0.0'
+  },
+  {
+    value: 'group=group1'
+  },
+  {
+    value: 'version=1.0.0,group=group1'
+  }
+])
+
+const versionAndGroup = ref(versionAndGroupOptions[0].value)
+
+const enterParamType = [
+  {
+    title: 'param0: java.lang.String',
+    key: '0'
+  },
+  {
+    title: 'param1: org.apache.dubbo.samples.api.User',
+    key: '1',
+    children: [
+      {
+        title: 'name: java.lang.String',
+        key: '1-0'
+      },
+      {
+        title: 'id: java.lang.Long',
+        key: '1-1'
+      },
+      {
+        title: 'createTime: java.Date.DateTime',
+        key: '1-2'
+      }
+    ]
+  }
+]
+
+const outputParamType = [
+  {
+    title: 'param0: org.apache.dubbo.samples.api.Response',
+    key: '0',
+    children: [
+      {
+        title: 'success: boolean',
+        key: '0-0'
+      },
+      {
+        title: 'phone: java.lang.String',
+        key: '0-1'
+      },
+      {
+        title: 'lastLoginTime: java.Date.LocalDateTime',
+        key: '0-2'
+      },
+      {
+        title: 'follows: java.util.List',
+        key: '0-3'
+      }
+    ]
+  }
+]
+
+const requestValue = ref('')
+const responseValue = ref('')
+</script>
+<style lang="less" scoped>
+.__container_services_tabs_debug {
+  width: 100%;
+  .tabs-title {
+    width: 200px;
+    text-align: center;
+  }
+  .description-item-content {
+    margin-left: 20px;
+    width: 90%;
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/detail.vue b/ui-vue3/src/views/resources/services/tabs/detail.vue
new file mode 100644
index 0000000..1a26063
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/tabs/detail.vue
@@ -0,0 +1,69 @@
+<!--
+  ~ 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 class="__container_services_tabs_detail">
+    <a-flex>
+      <a-descriptions class="description-column" :column="1" layout="vertical">
+        <a-descriptions-item label="服务名">
+          <p class="description-item-content">org.apache.dubbo.samples.UserService</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="服务版本&分组">
+          <div class="description-item-content">
+            <a-tag color="cyan">version=v1</a-tag>
+            <a-tag color="cyan">version=2.0,group=group1</a-tag>
+          </div>
+        </a-descriptions-item>
+        <a-descriptions-item label="RPC协议">
+          <p class="description-item-content">triple</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="延迟注册时间">
+          <p class="description-item-content">3000ms</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="超时时间">
+          <p class="description-item-content">3000ms</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="重试次数">
+          <p class="description-item-content">3</p>
+        </a-descriptions-item>
+      </a-descriptions>
+      <a-descriptions class="description-column" :column="1" layout="vertical">
+        <a-descriptions-item label="请求总量(1min)">
+          <p class="description-item-content">1384</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="平均RT(1min)">
+          <p class="description-item-content">96ms</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="平均qps(1min)">
+          <p class="description-item-content">12</p>
+        </a-descriptions-item>
+        <a-descriptions-item label="是否过时">
+          <p class="description-item-content">false</p>
+        </a-descriptions-item>
+      </a-descriptions>
+    </a-flex>
+  </div>
+</template>
+
+<script setup lang="ts"></script>
+<style lang="less" scoped>
+.__container_services_tabs_detail {
+  .description-item-content {
+    margin-left: 20px;
+    width: 90%;
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/distribution.vue b/ui-vue3/src/views/resources/services/tabs/distribution.vue
new file mode 100644
index 0000000..9a402e6
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/tabs/distribution.vue
@@ -0,0 +1,211 @@
+<!--
+  ~ 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 class="__container_services_tabs_distribution">
+    <a-flex vertical>
+      <a-flex class="service-filter">
+        <a-flex>
+          <div>
+            <span>版本&分组:</span>
+            <a-select
+              v-model:value="versionAndGroup"
+              :options="versionAndGroupOptions"
+              class="service-filter-select"
+            ></a-select>
+          </div>
+          <a-input-search
+            v-model:value="searchValue"
+            placeholder="搜索应用,ip,支持前缀搜索"
+            class="service-filter-input"
+            @search="() => {}"
+            enter-button
+          />
+        </a-flex>
+        <div>
+          <a-radio-group v-model:value="type" button-style="solid">
+            <a-radio-button value="producer">生产者</a-radio-button>
+            <a-radio-button value="consumer">消费者</a-radio-button>
+          </a-radio-group>
+        </div>
+      </a-flex>
+      <a-table
+        :columns="tableColumns"
+        :data-source="tableData"
+        :scroll="{ y: '45vh' }"
+        :pagination="pagination"
+      >
+        <template #bodyCell="{ column, text }">
+          <template v-if="column.dataIndex === 'applicationName'">
+            <a-button type="link">{{ text }}</a-button>
+          </template>
+          <template v-if="column.dataIndex === 'instanceIP'">
+            <a-flex justify="">
+              <a-button v-for="ip in text.slice(0, 3)" :key="ip" type="link">{{ ip }}</a-button>
+              <a-button v-if="text.length > 3" type="link">更多</a-button>
+            </a-flex>
+          </template>
+        </template>
+      </a-table>
+    </a-flex>
+  </div>
+</template>
+
+<script setup lang="ts">
+import type { ComponentInternalInstance } from 'vue'
+import { ref, reactive, getCurrentInstance } from 'vue'
+
+const {
+  appContext: {
+    config: { globalProperties }
+  }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+const searchValue = ref('')
+const versionAndGroupOptions = reactive([
+  {
+    label: '不指定',
+    value: ''
+  },
+  {
+    label: 'version=1.0.0',
+    value: 'version=1.0.0'
+  },
+  {
+    label: 'group=group1',
+    value: 'group=group1'
+  },
+  {
+    label: 'version=1.0.0,group=group1',
+    value: 'version=1.0.0,group=group1'
+  }
+])
+const versionAndGroup = ref(versionAndGroupOptions[0].value)
+const type = ref('producer')
+
+const tableColumns = [
+  {
+    title: '应用名',
+    dataIndex: 'applicationName',
+    width: '25%',
+    sorter: true
+  },
+  {
+    title: '实例数',
+    dataIndex: 'instanceNum',
+    width: '25%',
+    sorter: true
+  },
+  {
+    title: '实例ip',
+    dataIndex: 'instanceIP',
+    width: '50%'
+  }
+]
+
+const tableData = reactive([
+  {
+    applicationName: 'shop-order',
+    instanceNum: 15,
+    instanceIP: [
+      '192.168.32.28:8697',
+      '192.168.32.26:20880',
+      '192.168.32.24:28080',
+      '192.168.32.22:20880'
+    ]
+  },
+  {
+    applicationName: 'shop-order',
+    instanceNum: 15,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-user',
+    instanceNum: 12,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-order',
+    instanceNum: 15,
+    instanceIP: [
+      '192.168.32.28:8697',
+      '192.168.32.26:20880',
+      '192.168.32.24:28080',
+      '192.168.32.22:20880'
+    ]
+  },
+  {
+    applicationName: 'shop-order',
+    instanceNum: 15,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-user',
+    instanceNum: 12,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-order',
+    instanceNum: 15,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-user',
+    instanceNum: 12,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-user',
+    instanceNum: 12,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-order',
+    instanceNum: 15,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+  },
+  {
+    applicationName: 'shop-user',
+    instanceNum: 12,
+    instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+  }
+])
+
+const pagination = {
+  showTotal: (v: any) =>
+    globalProperties.$t('searchDomain.total') +
+    ': ' +
+    v +
+    ' ' +
+    globalProperties.$t('searchDomain.unit')
+}
+</script>
+<style lang="less" scoped>
+.__container_services_tabs_distribution {
+  .service-filter {
+    justify-content: space-between;
+    margin-bottom: 20px;
+    .service-filter-select {
+      margin-left: 10px;
+      width: 250px;
+    }
+    .service-filter-input {
+      margin-left: 30px;
+      width: 300px;
+    }
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/service/index.vue b/ui-vue3/src/views/resources/services/tabs/event.vue
similarity index 78%
copy from ui-vue3/src/views/service/index.vue
copy to ui-vue3/src/views/resources/services/tabs/event.vue
index c97d821..d9a86c0 100644
--- a/ui-vue3/src/views/service/index.vue
+++ b/ui-vue3/src/views/resources/services/tabs/event.vue
@@ -15,15 +15,8 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_home_index">
-    <h1>{{ $t(routeName) }}</h1>
-  </div>
+  <div class="__container_services_tabs_event">事件todo</div>
 </template>
 
-<script setup lang="ts">
-import { Icon } from '@iconify/vue'
-
-import { useRoute } from 'vue-router'
-const routeName = <string>useRoute().name
-</script>
+<script setup lang="ts"></script>
 <style lang="less" scoped></style>
diff --git a/ui-vue3/src/views/service/index.vue b/ui-vue3/src/views/resources/services/tabs/monitor.vue
similarity index 78%
copy from ui-vue3/src/views/service/index.vue
copy to ui-vue3/src/views/resources/services/tabs/monitor.vue
index c97d821..baa40c4 100644
--- a/ui-vue3/src/views/service/index.vue
+++ b/ui-vue3/src/views/resources/services/tabs/monitor.vue
@@ -15,15 +15,8 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_home_index">
-    <h1>{{ $t(routeName) }}</h1>
-  </div>
+  <div class="__container_services_tabs_monitor">监控todo</div>
 </template>
 
-<script setup lang="ts">
-import { Icon } from '@iconify/vue'
-
-import { useRoute } from 'vue-router'
-const routeName = <string>useRoute().name
-</script>
+<script setup lang="ts"></script>
 <style lang="less" scoped></style>
diff --git a/ui-vue3/src/views/service/index.vue b/ui-vue3/src/views/resources/services/tabs/tracing.vue
similarity index 78%
rename from ui-vue3/src/views/service/index.vue
rename to ui-vue3/src/views/resources/services/tabs/tracing.vue
index c97d821..ca3bc51 100644
--- a/ui-vue3/src/views/service/index.vue
+++ b/ui-vue3/src/views/resources/services/tabs/tracing.vue
@@ -15,15 +15,8 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_home_index">
-    <h1>{{ $t(routeName) }}</h1>
-  </div>
+  <div class="__container_services_tabs_tracing">链路todo</div>
 </template>
 
-<script setup lang="ts">
-import { Icon } from '@iconify/vue'
-
-import { useRoute } from 'vue-router'
-const routeName = <string>useRoute().name
-</script>
+<script setup lang="ts"></script>
 <style lang="less" scoped></style>
diff --git a/ui-vue3/yarn.lock b/ui-vue3/yarn.lock
index 9cc6265..fbecaf6 100644
--- a/ui-vue3/yarn.lock
+++ b/ui-vue3/yarn.lock
@@ -4480,6 +4480,11 @@
     "@vue/devtools-api" "^6.5.0"
     vue-demi ">=0.14.5"
 
+pinyin-pro@^3.19.3:
+  version "3.19.3"
+  resolved "https://registry.npmmirror.com/pinyin-pro/-/pinyin-pro-3.19.3.tgz#804ee7bf2266a927194a87764b4c3a2be06ddedc"
+  integrity sha512-tIBL4ibq3P+MFbSwOTTi42Xf6jau726swgKqEmSkkiZiE1iUMhQdguR9aavLt9nqnRDpcBSIanNV8tPuKPSmvw==
+
 pkg-types@^1.0.3:
   version "1.0.3"
   resolved "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"