ChartsGroupTwo.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <template>
  2. <el-row :gutter="20">
  3. <!-- 超时工单情况的柱状图 -->
  4. <el-col :span="12" v-if="showOvertimeChart">
  5. <div class="chart-container floating-card">
  6. <div ref="overtimeChart" class="chart"></div>
  7. </div>
  8. </el-col>
  9. <!-- 班组成员工分情况的横向柱状图 -->
  10. <el-col :span="12" v-if="showTeamPointsChart">
  11. <div class="chart-container floating-card">
  12. <div ref="teamPointsChart" class="chart"></div>
  13. </div>
  14. </el-col>
  15. <!-- 平均设备故障复发时间(MTF)的曲线图 -->
  16. <el-col :span="24" v-if="showMtfChart">
  17. <div class="chart-container floating-card">
  18. <div ref="mtfChart" class="chart"></div>
  19. </div>
  20. </el-col>
  21. </el-row>
  22. </template>
  23. <script>
  24. import * as echarts from 'echarts';
  25. import { getChartData } from '@/api/system/dashboard';
  26. export default {
  27. name: 'ChartsGroupTwo',
  28. data() {
  29. return {
  30. overtimeChart: null,
  31. teamPointsChart: null,
  32. mtfChart: null,
  33. showOvertimeChart: true, // 控制超时工单情况图表的显示
  34. showTeamPointsChart: true, // 控制班组成员工分情况图表的显示
  35. showMtfChart: true, // 控制MTF曲线图的显示
  36. }
  37. },
  38. mounted() {
  39. this.initCharts()
  40. },
  41. beforeDestroy() {
  42. // 销毁所有图表实例
  43. const charts = [this.overtimeChart, this.teamPointsChart, this.mtfChart];
  44. charts.forEach(chart => {
  45. if (chart) {
  46. chart.dispose();
  47. }
  48. });
  49. },
  50. methods: {
  51. // 初始化图表
  52. initCharts() {
  53. this.overtimeChart = echarts.init(this.$refs.overtimeChart)
  54. this.teamPointsChart = echarts.init(this.$refs.teamPointsChart)
  55. this.mtfChart = echarts.init(this.$refs.mtfChart)
  56. // 获取图表数据
  57. this.fetchChartData();
  58. },
  59. // 获取图表数据
  60. async fetchChartData() {
  61. try {
  62. const response = await getChartData();
  63. // 处理班组成员工分情况数据
  64. if (response && response.pepolePointStatistics) {
  65. // 更新班组成员工分情况图表
  66. this.updateTeamPointsChart(response.pepolePointStatistics);
  67. } else {
  68. // 如果没有获取到数据,隐藏图表
  69. this.showTeamPointsChart = false;
  70. // 销毁图表实例
  71. if (this.teamPointsChart) {
  72. this.teamPointsChart.dispose();
  73. this.teamPointsChart = null;
  74. }
  75. }
  76. // 处理超时工单情况数据
  77. if (response && response.overTimeStatistics) {
  78. // 更新超时工单情况图表
  79. this.updateOvertimeChart(response.overTimeStatistics);
  80. } else {
  81. // 如果没有获取到数据,隐藏图表
  82. this.showOvertimeChart = false;
  83. // 销毁图表实例
  84. if (this.overtimeChart) {
  85. this.overtimeChart.dispose();
  86. this.overtimeChart = null;
  87. }
  88. }
  89. // 处理平均设备故障复发时间(MTF)数据
  90. if (response && response.faultStatistics) {
  91. // 更新MTF曲线图
  92. this.updateMtfChart(response.faultStatistics);
  93. } else {
  94. // 如果没有获取到数据,隐藏图表
  95. this.showMtfChart = false;
  96. // 销毁图表实例
  97. if (this.mtfChart) {
  98. this.mtfChart.dispose();
  99. this.mtfChart = null;
  100. }
  101. }
  102. } catch (error) {
  103. console.error('获取图表数据失败:', error);
  104. // 出错时隐藏图表
  105. this.showTeamPointsChart = false;
  106. this.showOvertimeChart = false;
  107. this.showMtfChart = false;
  108. // 销毁图表实例
  109. if (this.teamPointsChart) {
  110. this.teamPointsChart.dispose();
  111. this.teamPointsChart = null;
  112. }
  113. if (this.overtimeChart) {
  114. this.overtimeChart.dispose();
  115. this.overtimeChart = null;
  116. }
  117. if (this.mtfChart) {
  118. this.mtfChart.dispose();
  119. this.mtfChart = null;
  120. }
  121. }
  122. },
  123. // 计算轴的最大值和间隔的新方法
  124. calculateAxisRange(maxValue) {
  125. // 数据中的最大值除以5
  126. const dividedValue = maxValue / 5;
  127. // 预定义的数组
  128. const presetValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40, 45, 50, 70, 100, 150, 200, 250, 300, 500, 700, 1000, 2000, 5000];
  129. // 获取第一个大于dividedValue的值
  130. let selectedValue = presetValues[presetValues.length - 1]; // 默认取最大值
  131. for (let i = 0; i < presetValues.length; i++) {
  132. if (presetValues[i] >= dividedValue) {
  133. selectedValue = presetValues[i];
  134. break;
  135. }
  136. }
  137. // 乘以5作为最终的最大值
  138. const maxAxisValue = selectedValue * 5;
  139. // 计算间隔
  140. const interval = selectedValue;
  141. return { maxAxisValue, interval };
  142. },
  143. // 更新班组成员工分情况图表
  144. updateTeamPointsChart(pepolePointStatistics) {
  145. // 处理后台传回的数据格式 [{"key":"张三","value":"185"}]
  146. let names = [];
  147. let values = [];
  148. if (pepolePointStatistics && Array.isArray(pepolePointStatistics) && pepolePointStatistics.length > 0) {
  149. // 从后台数据中提取姓名和工分值
  150. names = pepolePointStatistics.map(item => item.key);
  151. values = pepolePointStatistics.map(item => parseInt(item.value) || 0);
  152. // 显示图表
  153. this.showTeamPointsChart = true;
  154. } else {
  155. // 隐藏图表
  156. this.showTeamPointsChart = false;
  157. // 销毁图表实例
  158. if (this.teamPointsChart) {
  159. this.teamPointsChart.dispose();
  160. this.teamPointsChart = null;
  161. }
  162. return;
  163. }
  164. // 使用新的计算方法
  165. const maxValue = Math.max(...values, 5); // 至少显示到5
  166. const { maxAxisValue, interval } = this.calculateAxisRange(maxValue);
  167. // 班组成员工分情况的横向柱状图(柱子颜色淡蓝色)
  168. const teamPointsOption = {
  169. title: {
  170. text: '班组成员工分情况',
  171. left: 'left',
  172. top: 'top'
  173. },
  174. grid: {
  175. left: '2%', // 左边距 5%
  176. right: '2%', // 右边距 5%
  177. top: '20%', // 上边距(根据需求调整)
  178. bottom: '5%', // 下边距(根据需求调整)
  179. containLabel: true, // 确保标签在 grid 区域内
  180. },
  181. tooltip: {
  182. show: true,
  183. trigger: 'item',
  184. padding: [1, 5],
  185. backgroundColor: 'rgba(0, 0, 0, 0.7)',
  186. borderColor: 'rgba(0, 0, 0, 0.7)',
  187. textStyle: {
  188. color: '#fff'
  189. },
  190. formatter: function(params) {
  191. return `
  192. <div style="font-weight:bold; font-size:12px;">${params.name}</div>
  193. <div style="display: flex; align-items: center; font-size:12px;">
  194. <span style="display:inline-block; width:12px; height:12px; background:${params.color};margin-right: 4px;box-shadow: 0 0 0 0.5px white;"></span>
  195. <span>工分数:</span>
  196. <span style="margin-left: 4px;">${params.value}</span>
  197. </div>
  198. `;
  199. },
  200. position: 'top',
  201. },
  202. xAxis: {
  203. type: 'value',
  204. min: 0,
  205. max: maxAxisValue,
  206. interval: interval,
  207. axisLabel: {
  208. formatter: '{value}'
  209. },
  210. splitLine: {
  211. show: true,
  212. lineStyle: {
  213. color: 'rgba(200, 200, 200, 0.3)',
  214. type: 'solid'
  215. }
  216. }
  217. },
  218. yAxis: {
  219. type: 'category',
  220. data: names, // 班组成员
  221. splitLine: {
  222. show: true,
  223. lineStyle: {
  224. color: 'rgba(200, 200, 200, 0.3)',
  225. type: 'solid'
  226. }
  227. }
  228. },
  229. series: [
  230. {
  231. data: values,
  232. type: 'bar',
  233. label: {
  234. show: false,
  235. },
  236. barWidth: '70%',
  237. itemStyle: {
  238. borderRadius: [0, 5, 5, 0],
  239. color: 'rgba(22, 93, 255, 0.7)' // 工单总工时颜色
  240. }
  241. }
  242. ]
  243. }
  244. // 初始化图表实例(如果还没有)
  245. if (!this.teamPointsChart) {
  246. this.teamPointsChart = echarts.init(this.$refs.teamPointsChart);
  247. }
  248. // 设置班组成员工分情况图表配置
  249. this.teamPointsChart.setOption(teamPointsOption, true);
  250. },
  251. // 更新超时工单情况图表
  252. updateOvertimeChart(overTimeStatistics) {
  253. // 处理后台传回的数据格式 [{"key":"超时1天","value":"25"}]
  254. let names = [];
  255. let values = [];
  256. if (overTimeStatistics && Array.isArray(overTimeStatistics) && overTimeStatistics.length > 0) {
  257. // 从后台数据中提取超时天数和工单数
  258. names = overTimeStatistics.map(item => item.key);
  259. values = overTimeStatistics.map(item => parseInt(item.value) || 0);
  260. // 显示图表
  261. this.showOvertimeChart = true;
  262. } else {
  263. // 隐藏图表
  264. this.showOvertimeChart = false;
  265. // 销毁图表实例
  266. if (this.overtimeChart) {
  267. this.overtimeChart.dispose();
  268. this.overtimeChart = null;
  269. }
  270. return;
  271. }
  272. // 使用新的计算方法
  273. const maxValue = Math.max(...values, 5); // 至少显示到5
  274. const { maxAxisValue, interval } = this.calculateAxisRange(maxValue);
  275. // 超时工单情况的柱状图(柱子颜色淡红色)
  276. const overtimeOption = {
  277. title: {
  278. text: '超时工单情况',
  279. left: 'left',
  280. top: 'top'
  281. },
  282. grid: {
  283. left: '2%', // 左边距 5%
  284. right: '2%', // 右边距 5%
  285. top: '20%', // 上边距(根据需求调整)
  286. bottom: '5%', // 下边距(根据需求调整)
  287. containLabel: true, // 确保标签在 grid 区域内
  288. },
  289. tooltip: {
  290. show: true,
  291. trigger: 'item',
  292. padding: [1, 5],
  293. backgroundColor: 'rgba(0, 0, 0, 0.7)',
  294. borderColor: 'rgba(0, 0, 0, 0.7)',
  295. textStyle: {
  296. color: '#fff'
  297. },
  298. formatter: function(params) {
  299. return `
  300. <div style="font-weight:bold; font-size:12px;">${params.name}</div>
  301. <div style="display: flex; align-items: center; font-size:12px;">
  302. <span style="display:inline-block; width:12px; height:12px; background:${params.color};margin-right: 4px;box-shadow: 0 0 0 0.5px white;"></span>
  303. <span>工单数:</span>
  304. <span style="margin-left: 4px;">${params.value}</span>
  305. </div>
  306. `;
  307. },
  308. position: 'top',
  309. },
  310. xAxis: {
  311. type: 'category',
  312. data: names, // 超时天数
  313. splitLine: {
  314. show: true,
  315. lineStyle: {
  316. color: 'rgba(200, 200, 200, 0.3)',
  317. type: 'solid'
  318. }
  319. }
  320. },
  321. yAxis: {
  322. type: 'value',
  323. min: 0,
  324. max: maxAxisValue,
  325. interval: interval,
  326. axisLabel: {
  327. formatter: '{value}天'
  328. },
  329. name: '天数',
  330. nameLocation: 'middle',
  331. nameRotate: 90,
  332. nameGap: 40,
  333. splitLine: {
  334. show: true,
  335. lineStyle: {
  336. color: 'rgba(200, 200, 200, 0.3)',
  337. type: 'solid'
  338. }
  339. }
  340. },
  341. series: [
  342. {
  343. data: values, // 工单数
  344. type: 'bar',
  345. label: {
  346. show: false,
  347. },
  348. barWidth: '70%',
  349. itemStyle: {
  350. borderRadius: [5, 5, 0, 0],
  351. color: 'rgba(255, 77, 79, 0.7)' // 淡红色
  352. }
  353. }
  354. ]
  355. }
  356. // 初始化图表实例(如果还没有)
  357. if (!this.overtimeChart) {
  358. this.overtimeChart = echarts.init(this.$refs.overtimeChart);
  359. }
  360. // 设置超时工单情况图表配置
  361. this.overtimeChart.setOption(overtimeOption, true);
  362. },
  363. // 更新平均设备故障复发时间(MTF)曲线图
  364. updateMtfChart(faultStatistics) {
  365. // 处理后台传回的数据格式 [{"key":"1月","value":"45"}]
  366. let names = [];
  367. let values = [];
  368. if (faultStatistics && Array.isArray(faultStatistics) && faultStatistics.length > 0) {
  369. // 从后台数据中提取月份和MTF值
  370. names = faultStatistics.map(item => item.key);
  371. values = faultStatistics.map(item => parseInt(item.value) || 0);
  372. // 显示图表
  373. this.showMtfChart = true;
  374. } else {
  375. // 隐藏图表
  376. this.showMtfChart = false;
  377. // 销毁图表实例
  378. if (this.mtfChart) {
  379. this.mtfChart.dispose();
  380. this.mtfChart = null;
  381. }
  382. return;
  383. }
  384. // 使用新的计算方法
  385. const maxValue = Math.max(...values, 5); // 至少显示到5
  386. const { maxAxisValue, interval } = this.calculateAxisRange(maxValue);
  387. // 平均设备故障复发时间(MTF)的曲线图(绿色曲线,浅绿色区域)
  388. const mtfOption = {
  389. title: {
  390. text: '平均设备故障复发时间(MTF)',
  391. left: 'left',
  392. top: 'top'
  393. },
  394. grid: {
  395. left: '2%', // 左边距 5%
  396. right: '2%', // 右边距 5%
  397. top: '20%', // 上边距(根据需求调整)
  398. bottom: '5%', // 下边距(根据需求调整)
  399. containLabel: true, // 确保标签在 grid 区域内
  400. },
  401. tooltip: {
  402. show: true,
  403. trigger: 'item',
  404. padding: [1, 5],
  405. backgroundColor: 'rgba(0, 0, 0, 0.7)',
  406. borderColor: 'rgba(0, 0, 0, 0.7)',
  407. textStyle: {
  408. color: '#fff'
  409. },
  410. formatter: function(params) {
  411. return `
  412. <div style="font-weight:bold; font-size:12px;">${params.name}</div>
  413. <div style="display: flex; align-items: center; font-size:12px;">
  414. <span style="display:inline-block; width:12px; height:12px; background:rgba(0, 200, 0, 0.8);margin-right: 4px;box-shadow: 0 0 0 0.5px white;"></span>
  415. <span>平均设备故障复发时间(天):</span>
  416. <span style="margin-left: 4px;">${params.value}</span>
  417. </div>
  418. `;
  419. }
  420. },
  421. xAxis: {
  422. type: 'category',
  423. data: names, // 月份
  424. splitLine: {
  425. show: true,
  426. lineStyle: {
  427. color: 'rgba(200, 200, 200, 0.3)',
  428. type: 'solid'
  429. }
  430. },
  431. // 确保0点也能显示x轴第一个名字
  432. boundaryGap: false
  433. },
  434. yAxis: {
  435. name: '天数',
  436. nameLocation: 'middle',
  437. nameGap: 30,
  438. type: 'value',
  439. min: 0,
  440. max: maxAxisValue,
  441. interval: interval,
  442. axisLabel: {
  443. formatter: '{value}'
  444. },
  445. splitLine: {
  446. show: true,
  447. lineStyle: {
  448. color: 'rgba(200, 200, 200, 0.3)',
  449. type: 'solid'
  450. }
  451. }
  452. },
  453. series: [
  454. {
  455. name: 'MTF',
  456. data: values, // MTF值
  457. type: 'line',
  458. smooth: true,
  459. symbolSize: 8,
  460. lineStyle: {
  461. color: 'rgba(0, 200, 0, 0.8)', // 绿色曲线
  462. width: 3
  463. },
  464. areaStyle: {
  465. color: 'rgba(0, 200, 0, 0.2)' // 浅绿色区域
  466. }
  467. }
  468. ]
  469. }
  470. // 初始化图表实例(如果还没有)
  471. if (!this.mtfChart) {
  472. this.mtfChart = echarts.init(this.$refs.mtfChart);
  473. }
  474. // 设置MTF曲线图配置
  475. this.mtfChart.setOption(mtfOption, true);
  476. },
  477. // 图表自适应
  478. resizeCharts() {
  479. const charts = [this.overtimeChart, this.teamPointsChart, this.mtfChart];
  480. charts.forEach(chart => {
  481. if (chart) {
  482. chart.resize();
  483. }
  484. });
  485. }
  486. }
  487. }
  488. </script>
  489. <style scoped>
  490. /* 悬浮卡片通用样式 */
  491. .floating-card {
  492. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  493. transition: all 0.3s ease;
  494. }
  495. .floating-card:hover {
  496. transform: translateY(-8px);
  497. box-shadow: 0 12px 30px rgba(0, 0, 0, 0.15);
  498. }
  499. /* 图表样式 */
  500. .chart-container {
  501. height: 340px;
  502. background-color: rgba(255, 255, 255, 0.7);
  503. border-radius: 8px;
  504. padding: 20px;
  505. margin-top: 20px;
  506. box-sizing: border-box;
  507. backdrop-filter: blur(5px);
  508. }
  509. .chart-container:hover {
  510. transform: translateY(-8px);
  511. box-shadow: 0 12px 30px rgba(0, 0, 0, 0.15);
  512. }
  513. .chart {
  514. width: 100%;
  515. height: 100%;
  516. }
  517. </style>