simulation.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <template>
  2. <div class="realtime-container">
  3. <div ref="chart" class="chart" :style="{height: '500px', width: '100%'}" />
  4. </div>
  5. </template>
  6. <script>
  7. import * as echarts from "echarts";
  8. require('echarts/theme/macarons') // echarts theme
  9. import resize from '@/utils/resize'
  10. import { getConfig } from '@/api/site/site'
  11. import { getSiteSection } from '@/api/site/berthing'
  12. import { getCarLocation, getWaterLevel } from '@/api/analysis/achievement'
  13. import CarSvg from '@/assets/images/car.svg'
  14. import BarSvg from '@/assets/images/bar.svg'
  15. export default {
  16. mixins: [resize],
  17. props: {
  18. siteId: Number | String,
  19. },
  20. data() {
  21. return {
  22. sections: [],
  23. config: {},
  24. location: 0,
  25. waterlevel: 0,
  26. }
  27. },
  28. mounted() {
  29. this.loadSection();
  30. this.loadSiteConfig();
  31. this.loadCarLocation();
  32. this.timer1 = setInterval(() => this.loadCarLocation(), 5e3)
  33. this.loadWaterLevel();
  34. this.timer2 = setInterval(() => this.loadWaterLevel(), 5e3)
  35. this.$nextTick(() => {
  36. this.chart = echarts.init(this.$refs.chart, 'macarons');
  37. })
  38. },
  39. beforeDestroy() {
  40. if (this.timer1) clearTimeout(this.timer1);
  41. if (this.timer2) clearTimeout(this.timer2);
  42. if (this.chart) {
  43. this.chart.dispose()
  44. this.chart = null
  45. }
  46. },
  47. methods: {
  48. loadCarLocation() {
  49. getCarLocation(this.siteId).then((res) => {
  50. this.location = res.data?.position || 0;
  51. this.setOptions();
  52. })
  53. },
  54. loadSection() {
  55. getSiteSection(this.siteId).then((res) => {
  56. this.sections = JSON.parse(res.data.positions) || [];
  57. this.setOptions();
  58. })
  59. },
  60. loadWaterLevel() {
  61. getWaterLevel(this.siteId).then((res) => {
  62. this.waterlevel = res.data.waterlevel || 0;
  63. this.setOptions();
  64. })
  65. },
  66. loadSiteConfig() {
  67. getConfig(this.siteId).then((res) => {
  68. this.config = res.data || {};
  69. this.setOptions();
  70. })
  71. },
  72. setOptions() {
  73. if (!this.chart || !this.config || this.sections.length === 0) {
  74. return;
  75. }
  76. const carWidth = 50; // 小车宽度
  77. const carHeight = 32; // 小车高度
  78. const gap = carWidth / 2; // 断面左右留的边距,方便绘制立柱
  79. const barWidth = 18; // 立柱宽度
  80. const waterlevel = this.waterlevel;
  81. const start = this.config.offset;
  82. const location = this.location;
  83. const sections = [
  84. [this.sections[0].x - gap, this.sections[0].y],
  85. ...this.sections.map(({x, y}) => [x, y]),
  86. [this.sections[this.sections.length - 1].x + gap, this.sections[this.sections.length - 1].y],
  87. ];
  88. const x = sections.map(([x]) => x);
  89. const maxX = Math.max(...x);
  90. const minX = Math.min(...x);
  91. const bar1X = start - (carWidth - barWidth) / 2; // 左侧立柱x坐标
  92. const bar2X = maxX - (start - minX) + (carWidth - barWidth) / 2; // 右侧立柱x坐标
  93. const boxWidth = (carWidth - barWidth) * 2; // 小车充电盒子宽度
  94. const y = sections.map(([,y]) => y);
  95. const maxY = Math.max(...y);
  96. const minY = Math.min(...y);
  97. const disY = maxY - minY;
  98. const barY = maxY + disY * 1.2;
  99. const lineY = maxY + disY * 0.8;
  100. const options = {
  101. grid: {
  102. top: 0,
  103. left: 0,
  104. right: 0,
  105. bottom: 0,
  106. show: false,
  107. },
  108. xAxis: {
  109. type: 'value',
  110. min: minX,
  111. max: maxX,
  112. show: false,
  113. },
  114. yAxis: {
  115. min: minY - disY * 0.1,
  116. max: maxY + disY * 1.5,
  117. type: 'value',
  118. show: false,
  119. },
  120. series: [
  121. {
  122. data: sections,
  123. type: 'line',
  124. // symbol: 'none',
  125. animation: false,
  126. z: 10,
  127. lineStyle: {
  128. width: 1,
  129. color: '#FF8500',
  130. },
  131. areaStyle: {
  132. opacity: 1,
  133. color: '#ffc27f',
  134. }
  135. },
  136. {
  137. data: [[minX, waterlevel],[maxX, waterlevel]],
  138. type: 'line',
  139. symbol: 'none',
  140. z: 0,
  141. lineStyle: {
  142. width: 0,
  143. },
  144. areaStyle: {
  145. opacity: 1,
  146. color: '#a5cdf7',
  147. }
  148. },
  149. {
  150. data: [[bar1X, lineY],[bar2X, lineY]],
  151. type: 'line',
  152. symbol: 'none',
  153. z: 2,
  154. lineStyle: {
  155. width: 2,
  156. color: '#54606C',
  157. },
  158. },
  159. {
  160. data: [[bar1X, barY],[bar2X, barY]],
  161. type: 'bar',
  162. barWidth: barWidth,
  163. z: 3,
  164. itemStyle: {
  165. color: '#A6B7C7'
  166. }
  167. },
  168. {
  169. data: [[location, lineY]],
  170. type: 'scatter',
  171. symbol: `image://${CarSvg}`,
  172. symbolSize: [carWidth, carHeight],
  173. symbolOffset: [0, -6],
  174. silent: true,
  175. z: 3,
  176. itemStyle: {
  177. opacity: 1
  178. }
  179. },
  180. {
  181. data: [[start, lineY]],
  182. type: 'scatter',
  183. symbol: 'rect',
  184. symbolSize: [boxWidth, 30],
  185. symbolOffset: [0, '-50%'],
  186. silent: true,
  187. z: 4,
  188. itemStyle: {
  189. color: '#778CB2',
  190. opacity: 0.5
  191. }
  192. },
  193. ]
  194. };
  195. this.chart.setOption(options);
  196. },
  197. }
  198. }
  199. </script>
  200. <style scoped>
  201. .realtime-container {
  202. height: 500px;
  203. background: linear-gradient(0, #FFFFFF 57%, #D1E8FF 100%);
  204. border-radius: 4px;
  205. }
  206. </style>