lineChart.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <script lang="ts" setup>
  2. import * as echarts from 'echarts';
  3. import { onBeforeUnmount, onMounted, ref } from 'vue';
  4. import { getChartData } from '/src/views/score-statistics/api';
  5. let chartObj: any;
  6. const chartRef = ref(null);
  7. const loading = ref(false);
  8. const newAverageScores = ref([]);
  9. const oldAverageScores = ref([]);
  10. const newStarRatings = ref<number[][]>([]);
  11. const oldStarRatings = ref<number[][]>([]);
  12. // 初始化 ECharts 图表
  13. onMounted(() => {
  14. addResize();
  15. initLine();
  16. });
  17. // 组件卸载前清理
  18. onBeforeUnmount(() => {
  19. if (chartObj) {
  20. chartObj.dispose();
  21. chartObj = null;
  22. }
  23. removeResize();
  24. });
  25. // ECharts 配置项
  26. const option: any = {
  27. // dataset: {
  28. // source: [],
  29. // },
  30. title: {
  31. left: '0%',
  32. text: '每月平均评分',
  33. textStyle: {
  34. fontSize: '16px',
  35. fontWeight: '500',
  36. },
  37. },
  38. tooltip: {
  39. trigger: 'axis',
  40. },
  41. legend: {
  42. top: '10%',
  43. align: 'right',
  44. },
  45. grid: {
  46. top: '30%',
  47. left: '5%',
  48. right: '5%',
  49. bottom: '5%',
  50. containLabel: true,
  51. },
  52. xAxis: {
  53. type: 'category',
  54. data: [],
  55. },
  56. yAxis: [
  57. {
  58. type: 'value',
  59. name: '平均评分',
  60. min: 2,
  61. max: 5,
  62. interval: 0.5,
  63. position: 'left',
  64. axisLine: {
  65. show: true,
  66. lineStyle: {
  67. color: '#70b6e3',
  68. },
  69. },
  70. axisLabel: {
  71. formatter: '{value} 分',
  72. },
  73. },
  74. {
  75. type: 'value',
  76. name: '评分数量',
  77. min: 0,
  78. position: 'right',
  79. splitLine: {
  80. show: false,
  81. },
  82. axisLine: {
  83. show: true,
  84. lineStyle: {
  85. color: '#5470c6',
  86. },
  87. },
  88. axisLabel: {
  89. formatter: '{value} 个',
  90. },
  91. },
  92. ],
  93. series: [
  94. {
  95. id: 0,
  96. name: '新品平均评分',
  97. type: 'line',
  98. yAxisIndex: 0,
  99. itemStyle: {
  100. color: '#70b6e3', //改变折线点的颜色
  101. lineStyle: {
  102. color: '#70b6e3', //改变折线颜色
  103. },
  104. },
  105. data: [],
  106. },
  107. {
  108. id: 1,
  109. name: '旧品平均评分',
  110. type: 'line',
  111. yAxisIndex: 0,
  112. itemStyle: {
  113. color: '#8170cc', //改变折线点的颜色
  114. lineStyle: {
  115. color: '#3f3da4', //改变折线颜色
  116. },
  117. },
  118. data: [],
  119. },
  120. {
  121. name: '新品一星',
  122. type: 'bar',
  123. stack: 'stack1',
  124. yAxisIndex: 1,
  125. barGap: 0,
  126. barMaxWidth: '14px',
  127. itemStyle: {
  128. color: '#e7b3b3',
  129. //borderRadius: 4,
  130. },
  131. data: [],
  132. },
  133. {
  134. name: '旧品一星',
  135. type: 'bar',
  136. stack: 'stack1',
  137. yAxisIndex: 1,
  138. barGap: 0,
  139. barMaxWidth: '14px',
  140. itemStyle: {
  141. color: 'rgba(231,179,179,0.73)',
  142. //borderRadius: 4,
  143. },
  144. data: [],
  145. },
  146. {
  147. name: '新品二星',
  148. type: 'bar',
  149. stack: 'stack2',
  150. yAxisIndex: 1,
  151. barMaxWidth: '14px',
  152. itemStyle: {
  153. color: '#fdba74',
  154. //borderRadius: 4,
  155. },
  156. data: [],
  157. },
  158. {
  159. name: '旧品二星',
  160. type: 'bar',
  161. stack: 'stack2',
  162. yAxisIndex: 1,
  163. barMaxWidth: '14px',
  164. itemStyle: {
  165. color: 'rgba(253,186,116,0.79)',
  166. //borderRadius: 4,
  167. },
  168. data: [],
  169. },
  170. {
  171. name: '新品三星',
  172. type: 'bar',
  173. stack: 'stack3',
  174. yAxisIndex: 1,
  175. barMaxWidth: '14px',
  176. itemStyle: {
  177. color: '#eae09e',
  178. //borderRadius: 4,
  179. },
  180. data: [],
  181. },
  182. {
  183. name: '旧品三星',
  184. type: 'bar',
  185. stack: 'stack3',
  186. yAxisIndex: 1,
  187. barMaxWidth: '14px',
  188. itemStyle: {
  189. color: 'rgba(234,224,158,0.76)',
  190. //borderRadius: 4,
  191. },
  192. data: [],
  193. },
  194. {
  195. name: '新品四星',
  196. type: 'bar',
  197. yAxisIndex: 1,
  198. stack: 'stack4',
  199. barMaxWidth: '14px',
  200. itemStyle: {
  201. color: '#bae6fd',
  202. //borderRadius: 4,
  203. },
  204. data: [],
  205. },
  206. {
  207. name: '旧品四星',
  208. type: 'bar',
  209. yAxisIndex: 1,
  210. stack: 'stack4',
  211. barMaxWidth: '14px',
  212. itemStyle: {
  213. color: 'rgba(186,230,253,0.76)',
  214. //borderRadius: 4,
  215. },
  216. data: [],
  217. },
  218. {
  219. name: '新品五星',
  220. type: 'bar',
  221. stack: 'stack5',
  222. yAxisIndex: 1,
  223. barMaxWidth: '14px',
  224. itemStyle: {
  225. color: '#a6e3a1',
  226. //borderRadius: 4,
  227. },
  228. data: [],
  229. },
  230. {
  231. name: '旧品五星',
  232. type: 'bar',
  233. stack: 'stack5',
  234. yAxisIndex: 1,
  235. barMaxWidth: '14px',
  236. itemStyle: {
  237. color: 'rgba(166,227,161,0.66)',
  238. //borderRadius: 4,
  239. },
  240. data: [],
  241. },
  242. ],
  243. };
  244. // 初始化 ECharts 图表的函数
  245. async function initLine() {
  246. await loadData();
  247. chartObj = echarts.init(chartRef.value);
  248. chartObj.setOption(option, true);
  249. }
  250. // 加载数据
  251. async function loadData() {
  252. try {
  253. loading.value = true;
  254. const query = {};
  255. const resp = await getChartData(query);
  256. const trendData = resp.data;
  257. newAverageScores.value = trendData.new_avg_score.map((item) => item.avg_val);
  258. oldAverageScores.value = trendData.old_avg_score.map((item) => item.avg_val);
  259. option.xAxis.data = trendData.new_avg_score.map((item) => item.date);
  260. option.series[0].data = newAverageScores.value;
  261. option.series[1].data = oldAverageScores.value;
  262. trendData.reviews_items.forEach((item) => {
  263. const ratings = [item.reviews1_sum || 0, item.reviews2_sum || 0, item.reviews3_sum || 0, item.reviews4_sum || 0, item.reviews5_sum || 0];
  264. if (item.is_new_sku) {
  265. newStarRatings.value.push(ratings);
  266. } else {
  267. oldStarRatings.value.push(ratings);
  268. }
  269. });
  270. const starRatings = [newStarRatings, oldStarRatings];
  271. starRatings.forEach((ratings, i) => {
  272. ratings.value.forEach((rating, j) => {
  273. rating.forEach((value, k) => {
  274. option.series[2 * k + i + 2].data = option.series[2 * k + i + 2].data || [];
  275. option.series[2 * k + i + 2].data[j] = value;
  276. });
  277. });
  278. });
  279. } catch (e) {
  280. ElMessage.error('加载数据失败,请稍后再试');
  281. } finally {
  282. loading.value = false;
  283. }
  284. }
  285. // 处理窗口大小变化
  286. function resizeChart() {
  287. chartObj.resize();
  288. }
  289. // 添加窗口大小变化事件监听
  290. function addResize() {
  291. window.addEventListener('resize', resizeChart);
  292. }
  293. // 移除窗口大小变化事件监听
  294. function removeResize() {
  295. window.removeEventListener('resize', resizeChart);
  296. }
  297. </script>
  298. <template>
  299. <div v-loading="loading">
  300. <!-- 图表区域 -->
  301. <div ref="chartRef" style="width: 100%; height: 520px; background: #fff"></div>
  302. </div>
  303. </template>
  304. <style scoped></style>