Merge branch 'master' of https://github.com/apache/dubbo-kubernetes
diff --git a/ui-vue3/src/api/mock/mockApp.ts b/ui-vue3/src/api/mock/mockApp.ts
index 9a01e18..821f144 100644
--- a/ui-vue3/src/api/mock/mockApp.ts
+++ b/ui-vue3/src/api/mock/mockApp.ts
@@ -22,7 +22,7 @@
   return {
     code: 200,
     message: 'success',
-    data: 'http://101.201.225.179:3000/d/a0b114ca-edf7-4dfe-ac2c-34a4fc545fed/application?orgId=1&refresh=1m&from=1710644821536&to=1710731221536&theme=light'
+    data: 'http://8.147.104.101:3000/d/a0b114ca-edf7-4dfe-ac2c-34a4fc545fed/application?orgId=1&refresh=1m&from=1711855893859&to=1711877493859&theme=light'
   }
 })
 
@@ -136,3 +136,22 @@
     }
   }
 })
+
+Mock.mock('/mock/application/event', 'get', () => {
+  let list = Mock.mock({
+    'list|10': [
+      {
+        desc: `Scaled down replica set shop-detail-v1-5847b7cdfd to @integer(3,10) from @integer(3,10)`,
+        time: '@DATETIME("yyyy-MM-dd HH:mm:ss")',
+        type: 'deployment-controller'
+      }
+    ]
+  })
+  return {
+    code: 200,
+    message: 'success',
+    data: {
+      ...list
+    }
+  }
+})
diff --git a/ui-vue3/src/api/mock/mockDestinationRule.ts b/ui-vue3/src/api/mock/mockDestinationRule.ts
new file mode 100644
index 0000000..4206b29
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockDestinationRule.ts
@@ -0,0 +1,39 @@
+/*
+ * 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/destinationRule/search', 'get', () => {
+  const total = Mock.mock('@integer(8, 1000)')
+  const list = []
+  for (let i = 0; i < total; i++) {
+    list.push({
+      ruleName: 'app_' + Mock.mock('@string(2,10)'),
+      createTime: Mock.mock('@datetime')
+    })
+  }
+  return {
+    code: 200,
+    message: 'success',
+    data: Mock.mock({
+      total: total,
+      curPage: 1,
+      pageSize: 10,
+      data: list
+    })
+  }
+})
diff --git a/ui-vue3/src/api/mock/mockDynamicConfig.ts b/ui-vue3/src/api/mock/mockDynamicConfig.ts
new file mode 100644
index 0000000..879df89
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockDynamicConfig.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/dynamicConfig/search', 'get', () => {
+  const total = Mock.mock('@integer(8, 1000)')
+  const list = []
+  for (let i = 0; i < total; i++) {
+    list.push({
+      ruleName: 'app_' + Mock.mock('@string(2,10)'),
+      ruleGranularity: Mock.mock('@boolean'),
+      enable: Mock.mock('@boolean'),
+      createTime: Mock.mock('@datetime')
+    })
+  }
+  return {
+    code: 200,
+    message: 'success',
+    data: Mock.mock({
+      total: total,
+      curPage: 1,
+      pageSize: 10,
+      data: list
+    })
+  }
+})
diff --git a/ui-vue3/src/api/mock/mockRoutingRule.ts b/ui-vue3/src/api/mock/mockRoutingRule.ts
index bd352da..e9fe0b5 100644
--- a/ui-vue3/src/api/mock/mockRoutingRule.ts
+++ b/ui-vue3/src/api/mock/mockRoutingRule.ts
@@ -23,10 +23,9 @@
   for (let i = 0; i < total; i++) {
     list.push({
       ruleName: 'app_' + Mock.mock('@string(2,10)'),
-      ruleGranularity: Mock.mock('@integer(80, 200)'),
+      ruleGranularity: Mock.mock('@boolean'),
       enable: Mock.mock('@boolean'),
-      effectiveTime: Mock.mock('@datetime'),
-      protection: Mock.mock('@boolean')
+      createTime: Mock.mock('@datetime')
     })
   }
   return {
diff --git a/ui-vue3/src/api/mock/mockTagRule.ts b/ui-vue3/src/api/mock/mockTagRule.ts
new file mode 100644
index 0000000..6dd1984
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockTagRule.ts
@@ -0,0 +1,40 @@
+/*
+ * 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/tagRule/search', 'get', () => {
+  const total = Mock.mock('@integer(8, 1000)')
+  const list = []
+  for (let i = 0; i < total; i++) {
+    list.push({
+      ruleName: 'app_' + Mock.mock('@string(2,10)'),
+      enable: Mock.mock('@boolean'),
+      createTime: Mock.mock('@datetime')
+    })
+  }
+  return {
+    code: 200,
+    message: 'success',
+    data: Mock.mock({
+      total: total,
+      curPage: 1,
+      pageSize: 10,
+      data: list
+    })
+  }
+})
diff --git a/ui-vue3/src/api/mock/mockVirtualService.ts b/ui-vue3/src/api/mock/mockVirtualService.ts
new file mode 100644
index 0000000..5469277
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockVirtualService.ts
@@ -0,0 +1,40 @@
+/*
+ * 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/virtualService/search', 'get', () => {
+  const total = Mock.mock('@integer(8, 1000)')
+  const list = []
+  for (let i = 0; i < total; i++) {
+    list.push({
+      ruleName: 'app_' + Mock.mock('@string(2,10)'),
+      createTime: Mock.mock('@datetime'),
+      lastModifiedTime: Mock.mock('@datetime')
+    })
+  }
+  return {
+    code: 200,
+    message: 'success',
+    data: Mock.mock({
+      total: total,
+      curPage: 1,
+      pageSize: 10,
+      data: list
+    })
+  }
+})
diff --git a/ui-vue3/src/api/service/app.ts b/ui-vue3/src/api/service/app.ts
index 44c32a7..6bc08ae 100644
--- a/ui-vue3/src/api/service/app.ts
+++ b/ui-vue3/src/api/service/app.ts
@@ -54,3 +54,10 @@
     params
   })
 }
+export const listApplicationEvent = (params: any): Promise<any> => {
+  return request({
+    url: '/application/event',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/api/service/metricInfo.ts b/ui-vue3/src/api/service/metricInfo.ts
new file mode 100644
index 0000000..3cf499f
--- /dev/null
+++ b/ui-vue3/src/api/service/metricInfo.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 queryPromSql = (params: any): Promise<any> => {
+  return request({
+    url: 'http://123.56.255.156:9090/api/v1/query',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/api/service/traffic.ts b/ui-vue3/src/api/service/traffic.ts
index 380f230..24f97d6 100644
--- a/ui-vue3/src/api/service/traffic.ts
+++ b/ui-vue3/src/api/service/traffic.ts
@@ -24,3 +24,35 @@
     params
   })
 }
+
+export const searchTagRule = (params: any): Promise<any> => {
+  return request({
+    url: '/tagRule/search',
+    method: 'get',
+    params
+  })
+}
+
+export const searchDynamicConfig = (params: any): Promise<any> => {
+  return request({
+    url: '/dynamicConfig/search',
+    method: 'get',
+    params
+  })
+}
+
+export const searchVirtualService = (params: any): Promise<any> => {
+  return request({
+    url: '/virtualService/search',
+    method: 'get',
+    params
+  })
+}
+
+export const searchDestinationRule = (params: any): Promise<any> => {
+  return request({
+    url: '/destinationRule/search',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/base/constants.ts b/ui-vue3/src/base/constants.ts
index 08aa943..5fbe6ff 100644
--- a/ui-vue3/src/base/constants.ts
+++ b/ui-vue3/src/base/constants.ts
@@ -15,8 +15,12 @@
  * limitations under the License.
  */
 
