BoxLineCharts.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <template>
  2. <div style="width: 100%; height: 500px">
  3. <!-- 条件渲染日期范围选择器 -->
  4. <el-date-picker
  5. v-if="isDateType(chartData.data[0]?.xData)"
  6. <!--
  7. 只有xData是日期时才显示
  8. --
  9. >
  10. v-model="dateRange" type="daterange" align="right" unlink-panels
  11. range-separator="至" start-placeholder="开始日期"
  12. end-placeholder="结束日期" @change="onDateRangeChange" size="mini"
  13. style="margin: 20px 0 0 0" ></el-date-picker
  14. >
  15. <!-- boxLineCharts -->
  16. <div
  17. v-loading="loading"
  18. :id="`plotDivBox-${index}`"
  19. style="width: 100%; height: 450px"
  20. >
  21. <el-empty v-if="isError" description="请求失败"></el-empty>
  22. </div>
  23. </div>
  24. </template>
  25. <script>
  26. import Plotly from "plotly.js-dist";
  27. import axios from "axios";
  28. import { myMixin } from "@/mixins/chartRequestMixin";
  29. export default {
  30. mixins: [myMixin],
  31. props: {
  32. fileAddr: {
  33. default: "",
  34. type: String,
  35. },
  36. index: {
  37. type: String,
  38. },
  39. },
  40. data() {
  41. return {
  42. chartData: {},
  43. dateRange: [], // 用于存储选中的日期范围
  44. loading: false,
  45. isError: false,
  46. };
  47. },
  48. mounted() {
  49. console.log(this.fileAddr, "fileAddr 连接");
  50. this.getData();
  51. },
  52. methods: {
  53. async getData() {
  54. if (this.fileAddr !== "") {
  55. try {
  56. this.loading = true;
  57. this.cancelToken = axios.CancelToken.source();
  58. const resultChartsData = await axios.get(this.fileAddr, {
  59. cancelToken: this.cancelToken.token,
  60. });
  61. this.chartData = resultChartsData.data;
  62. this.drawBoxPlot();
  63. this.loading = false;
  64. this.isError = false;
  65. } catch (error) {
  66. this.loading = false;
  67. this.isError = true;
  68. }
  69. }
  70. },
  71. // 处理日期范围变化
  72. onDateRangeChange() {
  73. this.drawBoxPlot(); // 日期变化时重新绘制图表
  74. },
  75. // 判断时间戳是否在选择的日期范围内
  76. isInDateRange(timestamp) {
  77. const [startDate, endDate] = this.dateRange;
  78. if (!startDate || !endDate) return true;
  79. const date = new Date(timestamp);
  80. return date >= new Date(startDate) && date <= new Date(endDate);
  81. },
  82. // 判断xData是否为日期格式
  83. isDateType(xData) {
  84. // 假设第一个元素如果是日期字符串或Date对象则为日期类型
  85. const firstTimestamp = xData[0];
  86. return !isNaN(Date.parse(firstTimestamp)); // 判断是否是有效日期
  87. },
  88. // 过滤数据
  89. filterData(group) {
  90. const filteredXData = [];
  91. const filteredYData = [];
  92. const filteredMedians = group.medians ? { x: [], y: [] } : null;
  93. // 如果是日期类型数据,则进行日期过滤
  94. if (this.isDateType(group.xData)) {
  95. group.xData.forEach((timestamp, index) => {
  96. if (this.isInDateRange(timestamp)) {
  97. filteredXData.push(timestamp);
  98. filteredYData.push(group.yData[index]);
  99. // 处理中位值数据,确保索引一致
  100. if (filteredMedians && this.isInDateRange(group.medians.x[index])) {
  101. filteredMedians.x.push(group.medians.x[index]);
  102. filteredMedians.y.push(group.medians.y[index]);
  103. }
  104. }
  105. });
  106. } else {
  107. // 如果不是日期类型,直接将数据添加到数组
  108. filteredXData.push(...group.xData);
  109. filteredYData.push(...group.yData);
  110. if (group.medians) {
  111. filteredMedians.x.push(...group.medians.x);
  112. filteredMedians.y.push(...group.medians.y);
  113. }
  114. }
  115. return {
  116. ...group,
  117. xData: filteredXData,
  118. yData: filteredYData,
  119. medians: filteredMedians,
  120. };
  121. },
  122. // 绘制箱线图
  123. drawBoxPlot() {
  124. const chartData = this.chartData.data[0];
  125. // 过滤数据
  126. const filteredData = this.filterData(chartData);
  127. // 构建箱线图
  128. console.log(filteredData, "filteredData");
  129. const trace = {
  130. x: filteredData.xData,
  131. y: filteredData.yData,
  132. type: "box",
  133. marker: {
  134. color: "lightgray",
  135. },
  136. boxpoints: false, // 不显示散点
  137. boxmean: false, // 不显示均值
  138. name: filteredData.title,
  139. };
  140. let trace2 = {}; // 初始化trace2为一个空对象
  141. // 如果有中位值并且中位值的x轴数据不为空,则显示中位点
  142. if (filteredData.medians && filteredData.medians.x.length > 0) {
  143. trace2 = {
  144. x: filteredData.medians.x,
  145. y: filteredData.medians.y,
  146. mode: "markers",
  147. marker: {
  148. color: "#406DAB",
  149. size: 3,
  150. },
  151. name: `${filteredData.title} - 中位点`,
  152. type: "scatter",
  153. };
  154. }
  155. console.log(filteredData, "filteredData");
  156. // 布局设置
  157. const layout = {
  158. title: filteredData.title,
  159. xaxis: {
  160. title: this.chartData.xaixs,
  161. // 如果xData不是日期格式,则不配置type: "date"
  162. title: this.chartData.xaixs,
  163. // tickvals: filteredData.xData, // 设置tickvals为原始的xData
  164. // ticktext: filteredData.xData, // 设置ticktext为原始的xData(避免被转换成数字)
  165. // tickformat: this.isDateType(filteredData.xData)
  166. // ? "%Y-%m-%d"
  167. // : undefined,
  168. tickmode: "array", // 精确控制刻度
  169. },
  170. yaxis: { title: this.chartData.yaixs },
  171. showlegend: true,
  172. };
  173. // 使用 v-if 防止重新渲染
  174. if (this.chartData.data && this.chartData.data.length > 0) {
  175. Plotly.newPlot(`plotDivBox-${this.index}`, [trace, trace2], layout);
  176. }
  177. // Plotly.newPlot(`plotDivBox-${this.index}`, [trace, trace2], layout);
  178. },
  179. },
  180. };
  181. </script>
  182. <style scoped>
  183. /* 可根据需要自定义样式 */
  184. </style>