Merge pull request #239 from Helltab/feature/ui/framework/vue3

feat(#237):
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..48e341a
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3 @@
+{
+  "lockfileVersion": 1
+}
diff --git a/ui-vue3/src/api/mock/mockRoutingRule.ts b/ui-vue3/src/api/mock/mockRoutingRule.ts
new file mode 100644
index 0000000..bd352da
--- /dev/null
+++ b/ui-vue3/src/api/mock/mockRoutingRule.ts
@@ -0,0 +1,42 @@
+/*
+ * 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/routingRule/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('@integer(80, 200)'),
+      enable: Mock.mock('@boolean'),
+      effectiveTime: Mock.mock('@datetime'),
+      protection: Mock.mock('@boolean')
+    })
+  }
+  return {
+    code: 200,
+    message: 'success',
+    data: Mock.mock({
+      total: total,
+      curPage: 1,
+      pageSize: 10,
+      data: list
+    })
+  }
+})
diff --git a/ui-vue3/src/api/service/traffic.ts b/ui-vue3/src/api/service/traffic.ts
new file mode 100644
index 0000000..380f230
--- /dev/null
+++ b/ui-vue3/src/api/service/traffic.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 searchRoutingRule = (params: any): Promise<any> => {
+  return request({
+    url: '/routingRule/search',
+    method: 'get',
+    params
+  })
+}
diff --git a/ui-vue3/src/base/i18n/zh.ts b/ui-vue3/src/base/i18n/zh.ts
index 28f8940..aa36396 100644
--- a/ui-vue3/src/base/i18n/zh.ts
+++ b/ui-vue3/src/base/i18n/zh.ts
@@ -289,6 +289,11 @@
   editMockRule: '修改规则',
   deleteRuleTitle: '确定要删除此服务Mock规则吗?',
 
+  ruleName: '规则名',
+  ruleGranularity: '规则粒度',
+  effectiveTime: '生效时间',
+  enable: '是否启用',
+  protection: '容错保护',
   trafficTimeout: '超时时间',
   trafficRetry: '调用重试',
   trafficRegion: '同区域优先',
@@ -348,7 +353,8 @@
 
   placeholder: {
     typeAppName: '请输入应用名,支持前缀搜索',
-    typeDefault: '请输入'
+    typeDefault: '请输入',
+    typeRoutingRules: '搜索路由规则,支持前缀过滤'
   },
   none: '无',
   details: '详情',
diff --git a/ui-vue3/src/router/defaultRoutes.ts b/ui-vue3/src/router/defaultRoutes.ts
index 361d7ef..70884b9 100644
--- a/ui-vue3/src/router/defaultRoutes.ts
+++ b/ui-vue3/src/router/defaultRoutes.ts
@@ -268,6 +268,32 @@
         ]
       },
       {
+        path: '/traffic',
+        name: 'trafficManagement',
+        meta: {
+          icon: 'eos-icons:cluster-management'
+        },
+        children: [
+          {
+            path: '/routingRule',
+            name: 'routingRule',
+            component: () => import('../views/traffic/routingRule/index.vue')
+          },
+          {
+            path: '/tagRule',
+            name: 'tagRule',
+            component: () => import('../views/traffic/tagRule/index.vue'),
+            meta: {}
+          },
+          {
+            path: '/dynamicConfig',
+            name: 'dynamicConfig',
+            component: () => import('../views/traffic/dynamicConfig/index.vue'),
+            meta: {}
+          }
+        ]
+      },
+      {
         path: '/common',
         name: 'commonDemo',
         redirect: 'tab',
@@ -341,7 +367,7 @@
   parent: RouteRecordType | undefined
 ) {
   if (!routes) return
-  for (let route of routes) {
+  for (const route of routes) {
     if (parent) {
       route.path = handlePath(parent?.path, route.path)
     }
diff --git a/ui-vue3/src/views/traffic/routingRule/index.vue b/ui-vue3/src/views/traffic/routingRule/index.vue
index c97d821..c899666 100644
--- a/ui-vue3/src/views/traffic/routingRule/index.vue
+++ b/ui-vue3/src/views/traffic/routingRule/index.vue
@@ -15,15 +15,89 @@
   ~ 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 #bodyCell="{ text, record, index, column }">
+        <template v-if="column.dataIndex === 'enable'">
+          {{ text ? '是' : '否' }}
+        </template>
+        <template v-if="column.dataIndex === 'protection'">
+          {{ text ? '是' : '否' }}
+        </template>
+      </template>
+    </search-table>
   </div>
 </template>
 
 <script setup lang="ts">
-import { Icon } from '@iconify/vue'
+import { onMounted, provide, reactive } from 'vue'
+import { searchRoutingRule } 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',
+    width: 100,
+    sorter: (a: any, b: any) => sortString(a.instanceNum, b.instanceNum)
+  },
+  {
+    title: 'enable',
+    key: 'enable',
+    dataIndex: 'enable',
+    render: (text, record) => (record.enable ? '是' : '否'),
+    width: 120
+  },
+  {
+    title: 'effectiveTime',
+    key: 'effectiveTime',
+    dataIndex: 'effectiveTime',
+    width: 120
+  },
+  {
+    title: 'protection',
+    key: 'protection',
+    dataIndex: 'protection',
+    render: (text, record) => (record.protection ? '是' : '否'),
+    width: 200
+  }
+]
+const searchDomain = reactive(
+  new SearchDomain(
+    [
+      {
+        label: 'serviceGovernance',
+        param: 'serviceGovernance',
+        placeholder: 'typeRoutingRules',
+        style: {
+          width: '200px'
+        }
+      }
+    ],
+    searchRoutingRule,
+    columns
+  )
+)
+
+onMounted(() => {
+  searchDomain.onSearch()
+})
+
+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>