StatisticalAnalysis.vue 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. <template>
  2. <div class="mainBox" v-loading="getDataStatus">
  3. <!-- 搜索区域 -->
  4. <div class="searchBox">
  5. <div style="font-size: large">
  6. <!-- 对比时间:
  7. <el-date-picker
  8. v-model="lastTimes"
  9. type="daterange"
  10. unlink-panels
  11. range-separator="到"
  12. start-placeholder="开始时间"
  13. end-placeholder="结束时间"
  14. disabled
  15. size="large"
  16. style="margin-right: 30px"
  17. /> -->
  18. 搜索时间:
  19. <el-date-picker
  20. v-model="nowTimes"
  21. type="daterange"
  22. :clearable="false"
  23. unlink-panels
  24. range-separator="到"
  25. start-placeholder="开始时间"
  26. end-placeholder="结束时间"
  27. :shortcuts="shortcutsFun"
  28. size="large"
  29. />
  30. </div>
  31. </div>
  32. <!-- 服务调用card -->
  33. <div class="flex">
  34. <card
  35. v-for="item in TopCardDatas"
  36. :key="item.name"
  37. class="card flex"
  38. :title="item.name"
  39. :value="item.value"
  40. :growth="item.growth"
  41. :iconName="item.iconName"
  42. :iconColor="item.iconColor"
  43. :upStatus="item.upStatus"
  44. />
  45. </div>
  46. <!-- 服务类信息统计 -->
  47. <div class="bigCard">
  48. <div class="bigCard_title">服务类信息统计</div>
  49. <div class="tools">
  50. <el-button @click="downFileAllServiceDatas" :disabled="exportLoading">
  51. <!-- 这个要查询查询时间范围内的所有服务数据 -->
  52. <el-icon v-if="!exportLoading"><Upload /></el-icon>
  53. <el-icon v-else><Loading class="loading-icon" /></el-icon>
  54. 导出数据
  55. </el-button>
  56. <el-button :disabled="dialogLoading" type="primary" @click="showDetailReport">
  57. <!-- 这个直接弹窗展示所有的服务调用数据,而不是下载 -->
  58. <el-icon v-if="!dialogLoading"><TrendCharts /></el-icon>
  59. <!-- 让图标旋转 -->
  60. <el-icon v-else><Loading class="loading-icon" /></el-icon>
  61. 详细报告
  62. </el-button>
  63. </div>
  64. <div class="flex">
  65. <div style="width: 48%; height: 400px">
  66. <EchartsDome :chartOption="chartOptions['服务调用趋势']" title="服务调用趋势" />
  67. </div>
  68. <div style="width: 48%; height: 400px">
  69. <EchartsDome title="服务类别分布" :chartOption="chartOptions['服务类别分布']" />
  70. </div>
  71. </div>
  72. <div style="width: 100%; height: 400px">
  73. <Table title="服务调用TOP10" :tableData="tableDatas" />
  74. </div>
  75. </div>
  76. <!-- 服务调用详情弹窗 -->
  77. <el-dialog
  78. v-model="showServerDetailReport"
  79. width="80%"
  80. :close-on-click-modal="false"
  81. :close-on-press-escape="false"
  82. :show-close="true"
  83. title="服务调用详情"
  84. >
  85. <div style="width: 100%; position: relative; padding-bottom: 30px" v-loading="dialogLoading">
  86. <!-- 过滤条件,全字段(除了调用时间)下拉框选择 -->
  87. <div style="width: 100%; margin-bottom: 10px; z-index: 1000">
  88. <!-- 再添加一个调用次数筛选 -->
  89. <div style="display: flex; align-items: center; margin-bottom: 10px">
  90. <span style="margin-right: 10px">调用次数:</span>
  91. <el-slider
  92. v-model="selectedModel['count']"
  93. :min="countMin"
  94. :max="countMax"
  95. range
  96. @change="changeSelectModel"
  97. style="width: calc(60%); margin: 0 20px"
  98. />
  99. 最小值:
  100. <el-input-number
  101. v-model="selectedModel.count[0]"
  102. :min="countMin"
  103. :max="selectedModel.count[1]"
  104. @change="changeSelectModel"
  105. />
  106. 最大值:
  107. <el-input-number
  108. v-model="selectedModel.count[1]"
  109. :min="selectedModel.count[0]"
  110. :max="countMax"
  111. @change="changeSelectModel"
  112. />
  113. </div>
  114. <!-- 支持筛选条件清空 -->
  115. <el-select
  116. style="width: 200px; margin-right: 10px"
  117. v-for="(value, key, index) in selectOptions"
  118. :key="key + index"
  119. v-model="selectedModel[key]"
  120. @change="changeSelectModel"
  121. :placeholder="'请选择' + selectKeyName[key]"
  122. filterable
  123. clearable
  124. >
  125. <el-option v-for="item in value" :key="item" :label="item" :value="item" />
  126. </el-select>
  127. <!-- 再添加一个时间搜索范围,字段是date, 格式是YYYY-MM-DD -->
  128. <el-date-picker
  129. style="margin-right: 10px"
  130. v-model="selectedModel['date']"
  131. type="daterange"
  132. clearable
  133. unlink-panels
  134. @change="changeSelectModel"
  135. :disabled-date="disabledDate"
  136. range-separator="到"
  137. start-placeholder="开始时间"
  138. end-placeholder="结束时间"
  139. />
  140. <!-- 重置按钮 -->
  141. <el-button type="primary" @click="resetSelectModel">重置</el-button>
  142. </div>
  143. <el-table
  144. :data="showServerData"
  145. style="width: 100%"
  146. height="500px"
  147. border
  148. fit
  149. highlight-current-row
  150. >
  151. <el-table-column prop="application" label="应用名称" />
  152. <el-table-column prop="path_comment" label="服务名称" />
  153. <el-table-column prop="unit" label="委办单位" width="200" />
  154. <el-table-column label="服务类别" width="120">
  155. <template #default="{ row }">
  156. {{ formatYxglServiceCategory(row.type, row.type) }}
  157. </template>
  158. </el-table-column>
  159. <el-table-column prop="count" label="调用次数" width="100" />
  160. <el-table-column prop="date" label="调用时间" width="120" />
  161. </el-table>
  162. <div style="float: right; padding-top: 10px">
  163. <el-pagination
  164. v-model:current-page="currentPage"
  165. v-model:page-size="pageSize"
  166. :page-sizes="[50, 100, 200, 500]"
  167. background
  168. layout="total, sizes, prev, pager, next, jumper"
  169. :total="tableDataTotal"
  170. @size-change="handleSizeChange"
  171. @current-change="handleCurrentChange"
  172. />
  173. </div>
  174. </div>
  175. </el-dialog>
  176. <!-- 委办信息统计 -->
  177. <div class="bigCard">
  178. <div class="bigCard_title">委办信息统计</div>
  179. <div class="flex">
  180. <div style="width: 28%; height: 400px">
  181. <EchartsDome :chartOption="chartOptions['委办分布']" title="委办分布" />
  182. </div>
  183. <div style="width: 68%; height: 400px">
  184. <EchartsDome :chartOption="chartOptions['委办活跃度趋势']" title="委办活跃度趋势" />
  185. </div>
  186. </div>
  187. <!-- <div style="width: 100%; height: 400px">
  188. <EchartsDome :chartOption="chartOptions['用户部门分布']" title="用户部门分布" />
  189. </div> -->
  190. </div>
  191. <!-- 应用类信息统计 -->
  192. <div class="bigCard">
  193. <div class="bigCard_title">应用类信息统计</div>
  194. <div class="flex">
  195. <div style="width: 48%; height: 400px">
  196. <EchartsDome :chartOption="chartOptions['热点应用TOP10排名']" title="热点应用TOP10排名" />
  197. </div>
  198. <!-- <div style="width: 33%; height: 400px">
  199. <EchartsDome :chartOption="chartOptions['栏目统计分布']" title="栏目统计分布" />
  200. </div> -->
  201. <div style="width: 48%; height: 400px">
  202. <EchartsDome :chartOption="chartOptions['应用状态分布']" title="应用状态分布" />
  203. </div>
  204. </div>
  205. </div>
  206. <!-- 数据类信息统计 -->
  207. <div class="bigCard">
  208. <div class="bigCard_title">数据类信息统计</div>
  209. <div class="flex">
  210. <div style="width: 50%; height: 400px">
  211. <EchartsDome :chartOption="chartOptions['数据类别分布']" title="数据类别分布" />
  212. </div>
  213. <div style="width: 50%; height: 400px">
  214. <EchartsDome :chartOption="chartOptions['数据质量评分']" title="数据质量评分" />
  215. </div>
  216. </div>
  217. </div>
  218. <!-- 验收版-隐藏 演示版-显示 现已全显示 -->
  219. <HomePage_Demo />
  220. <!-- 区级特色信息统计 -->
  221. <!-- <div class="bigCard">
  222. <div class="bigCard_title">区级特色信息统计</div>
  223. <div class="flex" style="margin-top: 20px">
  224. <div class="flex_column" style="width: 28%; height: 420px">
  225. <card
  226. class="card2 flex"
  227. :title="'服务机构总数'"
  228. value="8"
  229. :growth="'较上个月增长了12%'"
  230. iconColor="#2563db"
  231. :upStatus="1"
  232. />
  233. <card
  234. class="card2 flex"
  235. :title="'服务总数'"
  236. value="100"
  237. :growth="'较上个月下降了12%'"
  238. iconColor="#16a34a"
  239. :upStatus="-1"
  240. />
  241. <card
  242. class="card2 flex"
  243. :title="'服务调用总次数'"
  244. value="1000"
  245. :growth="'较上个月增长了12%'"
  246. iconColor="#9333ea"
  247. :upStatus="1"
  248. />
  249. </div>
  250. <div style="width: 68%; height: 420px">
  251. <EchartsDome :chartOption="chartOptions['服务调用趋势']" title="服务调用趋势" />
  252. </div>
  253. </div>
  254. </div> -->
  255. </div>
  256. </template>
  257. <script>
  258. import card from "@/components/yxgl/card.vue";
  259. import EchartsDome from "@/components/yxgl/EchartsDome.vue";
  260. import Table from "@/components/yxgl/table.vue";
  261. import appCenter from "@/api/appCenter";
  262. import { countlmType } from "@/api/count";
  263. import { formatYxglServiceCategory } from "@/utils/yxglServiceCategoryLabels.js";
  264. import HomePage_Demo from "@/views/HomePage_Demo.vue";
  265. export default {
  266. name: "",
  267. components: {
  268. card,
  269. EchartsDome,
  270. Table,
  271. HomePage_Demo,
  272. },
  273. data() {
  274. return {
  275. // 导出Loading状态
  276. exportLoading: false,
  277. // 弹窗加载状态
  278. dialogLoading: false,
  279. // 获取数据状态
  280. getDataStatus: false,
  281. // 比较的时间范围(默认60天到30天前,主要跟nowTimes有关系)
  282. lastTimes: [],
  283. // 当前选中的时间范围
  284. nowTimes: [],
  285. // 服务调用详情弹窗是否显示
  286. showServerDetailReport: false,
  287. // 服务调用详情数据
  288. serverData: [],
  289. // 展示的详细数据(能根据时间和字段模糊筛选)
  290. showServerData: [],
  291. // 筛选条件下拉框选项
  292. selectOptions: {
  293. application: [],
  294. path_comment: [],
  295. unit: [],
  296. type: [],
  297. },
  298. selectKeyName: {
  299. application: "应用名称",
  300. path_comment: "服务名称",
  301. unit: "委办单位",
  302. count: "调用次数",
  303. type: "服务类型",
  304. },
  305. // 筛选条件下拉框选中的值
  306. selectedModel: {
  307. application: "",
  308. path_comment: "",
  309. unit: "",
  310. type: "",
  311. count: "",
  312. date: "",
  313. },
  314. // 调用次数范围的最小值和最大值
  315. countMin: 0,
  316. countMax: 0,
  317. tableColumns: ["application", "path_comment", "unit", "count", "type", "date"],
  318. // 分页大小
  319. pageSize: 50,
  320. // 当前页码
  321. currentPage: 1,
  322. // 表格数据总条数
  323. tableDataTotal: 0,
  324. TopCardDatas: [
  325. {
  326. name: "委办总数",
  327. value: "0",
  328. growth: "--",
  329. iconColor: "#2563db",
  330. iconName: "OfficeBuilding",
  331. upStatus: 0,
  332. },
  333. {
  334. name: "系统总数",
  335. value: "0",
  336. growth: "--",
  337. iconColor: "#16a34a",
  338. iconName: "WalletFilled",
  339. upStatus: 0,
  340. },
  341. {
  342. name: "服务总数",
  343. value: "0",
  344. growth: "--",
  345. iconColor: "#9333ea",
  346. iconName: "CollectionTag",
  347. upStatus: 0,
  348. },
  349. {
  350. name: "服务调用总数",
  351. value: "0",
  352. growth: "--",
  353. iconColor: "#ca8a04",
  354. iconName: "Paperclip",
  355. upStatus: 0,
  356. },
  357. ],
  358. shortcutsFun: this.shortcuts(),
  359. chartOptions: {},
  360. tableDatas: [],
  361. columnList: [],
  362. };
  363. },
  364. watch: {
  365. nowTimes: {
  366. handler(newVal, oldVal) {
  367. if (newVal !== oldVal && newVal.length > 0) {
  368. // 计算出比较的时间范围
  369. this.lastTimes = [
  370. this.$moment(
  371. new Date(
  372. new Date(newVal[0]).setTime(
  373. new Date(newVal[0]).getTime() - (newVal[1] - newVal[0]) - 24 * 60 * 60 * 1000
  374. )
  375. )
  376. ).format("YYYY-MM-DD 00:00:00"),
  377. this.$moment(new Date(newVal[0])).format("YYYY-MM-DD 00:00:00"),
  378. ];
  379. // this.getColumnListDate();
  380. this.initChart();
  381. // 栏目统计分布
  382. this.getColumnCount();
  383. }
  384. },
  385. deep: true,
  386. // immediate: true,
  387. },
  388. },
  389. mounted() {
  390. this.$nextTick(() => {
  391. this.nowTimes = [
  392. new Date(new Date().setTime(new Date() - 3600 * 1000 * 24 * 30)),
  393. new Date(),
  394. ];
  395. });
  396. },
  397. methods: {
  398. formatYxglServiceCategory,
  399. getColumnListDate(){
  400. let that = this;
  401. that.columnList = [];
  402. appCenter.getColumnListDate().then((res) => {
  403. // console.log("====getColumnListDate====="+res);
  404. if (res.code === 200) {
  405. // this.serverData = res.content;
  406. res.content.forEach(e => {
  407. that.getChildren(e)
  408. });
  409. }
  410. });
  411. },
  412. //递归查找子目录
  413. getChildren(e){
  414. let that = this;
  415. if(e.columnList.length>0){
  416. e.value=e.id
  417. e.label=e.title
  418. e.children = e.columnList;
  419. e.columnList.forEach(e2 => {
  420. that.getChildren(e2)
  421. });
  422. }else{
  423. e.value=e.modelId
  424. e.label=e.title
  425. e.children = []
  426. let str = {id:e.id,mid:e.modelId,title:e.title}
  427. that.columnList.push(str);
  428. }
  429. },
  430. // 禁用日期选择器中未来的日期(不能选择nowTimes以外的时间)
  431. disabledDate(time) {
  432. return (
  433. time.getTime() > this.nowTimes[1].getTime() ||
  434. time.getTime() < this.nowTimes[0].getTime() - 24 * 60 * 60 * 1000
  435. );
  436. },
  437. // 分页大小改变时,更新展示数据
  438. handleSizeChange() {
  439. this.dialogLoading = true;
  440. let searchDatas = this.serverData.filter((item) => {
  441. return (
  442. (!this.selectedModel.application ||
  443. item.application === this.selectedModel.application) &&
  444. (!this.selectedModel.path_comment ||
  445. item.path_comment === this.selectedModel.path_comment) &&
  446. (!this.selectedModel.unit || item.unit === this.selectedModel.unit) &&
  447. (!this.selectedModel.type || item.type === this.selectedModel.type)
  448. );
  449. });
  450. // 添加时间范围搜索逻辑
  451. if (this.selectedModel.date) {
  452. searchDatas = searchDatas.filter((item) => {
  453. return (
  454. new Date(item.date).getTime() >=
  455. new Date(
  456. this.$moment(new Date(this.selectedModel.date[0])).format("YYYY-MM-DD 00:00:00")
  457. ).getTime() &&
  458. new Date(item.date).getTime() <=
  459. new Date(
  460. this.$moment(new Date(this.selectedModel.date[1])).format("YYYY-MM-DD 23:59:59")
  461. ).getTime()
  462. );
  463. });
  464. }
  465. // 添加调用次数范围搜索逻辑
  466. if (this.selectedModel.count) {
  467. searchDatas = searchDatas.filter((item) => {
  468. return (
  469. item.count >= this.selectedModel.count[0] && item.count <= this.selectedModel.count[1]
  470. );
  471. });
  472. }
  473. // 格式化日期为YYYY-MM-DD格式
  474. searchDatas.forEach((item) => {
  475. item.date = this.$moment(new Date(item.date)).format("YYYY-MM-DD");
  476. });
  477. // 分页展示数据
  478. this.showServerData = searchDatas.slice(
  479. (this.currentPage - 1) * this.pageSize,
  480. this.currentPage * this.pageSize
  481. );
  482. // 遍历筛选条件下拉框选项,更新每个选项的数组
  483. for (let key in this.selectOptions) {
  484. this.selectOptions[key] = [
  485. ...new Set(searchDatas.map((item) => (item[key] ? item[key] : "未知"))),
  486. ];
  487. }
  488. // 更新分页组件数据
  489. this.tableDataTotal = searchDatas.length;
  490. this.showServerDetailReport = true;
  491. this.dialogLoading = false;
  492. },
  493. handleCurrentChange() {
  494. this.handleSizeChange();
  495. },
  496. // 筛选条件下拉框选中值改变时,触发的事件
  497. changeSelectModel() {
  498. // console.log(this.selectedModel);
  499. // 重置当前页码为第一页
  500. this.currentPage = 1;
  501. // 处理分页数据
  502. this.handleSizeChange();
  503. },
  504. // 重置筛选条件下拉框选中的值
  505. resetSelectModel() {
  506. for (let key in this.selectedModel) {
  507. this.selectedModel[key] = "";
  508. }
  509. this.selectedModel.count = [this.countMin, this.countMax];
  510. this.handleSizeChange();
  511. },
  512. // 打开弹窗,渲染table列表,展示查询时间的所有数据详情
  513. showDetailReport() {
  514. this.dialogLoading = true;
  515. // 要先请求,然后时间排序一下。
  516. appCenter
  517. .getServiceDataByDate({
  518. nowTimes: [
  519. this.$moment(new Date(this.nowTimes[0])).format("YYYY-MM-DD 00:00:00"),
  520. this.$moment(new Date(this.nowTimes[1])).format("YYYY-MM-DD 23:59:59"),
  521. ],
  522. })
  523. .then((res) => {
  524. if (res && res.code == 200) {
  525. if (res.content && res.content.length > 0) {
  526. res.content.forEach((item) => {
  527. this.tableColumns.forEach((key) => {
  528. if (item[key] == undefined) {
  529. item[key] = "未知";
  530. }
  531. });
  532. });
  533. this.serverData = res.content;
  534. // 得到count的最大值和最小值
  535. this.countMax = Math.max(...this.serverData.map((item) => item.count));
  536. this.countMin = Math.min(...this.serverData.map((item) => item.count));
  537. this.resetSelectModel();
  538. this.handleSizeChange();
  539. } else {
  540. this.$message({
  541. message: "暂无数据",
  542. type: "warning",
  543. });
  544. }
  545. }
  546. })
  547. .catch((e) => {
  548. this.$message({
  549. message: e,
  550. type: "error",
  551. });
  552. })
  553. .finally(() => {
  554. this.dialogLoading = false;
  555. });
  556. },
  557. initChart() {
  558. this.getDataStatus = true;
  559. // 获取运行管理页面数据
  560. appCenter
  561. .getAllYxglDatas({
  562. nowTimes: [
  563. this.$moment(new Date(this.nowTimes[0])).format("YYYY-MM-DD 00:00:00"),
  564. this.$moment(new Date(this.nowTimes[1])).format("YYYY-MM-DD 23:59:59"),
  565. ],
  566. lastTimes: this.lastTimes,
  567. })
  568. .then((res) => {
  569. if (res && res.code == 200) {
  570. if (res.content.TopCardDatas && typeof res.content.TopCardDatas === "object") {
  571. // 不能覆盖iconName
  572. this.TopCardDatas.forEach((cardItem) => {
  573. res.content.TopCardDatas.forEach((cardData) => {
  574. if (cardItem.name == cardData.name) {
  575. cardItem.value = cardData.value;
  576. cardItem.growth = cardData.growth;
  577. cardItem.upStatus = cardData.upStatus;
  578. }
  579. });
  580. });
  581. }
  582. this.dataToOption("服务调用趋势", "line", [...res.content.serviceCountTrend], {
  583. legend: { data: ["调用次数"] },
  584. xData: [],
  585. xKey: "date",
  586. xFormart: "YYYY-MM-DD",
  587. yAxis: {
  588. type: "value",
  589. name: "调用次数",
  590. axisLine: { lineStyle: { color: "#42a5f5" } }, // 区分样式
  591. },
  592. yData: {
  593. key: "count",
  594. name: "调用次数",
  595. color: "#42a5f5",
  596. data: [],
  597. yAxisIndex: 0,
  598. },
  599. });
  600. let serviceCountType = [];
  601. serviceCountType = res.content.serviceCountType;
  602. if (res.content.serviceCountType && res.content.serviceCountType.length > 0) {
  603. serviceCountType.forEach((item) => {
  604. if (!item.type) {
  605. item.type = "未知";
  606. }
  607. });
  608. }
  609. serviceCountType.forEach((item) => {
  610. item.name = formatYxglServiceCategory(item.type);
  611. });
  612. // 初始化服务类别分布,这个地方需要先根据serviceType进行groupBy统计调用次数
  613. this.dataToOption("服务类别分布", "pie", serviceCountType, {
  614. pieKey: { value: "count", name: "name" },
  615. pieData: [],
  616. padAngle: 0,
  617. borderRadius: 0,
  618. label: {
  619. textStyle: {
  620. textShadow: 'none',
  621. color: '#fff'
  622. }
  623. },
  624. });
  625. // 服务调用TOP10
  626. let serviceDatas = [];
  627. serviceDatas = res.content.serviceCountTop;
  628. if (serviceDatas && serviceDatas.length > 0) {
  629. serviceDatas.forEach((item) => {
  630. if (!item.path_comment) {
  631. item.path_comment = "未知";
  632. }
  633. if (!item.type) {
  634. item.type = "未知";
  635. }
  636. });
  637. }
  638. // 排序
  639. serviceDatas.sort((a, b) => b.count - a.count);
  640. this.initTableDatas(serviceDatas.slice(0, 10));
  641. let serviceCountUnit = [];
  642. serviceCountUnit = res.content.serviceCountUnit;
  643. if (serviceCountUnit && serviceCountUnit.length > 0) {
  644. serviceCountUnit.forEach((item) => {
  645. if (!item.unit) {
  646. item.unit = "未知";
  647. }
  648. });
  649. }
  650. // 委办分布
  651. this.dataToOption("委办分布", "pie", serviceCountUnit, {
  652. pieKey: { value: "count", name: "unit" },
  653. pieData: [],
  654. legend: {
  655. show: false,
  656. bottom: 10,
  657. },
  658. radius: "60%",
  659. padAngle: 0,
  660. borderRadius: 0,
  661. label: {
  662. textStyle: {
  663. textShadow: 'none',
  664. color: '#fff'
  665. }
  666. },
  667. });
  668. // 先同步一下legend
  669. let serviceCountUnitTrendLegend = [];
  670. serviceCountUnit.forEach((item) => {
  671. serviceCountUnitTrendLegend.push(item.unit);
  672. });
  673. this.dataToOption("委办活跃度趋势", "line", [...res.content.serviceCountUnitTrend], {
  674. legend: { data: serviceCountUnitTrendLegend },
  675. xData: [],
  676. xKey: "date",
  677. xFormart: "YYYY-MM-DD",
  678. yAxis: {
  679. type: "value",
  680. axisLine: { lineStyle: { color: "#fff" } }, // 区分样式
  681. },
  682. yDatas: { auto: true },
  683. });
  684. let serviceDatas2 = [];
  685. serviceDatas2 = res.content.serviceCountApplicationTop;
  686. // 排序
  687. serviceDatas2.sort((a, b) => b.count - a.count);
  688. let forData = serviceDatas2.slice(0, 10);
  689. let showApplicationTopDatas = [];
  690. for (let i = forData.length - 1; i >= 0; i--) {
  691. showApplicationTopDatas.push(forData[i]);
  692. }
  693. // 用户部门分布
  694. this.dataToOption("热点应用TOP10排名", "bar", showApplicationTopDatas, {
  695. showLegend: false,
  696. xData: [],
  697. xKey: "application",
  698. yData: {
  699. key: "count",
  700. name: "调用次数",
  701. color: "#42a5f5",
  702. data: [],
  703. },
  704. });
  705. // 数据类别分布
  706. if (res.content.dataTypes && res.content.dataTypes.length > 0) {
  707. this.dataToOption("数据类别分布", "pie", [...res.content.dataTypes], {
  708. pieKey: { value: "count", name: "service_name" },
  709. pieData: [],
  710. legend: {
  711. bottom: 10,
  712. },
  713. radius: "60%",
  714. padAngle: 0,
  715. borderRadius: 0,
  716. label: {
  717. textStyle: {
  718. textShadow: 'none',
  719. color: '#fff'
  720. }
  721. },
  722. });
  723. }
  724. // console.log("getAllYxglDatas", res);
  725. }
  726. this.getDataStatus = false;
  727. })
  728. .catch((error) => {
  729. this.getDataStatus = false;
  730. this.$message({
  731. type: "error",
  732. message: "服务器忙碌,请稍后重试!",
  733. });
  734. });
  735. // 应用状态分布,cloumnId:1659
  736. appCenter
  737. .getDmsDataList({
  738. columnId: systemConfig.columnIds[1],
  739. pageSize: 1000,
  740. page: 0,
  741. })
  742. .then((res) => {
  743. if (res.code == 200) {
  744. let dmsDatas = res.content.data;
  745. let tableDatas = [];
  746. // 根据状态进行groupBy统计个数
  747. let statusMap = {};
  748. dmsDatas.forEach((item) => {
  749. if (statusMap[item.appstauts + ""]) {
  750. statusMap[item.appstauts + ""] += 1;
  751. } else {
  752. statusMap[item.appstauts + ""] = 1;
  753. }
  754. });
  755. // 转换为数组
  756. for (let key in statusMap) {
  757. if (statusMap[key] && key) {
  758. tableDatas.push({
  759. name: this.$getDmsTypes("appstatus", key),
  760. value: statusMap[key],
  761. });
  762. }
  763. }
  764. this.dataToOption("应用状态分布", "pie", tableDatas, {
  765. pieKey: { value: "value", name: "name" },
  766. pieData: [],
  767. });
  768. } else {
  769. this.$message({
  770. type: "error",
  771. message: "服务器忙碌,请稍后重试!",
  772. });
  773. }
  774. });
  775. this.dataToOption("数据质量评分", "radar", null, null);
  776. },
  777. getColumnCount() {
  778. let param = {
  779. start: this.$moment(new Date(this.nowTimes[0])).format("YYYY-MM-DD"),
  780. end: this.$moment(new Date(this.nowTimes[1])).format("YYYY-MM-DD"),
  781. };
  782. countlmType(param.start, param.end).then((res) => {
  783. if (res.length > 0) {
  784. this.dataToOption("栏目统计分布", "pie", [...res], {
  785. pieKey: { value: "count", name: "column" },
  786. pieData: [],
  787. legend: {
  788. type: 'scroll',
  789. bottom: 0,
  790. // orient: 'vertical', // 水平布局 horizontal 垂直 vertical
  791. pageButtonPosition: 'end', // 翻页按钮放右侧
  792. pageIconColor: '#fff',
  793. pageIconInactiveColor: '#999',
  794. itemWidth: 10, // 图例标记宽度
  795. itemHeight: 10, // 图例标记高度
  796. pageTextStyle: {
  797. color: '#fff'
  798. },
  799. pageIconSize: 12 // 翻页图标大小
  800. },
  801. radius: "60%",
  802. padAngle: 0,
  803. borderRadius: 0,
  804. label: {
  805. textStyle: {
  806. textShadow: 'none',
  807. color: '#fff'
  808. }
  809. },
  810. });
  811. }
  812. })
  813. },
  814. // 导出所选时间范围内的所有服务数据
  815. downFileAllServiceDatas() {
  816. this.exportLoading = true;
  817. appCenter
  818. .downFileAllServiceDatas({ nowTimes: this.nowTimes })
  819. .then((res) => {
  820. const blob = res; // 响应体是 Blob 类型
  821. if (!blob) {
  822. that.$message.error("下载失败:文件流为空");
  823. reject("文件流为空");
  824. return;
  825. }
  826. if (!(blob instanceof Blob) || blob.size === 0) {
  827. this.$message.error("文件流解析失败,无法生成下载链接");
  828. return;
  829. }
  830. const url = window.URL.createObjectURL(blob); // 将 Blob 转为临时 URL
  831. const link = document.createElement("a");
  832. link.href = url;
  833. link.download = "服务信息统计.xlsx"; // 设置下载文件名
  834. link.style.display = "none";
  835. document.body.appendChild(link);
  836. link.click(); // 触发点击下载
  837. document.body.removeChild(link);
  838. window.URL.revokeObjectURL(url); // 销毁临时 URL,避免内存泄漏
  839. })
  840. .catch((e) => {
  841. this.$message({
  842. message: "导出数据失败",
  843. type: "error",
  844. });
  845. })
  846. .finally(() => {
  847. this.exportLoading = false;
  848. });
  849. },
  850. /**
  851. * 数据转换为图表选项
  852. * @param title 图表标题
  853. * @param type 图表类型
  854. * @param datas 原始数据
  855. * @param keyRule 解析规则
  856. */
  857. async dataToOption(title, type, datas, keyRule) {
  858. // 根据规则解析数据
  859. if (keyRule) {
  860. datas.forEach((item) => {
  861. // 有的图表没有X轴
  862. if (keyRule.xKey) {
  863. if (keyRule.xFormart) {
  864. item[keyRule.xKey] = this.$moment(item[keyRule.xKey]).format(keyRule.xFormart);
  865. }
  866. keyRule.xData.push(item[keyRule.xKey]);
  867. }
  868. if (keyRule.yData) {
  869. keyRule.yData.data.push(item[keyRule.yData.key]);
  870. keyRule.series = [
  871. {
  872. name: keyRule.yData.name,
  873. type: "line",
  874. smooth: true,
  875. data: keyRule.yData.data,
  876. lineStyle: {
  877. color: keyRule.yData.color ? keyRule.yData.color : "",
  878. type: keyRule.yData.ifDashed ? "dashed" : "",
  879. }, // 蓝色线条
  880. itemStyle: keyRule.yData.color,
  881. symbol: "circle", // 节点形状
  882. symbolSize: 6, // 节点大小
  883. },
  884. ];
  885. }
  886. if (keyRule.yDatas) {
  887. // 先根据lenged得到data集合
  888. for (let name of keyRule.legend.data) {
  889. if (keyRule.yDatas[name]) {
  890. keyRule.yDatas[name].data.push(item[name]);
  891. } else {
  892. keyRule.yDatas[name] = {
  893. name: name,
  894. data: [item[name]],
  895. };
  896. }
  897. }
  898. }
  899. if (keyRule.pieKey) {
  900. keyRule.pieData.push({
  901. value: item[keyRule.pieKey.value],
  902. name: item[keyRule.pieKey.name],
  903. });
  904. }
  905. });
  906. // 专门用来处理多y数据的series
  907. if (keyRule.yDatas) {
  908. keyRule.series = [];
  909. for (let name of keyRule.legend.data) {
  910. keyRule.series.push({
  911. name: name,
  912. type: "line",
  913. smooth: true,
  914. data: keyRule.yDatas[name].data,
  915. symbol: "circle", // 节点形状
  916. symbolSize: 6, // 节点大小
  917. });
  918. }
  919. }
  920. }
  921. let _option = {};
  922. switch (type) {
  923. case "line":
  924. // 折线图基础
  925. _option = {
  926. legend: {
  927. data: keyRule.legend.data,
  928. // type: 'scroll',
  929. // top: 0,
  930. // // orient: 'vertical', // 水平布局 horizontal 垂直 vertical
  931. // pageButtonPosition: 'end', // 翻页按钮放右侧
  932. // pageIconColor: '#fff',
  933. // pageIconInactiveColor: '#999',
  934. // itemWidth: 10, // 图例标记宽度
  935. // itemHeight: 10, // 图例标记高度
  936. // pageTextStyle: {
  937. // color: '#fff'
  938. // },
  939. // pageIconSize: 12 // 翻页图标大小
  940. },
  941. tooltip: {
  942. show: true,
  943. trigger: "axis",
  944. axisPointer: { type: "shadow" },
  945. },
  946. // 默认样式
  947. xAxis: {
  948. type: "category",
  949. data: keyRule.xData,
  950. axisTick: { show: false }, // 隐藏刻度
  951. splitLine: { show: false }, // 隐藏分割线
  952. axisLabel: {
  953. color: "#fff", // 字体颜色(支持十六进制、RGB、颜色名)
  954. fontSize: 14, // 可选:字体大小
  955. fontWeight: "normal", // 可选:字体粗细
  956. textShadow: 'none'
  957. },
  958. },
  959. yAxis: {
  960. type: "value",
  961. axisLabel: {
  962. color: "#fff", // 字体颜色(支持十六进制、RGB、颜色名)
  963. fontSize: 14, // 可选:字体大小
  964. fontWeight: "normal", // 可选:字体粗细
  965. textShadow: 'none'
  966. },
  967. splitLine: {
  968. lineStyle: {
  969. type: "dashed", // 虚线网格
  970. color: "#113761",
  971. },
  972. },
  973. },
  974. series: keyRule.series,
  975. };
  976. break;
  977. case "pie":
  978. // 饼状图
  979. _option = {
  980. tooltip: {
  981. trigger: "item",
  982. },
  983. legend: keyRule.legend
  984. ? keyRule.legend
  985. : {
  986. orient: "vertical",
  987. top: "50%",
  988. right: 10,
  989. },
  990. series: [
  991. {
  992. name: title,
  993. type: "pie",
  994. radius: keyRule.radius ? keyRule.radius : ["40%", "70%"],
  995. avoidLabelOverlap: false,
  996. padAngle: keyRule.padAngle != undefined ? keyRule.padAngle : 5,
  997. itemStyle: {
  998. borderRadius: keyRule.borderRadius != undefined ? keyRule.borderRadius : 10,
  999. },
  1000. label:
  1001. keyRule.label != undefined
  1002. ? keyRule.label
  1003. : {
  1004. show: false,
  1005. position: "center",
  1006. textStyle: {
  1007. textShadow: 'none',
  1008. color: '#fff'
  1009. }
  1010. },
  1011. emphasis: {
  1012. label: {
  1013. show: true,
  1014. fontSize: 20,
  1015. fontWeight: "bold",
  1016. textStyle: {
  1017. textShadow: 'none',
  1018. color: '#fff'
  1019. }
  1020. },
  1021. },
  1022. labelLine: {
  1023. show: true,
  1024. },
  1025. data: keyRule.pieData,
  1026. },
  1027. ],
  1028. };
  1029. break;
  1030. case "bar":
  1031. // 柱状图
  1032. _option = {
  1033. tooltip: {
  1034. trigger: "axis",
  1035. axisPointer: {
  1036. type: "shadow",
  1037. },
  1038. },
  1039. legend: {
  1040. show: keyRule.showLegend ? keyRule.showLegend : false,
  1041. data: keyRule.legend,
  1042. },
  1043. xAxis: {
  1044. type: "value",
  1045. interval: 0,
  1046. axisLabel: {
  1047. color: "#fff", // 字体颜色(支持十六进制、RGB、颜色名)
  1048. fontSize: 12, // 可选:字体大小
  1049. fontWeight: "normal", // 可选:字体粗细
  1050. rotate: 30, // 旋转30度
  1051. textShadow: 'none',
  1052. textStyle: {
  1053. textShadow: 'none',
  1054. color: '#fff'
  1055. }
  1056. },
  1057. splitLine: { lineStyle: { color: "#fff" } },
  1058. },
  1059. yAxis: {
  1060. type: "category",
  1061. data: keyRule.xData,
  1062. axisLabel: {
  1063. color: "#F2F3F5cc", // 字体颜色(支持十六进制、RGB、颜色名)
  1064. fontSize: 14, // 可选:字体大小
  1065. fontWeight: "normal", // 可选:字体粗细
  1066. textShadow: 'none',
  1067. textStyle: {
  1068. textShadow: 'none',
  1069. color: '#fff'
  1070. }
  1071. },
  1072. },
  1073. series: [
  1074. {
  1075. name: keyRule.yData.name,
  1076. type: "bar",
  1077. label: {
  1078. show: true,
  1079. position: 'right',
  1080. valueAnimation: true,
  1081. textStyle: {
  1082. textShadow: 'none',
  1083. color: '#fff'
  1084. }
  1085. },
  1086. emphasis: {
  1087. focus: "series",
  1088. },
  1089. data: keyRule.yData.data,
  1090. },
  1091. ],
  1092. };
  1093. break;
  1094. default:
  1095. // 雷达图
  1096. _option = {
  1097. tooltip: {
  1098. trigger: "axis",
  1099. },
  1100. legend: {
  1101. show: false,
  1102. left: "center",
  1103. },
  1104. radar: [
  1105. {
  1106. indicator: [
  1107. { name: "健壮性", max: 100 },
  1108. { name: "完整性", max: 100 },
  1109. { name: "一致性", max: 100 },
  1110. { name: "及时性", max: 100 },
  1111. { name: "准确性", max: 100 },
  1112. ],
  1113. axisLabel: {
  1114. color: '#fff',
  1115. fontSize: 14,
  1116. fontWeight: 'bold'
  1117. },
  1118. },
  1119. ],
  1120. series: [
  1121. {
  1122. type: "radar",
  1123. tooltip: {
  1124. trigger: 'item'
  1125. },
  1126. itemStyle: {
  1127. color: '#4169E1',
  1128. borderColor: '#4169E1',
  1129. borderWidth: 2
  1130. },
  1131. areaStyle: {},
  1132. data: [
  1133. {
  1134. value: [93, 85, 92, 95, 93],
  1135. name: "数据质量评分",
  1136. },
  1137. ],
  1138. },
  1139. ],
  1140. };
  1141. break;
  1142. }
  1143. this.chartOptions[title] = _option;
  1144. },
  1145. // 服务调用列表
  1146. initTableDatas(serviceTypeDatas2) {
  1147. this.tableDatas = serviceTypeDatas2;
  1148. },
  1149. // 时间范围自定义时间
  1150. shortcuts() {
  1151. return [
  1152. {
  1153. text: "当天",
  1154. value: () => {
  1155. const end = new Date();
  1156. const start = new Date();
  1157. return [start, end];
  1158. },
  1159. },
  1160. {
  1161. text: "最近7天",
  1162. value: () => {
  1163. const end = new Date();
  1164. const start = new Date();
  1165. start.setTime(start.getTime() - 3600 * 1000 * 24 * 6);
  1166. return [start, end];
  1167. },
  1168. },
  1169. {
  1170. text: "最近30天",
  1171. value: () => {
  1172. const end = new Date();
  1173. const start = new Date();
  1174. start.setTime(start.getTime() - 3600 * 1000 * 24 * 29);
  1175. return [start, end];
  1176. },
  1177. },
  1178. {
  1179. text: "最近90天",
  1180. value: () => {
  1181. const end = new Date();
  1182. const start = new Date();
  1183. start.setTime(start.getTime() - 3600 * 1000 * 24 * 89);
  1184. return [start, end];
  1185. },
  1186. },
  1187. {
  1188. text: "最近1年",
  1189. value: () => {
  1190. const end = new Date();
  1191. const start = new Date();
  1192. start.setTime(start.getTime() - 3600 * 1000 * 24 * 364);
  1193. return [start, end];
  1194. },
  1195. },
  1196. ];
  1197. },
  1198. },
  1199. };
  1200. </script>
  1201. <style lang="less" scoped>
  1202. .mainBox {
  1203. width: calc(100% - 60px);
  1204. margin: 30px;
  1205. padding-bottom: 80px;
  1206. & > div {
  1207. margin: 20px 0;
  1208. display: flex;
  1209. }
  1210. .card {
  1211. width: calc(25% - 56px);
  1212. border-radius: 5px;
  1213. padding: 20px 18px;
  1214. // background: #00000032;
  1215. background: #eeeeee0b;
  1216. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
  1217. }
  1218. .card2 {
  1219. width: 100%;
  1220. border-radius: 5px;
  1221. padding: 20px 18px;
  1222. // background: rgba(255, 255, 255, 0.1);
  1223. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
  1224. }
  1225. .bigCard {
  1226. width: calc(100% - 36px);
  1227. border-radius: 5px;
  1228. padding: 20px 18px;
  1229. // background: #00000032;
  1230. position: relative;
  1231. flex-direction: column;
  1232. background: #eeeeee0b;
  1233. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
  1234. .tools {
  1235. position: absolute;
  1236. top: 20px;
  1237. right: 20px;
  1238. display: flex;
  1239. flex-direction: row;
  1240. justify-content: flex-end;
  1241. }
  1242. &_title {
  1243. font-size: 20px;
  1244. font-weight: bold;
  1245. }
  1246. }
  1247. .flex {
  1248. display: flex;
  1249. flex-direction: row;
  1250. justify-content: space-between;
  1251. }
  1252. .flex_column {
  1253. display: flex;
  1254. flex-direction: column;
  1255. justify-content: space-between;
  1256. align-items: flex-start;
  1257. box-sizing: border-box;
  1258. }
  1259. .searchBox {
  1260. width: 100%;
  1261. height: 50px;
  1262. line-height: 50px;
  1263. text-align: center;
  1264. font-size: 20px;
  1265. font-weight: bold;
  1266. flex-direction: row-reverse;
  1267. display: flex;
  1268. }
  1269. }
  1270. // 日期选择框样式
  1271. .demo-date-picker {
  1272. display: flex;
  1273. width: 100%;
  1274. padding: 0;
  1275. flex-wrap: wrap;
  1276. }
  1277. .demo-date-picker .block {
  1278. padding: 1.5rem 0;
  1279. text-align: center;
  1280. border-right: solid 1px var(--el-border-color);
  1281. flex: 1;
  1282. min-width: 400px;
  1283. display: flex;
  1284. flex-direction: column;
  1285. align-items: center;
  1286. }
  1287. .demo-date-picker .block:last-child {
  1288. border-right: none;
  1289. }
  1290. .demo-date-picker .demonstration {
  1291. display: block;
  1292. color: var(--el-text-color-secondary);
  1293. font-size: 14px;
  1294. margin-bottom: 1rem;
  1295. }
  1296. @media screen and (max-width: 1200px) {
  1297. .demo-date-picker .block {
  1298. flex: 0 0 100%;
  1299. padding: 1rem 0;
  1300. min-width: auto;
  1301. border-right: none;
  1302. border-bottom: solid 1px var(--el-border-color);
  1303. }
  1304. .demo-date-picker .block:last-child {
  1305. border-bottom: none;
  1306. }
  1307. }
  1308. // 弹窗加载状态图标
  1309. .loading-icon {
  1310. // 图标顺时针旋转动画
  1311. animation: loading-icon-spin 2s linear infinite;
  1312. }
  1313. @keyframes loading-icon-spin {
  1314. 0% {
  1315. transform: rotate(0deg);
  1316. }
  1317. 100% {
  1318. transform: rotate(360deg);
  1319. }
  1320. }
  1321. </style>