Time3DChart.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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. <div
  37. v-loading="loading"
  38. :id="`plotly-3d-chart-` + index"
  39. ref="plotlyChart"
  40. style="height: 600px"
  41. >
  42. <el-empty v-if="isError" description="请求失败"></el-empty>
  43. </div>
  44. </div>
  45. </template>
  46. <script>
  47. import Plotly from "plotly.js-dist";
  48. import axios from "axios";
  49. import { myMixin } from "@/mixins/chartRequestMixin";
  50. import { colorSchemes } from "@/views/overview/js/colors";
  51. import { mapState } from "vuex";
  52. export default {
  53. props: {
  54. fileAddr: {
  55. default: "",
  56. type: String,
  57. },
  58. index: {
  59. default: "",
  60. type: String,
  61. },
  62. setUpImgData: {
  63. default: () => [],
  64. type: Array,
  65. },
  66. },
  67. data() {
  68. return {
  69. color1: [
  70. "#DBEEBC",
  71. "#A8D7BE",
  72. "#8ECAC1",
  73. "#77BDC2",
  74. "#64ADC2",
  75. "#559ABE",
  76. "#4884B7",
  77. "#406DAB",
  78. "#3856A0",
  79. "#314291",
  80. "#28357A",
  81. "#1A285E",
  82. "#FF7F50",
  83. "#FFD700",
  84. "#90EE90",
  85. ], // 默认颜色
  86. chartData: {},
  87. chartType: "scatter", // 当前图表类型(默认是散点图)
  88. colorSchemes: [...colorSchemes],
  89. pointSize: 2, // 默认点大小
  90. };
  91. },
  92. mixins: [myMixin],
  93. computed: {
  94. ...mapState("themes", {
  95. themeColor: "themeColor",
  96. }),
  97. },
  98. watch: {
  99. themeColor: {
  100. handler() {
  101. this.color1 = this.themeColor;
  102. this.updateChartColor();
  103. },
  104. deep: true,
  105. },
  106. setUpImgData: {
  107. handler(newType) {
  108. this.renderChart();
  109. },
  110. deep: true,
  111. },
  112. },
  113. async mounted() {
  114. this.getData();
  115. this.color1 = this.themeColor;
  116. },
  117. methods: {
  118. async getData() {
  119. if (this.fileAddr !== "") {
  120. try {
  121. this.loading = true;
  122. this.cancelToken = axios.CancelToken.source();
  123. const resultChartsData = await axios.get(this.fileAddr, {
  124. cancelToken: this.cancelToken.token,
  125. });
  126. if (typeof resultChartsData.data === "string") {
  127. let dataString = resultChartsData.data;
  128. dataString = dataString.trim(); // 去除前后空格
  129. dataString = dataString.replace(/Infinity/g, '"Infinity"'); // 处理无效字符
  130. try {
  131. const parsedData = JSON.parse(dataString);
  132. this.chartData = parsedData;
  133. } catch (error) {
  134. console.error("JSON 解析失败:", error);
  135. }
  136. } else {
  137. this.chartData = resultChartsData.data;
  138. }
  139. this.renderChart();
  140. this.isError = false;
  141. this.loading = false;
  142. } catch (error) {
  143. this.isError = true;
  144. this.loading = false;
  145. }
  146. }
  147. },
  148. // 格式化日期为 YY-MM 格式
  149. formatDate(dateString) {
  150. const date = new Date(dateString);
  151. const year = date.getFullYear().toString().slice(2); // 获取年份后两位
  152. const month = ("0" + (date.getMonth() + 1)).slice(-2); // 获取月份并确保两位数
  153. return `${year}-${month}`;
  154. },
  155. renderChart() {
  156. // 提取 Y 轴数据中的月份,并去重
  157. const uniqueMonths = Array.from(
  158. new Set(
  159. this.chartData.data[0].yData.map((date) => this.formatDate(date))
  160. )
  161. );
  162. if (!this.color1) {
  163. this.color1 = colorSchemes[0].colors;
  164. }
  165. // 设置每个月份对应的颜色
  166. const monthColors = this.color1;
  167. // 为每个月份生成独立的 trace,每个 trace 对应一个月份
  168. const traces = uniqueMonths.map((month, monthIndex) => {
  169. const monthData = this.chartData.data[0].yData
  170. .map((date, index) => (this.formatDate(date) === month ? index : -1))
  171. .filter((index) => index !== -1);
  172. const trace = {
  173. x: monthData.map((index) => this.chartData.data[0].xData[index]), // 发电机转速
  174. y: monthData.map((index) => this.chartData.data[0].yData[index]), // 时间
  175. z: monthData.map((index) => this.chartData.data[0].zData[index]), // 有功功率
  176. mode: "markers",
  177. type: "scatter3d", // 3D 散点图
  178. marker: {
  179. size: this.pointSize,
  180. color: monthColors[monthIndex],
  181. },
  182. name: month, // 图例项名称,格式为 YY-MM
  183. legendgroup: `month-${monthIndex}`, // 图例分组
  184. hovertemplate:
  185. `${this.chartData.xaixs}:` +
  186. ` %{x} <br> ` +
  187. `${this.chartData.yaixs}:` +
  188. "%{y} <br>" +
  189. `${this.chartData.zaixs}:` +
  190. "%{z} <br>",
  191. };
  192. return trace;
  193. });
  194. const layout = {
  195. title: this.chartData.data[0].title,
  196. scene: {
  197. xaxis: {
  198. title: this.chartData.xaixs, // X 轴标题
  199. gridcolor: "rgb(255,255,255)",
  200. tickcolor: "rgb(255,255,255)",
  201. backgroundcolor: "#e5ecf6",
  202. showbackground: true,
  203. },
  204. yaxis: {
  205. title: this.chartData.yaixs, // Y 轴标题
  206. tickformat: "%y-%m", // 设置 Y 轴的时间格式为 YY-MM
  207. gridcolor: "rgb(255,255,255)",
  208. tickcolor: "rgb(255,255,255)",
  209. backgroundcolor: "#e5ecf6",
  210. showbackground: true,
  211. },
  212. zaxis: {
  213. title: this.chartData.zaixs, // Z 轴标题
  214. gridcolor: "rgb(255,255,255)",
  215. tickcolor: "rgb(255,255,255)",
  216. backgroundcolor: "#e5ecf6",
  217. showbackground: true,
  218. },
  219. // aspectratio: {
  220. // x: 1,
  221. // y: 3,
  222. // z: 1,
  223. // },
  224. plot_bgcolor: "#e5ecf6",
  225. gridcolor: "#fff",
  226. // camera: {
  227. // center: { x: 0, y: 0, z: 0 },
  228. // eye: { x: 0, y: 0, z: 2.5 },
  229. // up: { x: 0, y: 0, z: 1 },
  230. // },
  231. },
  232. margin: { t: 50, b: 50, l: 50, r: 50 },
  233. staticPlot: false,
  234. showlegend: true,
  235. legend: {
  236. marker: {
  237. size: 10, // 图例中点的大小
  238. },
  239. },
  240. };
  241. // 获取x轴和y轴的设置
  242. const getChartSetUp = (axisTitle) => {
  243. return this.setUpImgData.find((item) => item.text.includes(axisTitle));
  244. };
  245. // 更新x轴和y轴的范围与步长
  246. const xChartSetUp = getChartSetUp(layout.scene.xaxis.title);
  247. if (xChartSetUp) {
  248. layout.scene.xaxis.dtick = xChartSetUp.dtick;
  249. layout.scene.xaxis.range = [xChartSetUp.min, xChartSetUp.max];
  250. }
  251. const yChartSetUp = getChartSetUp(layout.scene.yaxis.title);
  252. if (yChartSetUp) {
  253. layout.scene.yaxis.dtick = yChartSetUp.dtick;
  254. layout.scene.yaxis.range = [yChartSetUp.min, yChartSetUp.max];
  255. }
  256. const zChartSetUp = getChartSetUp(layout.scene.zaxis.title);
  257. if (zChartSetUp) {
  258. layout.scene.zaxis.dtick = zChartSetUp.dtick;
  259. layout.scene.zaxis.range = [zChartSetUp.min, zChartSetUp.max];
  260. }
  261. console.log(zChartSetUp, yChartSetUp, xChartSetUp, "xChartSetUp");
  262. Plotly.newPlot(`plotly-3d-chart-` + this.index, traces, layout);
  263. },
  264. updateChartColor() {
  265. this.renderChart(); // 当配色方案或点大小发生变化时重新渲染图表
  266. },
  267. // 获取配色选项样式
  268. getOptionStyle(scheme) {
  269. return {
  270. background: `linear-gradient(to right, ${scheme
  271. .slice(0, 8)
  272. .join(", ")})`,
  273. color: "#fff",
  274. height: "30px",
  275. lineHeight: "30px",
  276. borderRadius: "0px",
  277. };
  278. },
  279. },
  280. };
  281. </script>
  282. <style scoped>
  283. /* 样式可以根据需求自定义 */
  284. #plotly-3d-chart {
  285. width: 100%;
  286. height: 500px;
  287. }
  288. ::v-deep canvas {
  289. /* height: 400px !important; */
  290. }
  291. </style>