Merge pull request #263 from jianyi-gronk/feat/ui/service_pages

complete service pages
diff --git a/ui-vue3/src/components/editor/MonacoEditor.vue b/ui-vue3/src/components/editor/MonacoEditor.vue
index 0dfee0e..fc7df7b 100644
--- a/ui-vue3/src/components/editor/MonacoEditor.vue
+++ b/ui-vue3/src/components/editor/MonacoEditor.vue
@@ -25,7 +25,7 @@
 </template>
 
 <script lang="ts" setup>
-import { defineEmits, onMounted, watch } from 'vue'
+import { onMounted, watch } from 'vue'
 import useMonaco from './MonacoEditor'
 
 const emit = defineEmits(['update:modelValue', 'blur'])
diff --git a/ui-vue3/src/views/resources/services/tabs/distribution.vue b/ui-vue3/src/views/resources/services/tabs/distribution.vue
index ef7a335..5ba4a80 100644
--- a/ui-vue3/src/views/resources/services/tabs/distribution.vue
+++ b/ui-vue3/src/views/resources/services/tabs/distribution.vue
@@ -38,7 +38,18 @@
       >
         <template #bodyCell="{ column, text }">
           <template v-if="column.dataIndex === 'applicationName'">
-            <span class="app-link" @click="viewDetail(text)">
+            <span class="link" @click="router.push('/resources/applications/detail/' + text)">
+              <b>
+                <Icon
+                  style="margin-bottom: -2px"
+                  icon="material-symbols:attach-file-rounded"
+                ></Icon>
+                {{ text }}
+              </b>
+            </span>
+          </template>
+          <template v-if="column.dataIndex === 'instanceName'">
+            <span class="link" @click="router.push('/resources/instances/detail/' + text)">
               <b>
                 <Icon
                   style="margin-bottom: -2px"
@@ -60,12 +71,14 @@
 <script setup lang="ts">
 import type { ComponentInternalInstance } from 'vue'
 import { ref, reactive, getCurrentInstance } from 'vue'
+import { useRouter } from 'vue-router'
 import { getServiceDistribution } from '@/api/service/service'
 import { debounce } from 'lodash'
 import { PRIMARY_COLOR } from '@/base/constants'
 import { Icon } from '@iconify/vue'
 
 let __null = PRIMARY_COLOR
+const router = useRouter();
 const {
   appContext: {
     config: { globalProperties }
@@ -179,7 +192,7 @@
       width: 300px;
     }
   }
