NoColourBandTwoDMarkerChart.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <!--
  2. * @Author: your name
  3. * @Date: 2025-01-17 17:22:04
  4. * @LastEditTime: 2025-01-17 17:23:46
  5. * @LastEditors: bogon
  6. * @Description: In User Settings Edit
  7. * @FilePath: /performance-test/src/views/performance/components/chartsCom/NoColourBandTwoDMarkerChart.vue
  8. -->
  9. <template>
  10. <div style="height: 400px">
  11. <!-- 2D散点图 没色带 -->
  12. <template>
  13. <div style="display: flex; align-items: center; margin-top: 20px">
  14. <div style="margin-right: 20px; display: flex; align-items: center">
  15. <el-color-picker
  16. size="small"
  17. v-model="color1"
  18. show-alpha
  19. @change="updateChartColor"
  20. ></el-color-picker>
  21. <span style="margin-left: 10px">自定义颜色</span>
  22. </div>
  23. <!-- 图表类型切换按钮 -->
  24. <div>
  25. <el-button size="small" @click="setChartType('scatter')"
  26. >散点图</el-button
  27. >
  28. <el-button size="small" @click="setChartType('line')"
  29. >折线图</el-button
  30. >
  31. </div>
  32. </div>
  33. <div v-loading="loading" ref="plotlyChart" style="height: 400px">
  34. <el-empty v-if="isError" description="请求失败"></el-empty>
  35. </div>
  36. </template>
  37. </div>
  38. </template>
  39. <script>
  40. import Plotly from "plotly.js-dist";
  41. import axios from "axios";
  42. import { myMixin } from "@/mixins/chartRequestMixin";
  43. export default {
  44. mixins: [myMixin],
  45. props: {
  46. fileAddr: {
  47. default: "",
  48. type: String,
  49. },
  50. index: {
  51. type: Number,
  52. }
  53. },
  54. data() {
  55. return {
  56. chartData: {},
  57. chartType: "scatter", // 初始化为散点图 (scatter)
  58. color1: "", // 默认颜色
  59. selectedPoints: [],
  60. originalColors: [],
  61. originalSizes: [],
  62. };
  63. },
  64. async mounted() {
  65. this.getData();
  66. },
  67. methods: {
  68. async getData() {
  69. if (this.fileAddr !== "") {
  70. try {
  71. this.loading = true
  72. this.cancelToken = axios.CancelToken.source();
  73. console.log(this.cancelToken)
  74. const resultChartsData = await axios.get(this.fileAddr, {
  75. cancelToken: this.cancelToken.token
  76. });
  77. this.chartData = resultChartsData.data;
  78. this.drawChart();
  79. this.isError = false
  80. this.loading = false
  81. } catch (error) {
  82. this.isError = true
  83. this.loading = false
  84. }
  85. }
  86. },
  87. drawChart() {
  88. const data = this.chartData.data && this.chartData.data[0];
  89. let trace = {};
  90. // 保存原始颜色和大小
  91. this.originalColors = [...data.yData];
  92. this.originalSizes = new Array(data.xData.length).fill(6); // 初始点大小
  93. if (this.chartType === "scatter") {
  94. // 绘制 2D 散点图
  95. console.log("重新绘制图表", this.color1);
  96. trace = {
  97. x: data.xData,
  98. y: data.yData,
  99. mode: "markers",
  100. type: "scattergl", // 这里改为 scattergl
  101. text: data.engineName, // 提示文本
  102. marker: {
  103. color: data.yData, // 根据 yData 的值设置颜色
  104. // colorscale: "Viridis", // 使用的颜色区间
  105. colorscale: this.color1
  106. ? [
  107. [0, "#F9FDD2"], // 颜色从 this.color1 开始
  108. [1, this.color1], // 结束颜色为其他颜色
  109. ]
  110. : [
  111. [0, "#F9FDD2"],
  112. [0.15, "#E9F6BD"],
  113. [0.3, "#C2E3B9"],
  114. [0.45, "#8AC8BE"],
  115. [0.6, "#5CA8BF"],
  116. [0.75, "#407DB3"],
  117. [0.9, "#2E4C9A"],
  118. [1, "#1B2973"],
  119. ],
  120. // colorbar: {
  121. // title: data.colorbartitle, // 色标标题
  122. // },
  123. size: new Array(data.xData.length).fill(6), // 初始点大小
  124. },
  125. };
  126. } else if (this.chartType === "line") {
  127. // 折线图
  128. trace = {
  129. x: data.xData,
  130. y: data.yData,
  131. mode: "lines",
  132. type: "scattergl", // 折线图
  133. text: data.engineName,
  134. line: {
  135. color: this.color1, // 使用自定义颜色
  136. },
  137. };
  138. } else if (this.chartType === "bar") {
  139. // 柱状图
  140. trace = {
  141. x: data.xData,
  142. y: data.yData,
  143. type: "bar", // 柱状图
  144. marker: {
  145. color: this.color1, // 使用自定义颜色
  146. },
  147. };
  148. }
  149. // 图表布局;
  150. const layout = {
  151. title: data.title,
  152. xaxis: {
  153. title: this.chartData.xaixs,
  154. },
  155. yaxis: {
  156. title: this.chartData.yaixs,
  157. },
  158. showlegend: false,
  159. };
  160. const config = {
  161. modeBarButtonsToAdd: [
  162. {
  163. name: "选择",
  164. icon: Plotly.Icons.pencil,
  165. click: (gd) => this.handleSelectClick(gd),
  166. },
  167. {
  168. name: "清除选中",
  169. icon: Plotly.Icons.undo,
  170. click: (gd) => this.handleClearSelect(gd),
  171. },
  172. {
  173. name: "下载CSV文件",
  174. icon: Plotly.Icons.disk,
  175. click: (gd) => this.handleDownloadCSV(gd),
  176. },
  177. ],
  178. modeBarButtonsToRemove: [
  179. // 移除不需要的工具按钮
  180. "lasso2d",
  181. ],
  182. displaylogo: false,
  183. editable: true,
  184. scrollZoom: false,
  185. };
  186. // 使用 Plotly 绘制图表
  187. Plotly.react(this.$refs.plotlyChart, [trace], layout, config).then(() => {
  188. // 确保在图表加载完成后设置工具栏按钮
  189. const plotElement = this.$refs.plotlyChart;
  190. Plotly.relayout(plotElement, layout); // 使用 relayout 来确保自定义按钮应用
  191. });
  192. },
  193. handleSelectClick(gd) {
  194. // 绑定 plotly_click 事件
  195. gd.on("plotly_click", (data) => {
  196. const pointIndex = data.points[0].pointIndex;
  197. const xClick = data.points[0].x;
  198. const yClick = data.points[0].y;
  199. // 将点击的点添加到选中的点数组
  200. this.selectedPoints.push({
  201. x: xClick, // 点击点的 x 坐标
  202. y: yClick, // 点击点的 y 坐标
  203. index: pointIndex, // 点击点的索引
  204. time: data.points[0].text, // 点击点的时间信息
  205. });
  206. // 初始化颜色和大小数组
  207. let newColors = [...this.originalColors];
  208. let newSize = [...this.originalSizes];
  209. // 如果选中的点数大于等于3,进行多边形选择区域的处理
  210. if (this.selectedPoints.length >= 3) {
  211. const xv = this.selectedPoints.map((p) => p.x);
  212. const yv = this.selectedPoints.map((p) => p.y);
  213. // 判断点是否在多边形内
  214. function inPolygon(x, y, xv, yv) {
  215. let inside = false;
  216. for (let i = 0, j = xv.length - 1; i < xv.length; j = i++) {
  217. const intersect =
  218. yv[i] > y !== yv[j] > y &&
  219. x < ((xv[j] - xv[i]) * (y - yv[i])) / (yv[j] - yv[i]) + xv[i];
  220. if (intersect) inside = !inside;
  221. }
  222. return inside;
  223. }
  224. // 用于跟踪已添加的 (x, y) 组合
  225. const addedPoints = {};
  226. // 遍历图表数据中的所有点,检查是否在多边形内
  227. gd.data[0].x.forEach((xVal, i) => {
  228. const yVal = gd.data[0].y[i];
  229. if (inPolygon(xVal, yVal, xv, yv)) {
  230. const pointKey = `${xVal}-${yVal}`;
  231. if (!addedPoints[pointKey]) {
  232. this.selectedPoints.push({
  233. x: gd.data[0].x[i],
  234. y: gd.data[0].y[i],
  235. time: gd.data[0].text[i],
  236. });
  237. newColors[i] = "red"; // 高亮选择的点
  238. newSize[i] = 10; // 设置点的大小
  239. addedPoints[pointKey] = true;
  240. }
  241. }
  242. });
  243. }
  244. // 更新选中点的颜色和大小
  245. this.selectedPoints.forEach((point) => {
  246. newColors[point.index] = "red";
  247. newSize[point.index] = 10;
  248. });
  249. // 使用 Plotly.restyle 更新颜色和大小
  250. Plotly.restyle(gd, {
  251. "marker.color": [newColors],
  252. "marker.size": [newSize],
  253. });
  254. // 处理选中的数据
  255. this.getSelectData(this.selectedPoints, gd.layout);
  256. });
  257. },
  258. handleClearSelect(gd) {
  259. this.selectedPoints = [];
  260. Plotly.restyle(gd, {
  261. "marker.color": [this.originalColors],
  262. "marker.size": [this.originalSizes],
  263. });
  264. },
  265. getSelectData(selectedPoints, layout) {
  266. // 在这里处理选中的数据,您可以将其展示或导出等
  267. console.log("选中的点数据:", selectedPoints);
  268. console.log("布局信息:", layout);
  269. },
  270. handleDownloadCSV(gd) {
  271. if (this.selectedPoints.length === 0) {
  272. alert("没有选中的数据");
  273. return;
  274. }
  275. this.downloadCSV();
  276. },
  277. downloadCSV() {
  278. const headers = [this.chartData.xaixs, this.chartData.yaixs];
  279. const csvRows = [headers]; // 保存标头
  280. // 使用 Set 或 Map 去重
  281. const uniquePoints = [];
  282. this.selectedPoints.forEach((point) => {
  283. if (!uniquePoints.some((p) => p.x === point.x && p.y === point.y)) {
  284. uniquePoints.push(point);
  285. }
  286. });
  287. // 将去重后的点加入 CSV 数据
  288. uniquePoints.forEach((point) => {
  289. csvRows.push(`${point.x},${point.y}`);
  290. });
  291. const csvString = csvRows.join("\n");
  292. const blob = new Blob([csvString], { type: "text/csv; charset=utf-8" });
  293. const url = URL.createObjectURL(blob);
  294. const a = document.createElement("a");
  295. a.href = url;
  296. a.download = "selected_data.csv";
  297. a.click();
  298. URL.revokeObjectURL(url);
  299. },
  300. setChartType(type) {
  301. // 切换图表类型
  302. this.chartType = type;
  303. this.drawChart(); // 重新绘制图表
  304. },
  305. updateChartColor(color) {
  306. // 更新图表颜色
  307. this.color1 = color;
  308. console.log(this.color1, "this.color1");
  309. this.drawChart();
  310. },
  311. },
  312. };
  313. </script>
  314. <style scoped></style>