You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2021/04/06 00:22:00 UTC

[skywalking-rocketbot-ui] branch master updated: feat: extend table chart (#459)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-rocketbot-ui.git


The following commit(s) were added to refs/heads/master by this push:
     new fa32fdb  feat: extend table chart (#459)
fa32fdb is described below

commit fa32fdb0d925f3bf5568f23e5ded47e86cd2ba13
Author: Qiuxia Fan <fi...@outlook.com>
AuthorDate: Tue Apr 6 08:21:50 2021 +0800

    feat: extend table chart (#459)
---
 src/assets/lang/en.ts                              |   4 +
 src/assets/lang/zh.ts                              |   4 +
 .../components/dashboard/charts/chart-edit.vue     | 250 ++++++++++++---------
 .../components/dashboard/charts/chart-table.vue    |  94 ++++++--
 src/views/components/dashboard/dashboard-comp.vue  |   4 +-
 src/views/components/dashboard/dashboard-item.vue  |  18 +-
 6 files changed, 242 insertions(+), 132 deletions(-)

diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index cafc189..645d8c1 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -201,6 +201,10 @@ const m = {
   keywordsOfContentLogTips: 'Current storage of SkyWalking OAP server does not support this.',
   instanceAttributes: 'Instance Attributes',
   value: 'Value',
+  tableHeader: 'Header Names',
+  tableValues: 'Table Values',
+  show: 'Show',
+  hide: 'Hide',
 };
 
 export default m;
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index bc9c321..f222588 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -199,6 +199,10 @@ const m = {
   keywordsOfContentLogTips: 'SkyWalking OAP服务器的当前存储不支持此操作',
   instanceAttributes: '查看实例属性',
   value: '数值',
+  tableHeader: '表头名称',
+  tableValues: '表值',
+  show: '展示',
+  hide: '隐藏',
 };
 
 export default m;
diff --git a/src/views/components/dashboard/charts/chart-edit.vue b/src/views/components/dashboard/charts/chart-edit.vue
index e161df1..d7bf985 100755
--- a/src/views/components/dashboard/charts/chart-edit.vue
+++ b/src/views/components/dashboard/charts/chart-edit.vue
@@ -205,7 +205,6 @@ limitations under the License. -->
         >
           <option :value="'DES'">{{ $t('descendOrder') }}</option>
           <option :value="'ASC'">{{ $t('increaseOrder') }}</option>
-          <!--                    <option :value="''" v-if="isBrowser">{{ $t('defaultOrder') }}</option>-->
         </select>
       </div>
       <div class="flex-h mb-5">
@@ -218,6 +217,27 @@ limitations under the License. -->
         />
       </div>
       <div class="flex-h mb-5">
+        <div class="title grey sm">{{ $t('width') }}:</div>
+        <input
+          type="number"
+          min="1"
+          max="12"
+          class="rk-chart-edit-input long"
+          :value="itemConfig.width"
+          @change="setItemConfig({ type: 'width', value: $event.target.value })"
+        />
+      </div>
+      <div class="flex-h mb-5">
+        <div class="title grey sm">{{ $t('height') }}:</div>
+        <input
+          type="number"
+          min="1"
+          class="rk-chart-edit-input long"
+          :value="itemConfig.height"
+          @change="setItemConfig({ type: 'height', value: $event.target.value })"
+        />
+      </div>
+      <div class="flex-h mb-5">
         <div class="title grey sm">{{ $t('aggregation') }}:</div>
         <select
           class="long"
@@ -233,27 +253,34 @@ limitations under the License. -->
           @change="setItemConfig({ type: 'aggregationNum', value: $event.target.value })"
         />
       </div>
-      <div class="flex-h mb-5">
-        <div class="title grey sm">{{ $t('width') }}:</div>
+      <div class="flex-h mb-5" v-show="itemConfig.chartType === ChartTypeOptions[3].value">
+        <div class="title grey sm">{{ $t('tableHeader') }}:</div>
         <input
-          type="number"
-          min="1"
-          max="12"
+          type="text"
           class="rk-chart-edit-input long"
-          :value="itemConfig.width"
-          @change="setItemConfig({ type: 'width', value: $event.target.value })"
+          placeholder="col-1"
+          :value="itemConfig.tableHeaderCol1"
+          @change="setItemConfig({ type: 'tableHeaderCol1', value: $event.target.value })"
         />
-      </div>
-      <div class="flex-h">
-        <div class="title grey sm">{{ $t('height') }}:</div>
         <input
-          type="number"
-          min="1"
+          type="text"
           class="rk-chart-edit-input long"
-          :value="itemConfig.height"
-          @change="setItemConfig({ type: 'height', value: $event.target.value })"
+          placeholder="col-2"
+          :value="itemConfig.tableHeaderCol2"
+          @change="setItemConfig({ type: 'tableHeaderCol2', value: $event.target.value })"
         />
       </div>
+      <div class="flex-h mb-5" v-show="itemConfig.chartType === ChartTypeOptions[3].value">
+        <div class="title grey sm">{{ $t('tableValues') }}:</div>
+        <select
+          class="long"
+          v-model="itemConfig.showTableValues"
+          @change="setItemConfig({ type: 'showTableValues', value: $event.target.value })"
+        >
+          <option :value="true">{{ $t('show') }}</option>
+          <option :value="false">{{ $t('hide') }}</option>
+        </select>
+      </div>
     </div>
   </div>
 </template>
@@ -262,8 +289,7 @@ limitations under the License. -->
   import Vue from 'vue';
   import { State, Getter, Mutation, Action } from 'vuex-class';
   import { Component, Prop, Watch } from 'vue-property-decorator';
-
-  import { TopologyType, ObjectsType } from '../../../../constants/constant';
+  import { TopologyType } from '@/constants/constant';
   import {
     EntityType,
     BrowserEntityType,
@@ -306,12 +332,20 @@ limitations under the License. -->
     private isLabel = false;
     private isIndependentSelector = false;
     private nameMetrics = ['sortMetrics', 'readSampledRecords'];
-    private pageTypes = [TopologyType.TOPOLOGY_ENDPOINT, TopologyType.TOPOLOGY_INSTANCE] as any[];
+    private pageTypes = [TopologyType.TOPOLOGY_ENDPOINT, TopologyType.TOPOLOGY_INSTANCE] as string[];
     private isChartType = false;
     private isReadSingleValue = false;
 
     private created() {
       this.itemConfig = this.item;
+      this.initConfig();
+      if (!this.itemConfig.independentSelector || this.pageTypes.includes(this.type)) {
+        return;
+      }
+      this.setItemServices();
+    }
+
+    private initConfig() {
       this.isDatabase = this.pageTypes.includes(this.type)
         ? false
         : this.rocketComps.tree[this.rocketComps.group].type === DASHBOARDTYPE.DATABASE
@@ -328,10 +362,6 @@ limitations under the License. -->
       this.isIndependentSelector =
         this.rocketComps.tree[this.rocketComps.group].type === 'metric' || this.pageTypes.includes(this.type);
       this.isChartType = ['readMetricsValues', 'readLabeledMetricsValues'].includes(this.itemConfig.queryMetricType);
-      if (!this.itemConfig.independentSelector || this.pageTypes.includes(this.type)) {
-        return;
-      }
-      this.setItemServices();
     }
 
     private setItemConfig(params: { type: string; value: string }) {
@@ -353,129 +383,137 @@ limitations under the License. -->
         }
       }
       if (params.type === 'metricName') {
-        this.TYPE_METRICS({ name: params.value }).then((data: Array<{ typeOfMetrics: string }>) => {
-          if (!data.length) {
-            return;
-          }
-          if (data.length > 1) {
-            const length = data.filter((d: { typeOfMetrics: string }) => d.typeOfMetrics !== MetricsType.REGULAR_VALUE)
-              .length;
-            if (length) {
-              this.$emit('updateStatus', 'metricType', MetricsType.UNKNOWN);
-              return;
-            }
-          }
-          const { typeOfMetrics } = data[0];
-          this.$emit('updateStatus', 'metricType', typeOfMetrics);
-          this.queryMetricTypesList = QueryMetricTypes[typeOfMetrics] || [];
-          this.itemConfig.queryMetricType = this.queryMetricTypesList[0] && this.queryMetricTypesList[0].value;
-          this.isChartType = ['readMetricsValues', 'readLabeledMetricsValues'].includes(
-            this.itemConfig.queryMetricType,
-          );
-          this.isLabel = typeOfMetrics === MetricsType.LABELED_VALUE ? true : false;
-          const values = {
-            metricType: typeOfMetrics,
-            queryMetricType: this.itemConfig.queryMetricType,
-            chartType: MetricChartType[this.itemConfig.queryMetricType],
-            metricName: params.value,
-          };
-          if (this.type === this.pageTypes[0]) {
-            this.EDIT_TOPO_ENDPOINT_CONFIG({
-              index: this.index,
-              values,
-            });
-          } else if (this.type === this.pageTypes[1]) {
-            this.EDIT_TOPO_INSTANCE_CONFIG({
-              index: this.index,
-              values,
-            });
-          } else {
-            this.EDIT_COMP_CONFIG({
-              index: this.index,
-              values,
-            });
-          }
-          this.itemConfig = {
-            ...this.itemConfig,
-            ...values,
-          };
-        });
+        this.updateMetricName(params);
         return;
       }
       if (params.type === 'queryMetricType') {
-        const values = {
-          chartType: MetricChartType[params.value],
-          [params.type]: params.value,
-        };
+        this.updateQueryMetricType(params);
+        return;
+      }
+      if (params.type === 'independentSelector' || params.type === 'parentService') {
+        this.itemConfig[params.type] = params.value === 'true' ? true : false;
         if (this.type === this.pageTypes[0]) {
           this.EDIT_TOPO_ENDPOINT_CONFIG({
             index: this.index,
-            values,
+            values: { [params.type]: this.itemConfig[params.type] },
           });
         } else if (this.type === this.pageTypes[1]) {
           this.EDIT_TOPO_INSTANCE_CONFIG({
             index: this.index,
-            values,
+            values: { [params.type]: this.itemConfig[params.type] },
           });
         } else {
-          this.EDIT_COMP_CONFIG({
-            index: this.index,
-            values,
-          });
+          this.EDIT_COMP_CONFIG({ index: this.index, values: { [params.type]: this.itemConfig[params.type] } });
         }
-        this.itemConfig = {
-          ...this.itemConfig,
-          ...values,
-        };
-        this.isChartType = ['readMetricsValues', 'readLabeledMetricsValues'].includes(this.itemConfig.queryMetricType);
+      }
+      if (params.type === 'aggregation' && ['milliseconds', 'seconds'].includes(this.itemConfig.aggregation)) {
+        this.updateAggregation(params);
         return;
       }
-      if (params.type === 'independentSelector' || params.type === 'parentService') {
-        this.itemConfig[params.type] = params.value === 'true' ? true : false;
+      if (this.type === this.pageTypes[0]) {
+        this.EDIT_TOPO_ENDPOINT_CONFIG({
+          index: this.index,
+          values: { [params.type]: params.value },
+        });
+      } else if (this.type === this.pageTypes[1]) {
+        this.EDIT_TOPO_INSTANCE_CONFIG({
+          index: this.index,
+          values: { [params.type]: params.value },
+        });
+      } else {
+        this.EDIT_COMP_CONFIG({ index: this.index, values: { [params.type]: params.value } });
+      }
+    }
+
+    private updateMetricName(params: { type: string; value: string }) {
+      this.TYPE_METRICS({ name: params.value }).then((data: Array<{ typeOfMetrics: string }>) => {
+        if (!data.length) {
+          return;
+        }
+        if (data.length > 1) {
+          const length = data.filter((d: { typeOfMetrics: string }) => d.typeOfMetrics !== MetricsType.REGULAR_VALUE)
+            .length;
+          if (length) {
+            this.$emit('updateStatus', 'metricType', MetricsType.UNKNOWN);
+            return;
+          }
+        }
+        const { typeOfMetrics } = data[0];
+        this.$emit('updateStatus', 'metricType', typeOfMetrics);
+        this.queryMetricTypesList = QueryMetricTypes[typeOfMetrics] || [];
+        this.itemConfig.queryMetricType = this.queryMetricTypesList[0] && this.queryMetricTypesList[0].value;
+        this.isChartType = ['readMetricsValues', 'readLabeledMetricsValues'].includes(this.itemConfig.queryMetricType);
+        this.isLabel = typeOfMetrics === MetricsType.LABELED_VALUE ? true : false;
+        const values = {
+          metricType: typeOfMetrics,
+          queryMetricType: this.itemConfig.queryMetricType,
+          chartType: MetricChartType[this.itemConfig.queryMetricType],
+          metricName: params.value,
+        };
         if (this.type === this.pageTypes[0]) {
           this.EDIT_TOPO_ENDPOINT_CONFIG({
             index: this.index,
-            values: { [params.type]: this.itemConfig[params.type] },
+            values,
           });
         } else if (this.type === this.pageTypes[1]) {
           this.EDIT_TOPO_INSTANCE_CONFIG({
             index: this.index,
-            values: { [params.type]: this.itemConfig[params.type] },
+            values,
           });
         } else {
-          this.EDIT_COMP_CONFIG({ index: this.index, values: { [params.type]: this.itemConfig[params.type] } });
+          this.EDIT_COMP_CONFIG({
+            index: this.index,
+            values,
+          });
         }
-
-        return;
-      }
-      if (params.type === 'aggregation' && ['milliseconds', 'seconds'].includes(this.itemConfig.aggregation)) {
-        const values = {
-          aggregationNum: 'YYYY-MM-DD HH:mm:ss',
-          [params.type]: params.value,
-        };
         this.itemConfig = {
           ...this.itemConfig,
           ...values,
         };
-        this.EDIT_COMP_CONFIG({
-          index: this.index,
-          values,
-        });
-        return;
-      }
+      });
+    }
+
+    private updateAggregation(params: { type: string; value: string }) {
+      const values = {
+        aggregationNum: 'YYYY-MM-DD HH:mm:ss',
+        [params.type]: params.value,
+      };
+      this.itemConfig = {
+        ...this.itemConfig,
+        ...values,
+      };
+      this.EDIT_COMP_CONFIG({
+        index: this.index,
+        values,
+      });
+    }
+
+    private updateQueryMetricType(params: { type: string; value: string }) {
+      const values = {
+        chartType: MetricChartType[params.value],
+        [params.type]: params.value,
+      };
       if (this.type === this.pageTypes[0]) {
         this.EDIT_TOPO_ENDPOINT_CONFIG({
           index: this.index,
-          values: { [params.type]: params.value },
+          values,
         });
       } else if (this.type === this.pageTypes[1]) {
         this.EDIT_TOPO_INSTANCE_CONFIG({
           index: this.index,
-          values: { [params.type]: params.value },
+          values,
         });
       } else {
-        this.EDIT_COMP_CONFIG({ index: this.index, values: { [params.type]: params.value } });
+        this.EDIT_COMP_CONFIG({
+          index: this.index,
+          values,
+        });
       }
+      this.itemConfig = {
+        ...this.itemConfig,
+        ...values,
+      };
+      this.isChartType = ['readMetricsValues', 'readLabeledMetricsValues'].includes(this.itemConfig.queryMetricType);
     }
 
     private setItemServices(update: boolean = false) {
diff --git a/src/views/components/dashboard/charts/chart-table.vue b/src/views/components/dashboard/charts/chart-table.vue
index f7bc4de..856880b 100644
--- a/src/views/components/dashboard/charts/chart-table.vue
+++ b/src/views/components/dashboard/charts/chart-table.vue
@@ -15,16 +15,21 @@ limitations under the License. -->
 
 <template>
   <div class="rk-chart-table">
-    <table>
-      <tr>
-        <th>{{ $t('name') }}</th>
-        <th>{{ $t('value') }}</th>
-      </tr>
-      <tr v-for="key in dataKeys" :key="key">
-        <td>{{ key }}</td>
-        <td>{{ data[key][dataLength(data[key])] }}</td>
-      </tr>
-    </table>
+    <div ref="chartTable">
+      <div class="row flex-h" :style="`width: ${nameWidth + initWidth}px`">
+        <div class="name" :style="`width: ${nameWidth}px`">
+          {{ item.tableHeaderCol1 || $t('name') }}
+          <i class="r cp" ref="draggerName"><rk-icon icon="settings_ethernet"/></i>
+        </div>
+        <div class="value-col" v-if="showTableValues">
+          {{ item.tableHeaderCol2 || $t('value') }}
+        </div>
+      </div>
+      <div class="row flex-h" v-for="key in dataKeys" :key="key" :style="`width: ${nameWidth + initWidth}px`">
+        <div :style="`width: ${nameWidth}px`">{{ key }}</div>
+        <div class="value-col" v-if="showTableValues">{{ data[key][dataLength(data[key])] }}</div>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -35,34 +40,77 @@ limitations under the License. -->
   @Component
   export default class ChartTable extends Vue {
     @Prop() private data!: any;
+    @Prop() private item: any;
+
+    private nameWidth: number = 0;
+    private initWidth: number = 0;
 
     private get dataKeys() {
       const keys = Object.keys(this.data || {}).filter((i: any) => Array.isArray(this.data[i]) && this.data[i].length);
       return keys;
     }
 
-    private dataLength(param: any[]) {
+    private get showTableValues() {
+      return this.item.showTableValues === 'true' || this.item.showTableValues === true ? true : false;
+    }
+
+    private mounted() {
+      const chartTable: any = this.$refs.chartTable;
+      const width = this.showTableValues ? chartTable.offsetWidth / 2 : chartTable.offsetWidth;
+      this.initWidth = this.showTableValues ? chartTable.offsetWidth / 2 : 0;
+      this.nameWidth = width - 5;
+      const drag: any = this.$refs.draggerName;
+      drag.onmousedown = (event: MouseEvent) => {
+        const diffX = event.clientX;
+        const copy = this.nameWidth;
+        document.onmousemove = (documentEvent) => {
+          const moveX = documentEvent.clientX - diffX;
+          this.nameWidth = copy + moveX;
+        };
+        document.onmouseup = () => {
+          document.onmousemove = null;
+          document.onmouseup = null;
+        };
+      };
+    }
+
+    private dataLength(param: number[]) {
       return param.length - 1 || 0;
     }
   }
 </script>
 <style lang="scss" scoped>
   .rk-chart-table {
-    table {
-      width: 100%;
-      border-top: 1px solid #ccc;
-      border-right: 1px solid #ccc;
-    }
-    tr {
-      width: 100%;
-      border: 1px solid #ccc;
+    height: 100%;
+    width: 100%;
+    overflow: auto;
+    .name {
+      padding-left: 15px;
     }
-    th,
-    td {
+    .row {
       border-left: 1px solid #ccc;
-      border-bottom: 1px solid #ccc;
+      height: 20px;
+      div {
+        border-right: 1px solid #ccc;
+        text-align: center;
+        height: 20px;
+        line-height: 20px;
+        display: inline-block;
+      }
+      div:last-child {
+        border-bottom: 1px solid #ccc;
+      }
+      div:nth-last-child(2) {
+        border-bottom: 1px solid #ccc;
+      }
+    }
+    .row:first-child {
+      div {
+        border-top: 1px solid #ccc;
+      }
+    }
+    .value-col {
       width: 50%;
-      text-align: center;
     }
   }
 </style>
diff --git a/src/views/components/dashboard/dashboard-comp.vue b/src/views/components/dashboard/dashboard-comp.vue
index ec697cf..d03d57d 100644
--- a/src/views/components/dashboard/dashboard-comp.vue
+++ b/src/views/components/dashboard/dashboard-comp.vue
@@ -51,8 +51,8 @@ limitations under the License. -->
 </template>
 
 <script lang="ts">
-  import { Vue, Component, Watch, Prop } from 'vue-property-decorator';
-  import { State, Mutation } from 'vuex-class';
+  import { Vue, Component, Prop } from 'vue-property-decorator';
+  import { Mutation } from 'vuex-class';
   import copy from '@/utils/copy';
 
   @Component
diff --git a/src/views/components/dashboard/dashboard-item.vue b/src/views/components/dashboard/dashboard-item.vue
index b9730ef..8df84fc 100644
--- a/src/views/components/dashboard/dashboard-item.vue
+++ b/src/views/components/dashboard/dashboard-item.vue
@@ -26,9 +26,12 @@ limitations under the License. -->
           <use xlink:href="#lock"></use>
         </svg>
       </span>
+      <span v-show="!rocketGlobal.edit && itemConfig.chartType === 'ChartTable'" @click="copyTable">
+        <rk-icon class="r cp" icon="review-list" />
+      </span>
     </div>
     <div class="rk-dashboard-item-body">
-      <div style="height:100%;">
+      <div style="height:100%; width:100%">
         <component
           :is="rocketGlobal.edit ? 'ChartEdit' : itemConfig.chartType"
           ref="chart"
@@ -72,6 +75,7 @@ limitations under the License. -->
   import { CalculationType } from './charts/constant';
   import { State as globalState } from '@/store/modules/global';
   import { State as optionState } from '@/store/modules/global/selectors';
+  import copy from '@/utils/copy';
 
   @Component({
     components: { ...charts },
@@ -313,6 +317,18 @@ limitations under the License. -->
       }
     }
 
+    private copyTable() {
+      const data: any = {};
+      const keys = Object.keys(this.chartSource || {}).filter(
+        (i: any) => Array.isArray(this.chartSource[i]) && this.chartSource[i].length,
+      );
+      for (const key of keys) {
+        const index = this.chartSource[key].length - 1 || 0;
+        data[key] = this.chartSource[key][index];
+      }
+      copy(JSON.stringify(data));
+    }
+
     private deleteItem(index: number) {
       if (this.type === this.pageTypes[0]) {
         this.DELETE_TOPO_ENDPOINT(index);