3DDrawingChart.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <template>
  2. <div>
  3. <!-- 配色方案选择和图表类型切换 -->
  4. <div style="display: flex; align-items: center; padding-top: 20px">
  5. <div style="margin-right: 20px; display: flex; align-items: center">
  6. <el-select
  7. size="small"
  8. v-model="color1"
  9. @change="updateChartColor"
  10. placeholder="选择配色方案"
  11. style="width: 200px"
  12. >
  13. <el-option
  14. v-for="(scheme, index) in colorSchemes"
  15. :key="index"
  16. :label="scheme.label"
  17. :value="scheme.colors"
  18. :style="getOptionStyle(scheme.colors)"
  19. ></el-option>
  20. </el-select>
  21. </div>
  22. </div>
  23. <!-- 点大小控制 -->
  24. <div style="display: flex; align-items: center">
  25. <el-slider
  26. v-model="pointSize"
  27. :min="1"
  28. :max="15"
  29. :step="1"
  30. label="点的大小"
  31. show-stops
  32. style="width: 150px"
  33. @change="updateChartColor"
  34. ></el-slider>
  35. </div>
  36. <!-- 图表展示区域 -->
  37. <div style="height: 500px">
  38. <div
  39. v-loading="loading"
  40. :id="`plotly-3d-chart-` + index"
  41. ref="plotlyChart"
  42. style="height: 500px; background-color: #e5ecf6"
  43. >
  44. <el-empty v-if="isError" description="请求失败"></el-empty>
  45. </div>
  46. </div>
  47. </div>
  48. </template>
  49. <script>
  50. import Plotly from "plotly.js-dist";
  51. import axios from "axios";
  52. import { myMixin } from "@/mixins/chartRequestMixin";
  53. import { colorSchemes } from "@/views/overview/js/colors";
  54. import { mapState } from "vuex";
  55. export default {
  56. props: {
  57. fileAddr: {
  58. default: "",
  59. type: String,
  60. },
  61. index: {
  62. default: "",
  63. type: String,
  64. },
  65. setUpImgData: {
  66. default: () => [],
  67. type: Array,
  68. },
  69. },
  70. data() {
  71. return {
  72. color1: [
  73. "#DBEEBC",
  74. "#A8D7BE",
  75. "#8ECAC1",
  76. "#77BDC2",
  77. "#64ADC2",
  78. "#559ABE",
  79. "#4884B7",
  80. "#406DAB",
  81. "#3856A0",
  82. "#314291",
  83. "#28357A",
  84. "#1A285E",
  85. "#FF7F50",
  86. "#FFD700",
  87. "#90EE90",
  88. ], // 默认颜色
  89. chartData: {},
  90. chartType: "scatter", // 当前图表类型(默认是散点图)
  91. colorSchemes: [...colorSchemes],
  92. pointSize: 2, // 默认点大小
  93. };
  94. },
  95. mixins: [myMixin],
  96. async mounted() {
  97. this.getData();
  98. this.color1 = this.themeColor;
  99. },
  100. computed: {
  101. ...mapState("themes", {
  102. themeColor: "themeColor",
  103. }),
  104. },
  105. watch: {
  106. themeColor: {
  107. handler() {
  108. this.color1 = this.themeColor;
  109. this.updateChartColor();
  110. },
  111. deep: true,
  112. },
  113. setUpImgData: {
  114. handler(newType) {
  115. this.renderChart();
  116. },
  117. deep: true,
  118. },
  119. },
  120. methods: {
  121. async getData() {
  122. if (this.fileAddr !== "") {
  123. try {
  124. this.loading = true;
  125. this.cancelToken = axios.CancelToken.source();
  126. const resultChartsData = await axios.get(this.fileAddr, {
  127. cancelToken: this.cancelToken.token,
  128. });
  129. if (typeof resultChartsData.data === "string") {
  130. let dataString = resultChartsData.data;
  131. dataString = dataString.trim(); // 去除前后空格
  132. dataString = dataString.replace(/Infinity/g, '"Infinity"'); // 替换 Infinity 为 "Infinity"
  133. try {
  134. const parsedData = JSON.parse(dataString);
  135. this.chartData = parsedData;
  136. } catch (error) {
  137. console.error("JSON 解析失败:", error);
  138. }
  139. } else {
  140. this.chartData = resultChartsData.data;
  141. }
  142. this.renderChart();
  143. this.isError = false;
  144. this.loading = false;
  145. } catch (error) {
  146. this.isError = true;
  147. this.loading = false;
  148. }
  149. }
  150. },
  151. // 更新配色方案
  152. updateChartColor() {
  153. this.renderChart(); // 当配色方案或点大小发生变化时重新渲染图表
  154. },
  155. // 切换图表类型
  156. setChartType(type) {
  157. this.chartType = type;
  158. this.renderChart(); // 切换图表类型时重新渲染图表
  159. },
  160. // 获取配色选项样式
  161. getOptionStyle(scheme) {
  162. return {
  163. background: `linear-gradient(to right, ${scheme
  164. .slice(0, 8)
  165. .join(", ")})`,
  166. color: "#fff",
  167. height: "30px",
  168. lineHeight: "30px",
  169. borderRadius: "0px",
  170. };
  171. },
  172. renderChart() {
  173. console.log(this.setUpImgData, "setUpImgData");
  174. const uniqueColors = [...new Set(this.chartData.data[0].color)];
  175. if (!this.color1) {
  176. this.color1 = colorSchemes[0].colors;
  177. }
  178. const traces = uniqueColors.map((color, idx) => {
  179. const colorData = this.chartData.data[0].color.map((c) =>
  180. c === color ? 1 : 0
  181. );
  182. const trace = {
  183. x: this.chartData.data[0].xData.filter((_, i) => colorData[i] === 1),
  184. y: this.chartData.data[0].yData.filter((_, i) => colorData[i] === 1),
  185. z: this.chartData.data[0].zData.filter((_, i) => colorData[i] === 1),
  186. mode: this.chartType === "scatter" ? "markers" : "lines", // 根据选择的图表类型来设置模式
  187. type: "scatter3d",
  188. marker: {
  189. size: this.pointSize, // 使用动态点大小
  190. color: this.color1[idx], // 使用配色方案
  191. colorscale: "YlGnBu",
  192. },
  193. name: ` ${color}`,
  194. legendgroup: `group-${idx}`,
  195. hovertemplate:
  196. `${this.chartData.xaixs}:` +
  197. ` %{x} <br> ` +
  198. `${this.chartData.yaixs}:` +
  199. "%{y} <br>" +
  200. `${this.chartData.zaixs}:` +
  201. "%{z} <br>",
  202. };
  203. return trace;
  204. });
  205. const layout = {
  206. title: this.chartData.data[0].title,
  207. scene: {
  208. xaxis: {
  209. title: this.chartData.xaixs,
  210. gridcolor: "rgb(255,255,255)",
  211. tickcolor: "rgb(255,255,255)",
  212. backgroundcolor: "#e5ecf6",
  213. showbackground: true,
  214. },
  215. yaxis: {
  216. title: this.chartData.yaixs,
  217. tickvals: [...new Set(this.chartData.data[0].yData)],
  218. ticktext: [...new Set(this.chartData.data[0].yData)],
  219. gridcolor: "rgb(255,255,255)",
  220. tickcolor: "rgb(255,255,255)",
  221. backgroundcolor: "#e5ecf6",
  222. showbackground: true,
  223. },
  224. zaxis: {
  225. title: this.chartData.zaixs,
  226. gridcolor: "rgb(255,255,255)",
  227. tickcolor: "rgb(255,255,255)",
  228. backgroundcolor: "#e5ecf6",
  229. showbackground: true,
  230. },
  231. aspectratio: {
  232. x: 3,
  233. y: 1,
  234. z: 1,
  235. },
  236. // plot_bgcolor: "#e5ecf6",
  237. // gridcolor: "#fff",
  238. camera: {
  239. center: { x: -0.233946, y: -0.319396, z: 0.483055 },
  240. eye: { x: -1.729102, y: -3.506569, z: -1.296184 },
  241. up: { x: 0.12359, y: 0.43886022, z: -0.89001518 },
  242. projection: {
  243. type: "perspective",
  244. },
  245. },
  246. },
  247. margin: { t: 50, b: 50, l: 50, r: 50 },
  248. staticPlot: false,
  249. showlegend: true,
  250. legend: {
  251. marker: {
  252. size: 10, // 图例中点的大小
  253. },
  254. },
  255. };
  256. // 获取x轴和y轴的设置
  257. const getChartSetUp = (axisTitle) => {
  258. return this.setUpImgData.find((item) => item.text.includes(axisTitle));
  259. };
  260. // 更新x轴和y轴的范围与步长
  261. const xChartSetUp = getChartSetUp(layout.scene.xaxis.title);
  262. if (xChartSetUp) {
  263. layout.scene.xaxis.dtick = xChartSetUp.dtick;
  264. layout.scene.xaxis.range = [xChartSetUp.min, xChartSetUp.max];
  265. }
  266. const yChartSetUp = getChartSetUp(layout.scene.yaxis.title);
  267. if (yChartSetUp) {
  268. layout.scene.yaxis.dtick = yChartSetUp.dtick;
  269. layout.scene.yaxis.range = [yChartSetUp.min, yChartSetUp.max];
  270. }
  271. const zChartSetUp = getChartSetUp(layout.scene.zaxis.title);
  272. if (zChartSetUp) {
  273. layout.scene.zaxis.dtick = zChartSetUp.dtick;
  274. layout.scene.zaxis.range = [zChartSetUp.min, zChartSetUp.max];
  275. }
  276. try {
  277. // 假设这里是 WebGL 的相关初始化代码
  278. Plotly.react(`plotly-3d-chart-` + this.index, traces, layout).catch(
  279. (err) => {
  280. console.error("WebGL 错误: ", err);
  281. // 你可以根据错误类型做更多处理
  282. if (err.message.includes("shaderSource")) {
  283. // alert("着色器编译失败!");
  284. }
  285. }
  286. );
  287. // 监听图表的 relayout 事件,获取并输出相机视角
  288. const plotElement = document.getElementById(
  289. `plotly-3d-chart-` + this.index
  290. );
  291. plotElement.on("plotly_relayout", function (eventData) {
  292. // 在每次布局变更时,打印当前相机视角
  293. if (eventData["scene.camera"]) {
  294. console.log(
  295. "当前相机视角:",
  296. eventData["scene.camera"],
  297. eventData["scene.aspectratio"]
  298. );
  299. }
  300. });
  301. } catch (e) {
  302. console.error("捕获到 WebGL 错误:", e);
  303. // alert("图表渲染失败!");
  304. }
  305. // Plotly.newPlot(`plotly-3d-chart-` + this.index, traces, layout);
  306. },
  307. },
  308. };
  309. </script>
  310. <style scoped>
  311. #scene {
  312. background: #e5ecf6 !important;
  313. }
  314. .js-plotly-plot .plotly,
  315. .js-plotly-plot .plotly div {
  316. background: #e5ecf6 !important;
  317. }
  318. /* 样式可以根据需求自定义 */
  319. #plotly-3d-chart {
  320. width: 100%;
  321. height: 500px;
  322. }
  323. ::v-deep canvas {
  324. /* height: 400px !important; */
  325. }
  326. </style>