+import type { Component } from 'vue'
 import { computed, h, reactive, ref } from 'vue'
+import type { RouteRecordType } from '@/router/defaultRoutes'
+import type { RouteLocationNormalizedLoaded } from 'vue-router'
 
+// 2aacb8
 export const PRIMARY_COLOR_DEFAULT = '#17b392'
 
 export const LOCAL_STORAGE_LOCALE = 'LOCAL_STORAGE_LOCALE'
@@ -31,13 +35,17 @@
   HEALTHY: 'green'
 }
 
-export const TAB_HEADER_TITLE_VNODE = reactive({
-  vnode: h('div', 'something')
-})
-export const TAB_HEADER_TITLE = {
+export const TAB_HEADER_TITLE: Component = {
   functional: true,
-  render: () => {
-    return TAB_HEADER_TITLE_VNODE.vnode
+  props: ['route'],
+  render: (
+    a: any,
+    b: any,
+    c: { [key: string]: RouteRecordType & RouteLocationNormalizedLoaded }
+  ) => {
+    let route = c.route
+    let header: any = route.meta?.slots?.header
+    return h(header) || h('div', route.params?.pathId)
     // console.log(h)
     // return h("div", "foo")
   }
diff --git a/ui-vue3/src/base/i18n/en.ts b/ui-vue3/src/base/i18n/en.ts
index c95132c..03deca7 100644
--- a/ui-vue3/src/base/i18n/en.ts
+++ b/ui-vue3/src/base/i18n/en.ts
@@ -332,6 +332,8 @@
   editMockRule: 'Edit Mock Rule',
   deleteRuleTitle: 'Are you sure to delete this mock rule?',
 
+  createTime: 'Create Time',
+  lastModifiedTime: 'Last Modified Time',
   trafficTimeout: 'Timeout',
   trafficRetry: 'Retry',
   trafficRegion: 'Region Aware',
diff --git a/ui-vue3/src/base/i18n/zh.ts b/ui-vue3/src/base/i18n/zh.ts
index aa36396..8428a8c 100644
--- a/ui-vue3/src/base/i18n/zh.ts
+++ b/ui-vue3/src/base/i18n/zh.ts
@@ -291,7 +291,8 @@
 
   ruleName: '规则名',
   ruleGranularity: '规则粒度',
-  effectiveTime: '生效时间',
+  createTime: '创建时间',
+  lastModifiedTime: '最后修改时间',
   enable: '是否启用',
   protection: '容错保护',
   trafficTimeout: '超时时间',
diff --git a/ui-vue3/src/components/ConfigPage.vue b/ui-vue3/src/components/ConfigPage.vue
index 2fb3c0a..4a535ac 100644
--- a/ui-vue3/src/components/ConfigPage.vue
+++ b/ui-vue3/src/components/ConfigPage.vue
@@ -49,6 +49,11 @@
         <a-card>
           <template #title>
             {{ $t(currentOption.title) }}
+            <div v-if="currentOption?.ext" style="float: right">
+              <a-button type="primary" @click="currentOption?.ext?.fun">{{
+                currentOption?.ext?.title
+              }}</a-button>
+            </div>
           </template>
           <a-spin :spinning="waitResponse">
             <a-form
@@ -60,7 +65,7 @@
               layout="horizontal"
             >
               <slot :name="'form_' + currentOption.key" :current="currentOption"></slot>
-              <a-form-item style="margin-left: 100px">
+              <a-form-item style="margin: 20px 0 0 100px">
                 <a-button type="primary" @click="submit">{{ $t('submit') }}</a-button>
                 <a-button style="margin-left: 10px" @click="reset">{{ $t('reset') }}</a-button>
               </a-form-item>
@@ -108,7 +113,7 @@
     waitResponse.value = false
   })
 
-  let cur = currentOption.value
+  let cur = props.options.list[props.options.current[0]]
   await cur.submit(cur.form).catch((e: any) => {
     message.error('submit failed [server error]: ' + e)
   })
diff --git a/ui-vue3/src/components/SearchTable.vue b/ui-vue3/src/components/SearchTable.vue
index db40551..7a6d4bf 100644
--- a/ui-vue3/src/components/SearchTable.vue
+++ b/ui-vue3/src/components/SearchTable.vue
@@ -68,26 +68,27 @@
           </a-form>
         </a-col>
         <a-col :span="4">
-          <div class="common-tool">
-            <a-dropdown placement="bottom" :trigger="['click']">
-              <div class="custom-column button">
-                <Icon icon="material-symbols-light:format-list-bulleted-rounded"></Icon>
-              </div>
-
-              <template #overlay>
-                <a-card title="Custom Column">
-                  <a-menu>
-                    <a-menu-item>
-                      <Icon
-                        style="margin-bottom: -3px; font-size: 1rem"
-                        icon="material-symbols-light:format-list-bulleted-rounded"
-                      ></Icon>
-                      3
-                    </a-menu-item>
-                  </a-menu>
-                </a-card>
-              </template>
-            </a-dropdown>
+          <div class="common-tool" @click="commonTool.customColumns = !commonTool.customColumns">
+            <div class="custom-column button">
+              <Icon icon="material-symbols-light:format-list-bulleted-rounded"></Icon>
+            </div>
+            <div class="dropdown" v-show="commonTool.customColumns">
+              <a-card style="max-width: 300px" title="Custom Column">
+                <div class="body">
+                  <div
+                    class="item"
+                    @click.stop="hideColumn(item)"
+                    v-for="(item, i) in searchDomain?.table.columns"
+                  >
+                    <Icon
+                      style="margin-bottom: -4px; font-size: 1rem; margin-right: 2px"
+                      :icon="item.__hide ? 'zondicons:view-hide' : 'zondicons:view-show'"
+                    ></Icon>
+                    {{ item.title }}
+                  </div>
+                </div>
+              </a-card>
+            </div>
           </div>
         </a-col>
       </a-row>
@@ -102,7 +103,7 @@
           y: searchDomain.tableStyle?.scrollY || '',
           x: searchDomain.tableStyle?.scrollX || ''
         }"
