ServiceMap.vue 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191
  1. <template>
  2. <div style="padding:16px;">
  3. <!-- <el-form ref="queryForm" :model="queryParams" :inline="true" label-width="68px" @submit.native.prevent>
  4. <el-form-item label="" prop="url">
  5. <el-input
  6. v-model="queryParams.url"
  7. placeholder="请输入url"
  8. clearable
  9. size="mini"
  10. @keyup.enter.native="handleQuery"
  11. />
  12. </el-form-item>
  13. </el-form> -->
  14. <!-- <el-row :gutter="10" class="mb8">
  15. <el-col :span="1.5">
  16. <el-button
  17. type="primary"
  18. icon="el-icon-plus"
  19. size="mini"
  20. @click="handleAdd"
  21. >新增</el-button>
  22. </el-col>
  23. </el-row> -->
  24. <div>
  25. <!-- <el-table
  26. v-loading="loading"
  27. :key="isUpdate"
  28. :data="serveceMapList"
  29. :default-sort = "{prop: 'total', order: 'descending'}"
  30. @row-click="handleRowClick"
  31. > -->
  32. <el-table
  33. v-loading="loading"
  34. :key="isUpdate"
  35. :data="serveceMapList"
  36. :default-sort = "{prop: 'total', order: 'descending'}"
  37. @row-click="handleRowClick"
  38. >
  39. <el-table-column header-align="left" label="服务名" prop="name" align="left" :show-overflow-tooltip="true"/>
  40. <el-table-column header-align="left" label="起始服务" prop="service_name" align="left" :show-overflow-tooltip="true">
  41. <template slot-scope="scope">
  42. <span>{{scope.row.service_name}}</span>
  43. </template>
  44. </el-table-column>
  45. <!-- <el-table-column header-align="left" label="SpanName" prop="span_name" align="left" :show-overflow-tooltip="true">
  46. <template slot-scope="scope">
  47. <span>{{scope.row.span_name}}</span>
  48. </template>
  49. </el-table-column> -->
  50. <el-table-column header-align="center" label="rpm" sortable prop="rpm" align="center" :show-overflow-tooltip="true">
  51. <template slot-scope="scope">
  52. <el-tag>{{ scope.row.rpm == undefined?0:Number(scope.row.rpm).toFixed(2)}}</el-tag>
  53. </template>
  54. </el-table-column>
  55. <el-table-column header-align="center" label="count" sortable prop="total" align="center">
  56. <template slot-scope="scope">
  57. <!-- <span>{{ Number(scope.row.rpm).toFixed(2)}}</span> -->
  58. <el-tag>{{ scope.row.total== undefined?0:Number(scope.row.total).toFixed(2)}}</el-tag>
  59. </template>
  60. </el-table-column>
  61. <el-table-column header-align="center" label="errRate" sortable prop="error_rate" align="center">
  62. <template slot-scope="scope">
  63. <el-tag v-if="scope.row.error_rate==0">{{ scope.row.error_rate }}</el-tag>
  64. <el-tag v-else type="danger">{{ Number(scope.row.error_rate).toFixed(2)}}</el-tag>
  65. </template>
  66. </el-table-column>
  67. <el-table-column header-align="center" label="max(ms)" sortable prop="max" align="center" width="100">
  68. <template slot-scope="scope">
  69. <!-- <span>{{ Number(scope.row.max).toFixed(2)}}</span> -->
  70. <el-tag v-if="scope.row.max>=2000" type="danger">{{ Number(scope.row.max).toFixed(2) }}</el-tag>
  71. <el-tag v-if="scope.row.max<2000">{{ Number(scope.row.max).toFixed(2) }}</el-tag>
  72. </template>
  73. </el-table-column>
  74. <el-table-column header-align="center" label="avg(ms)" sortable prop="avg" align="center" width="100">
  75. <template slot-scope="scope">
  76. <!-- <span>{{ Number(scope.row.avg).toFixed(2)}}</span> -->
  77. <el-tag v-if="scope.row.avg>=2000" type="danger">{{ Number(scope.row.avg).toFixed(2) }}</el-tag>
  78. <el-tag v-if="scope.row.avg<2000">{{ Number(scope.row.avg).toFixed(2) }}</el-tag>
  79. </template>
  80. </el-table-column>
  81. <el-table-column header-align="center" label="延迟比例" prop="duration_stats" align="center">
  82. <template slot-scope="scope">
  83. {{ drawEchartsp5(scope.row, scope.$index, 'five') }}
  84. <div :id="`tiger-five-trend-index` + scope.$index" class="tiger-trend-charts"></div>
  85. </template>
  86. </el-table-column>
  87. <!-- <el-table-column
  88. label="操作"
  89. align="center"
  90. class-name="small-padding fixed-width"
  91. sortable prop="favor"
  92. >
  93. <template slot-scope="scope">
  94. <el-button
  95. size="mini"
  96. type="text"
  97. icon="el-icon-edit"
  98. @click.native.stop="handleUpdate(scope.row)"
  99. >标注</el-button>
  100. <el-button
  101. v-if="scope.row.favor == 0"
  102. size="mini"
  103. type="text"
  104. icon="el-icon-star-off"
  105. @click.native.stop="handleFavor(scope.row)"
  106. ></el-button>
  107. <el-button
  108. v-if="scope.row.favor == 1"
  109. size="mini"
  110. type="text"
  111. icon="el-icon-star-on"
  112. @click.native.stop="handleFavor(scope.row)"
  113. ></el-button>
  114. <el-button
  115. style='color:#F56C6C'
  116. size="mini"
  117. type="text"
  118. icon="el-icon-delete"
  119. @click.native.stop="handleDelete(scope.row)"
  120. >删除</el-button>
  121. </template>
  122. </el-table-column> -->
  123. </el-table>
  124. </div>
  125. <div class="more" v-if="serveceMapList.length<tableCount" @click="loadMore">加载更多</div>
  126. <div class="more nomore" v-if="loading==false&&serveceMapList.length>=tableCount">已加载完所有的数据!</div>
  127. <!-- <pagination
  128. v-show="total>0"
  129. :total="total"
  130. :page.sync="childQueryParams.pageIndex"
  131. :limit.sync="childQueryParams.pageSize"
  132. @pagination="handelgetBizDetail"
  133. /> -->
  134. <!-- 添加或修改应用配置对话框 -->
  135. <el-dialog v-if="open" :title="title" :visible.sync="open" width="700px" :center='true' :close-on-click-modal="false">
  136. <el-form ref="form" :model="form" :rules="rules" label-width="95px">
  137. <el-form-item label="业务定义" prop="name">
  138. <el-input v-model="form.name" placeholder="请输入业务定义" />
  139. </el-form-item>
  140. <el-form-item label="URL组" prop="url">
  141. <span slot="label">
  142. URL组
  143. <el-tooltip>
  144. <div slot="content">
  145. <div>URL匹配示例:</div>
  146. <div style="display:flex;justify-content:space-between;font-size:12px;">
  147. <div>1、/aaa/bbb 完全匹配</div>
  148. </div>
  149. <div>
  150. <div>2、/aaa/{regexp}, 如/aaa/.* 正则匹配</div>
  151. </div>
  152. <!-- <div>
  153. <div>3、/aaa/{:words} 匹配任意字母</div>
  154. </div> -->
  155. </div>
  156. <i class="el-icon-question" />
  157. </el-tooltip>
  158. </span>
  159. <el-input v-model="form.url" placeholder="请输入URL组"/>
  160. </el-form-item>
  161. <el-form-item label="类型" prop="type">
  162. <el-radio-group v-model="form.type">
  163. <el-radio label="1">对外</el-radio>
  164. <el-radio label="2">对内</el-radio>
  165. </el-radio-group>
  166. </el-form-item>
  167. <el-form-item label="所属模块" prop="module">
  168. <el-input v-model="form.module" placeholder="所属模块" />
  169. </el-form-item>
  170. <el-form-item label="业务概述" prop="summary">
  171. <el-input v-model="form.summary" type="textarea" placeholder="请输入业务概述" />
  172. </el-form-item>
  173. </el-form>
  174. <div slot="footer" class="dialog-footer">
  175. <el-button type="primary" @click="submitForm">确 定</el-button>
  176. <el-button @click="cancel">取 消</el-button>
  177. </div>
  178. </el-dialog>
  179. <!-- <div class="back" @click="toggle()">
  180. <i class="iconfont icon-qiehuan1"></i>
  181. </div> -->
  182. <el-drawer
  183. :visible.sync="drawer"
  184. direction="rtl"
  185. size="calc( 100vw - 210px)"
  186. @close="drawerClose"
  187. >
  188. <div style="margin-bottom:16px;margin-top:-32px;">
  189. <div class="flexWrap">
  190. <div class="flex-left">
  191. <!-- <Topo /> -->
  192. <Topo v-if='drawer' :graphData='graphData' topoId='container' v-on:changeState="clickRowHandleNode" ref='topo'/>
  193. </div>
  194. <div class="flex-right">
  195. <div id="scaleMain" style="width:100%;min-width:500px;height:180px;margin-bottom:24px"></div>
  196. <el-table
  197. v-loading="tableLoading"
  198. :data="rowData"
  199. >
  200. <el-table-column header-align="center" label="TraceID" prop="trace_id" align="center" width="240">
  201. <template slot-scope="scope">
  202. <span @click="goto(scope.row)">{{scope.row.trace_id}}</span>
  203. </template>
  204. </el-table-column>
  205. <el-table-column header-align="center" label="ServiceName" prop="service_name" align="center" :show-overflow-tooltip="true" />
  206. <!-- <el-table-column header-align="center" label="SpanName" prop="span_name" align="center" :show-overflow-tooltip="true" />
  207. <el-table-column header-align="center" label="SpanKind" prop="span_kind" align="center" width="100"/> -->
  208. <el-table-column header-align="center" label="Target" prop="target" align="center" :show-overflow-tooltip="true" />
  209. <el-table-column header-align="center" label="Method" prop="method" align="center">
  210. <template slot-scope="scope">
  211. <el-tag type="info">{{ scope.row.method }}</el-tag>
  212. </template>
  213. </el-table-column>
  214. <el-table-column header-align="center" label="StatusCode" prop="status_code" align="center">
  215. <template slot-scope="scope">
  216. <span>
  217. <span v-if="scope.row.status_code<400"><i class="green_small"></i>{{scope.row.status_code}}</span>
  218. <span v-else><i class="red_small"></i>{{scope.row.status_code}}</span>
  219. </span>
  220. </template>
  221. </el-table-column>
  222. <el-table-column header-align="center" label="Duration(ms)" prop="duration" align="center">
  223. <template slot-scope="scope">
  224. <el-tag v-if="scope.row.duration>=2000" type="danger">{{ scope.row.duration }}</el-tag>
  225. <el-tag v-if="scope.row.duration<2000" type="info">{{ scope.row.duration }}</el-tag>
  226. </template>
  227. </el-table-column>
  228. <!-- <el-table-column header-align="center" label="max(ms)" sortable="custom" prop="duration_stats" align="center" width="100">
  229. <template slot-scope="scope">
  230. <span>{{ Number(scope.row.max).toFixed(2)}}</span>
  231. </template>
  232. </el-table-column>
  233. <el-table-column header-align="center" label="avg(ms)" sortable="custom" prop="duration_stats" align="center" width="100">
  234. <template slot-scope="scope">
  235. <span>{{ Number(scope.row.avg).toFixed(2)}}</span>
  236. </template>
  237. </el-table-column> -->
  238. </el-table>
  239. <pagination
  240. v-show="total>0"
  241. :total="total"
  242. :page.sync="childQueryParams.pageIndex"
  243. :limit.sync="childQueryParams.pageSize"
  244. @pagination="handelgetBizDetail"
  245. />
  246. </div>
  247. </div>
  248. </div>
  249. </el-drawer>
  250. <el-drawer
  251. :visible.sync="drawerDetail"
  252. direction="rtl"
  253. size="55%"
  254. @close="drawerCloseDetail"
  255. v-if='JSON.stringify(detailData) != "{}"'
  256. >
  257. <el-collapse v-model="activeNames">
  258. <el-collapse-item title="Attributes" name="one">
  259. <ul class='ul_box' v-if='detailData.span_attributes !=null'>
  260. <li v-for='(value,key) in detailData.span_attributes' :key='key'>
  261. <div class="ul_label">{{key}}</div>
  262. <div class="ul_value" style='color:#008080;white-space: pre-wrap;'>"{{value}}"</div>
  263. </li>
  264. </ul>
  265. </el-collapse-item>
  266. <el-collapse-item title="Resource" name="tow">
  267. <ul class='ul_box' v-if='detailData.resource_attributes !=null'>
  268. <li v-for='(value,key) in detailData.resource_attributes' :key='key'>
  269. <div class="ul_label">{{key}}</div>
  270. <div class="ul_value" style='color:#008080;white-space: pre-wrap;'>"{{value}}"</div>
  271. </li>
  272. </ul>
  273. </el-collapse-item>
  274. <el-collapse-item v-if='detailData.events_timestamp.length>0 && detailData.events_timestamp!=null' :title="'Events'+'('+ detailData.events_timestamp.length +')'" name="three">
  275. <div class='events_box'>
  276. <el-collapse :value="activeIn">
  277. <el-collapse-item v-for='(item,index) in detailData.events_timestamp' :key='index' :title="item" :name="index">
  278. <ul class='ul_box'>
  279. <li>
  280. <div class="ul_label">message</div>
  281. <div class="ul_value" style='color:#008080;white-space: pre-wrap;'>"{{detailData.events_name[index]}}"</div>
  282. </li>
  283. <li v-for='(value,key) in detailData.events_attributes[index]' :key='key'>
  284. <div class="ul_label">{{key}}</div>
  285. <div class="ul_value" style='color:#008080;white-space: pre-wrap;'>"{{value}}"</div>
  286. </li>
  287. </ul>
  288. </el-collapse-item>
  289. </el-collapse>
  290. </div>
  291. </el-collapse-item>
  292. </el-collapse>
  293. </el-drawer>
  294. </div>
  295. </template>
  296. <script>
  297. import { listUrlMapping, addUrlMapping, getUrlMapping, updateUrlMapping, delUrlMapping, favor,getUrlMappingRange,getBizDetail,listBizGraph,listBizGraphStats,updateBiz,delBiz,favorBiz } from '@/api/mapping'
  298. import { formatJson } from '@/utils'
  299. import storage from '@/utils/storage'
  300. // import bus from '@/utils/bus'
  301. import {listService,listServiceStats,serviceSpans} from "@/api/service"
  302. import { getToken } from '@/utils/auth'
  303. // import echarts from 'echarts'
  304. import resize from './mixins/resize'
  305. import moment from 'moment'
  306. // import elTableInfiniteScroll from 'el-table-infinite-scroll';
  307. import Topo from './TopoNew1'
  308. export default {
  309. name: 'ServiceMap',
  310. mixins: [resize],
  311. components: {
  312. Topo
  313. },
  314. // directives: {
  315. // 'el-table-infinite-scroll': elTableInfiniteScroll
  316. // },
  317. data() {
  318. return {
  319. isUpdate:false,
  320. isflag:false,
  321. tableflag:false,
  322. drawer:false,
  323. detailData:{},
  324. drawerDetail:false,
  325. tableLoading:false,
  326. // 表格高度
  327. tableHeight:"",
  328. // 数据总数
  329. tableCount:0,
  330. serveceMapList:[],
  331. //应用数据列表
  332. appData:[],
  333. // 遮罩层
  334. loading: false,
  335. // 选中数组
  336. ids: [],
  337. // 非单个禁用
  338. single: true,
  339. // 非多个禁用
  340. multiple: true,
  341. // 总条数
  342. total: 0,
  343. menuIdsChecked: [],
  344. // 弹出层标题
  345. title: '',
  346. // 是否显示弹出层
  347. open: false,
  348. isEdit: false,
  349. // 日期范围
  350. dateRange: [],
  351. // 查询参数
  352. queryParams: {
  353. // start_time:Math.round((new Date().getTime())/1000 - (5*60)),
  354. // end_time:Math.round(new Date().getTime()/1000),
  355. app_id:'',
  356. start_time:'',
  357. end_time:'',
  358. pageIndex:1,
  359. pageSize:50,
  360. },
  361. // 表单参数
  362. form: {
  363. },
  364. // 表单校验
  365. rules: {
  366. name: [
  367. { required: true, message: '接口名称不能为空', trigger: 'blur' }
  368. ],
  369. url: [
  370. { required: true, message: 'URL组不能为空', trigger: 'blur' }
  371. ]
  372. },
  373. appsAction: process.env.VUE_APP_BASE_API + '/api/v1/public/uploadFile',
  374. appsfileList: [],
  375. headers: { 'Authorization': 'Bearer ' + getToken() },
  376. appsItem:{},
  377. expands:[],
  378. childQueryParams:{
  379. // start_time:Math.round((new Date().getTime())/1000 - (5*60)),
  380. // end_time:Math.round(new Date().getTime()/1000),
  381. start_time:0,
  382. end_time:0,
  383. biz_id:1,
  384. pageIndex: 1,
  385. pageSize: 10,
  386. },
  387. rowData:[],
  388. serviceMapTimer:null,
  389. BizStatsQuery:{
  390. id:0,
  391. start_time:'',
  392. end_time:'',
  393. },
  394. tempList:[],
  395. graphData:{},
  396. graphStatsQuery:{
  397. biz_node_id:0,// 仅支持传递单个节点,多个节点需要并发请求分别调用
  398. end_time:1702972800,
  399. start_time:1702969200,
  400. },
  401. graphObj:[]
  402. }
  403. },
  404. watch: {
  405. '$store.state.time.globalTimes': {
  406. handler(newValue, oldValue) {
  407. if(newValue){
  408. this.queryParams.start_time = newValue.startTime;
  409. this.queryParams.end_time = newValue.endTime
  410. this.BizStatsQuery.start_time= newValue.start_time;
  411. this.BizStatsQuery.end_time= newValue.end_time;
  412. this.graphStatsQuery.start_time= newValue.start_time;
  413. this.graphStatsQuery.end_time= newValue.end_time;
  414. this.childQueryParams.start_time = newValue.startTime;
  415. this.childQueryParams.end_time = newValue.endTime
  416. this.queryParams.pageIndex = 1
  417. this.serveceMapList=[];
  418. this.getList();
  419. if(newValue.timeOut){
  420. console.log(newValue.timeOut,'业务接口页面假如timeOut有变化了执行,否而不执行')
  421. if(newValue.timeOut == 1){
  422. clearInterval(this.serviceMapTimer);
  423. }else{
  424. clearInterval(this.serviceMapTimer);
  425. this.Refresh(newValue.timeOut);
  426. }
  427. }
  428. }
  429. },
  430. // immediate: true,
  431. deep: true
  432. },
  433. serveceMapList:{
  434. handler(newVal,oldVal){
  435. if(newVal){
  436. this.serveceMapList = newVal;
  437. this.tableflag = true;
  438. }
  439. },
  440. // immediate: true,
  441. deep:true
  442. }
  443. },
  444. beforeDestroy() {
  445. clearInterval(this.serviceMapTimer);
  446. },
  447. // created() {
  448. // let windowHeight =document.documentElement.clientHeight || document.body.clientHeight;
  449. // // 动态计算表格的高度,200为屏幕内除了表格以外其他元素的高度,依实际情况而定
  450. // this.tableHeight = windowHeight - 200 + "px";
  451. // this.appsItem=storage.get('appsItem');
  452. // if(this.appsItem.id!=''||this.appsItem.id!=undefined){
  453. // this.queryParams.app_id=this.appsItem.id;
  454. // this.childQueryParams.app_id = this.appsItem.id;
  455. // this.getList()
  456. // }
  457. // },
  458. created(){
  459. this.$store.commit('time/setTimeFlag',false)
  460. this.appItem=storage.get('appsItem');
  461. if(JSON.stringify(this.appItem) !="{}"){
  462. this.queryParams.app_id = this.appItem.id;
  463. this.queryParams.start_time = this.appItem.start_time;
  464. this.queryParams.end_time = this.appItem.end_time;
  465. this.BizStatsQuery.start_time= this.appItem.start_time;
  466. this.BizStatsQuery.end_time= this.appItem.end_time;
  467. this.childQueryParams.start_time = this.appItem.start_time;
  468. this.childQueryParams.end_time = this.appItem.end_time;
  469. this.getList()
  470. this.$forceUpdate();
  471. }
  472. // this.columnChange();
  473. },
  474. mounted(){
  475. this.$forceUpdate();
  476. },
  477. methods: {
  478. // clickFull() {
  479. // this.Visible=true;
  480. // this.$refs['topo'].initGraph();
  481. // },
  482. Refresh(timeOut){
  483. this.serviceMapTimer = setInterval(()=>{
  484. this.getList();
  485. },timeOut)
  486. },
  487. goto(row){
  488. let href = this.$router.resolve({
  489. path:'/latency/index',
  490. query:{
  491. id:row.trace_id
  492. }
  493. })
  494. console.log(href)
  495. window.open(window.location.origin+"/"+href.href,"_blank")
  496. },
  497. drawerClose(){
  498. this.drawer = false;
  499. },
  500. drawerCloseDetail(){
  501. this.drawerDetail = false;
  502. },
  503. toogleExpand(row, column, event){
  504. console.log(row,'点击展开行');
  505. this.drawer=true;
  506. this.childQueryParams.id = row.id;
  507. this.handelgetBizDetail();
  508. },
  509. handleRowClick(row, column, event){
  510. console.log(row,'点击行');
  511. console.log(column,'column')
  512. this.childQueryParams.biz_id = row.id;
  513. this.handelgetBizDetail();
  514. this.getListBizGraph(row.id)
  515. setTimeout(()=>{
  516. // this.childQueryParams.biz_id = row.id;
  517. // this.handelgetBizDetail();
  518. // this.getListBizGraph(row.id)
  519. this.drawer=true;
  520. this.$nextTick(() =>{
  521. this.drawEchartsScale(row);
  522. })
  523. },300)
  524. },
  525. handelgetBizDetail(){
  526. this.tableLoading = true
  527. getBizDetail(this.childQueryParams).then(res => {
  528. if(res.code == 200){
  529. this.tableLoading = false;
  530. this.rowData= res.data.list;
  531. this.total = res.data.count;
  532. }
  533. })
  534. },
  535. getListBizGraph(id){
  536. listBizGraph({biz_id:id}).then((res)=>{
  537. console.log(res,'决策树数据')
  538. if(res.code == 200){
  539. let arr = res.data
  540. if(arr.length>0){
  541. let List = this.handelListBizData(arr)
  542. console.log(List,'处理后的数据')
  543. this.graphData =List[0];
  544. console.log(this.graphData,'graphData=======')
  545. }
  546. }
  547. })
  548. },
  549. handelListBizData(data){
  550. data.forEach((v,k)=>{
  551. v.collapsed = false;
  552. v.id = String(v.id);
  553. v.name = v.name;
  554. v.label = v.stats.duration!=undefined?v.stats.duration.toFixed(2):0;
  555. v.currency = 'ms';
  556. v.rate = v.stats.success_rate;
  557. v.status = 'B'
  558. v.variableValue = v.stats.success_rate;
  559. if (v.children != undefined && v.children.length > 0) {
  560. v.children = this.handelListBizData(v.children)
  561. }
  562. })
  563. return data;
  564. },
  565. handleData(data) {
  566. data.forEach((v, k) => {
  567. // let string = key === '' ? k + 1 : key + '.' + (k + 1)
  568. // v.span_name += '---' + string
  569. // v.id = v.span_name;
  570. v.id = v.biz_id;
  571. v.collapsed = false;
  572. v.name = v.name;
  573. // v.label = (v.duration).toFixed(2);
  574. v.label = v.service_name;
  575. v.currency='ms';
  576. v.rate = v.duration_persent;
  577. v.status = (v.status_code == 'STATUS_CODE_ERROR'||v.events_timestamp.length>0||v.http_code>399)?'R':(v.duration>500)?'Y':'G';
  578. v.variableValue = v.duration_persent;
  579. // v.status_code = 'UNSET'
  580. if (v.children != undefined && v.children.length > 0) {
  581. v.children = this.handleData(v.children)
  582. }
  583. })
  584. return data
  585. },
  586. // 根据行的id判断
  587. getRowKeys(row) {
  588. return row.id;
  589. },
  590. clickRowHandle(row, column, event) {
  591. // alert('a')
  592. },
  593. clickRowHandleNode(item){
  594. console.log(item,'点击节点')
  595. this.detailData = item;
  596. this.drawerDetail=true;
  597. },
  598. handleSortChang(column, prop, order) {
  599. prop = column.prop
  600. order = column.order
  601. if (this.order !== '' && this.order !== prop + 'Order') {
  602. this.queryParams[this.order] = undefined
  603. }
  604. if (order === 'descending') {
  605. this.queryParams[prop + 'Order'] = 'desc'
  606. this.order = prop + 'Order'
  607. } else if (order === 'ascending') {
  608. this.queryParams[prop + 'Order'] = 'asc'
  609. this.order = prop + 'Order'
  610. } else {
  611. this.queryParams[prop + 'Order'] = undefined
  612. }
  613. this.getList()
  614. },
  615. toggle(){
  616. this.$router.push({
  617. path:'dashboard'
  618. })
  619. },
  620. // 加载更多
  621. loadMore() {
  622. if (!this.loading) {
  623. this.loading = true;
  624. if (this.serveceMapList.length < this.tableCount) {
  625. this.queryParams.pageIndex++;
  626. this.getList();
  627. } else {
  628. // this.$message("已加载完所有的数据!");
  629. this.loading = false;
  630. }
  631. }
  632. },
  633. getList() {
  634. this.loading = true
  635. const _this = this;
  636. listService(this.queryParams).then(res => {
  637. console.log(res.data.list,'服务列表')
  638. if(res.code == 200){
  639. _this.serveceMapList= _this.serveceMapList.concat(res.data.list);
  640. this.tableCount = res.data.count;
  641. this.queryParams.pageIndex = res.data.pageIndex;
  642. this.loading = false;
  643. // this.tempList = this.tempList.concat(res.data.list);
  644. if(_this.serveceMapList.length>0){
  645. for(let i=0;i<_this.serveceMapList.length;i++){
  646. this.BizStatsQuery.id = _this.serveceMapList[i].id
  647. listServiceStats(this.BizStatsQuery).then(resq=>{
  648. console.log(resq,'服务解析列表=======')
  649. if(resq.data.id != undefined){
  650. if(_this.serveceMapList[i].id == resq.data.id){
  651. _this.serveceMapList[i] = Object.assign({},_this.serveceMapList[i], resq.data);
  652. _this.isUpdate = !_this.isUpdate;
  653. }
  654. }
  655. })
  656. }
  657. }
  658. this.$nextTick(()=>{
  659. this.$forceUpdate();
  660. })
  661. }
  662. })
  663. },
  664. beforeUpload(file) {
  665. const isRightSize = file.size / 1024 / 1024 < 2
  666. if (!isRightSize) {
  667. this.$message.error('文件大小超过 2MB')
  668. }
  669. return isRightSize
  670. },
  671. uploadSuccess(response, file, fileList) {
  672. this.form.imgUrl = process.env.VUE_APP_BASE_API + response.data.full_path
  673. console.log(response.data.full_path)
  674. },
  675. // 取消按钮
  676. cancel() {
  677. this.open = false
  678. this.reset()
  679. },
  680. // 表单重置
  681. reset() {
  682. this.form = {
  683. id: undefined,
  684. name: undefined,
  685. url: undefined,
  686. type: undefined,
  687. module:undefined,
  688. summary:undefined,
  689. favor:undefined
  690. }
  691. this.resetForm('form')
  692. },
  693. /** 搜索按钮操作 */
  694. handleQuery() {
  695. this.queryParams.pageIndex = 1
  696. this.serveceMapList=[];
  697. this.tempList=[];
  698. this.getList()
  699. // this.getList()
  700. // if(this.queryParams.url != ''){
  701. // this.serveceMapList=this.serveceMapList.map((item)=>{
  702. // if (item.url.includes(this.queryParams.url)){
  703. // return item;
  704. // }
  705. // })
  706. // }
  707. },
  708. /** 重置按钮操作 */
  709. resetQuery() {
  710. this.dateRange = []
  711. this.resetForm('queryForm')
  712. this.handleQuery()
  713. },
  714. // 多选框选中数据
  715. handleSelectionChange(selection) {
  716. this.ids = selection.map(item => item.id)
  717. this.single = selection.length !== 1
  718. this.multiple = !selection.length
  719. },
  720. /** 新增按钮操作 */
  721. handleAdd() {
  722. this.reset()
  723. // this.getMenuTreeselect(0)
  724. this.open = true
  725. this.title = '新增URL组'
  726. this.isEdit = false
  727. },
  728. handleSortChang(column, prop, order) {
  729. prop = column.prop
  730. order = column.order
  731. if (order === 'descending') {
  732. this.queryParams[prop + 'Order'] = 'desc'
  733. } else if (order === 'ascending') {
  734. this.queryParams[prop + 'Order'] = 'asc'
  735. } else {
  736. this.queryParams[prop + 'Order'] = undefined
  737. }
  738. this.getList()
  739. },
  740. /** 修改按钮操作 */
  741. handleUpdate(row) {
  742. this.reset()
  743. const id = row.id || this.ids
  744. if(row.id != 0){
  745. getUrlMapping(id).then(response => {
  746. console.log(response.data,'修改URL组')
  747. this.form = response.data
  748. this.form.id = row.id
  749. this.title = '修改业务名'
  750. this.isEdit = true
  751. this.open = true
  752. })
  753. }else{
  754. this.form = row
  755. // this.form.id = undefined
  756. this.isEdit = true
  757. this.open = true
  758. }
  759. },
  760. /** 提交按钮 */
  761. submitForm: function() {
  762. this.$refs['form'].validate(valid => {
  763. if (valid) {
  764. if (this.form.id !== undefined || this.form.id !=0) {
  765. updateBiz(this.form, this.form.id).then(response => {
  766. if (response.code === 200) {
  767. this.msgSuccess(response.msg)
  768. this.open = false
  769. this.queryParams.pageIndex =1;
  770. this.serveceMapList=[];
  771. this.tempList=[];
  772. this.getList()
  773. } else {
  774. this.msgError(response.msg)
  775. }
  776. })
  777. } else {
  778. addUrlMapping(this.form).then(response => {
  779. if (response.code === 200) {
  780. this.msgSuccess(response.msg)
  781. this.open = false
  782. this.queryParams.pageIndex =1;
  783. this.serveceMapList=[];
  784. this.tempList=[];
  785. this.getList()
  786. } else {
  787. this.msgError(response.msg)
  788. }
  789. })
  790. }
  791. }
  792. })
  793. },
  794. /** 删除按钮操作 */
  795. handleDelete(row) {
  796. // const id = (row.id && [row.id]) || this.ids
  797. const id = row.id
  798. this.$confirm('是否确认删除接口映射编号为"' + id + '"的数据项?', '警告', {
  799. confirmButtonText: '确定',
  800. cancelButtonText: '取消',
  801. type: 'warning'
  802. }).then(function() {
  803. return delBiz(id)
  804. }).then((response) => {
  805. this.queryParams.pageIndex =1;
  806. this.serveceMapList=[];
  807. this.tempList=[];
  808. this.msgSuccess(response.msg);
  809. this.getList()
  810. }).catch(function() {})
  811. },
  812. handleFavor(row){
  813. if(row.favor == 1){
  814. row.favor = '0';
  815. }else if(row.favor==0){
  816. row.favor = '1';
  817. }
  818. favorBiz({favor:row.favor},row.id).then(res=>{
  819. if(res.code = 200){
  820. this.msgSuccess(res.msg);
  821. this.queryParams.pageIndex =1;
  822. this.serveceMapList=[];
  823. this.tempList=[];
  824. this.getList()
  825. }
  826. })
  827. // if(row.id != 0){
  828. // favor({favor:row.favor},row.id).then(res=>{
  829. // if(res.code = 200){
  830. // this.msgSuccess(res.msg);
  831. // this.queryParams.pageIndex =1;
  832. // this.serveceMapList=[];
  833. // this.getList()
  834. // }
  835. // })
  836. // }else{
  837. // this.form = row.favor;
  838. // this.submitForm();
  839. // }
  840. },
  841. /** 导出按钮操作 */
  842. handleExport() {
  843. this.$confirm('是否确认导出所有角色数据项?', '警告', {
  844. confirmButtonText: '确定',
  845. cancelButtonText: '取消',
  846. type: 'warning'
  847. }).then(() => {
  848. this.downloadLoading = true
  849. import('@/vendor/Export2Excel').then(excel => {
  850. const tHeader = ['角色编号', '角色名称', '权限字符', '显示顺序', '状态', '创建时间']
  851. const filterVal = ['roleId', 'roleName', 'roleKey', 'roleSort', 'status', 'createdAt']
  852. const list = this.roleList
  853. const data = formatJson(filterVal, list)
  854. excel.export_json_to_excel({
  855. header: tHeader,
  856. data,
  857. filename: '角色管理',
  858. autoWidth: true, // Optional
  859. bookType: 'xlsx' // Optional
  860. })
  861. this.downloadLoading = false
  862. })
  863. })
  864. },
  865. drawEchartsp5(row, index) {
  866. let option = {
  867. tooltip: {
  868. trigger: 'axis',
  869. confine: false,
  870. appendToBody: true
  871. },
  872. grid: {
  873. top:10,
  874. left: 0,
  875. right: '4%',
  876. bottom: "-2%",
  877. containLabel: false
  878. },
  879. xAxis: {
  880. type: 'category',
  881. boundaryGap: false,
  882. // data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  883. data: row.quantiles.time,
  884. show:false
  885. },
  886. yAxis: {
  887. type: 'value',
  888. show:false
  889. },
  890. series: [
  891. {
  892. name: 'P.50',
  893. type: 'line',
  894. stack: 'Total',
  895. symbol: 'none',
  896. // // data: [120, 132, 101, 134, 90, 230, 210]
  897. // data: Number(row.duration_stats.p50).toFixed(2)
  898. data:row.quantiles.p50
  899. },
  900. {
  901. name: 'P.95',
  902. type: 'line',
  903. stack: 'Total',
  904. symbol: 'none',
  905. // data: [320, 332, 301, 334, 390, 330, 320]
  906. // data: Number(row.duration_stats.p90).toFixed(2)
  907. data:row.quantiles.p90
  908. },
  909. {
  910. name: 'P.99',
  911. type: 'line',
  912. stack: 'Total',
  913. symbol: 'none',
  914. // data: [820, 932, 901, 934, 1290, 1330, 1320]
  915. // data: Number(row.duration_stats.p99).toFixed(2)
  916. data:row.quantiles.p99
  917. }
  918. ]
  919. };
  920. let chartId = 'tiger-' + arguments[2] + '-trend-index' + arguments[1];
  921. this.$nextTick(() => {
  922. let myChart = this.$echarts5.init(document.getElementById(chartId), 'macarons');
  923. myChart.setOption(option);
  924. myChart.resize();
  925. });
  926. },
  927. drawEchartsScale(row) {
  928. console.log(row,'echarts')
  929. let _this = this;
  930. let myChartScale = this.$echarts5.init(document.getElementById('scaleMain'));
  931. //绘制趋势echarts
  932. let option = {
  933. tooltip: {
  934. trigger: 'axis',
  935. confine: false,
  936. appendToBody: true,
  937. show: true,
  938. // formatter: function (params) {
  939. // storage.set('paramsValue',params)
  940. // return params
  941. // },
  942. formatter: function (params) {
  943. storage.set('paramsValue',params)
  944. let axisValueLabel = params[0].axisValueLabel
  945. let str0 = '';
  946. params.forEach((item, idx) => {
  947. // str1+=`${}`
  948. str0 += `${item.marker}${item.seriesName}<span style='margin-left:30px;text-align:right;font-weight:400'>${item.data}</span>`
  949. switch (idx) {
  950. case 0:
  951. str0;
  952. break;
  953. case 1:
  954. str0;
  955. break;
  956. default:
  957. str0
  958. }
  959. str0 += idx === params.length - 1 ? '' : '<br/>'
  960. })
  961. return axisValueLabel+ '<br>' + str0
  962. }
  963. },
  964. grid: {
  965. top:'2%',
  966. left: '3%',
  967. right: '4%',
  968. bottom: '1%',
  969. containLabel: true
  970. },
  971. xAxis: {
  972. type: 'category',
  973. boundaryGap: false,
  974. data: row.quantiles.time,
  975. // show:true
  976. },
  977. yAxis: {
  978. type: 'value',
  979. scale: true,
  980. gridIndex:0,
  981. axisLabel: {
  982. formatter: '{value}'
  983. },
  984. axisLine:{
  985. show:true
  986. },
  987. axisTick:{
  988. show:true
  989. },
  990. splitLine: {
  991. show: true
  992. },
  993. },
  994. series: [
  995. {
  996. name: 'P.50',
  997. type: 'line',
  998. stack: 'Total',
  999. symbol: 'none',
  1000. // symbol: 'emptyCircle',//拐点
  1001. data:row.quantiles.p50
  1002. },
  1003. {
  1004. name: 'P.95',
  1005. type: 'line',
  1006. stack: 'Total',
  1007. symbol: 'none',//拐点
  1008. // symbol: 'emptyCircle',//拐点
  1009. data:row.quantiles.p90
  1010. },
  1011. {
  1012. name: 'P.99',
  1013. type: 'line',
  1014. stack: 'Total',
  1015. symbol: 'none',//拐点
  1016. // symbol: 'emptyCircle',//拐点
  1017. data:row.quantiles.p99
  1018. }
  1019. ]
  1020. };
  1021. // this.$nextTick(() => {
  1022. // let myChartScale = this.$echarts5.init(document.getElementById('scale'));
  1023. myChartScale.setOption(option);
  1024. // myChartScale.resize();
  1025. // });
  1026. // myChartScale.on('click',params=>{
  1027. // console.log(params,'点击折线')
  1028. // })
  1029. myChartScale.getZr().on('click', params => {
  1030. // myChartScale.on('click', params => {
  1031. // console.log(params,'echarts,折线点击')
  1032. // const {target} = params;
  1033. // console.log(target,'target----')
  1034. // if (target && target.z === 3) {
  1035. // const parent = params.target.parent.parent;
  1036. // let seriesIndex = parent.__ecComponentInfo ? parent.__ecComponentInfo.index : parent.parent.__ecComponentInfo.index
  1037. // console.log(seriesIndex);
  1038. // // 获取数据,进行操作
  1039. // }
  1040. let data = storage.get('paramsValue')
  1041. let timestamp = moment(data[0].name).unix();
  1042. _this.childQueryParams.start_time = timestamp
  1043. _this.childQueryParams.end_time =timestamp
  1044. _this.handelgetBizDetail();
  1045. })
  1046. },
  1047. drawEchartsp99() {
  1048. //绘制趋势echarts
  1049. let option = {
  1050. xAxis: {
  1051. type: 'category',
  1052. boundaryGap: false,
  1053. show:false,
  1054. // data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  1055. },
  1056. yAxis: {
  1057. type: 'value',
  1058. show:false,
  1059. },
  1060. series: [
  1061. {
  1062. // data: arguments[0].duration_stats.p99,
  1063. data:[156, 932, 876, 234, 424, 189, 1320],
  1064. type: 'line',
  1065. smooth: true,
  1066. areaStyle: {
  1067. color:'#EAF5F8'
  1068. },
  1069. lineStyle:{
  1070. color:'#7EBBD6'
  1071. },
  1072. symbol: 'none',
  1073. }
  1074. ]
  1075. };
  1076. let chartId = 'tiger-' + arguments[2] + '-trend-index' + arguments[1];
  1077. this.$nextTick(() => {
  1078. let myChart = this.$echarts5.init(document.getElementById(chartId), 'macarons');
  1079. myChart.setOption(option);
  1080. myChart.resize();
  1081. });
  1082. },
  1083. }
  1084. }
  1085. </script>
  1086. <style lang="scss" scoped>
  1087. @font-face {
  1088. font-family: 'iconfont'; /* Project id 4241886 */
  1089. src: url('https://at.alicdn.com/t/c/font_4241886_44emlzl9ad4.woff2?t=1694073615647') format('woff2'),
  1090. url('https://at.alicdn.com/t/c/font_4241886_44emlzl9ad4.woff?t=1694073615647') format('woff'),
  1091. url('https://at.alicdn.com/t/c/font_4241886_44emlzl9ad4.ttf?t=1694073615647') format('truetype');
  1092. }
  1093. .iconfont {
  1094. font-family: "iconfont" !important;
  1095. font-size: 20px;
  1096. font-style: normal;
  1097. -webkit-font-smoothing: antialiased;
  1098. -moz-osx-font-smoothing: grayscale;
  1099. }
  1100. .icon-jiantou_zuoyouqiehuan:before {
  1101. content: "\eb0d";
  1102. }
  1103. .icon-qiehuan:before {
  1104. content: "\e606";
  1105. }
  1106. .icon-qiehuan1:before {
  1107. content: "\e789";
  1108. }
  1109. ::v-deep .el-card__body{
  1110. position: relative !important;
  1111. }
  1112. .back{
  1113. position: absolute;
  1114. top:10px;
  1115. right:10px;
  1116. width:20px;
  1117. height:20px;
  1118. }
  1119. .tiger-trend-charts {
  1120. height: 40px;
  1121. min-width: 140px;
  1122. }
  1123. ::v-deep .el-icon-star-on{
  1124. font-size: 16px;
  1125. }
  1126. .more{
  1127. margin:16px 0;
  1128. text-align: center;
  1129. font-size: 16px;
  1130. color:#1890ff;
  1131. cursor: pointer;
  1132. }
  1133. .nomore{
  1134. color:#999;
  1135. }
  1136. .green_small{
  1137. display: inline-block;
  1138. width:8px;
  1139. height: 8px;
  1140. border-radius: 50%;
  1141. background:#67C23A;
  1142. margin-right:8px;
  1143. }
  1144. .red_small{
  1145. width:8px;
  1146. height: 8px;
  1147. display: inline-block;
  1148. border-radius: 50%;
  1149. background:#F56C6C;
  1150. margin-right:8px;
  1151. }
  1152. .flexWrap{
  1153. display:flex;
  1154. justify-content: space-between;
  1155. }
  1156. .flex-left{
  1157. width:60%;
  1158. }
  1159. .flex-right{
  1160. width:39.99
  1161. }
  1162. </style>