Feat: visualize Events on the alarm page (#502)

diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index 4f6c801..fac7849 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -228,6 +228,7 @@
   scope: 'Scope',
   destService: 'Destination Service',
   destServiceInstance: 'Destination Service Instance',
+  eventSource: 'Event Source',
 };
 
 export default m;
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index 5ed9f6d..75b8d94 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -226,6 +226,7 @@
   scope: '范围',
   destService: '终点服务',
   destServiceInstance: '终点实例',
+  eventSource: '事件资源',
 };
 
 export default m;
diff --git a/src/graph/fragments/alarm.ts b/src/graph/fragments/alarm.ts
index 1643676..cc9e5c1 100644
--- a/src/graph/fragments/alarm.ts
+++ b/src/graph/fragments/alarm.ts
@@ -28,6 +28,21 @@
           key
           value
         }
+        events {
+          uuid
+          source {
+            service serviceInstance endpoint
+          }
+          name
+          type
+          message
+          parameters {
+            key
+            value
+          }
+          startTime
+          endTime
+        }
       }
       total
     }`,
diff --git a/src/types/alarm.d.ts b/src/types/alarm.d.ts
index 93b8cdc..6358b5d 100644
--- a/src/types/alarm.d.ts
+++ b/src/types/alarm.d.ts
@@ -26,4 +26,21 @@
   startTime: string;
   scope: string;
   tags: Array<{ key: string; value: string }>;
+  events: any[];
+}
+
+export interface Event {
+  endTime: number;
+  startTime: number;
+  type: string;
+  name: string;
+  message: string;
+  uuid: string;
+  source: Source;
+}
+
+export interface Source {
+  service?: string;
+  endpoint?: string;
+  serviceInstance?: string;
 }
diff --git a/src/views/components/alarm/alarm-table.vue b/src/views/components/alarm/alarm-table.vue
index 4cf62c1..d52ecb9 100644
--- a/src/views/components/alarm/alarm-table.vue
+++ b/src/views/components/alarm/alarm-table.vue
@@ -37,15 +37,59 @@
         </div>
       </div>
     </div>
-    <rk-sidebox :width="'50%'" :show.sync="isShowDetails" :title="$t('alarmDetail')">
+    <rk-sidebox :width="'90%'" :show.sync="isShowDetails" :title="$t('alarmDetail')">
       <div class="mb-10 clear rk-flex" v-for="(item, index) in AlarmDetailCol" :key="index">
         <template>
-          <span class="g-sm-4 grey">{{ $t(item.value) }}:</span>
-          <span v-if="item.label === 'startTime'" class="g-sm-8">{{ currentDetail[item.label] | dateformat }}</span>
-          <span v-else-if="item.label === 'tags'" class="g-sm-8">
+          <span class="g-sm-2 grey">{{ $t(item.value) }}:</span>
+          <span v-if="item.label === 'startTime'">{{ currentDetail[item.label] | dateformat }}</span>
+          <span v-else-if="item.label === 'tags'">
             <div v-for="(d, index) in alarmTags" :key="index">{{ d }}</div>
           </span>
-          <span v-else class="g-sm-8">{{ currentDetail[item.label] }}</span>
+          <span v-else-if="item.label === 'events'" class="event-detail">
+            <div>
+              <ul>
+                <li>
+                  <span v-for="(i, index) of eventsHeaders" :class="i.class" :key="i.class + index">
+                    {{ $t(i.text) }}
+                  </span>
+                </li>
+                <li v-for="event in currentEvents" :key="event.uuid" @click="viewEventDetail(event)">
+                  <span v-for="(d, index) of eventsHeaders" :class="d.class" :key="event.uuid + index">
+                    <span v-if="d.class === 'startTime' || d.class === 'endTime'">
+                      {{ event[d.class] | dateformat }}
+                    </span>
+                    <span v-else>
+                      {{ event[d.class] }}
+                    </span>
+                  </span>
+                </li>
+              </ul>
+              <rk-sidebox :width="'90%'" :show.sync="showEventDetails" :title="$t('eventDetail')">
+                <div class="event-detail">
+                  <div class="mb-10 rk-flex" v-for="(eventKey, index) in eventsDetailKeys" :key="index">
+                    <span class="keys">{{ $t(eventKey.text) }}</span>
+                    <span v-if="eventKey.class === 'parameters'">
+                      <span v-for="(d, index) of currentEvent[d.class]" :key="index">{{ d.key }}={{ d.value }}; </span>
+                    </span>
+                    <span v-else-if="eventKey.class === 'startTime' || eventKey.class === 'endTime'">{{
+                      currentEvent[eventKey.class] | dateformat
+                    }}</span>
+                    <span v-else-if="eventKey.class === 'source'" class="source">
+                      <div>{{ $t('currentService') }}: {{ currentEvent[eventKey.class].service }}</div>
+                      <div v-show="currentEvent[eventKey.class].endpoint">
+                        {{ $t('currentEndpoint') }}: {{ currentEvent[eventKey.class].endpoint }}
+                      </div>
+                      <div v-show="currentEvent[eventKey.class].serviceInstance">
+                        {{ $t('currentInstance') }}: {{ currentEvent[eventKey.class].serviceInstance }}
+                      </div>
+                    </span>
+                    <span v-else>{{ currentEvent[eventKey.class] }}</span>
+                  </div>
+                </div>
+              </rk-sidebox>
+            </div>
+          </span>
+          <span v-else>{{ currentDetail[item.label] }}</span>
         </template>
       </div>
     </rk-sidebox>
@@ -55,7 +99,8 @@
 <script lang="ts">
   import Vue from 'vue';
   import { Component, Prop } from 'vue-property-decorator';
-  import { Alarm } from '@/types/alarm';
+  import { Alarm, Event } from '@/types/alarm';
+  import { EventsDetailHeaders, AlarmDetailCol, AlarmEventsDetailKeys } from './constant';
 
   @Component
   export default class AlarmTable extends Vue {
@@ -67,34 +112,37 @@
       message: '',
       key: '',
       startTime: '',
+      events: [],
     };
+    private showEventDetails: boolean = false;
     private alarmTags: string[] = [];
-    private AlarmDetailCol = [
-      {
-        label: 'scope',
-        value: 'scope',
-      },
-      {
-        label: 'startTime',
-        value: 'startTime',
-      },
-      {
-        label: 'tags',
-        value: 'tags',
-      },
-      {
-        label: 'message',
-        value: 'message',
-      },
-    ];
+    private AlarmDetailCol = AlarmDetailCol;
+    private eventsHeaders = EventsDetailHeaders;
+    private eventsDetailKeys = AlarmEventsDetailKeys;
+    private currentEvents: Event[] = [];
+    private currentEvent: Event = {
+      startTime: 0,
+      endTime: 0,
+      message: '',
+      name: '',
+      type: '',
+      uuid: '',
+      source: {},
+    };
 
     private showDetails(item: Alarm) {
       this.currentDetail = item;
+      this.currentEvents = item.events;
       this.alarmTags = this.currentDetail.tags.map((d: { key: string; value: string }) => {
         return `${d.key} = ${d.value}`;
       });
       this.isShowDetails = true;
     }
+
+    private viewEventDetail(event: Event) {
+      this.showEventDetails = true;
+      this.currentEvent = event;
+    }
   }
 </script>
 
@@ -147,4 +195,32 @@
   .alarm-item {
     cursor: pointer;
   }
+  ul {
+    min-height: 100px;
+    overflow: auto;
+    margin-bottom: 20px;
+  }
+  li {
+    cursor: pointer;
+    > span {
+      width: 160px;
+      height: 20px;
+      line-height: 20px;
+      text-align: center;
+      display: inline-block;
+      border-bottom: 1px solid #ccc;
+      overflow: hidden;
+    }
+    .uuid {
+      width: 280px;
+    }
+  }
+  .keys {
+    font-weight: bold;
+    display: inline-block;
+    width: 120px;
+  }
+  .source > div {
+    padding-left: 120px;
+  }
 </style>
diff --git a/src/views/components/alarm/constant.ts b/src/views/components/alarm/constant.ts
new file mode 100644
index 0000000..40c9540
--- /dev/null
+++ b/src/views/components/alarm/constant.ts
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const EventsDetailHeaders = [
+  { text: 'eventID', class: 'uuid' },
+  { text: 'eventName', class: 'name' },
+  { text: 'eventsType', class: 'type' },
+  { text: 'startTime', class: 'startTime' },
+  { text: 'endTime', class: 'endTime' },
+];
+
+export const AlarmEventsDetailKeys = [
+  { text: 'eventID', class: 'uuid' },
+  { text: 'eventName', class: 'name' },
+  { text: 'eventsType', class: 'type' },
+  { text: 'startTime', class: 'startTime' },
+  { text: 'endTime', class: 'endTime' },
+  { text: 'eventsMessage', class: 'message' },
+  { text: 'eventSource', class: 'source' },
+];
+
+export const AlarmDetailCol = [
+  {
+    label: 'scope',
+    value: 'scope',
+  },
+  {
+    label: 'startTime',
+    value: 'startTime',
+  },
+  {
+    label: 'tags',
+    value: 'tags',
+  },
+  {
+    label: 'message',
+    value: 'message',
+  },
+  {
+    label: 'events',
+    value: 'eventDetail',
+  },
+];