SingleProductionQuantityCompletion.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <div class="quality-target-bar-container">
  3. <ScreenComHeader :module-id="moduleId" title="成品率对比" />
  4. <div ref="chartRef" style="width: 100%; height: 100%"></div>
  5. </div>
  6. </template>
  7. <script setup>
  8. import * as echarts from "echarts";
  9. import ScreenComHeader from "@/views/screens/configs/screenComHeader.vue";
  10. import { qualityReport } from "@/api/screens";
  11. const props = defineProps({
  12. moduleId: {
  13. type: String,
  14. required: true,
  15. },
  16. });
  17. const chartRef = ref(null);
  18. let chartInstance = null;
  19. let timer = null;
  20. const currentIndex = ref(0);
  21. const chartData = ref([]);
  22. // 初始化图表
  23. const initChart = () => {
  24. if (!chartRef.value) return;
  25. chartInstance = echarts.init(chartRef.value);
  26. updateChart();
  27. window.addEventListener("resize", handleResize);
  28. // 启动轮播定时器
  29. startAutoPlay();
  30. };
  31. // 启动自动轮播
  32. const startAutoPlay = () => {
  33. if (timer) clearInterval(timer);
  34. timer = setInterval(() => {
  35. currentIndex.value = (currentIndex.value + 1) % chartData.value.length;
  36. updateChart();
  37. }, 3000); // 3秒切换一次
  38. };
  39. // 数据转换函数 - 只提取成品率数据
  40. const transformData = (rawData) => {
  41. return rawData.map((item) => {
  42. const operationNumbers = item.operationNumberList;
  43. const yieldIndex = operationNumbers.length - 3;
  44. return {
  45. name:
  46. item.materialModel +
  47. (item.materialCategory ? `(${item.materialCategory})` : ""),
  48. target: parseFloat(operationNumbers[yieldIndex]),
  49. actual: parseFloat(operationNumbers[yieldIndex + 1]),
  50. diff: parseFloat(operationNumbers[yieldIndex + 2]),
  51. };
  52. });
  53. };
  54. // 更新图表数据 - 只显示当前索引的产品
  55. const updateChart = () => {
  56. if (!chartInstance || !chartData.value.length) return;
  57. const currentData = chartData.value[currentIndex.value];
  58. const option = {
  59. backgroundColor: "transparent",
  60. title: {
  61. text: currentData.name,
  62. left: "center",
  63. textStyle: {
  64. fontSize: 14,
  65. color: "#fff",
  66. },
  67. },
  68. tooltip: {
  69. trigger: "axis",
  70. axisPointer: {
  71. type: "shadow",
  72. },
  73. backgroundColor: "rgba(0,0,0,0.7)",
  74. textStyle: {
  75. color: "#fff",
  76. },
  77. formatter: () => {
  78. const diffColor = currentData.diff >= 0 ? "#91CC75" : "#EE6666";
  79. const diffSymbol = currentData.diff >= 0 ? "+" : "";
  80. return `
  81. <div style="font-weight:bold;margin-bottom:5px">${currentData.name}</div>
  82. <div style="display:flex;align-items:center;margin:3px 0">
  83. <span style="display:inline-block;width:10px;height:10px;background:#5470C6;margin-right:5px"></span>
  84. <span style="flex:1">目标成品率:</span>
  85. <span style="font-weight:bold">${currentData.target}%</span>
  86. </div>
  87. <div style="display:flex;align-items:center;margin:3px 0">
  88. <span style="display:inline-block;width:10px;height:10px;background:#91CC75;margin-right:5px"></span>
  89. <span style="flex:1">实际成品率:</span>
  90. <span style="font-weight:bold">${currentData.actual}%</span>
  91. </div>
  92. <div style="display:flex;align-items:center;margin:3px 0">
  93. <span style="display:inline-block;width:10px;height:10px;background:${diffColor};margin-right:5px"></span>
  94. <span style="flex:1">差值:</span>
  95. <span style="font-weight:bold;color:${diffColor}">${diffSymbol}${currentData.diff}%</span>
  96. </div>
  97. `;
  98. },
  99. },
  100. legend: {
  101. data: ["目标成品率", "实际成品率"],
  102. bottom: 35,
  103. textStyle: {
  104. color: "#fff",
  105. },
  106. itemWidth: 20,
  107. itemHeight: 10,
  108. },
  109. grid: {
  110. top: "10%",
  111. right: "10%",
  112. bottom: "15%",
  113. left: "10%",
  114. containLabel: true,
  115. },
  116. xAxis: {
  117. type: "category",
  118. data: ["产品成品率对比"],
  119. axisLine: {
  120. lineStyle: {
  121. color: "#fff",
  122. },
  123. },
  124. axisLabel: {
  125. color: "#fff",
  126. },
  127. },
  128. yAxis: {
  129. type: "value",
  130. name: "成品率(%)",
  131. nameTextStyle: {
  132. color: "#fff",
  133. padding: [0, 0, 0, 40],
  134. },
  135. min: 0,
  136. max: 100,
  137. axisLine: {
  138. lineStyle: {
  139. color: "#fff",
  140. },
  141. },
  142. axisLabel: {
  143. color: "#fff",
  144. formatter: "{value}%",
  145. },
  146. splitLine: {
  147. lineStyle: {
  148. color: "rgba(255,255,255,0.1)",
  149. },
  150. },
  151. },
  152. series: [
  153. {
  154. name: "目标成品率",
  155. type: "bar",
  156. barWidth: 40,
  157. data: [
  158. {
  159. value: currentData.target,
  160. itemStyle: {
  161. color: "#5470C6",
  162. },
  163. },
  164. ],
  165. label: {
  166. show: true,
  167. position: "top",
  168. formatter: "{c}%",
  169. color: "#fff",
  170. },
  171. },
  172. {
  173. name: "实际成品率",
  174. type: "bar",
  175. barWidth: 40,
  176. data: [
  177. {
  178. value: currentData.actual,
  179. itemStyle: {
  180. color:
  181. currentData.actual >= currentData.target
  182. ? "#91CC75"
  183. : "#EE6666",
  184. },
  185. },
  186. ],
  187. label: {
  188. show: true,
  189. position: "top",
  190. formatter: (params) => {
  191. const diff = currentData.diff;
  192. const diffColor = diff >= 0 ? "#91CC75" : "#EE6666";
  193. const diffSymbol = diff >= 0 ? "+" : "";
  194. return `${params.value}% {diff|${diffSymbol}${diff}%}`;
  195. },
  196. color: "#fff",
  197. rich: {
  198. diff: {
  199. color: "#EE6666",
  200. padding: [0, 0, 0, 5],
  201. },
  202. },
  203. },
  204. },
  205. ],
  206. animationDuration: 500,
  207. };
  208. chartInstance.setOption(option, true);
  209. };
  210. const handleResize = () => {
  211. chartInstance?.resize();
  212. };
  213. const loadData = async () => {
  214. try {
  215. const rowData = await qualityReport();
  216. chartData.value = transformData(rowData.data);
  217. if (chartData.value.length > 0) {
  218. initChart();
  219. }
  220. } catch (error) {
  221. console.error("加载数据失败:", error);
  222. }
  223. };
  224. onMounted(() => {
  225. loadData();
  226. });
  227. onBeforeUnmount(() => {
  228. if (chartInstance) {
  229. chartInstance.dispose();
  230. window.removeEventListener("resize", handleResize);
  231. }
  232. if (timer) clearInterval(timer);
  233. });
  234. </script>
  235. <style scoped>
  236. .quality-target-bar-container {
  237. width: 100%;
  238. height: 100%;
  239. min-height: 300px;
  240. position: relative;
  241. background-color: transparent;
  242. border-radius: 8px;
  243. padding: 10px;
  244. box-sizing: border-box;
  245. }
  246. </style>