-        :columns="searchDomain?.table.columns"
+        :columns="searchDomain?.table.columns.filter((x: any) => !x.__hide)"
         :data-source="searchDomain?.result"
         @change="handleTableChange"
       >
@@ -131,6 +132,7 @@
 import type { SearchDomain } from '@/utils/SearchUtil'
 import { Icon } from '@iconify/vue'
 import { PRIMARY_COLOR } from '@/base/constants'
+import { message } from 'ant-design-vue'
 
 const commonTool = reactive({
   customColumns: false
@@ -150,8 +152,10 @@
     column.title = computed(() => globalProperties.$t(tmp))
   }
 })
-const pagination = computed(() => {
-  console.log(pagination)
+const pagination: any = computed(() => {
+  if (searchDomain.noPaged) {
+    return false
+  }
   return {
     pageSize: searchDomain.paged.pageSize,
     current: searchDomain.paged.curPage,
@@ -174,6 +178,14 @@
   searchDomain.onSearch()
   return
 }
+function hideColumn(item: any) {
+  let filter = searchDomain?.table.columns.filter((x: any) => !x.__hide)
+  if (!item.__hide && filter.length <= 1) {
+    message.warn('must show at least one column')
+    return
+  }
+  item.__hide = !item.__hide
+}
 </script>
 <style lang="less" scoped>
 .__container_search_table {
@@ -197,13 +209,11 @@
     background: #fafafa;
   }
 
-  :deep(.ant-popover-arrow) {
-    display: none;
-  }
-
   .common-tool {
     margin-top: 5px;
-
+    width: 200px;
+    cursor: pointer;
+    position: relative;
     .button {
       vertical-align: center;
       line-height: 24px;
@@ -218,6 +228,24 @@
         margin-left: 10px;
       }
     }
+    .dropdown {
+      top: 40px;
+      right: -40px;
+      position: absolute;
+      height: auto;
+      z-index: 1000;
+      .body {
+        max-height: 200px;
+        overflow: auto;
+      }
+
+      .item {
+        line-height: 30px;
+        &:hover {
+          color: v-bind('PRIMARY_COLOR');
+        }
+      }
+    }
   }
 }
 </style>
diff --git a/ui-vue3/src/layout/index.vue b/ui-vue3/src/layout/index.vue
index 06f9199..82be705 100644
--- a/ui-vue3/src/layout/index.vue
+++ b/ui-vue3/src/layout/index.vue
@@ -70,10 +70,12 @@
     transitionFlag.value = true
   }, 500)
 })
