You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

data-process.vue 50 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590
  1. <!--
  2. Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. <template>
  14. <div class="data-process-wrap">
  15. <div class="title">{{$t('profiling.minddataTitle')}}</div>
  16. <el-tabs v-model="activeName"
  17. @tab-click="handleClick">
  18. <el-tab-pane :label="$t('profiling.queueInfo')"
  19. name="queueInfo">
  20. <div class="data-process-top"
  21. v-show="!processSummary.noData">
  22. <div class="cell-container data-process">
  23. <div class="title">
  24. {{$t('profiling.pipeline')}}
  25. </div>
  26. </div>
  27. <div class="queue-container">
  28. <div class="img">
  29. <div class="edge">
  30. <img src="@/assets/images/data-flow.png"
  31. alt="" />
  32. </div>
  33. <div class="icon">
  34. <img src="@/assets/images/queue.svg"
  35. alt=""
  36. @click="highlight('connector_queue')"
  37. clickKey="connector_queue" />
  38. </div>
  39. <div class="edge">
  40. <img src="@/assets/images/data-flow.png"
  41. alt="" />
  42. </div>
  43. </div>
  44. <div class="title">{{connectorQuene}}</div>
  45. <div class="description">
  46. <div class="item"
  47. v-if="processSummary.device.empty || processSummary.device.empty === 0">
  48. {{$t('profiling.queueTip2')}}
  49. <span class="num">
  50. {{processSummary.device.empty}}/{{processSummary.device.total}}
  51. </span>
  52. </div>
  53. <div class="item"
  54. v-if="processSummary.device.full || processSummary.device.full === 0">
  55. {{$t('profiling.queueTip1')}}
  56. <span class="num">
  57. {{processSummary.device.full}}/{{processSummary.device.total}}
  58. </span>
  59. </div>
  60. </div>
  61. </div>
  62. <div class="cell-container device_queue_op"
  63. @click="highlight('device_queue_op')"
  64. clickKey="device_queue_op"
  65. :style="{cursor: processSummary.count !== processSummary.maxCount ? 'default' : 'pointer'}">
  66. <div class="title">
  67. {{$t('profiling.deviceQueueOp')}}
  68. </div>
  69. </div>
  70. <div class="queue-container"
  71. v-show="processSummary.count === processSummary.maxCount">
  72. <div class="img">
  73. <div class="edge">
  74. <img src="@/assets/images/data-flow.png"
  75. alt="" />
  76. </div>
  77. <div class="icon">
  78. <img src="@/assets/images/queue.svg"
  79. @click="highlight('data_queue')"
  80. clickKey="data_queue"
  81. alt="" />
  82. </div>
  83. <div class="edge">
  84. <img src="@/assets/images/data-flow.png"
  85. alt="" />
  86. </div>
  87. </div>
  88. <div class="title">{{$t('profiling.dataQueue')}}</div>
  89. <div class="description">
  90. <div class="item"
  91. v-if="processSummary.get_next.empty || processSummary.get_next.empty === 0">
  92. {{$t('profiling.queueTip2')}}
  93. <span class="num">
  94. {{processSummary.get_next.empty}}/{{processSummary.get_next.total}}
  95. </span>
  96. </div>
  97. <div class="item"
  98. v-if="processSummary.get_next.full || processSummary.get_next.full === 0">
  99. {{$t('profiling.queueTip1')}}
  100. <span class="num">
  101. {{processSummary.get_next.full}}/{{processSummary.get_next.total}}
  102. </span>
  103. </div>
  104. </div>
  105. </div>
  106. <div class="cell-container get-next"
  107. @click="highlight('get_next')"
  108. clickKey="get_next"
  109. v-if="processSummary.count === processSummary.maxCount">
  110. <div class="title">
  111. {{$t('profiling.getData')}}
  112. </div>
  113. </div>
  114. </div>
  115. <div class="data-process-bottom" v-show="!processSummary.noData">
  116. <div class="queue-step-wrap"
  117. v-if="processSummary.count === processSummary.maxCount">
  118. <div class="title">{{$t('profiling.queueStep')}}</div>
  119. <div class="chart-content">
  120. <div class="chart-wrap"
  121. :class="{highlight:selected==='connector_queue'}">
  122. <div class="title">{{connectorQuene}}</div>
  123. <template v-if="!connectQueueChart.noData">
  124. <div class="data-tips">
  125. <div v-if="connectQueueChart.queueSummary.empty_queue!==undefined">
  126. {{$t('profiling.queueTip2')}}{{connectQueueChart.queueSummary.empty_queue}}
  127. /{{connectQueueChart.size}}</div>
  128. <div v-if="connectQueueChart.queueSummary.full_queue!==undefined">
  129. {{$t('profiling.queueTip1')}}{{connectQueueChart.queueSummary.full_queue}}
  130. /{{connectQueueChart.size}}</div>
  131. </div>
  132. <div id="connect-queue"
  133. class="chart"></div>
  134. </template>
  135. </div>
  136. <div class="chart-wrap"
  137. :class="{highlight:selected==='data_queue'}">
  138. <div class="title">{{$t('profiling.dataQueue')}}</div>
  139. <template v-if="!dataQueueChart.noData">
  140. <div class="data-tips">
  141. <div v-if="dataQueueChart.queueSummary.empty_queue!==undefined">
  142. {{$t('profiling.queueTip2')}}{{dataQueueChart.queueSummary.empty_queue}}
  143. /{{dataQueueChart.size}}</div>
  144. <div v-if="dataQueueChart.queueSummary.full_queue!==undefined">
  145. {{$t('profiling.queueTip1')}}{{dataQueueChart.queueSummary.full_queue}}
  146. /{{dataQueueChart.size}}</div>
  147. </div>
  148. <div id="data-queue"
  149. class="chart"></div>
  150. </template>
  151. </div>
  152. </div>
  153. </div>
  154. <div class="queue-step-wrap"
  155. v-if="processSummary.count === processSummary.maxCount">
  156. <div class="title">{{$t('profiling.operatorTimeConAnalysis')}}</div>
  157. <div class="chart-content second">
  158. <div class="chart-wrap analysis"
  159. :class="{highlight:selected==='device_queue_op'}">
  160. <div class="title">{{$t('profiling.deviceQueueOp')}}</div>
  161. <template v-if="!deviceQueueOpChart.noData">
  162. <div class="data-tips">
  163. <div v-if="deviceQueueOpChart.timeSummary.avg_cost!==undefined">
  164. {{$t('profiling.avgCost')}}{{deviceQueueOpChart.timeSummary.avg_cost}}ms</div>
  165. <div v-if="deviceQueueOpChart.timeSummary.get_cost!==undefined">
  166. {{$t('profiling.getCost')}}{{deviceQueueOpChart.timeSummary.get_cost}}ms</div>
  167. <div v-if="deviceQueueOpChart.timeSummary.push_cost!==undefined">
  168. {{$t('profiling.pushCost')}}{{deviceQueueOpChart.timeSummary.push_cost}}ms</div>
  169. </div>
  170. <div id="device_queue_op"
  171. class="chart"></div>
  172. </template>
  173. </div>
  174. <div class="chart-wrap analysis"
  175. :class="{highlight:selected==='get_next'}">
  176. <div class="title">{{$t('profiling.getNext')}}</div>
  177. <template v-if="!getNextChart.noData">
  178. <div class="data-tips">
  179. <div v-if="getNextChart.timeSummary.avg_cost!==undefined">
  180. {{$t('profiling.avgCost')}}{{getNextChart.timeSummary.avg_cost}}ms</div>
  181. <div v-if="getNextChart.timeSummary.get_cost!==undefined">
  182. {{$t('profiling.getCost')}}{{getNextChart.timeSummary.get_cost}}ms</div>
  183. <div v-if="getNextChart.timeSummary.push_cost!==undefined">
  184. {{$t('profiling.pushCost')}}{{getNextChart.timeSummary.push_cost}}ms</div>
  185. </div>
  186. <div id="get_next"
  187. class="chart"></div>
  188. </template>
  189. </div>
  190. </div>
  191. </div>
  192. <div class="queue-step-wrap single"
  193. v-if="processSummary.count !== processSummary.maxCount">
  194. <div class="title">{{$t('profiling.queueStep')}}</div>
  195. <div class="chart-content">
  196. <div class="chart-wrap"
  197. :class="{highlight:selected==='connector_queue'}">
  198. <div class="title">{{connectorQuene}}</div>
  199. <template v-if="!connectQueueChart.noData">
  200. <div class="data-tips">
  201. <div v-if="connectQueueChart.queueSummary.empty_queue!==undefined">
  202. {{$t('profiling.queueTip2')}}{{connectQueueChart.queueSummary.empty_queue}}
  203. /{{connectQueueChart.size}}</div>
  204. <div v-if="connectQueueChart.queueSummary.full_queue!==undefined">
  205. {{$t('profiling.queueTip1')}}{{connectQueueChart.queueSummary.full_queue}}
  206. /{{connectQueueChart.size}}</div>
  207. </div>
  208. <div id="connect-queue"
  209. class="chart"></div>
  210. </template>
  211. </div>
  212. </div>
  213. </div>
  214. </div>
  215. <div class="image-noData"
  216. v-if="processSummary.noData">
  217. <div>
  218. <img :src="require('@/assets/images/nodata.png')"
  219. alt="" />
  220. </div>
  221. <p>{{(initOver)?$t("public.noData"):$t("public.dataLoading")}}</p>
  222. </div>
  223. </el-tab-pane>
  224. <el-tab-pane :label="$t('profiling.pipeline')"
  225. name="pipeLine">
  226. <div class="pipeline-wrap"
  227. v-show="!pipeData">
  228. <div class="pipeline-top">
  229. <div class="pipeline-top-title">
  230. {{$t('profiling.pipelineTopTitle')}}
  231. </div>
  232. <div class="average-rate-wrap">
  233. <div id="average-rate"></div>
  234. </div>
  235. </div>
  236. <div class="pipeline-middle">
  237. <div class="pipeline-middle-title">
  238. {{$t('profiling.pipelineMiddleTitle')}}
  239. </div>
  240. <div class="operator-graph">
  241. <div id="graph"></div>
  242. </div>
  243. </div>
  244. <div class="pipeline-bottom">
  245. <div class="queue-deep-wrap">
  246. <div class="left">
  247. <div id="queue-deep"></div>
  248. </div>
  249. <div class="right">
  250. <div class="title">{{$t('profiling.operatorInfo',{msg1:current_op.name,msg2:parent_op.name})}}</div>
  251. <div class="item-wrap">
  252. <div class="item"><span>{{current_op.name}} ID:</span>{{current_op.op_id}}</div>
  253. <div class="item"><span>{{current_op.name}} type:</span>{{current_op.op_type}}</div>
  254. <div class="item">
  255. <span>{{current_op.name}} {{$t('profiling.workersNum')}}:</span>
  256. {{current_op.num_workers}}
  257. </div>
  258. <div class="item"><span>{{parent_op.name}} ID:</span>{{parent_op.op_id}}</div>
  259. <div class="item"><span>{{parent_op.name}} type:</span>{{parent_op.op_type}}</div>
  260. <div class="item">
  261. <span>{{parent_op.name}} {{$t('profiling.workersNum')}}:</span>
  262. {{parent_op.num_workers}}
  263. </div>
  264. </div>
  265. </div>
  266. </div>
  267. </div>
  268. </div>
  269. <div class="image-noData"
  270. v-if="pipeData">
  271. <div>
  272. <img :src="require('@/assets/images/nodata.png')"
  273. alt="" />
  274. </div>
  275. <p>{{initOver?$t("public.noData"):$t("public.dataLoading")}}</p>
  276. </div>
  277. </el-tab-pane>
  278. </el-tabs>
  279. </div>
  280. </template>
  281. <script>
  282. import echarts from 'echarts';
  283. import RequestService from '../../services/request-service';
  284. import {select, selectAll, zoom} from 'd3';
  285. import {event as currentEvent} from 'd3-selection';
  286. import 'd3-graphviz';
  287. const d3 = {select, selectAll, zoom};
  288. export default {
  289. props: {},
  290. data() {
  291. return {
  292. dir: '', // Profiler path
  293. currentCard: '', // Purrent card number
  294. connectQueueChart: {
  295. // Ponnect queue chart object
  296. id: 'connect-queue',
  297. chartDom: null,
  298. data: [],
  299. queueSummary: {},
  300. advise: '',
  301. type: 0,
  302. params: 'device_queue',
  303. noData: true,
  304. size: null,
  305. deviceId: null,
  306. },
  307. dataQueueChart: {
  308. // Data queue chart object
  309. id: 'data-queue',
  310. chartDom: null,
  311. data: [],
  312. queueSummary: {},
  313. advise: '',
  314. type: 0,
  315. params: 'get_next',
  316. noData: true,
  317. size: null,
  318. },
  319. deviceQueueOpChart: {
  320. // Device queue chart object
  321. id: 'device_queue_op',
  322. chartDom: null,
  323. data: [],
  324. timeSummary: {},
  325. type: 1,
  326. params: 'device_queue',
  327. noData: true,
  328. },
  329. getNextChart: {
  330. // Get next chart object
  331. id: 'get_next',
  332. chartDom: null,
  333. data: [],
  334. timeSummary: {},
  335. type: 1,
  336. params: 'get_next',
  337. noData: true,
  338. },
  339. processSummary: {
  340. // Process summary object
  341. noData: true,
  342. count: 6,
  343. maxCount: 6,
  344. device: {
  345. empty: 0,
  346. full: 0,
  347. total: 0,
  348. },
  349. get_next: {
  350. empty: 0,
  351. full: 0,
  352. total: 0,
  353. },
  354. },
  355. activeName: 'queueInfo',
  356. averageRateChart: {
  357. // Average rate chart object
  358. id: 'average-rate',
  359. chartDom: null,
  360. deviceId: null,
  361. },
  362. queueDeepChart: {
  363. // Queue deep chart object
  364. id: 'queue-deep',
  365. chartDom: null,
  366. },
  367. current_op: {},
  368. parent_op: {},
  369. pipeData: true,
  370. initOver: false, // Identify whether the interface returns
  371. allGraphData: {},
  372. graphviz: null,
  373. totalMemory: 16777216 * 2, // Memory size of the graph plug-in
  374. scaleRange: [0.0001, 10000], // Graph zooms in and zooms out.
  375. initQueue: '',
  376. trainId: '',
  377. selected: '',
  378. connectorQuene: '',
  379. };
  380. },
  381. watch: {
  382. '$parent.curDashboardInfo.curCardNum': {
  383. handler(newValue, oldValue) {
  384. if (newValue || newValue === 0) {
  385. this.dir = this.$route.query.dir;
  386. this.trainId = this.$route.query.id;
  387. this.currentCard = newValue;
  388. if (this.trainId) {
  389. document.title =
  390. `${decodeURIComponent(this.trainId)}` +
  391. `-${this.$t('profiling.mindData')}-MindInsight`;
  392. } else {
  393. document.title = `${this.$t('profiling.mindData')}-MindInsight`;
  394. }
  395. if (this.activeName === 'queueInfo') {
  396. this.initOver = false;
  397. this.init();
  398. } else {
  399. this.queryAverageRate();
  400. }
  401. }
  402. if (this.activeName === 'queueInfo' && this.$parent.curDashboardInfo.initOver) {
  403. this.initOver = true;
  404. }
  405. },
  406. deep: true,
  407. immediate: true,
  408. },
  409. },
  410. computed: {},
  411. mounted() {
  412. window.addEventListener(
  413. 'resize',
  414. this.debounce(this.resizeCallback, 200),
  415. false,
  416. );
  417. setTimeout(() => {
  418. this.$bus.$on('collapse', this.debounce(this.resizeCallback, 200));
  419. }, 500);
  420. },
  421. methods: {
  422. /**
  423. * Anti-shake
  424. * @param { Function } fn callback function
  425. * @param { Number } delay delay time
  426. * @return { Function }
  427. */
  428. debounce(fn, delay) {
  429. let timer = null;
  430. return function() {
  431. if (timer) {
  432. clearTimeout(timer);
  433. }
  434. timer = setTimeout(fn, delay);
  435. };
  436. },
  437. /**
  438. * Tabs switch
  439. */
  440. handleClick() {
  441. if (
  442. this.activeName === 'pipeLine' &&
  443. this.averageRateChart.deviceId !== this.currentCard
  444. ) {
  445. this.queryAverageRate();
  446. } else if (
  447. this.activeName === 'queueInfo' &&
  448. this.connectQueueChart.deviceId !== this.currentCard
  449. ) {
  450. this.init();
  451. }
  452. this.$nextTick(() => {
  453. this.resizeCallback();
  454. });
  455. },
  456. init() {
  457. this.connectorQuene = this.$t('profiling.connectorQuene');
  458. this.queryProcessSummary();
  459. },
  460. /**
  461. * Resize callback function
  462. */
  463. resizeCallback() {
  464. const chartArr = [
  465. 'connectQueueChart',
  466. 'dataQueueChart',
  467. 'deviceQueueOpChart',
  468. 'getNextChart',
  469. 'averageRateChart',
  470. 'queueDeepChart',
  471. ];
  472. chartArr.forEach((val) => {
  473. if (this[val].chartDom) {
  474. setTimeout(() => {
  475. this[val].chartDom.resize();
  476. }, 300);
  477. }
  478. });
  479. },
  480. /**
  481. * Query minddata data
  482. * @param {Object} chart Chart object
  483. */
  484. queryMinddataOp(chart) {
  485. const params = {
  486. profile: this.dir,
  487. device_id: this.currentCard,
  488. type: chart.params,
  489. train_id: this.trainId,
  490. };
  491. RequestService.minddataOp(params).then(
  492. (res) => {
  493. if (res && res.data) {
  494. const result = res.data;
  495. chart.data = result.info;
  496. if (result.summary) {
  497. chart.timeSummary = result.summary.time_summary || {};
  498. Object.keys(chart.timeSummary).forEach((val) => {
  499. chart.timeSummary[val] = parseFloat(
  500. chart.timeSummary[val],
  501. ).toFixed(3);
  502. });
  503. }
  504. chart.advise = result.advise;
  505. if (result.size > 0) {
  506. chart.noData = false;
  507. this.$nextTick(() => {
  508. this.setOption(chart);
  509. });
  510. } else {
  511. if (chart.chartDom) {
  512. chart.chartDom.clear();
  513. }
  514. chart.noData = true;
  515. }
  516. }
  517. },
  518. (err) => {
  519. if (chart.chartDom) {
  520. chart.chartDom.clear();
  521. }
  522. chart.noData = true;
  523. },
  524. );
  525. },
  526. /**
  527. * Query queue info
  528. * @param {Object} chart Chart object
  529. */
  530. queryQueueInfo(chart) {
  531. const params = {
  532. profile: this.dir,
  533. device_id: this.currentCard,
  534. type: chart.params,
  535. train_id: this.trainId,
  536. };
  537. RequestService.queueInfo(params).then(
  538. (res) => {
  539. if (res && res.data) {
  540. const result = res.data;
  541. chart.data = result.info;
  542. if (result.summary) {
  543. chart.queueSummary = result.summary.queue_summary || {};
  544. }
  545. chart.advise = result.advise;
  546. if (result.size > 0) {
  547. chart.noData = false;
  548. chart.size = result.size;
  549. this.$nextTick(() => {
  550. this.setOption(chart);
  551. });
  552. } else {
  553. if (chart.chartDom) {
  554. chart.chartDom.clear();
  555. }
  556. chart.noData = true;
  557. }
  558. }
  559. },
  560. (err) => {
  561. chart.noData = true;
  562. if (chart.chartDom) {
  563. chart.chartDom.clear();
  564. }
  565. },
  566. );
  567. },
  568. /**
  569. * Chart set option
  570. * @param {Object} chart Chart object
  571. */
  572. setOption(chart) {
  573. const myChart = echarts.init(document.getElementById(chart.id));
  574. const option = {
  575. tooltip: {
  576. trigger: 'axis',
  577. },
  578. toolbox: {
  579. show: true,
  580. },
  581. xAxis: {
  582. name: 'step',
  583. data: [],
  584. },
  585. yAxis: {},
  586. series: [],
  587. grid: {
  588. left: 50,
  589. top: 20,
  590. right: 50,
  591. bottom: 60,
  592. },
  593. };
  594. option.dataZoom = [
  595. {
  596. start: 0,
  597. end: 100,
  598. bottom: 0,
  599. },
  600. {
  601. start: 0,
  602. end: 100,
  603. type: 'inside',
  604. bottom: 0,
  605. },
  606. ];
  607. const arr = [];
  608. Object.keys(chart.data).forEach((val, index) => {
  609. const item = {};
  610. item.type = 'line';
  611. item.data = chart.data[val];
  612. item.name = val;
  613. if (chart.type === 0) {
  614. const markPointArr = [];
  615. item.data.forEach((val, key) => {
  616. if (val === 0) {
  617. markPointArr.push({xAxis: key, yAxis: val, symbolSize: 20});
  618. }
  619. });
  620. item.markPoint = {data: markPointArr};
  621. }
  622. arr.push(item);
  623. });
  624. option.series = arr;
  625. option.xAxis.data = chart.data[Object.keys(chart.data)[0]].map(
  626. (val, index) => index + 1,
  627. );
  628. myChart.setOption(option);
  629. chart.chartDom = myChart;
  630. if (this.connectQueueChart.chartDom && this.deviceQueueOpChart.chartDom) {
  631. echarts.connect([
  632. this.connectQueueChart.chartDom,
  633. this.deviceQueueOpChart.chartDom,
  634. ]);
  635. }
  636. if (this.getNextChart.chartDom && this.dataQueueChart.chartDom) {
  637. echarts.connect([
  638. this.getNextChart.chartDom,
  639. this.dataQueueChart.chartDom,
  640. ]);
  641. }
  642. },
  643. /**
  644. * Query process summary info
  645. */
  646. queryProcessSummary() {
  647. const params = {
  648. profile: this.dir,
  649. device_id: this.currentCard,
  650. train_id: this.trainId,
  651. };
  652. this.connectQueueChart.deviceId = this.currentCard;
  653. this.initOver = false;
  654. RequestService.queryProcessSummary(params).then(
  655. (res) => {
  656. if (res && res.data) {
  657. const data = JSON.parse(JSON.stringify(res.data));
  658. this.processSummary.count = Object.keys(data).length;
  659. if (this.processSummary.count) {
  660. this.dealProcess(data);
  661. this.$nextTick(() => {
  662. if (this.processSummary.count < this.processSummary.maxCount) {
  663. this.queryQueueInfo(this.connectQueueChart);
  664. this.dataQueueChart.noData = false;
  665. this.deviceQueueOpChart.noData = false;
  666. this.getNextChart.noData = false;
  667. } else {
  668. this.queryQueueInfo(this.connectQueueChart);
  669. this.queryQueueInfo(this.dataQueueChart);
  670. this.queryMinddataOp(this.deviceQueueOpChart);
  671. this.queryMinddataOp(this.getNextChart);
  672. }
  673. });
  674. } else {
  675. this.dealProcess(null);
  676. }
  677. } else {
  678. this.dealProcess(null);
  679. }
  680. },
  681. (error) => {
  682. this.dealProcess(null);
  683. },
  684. );
  685. },
  686. /**
  687. * Deal process data
  688. * @param {Object} data Process data
  689. */
  690. dealProcess(data) {
  691. this.processSummary.device = {
  692. empty: 0,
  693. full: 0,
  694. total: 0,
  695. };
  696. this.processSummary.get_next = {
  697. empty: 0,
  698. full: 0,
  699. total: 0,
  700. };
  701. this.processSummary.noData = true;
  702. this.initOver = true;
  703. if (data) {
  704. if (data.device_queue_info && data.device_queue_info.summary) {
  705. this.processSummary.device = {
  706. empty: data.device_queue_info.summary.empty_batch_count,
  707. full: data.device_queue_info.summary.full_batch_count,
  708. total: data.device_queue_info.summary.total_batch,
  709. };
  710. }
  711. if (data.get_next_queue_info && data.get_next_queue_info.summary) {
  712. this.processSummary.get_next = {
  713. empty: data.get_next_queue_info.summary.empty_batch_count,
  714. full: data.get_next_queue_info.summary.full_batch_count,
  715. total: data.get_next_queue_info.summary.total_batch,
  716. };
  717. }
  718. this.processSummary.noData = false;
  719. }
  720. },
  721. /**
  722. * Query average rate info
  723. */
  724. queryAverageRate() {
  725. const params = {
  726. params: {
  727. train_id: this.trainId,
  728. profile: this.dir,
  729. },
  730. body: {
  731. device_id: this.currentCard,
  732. },
  733. };
  734. this.averageRateChart.deviceId = this.currentCard;
  735. this.initOver = false;
  736. RequestService.queryOpQueue(params).then(
  737. (res) => {
  738. this.initOver = true;
  739. if (res && res.data) {
  740. this.removeGraph();
  741. const data = JSON.parse(JSON.stringify(res.data));
  742. this.dealPipeLineData(data);
  743. this.pipeData = !!!(res.data.object && res.data.object.length);
  744. if (
  745. res.data.object &&
  746. res.data.object.length &&
  747. res.data.col_name
  748. ) {
  749. const result = res.data.object.map((val) => {
  750. const obj = {};
  751. res.data.col_name.forEach((value, key) => {
  752. obj[value] = val[key];
  753. });
  754. return obj;
  755. });
  756. const data = result
  757. .sort((a, b) => {
  758. return a.output_queue_usage_rate - b.output_queue_usage_rate;
  759. })
  760. .filter((val) => {
  761. return val.parent_id !== null;
  762. });
  763. const dataY = data.map((val) => {
  764. return (val.output_queue_usage_rate * 100).toFixed(4);
  765. });
  766. const dataX = data.map((val) => {
  767. return `Queue_${val.op_id}`;
  768. });
  769. const profiling = this.$t('profiling');
  770. const option = {
  771. tooltip: {
  772. trigger: 'axis',
  773. axisPointer: {
  774. type: 'shadow',
  775. },
  776. formatter(params) {
  777. let value = {};
  778. data.forEach((val) => {
  779. if (`${val.op_id}` === params[0].axisValue.split('')[6]) {
  780. value = val;
  781. }
  782. });
  783. return (
  784. `${params[0].axisValue}<br>${params[0].marker}` +
  785. `${profiling.averageCapacity}:${value.output_queue_average_size}<br>` +
  786. `${params[0].marker}${profiling.totalCapacity}:` +
  787. `${value.output_queue_length}`
  788. );
  789. },
  790. },
  791. grid: {
  792. left: 70,
  793. top: 20,
  794. right: 100,
  795. bottom: 50,
  796. },
  797. xAxis: {
  798. type: 'value',
  799. boundaryGap: [0, 0.01],
  800. max: 100,
  801. axisLabel: {
  802. formatter(params) {
  803. return `${params}%`;
  804. },
  805. },
  806. },
  807. yAxis: {
  808. type: 'category',
  809. data: dataX,
  810. },
  811. series: [
  812. {
  813. type: 'bar',
  814. data: dataY,
  815. itemStyle: {
  816. color: '#00a5a7',
  817. },
  818. label: {
  819. show: true,
  820. position: 'right',
  821. color: '#000',
  822. formatter(params) {
  823. return `${params.value}%`;
  824. },
  825. },
  826. },
  827. ],
  828. dataZoom: [
  829. {start: 0, end: 100, orient: 'vertical', right: 10},
  830. {
  831. start: 0,
  832. end: 100,
  833. type: 'inside',
  834. orient: 'vertical',
  835. right: 10,
  836. },
  837. ],
  838. };
  839. this.$nextTick(() => {
  840. const echart = echarts.init(
  841. document.getElementById(this.averageRateChart.id),
  842. );
  843. echart.setOption(option);
  844. this.averageRateChart.chartDom = echart;
  845. });
  846. }
  847. }
  848. },
  849. () => {
  850. this.initOver = true;
  851. this.pipeData = true;
  852. this.removeGraph();
  853. },
  854. );
  855. },
  856. /**
  857. * Query queue info
  858. * @param {Number} id Op id
  859. */
  860. queryQueue(id) {
  861. const params = {
  862. profile: this.dir,
  863. train_id: this.trainId,
  864. device_id: this.currentCard,
  865. op_id: id,
  866. };
  867. RequestService.queryQueue(params).then((res) => {
  868. if (res && res.data) {
  869. const data = res.data.queue_info;
  870. const dataY = data.output_queue_size;
  871. this.current_op = res.data.current_op || {};
  872. this.parent_op = res.data.parent_op || {};
  873. this.current_op.name = `${this.current_op.op_type}_${this.current_op.op_id}`;
  874. this.parent_op.name = `${this.parent_op.op_type}_${this.parent_op.op_id}`;
  875. const option = {
  876. title: {
  877. text: this.$t('profiling.queueDeepChartTitle', {
  878. msg: `Queue${id}`,
  879. }),
  880. textStyle: {
  881. fontSize: 13,
  882. },
  883. left: 20,
  884. top: 10,
  885. },
  886. tooltip: {
  887. trigger: 'axis',
  888. },
  889. xAxis: {
  890. name: `${this.$t('profiling.sampleInterval')}/${
  891. data.sample_interval
  892. }ms`
  893. .split(' ')
  894. .join('\n'),
  895. data: dataY.map((val, index) => index + 1),
  896. },
  897. yAxis: {
  898. name: '',
  899. },
  900. series: [
  901. {
  902. type: 'line',
  903. data: dataY,
  904. itemStyle: {
  905. color: '#00a5a7',
  906. },
  907. },
  908. ],
  909. grid: {
  910. left: 50,
  911. top: 40,
  912. right: 100,
  913. bottom: 60,
  914. },
  915. dataZoom: [
  916. {
  917. start: 0,
  918. end: 100,
  919. bottom: 10,
  920. },
  921. {start: 0, end: 100, type: 'inside', bottom: 10},
  922. ],
  923. };
  924. this.$nextTick(() => {
  925. const echart = echarts.init(
  926. document.getElementById(this.queueDeepChart.id),
  927. );
  928. echart.setOption(option);
  929. this.queueDeepChart.chartDom = echart;
  930. });
  931. }
  932. });
  933. },
  934. highlight(key) {
  935. if (
  936. key === 'device_queue_op' &&
  937. this.processSummary.count !== this.processSummary.maxCount
  938. ) {
  939. return;
  940. }
  941. const domList = document.querySelectorAll('.data-process-top *[clickKey]');
  942. Array.prototype.forEach.call(domList, (dom) => {
  943. if (dom.getAttribute('clickKey') === key) {
  944. dom.classList.add('selected');
  945. } else {
  946. dom.classList.remove('selected');
  947. }
  948. });
  949. this.selected = key;
  950. },
  951. /** ************************ graph ****************************/
  952. /**
  953. * Processing Graph Data
  954. * @param {Object} data Data of the graph
  955. */
  956. dealPipeLineData(data) {
  957. const colName = data.col_name;
  958. const colCount = colName.length;
  959. const list = data.object;
  960. list.forEach((i) => {
  961. if (i && i.length === colCount) {
  962. const obj = {
  963. output: '',
  964. };
  965. colName.forEach((key, index) => {
  966. obj[key] = i[index];
  967. });
  968. if (obj.op_id || obj.op_id === 0) {
  969. obj.key = `${obj.op_id}_operator`;
  970. if (obj.parent_id || obj.parent_id === 0) {
  971. const queueKey = `${obj.op_id}_queue`;
  972. obj.output = queueKey;
  973. const queueObj = JSON.parse(JSON.stringify(obj));
  974. queueObj.key = queueKey;
  975. queueObj.op_type = 'queue';
  976. queueObj.output = `${obj.parent_id}_operator`;
  977. this.allGraphData[queueKey] = queueObj;
  978. if (!(this.initQueue || this.initQueue === 0)) {
  979. this.initQueue = queueObj.op_id;
  980. }
  981. }
  982. this.allGraphData[obj.key] = obj;
  983. }
  984. }
  985. });
  986. if (Object.keys(this.allGraphData).length) {
  987. const dot = this.packageGraphData();
  988. this.initGraph(dot);
  989. } else {
  990. this.removeGraph();
  991. }
  992. },
  993. /**
  994. * Encapsulates graph data into dot data.
  995. * @return {String} Dot string for packing graph data
  996. */
  997. packageGraphData() {
  998. let nodeStr = '';
  999. let edgeStr = '';
  1000. Object.keys(this.allGraphData).forEach((key) => {
  1001. const node = this.allGraphData[key];
  1002. nodeStr +=
  1003. `<${key}>[id="${key}";` +
  1004. `${
  1005. node.op_type === 'queue'
  1006. ? `shape=rect;class="queue";label="Queue_${
  1007. node.op_id
  1008. }(${parseFloat(
  1009. ((node.output_queue_usage_rate || 0) * 100).toFixed(4),
  1010. )}%)";`
  1011. : `shape=Mrecord;class="operator";label="${node.op_type}_${node.op_id}";`
  1012. }];`;
  1013. if (node.output) {
  1014. edgeStr += `<${node.key}>-><${node.output}>`;
  1015. }
  1016. });
  1017. const initSetting =
  1018. 'node[style="filled";fontsize="10px"];edge[fontsize="6px";];';
  1019. return `digraph {compound=true;rankdir=LR;${initSetting}${nodeStr}${edgeStr}}`;
  1020. },
  1021. /**
  1022. * Initializing the dataset graph
  1023. * @param {String} dot Dot statement encapsulated in graph data
  1024. */
  1025. initGraph(dot) {
  1026. this.graphviz = d3
  1027. .select('#graph')
  1028. .graphviz({useWorker: false, totalMemory: this.totalMemory})
  1029. .zoomScaleExtent(this.scaleRange)
  1030. .dot(dot)
  1031. .attributer(this.attributer)
  1032. .render(this.startApp);
  1033. },
  1034. /**
  1035. * Default method of the graph rendering adjustment. Set the node format.
  1036. * @param {Object} datum Object of the current rendering element.
  1037. * @param {Number} index Indicates the subscript of the current rendering element.
  1038. * @param {Array} nodes An array encapsulated with the current rendering element.
  1039. */
  1040. attributer(datum, index, nodes) {
  1041. if (datum.tag === 'svg') {
  1042. const width = '100%';
  1043. const height = '100%';
  1044. datum.attributes.width = width;
  1045. datum.attributes.height = height;
  1046. }
  1047. },
  1048. /**
  1049. * Initialization method executed after the graph rendering is complete
  1050. */
  1051. startApp() {
  1052. setTimeout(() => {
  1053. if (this.graphviz) {
  1054. this.graphviz._data = null;
  1055. this.graphviz._dictionary = null;
  1056. this.graphviz = null;
  1057. }
  1058. }, 500);
  1059. d3.select('#graph').selectAll('.operator>title').remove();
  1060. const queueList = Array.from(document.querySelectorAll('#graph .queue'));
  1061. for (let i = 0, len = queueList.length; i < len; i++) {
  1062. const node = queueList[i];
  1063. const data = this.allGraphData[node.id];
  1064. if (data) {
  1065. const polygon = node.querySelector('polygon');
  1066. const color = `rgba(19, 171, 173, ${data.output_queue_usage_rate})`;
  1067. polygon.setAttribute('fill', color);
  1068. polygon.setAttribute('stroke', color);
  1069. const title = node.querySelector('title');
  1070. title.textContent =
  1071. `${this.$t('profiling.averageCapacity')}:${
  1072. data.output_queue_average_size || 0
  1073. }\n` +
  1074. `${this.$t('profiling.totalCapacity')}:${
  1075. data.output_queue_length || 0
  1076. }`;
  1077. }
  1078. }
  1079. this.initZooming();
  1080. const nodes = d3.selectAll('g.queue');
  1081. nodes.on('click', (target, index, nodesList) => {
  1082. const selectedNode = nodesList[index];
  1083. nodes.classed('selected', false);
  1084. d3.select(`g[id="${selectedNode.id}"]`).classed('selected', true);
  1085. const nodeData = this.allGraphData[selectedNode.id];
  1086. if (nodeData) {
  1087. this.queryQueue(nodeData.op_id);
  1088. }
  1089. });
  1090. d3.select(`g[id="${this.initQueue}_queue"]`).classed('selected', true);
  1091. this.queryQueue(this.initQueue);
  1092. },
  1093. /**
  1094. * Initializing the Zoom Function of a Graph
  1095. */
  1096. initZooming() {
  1097. const svgDom = document.querySelector('#graph svg');
  1098. const svgRect = svgDom.getBoundingClientRect();
  1099. const graphDom = document.querySelector('#graph #graph0');
  1100. const graphBox = graphDom.getBBox();
  1101. const graphRect = graphDom.getBoundingClientRect();
  1102. let graphTransform = {};
  1103. const minScale = Math.min(
  1104. svgRect.width / 2 / graphRect.width,
  1105. svgRect.height / 2 / graphRect.height,
  1106. );
  1107. const padding = 4;
  1108. const minDistance = 20;
  1109. const pointer = {start: {x: 0, y: 0}, end: {x: 0, y: 0}};
  1110. const zoom = d3
  1111. .zoom()
  1112. .on('start', () => {
  1113. const event = currentEvent.sourceEvent;
  1114. pointer.start.x = event.x;
  1115. pointer.start.y = event.y;
  1116. })
  1117. .on('zoom', () => {
  1118. const event = currentEvent.sourceEvent;
  1119. const transformData = this.getTransformData(graphDom);
  1120. if (!Object.keys(graphTransform).length) {
  1121. graphTransform = {
  1122. x: transformData.translate[0],
  1123. y: transformData.translate[1],
  1124. k: transformData.scale[0],
  1125. };
  1126. }
  1127. let tempStr = '';
  1128. let change = {};
  1129. let scale = transformData.scale[0];
  1130. const graphRect = graphDom.getBoundingClientRect();
  1131. const transRate = graphBox.width / graphRect.width;
  1132. if (event.type === 'mousemove') {
  1133. pointer.end.x = event.x;
  1134. pointer.end.y = event.y;
  1135. let tempX = pointer.end.x - pointer.start.x;
  1136. let tempY = pointer.end.y - pointer.start.y;
  1137. const paddingTrans = Math.max(
  1138. (padding / transRate) * scale,
  1139. minDistance,
  1140. );
  1141. if (
  1142. graphRect.left + paddingTrans + tempX >=
  1143. svgRect.left + svgRect.width
  1144. ) {
  1145. tempX = Math.min(tempX, 0);
  1146. }
  1147. if (
  1148. graphRect.left + graphRect.width - paddingTrans + tempX <=
  1149. svgRect.left
  1150. ) {
  1151. tempX = Math.max(tempX, 0);
  1152. }
  1153. if (
  1154. graphRect.top + paddingTrans + tempY >=
  1155. svgRect.top + svgRect.height
  1156. ) {
  1157. tempY = Math.min(tempY, 0);
  1158. }
  1159. if (
  1160. graphRect.top + graphRect.height - paddingTrans + tempY <=
  1161. svgRect.top
  1162. ) {
  1163. tempY = Math.max(tempY, 0);
  1164. }
  1165. change = {
  1166. x: tempX * transRate * scale,
  1167. y: tempY * transRate * scale,
  1168. };
  1169. pointer.start.x = pointer.end.x;
  1170. pointer.start.y = pointer.end.y;
  1171. } else if (event.type === 'wheel') {
  1172. const wheelDelta = -event.deltaY;
  1173. const rate = 1.2;
  1174. scale =
  1175. wheelDelta > 0
  1176. ? transformData.scale[0] * rate
  1177. : transformData.scale[0] / rate;
  1178. scale = Math.max(this.scaleRange[0], scale, minScale);
  1179. scale = Math.min(this.scaleRange[1], scale);
  1180. change = {
  1181. x:
  1182. (graphRect.x + padding / transRate - event.x) *
  1183. transRate *
  1184. (scale - transformData.scale[0]),
  1185. y:
  1186. (graphRect.bottom - padding / transRate - event.y) *
  1187. transRate *
  1188. (scale - transformData.scale[0]),
  1189. };
  1190. }
  1191. graphTransform = {
  1192. x: transformData.translate[0] + change.x,
  1193. y: transformData.translate[1] + change.y,
  1194. k: scale,
  1195. };
  1196. tempStr = `translate(${graphTransform.x},${graphTransform.y}) scale(${graphTransform.k})`;
  1197. graphDom.setAttribute('transform', tempStr);
  1198. event.stopPropagation();
  1199. event.preventDefault();
  1200. });
  1201. const svg = d3.select('#graph svg');
  1202. svg.on('.zoom', null);
  1203. svg.call(zoom);
  1204. svg.on('dblclick.zoom', null);
  1205. svg.on('wheel.zoom', null);
  1206. const graph0 = d3.select('#graph #graph0');
  1207. graph0.on('.zoom', null);
  1208. graph0.call(zoom);
  1209. },
  1210. /**
  1211. * Obtains the transform data of a node.
  1212. * @param {Object} node Node dom data
  1213. * @return {Object} Transform data of a node
  1214. */
  1215. getTransformData(node) {
  1216. if (!node) {
  1217. return [];
  1218. }
  1219. const transformData = node.getAttribute('transform');
  1220. const attrObj = {};
  1221. if (transformData) {
  1222. const lists = transformData.trim().split(' ');
  1223. lists.forEach((item) => {
  1224. const index1 = item.indexOf('(');
  1225. const index2 = item.indexOf(')');
  1226. const name = item.substring(0, index1);
  1227. const params = item
  1228. .substring(index1 + 1, index2)
  1229. .split(',')
  1230. .map((i) => {
  1231. return parseFloat(i) || 0;
  1232. });
  1233. attrObj[name] = params;
  1234. });
  1235. }
  1236. return attrObj;
  1237. },
  1238. removeGraph() {
  1239. const svg = document.querySelector('#graph svg');
  1240. if (svg) {
  1241. svg.remove();
  1242. }
  1243. this.allGraphData = {};
  1244. },
  1245. },
  1246. destroyed() {
  1247. // Remove the listener of window size change
  1248. window.removeEventListener(
  1249. 'resize',
  1250. this.debounce(this.resizeCallback, 200),
  1251. false,
  1252. );
  1253. this.$bus.$off('collapse');
  1254. },
  1255. };
  1256. </script>
  1257. <style lang="scss">
  1258. .data-process-wrap {
  1259. height: 100%;
  1260. background: #fff;
  1261. padding: 0 16px;
  1262. .title {
  1263. font-size: 16px;
  1264. font-weight: bold;
  1265. text-align: left;
  1266. }
  1267. .el-tabs.el-tabs--top {
  1268. height: calc(100% - 22px);
  1269. }
  1270. .el-tabs__content {
  1271. height: calc(100% - 54px);
  1272. & > .el-tab-pane {
  1273. height: 100%;
  1274. }
  1275. }
  1276. .el-tabs__item.is-active {
  1277. color: #00a5a7;
  1278. font-weight: bold;
  1279. }
  1280. .data-process-top {
  1281. height: 156px;
  1282. font-size: 0;
  1283. display: flex;
  1284. align-items: flex-start;
  1285. margin-top: 20px;
  1286. .cell-container {
  1287. width: 20%;
  1288. cursor: pointer;
  1289. padding: 20px 0;
  1290. border: 2px solid transparent;
  1291. .title {
  1292. font-size: 14px;
  1293. line-height: 20px;
  1294. padding: 0 0 0 20px;
  1295. font-weight: bold;
  1296. }
  1297. }
  1298. .data-process {
  1299. background-color: #e3f8eb;
  1300. cursor: default;
  1301. .title {
  1302. border-left: 2px solid #00a5a7;
  1303. }
  1304. }
  1305. .device_queue_op {
  1306. background-color: #e1f2ff;
  1307. .title {
  1308. border-left: 2px solid #6cbfff;
  1309. }
  1310. }
  1311. .get-next {
  1312. background-color: #fef4dd;
  1313. .title {
  1314. border-left: 2px solid #fdca5a;
  1315. }
  1316. }
  1317. .queue-container {
  1318. width: 20%;
  1319. position: relative;
  1320. .img {
  1321. width: 100%;
  1322. height: 37px;
  1323. margin-top: 13px;
  1324. .edge {
  1325. width: calc(50% - 40px);
  1326. display: inline-block;
  1327. padding-top: 11px;
  1328. img {
  1329. width: 100%;
  1330. }
  1331. }
  1332. .icon {
  1333. width: 80px;
  1334. padding: 0 20px;
  1335. display: inline-block;
  1336. vertical-align: middle;
  1337. img {
  1338. padding: 3px;
  1339. border: 2px solid transparent;
  1340. cursor: pointer;
  1341. }
  1342. }
  1343. }
  1344. .title {
  1345. text-align: center;
  1346. font-size: 14px;
  1347. margin-top: 10px;
  1348. font-weight: bold;
  1349. }
  1350. .description {
  1351. position: absolute;
  1352. font-size: 12px;
  1353. line-height: 12px;
  1354. white-space: nowrap;
  1355. overflow: hidden;
  1356. width: 100%;
  1357. text-align: center;
  1358. .item {
  1359. font-size: 12px;
  1360. line-height: 16px;
  1361. white-space: normal;
  1362. .num {
  1363. color: #07a695;
  1364. }
  1365. }
  1366. }
  1367. }
  1368. .selected {
  1369. border: 2px solid #3399ff !important;
  1370. }
  1371. }
  1372. .data-process-bottom {
  1373. height: calc(100% - 156px);
  1374. .queue-step-wrap {
  1375. &:first-child {
  1376. height: 50%;
  1377. }
  1378. &:last-child {
  1379. height: 50%;
  1380. }
  1381. & > .title {
  1382. margin-bottom: 15px;
  1383. font-weight: bold;
  1384. font-size: 15px;
  1385. }
  1386. .chart-content {
  1387. height: calc(100% - 30px);
  1388. .chart-wrap {
  1389. float: left;
  1390. width: calc(50% - 12px);
  1391. height: calc(100% - 10px);
  1392. border-radius: 4px;
  1393. overflow-y: auto;
  1394. border: 1px solid #eee;
  1395. &:first-child {
  1396. margin-right: 20px;
  1397. }
  1398. .title {
  1399. font-size: 13px;
  1400. padding: 10px;
  1401. font-weight: bold;
  1402. }
  1403. .data-tips {
  1404. color: #999;
  1405. padding: 0 0 0 10px;
  1406. & > div {
  1407. display: inline-block;
  1408. margin-right: 10px;
  1409. }
  1410. }
  1411. .chart {
  1412. height: calc(100% - 70px);
  1413. min-height: 150px;
  1414. overflow: hidden;
  1415. }
  1416. }
  1417. .chart-wrap.highlight {
  1418. border-color: #3399ff;
  1419. }
  1420. }
  1421. .chart-content.second {
  1422. height: calc(100% - 25px);
  1423. }
  1424. }
  1425. .queue-step-wrap.single {
  1426. height: 100%;
  1427. .chart-content {
  1428. .chart-wrap {
  1429. width: 100%;
  1430. }
  1431. }
  1432. }
  1433. }
  1434. .pipeline-wrap {
  1435. height: 100%;
  1436. .pipeline-top {
  1437. height: 35%;
  1438. .pipeline-top-title {
  1439. font-size: 15px;
  1440. font-weight: bold;
  1441. }
  1442. .average-rate-wrap {
  1443. overflow-y: auto;
  1444. height: calc(100% - 20px);
  1445. #average-rate {
  1446. height: 100%;
  1447. min-height: 180px;
  1448. overflow: hidden;
  1449. }
  1450. }
  1451. }
  1452. .pipeline-middle {
  1453. height: 30%;
  1454. .pipeline-middle-title {
  1455. font-size: 15px;
  1456. font-weight: bold;
  1457. }
  1458. .operator-graph {
  1459. height: calc(100% - 20px);
  1460. #graph {
  1461. width: 100%;
  1462. height: 100%;
  1463. background-color: #f7faff;
  1464. #graph0 > polygon {
  1465. fill: transparent;
  1466. }
  1467. .node.queue {
  1468. cursor: pointer;
  1469. }
  1470. .operator {
  1471. path {
  1472. stroke: #e6ebf5;
  1473. fill: #e6ebf5;
  1474. }
  1475. }
  1476. .selected {
  1477. path,
  1478. polygon {
  1479. stroke: red;
  1480. }
  1481. }
  1482. .edge {
  1483. path,
  1484. polygon {
  1485. stroke: #e6ebf5;
  1486. fill: #e6ebf5;
  1487. }
  1488. }
  1489. }
  1490. }
  1491. }
  1492. .pipeline-bottom {
  1493. height: 35%;
  1494. .queue-deep-wrap {
  1495. height: 100%;
  1496. background: #fafbfc;
  1497. & > div {
  1498. float: left;
  1499. height: 100%;
  1500. }
  1501. .left {
  1502. width: calc(60% - 20px);
  1503. overflow-y: auto;
  1504. height: 100%;
  1505. border-right: 1px dashed #ccc;
  1506. padding-right: 20px;
  1507. margin-right: 20px;
  1508. #queue-deep {
  1509. height: 100%;
  1510. width: 100%;
  1511. min-height: 220px;
  1512. overflow: hidden;
  1513. }
  1514. }
  1515. .right {
  1516. width: 40%;
  1517. .title {
  1518. font-size: 13px;
  1519. margin-top: 7px;
  1520. }
  1521. .item-wrap {
  1522. padding-top: 10px;
  1523. height: calc(100% - 26px);
  1524. overflow-y: auto;
  1525. .item {
  1526. margin-top: 10px;
  1527. & > span {
  1528. color: #757b88;
  1529. display: inline-block;
  1530. width: 50%;
  1531. }
  1532. }
  1533. }
  1534. }
  1535. }
  1536. }
  1537. }
  1538. .image-noData {
  1539. width: 100%;
  1540. height: calc(100% - 37px);
  1541. display: flex;
  1542. justify-content: center;
  1543. align-items: center;
  1544. flex-direction: column;
  1545. p {
  1546. font-size: 16px;
  1547. padding-top: 10px;
  1548. }
  1549. }
  1550. }
  1551. </style>