feat: add traffic page
diff --git a/ui-vue3/src/components/SearchTable.vue b/ui-vue3/src/components/SearchTable.vue
index db40551..d9f53e8 100644
--- a/ui-vue3/src/components/SearchTable.vue
+++ b/ui-vue3/src/components/SearchTable.vue
@@ -18,7 +18,7 @@
   <div class="__container_search_table">
     <div class="search-query-container">
       <a-row>
-        <a-col :span="20">
+        <a-col :span="18">
           <a-form>
             <a-flex wrap="wrap" gap="large">
               <template v-for="q in searchDomain.params">
@@ -67,28 +67,31 @@
             </a-flex>
           </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>
+        <a-col :span="6">
+          <a-flex style="justify-content: flex-end;">
+            <slot name="customOperation"></slot>
+            <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>
+                <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>
+          </a-flex>
         </a-col>
       </a-row>
     </div>
@@ -125,13 +128,15 @@
 
 <script setup lang="ts">
 import type { ComponentInternalInstance } from 'vue'
-import { computed, getCurrentInstance, inject, reactive } from 'vue'
+import { computed, getCurrentInstance, inject, reactive, useSlots } from 'vue'
 
 import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
 import type { SearchDomain } from '@/utils/SearchUtil'
 import { Icon } from '@iconify/vue'
 import { PRIMARY_COLOR } from '@/base/constants'
 
+const customOperation = !!useSlots().customOperation;
+
 const commonTool = reactive({
   customColumns: false
 })
@@ -215,7 +220,7 @@
       }
 
       svg {
-        margin-left: 10px;
+        margin-left: 20px;
       }
     }
   }
diff --git a/ui-vue3/src/router/defaultRoutes.ts b/ui-vue3/src/router/defaultRoutes.ts
index 70884b9..f2e528c 100644
--- a/ui-vue3/src/router/defaultRoutes.ts
+++ b/ui-vue3/src/router/defaultRoutes.ts
@@ -290,6 +290,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/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>