Merge pull request #16 from TinyAllen/develop

Merge from Develop branch
diff --git a/README.md b/README.md
index e8d46b3..6b81b51 100644
--- a/README.md
+++ b/README.md
@@ -83,10 +83,10 @@
 ### Running the docker image
 
 ```
-docker run -p 8080:80 -d -e skywalking_collector=127.0.0.1:1234,127.0.0.1:1235 rocketbot
+docker run -p 8080:80 -d -e SKYWALKING_URL=127.0.0.1:1234,127.0.0.1:1235 rocketbot
 ```
 
-`skywalking_collector` is the address of your backend, multiple IP is changed by comma.
+`SKYWALKING_URL` is the address of your backend, multiple IP is changed by comma.
 
 
 The default frontend address is `http://localhost:8080`.
@@ -141,3 +141,10 @@
 
 Copyright © 2018, [Allen Wang](https://github.com/TinyAllen). Released under the [MIT](http://opensource.org/licenses/MIT) License.
 
+## Who Uses Rocketbot?
+按照登记顺序排序,更多接入公司,欢迎在 [https://github.com/TinyAllen/rocketbot/issues/15](https://github.com/TinyAllen/rocketbot/issues/15) 登记(仅供开源用户参考)
+<p>
+<img src="https://daoweb-resource.daocloud.io/logo/daocloud-logo-gray-account.svg" height="40px">
+<img src="http://springcloud.cn/default/img/logo.png" height="40px">
+<img src="https://user-images.githubusercontent.com/19775780/47834441-7c21d080-ddda-11e8-9e3a-67c43ab074bf.png" height="40px"  >
+</p>
\ No newline at end of file
diff --git a/src/assets/lib.scss b/src/assets/lib.scss
index d08d5bd..9e8219e 100644
--- a/src/assets/lib.scss
+++ b/src/assets/lib.scss
@@ -30,8 +30,8 @@
 .mb10{margin-bottom:10px;}
 .mb15{margin-bottom:15px;}
 .mb20{margin-bottom:20px;}
-.half{box-sizing: border-box;display:inline-block;width: 50%;padding-right: 20px;vertical-align: top;}
-
+.half{word-break: break-all;word-wrap: break-word;box-sizing: border-box;display:inline-block;width: 50%;padding-right: 20px;vertical-align: top;}
+.trans{transition:all .3s;}
 .two-fifth{
   box-sizing: border-box;
   display:inline-block;
@@ -50,6 +50,24 @@
   word-break: break-all;
   word-wrap: break-word;
 }
+.one-fifth{
+  box-sizing: border-box;
+  display:inline-block;
+  width: 20%;
+  padding-right: 20px;
+  vertical-align: top;
+  word-break: break-all;
+  word-wrap: break-word;
+}
+.four-fifth{
+  box-sizing: border-box;
+  display:inline-block;
+  width: 80%;
+  padding-right: 20px;
+  vertical-align: top;
+  word-break: break-all;
+  word-wrap: break-word;
+}
 .link-hover{
   transition: color .3s;
   &:hover{
diff --git a/src/assets/style.scss b/src/assets/style.scss
index 7ffce30..8850f0c 100644
--- a/src/assets/style.scss
+++ b/src/assets/style.scss
@@ -37,4 +37,6 @@
     padding-right: 0;
   }
 }
-
+.ivu-tooltip-popper{
+  word-break: break-all;
+}
diff --git a/src/store/modules/topo.ts b/src/store/modules/topo.ts
index dc09384..80a4072 100644
--- a/src/store/modules/topo.ts
+++ b/src/store/modules/topo.ts
@@ -1,6 +1,7 @@
 import { Commit, ActionTree } from 'vuex';
 import { getTopo, getTopoApp, getClusterBrief } from '@/api/topo';
 import * as types from '../mutation-types';
+import Axios from 'axios';
 interface Option {
   key: String;
   label: String;
@@ -13,7 +14,7 @@
   source: String;
   target: String;
 }
-interface Node{
+interface Node {
   apdex: Number;
   avgResponseTime: Number;
   cpm: Number;
@@ -26,7 +27,7 @@
   sla: Number;
   type: String;
 }
-interface Cluster{
+interface Cluster {
   numOfApplication: Number;
   numOfService: Number;
   numOfDatabase: Number;
@@ -37,14 +38,14 @@
 export interface State {
   calls: Call[];
   nodes: Node[];
-  cluster : Cluster;
-  current : Option;
+  cluster: Cluster;
+  current: Option;
 }
 
 const initState: State = {
   calls: [],
   nodes: [],
-  current : {
+  current: {
     key: 'default',
     label: 'default',
   },
@@ -84,25 +85,35 @@
 
 // actions
 const actions: ActionTree<State, any> = {
-  SET_TOPO_CURRENT(context: { commit: Commit; state: State, rootState: any }, data: Option) {
+  SET_TOPO_CURRENT(
+    context: { commit: Commit; state: State; rootState: any },
+    data: Option,
+  ) {
     context.commit(types.SET_TOPO_CURRENT, data);
   },
-  GET_TOPO(context: { commit: Commit; state: State, rootState: any }) {
+  GET_TOPO(context: { commit: Commit; state: State; rootState: any }) {
     return getTopo(context.rootState.global.duration).then((res) => {
       context.commit(types.SET_TOPO, res.data.data.getClusterTopology);
     });
+    // context.commit(types.SET_TOPO, data.data.getClusterTopology);
   },
-  GET_TOPO_APPLICATION(context: { commit: Commit; state: State, rootState: any }, applicationId:String) {
-    return getTopoApp({ duration: context.rootState.global.duration, applicationId }).then((res) => {
+  GET_TOPO_APPLICATION(
+    context: { commit: Commit; state: State; rootState: any },
+    applicationId: String,
+  ) {
+    return getTopoApp({
+      duration: context.rootState.global.duration,
+      applicationId,
+    }).then((res) => {
       context.commit(types.SET_TOPO, res.data.data.getApplicationTopology);
     });
   },
-  GET_CLUSTER(context: { commit: Commit; state: State, rootState: any }) {
+  GET_CLUSTER(context: { commit: Commit; state: State; rootState: any }) {
     return getClusterBrief(context.rootState.global.duration).then((res) => {
       context.commit(types.SET_CLUSTER, res.data.data.getClusterBrief);
     });
   },
-  CLEAR_TOPO(context: { commit: Commit; state: State, rootState: any }) {
+  CLEAR_TOPO(context: { commit: Commit; state: State; rootState: any }) {
     context.commit(types.CLEAR_TOPO);
   },
 };
diff --git a/src/store/modules/trace.ts b/src/store/modules/trace.ts
index 23309af..12a1006 100644
--- a/src/store/modules/trace.ts
+++ b/src/store/modules/trace.ts
@@ -32,18 +32,17 @@
 };
 
 // getters
-const getters = {
-};
+const getters = {};
 
 // mutations
 const mutations = {
   [types.SET_TRACE](state: State, data: Trace[]) {
     state.traces = data;
   },
-  [types.SET_TRACE_TOTAL](state: State, total:Number) {
+  [types.SET_TRACE_TOTAL](state: State, total: Number) {
     state.tracesTotal = total;
   },
-  [types.SET_SPAN](state: State, data:Span[]) {
+  [types.SET_SPAN](state: State, data: Span[]) {
     state.spans = data;
   },
   [types.CLEAR_TRACE](state: State) {
@@ -54,19 +53,29 @@
 
 // actions
 const actions: ActionTree<State, any> = {
-  GET_TRACES(context: { commit: Commit; state: State, rootState: any }, params:String) {
+  GET_TRACES(
+    context: { commit: Commit; state: State; rootState: any },
+    params: String,
+  ) {
     return getTraces(params).then((res) => {
       context.commit(types.SET_TRACE, res.data.data.queryBasicTraces.traces);
-      context.commit(types.SET_TRACE_TOTAL, res.data.data.queryBasicTraces.total);
+      context.commit(
+        types.SET_TRACE_TOTAL,
+        res.data.data.queryBasicTraces.total,
+      );
     });
   },
-  GET_SPANS(context: { commit: Commit; state: State, rootState: any }, params:String) {
+  GET_SPANS(
+    context: { commit: Commit; state: State; rootState: any },
+    params: String,
+  ) {
     context.commit(types.SET_SPAN, []);
-    return  getTraceSpans(params).then((res) => {
+    return getTraceSpans(params).then((res) => {
+      // context.commit(types.SET_SPAN, span.data.queryTrace.spans);
       context.commit(types.SET_SPAN, res.data.data.queryTrace.spans);
     });
   },
-  CLEAR_TRACE(context: { commit: Commit; state: State, rootState: any }) {
+  CLEAR_TRACE(context: { commit: Commit; state: State; rootState: any }) {
     context.commit(types.CLEAR_TRACE);
   },
 };
diff --git a/src/views/components/dashboard/slow-service.vue b/src/views/components/dashboard/slow-service.vue
index 18e40dd..ad94dae 100644
--- a/src/views/components/dashboard/slow-service.vue
+++ b/src/views/components/dashboard/slow-service.vue
@@ -4,7 +4,7 @@
     <div>
       <span class="r sm">{{i.value}} ms</span>
       <div class="mb5 cp link-hover" @click="changeService(i)" >
-        <Tooltip :content="i.service.label" placement="top" class="ell" style="max-width: 160px;">
+        <Tooltip :content="i.service.label" max-width="200"  placement="top" class="ell" style="max-width: 200px;">
           <span>{{i.service.label}}</span>
         </Tooltip>
       </div>
diff --git a/src/views/components/dashboard/top-slow.vue b/src/views/components/dashboard/top-slow.vue
index af0a2c1..c2b2d8b 100644
--- a/src/views/components/dashboard/top-slow.vue
+++ b/src/views/components/dashboard/top-slow.vue
@@ -4,7 +4,7 @@
     <div>
       <span class="r sm">{{i.duration}} ms</span>
       <div class="blue cp mb5" @click="$router.push({ path:'/trace/link', query:{traces:i.traceIds.join('&')}})">
-        <Tooltip :content="i.operationNames[0]" placement="top" class="ell" style="max-width: 160px;">
+        <Tooltip :content="`${moment(parseInt(i.start)).format('YYYY-MM-DD HH:mm:ss')}\n${i.operationNames[0]}`" placement="top" max-width="200" class="ell" style="max-width: 200px;">
           <span>{{i.operationNames[0]}}</span>
         </Tooltip>
       </div>
@@ -17,11 +17,13 @@
 <script lang="ts">
 import Vue from 'vue';
 import { State } from 'vuex-class';
+import moment from 'dayjs';
 import { Component } from 'vue-property-decorator';
 
 @Component({})
 export default class RkChartBox extends Vue {
   @State('dashboard') stateDashboard;
+  moment = moment;
   get maxDuration() {
     const temp:Number[] = [...this.stateDashboard.slowTrace].map(i => i.duration);
     return Math.max.apply(null, temp);
diff --git a/src/views/components/dashboard/top-throughput.vue b/src/views/components/dashboard/top-throughput.vue
index 0a5a325..0604a8d 100644
--- a/src/views/components/dashboard/top-throughput.vue
+++ b/src/views/components/dashboard/top-throughput.vue
@@ -4,7 +4,7 @@
     <div>
       <span class="r sm">{{i.value}} calls/ m</span>
       <div class="mb5 cp link-hover" @click="appChange(i)">
-        <Tooltip :content="i.label" placement="top" class="ell" style="max-width: 160px;">
+        <Tooltip :content="i.label" max-width="200" placement="top" class="ell" style="max-width: 200px;">
           <span>{{i.label}}</span>
         </Tooltip>
       </div>
diff --git a/src/views/components/topology/topo.vue b/src/views/components/topology/topo.vue
index 265d446..1a9bc81 100644
--- a/src/views/components/topology/topo.vue
+++ b/src/views/components/topology/topo.vue
@@ -173,6 +173,13 @@
   },
   methods: {
     draw() {
+      const codeId = this.datas.nodes.map(i => i.id);
+      for (let i = 0; i < this.datas.calls.length; i += 1) {
+        const element = this.datas.calls[i];
+        if(codeId.indexOf(element.target) === -1 ) {
+          this.datas.calls[i].target = this.datas.calls[i].source;
+        }
+      }
       this.svg.select('.graph').remove();
       this.force = d3
         .forceSimulation(this.datas.nodes)
@@ -188,6 +195,10 @@
       this.svg.call(this.getZoomBehavior(this.graph));
       this.svg.on('click', (d, i) => {
         this.$emit('setCurrentApp', {name: '', id: ''});
+        this.toggleNode(this.node, d, false);
+        this.toggleLine(this.line, d, false);
+        // this.toggleMarker(marker, currNode, true);
+        this.toggleLineText(this.linkText, d, false);
       });
       this.defs = this.graph.append('defs');
       this.arrowMarker = this.defs
@@ -203,7 +214,9 @@
       const arrow_path = 'M2,2 L10,6 L2,10 L6,6 L2,2';
       this.glink = this.graph.append('g').selectAll('.link');
       this.link = this.glink.data(this.datas.calls).enter().append('g');
-      this.line = this.link.append('path').attr('class', 'link').attr("marker-end","url(#arrow)");
+      this.line = this.link.append('path').attr('class', 'link')
+      .attr('stroke-dasharray', d => d.cpm ? '30 3': '0')
+      .attr('stroke', d => d.cpm ? 'rgba(255, 199, 31, 0.4)' : 'rgba(199, 199, 210, 0.3)').attr("marker-end","url(#arrow)");
       this.linkText = this.link.append('g');
       this.linkText
         .append('rect')
@@ -245,6 +258,10 @@
           delete copyD.fy;
           delete copyD.index;
           that.$emit('setCurrentApp', copyD);
+          that.toggleNode(that.node, d, true);
+          that.toggleLine(that.line, d, true);
+          // this.toggleMarker(marker, currNode, true);
+          that.toggleLineText(that.linkText, d, true);
         });
       this.node
         .append('rect')
@@ -296,6 +313,65 @@
         }
       });
     },
+    isLinkNode(currNode, node) {
+    if (currNode.id === node.id) {
+        return true;
+    }
+    return this.datas.calls.filter(i => 
+      (i.source.id === currNode.id || i.target.id === currNode.id) &&
+      (i.source.id === node.id || i.target.id === node.id)
+    ).length;
+  },
+    toggleNode(nodeCircle, currNode, isHover) {
+    if (isHover) {
+        // 提升节点层级 
+      nodeCircle.sort((a, b) => a.id === currNode.id ? 1 : -1);
+      nodeCircle
+          .style('opacity', .2)
+          .filter(node => this.isLinkNode(currNode, node))
+          .style('opacity', 1);
+    } else {
+        nodeCircle.style('opacity', 1);
+    }
+},
+toggleLine(linkLine, currNode, isHover) {
+  if (isHover) {
+    linkLine
+      .style('opacity', .05)
+      .style('animation', 'none')
+      .filter(link => this.isLinkLine(currNode, link))
+      .style('opacity', 1)
+      .style('animation', 'dash 6s linear infinite');
+      // .classed('link-active', true);
+    } else {
+      linkLine
+        .style('opacity', 1)
+        .style('animation', 'dash 6s linear infinite');
+        // .classed('link-active', false);
+    }
+  },
+isLinkLine(node, link) {
+    return link.source.id == node.id || link.target.id == node.id;
+},
+toggleLineText(lineText, currNode, isHover) {
+  if (isHover) {
+    lineText
+      .style('fill-opacity', link => this.isLinkLine(currNode, link) ? 1.0 : 0.0);
+      } else {
+      lineText
+      .style('fill-opacity', '1.0');
+    }
+  },
+    toggleMarker(marker, currNode, isHover) {
+      if (isHover) {
+        marker.filter(link => this.isLinkLine(currNode, link))
+          .style('transform', 'scale(1.5)');
+      } else {
+        marker
+          .attr('refX', nodeConf.radius.Company)
+          .style('transform', 'scale(1)');
+      }
+    },
     resize() {
       this.svg.attr('width', this.$refs.topo.offsetWidth);
       this.svg.attr('height', document.body.clientHeight - 64);
@@ -313,7 +389,6 @@
         );
       this.linkText.attr('transform',d => {
         let tagx = 1
-        // if(d.sx < d.tx) tagx = -tagx;
         return `translate(${d.sx - (d.sx-d.tx)/2}, ${d.sy - (d.sy-d.ty)/2})`
       });
       this.node.attr('transform', d => `translate(${d.x},${d.y - 20})`);
@@ -332,7 +407,6 @@
         });
     },
     dragstart(d) {
-      // console.log(this.node)
       this.node._groups[0].forEach(d => {
         d.__data__.fx = d.__data__.x;
         d.__data__.fy = d.__data__.y;
@@ -367,8 +441,6 @@
   .link {
     stroke-linecap: round;
     fill:rgba(255, 255, 255, 0);
-    stroke: rgba(255, 199, 31, 0.5);
-    stroke-dasharray: 30 3;
     animation: dash 6s linear infinite;
   }
 @keyframes dash {
@@ -381,9 +453,9 @@
   }
   .link2 {
     stroke: rgb(80, 80, 80);
-    stroke-dasharray: 200 20;
-    stroke-dashoffset: 0;
-    animation: dash 1.5s linear infinite;
+    // stroke-dasharray: 200 20;
+    // stroke-dashoffset: 0;
+    // animation: dash 1.5s linear infinite;
   }
   @keyframes dash {
     from {
diff --git a/src/views/components/trace/d3-trace.js b/src/views/components/trace/d3-trace.js
index 36c54f0..d544d62 100644
--- a/src/views/components/trace/d3-trace.js
+++ b/src/views/components/trace/d3-trace.js
@@ -5,6 +5,8 @@
   Http: '#72a5fd',
   Database: '#ff6732',
   Unknown: '#ffc107',
+  Cache: '#00bcd4',
+  RPCFramework: '#ee4395',
 };
 const trace = (data,width,row,vm) => {
   d3.select('svg').remove();
@@ -61,8 +63,8 @@
     nodeEnter.append('text').attr('dy', 2).attr('dx', 12)
       .style('fill', d => d.data.isError? '#f1483f' : '')
       .text(d => d.data.isError? 'x' : '');
-    nodeEnter.append('text').style('fill', d => type[d.data.layer]).style('stroke', d => type[d.data.layer]).style('stroke-width', '0.8').style('font-size', '11px').attr('dy', 2)
-      .attr('dx', d => d.data.label.length > 50 ? 320 : 40 + d.data.label.length * 6.9)
+    nodeEnter.append('text').style('fill', d => type[d.data.layer]).style('stroke', d => type[d.data.layer]).style('stroke-width', '0.6').style('font-size', '11px').attr('dy', 2)
+      .attr('dx', d => d.data.label.length > 50 ? 320 : 45 + d.data.label.length * 6.9)
       .text(d => d.data.layer);
     nodeEnter.append('text').attr('dy', 20).attr('dx', 30).style('fill', '#acacac').style('font-size', '11px')
       .text(d => `${d.data.applicationCode? d.data.applicationCode : ''} ${d.data.component? `> ${d.data.component}` : ''}`);
diff --git a/src/views/components/trace/trace-charts.vue b/src/views/components/trace/trace-charts.vue
index 781cbaa..5280448 100644
--- a/src/views/components/trace/trace-charts.vue
+++ b/src/views/components/trace/trace-charts.vue
@@ -83,10 +83,11 @@
             this.traverseTree(segmentGroup[ref.parentSegmentId],ref.parentSpanId,ref.parentSegmentId,segmentGroup[id])
           };
         })
-        if(segmentGroup[id].refs.length !==0 ) delete segmentGroup[id];
+        // if(segmentGroup[id].refs.length !==0 ) delete segmentGroup[id];
       })
       for (let i in segmentGroup) {
-        this.segmentId.push(segmentGroup[i])
+        if(segmentGroup[i].refs.length ===0 )
+        this.segmentId.push(segmentGroup[i]);
       }
       trace({label:`TraceID: ${this.traceId}`, children: this.segmentId},this.$el.offsetWidth, this.data,this);
     },
diff --git a/src/views/containers/topology.vue b/src/views/containers/topology.vue
index 6f0ddf9..11b9927 100644
--- a/src/views/containers/topology.vue
+++ b/src/views/containers/topology.vue
@@ -3,20 +3,23 @@
     <TopoChart :datas="stateTopo" @setCurrentApp="setCurrentApp"/>
     <div class="topology-select">
     </div>
-    <div class="topology-board" v-if="this.current.id">
+    <div class="topology-board" v-if="this.current.id" v-show="open">
       <div class="mb10" v-for="(value,key) in current" :key="key"><span class="half">{{key}}:</span><span class="half">{{value}}</span></div>
     </div>
-    <div class="topology-board" v-else>
+    <div class="topology-board" v-else  v-show="open">
       <div class="mb10"><span class="half">Application</span><span class="half">{{stateTopo.cluster.numOfApplication}}</span></div>
       <div class="mb10"><span class="half">DB & Cache</span><span class="half">{{stateTopo.cluster.numOfDatabase + stateTopo.cluster.numOfCache}}</span></div>
       <div class="mb10"><span class="half">Service</span><span class="half">{{stateTopo.cluster.numOfService}}</span></div>
       <div class="mb10"><span class="half">MQ</span><span class="half">{{stateTopo.cluster.numOfMQ}}</span></div>
     </div>
-    <div v-clickout="() => this.show = false">
-      <div class="topology-setting-btn" @click="show = true">
+    <div v-clickout="() => this.show = false" >
+      <div class="topology-setting-btn" @click="open = !open" :style="`right:${open?345:25}px`">
+        <Icon :type="open?'ios-arrow-forward':'ios-arrow-back'" style="vertical-align: initial;"/>
+      </div>
+      <div class="topology-setting-btn" @click="show = true" style="top:75px" :style="`right:${open?345:25}px`">
         <Icon type="md-settings" style="vertical-align: initial;"/>
       </div>
-      <div @click="routerGo" class="topology-setting-btn" v-if="this.current.avgResponseTime | this.current.cpm | this.current.sla" style="top:75px">
+      <div @click="routerGo" class="topology-setting-btn" :style="`right:${open?345:25}px`" v-if="this.current.avgResponseTime | this.current.cpm | this.current.sla" style="top:125px">
         <Icon type="md-cube" style="vertical-align: initial;"/>
       </div>
       <TopoTool :stateTopo="stateTopo" :stateOptions="stateOptions" :show.sync="show" :propsTime="stateGlobal.duration"/>
@@ -45,6 +48,7 @@
     id: '',
     name: '',
   };
+  open = true;
   show = false;
   beforeMount() {
     this.SET_EVENTS([this.getCluster, getTopo]);
@@ -91,7 +95,6 @@
   cursor: pointer;
   position:absolute;
   top:25px;
-  right:345px;
   height: 36px;
   width: 36px;
   text-align: center;
diff --git a/src/views/containers/trace-detail.vue b/src/views/containers/trace-detail.vue
index a87c33a..b1f9dd7 100644
--- a/src/views/containers/trace-detail.vue
+++ b/src/views/containers/trace-detail.vue
@@ -16,11 +16,20 @@
     </div>
     <div slot="inner" class="micro-panel-inner">
       <div class="rk-trace-detail">
+        <h4 class="mb10">Tags.</h4>
+        <div class="mb15"><span class="mr10">Label:</span><span>{{this.currentSpan.label}}</span></div>
         <div class="half mb15"><span class="two-fifth">Span Type:</span><span class="three-fifth">{{this.currentSpan.type}}</span></div>
         <div class="half mb15"><span class="two-fifth">Component:</span><span class="three-fifth">{{this.currentSpan.component}}</span></div>
         <div class="half mb15"><span class="two-fifth">Peer:</span><span class="three-fifth">{{this.currentSpan.peer||'No Peer'}}</span></div>
         <div class="half mb15"><span class="two-fifth">Error:</span><span class="three-fifth">{{this.currentSpan.isError}}</span></div>
         <div class="half mb15" v-for="i in this.currentSpan.tags" :key="i.key"><span class="two-fifth">{{i.key}}:</span><span class="three-fifth">{{i.value}}</span></div>
+        <h4 class="mb10" v-if="this.currentSpan.logs" v-show="this.currentSpan.logs.length">Logs.</h4>
+        <div v-for="(i, index) in this.currentSpan.logs" :key="index">
+          <div class="mb10 sm"><span class="mr10">Time:</span><span class="grey">{{i.time | dateformat}}</span></div>
+          <div class="mb15" v-for="(_i, _index) in i.data" :key="_index">
+           <span class="one-fifth">{{_i.key}}:</span><pre class="four-fifth mt0 mb0" style="font-size:12px">{{_i.value}}</pre>
+          </div>
+        </div>
       </div>
     </div>
   </RkDrawer>