-TAB_HEADER_TITLE_VNODE.vnode = h('div', route.params?.pathId)
 </script>
 <style lang="less" scoped>
 .__container_layout_index {
+  :deep(.ant-layout-content) {
+    padding: 16px !important;
+  }
   .logo {
     height: 40px;
     width: auto;
diff --git a/ui-vue3/src/layout/tab/layout_tab.vue b/ui-vue3/src/layout/tab/layout_tab.vue
index 45f5e11..33bc6c0 100644
--- a/ui-vue3/src/layout/tab/layout_tab.vue
+++ b/ui-vue3/src/layout/tab/layout_tab.vue
@@ -95,7 +95,7 @@
   }
 
   .back {
-    font-size: 20px;
+    font-size: 24px;
     margin-bottom: -2px;
     color: v-bind('PRIMARY_COLOR');
   }
diff --git a/ui-vue3/src/router/RouterMeta.ts b/ui-vue3/src/router/RouterMeta.ts
index f3a407b..0950b87 100644
--- a/ui-vue3/src/router/RouterMeta.ts
+++ b/ui-vue3/src/router/RouterMeta.ts
@@ -16,6 +16,7 @@
  */
 import type { RouteMeta } from 'vue-router'
 import type { RouteRecordType } from '@/router/defaultRoutes'
+import type { Component } from 'vue'
 
 export interface RouterMeta extends RouteMeta {
   icon?: string
@@ -25,4 +26,5 @@
   tab?: boolean
   _router_key?: string
   parent?: RouteRecordType
+  slots?: { [key: string]: Component }
 }
diff --git a/ui-vue3/src/router/defaultRoutes.ts b/ui-vue3/src/router/defaultRoutes.ts
index 70884b9..d67c200 100644
--- a/ui-vue3/src/router/defaultRoutes.ts
+++ b/ui-vue3/src/router/defaultRoutes.ts
@@ -19,6 +19,7 @@
 import type { RouteRecordRaw } from 'vue-router'
 import LayoutTab from '../layout/tab/layout_tab.vue'
 import _ from 'lodash'
+import AppTabHeaderSlot from '@/views/resources/applications/slots/AppTabHeaderSlot.vue'
 
 export declare type RouteRecordType = RouteRecordRaw & {
   key?: string
@@ -58,7 +59,10 @@
             component: LayoutTab,
             redirect: 'index',
             meta: {
-              tab_parent: true
+              tab_parent: true,
+              slots: {
+                header: AppTabHeaderSlot
+              }
             },
             children: [
               {
@@ -290,6 +294,22 @@
             name: 'dynamicConfig',
             component: () => import('../views/traffic/dynamicConfig/index.vue'),
             meta: {}
+          },
+          {
+            path: '/meshRule',
+            name: 'meshRule',
+            children: [
+              {
+                path: '/virtualService',
+                name: 'virtualService',
+                component: () => import('../views/traffic/virtualService/index.vue')
+              },
+              {
+                path: '/destinationRule',
+                name: 'destinationRule',
+                component: () => import('../views/traffic/destinationRule/index.vue')
+              }
+            ]
           }
         ]
       },
diff --git a/ui-vue3/src/utils/SearchUtil.ts b/ui-vue3/src/utils/SearchUtil.ts
index 31e4980..aeb39ec 100644
--- a/ui-vue3/src/utils/SearchUtil.ts
+++ b/ui-vue3/src/utils/SearchUtil.ts
@@ -22,6 +22,7 @@
 
 export class SearchDomain {
   // form of search
+  noPaged?: boolean
   queryForm: any
   params: [
     {
@@ -43,7 +44,7 @@
   tableStyle: any
   table: {
     loading?: boolean
-    columns: TableColumnsType
+    columns: (TableColumnsType & { __hide: boolean }) | any
   } = { columns: [] }
   paged = {
     curPage: 1,
@@ -55,9 +56,11 @@
     query: any,
     searchApi: any,
     columns: TableColumnsType | any,
-    paged?: any | undefined
+    paged?: any | undefined,
+    noPaged?: boolean
   ) {
     this.params = query
+    this.noPaged = noPaged
     this.queryForm = reactive({})
     this.table.columns = columns
     query.forEach((c: any) => {
diff --git a/ui-vue3/src/views/home/index.vue b/ui-vue3/src/views/home/index.vue
index 54d0a23..0d427f4 100644
--- a/ui-vue3/src/views/home/index.vue
+++ b/ui-vue3/src/views/home/index.vue
@@ -32,37 +32,43 @@
         </a-flex>
       </a-card>
     </a-flex>
-    <a-descriptions
-      title=" "
-      bordered
-      :column="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 1, xs: 1 }"
-      layout="horizontal"
-    >
-      <a-descriptions-item label="versions">
-        <a-tag :color="PRIMARY_COLOR" v-for="v in metricsMetadata.info.versions">{{ v }}</a-tag>
-      </a-descriptions-item>
-      <a-descriptions-item label="protocols">
-        <a-tag :color="PRIMARY_COLOR" v-for="v in metricsMetadata.info.protocols">{{ v }}</a-tag>
-      </a-descriptions-item>
-      <a-descriptions-item label="configCenter">{{
-        metricsMetadata.info.configCenter
-      }}</a-descriptions-item>
-      <a-descriptions-item label="registry">{{
-        metricsMetadata.info.registry
-      }}</a-descriptions-item>
-      <a-descriptions-item label="metadataCenter">{{
-        metricsMetadata.info.metadataCenter
-      }}</a-descriptions-item>
-      <a-descriptions-item label="grafana">{{ metricsMetadata.info.grafana }}</a-descriptions-item>
-      <a-descriptions-item label="prometheus">{{
-        metricsMetadata.info.prometheus
-      }}</a-descriptions-item>
-      <a-descriptions-item label="Remark">empty</a-descriptions-item>
-      <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>
-    <div id="report_container"></div>
+    <a-card class="card">
+      <a-descriptions
+        title=" "
+        bordered
+        :column="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 1, xs: 1 }"
+        layout="horizontal"
+      >
+        <a-descriptions-item label="versions">
+          <a-tag :color="PRIMARY_COLOR" v-for="v in metricsMetadata.info.versions">{{ v }}</a-tag>
+        </a-descriptions-item>
+        <a-descriptions-item label="protocols">
+          <a-tag :color="PRIMARY_COLOR" v-for="v in metricsMetadata.info.protocols">{{ v }}</a-tag>
+        </a-descriptions-item>
+        <a-descriptions-item label="configCenter">{{
+          metricsMetadata.info.configCenter
+        }}</a-descriptions-item>
+        <a-descriptions-item label="registry">{{
+          metricsMetadata.info.registry
+        }}</a-descriptions-item>
+        <a-descriptions-item label="metadataCenter">{{
+          metricsMetadata.info.metadataCenter
+        }}</a-descriptions-item>
+        <a-descriptions-item label="grafana">{{
+          metricsMetadata.info.grafana
+        }}</a-descriptions-item>
+        <a-descriptions-item label="prometheus">{{
+          metricsMetadata.info.prometheus
+        }}</a-descriptions-item>
+        <a-descriptions-item label="Remark">empty</a-descriptions-item>
+        <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>
+    </a-card>
+    <a-card class="card">
+      <div id="report_container"></div>
+    </a-card>
   </div>
 </template>
 
@@ -74,6 +80,7 @@
 import { getMetricsMetadata } from '@/api/service/serverInfo'
 import { useRoute } from 'vue-router'
 import { Chart } from '@antv/g2'
+import { queryPromSql } from '@/api/service/metricInfo'
 
 let __null = PRIMARY_COLOR
 
@@ -88,6 +95,12 @@
 })
 
 onMounted(async () => {
+  console.log(
+    await queryPromSql({
+      query:
+        '((node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes) / (node_memory_MemTotal_bytes)) * 100'
+    })
+  )
   let clusterData = (await getClusterInfo({})).data
   metricsMetadata.info = <{ [key: string]: string }>(await getMetricsMetadata({})).data
   clusterInfo.info = <{ [key: string]: string }>clusterData
@@ -165,5 +178,8 @@
     font-size: 20px;
     color: white;
   }
+  .card {
+    margin-top: 10px;
+  }
 }
 </style>
diff --git a/ui-vue3/src/views/resources/applications/index.vue b/ui-vue3/src/views/resources/applications/index.vue
index a0f3808..e23b0d9 100644
--- a/ui-vue3/src/views/resources/applications/index.vue
+++ b/ui-vue3/src/views/resources/applications/index.vue
@@ -19,7 +19,7 @@
     <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="grey">
+          <a-tag v-for="t in text">
             {{ t }}
           </a-tag>
         </template>
diff --git a/ui-vue3/src/views/resources/applications/slots/AppTabHeaderSlot.vue b/ui-vue3/src/views/resources/applications/slots/AppTabHeaderSlot.vue
new file mode 100644
index 0000000..8c92eeb
--- /dev/null
+++ b/ui-vue3/src/views/resources/applications/slots/AppTabHeaderSlot.vue
@@ -0,0 +1,44 @@
+<!--
+  ~ 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>
+  <!--      example like blow-->
+  <div class="__container_AppTabHeaderSlot">
+    <a-row>
+      <a-col :span="12">
+        <span class="header-desc"
+          >{{ $t('applicationDomain.name') }}: {{ route.params?.pathId }}</span
+        >
+      </a-col>
+      <!--      <a-col :span="12">-->
+      <!--       <a-button>custom function</a-button>-->
+      <!--      </a-col>-->
+    </a-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useRoute } from 'vue-router'
+const route = useRoute()
+</script>
+<style lang="less" scoped>
+.__container_AppTabHeaderSlot {
+  .header-desc {
+    line-height: 30px;
+    vertical-align: center;
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/applications/tabs/config.vue b/ui-vue3/src/views/resources/applications/tabs/config.vue
index 080cbc1..149e972 100644
--- a/ui-vue3/src/views/resources/applications/tabs/config.vue
+++ b/ui-vue3/src/views/resources/applications/tabs/config.vue
@@ -22,15 +22,135 @@
           <a-switch v-model:checked="current.form.logFlag"></a-switch>
         </a-form-item>
       </template>
-      <template v-slot:form_flow="{ current }"> 2 </template>
-      <template v-slot:form_gray="{ current }"> 3 </template>
+      <template v-slot:form_flow="{ current }">
+        <a-space>
+          <a-card v-for="(item, i) in current.form.rules">
+            <template #title>
+              {{ $t('applicationDomain.flowWeight') }} {{ i + 1 }}
+              <div style="float: right">
+                <a-space>
+                  <a-button type="dashed" @click="">
+                    <Icon
+                      style="font-size: 20px"
+                      icon="material-symbols-light:contract-edit"
+                    ></Icon>
+                  </a-button>
+                  <a-button danger type="dashed" @click="">
+                    <Icon style="font-size: 20px" icon="fluent:delete-12-filled"></Icon>
+                  </a-button>
+                </a-space>
+              </div>
+            </template>
+
+            <a-form-item :name="'rules[' + i + '].weight'" label="权重">
+              <a-input-number v-model:value="item.weight"></a-input-number>
+            </a-form-item>
+            <a-form-item label="作用范围">
+              <a-table
+                style="width: 40vw"
+                :pagination="false"
+                :columns="[
+                  { key: 'label', title: 'label' },
+                  { key: 'condition', title: 'condition' },
+                  { key: 'value', title: 'value' }
+                ]"
+                :data-source="[item.scope]"
+              >
+                <template #bodyCell="{ column, record, index }">
+                  <a-form-item
+                    v-if="column.key === 'condition'"
+                    :name="'rules[' + i + '].scope.condition'"
+                    label=""
+                  >
+                    <a-select v-model:value="item.scope.condition">
+                      <a-select-option value="=">=</a-select-option>
+                      <a-select-option value="!=">!=</a-select-option>
+                      <a-select-option value=">">></a-select-option>
+                      <a-select-option value="<">{{ '<' }}</a-select-option>
+                    </a-select>
+                  </a-form-item>
+                  <a-form-item
+                    v-else
+                    :name="'rules[' + i + '].scope.condition.' + column.key"
+                    label=""
+                  >
+                    <a-input v-model:value="item.scope[column.key]"></a-input>
+                  </a-form-item>
+                </template>
+              </a-table>
+            </a-form-item>
+          </a-card>
+        </a-space>
+      </template>
+      <template v-slot:form_gray="{ current }">
+        <a-space>
+          <a-card v-for="(item, i) in current.form.rules">
+            <template #title>
+              {{ $t('applicationDomain.gray') }} {{ i + 1 }}
+              <div style="float: right">
+                <a-space>
+                  <a-button type="dashed" @click="">
+                    <Icon
+                      style="font-size: 20px"
+                      icon="material-symbols-light:contract-edit"
+                    ></Icon>
+                  </a-button>
+                  <a-button danger type="dashed" @click="">
+                    <Icon style="font-size: 20px" icon="fluent:delete-12-filled"></Icon>
+                  </a-button>
+                </a-space>
+              </div>
+            </template>
+
+            <a-form-item :name="'rules[' + i + '].name'" label="环境名称">
+              <a-input v-model:value="item.name"></a-input>
+            </a-form-item>
+            <a-form-item label="作用范围">
+              <a-table
+                style="width: 40vw"
+                :pagination="false"
+                :columns="[
+                  { key: 'label', title: 'label' },
+                  { key: 'condition', title: 'condition' },
+                  { key: 'value', title: 'value' }
+                ]"
+                :data-source="[item.scope]"
+              >
+                <template #bodyCell="{ column, record, index }">
+                  <a-form-item
+                    v-if="column.key === 'condition'"
+                    :name="'rules[' + i + '].scope.condition'"
+                    label=""
+                  >
+                    <a-select v-model:value="item.scope.condition">
+                      <a-select-option value="=">=</a-select-option>
+                      <a-select-option value="!=">!=</a-select-option>
+                      <a-select-option value=">">></a-select-option>
+                      <a-select-option value="<">{{ '<' }}</a-select-option>
+                    </a-select>
+                  </a-form-item>
+                  <a-form-item
+                    v-else
+                    :name="'rules[' + i + '].scope.condition.' + column.key"
+                    label=""
+                  >
+                    <a-input v-model:value="item.scope[column.key]"></a-input>
+                  </a-form-item>
+                </template>
+              </a-table>
+            </a-form-item>
+          </a-card>
+        </a-space>
+      </template>
     </config-page>
   </div>
 </template>
 
 <script setup lang="ts">
-import { onMounted, reactive, ref } from 'vue'
+import { onMounted, reactive } from 'vue'
 import ConfigPage from '@/components/ConfigPage.vue'
+import { Icon } from '@iconify/vue'
+
 let options: any = reactive({
   list: [
     {
@@ -52,18 +172,57 @@
     },
     {
       title: 'applicationDomain.flowWeight',
-      form: {},
       key: 'flow',
+      ext: {
+        title: '添加权重配置',
+        fun() {}
+      },
+      form: {
+        rules: [
+          {
+            weight: '100',
+            scope: {
+              label: 'key1',
+              condition: '=',
+              value: 'value1'
+            }
+          }
+        ]
+      },
       submit(form: {}) {
-        console.log(form)
+        return new Promise((resolve) => {
+          setTimeout(() => {
+            resolve(1)
+          }, 1000)
+        })
       }
     },
+
     {
       title: 'applicationDomain.gray',
-      form: {},
       key: 'gray',
+      ext: {
+        title: '添加灰度环境',
+        fun() {}
+      },
+      form: {
+        rules: [
+          {
+            name: '100',
+            scope: {
+              label: 'key1',
+              condition: '=',
+              value: 'value1'
+            }
+          }
+        ]
+      },
       submit(form: {}) {
-        console.log(form)
+        return new Promise((resolve) => {
+          setTimeout(() => {
+            resolve(1)
+          }, 1000)
+        })
       }
     }
   ],
diff --git a/ui-vue3/src/views/resources/applications/tabs/event.vue b/ui-vue3/src/views/resources/applications/tabs/event.vue
index f584d4a..a91a2d2 100644
--- a/ui-vue3/src/views/resources/applications/tabs/event.vue
+++ b/ui-vue3/src/views/resources/applications/tabs/event.vue
@@ -15,14 +15,142 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_app_event">Event</div>
+  <div class="__container_app_event">
+    <a-timeline mode="left">
+      <a-timeline-item v-for="(item, i) in events.list">
+        <div class="box">
+          <div class="label" :class="{ yellow: i === 0 }">
+            <div class="type"></div>
+            <div class="body">
+              <b class="title">{{ item.type }}</b>
+              <p>{{ item.desc }}</p>
+            </div>
+          </div>
+          <span class="time">
+            {{ item.time }}
+          </span>
+        </div>
+        <template v-if="i === 0" #dot>
+          <clock-circle-outlined style="font-size: 16px; color: red" />
+        </template>
+
+        <!--        <a-card>-->
+        <!--          <a-row>-->
+        <!--            <a-col :span="4">-->
+        <!--&lt;!&ndash;             <div class="box">&ndash;&gt;-->
+        <!--&lt;!&ndash;               <div class="type"></div>&ndash;&gt;-->
+        <!--&lt;!&ndash;               <div class="body"></div>&ndash;&gt;-->
+        <!--&lt;!&ndash;             </div>&ndash;&gt;-->
+        <!--            </a-col>-->
+        <!--            <a-col :span="10">{{item.desc}}</a-col>-->
+        <!--          </a-row>-->
+        <!--        </a-card>-->
+      </a-timeline-item>
+    </a-timeline>
+  </div>
 </template>
 
 <script setup lang="ts">
-import { onMounted } from 'vue'
+import { onMounted, reactive } from 'vue'
+import { listApplicationEvent } from '@/api/service/app'
+import { ClockCircleOutlined } from '@ant-design/icons-vue'
+import { PRIMARY_COLOR } from '@/base/constants'
 
-onMounted(() => {
-  console.log(333)
+let __ = PRIMARY_COLOR
+let events: any = reactive({ list: [] })
+onMounted(async () => {
+  let eventsRes = await listApplicationEvent({})
+  events.list = eventsRes.data.list
+  console.log(events)
 })
 </script>
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.__container_app_event {
+  :deep(.ant-timeline-item-label) {
+    width: 200px;
+  }
+
+  background: #fafafa;
+  border-radius: 10px;
+  padding: 80px 300px 20px;
+
+  .box {
+    position: relative;
+    height: 100px;
+    margin-bottom: 20px;
+    //top:-38px;
+
+    .label {
+      position: absolute;
+      height: 100px;
+      top: -40px;
+
+      &.yellow {
+        .type {
+          border-right-color: #f8d347;
+        }
+        .body {
+          background: #f8d347;
+        }
+      }
+      &.red {
+        .type {
+          border-right-color: #eb4325;
+        }
+        .body {
+          background: #eb4325;
+        }
+      }
+      &.blue {
+        .type {
+          border-right-color: #3d89f6;
+        }
+        .body {
+          background: #3d89f6;
+        }
+      }
+      &.green {
+        .type {
+          border-right-color: #9cac35;
+        }
+        .body {
+          background: #9cac35;
+        }
+      }
+      .type {
+        position: absolute;
+        width: 50px;
+        height: 50px;
+        border-style: solid;
+        border-color: transparent;
+        border-width: 50px 26px 50px 0px;
+        border-right-color: v-bind('PRIMARY_COLOR');
+        display: inline;
+        border-radius: 4px;
+      }
+      .body {
+        position: absolute;
+        left: 49px;
+        width: 50vw;
+        border-radius: 3px 5px 5px 3px;
+        height: 100%;
+        display: inline;
+        color: white;
+        padding-left: 20px;
+        background: v-bind('PRIMARY_COLOR');
+        box-shadow: 8px 5px 10px #9f9c9c;
+
+        .title {
+          font-size: 30px;
+          line-height: 40px;
+        }
+      }
+    }
+    .time {
+      position: absolute;
+      left: -200px;
+      //top: 38px;
+    }
+  }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/applications/tabs/instance.vue b/ui-vue3/src/views/resources/applications/tabs/instance.vue
index 3ca62cf..7d7f9ac 100644
--- a/ui-vue3/src/views/resources/applications/tabs/instance.vue
+++ b/ui-vue3/src/views/resources/applications/tabs/instance.vue
@@ -40,9 +40,9 @@
           <a-tag :color="INSTANCE_DEPLOY_COLOR[text.toUpperCase()]">{{ text }}</a-tag>
         </template>
         <template v-if="column.dataIndex === 'registerStates'">
-          <a-tag :color="INSTANCE_REGISTER_COLOR[t.level.toUpperCase()]" v-for="t in text">{{
-            t.label
-          }}</a-tag>
+          <a-tag :color="INSTANCE_REGISTER_COLOR[t.level.toUpperCase()]" v-for="t in text"
+            >{{ t.label }}
+          </a-tag>
         </template>
         <template v-if="column.dataIndex === 'registerClusters'">
           <a-tag v-for="t in text">{{ t }}</a-tag>
@@ -57,14 +57,10 @@
 
 <script setup lang="ts">
 import { onMounted, provide, reactive } from 'vue'
-import { getClusterInfo } from '@/api/service/clusterInfo'
-import { getMetricsMetadata } from '@/api/service/serverInfo'
-import { Chart } from '@antv/g2'
 import { INSTANCE_DEPLOY_COLOR, INSTANCE_REGISTER_COLOR, PRIMARY_COLOR } from '@/base/constants'
 import { Icon } from '@iconify/vue'
 import SearchTable from '@/components/SearchTable.vue'
 import { SearchDomain } from '@/utils/SearchUtil'
-import { searchService } from '@/api/service/service'
 import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 import { useRoute, useRouter } from 'vue-router'
 import { getApplicationInstanceInfo, getApplicationInstanceStatistics } from '@/api/service/app'
@@ -206,13 +202,15 @@
     ],
     getApplicationInstanceInfo,
     columns,
-    { pageSize: 4 }
+    { pageSize: 4 },
+    true
   )
 )
 
 onMounted(() => {
   searchDomain.tableStyle = {
-    scrollX: '100'
+    scrollX: '100',
+    scrollY: '500px'
   }
   searchDomain.onSearch()
 })
diff --git a/ui-vue3/src/views/resources/applications/tabs/service.vue b/ui-vue3/src/views/resources/applications/tabs/service.vue
index 7dbd356..9841b6c 100644
--- a/ui-vue3/src/views/resources/applications/tabs/service.vue
+++ b/ui-vue3/src/views/resources/applications/tabs/service.vue
@@ -67,6 +67,10 @@
 })
 
 onMounted(async () => {
+  searchDomain.tableStyle = {
+    scrollX: '100',
+    scrollY: 'calc(100vh - 600px)'
+  }
   let clusterData = (await getClusterInfo({})).data
   metricsMetadata.info = <{ [key: string]: string }>(await getMetricsMetadata({})).data
   clusterInfo.info = <{ [key: string]: string }>clusterData
@@ -144,7 +148,8 @@
     ],
     searchService,
     columns,
-    { pageSize: 4 }
+    { pageSize: 4 },
+    true
   )
 )
 searchDomain.onSearch()
diff --git a/ui-vue3/src/views/traffic/destinationRule/index.vue b/ui-vue3/src/views/traffic/destinationRule/index.vue
new file mode 100644
index 0000000..05b92be
--- /dev/null
+++ b/ui-vue3/src/views/traffic/destinationRule/index.vue
@@ -0,0 +1,103 @@
+<!--
+  ~ 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_resources_application_index">
+    <search-table :search-domain="searchDomain">
+      <template #customOperation>
+        <a-button type="primary">新增 Destination Rule</a-button>
+      </template>
+      <template #bodyCell="{ text, column }">
+        <template v-if="column.dataIndex === 'ruleName'">
+          <a-button type="link">{{ text }}</a-button>
+        </template>
+        <template v-if="column.dataIndex === 'operation'">
+          <a-button type="link">查看</a-button>
+          <a-button type="link">修改</a-button>
+          <a-popconfirm
+            title="确认删除该动态配置?"
+            ok-text="Yes"
+            cancel-text="No"
+            @confirm="confirm"
+          >
+            <a-button type="link">删除</a-button>
+          </a-popconfirm>
+        </template>
+      </template>
+    </search-table>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, provide, reactive } from 'vue'
+import { searchDestinationRule } from '@/api/service/traffic'
+import SearchTable from '@/components/SearchTable.vue'
+import { SearchDomain, sortString } from '@/utils/SearchUtil'
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+
+let columns = [
+  {
+    title: 'ruleName',
+    key: 'ruleName',
+    dataIndex: 'ruleName',
+    sorter: (a: any, b: any) => sortString(a.appName, b.appName),
+    width: 140
+  },
+  {
+    title: 'createTime',
+    key: 'createTime',
+    dataIndex: 'createTime',
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'operation',
+    key: 'operation',
+    dataIndex: 'operation',
+    width: 200
+  }
+]
+const searchDomain = reactive(
+  new SearchDomain(
+    [
+      {
+        label: 'serviceGovernance',
+        param: 'serviceGovernance',
+        placeholder: 'typeRoutingRules',
+        style: {
+          width: '200px'
+        }
+      }
+    ],
+    searchDestinationRule,
+    columns
+  )
+)
+
+onMounted(() => {
+  searchDomain.onSearch()
+})
+
+const confirm = () => {}
+
+provide(PROVIDE_INJECT_KEY.SEARCH_DOMAIN, searchDomain)
+</script>
+<style lang="less" scoped>
+.search-table-container {
+  min-height: 60vh;
+  //max-height: 70vh; //overflow: auto;
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/dynamicConfig/index.vue b/ui-vue3/src/views/traffic/dynamicConfig/index.vue
index c97d821..fbef790 100644
--- a/ui-vue3/src/views/traffic/dynamicConfig/index.vue
+++ b/ui-vue3/src/views/traffic/dynamicConfig/index.vue
@@ -15,15 +15,112 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_home_index">
-    <h1>{{ $t(routeName) }}</h1>
+  <div class="__container_resources_application_index">
+    <search-table :search-domain="searchDomain">
+      <template #customOperation>
+        <a-button type="primary">新增动态配置</a-button>
+        <a-button type="primary" style="margin-left: 10px">从模版创建</a-button>
+      </template>
+      <template #bodyCell="{ text, column }">
+        <template v-if="column.dataIndex === 'ruleName'">
+          <a-button type="link">{{ text }}</a-button>
+        </template>
+        <template v-if="column.dataIndex === 'ruleGranularity'">
+          {{ text ? '服务' : '应用' }}
+        </template>
+        <template v-if="column.dataIndex === 'enable'">
+          {{ text ? '启用' : '禁用' }}
+        </template>
+        <template v-if="column.dataIndex === 'operation'">
+          <a-button type="link">查看</a-button>
+          <a-button type="link">修改</a-button>
+          <a-popconfirm
+            title="确认删除该动态配置?"
+            ok-text="Yes"
+            cancel-text="No"
+            @confirm="confirm"
+          >
+            <a-button type="link">删除</a-button>
+          </a-popconfirm>
+        </template>
+      </template>
+    </search-table>
   </div>
 </template>
 
 <script setup lang="ts">
-import { Icon } from '@iconify/vue'
+import { onMounted, provide, reactive } from 'vue'
+import { searchDynamicConfig } from '@/api/service/traffic'
+import SearchTable from '@/components/SearchTable.vue'
+import { SearchDomain, sortString } from '@/utils/SearchUtil'
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 
-import { useRoute } from 'vue-router'
-const routeName = <string>useRoute().name
+let columns = [
+  {
+    title: 'ruleName',
+    key: 'ruleName',
+    dataIndex: 'ruleName',
+    sorter: (a: any, b: any) => sortString(a.appName, b.appName),
+    width: 140
+  },
+  {
+    title: 'ruleGranularity',
+    key: 'ruleGranularity',
+    dataIndex: 'ruleGranularity',
+    render: (text, record) => (record.isService ? '服务' : '应用'),
+    width: 100,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'createTime',
+    key: 'createTime',
+    dataIndex: 'createTime',
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'enable',
+    key: 'enable',
+    dataIndex: 'enable',
+    render: (text, record) => (record.enable ? '是' : '否'),
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'operation',
+    key: 'operation',
+    dataIndex: 'operation',
+    width: 200
+  }
+]
+const searchDomain = reactive(
+  new SearchDomain(
+    [
+      {
+        label: 'serviceGovernance',
+        param: 'serviceGovernance',
+        placeholder: 'typeRoutingRules',
+        style: {
+          width: '200px'
+        }
+      }
+    ],
+    searchDynamicConfig,
+    columns
+  )
+)
+
+onMounted(() => {
+  searchDomain.onSearch()
+})
+
+const confirm = () => {}
+
+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/traffic/routingRule/index.vue b/ui-vue3/src/views/traffic/routingRule/index.vue
index c899666..14e82ed 100644
--- a/ui-vue3/src/views/traffic/routingRule/index.vue
+++ b/ui-vue3/src/views/traffic/routingRule/index.vue
@@ -17,12 +17,30 @@
 <template>
   <div class="__container_resources_application_index">
     <search-table :search-domain="searchDomain">
-      <template #bodyCell="{ text, record, index, column }">
-        <template v-if="column.dataIndex === 'enable'">
-          {{ text ? '是' : '否' }}
+      <template #customOperation>
+        <a-button type="primary">新增条件路由规则</a-button>
+      </template>
+      <template #bodyCell="{ text, column }">
+        <template v-if="column.dataIndex === 'ruleName'">
+          <a-button type="link">{{ text }}</a-button>
         </template>
-        <template v-if="column.dataIndex === 'protection'">
-          {{ text ? '是' : '否' }}
+        <template v-if="column.dataIndex === 'ruleGranularity'">
+          {{ text ? '服务' : '应用' }}
+        </template>
+        <template v-if="column.dataIndex === 'enable'">
+          {{ text ? '启用' : '禁用' }}
+        </template>
+        <template v-if="column.dataIndex === 'operation'">
+          <a-button type="link">查看</a-button>
+          <a-button type="link">修改</a-button>
+          <a-popconfirm
+            title="确认删除该条件路由规则?"
+            ok-text="Yes"
+            cancel-text="No"
+            @confirm="confirm"
+          >
+            <a-button type="link">删除</a-button>
+          </a-popconfirm>
         </template>
       </template>
     </search-table>
@@ -48,27 +66,29 @@
     title: 'ruleGranularity',
     key: 'ruleGranularity',
     dataIndex: 'ruleGranularity',
+    render: (text, record) => (record.isService ? '服务' : '应用'),
     width: 100,
     sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
   },
   {
+    title: 'createTime',
+    key: 'createTime',
+    dataIndex: 'createTime',
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
     title: 'enable',
     key: 'enable',
     dataIndex: 'enable',
     render: (text, record) => (record.enable ? '是' : '否'),
-    width: 120
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
   },
   {
-    title: 'effectiveTime',
-    key: 'effectiveTime',
-    dataIndex: 'effectiveTime',
-    width: 120
-  },
-  {
-    title: 'protection',
-    key: 'protection',
-    dataIndex: 'protection',
-    render: (text, record) => (record.protection ? '是' : '否'),
+    title: 'operation',
+    key: 'operation',
+    dataIndex: 'operation',
     width: 200
   }
 ]
@@ -93,6 +113,8 @@
   searchDomain.onSearch()
 })
 
