WindRoseChart.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <div>
  3. <div
  4. v-loading="loading"
  5. :id="`plotDiv-${inds}`"
  6. style="width: 100%; height: 450px"
  7. >
  8. <el-empty v-if="isError" description="请求失败"></el-empty>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. import Plotly from "plotly.js-dist";
  14. import axios from "axios";
  15. import { myMixin } from "@/mixins/chartRequestMixin";
  16. import { colorSchemes } from "@/views/overview/js/colors";
  17. import { mapState } from "vuex";
  18. export default {
  19. props: {
  20. fileAddr: {
  21. default: "",
  22. type: String,
  23. },
  24. inds: {
  25. type: [Number, String],
  26. default: 1,
  27. },
  28. },
  29. mixins: [myMixin],
  30. data() {
  31. return {
  32. // 数据结构
  33. chartData: {},
  34. color1: colorSchemes[0].colors, // 默认颜色
  35. // 配色方案列表(每个方案是一个颜色数组)
  36. colorSchemes: [...colorSchemes],
  37. };
  38. },
  39. computed: {
  40. ...mapState("themes", {
  41. themeColor: "themeColor",
  42. }),
  43. },
  44. watch: {
  45. chartData: {
  46. deep: true,
  47. handler(v) {
  48. if (v) {
  49. this.renderChart();
  50. }
  51. },
  52. },
  53. themeColor: {
  54. handler() {
  55. this.color1 = this.themeColor;
  56. this.renderChart();
  57. },
  58. deep: true,
  59. },
  60. },
  61. mounted() {
  62. this.$nextTick(() => {
  63. this.getData();
  64. // if (this.themeColor.length === 0) {
  65. this.color1 = this.colorSchemes[0].colors;
  66. // } else {
  67. // this.color1 = this.themeColor;
  68. // }
  69. });
  70. },
  71. methods: {
  72. async getData() {
  73. if (this.fileAddr !== "") {
  74. try {
  75. this.loading = true;
  76. this.cancelToken = axios.CancelToken.source();
  77. const resultChartsData = await axios.get(this.fileAddr, {
  78. cancelToken: this.cancelToken.token,
  79. });
  80. this.chartData = resultChartsData.data;
  81. this.renderChart();
  82. this.isError = false;
  83. this.loading = false;
  84. } catch (error) {
  85. this.isError = true;
  86. this.loading = false;
  87. }
  88. }
  89. },
  90. renderChart() {
  91. const { axes, data, analysisTypeCode } = this.chartData;
  92. // 从数据中提取 windSpeedRange 和动态生成 speedLabels 和 colorscale
  93. const windSpeedRanges = new Set();
  94. data.forEach((engine) => {
  95. engine.windRoseData.forEach((item) => {
  96. windSpeedRanges.add(item.windSpeedRange);
  97. });
  98. });
  99. const speedLabels = Array.from(windSpeedRanges).sort(); // 动态范围值
  100. const colors = [...this.color1]; // 可扩展颜色列表
  101. const colorscale = {};
  102. speedLabels.forEach((label, index) => {
  103. colorscale[label] = colors[(index % colors.length) + 4];
  104. });
  105. // 定义风向的 16 等分
  106. const windDirections = Array.from({ length: 16 }, (_, i) => i * 22.5);
  107. // 数据聚合
  108. const counts = {};
  109. speedLabels.forEach((speedLabel) => {
  110. counts[speedLabel] = Array(windDirections.length).fill(0);
  111. });
  112. data.forEach((engine) => {
  113. engine.windRoseData.forEach((item) => {
  114. const { windDirection, windSpeedRange } = item;
  115. const index = windDirections.findIndex(
  116. (dir, i) =>
  117. windDirection >= dir && windDirection < windDirections[i + 1]
  118. );
  119. if (index !== -1 && counts[windSpeedRange]) {
  120. counts[windSpeedRange][index] += item.frequency;
  121. }
  122. });
  123. });
  124. // 构建 traces
  125. const traces = speedLabels.map((speedLabel) => {
  126. const percentage = counts[speedLabel];
  127. return {
  128. r: percentage,
  129. theta: windDirections,
  130. name: speedLabel,
  131. type: "barpolar",
  132. marker: {
  133. color: colorscale[speedLabel],
  134. line: {
  135. color: "white",
  136. width: 1,
  137. },
  138. },
  139. hovertemplate:
  140. `${axes.radial}:` +
  141. ` %{r} <br> ` +
  142. `${axes.angular}:` +
  143. "%{theta} <br> <extra></extra>",
  144. };
  145. });
  146. // 图表布局
  147. const layout = {
  148. title: {
  149. // text: `${analysisTypeCode} - ${data[0]?.enginName} `,
  150. text: `风向玫瑰图 - ${data[0]?.enginName} `,
  151. font: {
  152. size: 16, // 设置标题字体大小(默认 16)
  153. weight: "bold",
  154. },
  155. },
  156. plot_bgcolor: "#e5ecf6",
  157. polar: {
  158. bgcolor: "#e5ecf6",
  159. radialaxis: {
  160. title: { text: axes.radial },
  161. gridcolor: "rgb(255,255,255)",
  162. showgrid: true,
  163. linecolor: "rgb(255,255,255)",
  164. },
  165. angularaxis: {
  166. title: { text: axes.angular },
  167. tickvals: windDirections,
  168. gridcolor: "rgb(255,255,255)",
  169. tickcolor: "rgb(255,255,255)",
  170. linecolor: "rgb(255,255,255)",
  171. showticklabels: false, // 隐藏角度标签
  172. },
  173. },
  174. showlegend: true,
  175. legend: { title: { text: axes.levelname } },
  176. };
  177. Plotly.newPlot(`plotDiv-${this.inds}`, traces, layout, {
  178. modeBarButtonsToRemove: [
  179. // 移除不需要的工具按钮
  180. "select2d", // 对应 "Box Select"
  181. "lasso2d", // 对应 "Lasso Select"
  182. "zoom2d", // 缩放按钮
  183. ],
  184. displaylogo: false,
  185. responsive: true,
  186. }).then(function (gd) {
  187. // 获取工具栏按钮
  188. const toolbar = gd.querySelector(".modebar");
  189. const buttons = toolbar.querySelectorAll(".modebar-btn");
  190. // 定义一个映射对象,方便修改按钮提示
  191. const titleMap = {
  192. "Download plot as a png": "保存图片",
  193. Autoscale: "缩放",
  194. Pan: "平移",
  195. "Zoom out": "缩小",
  196. "Zoom in": "放大",
  197. "Box Select": "选择框操作",
  198. "Lasso Select": "套索选择操作",
  199. "Reset axes": "重置操作",
  200. "Reset camera to default": "重置相机视角",
  201. "Turntable rotation": "转台式旋转",
  202. "Orbital rotation": "轨道式旋转",
  203. };
  204. // 遍历所有按钮,修改它们的 title
  205. buttons.forEach(function (button) {
  206. const dataTitle = button.getAttribute("data-title");
  207. // 如果标题匹配,修改属性值
  208. if (titleMap[dataTitle]) {
  209. button.setAttribute("data-title", titleMap[dataTitle]);
  210. }
  211. });
  212. });
  213. },
  214. },
  215. };
  216. </script>
  217. <style scoped></style>