-  .app-link {
+  .link {
     padding: 4px 10px 4px 4px;
     border-radius: 4px;
     color: v-bind('PRIMARY_COLOR');
diff --git a/ui-vue3/src/views/resources/services/tabs/event.vue b/ui-vue3/src/views/resources/services/tabs/event.vue
index 11a5eaa..d2d41af 100644
--- a/ui-vue3/src/views/resources/services/tabs/event.vue
+++ b/ui-vue3/src/views/resources/services/tabs/event.vue
@@ -16,17 +16,27 @@
 -->
 <template>
   <div class="__container_services_tabs_event">
-    <a-timeline class="timeline">
-      <a-timeline-item v-for="(item, index) in eventData" :key="index">
-        <a-tag class="time" :color="PRIMARY_COLOR">{{ item.time }}</a-tag>
-        <span class="description">{{ item.description }}</span>
-      </a-timeline-item>
-    </a-timeline>
+    <a-card class="timeline-container">
+      <a-timeline class="timeline">
+        <a-timeline-item>
+          <template #dot><MinusCircleOutlined style="font-size: 18px" /></template>
+        </a-timeline-item>
+        <a-timeline-item v-for="(item, index) in eventData" :key="index">
+          <a-tag class="time" :color="PRIMARY_COLOR">{{ item.time }}</a-tag>
+          <span class="description">{{ item.description }}</span>
+        </a-timeline-item>
+        <a-timeline-item>
+          <template #dot></template>
+          <span>过期事件不会存储</span>
+        </a-timeline-item>
+      </a-timeline>
+    </a-card>
   </div>
 </template>
 
 <script setup lang="ts">
 import { PRIMARY_COLOR } from '@/base/constants'
+import { MinusCircleOutlined } from '@ant-design/icons-vue'
 
 let __null = PRIMARY_COLOR
 const eventData = [
@@ -56,10 +66,13 @@
 <style lang="less" scoped>
 .__container_services_tabs_event {
   display: flex;
-  justify-content: center;
-  .timeline {
-    .description {
-      font-size: 16px;
+  .timeline-container {
+    width: 100%;
+    .timeline {
+      margin-left: 30px;
+      .description {
+        font-size: 18px;
+      }
     }
   }
 }
diff --git a/ui-vue3/src/views/resources/services/tabs/paramRoute.vue b/ui-vue3/src/views/resources/services/tabs/paramRoute.vue
index 6bcfce3..0dc370c 100644
--- a/ui-vue3/src/views/resources/services/tabs/paramRoute.vue
+++ b/ui-vue3/src/views/resources/services/tabs/paramRoute.vue
@@ -20,16 +20,20 @@
       <template #title>
         <a-flex justify="space-between">
           <span>路由</span>
-          <a-flex class="handle-form">
-            <EditOutlined class="edit-icon" />
-            <DeleteOutlined class="edit-icon" />
+          <a-flex class="handle-form" v-if="!isEdit">
+            <EditOutlined @click="changeEditState" class="edit-icon" />
+            <DeleteOutlined @click="emit('deleteParamRoute', props.index)" class="edit-icon" />
+          </a-flex>
+          <a-flex class="handle-form" v-else>
+            <CheckOutlined @click="update"  class="edit-icon" />
+            <CloseOutlined @click="reset" class="edit-icon" />
           </a-flex>
         </a-flex>
       </template>
-      <a-form :labelCol="{ span: 3 }">
+      <a-form :labelCol="{ span: 3 }" :disabled="!isEdit">
         <a-form-item label="选择方法">
-          <a-select v-model:value="method.value" style="width: 120px">
-            <a-select-option v-for="(item, index) in method.selectArr" :value="item" :key="index">
+          <a-select v-model:value="editValue.method.value" style="width: 120px">
+            <a-select-option v-for="(item, index) in editValue.method.selectArr" :value="item" :key="index">
               {{ item }}
             </a-select-option>
           </a-select>
@@ -37,28 +41,30 @@
         <a-form-item label="指定方法参数">
           <a-table
             :columns="functionParamsColumn"
-            :data-source="props.paramRouteForm.functionParams"
+            :data-source="editValue.functionParams"
             :pagination="false"
           >
             <template #bodyCell="{ column, index: idx }">
               <template v-if="column.dataIndex === 'param'">
-                <a-input v-model:value="functionParamsEdit[idx].param" />
+                <a-input v-model:value="editValue.functionParams[idx].param" />
               </template>
               <template v-if="column.dataIndex === 'relation'">
-                <a-input v-model:value="functionParamsEdit[idx].relation" />
+                <a-input v-model:value="editValue.functionParams[idx].relation" />
               </template>
               <template v-if="column.dataIndex === 'value'">
-                <a-input v-model:value="functionParamsEdit[idx].value" />
+                <a-input v-model:value="editValue.functionParams[idx].value" />
               </template>
               <template v-if="column.dataIndex === 'handle'">
                 <a-flex justify="space-between">
                   <PlusOutlined
                     class="edit-icon"
-                    @click="emit('addRow', 'functionParams', props.index, idx)"
+                    :class="{'disabled-icon': !isEdit}"
+                    @click="isEdit && addFunctionParams(props.index, idx)"
                   />
                   <MinusOutlined
                     class="edit-icon"
-                    @click="emit('deleteRow', 'functionParams', props.index, idx)"
+                    :class="{'disabled-icon': !isEdit || editValue.functionParams.length === 1}"
+                    @click="isEdit && editValue.functionParams.length !== 1 && deleteFunctionParams(props.index, idx)"
                   />
                 </a-flex>
               </template>
@@ -68,31 +74,33 @@
         <a-form-item label="路由目的地">
           <a-table
             :columns="destinationColumn"
-            :data-source="props.paramRouteForm.destination"
+            :data-source="editValue.destination"
             :pagination="false"
           >
             <template #bodyCell="{ column, index: idx }">
               <template v-if="column.dataIndex === 'label'">
-                <a-input v-model:value="destinationEdit[idx].label" />
+                <a-input v-model:value="editValue.destination[idx].label" />
               </template>
               <template v-if="column.dataIndex === 'relation'">
-                <a-input v-model:value="destinationEdit[idx].relation" />
+                <a-input v-model:value="editValue.destination[idx].relation" />
               </template>
               <template v-if="column.dataIndex === 'value'">
-                <a-input v-model:value="destinationEdit[idx].value" />
+                <a-input v-model:value="editValue.destination[idx].value" />
               </template>
               <template v-if="column.dataIndex === 'weight'">
-                <a-input v-model:value="destinationEdit[idx].weight" />
+                <a-input v-model:value="editValue.destination[idx].weight" />
               </template>
               <template v-if="column.dataIndex === 'handle'">
                 <a-flex justify="space-between">
                   <PlusOutlined
                     class="edit-icon"
-                    @click="emit('addRow', 'destination', props.index, idx)"
+                    :class="{'disabled-icon': !isEdit}"
+                    @click="isEdit && addDestination(idx)"
                   />
                   <MinusOutlined
                     class="edit-icon"
-                    @click="emit('deleteRow', 'destination', props.index, idx)"
+                    :class="{'disabled-icon': !isEdit || editValue.functionParams.length === 1}"
+                    @click="isEdit && editValue.functionParams.length !== 1 && deleteDestination(idx)"
                   />
                 </a-flex>
               </template>
@@ -105,24 +113,48 @@
 </template>
 
 <script setup lang="ts">
-import { EditOutlined, DeleteOutlined, PlusOutlined, MinusOutlined } from '@ant-design/icons-vue'
+import { CheckOutlined, CloseOutlined, EditOutlined, DeleteOutlined, PlusOutlined, MinusOutlined } from '@ant-design/icons-vue'
 import { ref } from 'vue'
 
-const props = defineProps<{
+const isEdit = ref(false);
+
+const changeEditState = () => {
+  isEdit.value = true;
+}
+
+const emit = defineEmits(['deleteParamRoute', 'update'])
+const props = defineProps({
   paramRouteForm: {
-    type: Object
-    default: {}
-  }
+    type: Object,
+    default: () => ({})
+  },
   index: {
     type: Number
   }
-}>()
+})
 
-console.log('props', props.paramRouteForm)
+const editValue = ref(
+  {
+    method: {
+      value: undefined,
+      selectArr: []
+    },
+    functionParams: [],
+    destination: []
+  }
+)
 
-const method = ref(JSON.parse(JSON.stringify(props.paramRouteForm.method)))
-const functionParamsEdit = ref(JSON.parse(JSON.stringify(props.paramRouteForm.functionParams)))
-const destinationEdit = ref(JSON.parse(JSON.stringify(props.paramRouteForm.destination)))
+const reset = () => {
+  isEdit.value = false;
+  editValue.value = JSON.parse(JSON.stringify(props.paramRouteForm))
+}
+
+reset();
+
+const update = () => {
+  isEdit.value = false;
+  emit('update', props.index, editValue)
+}
 
 const functionParamsColumn = [
   {
@@ -183,6 +215,31 @@
     width: '10%'
   }
 ]
+
+const addFunctionParams = (idx: number) => {
+  editValue.value.functionParams.splice(idx + 1, 0, {
+    param: '',
+    relation: '',
+    value: ''
+  })
+}
+
+const deleteFunctionParams = (idx: number) => {
+  editValue.value.functionParams.splice(idx, 1)
+}
+
+const addDestination = (idx: number) => {
+  editValue.value.destination.splice(idx + 1, 0, {
+    label: '',
+    relation: '',
+    value: '',
+    weight: ''
+  })
+}
+
+const deleteDestination = (idx: number) => {
+  editValue.value.destination.splice(idx, 1)
+}
 </script>
 
 <style lang="less" scoped>
@@ -194,5 +251,8 @@
   .edit-icon {
     font-size: 18px;
   }
+  .disabled-icon {
+    color: #71777d;
+  }
 }
 </style>
diff --git a/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue b/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue
index 2fc5d3e..6f11499 100644
--- a/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue
+++ b/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue
@@ -16,7 +16,7 @@
 -->
 <template>
   <div class="__container_services_tabs_scene_config">
-    <a-tabs v-model:activeKey="activeKey" :tab-position="'left'" animated>
+    <a-tabs v-model:activeKey="activeKey" tab-position="left" animated>
       <a-tab-pane key="timeout" tab="超时时间">
         <a-descriptions layout="vertical">
           <a-descriptions-item label="超时时间">
@@ -60,15 +60,16 @@
         </a-descriptions>
       </a-tab-pane>
       <a-tab-pane key="paramRoute" tab="参数路由">
-        <paramRoute
-          v-for="(item, index) in paramRouteForms"
+        <ParamRoute
+          v-for="item, index in paramRouteForms"
+          class="param-route"
           :key="index"
           :paramRouteForm="item"
           :index="index"
-          @addRow="() => {}"
-          @deleteRow="() => {}"
+          @update="() => {}"
+          @deleteParamRoute="deleteParamRoute"
         />
-        <a-button type="primary" style="margin-top: 20px">增加路由</a-button>
+        <a-button type="primary" style="margin-top: 20px" @click="addParamRoute">增加路由</a-button>
       </a-tab-pane>
     </a-tabs>
   </div>
@@ -77,7 +78,7 @@
 <script setup lang="ts">
 import { ref, reactive } from 'vue'
 import { EditOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons-vue'
-import paramRoute from './paramRoute.vue'
+import ParamRoute from './paramRoute.vue'
 
 const editForm = reactive({
   timeout: {
@@ -91,10 +92,6 @@
   sameArea: {
     value: 'close'
   },
-  paramRoute: {
-    isEdit: false,
-    value: {}
-  }
 })
 
 const activeKey = ref('timeout')
@@ -105,8 +102,10 @@
   editForm[param].isEdit = false
 }
 
-const paramRouteForms = [
-  {
+const paramRouteForms = ref([])
+
+const addParamRoute = () => {
+  paramRouteForms.value.push({
     method: {
       value: 'getUserInfo',
       selectArr: ['getUserInfo', 'register', 'login']
@@ -126,8 +125,14 @@
         weight: ''
       }
     ]
-  }
-]
+  })
+}
+
+addParamRoute()
+
+const deleteParamRoute = (index: number) => {
+  paramRouteForms.value.splice(index, 1);
+}
 </script>
 
 <style lang="less" scoped>
@@ -142,5 +147,8 @@
     margin-left: 15px;
     font-size: 18px;
   }
+  .param-route {
+    margin-bottom: 20px;
+  }
 }
 </style>