+const confirm = () => {}
+
 provide(PROVIDE_INJECT_KEY.SEARCH_DOMAIN, searchDomain)
 </script>
 <style lang="less" scoped>
diff --git a/ui-vue3/src/views/traffic/tagRule/index.vue b/ui-vue3/src/views/traffic/tagRule/index.vue
index c97d821..d72618b 100644
--- a/ui-vue3/src/views/traffic/tagRule/index.vue
+++ b/ui-vue3/src/views/traffic/tagRule/index.vue
@@ -15,15 +15,100 @@
   ~ limitations under the License.
 -->
 <template>
-  <div class="__container_home_index">
-    <h1>{{ $t(routeName) }}</h1>
+  <div class="__container_resources_application_index">
+    <search-table :search-domain="searchDomain">
+      <template #customOperation>
+        <a-button type="primary">新增标签路由规则</a-button>
+      </template>
+      <template #bodyCell="{ text, column }">
+        <template v-if="column.dataIndex === 'ruleName'">
+          <a-button type="link">{{ text }}</a-button>
+        </template>
+        <template v-if="column.dataIndex === 'enable'">
+          {{ text ? '启用' : '禁用' }}
+        </template>
+        <template v-if="column.dataIndex === 'operation'">
+          <a-button type="link">查看</a-button>
+          <a-button type="link">修改</a-button>
+          <a-popconfirm
+            title="确认删除该标签路由规则?"
+            ok-text="Yes"
+            cancel-text="No"
+            @confirm="confirm"
+          >
+            <a-button type="link">删除</a-button>
+          </a-popconfirm>
+        </template>
+      </template>
+    </search-table>
   </div>
 </template>
 
 <script setup lang="ts">
