windMap3D.vue 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. <template>
  2. <div id="loading" class="dataLoading" v-if="loading">
  3. <span class="loadText">数据加载中...</span>
  4. </div>
  5. <div class="mapBox">
  6. <div id="cesiumContainer" style="width: 100%; height: 100vh"></div>
  7. <div :class="!menuComTStyB ? 'menuComTSty' : 'menuComT'" v-if="0">
  8. <menuCom
  9. :showbasemap="false"
  10. :showwindspeed="false"
  11. :showcloud="false"
  12. :showrainfall="false"
  13. :showtemperature="false"
  14. :showcity="false"
  15. :showwind="false"
  16. :showexit="true"
  17. :showwindModel="true"
  18. @handleWindModel="showwindmodel"
  19. @handleInit="handleInitView"
  20. @handleExit="switchLayer"
  21. />
  22. </div>
  23. <el-drawer
  24. v-model="windDrawer"
  25. direction="rtl"
  26. class="windModelDrawer"
  27. :before-close="handleClose"
  28. >
  29. <template #header>
  30. <h3 style="font-weight: bold">{{ windDrawerHeader }}</h3>
  31. </template>
  32. <template #default>
  33. <div class="windDrawerCla">
  34. <div class="line" v-if="!showModelMsg">
  35. <div class="leftContent">
  36. <span>{{ windDrawerTitle }}</span>
  37. </div>
  38. </div>
  39. <div class="jcxx" v-if="showBasicMsg">
  40. <windHome :modelValItem="modelVal" />
  41. </div>
  42. <div class="spjk" v-if="showVideoMsg">
  43. <!-- <iframe
  44. src="/public/static/windVideo.mp4"
  45. frameborder="0"
  46. style="width: 100%; height: 100%"
  47. ></iframe> -->
  48. <video ref="videoPlayer" controls width="95%">
  49. <source src="/public/static/windVideo.mp4" type="video/mp4" />
  50. </video>
  51. </div>
  52. <div class="gzck" v-if="showProblemMsg">
  53. <windPro />
  54. </div>
  55. <div class="third" v-if="showModelMsg">
  56. <ModelUnpack
  57. :modelUnpackType="modelUnpackType"
  58. :showModelMsg="showModelMsg"
  59. />
  60. </div>
  61. </div>
  62. </template>
  63. </el-drawer>
  64. <comModelDialog
  65. :showcomModelDia="showcomModelDia"
  66. :modelVal="modelVal"
  67. @showDia="showComDia"
  68. />
  69. <windView v-if="showWindDetail" @coverOnChange="coverOnChange" :currentHeight="currentHeight"
  70. @showDetail="menuComTSty" @backStations="backStations" />
  71. </div>
  72. </template>
  73. <script>
  74. import * as Cesium from "../../Cesium";
  75. import "../../Cesium/Widgets/widgets.css";
  76. import fjLonLatJson from "./fjLonLat.json";
  77. import allStationJson from "./allStationJson.json"
  78. import basicGeoJson from "../../assets/geoJson/basic.json";
  79. import comModelDialog from "@/components/comModelDialog.vue";
  80. import windView from "./windView.vue";
  81. import menuCom from "../menuCom.vue";
  82. import cloudJson from "/public/static/exportData/cloud/layer.json";
  83. import rainJson from "/public/static/exportData/rain/layer.json";
  84. import tempJson from "/public/static/exportData/tmp/layer.json";
  85. // import bw from "@/assets/windimgs/fanSvg/bw.svg"
  86. //风场展示图标
  87. import fc from "@/assets/windimgs/fanSvg/fc.png"
  88. //火电展示图标
  89. import hd from "@/assets/windimgs/fanSvg/hd.png"
  90. //光伏电站展示图标
  91. import gf from "@/assets/windimgs/fanSvg/gf.png"
  92. //故障
  93. import gz from "@/assets/windimgs/fanSvg/gz.svg"
  94. //待机
  95. import dj from "@/assets/windimgs/fanSvg/dj.svg"
  96. //检修
  97. import jx from "@/assets/windimgs/fanSvg/jx.svg"
  98. //限电
  99. import xd from "@/assets/windimgs/fanSvg/xd.svg"
  100. //离线
  101. import lx from "@/assets/windimgs/fanSvg/lx.svg"
  102. //受累
  103. import sl from "@/assets/windimgs/fanSvg/sl.svg"
  104. //动图使用柱子和扇叶
  105. import bwzhu from "@/assets/windimgs/fanSvg/bwzhu.svg"
  106. import bwshan from "@/assets/windimgs/fanSvg/bwshan.png"
  107. import windHome from "@/components/windHome/index.vue";
  108. import windPro from "@/components/windProDetail/windProblem.vue";
  109. import ModelUnpack from "@/components/modelUnpack.vue";
  110. import { WindLayer } from "cesium-wind";
  111. import windGridData from "./windGridData.json";
  112. export default {
  113. name: "windMap3D",
  114. components: {
  115. comModelDialog,
  116. windView,
  117. menuCom,
  118. windHome,
  119. windPro,
  120. ModelUnpack,
  121. },
  122. data() {
  123. return {
  124. loading: true,
  125. viewer: null,
  126. windLayer: null, // 风场图
  127. windLayerTimmer: null, // 风场图计时器
  128. cloudImagesLayer: [], // 卫星云图
  129. cloudLayer: null, // 卫星云图
  130. cloudintervalId: null,
  131. rainImagesLayer: [], // 降雨图
  132. rainLayer: null, // 降雨图
  133. rainintervalId: null, // 降雨图
  134. tempImagesLayer: [], //温度图
  135. temperatureLayer: null, //温度图
  136. tempintervalId: null, //温度图
  137. showcomModelDia: false,
  138. currentHeight: 0,
  139. modelVal: null,
  140. menuComTStyB: false,
  141. modelUnpackType: "fengji",
  142. windDrawer: false,
  143. windDrawerTitle: "",
  144. windDrawerHeader: "",
  145. showBasicMsg: false,
  146. showVideoMsg: false,
  147. showProblemMsg: false,
  148. showModelMsg: false,
  149. showWindDetail: true,
  150. allStationentitys: [],
  151. allWindEntitys: [],
  152. urlTiles: "/public/static/tiles/{z}/{x}/{y}.jpg"
  153. };
  154. },
  155. mounted() {
  156. this.initCesium();
  157. setTimeout(() =>{
  158. this.loading = false
  159. }, 1000)
  160. },
  161. methods: {
  162. async initCesium() {
  163. const box = document.getElementById("cesiumContainer");
  164. const viewer = new Cesium.Viewer(box, {
  165. geocoder: false, // 地址搜索控件
  166. homeButton: false, // 返回地图初始位置控件
  167. infoBox: false, // 地图默认的信息控件
  168. sceneModePicker: false, // 场景模式切换控件
  169. baseLayerPicker: false, // 底图切换控件
  170. navigationHelpButton: false, // 帮助控件
  171. animation: false, // 动画控制控件
  172. timeline: false, // 时间线控件
  173. fullscreenButton: false, // 全屏按钮控件
  174. // imageryProvider: true, // 是否显示 Cesium 默认地图的底图
  175. vrButton: false,
  176. selectionIndicator: false,
  177. shouldAnimate: true,
  178. });
  179. const imageryProvider = new Cesium.UrlTemplateImageryProvider({
  180. // minimumLevel: 11,
  181. // maximumLevel: 18,
  182. // url: this.urlTiles,
  183. url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
  184. // url: "http://192.168.2.180:3007/tiles/map/{z}/{x}/{y}",
  185. credit: "影像地图",
  186. });
  187. imageryProvider.alpha = 0.55; // 透明度
  188. imageryProvider.brightness = 1; // 亮度
  189. imageryProvider.contrast = 1; // 对比度
  190. viewer.imageryLayers.addImageryProvider(imageryProvider);
  191. viewer._cesiumWidget._creditContainer.style.display = "none";
  192. // this.csceneElliposid(viewer)
  193. this.viewer = viewer;
  194. // 展示场站
  195. // this.showAllStation(viewer)
  196. // 展示风机
  197. this.showWindFromStation(viewer)
  198. this.initGeoJsonData()
  199. },
  200. // 初始化 geoJson 数据
  201. async initGeoJsonData() {
  202. // 创建GeoJSON数据源
  203. await new Cesium.GeoJsonDataSource.load(basicGeoJson, {
  204. stroke: Cesium.Color.GRAY, // 边界线颜色
  205. fill: Cesium.Color.BLACK.withAlpha(0), // 填充颜色
  206. strokeWidth: 1, // 边界线宽度
  207. markerSymbol: "?", // 点要素的符号
  208. clampToGround: true, // 贴地
  209. }).then((dataSource) => {
  210. // 添加到视图
  211. this.viewer.dataSources.add(dataSource);
  212. var entities = dataSource.entities.values;
  213. for (let i = 0; i < entities.length; i++) {
  214. let entity = entities[i];
  215. entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
  216. //单独设置线条样式
  217. var positions = entity.polygon.hierarchy._value.positions;
  218. entity.polyline = {
  219. positions: positions,
  220. width: 1,
  221. outline: false,
  222. };
  223. }
  224. // 添加中文标签图层
  225. const labelLayer = new Cesium.LabelCollection();
  226. this.viewer.scene.primitives.add(labelLayer);
  227. const cities = [];
  228. basicGeoJson?.features?.forEach((ele) => {
  229. if (Array.isArray(ele.properties.centroid)) {
  230. const name = ele.properties.name;
  231. const lon = ele.properties.centroid[0];
  232. const lat = ele.properties.centroid[1];
  233. cities.push({ name, lon, lat });
  234. }
  235. });
  236. cities.forEach((city, index) => {
  237. labelLayer.add({
  238. id: index,
  239. name: "cityLabel",
  240. position: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, 10),
  241. text: city.name,
  242. font: 'bold 14px "Microsoft YaHei", sans-serif',
  243. fillColor: Cesium.Color.fromCssColorString("#000"),
  244. outlineColor: Cesium.Color.WHITE,
  245. outlineWidth: 2,
  246. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  247. pixelOffset: new Cesium.Cartesian2(0, 0), // 设置为0
  248. horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
  249. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
  250. });
  251. });
  252. });
  253. },
  254. backStations() {
  255. this.showWindDetail = false
  256. this.allWindEntitys.forEach(({ entity, handler }) => {
  257. this.viewer.entities.remove(entity); // 移除实体
  258. if (!handler.isDestroyed()) {
  259. handler.destroy(); // 销毁事件处理器(关键!)
  260. }
  261. })
  262. this.allWindEntitys = []
  263. // if (this.viewer && this.viewer.destroy) {
  264. // this.viewer.destroy();
  265. // this.viewer = null
  266. // }
  267. // this.initCesium();
  268. this.showAllStation(this.viewer)
  269. },
  270. // 展示所有风场
  271. showAllStation(viewer) {
  272. allStationJson.station.forEach((e, index) => {
  273. if (e.energytype === "Wind") {
  274. this.showStationFn(viewer, e, index, fc)
  275. } else if(e.energytype === "Fire") {
  276. this.showStationFn(viewer, e, index, hd)
  277. } else {
  278. this.showStationFn(viewer, e, index, gf)
  279. }
  280. });
  281. this.resetAllStationViewport()
  282. },
  283. // 根据状态展示不同颜色风机贴图
  284. showStationFn(viewer, e, index, images) {
  285. const position = Cesium.Cartesian3.fromDegrees(e.longitude, e.latitude);
  286. const entity = viewer.entities.add({
  287. id: index,
  288. position, // 模型位置
  289. billboard: {
  290. image: images, // 也可以是 SVG 路径,如 'icon.svg'
  291. scale: 0.5,
  292. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  293. horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
  294. // 模型贴地
  295. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  296. },
  297. label: {
  298. text: e.plantname,
  299. font: '14px sans-serif',
  300. fillColor: Cesium.Color.fromBytes(255, 255, 255),
  301. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  302. }
  303. });
  304. let that = this
  305. const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  306. handler.setInputAction(function (movement) {
  307. var position = movement.position;
  308. var pickedObject = viewer.scene.pick(position);
  309. if (pickedObject && pickedObject.id.id === index) {
  310. console.log("你点击了标签或模型!", entity);
  311. console.log("选中风场或新能源场", e.plantname);
  312. that.showWindDetail = true
  313. that.allStationentitys.forEach(({ entity, handler }) => {
  314. viewer.entities.remove(entity); // 移除实体
  315. if (!handler.isDestroyed()) {
  316. handler.destroy(); // 销毁事件处理器(关键!)
  317. }
  318. })
  319. that.allStationentitys = []
  320. that.showWindFromStation(viewer)
  321. }
  322. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  323. that.allStationentitys.push({entity, handler})
  324. },
  325. // 展示所选风场的风机
  326. showWindFromStation(viewer) {
  327. fjLonLatJson.data.forEach((e, index) => {
  328. this.showStatuswind(viewer, e)
  329. });
  330. this.resetWindViewport()
  331. },
  332. // 根据状态展示不同颜色风机贴图
  333. showStatuswind(viewer, e, type, index) {
  334. this.addModel(
  335. viewer,
  336. type,
  337. e.longitude,
  338. e.latitude,
  339. e,
  340. Math.random().toFixed(4)*10000
  341. );
  342. },
  343. //给模型上描边
  344. getWtStatue(code) {
  345. if (code === 1) { //待机
  346. return "#05bb4c";
  347. } else if (code === 2) { //故障
  348. return "#d83238";
  349. } else if (code === 3) { //检修
  350. return "#ff8300";
  351. } else if (code === 4) { //限电
  352. return "#c732ca";
  353. } else if (code === 5) { //离线
  354. return "#6f7881";
  355. } else if (code === 6) { //受累
  356. return "#cbd1d7";
  357. } else {//并网
  358. return "#42a7f9";
  359. }
  360. },
  361. //添加模型
  362. addModel(viewer, name, lon, lat, val) {
  363. const hpRoll = new Cesium.HeadingPitchRoll(90.0, 0.0, 0.0);
  364. const position = Cesium.Cartesian3.fromDegrees(lon, lat);
  365. const orientation = Cesium.Transforms.headingPitchRollQuaternion(
  366. position,
  367. hpRoll
  368. );
  369. const wtStatue = val.status
  370. const statueColor = this.getWtStatue(wtStatue);
  371. const entity = viewer.entities.add({
  372. name, // 模型名称
  373. position, // 模型位置
  374. orientation, // 模型朝向
  375. animation: false,
  376. model: {
  377. uri: "/public/static/model/fjSolo/model.glb",
  378. scale: 0.7,
  379. // 模型贴地
  380. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  381. silhouetteSize: 2,
  382. silhouetteColor: Cesium.Color.fromCssColorString(statueColor),
  383. runAnimations: wtStatue !== 7 ? false : true,
  384. },
  385. //添加标签
  386. label: {
  387. text: val.name ? val.name : val.plantname,
  388. font: '14px sans-serif',
  389. fillColor: Cesium.Color.fromBytes(255, 255, 255),
  390. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  391. pixelOffset: val.name === 'F10号风机' ? new Cesium.Cartesian2(40, -20) : new Cesium.Cartesian2(0, 10)
  392. }
  393. });
  394. let that = this;
  395. // 创建事件处理器
  396. const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  397. // handler.setInputAction(function(click) {
  398. // const picked = viewer.scene.pick(click.position);
  399. // if (picked && picked.id === entity) {
  400. // console.log('你点击了标签或模型!', entity);
  401. // console.log('标签或模型数据!', val);
  402. // // alert('你点击了: ' + entity.label.text.getValue());
  403. // // this.$refs.comModelDialog.init(val)
  404. // if (name !== '光伏') {
  405. // that.showcomModelDia = true
  406. // that.modelVal = val
  407. // }
  408. // }
  409. // }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  410. handler.setInputAction(function (movement) {
  411. var position = movement.position;
  412. var pickedObject = viewer.scene.pick(position);
  413. if (pickedObject && pickedObject.id === entity) {
  414. console.log("你点击了标签或模型!", entity);
  415. console.log("标签或模型数据!", val);
  416. that.modelVal = val;
  417. // 找到实体,显示包含实体信息的弹框
  418. that.showRightClickPopup(position, val);
  419. return;
  420. }
  421. }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  422. },
  423. // 右键展示元素
  424. showRightClickPopup(screenPosition, val) {
  425. // 创建或获取弹框元素
  426. var popup = document.getElementById("rightClickPopup");
  427. if (!popup) {
  428. popup = document.createElement("div");
  429. popup.id = "rightClickPopup";
  430. popup.style.position = "absolute";
  431. // popup.style.backgroundColor = 'white';
  432. popup.style.border = "1px solid rgba(255, 255, 255, 0.2)";
  433. popup.style.borderRadius = "4px";
  434. popup.style.padding = "10px";
  435. popup.style.boxShadow = "0 2px 10px rgba(0,0,0,0.2)";
  436. popup.style.backdropFilter = `blur(10px)`;
  437. popup.style.zIndex = "1000";
  438. popup.style.pointerEvents = "auto"; // 确保弹框可交互
  439. // 添加一个最小宽度,避免内容过短
  440. popup.style.minWidth = "100px";
  441. // 可选:添加箭头指向点击点
  442. // 这需要更复杂的 CSS 或额外的 DOM 元素
  443. document.body.appendChild(popup);
  444. }
  445. // <span class="popup-menu-item" data-message-type="model" style="cursor:pointer">模型解构</span>
  446. let content = `
  447. <div style="display: flex;gap: 10px;flex-direction: column;text-align: center;color: #fff">
  448. <span class="popup-menu-item" data-message-type="basic" style="cursor:pointer">基本信息</span>
  449. <span class="popup-menu-item" data-message-type="video" style="cursor:pointer">视频监控</span>
  450. <span class="popup-menu-item" data-message-type="problem" style="cursor:pointer">故障详情</span>
  451. </div>
  452. `;
  453. // 设置弹框内容
  454. popup.innerHTML = content;
  455. // --- 关键修正:准确计算弹框位置 ---
  456. // 1. 获取 Cesium 画布 (Canvas) 相对于视口 (viewport) 的位置
  457. var canvas = this.viewer.scene.canvas;
  458. var canvasRect = canvas.getBoundingClientRect();
  459. // console.log('Canvas Rect:', canvasRect); // 调试用
  460. // 2. 计算弹框左上角在页面中的绝对 X 坐标
  461. // screenPosition.x 是相对于画布左上角的 X 偏移
  462. // canvasRect.left 是画布左上角相对于浏览器视口左上角的 X 偏移
  463. // window.pageXOffset 是页面水平滚动的距离
  464. var popupLeft = canvasRect.left + screenPosition.x + window.pageXOffset;
  465. // 3. 计算弹框左上角在页面中的绝对 Y 坐标
  466. var popupTop = canvasRect.top + screenPosition.y + window.pageYOffset;
  467. // --- 可选:添加偏移量,避免完全覆盖鼠标指针 ---
  468. // 例如,向下和向右偏移 10px
  469. var offsetX = 10;
  470. var offsetY = 10;
  471. popupLeft += offsetX;
  472. popupTop += offsetY;
  473. // --- 可选:边界检查,防止弹框超出屏幕 ---
  474. var popupWidth = popup.offsetWidth || 150; // 确保有宽度
  475. var popupHeight = popup.offsetHeight || 50; // 确保有高度
  476. var windowWidth = window.innerWidth;
  477. var windowHeight = window.innerHeight;
  478. // 如果弹框右边会超出窗口,则左移
  479. if (popupLeft + popupWidth > windowWidth) {
  480. popupLeft = windowWidth - popupWidth - 10; // 靠右留 10px 边距
  481. }
  482. // 如果弹框底边会超出窗口,则上移
  483. if (popupTop + popupHeight > windowHeight) {
  484. popupTop = windowHeight - popupHeight - 10; // 靠下留 10px 边距
  485. }
  486. // 如果左边超出 (不太可能,但安全起见)
  487. if (popupLeft < 0) popupLeft = 10;
  488. // 如果顶边超出
  489. if (popupTop < 0) popupTop = 10;
  490. // --- 设置最终位置 ---
  491. popup.style.left = popupLeft + "px";
  492. popup.style.top = popupTop + "px";
  493. // console.log('Popup Position:', { left: popupLeft, top: popupTop }); // 调试用
  494. // 显示弹框
  495. popup.style.display = "block";
  496. // --- 隐藏弹框的逻辑 (保持不变) ---
  497. function hidePopup(event) {
  498. // 检查点击是否发生在弹框内部,如果是,则不隐藏
  499. if (event && popup.contains(event.target)) {
  500. return;
  501. }
  502. popup.style.display = "none";
  503. document.removeEventListener("click", hidePopup);
  504. document.removeEventListener("keydown", keyDownHandler);
  505. }
  506. function keyDownHandler(event) {
  507. if (event.key === "Escape") {
  508. hidePopup();
  509. }
  510. }
  511. let that = this;
  512. that.showBasicMsg = false;
  513. that.showVideoMsg = false;
  514. that.showProblemMsg = false;
  515. that.showModelMsg = false;
  516. function popupMenuItemClick(event) {
  517. // 检查点击的是否是菜单项
  518. if (event.target.classList.contains("popup-menu-item")) {
  519. event.stopPropagation(); // 阻止冒泡,防止弹框立即关闭
  520. var messageType = event.target.dataset.messageType; // 获取 data-message-type
  521. console.log("点击了菜单项:", messageType);
  522. // 调用您的 showMessage 函数
  523. that.showMessage(messageType, val);
  524. hidePopup();
  525. // 可选:执行后关闭弹框
  526. // hideRightClickPopup();
  527. }
  528. }
  529. // 移除旧的监听器(避免重复绑定)
  530. document.removeEventListener("click", hidePopup);
  531. document.removeEventListener("click", popupMenuItemClick);
  532. document.removeEventListener("keydown", keyDownHandler);
  533. // 添加新的监听器
  534. document.addEventListener("click", hidePopup);
  535. document.addEventListener("click", popupMenuItemClick);
  536. document.addEventListener("keydown", keyDownHandler);
  537. },
  538. showMessage(type, val) {
  539. console.log("type===>>>", type);
  540. this.windDrawer = true;
  541. this.windDrawerHeader = val.name + "数据详情";
  542. if (type === "basic") {
  543. this.windDrawerTitle = "基础信息";
  544. this.showBasicMsg = true;
  545. } else if (type === "video") {
  546. this.windDrawerTitle = "视频监控";
  547. this.showVideoMsg = true;
  548. } else if (type === "problem") {
  549. this.windDrawerTitle = "故障查看";
  550. this.showProblemMsg = true;
  551. }
  552. // else {
  553. // this.windDrawerTitle = '模型解构'
  554. // this.showModelMsg = true
  555. // }
  556. },
  557. showwindmodel() {
  558. this.windDrawer = true;
  559. this.showBasicMsg = false;
  560. this.showVideoMsg = false;
  561. this.showProblemMsg = false;
  562. this.windDrawerHeader = "风机模型可视化解构说明";
  563. this.showModelMsg = true;
  564. },
  565. handleClose() {
  566. this.windDrawer = false;
  567. this.showBasicMsg = false;
  568. this.showVideoMsg = false;
  569. this.showProblemMsg = false;
  570. this.showModelMsg = false;
  571. },
  572. showComDia(val) {
  573. this.showcomModelDia = val;
  574. this.modelVal = null;
  575. },
  576. csceneElliposid(viewer) {
  577. let that = this;
  578. // 获取 scene 和 ellipsoid
  579. var scene = viewer.scene;
  580. var labels = viewer.scene.primitives.add(new Cesium.LabelCollection());
  581. // 创建 ScreenSpaceEventHandler
  582. var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  583. // 监听左键点击事件
  584. handler.setInputAction(async (click) => {
  585. // 获取点击位置的笛卡尔坐标
  586. var position = click.position;
  587. if (!position) return;
  588. // 使用 globe.pick 获取包含地形高度的坐标
  589. var ray = viewer.camera.getPickRay(position);
  590. var cartesian = viewer.scene.globe.pick(ray, viewer.scene);
  591. if (cartesian) {
  592. // 转换为地理坐标
  593. var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  594. var longitude = Cesium.Math.toDegrees(cartographic.longitude);
  595. var latitude = Cesium.Math.toDegrees(cartographic.latitude);
  596. var height = cartographic.height;
  597. // 格式化坐标
  598. var text = `经度: ${longitude.toFixed(6)}°\n纬度: ${latitude.toFixed(
  599. 6
  600. )}°`;
  601. // 创建一个标签
  602. var label = labels.add({
  603. position: cartesian,
  604. text: text,
  605. font: "14px monospace",
  606. fillColor: Cesium.Color.fromCssColorString("#1d70df"),
  607. // style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  608. // outlineColor: Cesium.Color.BLACK,
  609. outlineWidth: 2,
  610. verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 标签在点的上方
  611. pixelOffset: new Cesium.Cartesian2(0, -20), // 向上偏移一点
  612. disableDepthTest: true, // 让标签始终可见(即使在地球背面)
  613. scale: 0.8,
  614. });
  615. // 5秒后移除标签
  616. setTimeout(function () {
  617. labels.remove(label);
  618. }, 5000);
  619. console.log(
  620. `点击坐标: ${longitude.toFixed(6)}, ${latitude.toFixed(
  621. 6
  622. )}, ${height.toFixed(2)}m`
  623. );
  624. }
  625. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  626. },
  627. generateUniqueId(prefix = 'id') {
  628. let idCounter = 0;
  629. return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}-${idCounter++}`;
  630. },
  631. handleInitView() {
  632. if (this.showWindDetail) {
  633. this.resetWindViewport()
  634. } else {
  635. this.resetAllStationViewport()
  636. }
  637. },
  638. // 重置所有风场视角
  639. resetAllStationViewport() {
  640. this.viewer.camera.flyTo({
  641. destination: Cesium.Cartesian3.fromDegrees(114.502778, 35.326667, 3000000),
  642. orientation: {
  643. heading: Cesium.Math.toRadians(0),
  644. pitch: Cesium.Math.toRadians(-90),
  645. roll: 0.0,
  646. },
  647. duration: 3.0,
  648. });
  649. },
  650. // 重置风场中所有风机视角
  651. resetWindViewport() {
  652. let fromLon = this.$route.query.longitude*1
  653. let fromLat = this.$route.query.latitude*1
  654. let fromheight = this.$route.query.height*1
  655. // 设置镜头到指定的经纬度(度)、高度(米)
  656. this.viewer.camera.setView({
  657. destination: Cesium.Cartesian3.fromDegrees(
  658. fromLon, // 经度 (degrees)
  659. fromLat, // 纬度 (degrees)
  660. fromheight // 高度 (meters)
  661. ),
  662. orientation: {
  663. heading : Cesium.Math.toRadians(0.0), // 偏航角 (方向,0 指向北方)
  664. pitch : Cesium.Math.toRadians(-90.0), // 俯仰角 (-90 是垂直向下)
  665. roll : 0.0 // 翻滚角
  666. }
  667. });
  668. // 目标位置:经度、纬度、高度
  669. const targetLon = 114.502778;
  670. const targetLat = 35.326667;
  671. const targetHeight = 10000;
  672. const draggableHeightTolerance = 5000; // 允许拖拽的高度范围:20,000 ~ 30,000
  673. const minHeight = 5000; // 最低高度
  674. const maxHeight = 800000; // 最高高度
  675. const allowedOffsetDegrees = 2; // 允许拖拽的最大偏移(经纬度)
  676. const Lat3d = targetLat - 0.3;
  677. const minLon = targetLon - allowedOffsetDegrees;
  678. const maxLon = targetLon + allowedOffsetDegrees;
  679. const minLat = Lat3d - allowedOffsetDegrees;
  680. const maxLat = Lat3d + allowedOffsetDegrees;
  681. let that = this
  682. that.viewer.camera.flyTo({
  683. destination: Cesium.Cartesian3.fromDegrees(targetLon,Lat3d, targetHeight),
  684. // orientation: {
  685. // heading: Cesium.Math.toRadians(0),
  686. // pitch: Cesium.Math.toRadians(-90),
  687. // roll: 0.0,
  688. // },
  689. orientation: {
  690. heading: 0, //北京天安门角度 0 上海角度 1
  691. pitch: -0.3,
  692. roll: 0,
  693. },
  694. duration: 3.0,
  695. complete: function () {
  696. console.log('飞入完成,启用拖拽限制逻辑');
  697. enableHeightBasedDragControl();
  698. }
  699. });
  700. // ===== 控制逻辑:根据高度决定是否允许拖拽 =====
  701. function enableHeightBasedDragControl() {
  702. that.viewer.scene.preUpdate.addEventListener(function (scene, time) {
  703. const camera = that.viewer.camera;
  704. const posCartographic = camera.positionCartographic;
  705. if (!posCartographic) return;
  706. const currentHeight = posCartographic.height;
  707. that.currentHeight = currentHeight
  708. const currentLon = Cesium.Math.toDegrees(posCartographic.longitude);
  709. const currentLat = Cesium.Math.toDegrees(posCartographic.latitude);
  710. // === 第一步:限制相机高度不能超出 [10000, 100000] ===
  711. if (currentHeight < minHeight) {
  712. // 强制拉高到 minHeight,但保持当前水平视角
  713. const newPos = Cesium.Cartesian3.fromDegrees(currentLon, currentLat, minHeight);
  714. camera.setView({
  715. destination: newPos,
  716. orientation: {
  717. heading: camera.heading,
  718. pitch: camera.pitch,
  719. roll: camera.roll
  720. }
  721. });
  722. return; // 避免后续逻辑冲突
  723. }
  724. if (currentHeight > maxHeight) {
  725. const newPos = Cesium.Cartesian3.fromDegrees(currentLon, currentLat, maxHeight);
  726. camera.setView({
  727. destination: newPos,
  728. orientation: {
  729. heading: camera.heading,
  730. pitch: camera.pitch,
  731. roll: camera.roll
  732. }
  733. });
  734. return;
  735. }
  736. // if (currentHeight < (maxHeight-700000)) {
  737. // that.cancleAllLayer();
  738. // }
  739. // === 第二步:判断是否在“可拖拽高度区间” ===
  740. const isInDraggableRange =
  741. currentHeight >= (targetHeight - draggableHeightTolerance) &&
  742. currentHeight <= (targetHeight + draggableHeightTolerance);
  743. if (isInDraggableRange) {
  744. // ✅ 允许拖拽,但限制在指定范围内
  745. const correctedLon = Cesium.Math.clamp(currentLon, minLon, maxLon);
  746. const correctedLat = Cesium.Math.clamp(currentLat, minLat, maxLat);
  747. if (Math.abs(correctedLon - currentLon) > 1e-8 ||
  748. Math.abs(correctedLat - currentLat) > 1e-8) {
  749. // 越界了,纠正位置,保留当前高度和视角
  750. camera.setView({
  751. destination: Cesium.Cartesian3.fromDegrees(correctedLon, correctedLat, currentHeight),
  752. orientation: {
  753. heading: camera.heading,
  754. pitch: camera.pitch,
  755. roll: camera.roll
  756. }
  757. });
  758. }
  759. } else {
  760. // ❌ 不在可拖拽高度:禁止平移,强制回正到目标点
  761. const correctedPosition = Cesium.Cartesian3.fromDegrees(
  762. targetLon,
  763. targetLat,
  764. currentHeight // 保留当前缩放高度
  765. );
  766. camera.setView({
  767. destination: correctedPosition,
  768. orientation: {
  769. heading: camera.heading,
  770. pitch: camera.pitch,
  771. roll: camera.roll
  772. }
  773. });
  774. }
  775. });
  776. }
  777. },
  778. coverOnChange(val) {
  779. if (val.value === "风场") {
  780. this.switchWindLayer(val.check);
  781. } else if (val.value === "云层") {
  782. this.switchCloudLayer(val.check);
  783. } else if (val.value === "降雨") {
  784. this.switchRainLayer(val.check);
  785. } else if (val.value === "温度") {
  786. this.switchTemperatureLayerr(val.check);
  787. }
  788. },
  789. // 提供控制函数以便在需要时停止循环
  790. stopCycling(intervalId) {
  791. if (intervalId) {
  792. clearInterval(intervalId);
  793. intervalId = null;
  794. console.log("循环已停止");
  795. }
  796. },
  797. // 切换风场图显隐
  798. switchWindLayer() {
  799. this.viewer.scene.screenSpaceCameraController.enableZoom = true;
  800. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  801. this.removeRainLayer();
  802. this.stopCycling(this.rainintervalId);
  803. }
  804. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  805. this.removeCloudLayer();
  806. this.stopCycling(this.cloudintervalId);
  807. }
  808. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  809. this.removeTemperatureLayer();
  810. this.stopCycling(this.tempintervalId);
  811. }
  812. if (this.windLayer) {
  813. this.removeWindLayer();
  814. } else {
  815. this.showWindLayer();
  816. }
  817. },
  818. // 添加风场图
  819. async showWindLayer() {
  820. if (!this.windLayer) {
  821. this.windLayer = new WindLayer(windGridData, {
  822. particleSize: 2.0,
  823. particleOpacity: 0.6,
  824. particleSpeed: 0.01,
  825. maxVelocity: 25,// 风速最大值(用于颜色映射和速度缩放)
  826. minVelocity: 0, // 风速最小值阈值(低于此值不显示粒子)
  827. colorScale: [
  828. "rgb(36,104, 180)",
  829. "rgb(60,157, 194)",
  830. "rgb(128,205,193)",
  831. "rgb(151,218,168)",
  832. "rgb(198,231,181)",
  833. "rgb(238,247,217)",
  834. "rgb(255,238,159)",
  835. "rgb(252,217,125)",
  836. "rgb(255,182,100)",
  837. "rgb(252,150,75)",
  838. "rgb(250,112,52)",
  839. "rgb(245,64,32)",
  840. "rgb(237,45,28)",
  841. "rgb(220,24,32)",
  842. "rgb(180,0,35)",
  843. ],// 颜色强度缩放
  844. frameRate: 15,
  845. fadeOpacity: 0.995,
  846. particleAge: 150,
  847. maxAge: 60,
  848. globalAlpha: 0.8,
  849. velocityScale: 1 / 30,// 粒子移动速度缩放因子(控制动画快慢)
  850. paths: 2000,
  851. lineWidth: 2,
  852. });
  853. this.windLayer.addTo(this.viewer);
  854. }
  855. },
  856. // 切换卫星云图显隐
  857. switchCloudLayer(val) {
  858. if (this.windLayer) {
  859. this.removeWindLayer();
  860. }
  861. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  862. this.removeRainLayer();
  863. this.stopCycling(this.rainintervalId);
  864. }
  865. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  866. this.removeTemperatureLayer();
  867. this.stopCycling(this.tempintervalId);
  868. }
  869. if (!val || this.cloudLayer || this.cloudImagesLayer.length > 0) {
  870. this.removeCloudLayer();
  871. this.stopCycling(this.cloudintervalId);
  872. } else {
  873. this.showCloudLayer();
  874. }
  875. },
  876. // 切换降雨图显隐
  877. switchRainLayer() {
  878. this.viewer.scene.screenSpaceCameraController.enableZoom = true;
  879. if (this.windLayer) {
  880. this.removeWindLayer();
  881. }
  882. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  883. this.removeCloudLayer();
  884. this.stopCycling(this.cloudintervalId);
  885. }
  886. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  887. this.removeTemperatureLayer();
  888. this.stopCycling(this.tempintervalId);
  889. }
  890. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  891. this.removeRainLayer();
  892. this.stopCycling(this.rainintervalId);
  893. } else {
  894. this.showRainLayer();
  895. }
  896. },
  897. // 切换温度图显隐
  898. switchTemperatureLayerr() {
  899. this.viewer.scene.screenSpaceCameraController.enableZoom = true;
  900. if (this.windLayer) {
  901. this.removeWindLayer();
  902. }
  903. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  904. this.removeCloudLayer();
  905. this.stopCycling(this.cloudintervalId);
  906. }
  907. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  908. this.removeRainLayer();
  909. this.stopCycling(this.rainintervalId);
  910. }
  911. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  912. this.removeTemperatureLayer();
  913. this.stopCycling(this.tempintervalId);
  914. } else {
  915. this.showTemperatureLayer();
  916. }
  917. },
  918. // 显示云图
  919. showCloudLayer() {
  920. const imageUrls = [];
  921. cloudJson.forEach((it) => {
  922. imageUrls.push("/public/static" + it.path);
  923. });
  924. this.showeveryTypeImagesLayer(
  925. imageUrls,
  926. this.cloudintervalId,
  927. this.cloudImagesLayer
  928. );
  929. },
  930. //显示降雨图
  931. showRainLayer() {
  932. const imageUrls = [];
  933. rainJson.forEach((it) => {
  934. imageUrls.push("/public/static" + it.path);
  935. });
  936. this.showeveryTypeImagesLayer(
  937. imageUrls,
  938. this.rainintervalId,
  939. this.rainImagesLayer
  940. );
  941. this.csceneElliposid(this.viewer, "rain");
  942. },
  943. //显示温度图
  944. showTemperatureLayer() {
  945. const imageUrls = [];
  946. tempJson.forEach((it) => {
  947. imageUrls.push("/public/static" + it.path);
  948. });
  949. this.showeveryTypeImagesLayer(
  950. imageUrls,
  951. this.tempintervalId,
  952. this.tempImagesLayer
  953. );
  954. this.csceneElliposid(this.viewer, "temp");
  955. },
  956. async showeveryTypeImagesLayer(imageUrls, intervalId, ImagesLayers) {
  957. // 存储所有图片图层的数组
  958. let imageLayers = [];
  959. // 当前显示的图片索引
  960. let currentImageIndex = -1; // 初始为-1,表示没有图片显示
  961. // 创建所有图片图层并添加到Viewer,初始时全部隐藏
  962. await imageUrls.forEach((url) => {
  963. const provider = new Cesium.SingleTileImageryProvider({
  964. url: url,
  965. // url: URL.createObjectURL(url),
  966. rectangle: Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0), // 全球覆盖
  967. tileWidth: 1440, // 根据你的图片实际宽度修改
  968. tileHeight: 721,
  969. // 如果你的图片只覆盖特定区域,请修改rectangle参数
  970. });
  971. const Layer = this.viewer.imageryLayers.addImageryProvider(provider);
  972. Layer.alpha = 0.8; // 透明度
  973. Layer.brightness = 1; // 亮度
  974. Layer.contrast = 1; // 对比度
  975. Layer.show = false; // 初始隐藏
  976. imageLayers.push(Layer);
  977. ImagesLayers.push(Layer);
  978. });
  979. function showNextImage() {
  980. // 隐藏当前图片
  981. if (currentImageIndex >= 0 && currentImageIndex < imageLayers.length) {
  982. imageLayers[currentImageIndex].show = false;
  983. }
  984. // 计算下一张图片的索引
  985. currentImageIndex = (currentImageIndex + 1) % imageLayers.length;
  986. // 显示下一张图片
  987. imageLayers[currentImageIndex].show = true;
  988. // imageLayers[currentImageIndex + 1].show = true;
  989. console.log("当前显示图片: " + imageUrls[currentImageIndex]);
  990. }
  991. // 设置切换间隔(毫秒),例如每5秒切换一次
  992. const intervalMs = 5000;
  993. intervalId = setInterval(showNextImage, intervalMs);
  994. // 初始显示第一张图片
  995. showNextImage();
  996. },
  997. // 移除风场图
  998. removeWindLayer() {
  999. if (this.windLayer) {
  1000. // this.windLayer.destroy();
  1001. this.windLayer.remove();
  1002. this.windLayer = null;
  1003. }
  1004. },
  1005. // 移除卫星云图
  1006. removeCloudLayer() {
  1007. if (this.cloudLayer) {
  1008. this.tagMsg = null;
  1009. this.viewer.imageryLayers.remove(this.cloudLayer);
  1010. this.cloudLayer = null;
  1011. }
  1012. if (this.cloudImagesLayer.length > 0) {
  1013. this.cloudImagesLayer.forEach((it) => {
  1014. this.viewer.imageryLayers.remove(it);
  1015. });
  1016. this.cloudImagesLayer = [];
  1017. }
  1018. if (this.imageryProviderV) {
  1019. this.viewer.imageryLayers.remove(this.imageryProviderV);
  1020. this.imageryProviderV = null;
  1021. }
  1022. },
  1023. // 移除降雨图
  1024. removeRainLayer() {
  1025. if (this.rainLayer) {
  1026. this.tagMsg = null;
  1027. this.viewer.imageryLayers.remove(this.rainLayer);
  1028. this.rainLayer = null;
  1029. this.setMapImageryProvider();
  1030. this.handlerAction.removeInputAction(
  1031. Cesium.ScreenSpaceEventType.LEFT_CLICK
  1032. );
  1033. }
  1034. if (this.rainImagesLayer.length > 0) {
  1035. this.rainImagesLayer.forEach((it) => {
  1036. this.viewer.imageryLayers.remove(it);
  1037. });
  1038. this.rainImagesLayer = [];
  1039. }
  1040. if (this.imageryProviderV) {
  1041. this.viewer.imageryLayers.remove(this.imageryProviderV);
  1042. this.imageryProviderV = null;
  1043. }
  1044. },
  1045. // 移除温度图
  1046. removeTemperatureLayer() {
  1047. if (this.temperatureLayer) {
  1048. this.tagMsg = null;
  1049. this.viewer.imageryLayers.remove(this.temperatureLayer);
  1050. this.temperatureLayer = null;
  1051. this.setMapImageryProvider();
  1052. this.handlerAction.removeInputAction(
  1053. Cesium.ScreenSpaceEventType.LEFT_CLICK
  1054. );
  1055. }
  1056. if (this.tempImagesLayer.length > 0) {
  1057. this.tempImagesLayer.forEach((it) => {
  1058. this.viewer.imageryLayers.remove(it);
  1059. });
  1060. this.tempImagesLayer = [];
  1061. }
  1062. if (this.imageryProviderV) {
  1063. this.viewer.imageryLayers.remove(this.imageryProviderV);
  1064. this.imageryProviderV = null;
  1065. }
  1066. },
  1067. //取消所有图层加载
  1068. cancleAllLayer() {
  1069. if (this.windLayer) {
  1070. this.removeWindLayer();
  1071. }
  1072. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  1073. this.removeRainLayer();
  1074. this.stopCycling(this.rainintervalId);
  1075. }
  1076. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  1077. this.removeCloudLayer();
  1078. this.stopCycling(this.cloudintervalId);
  1079. }
  1080. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  1081. this.removeTemperatureLayer();
  1082. this.stopCycling(this.tempintervalId);
  1083. }
  1084. },
  1085. switchLayer() {
  1086. this.$router.push({
  1087. path: "/",
  1088. });
  1089. },
  1090. menuComTSty(val) {
  1091. this.menuComTStyB = val;
  1092. },
  1093. },
  1094. };
  1095. </script>
  1096. <style lang="less" scoped>
  1097. .dataLoading {
  1098. width: 100vw;
  1099. height: 100vh;
  1100. background: rgba(0, 0, 0, 0.5);
  1101. z-index: 999;
  1102. position: fixed;
  1103. .loadText{
  1104. position: absolute;
  1105. top: 50%;
  1106. left: 50%;
  1107. transform: translate(-50%, -50%);
  1108. background: rgba(255, 255, 255, 0.7);
  1109. padding: 15px 20px;
  1110. border-radius: 6px;
  1111. color: black;
  1112. font-size: 14px;
  1113. font-weight: bold;
  1114. }
  1115. }
  1116. .mapBox {
  1117. width: 100%;
  1118. height: 100%;
  1119. position: relative;
  1120. box-sizing: content-box;
  1121. overflow: hidden;
  1122. .menuComT {
  1123. position: fixed;
  1124. bottom: 400px;
  1125. left: 20px;
  1126. }
  1127. .menuComTSty {
  1128. position: fixed;
  1129. bottom: 20px;
  1130. left: 20px;
  1131. }
  1132. }
  1133. </style>
  1134. <style lang="less">
  1135. .el-overlay {
  1136. background-color: transparent !important;
  1137. .windModelDrawer {
  1138. width: 80% !important;
  1139. backdrop-filter: blur(15px) !important;
  1140. background: rgba(255, 255, 255, .8) !important;
  1141. border-radius: 10px 0 0 10px !important;
  1142. .el-drawer__body {
  1143. overflow: hidden;
  1144. padding-top: 0;
  1145. }
  1146. }
  1147. }
  1148. .windDrawerCla {
  1149. .line {
  1150. display: flex;
  1151. flex-direction: row;
  1152. align-items: center;
  1153. justify-content: space-between;
  1154. width: 100%;
  1155. margin-bottom: 10px;
  1156. .leftContent {
  1157. width: 242px;
  1158. height: 41px;
  1159. display: flex;
  1160. align-items: center;
  1161. background: url("@/assets/cesiumImg/title_left_bg.png") no-repeat;
  1162. span {
  1163. font-size: 16px;
  1164. font-family: Microsoft YaHei;
  1165. font-weight: 400;
  1166. color: #ffffff;
  1167. margin-left: 25px;
  1168. }
  1169. }
  1170. }
  1171. .jcxx,
  1172. .gzck {
  1173. height: 100%;
  1174. }
  1175. .spjk,
  1176. .third {
  1177. height: 80vh;
  1178. }
  1179. }
  1180. </style>