-import { Icon } from '@iconify/vue'
+import { onMounted, provide, reactive } from 'vue'
+import { searchTagRule } from '@/api/service/traffic'
+import SearchTable from '@/components/SearchTable.vue'
+import { SearchDomain, sortString } from '@/utils/SearchUtil'
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 
-import { useRoute } from 'vue-router'
-const routeName = <string>useRoute().name
+let columns = [
+  {
+    title: 'ruleName',
+    key: 'ruleName',
+    dataIndex: 'ruleName',
+    sorter: (a: any, b: any) => sortString(a.appName, b.appName),
+    width: 140
+  },
+  {
+    title: 'createTime',
+    key: 'createTime',
+    dataIndex: 'createTime',
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'enable',
+    key: 'enable',
+    dataIndex: 'enable',
+    render: (text, record) => (record.enable ? '是' : '否'),
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'operation',
+    key: 'operation',
+    dataIndex: 'operation',
+    width: 200
+  }
+]
+const searchDomain = reactive(
+  new SearchDomain(
+    [
+      {
+        label: 'serviceGovernance',
+        param: 'serviceGovernance',
+        placeholder: 'typeRoutingRules',
+        style: {
+          width: '200px'
+        }
+      }
+    ],
+    searchTagRule,
+    columns
+  )
+)
+
+onMounted(() => {
+  searchDomain.onSearch()
+})
+
+const confirm = () => {}
+
+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/traffic/virtualService/index.vue b/ui-vue3/src/views/traffic/virtualService/index.vue
new file mode 100644
index 0000000..4e1f049
--- /dev/null
+++ b/ui-vue3/src/views/traffic/virtualService/index.vue
@@ -0,0 +1,110 @@
+<!--
+  ~ 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_resources_application_index">
+    <search-table :search-domain="searchDomain">
+      <template #customOperation>
+        <a-button type="primary">新增路由规则</a-button>
+      </template>
+      <template #bodyCell="{ text, column }">
+        <template v-if="column.dataIndex === 'ruleName'">
+          <a-button type="link">{{ text }}</a-button>
+        </template>
+        <template v-if="column.dataIndex === 'operation'">
+          <a-button type="link">查看</a-button>
+          <a-button type="link">修改</a-button>
+          <a-popconfirm
+            title="确认删除该动态配置?"
+            ok-text="Yes"
+            cancel-text="No"
+            @confirm="confirm"
+          >
+            <a-button type="link">删除</a-button>
+          </a-popconfirm>
+        </template>
+      </template>
+    </search-table>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, provide, reactive } from 'vue'
+import { searchVirtualService } from '@/api/service/traffic'
+import SearchTable from '@/components/SearchTable.vue'
+import { SearchDomain, sortString } from '@/utils/SearchUtil'
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+
+let columns = [
+  {
+    title: 'ruleName',
+    key: 'ruleName',
+    dataIndex: 'ruleName',
+    sorter: (a: any, b: any) => sortString(a.appName, b.appName),
+    width: 140
+  },
+  {
+    title: 'createTime',
+    key: 'createTime',
+    dataIndex: 'createTime',
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'lastModifiedTime',
+    key: 'lastModifiedTime',
+    dataIndex: 'lastModifiedTime',
+    width: 120,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'operation',
+    key: 'operation',
+    dataIndex: 'operation',
+    width: 200
+  }
+]
+const searchDomain = reactive(
+  new SearchDomain(
+    [
+      {
+        label: 'serviceGovernance',
+        param: 'serviceGovernance',
+        placeholder: 'typeRoutingRules',
+        style: {
+          width: '200px'
+        }
+      }
+    ],
+    searchVirtualService,
+    columns
+  )
+)
+
+onMounted(() => {
+  searchDomain.onSearch()
+})
+
+const confirm = () => {}
+
+provide(PROVIDE_INJECT_KEY.SEARCH_DOMAIN, searchDomain)
+</script>
+<style lang="less" scoped>
+.search-table-container {
+  min-height: 60vh;
+  //max-height: 70vh; //overflow: auto;
+}
+</style>