index-amap.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. <template>
  2. <div style="height: 100%; width: 100%; display: flex; flex-direction: row">
  3. <!-- 左边是列表 -->
  4. <div style="width: 300px; height: 100%">
  5. <el-card style="height: 100%">
  6. <template #header>
  7. <div class="card-header">
  8. <el-icon :size="15" class="mr-2" style="vertical-align: middle">
  9. <component :is="Menu" />
  10. </el-icon>
  11. <span>项目列表</span>
  12. </div>
  13. </template>
  14. <el-menu
  15. :key="menuKey"
  16. :default-active="activePath"
  17. :default-openeds="defaultOpeneds"
  18. class="custom-menu"
  19. mode="vertical"
  20. @select="handleMenuSelect"
  21. >
  22. <el-sub-menu
  23. v-for="(parent, parentIndex) in menuItems"
  24. :key="parentIndex"
  25. :index="parent.index"
  26. :class="{ 'parent-active': isParentActive(parent.index) }"
  27. >
  28. <template #title>
  29. <!-- 父菜单图标 -->
  30. <el-icon :size="18" class="mr-2">
  31. <component :is="parent.icon" />
  32. </el-icon>
  33. <span>{{ parent.title }}</span>
  34. </template>
  35. <el-menu-item
  36. v-for="(child, childIndex) in parent.children"
  37. :key="childIndex"
  38. :index="child.index"
  39. >
  40. <!-- 子菜单图标 -->
  41. <el-icon :size="18" class="mr-2">
  42. <component :is="child.icon" />
  43. </el-icon>
  44. <span>{{ child.title }}</span>
  45. </el-menu-item>
  46. </el-sub-menu>
  47. </el-menu>
  48. </el-card>
  49. </div>
  50. <!-- 右边是地图引擎 -->
  51. <div style="width: 100%; height: 100%; display: grid">
  52. <div id="mapview" ref="mapview" v-loading="mapSet.loading" />
  53. <div class="float-stat">
  54. <div class="float-stat-item">
  55. <div class="float-stat-item-key" style="padding-top: 4px">
  56. 查询时间
  57. </div>
  58. <el-date-picker
  59. v-model="queryState.queryTime"
  60. type="datetimerange"
  61. :shortcuts="queryTimeShortCuts"
  62. range-separator="至"
  63. start-placeholder="开始日期时间"
  64. end-placeholder="结束日期时间"
  65. :popper-options="{
  66. placement: 'bottom-start'
  67. }"
  68. />
  69. <el-button
  70. style="margin-left: 10px"
  71. type="primary"
  72. :icon="useRenderIcon(Search)"
  73. @click="getHisPath"
  74. >查询数据</el-button
  75. >
  76. </div>
  77. <div style="display: flex; flex-direction: row">
  78. <div class="float-stat-item" style="margin-top: 10px">
  79. <div class="float-stat-item-key" style="padding-top: 4px">
  80. 播放时间
  81. </div>
  82. <div
  83. class="float-stat-item-value"
  84. style="padding-top: 4px; color: blue"
  85. >
  86. {{ parseTime(playTime) }}
  87. </div>
  88. </div>
  89. <div class="float-stat-item">
  90. <div
  91. class="float-stat-item-key"
  92. style="
  93. margin-top: 10px;
  94. padding-top: 4px;
  95. text-align: right;
  96. padding-right: 20px;
  97. "
  98. >
  99. 速度
  100. </div>
  101. <el-select
  102. v-model="playSpeed"
  103. class="float-stat-item-value"
  104. style="margin-top: 10px; width: 80px"
  105. >
  106. <el-option
  107. v-for="item in playSpeedOptions"
  108. :key="item.value"
  109. :label="item.label"
  110. :value="item.value"
  111. />
  112. </el-select>
  113. </div>
  114. <div class="float-stat-item" style="margin-top: 10px">
  115. <el-button
  116. style="margin-left: 10px"
  117. :icon="useRenderIcon(Search)"
  118. @click="play"
  119. :type="isPlaying ? 'danger' : 'success'"
  120. >{{ isPlaying ? "暂停" : "播放" }}</el-button
  121. >
  122. <el-button
  123. style="margin-left: 10px"
  124. type="primary"
  125. :icon="useRenderIcon(Search)"
  126. @click="getHisPath"
  127. >一键绘制</el-button
  128. >
  129. </div>
  130. </div>
  131. </div>
  132. </div>
  133. </div>
  134. </template>
  135. <script setup lang="ts">
  136. import {
  137. h,
  138. ref,
  139. onMounted,
  140. computed,
  141. onUnmounted,
  142. reactive,
  143. getCurrentInstance,
  144. onBeforeMount
  145. } from "vue";
  146. // 导入 Element Plus 内置图标
  147. import { Bottom, Folder, Menu } from "@element-plus/icons-vue";
  148. import { deviceDetection } from "@pureadmin/utils";
  149. import AMapLoader from "@amap/amap-jsapi-loader";
  150. import { mapJson } from "@/api/mock";
  151. import PicCar from "@/assets/car.png";
  152. import PicCarDisable from "@/assets/car-disable.png";
  153. import { ElNotification, ElMessageBox } from "element-plus";
  154. import { clear } from "console";
  155. import { duration } from "dayjs";
  156. import RtWatchPart from "./rtWatchPart.vue";
  157. import Location from "@/model/location";
  158. import Search from "~icons/ep/search";
  159. import IconDownLoad from "~icons/ep/download";
  160. import { useRenderIcon } from "@/components/ReIcon/src/hooks";
  161. import { fetchHisPath } from "@/api/path";
  162. import { parseTime } from "@/utils/time";
  163. const queryTimeShortCuts = [
  164. {
  165. text: "今天",
  166. value: () => {
  167. const now = new Date();
  168. const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  169. const end = new Date(
  170. now.getFullYear(),
  171. now.getMonth(),
  172. now.getDate() + 1
  173. );
  174. return [start, end];
  175. }
  176. },
  177. {
  178. text: "昨天",
  179. value: () => {
  180. const now = new Date();
  181. const start = new Date(
  182. now.getFullYear(),
  183. now.getMonth(),
  184. now.getDate() - 1
  185. );
  186. const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  187. return [start, end];
  188. }
  189. },
  190. {
  191. text: "本周",
  192. value: () => {
  193. const now = new Date();
  194. const start = new Date(
  195. now.getFullYear(),
  196. now.getMonth(),
  197. now.getDate() - now.getDay()
  198. );
  199. const end = new Date(
  200. now.getFullYear(),
  201. now.getMonth(),
  202. now.getDate() - now.getDay() + 6
  203. );
  204. return [start, end];
  205. }
  206. },
  207. {
  208. text: "本月",
  209. value: () => {
  210. const now = new Date();
  211. const start = new Date(now.getFullYear(), now.getMonth(), 1);
  212. const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
  213. return [start, end];
  214. }
  215. }
  216. ];
  217. const playSpeedOptions = ref([
  218. { label: "1X", value: 1 },
  219. { label: "2X", value: 2 },
  220. { label: "4X", value: 4 },
  221. { label: "10X", value: 10 },
  222. { label: "15X", value: 15 },
  223. { label: "20X", value: 20 },
  224. { label: "30X", value: 30 },
  225. { label: "50X", value: 50 },
  226. { label: "100X", value: 100 },
  227. { label: "120X", value: 120 },
  228. { label: "150X", value: 150 },
  229. { label: "180X", value: 180 },
  230. { label: "200X", value: 200 }
  231. ]);
  232. const playSpeed = ref(1);
  233. let paths = [];
  234. const isPlaying = ref(false);
  235. const playTime = ref(new Date().getTime());
  236. let loop = 0;
  237. const vecAnchor = [];
  238. const markerAnchors = [];
  239. const queryState = ref({
  240. queryTime: [
  241. // new Date(
  242. // new Date().getFullYear(),
  243. // new Date().getMonth(),
  244. // new Date().getDate()
  245. // ),
  246. new Date(2025, 8, 24, 9, 20, 0),
  247. new Date(
  248. new Date().getFullYear(),
  249. new Date().getMonth(),
  250. new Date().getDate() + 1
  251. )
  252. ],
  253. queryProjectName: "",
  254. queryLocationName: "",
  255. queryName: "",
  256. queryPhone: ""
  257. });
  258. const vGlobal = window.vueGlobal;
  259. export interface MapConfigureInter {
  260. on: Fn;
  261. destroy?: Fn;
  262. clearEvents?: Fn;
  263. addControl?: Fn;
  264. setCenter?: Fn;
  265. setZoom?: Fn;
  266. plugin?: Fn;
  267. }
  268. defineOptions({
  269. name: "Amap"
  270. });
  271. let MarkerCluster;
  272. let map: MapConfigureInter;
  273. let AMap = null;
  274. const instance = getCurrentInstance();
  275. const mapSet = reactive({
  276. loading: deviceDetection() ? false : true
  277. });
  278. // 菜单数据
  279. const menuItems = [
  280. {
  281. index: "1",
  282. title: "父菜单1",
  283. icon: Menu,
  284. children: [{ index: "1-1", title: "子菜单1-1", icon: Folder }]
  285. }
  286. ];
  287. const menuKey = ref(0);
  288. let dictMenus = {};
  289. let firstMenuId = null;
  290. // 当前选中的路径
  291. const activePath = ref("");
  292. // 计算所有父菜单的索引,用于默认展开所有子菜单
  293. const defaultOpeneds = computed(() => {
  294. // 提取所有父菜单的 index 组成数组
  295. return menuItems.map(item => item.index);
  296. });
  297. let scheduleId = null;
  298. const selLocation = ref(new Location());
  299. const createMenus = () => {
  300. menuItems.splice(0);
  301. dictMenus = {};
  302. for (const p of vGlobal.vecProject) {
  303. const pIdx = "" + p.id;
  304. menuItems.push({
  305. index: pIdx,
  306. title: p.name,
  307. icon: Menu,
  308. children: []
  309. });
  310. dictMenus[pIdx] = p;
  311. for (const m of p.locations) {
  312. const mIdx = pIdx + "-" + m.id;
  313. menuItems[menuItems.length - 1].children.push({
  314. index: mIdx,
  315. title: m.name,
  316. icon: Folder
  317. });
  318. dictMenus[mIdx] = m;
  319. if (!firstMenuId) {
  320. firstMenuId = mIdx;
  321. }
  322. }
  323. }
  324. menuKey.value++;
  325. };
  326. // 处理菜单选中事件
  327. const handleMenuSelect = index => {
  328. activePath.value = index;
  329. console.log("handleMenuSelect", index, dictMenus[index]);
  330. const location = dictMenus[index];
  331. if (location) {
  332. selLocation.value = location;
  333. map.setCenter([location.center.x, location.center.y]);
  334. map.setZoom(13);
  335. }
  336. };
  337. // 判断父菜单是否需要激活样式
  338. const isParentActive = parentIndex => {
  339. // 检查当前选中项是否属于该父菜单
  340. return activePath.value.startsWith(parentIndex + "-");
  341. };
  342. const genMarkerLabel = anc => {
  343. const label = {
  344. direction: "bottom",
  345. //设置文本标注偏移量
  346. offset: new AMap.Pixel(-4, 0),
  347. //设置文本标注内容
  348. content: anc.isCharging
  349. ? `<div style="color: red; font-weight: bold; font-size: 14px; " > A[#${anc.id}](充电中)</div>`
  350. : `<div style="font-size: 14px; "> A[#${anc.id}](${anc.percentBattery}%)</div>`
  351. };
  352. return label;
  353. };
  354. const getHisPath = () => {
  355. const proj = selLocation.value.parent;
  356. let projId = 0;
  357. if (proj) {
  358. projId = proj.id;
  359. }
  360. const queryParams = {
  361. beginTime: queryState.value.queryTime[0].getTime(),
  362. endTime: queryState.value.queryTime[1].getTime(),
  363. sortMode: 1,
  364. projId: projId
  365. };
  366. fetchHisPath(queryParams).then(res => {
  367. if (res.success) {
  368. paths = [];
  369. playTime.value = 0;
  370. for (const m of markerAnchors) {
  371. m.remove();
  372. }
  373. markerAnchors.splice(0);
  374. for (const t of res.items) {
  375. const tm = Number(t["tm"]);
  376. if (playTime.value == 0 || playTime.value > tm) {
  377. playTime.value = tm;
  378. }
  379. paths.push(t);
  380. }
  381. if (paths.length) {
  382. ElNotification.success({
  383. message: "查询成功"
  384. });
  385. } else {
  386. ElNotification.info({
  387. message: "查询时间段内无数据"
  388. });
  389. }
  390. playTime.value = queryState.value.queryTime[0].getTime();
  391. isPlaying.value = false;
  392. loop = 0;
  393. }
  394. });
  395. };
  396. const play = () => {
  397. isPlaying.value = !isPlaying.value;
  398. };
  399. const genBatStr = battery => {
  400. let perBat = Math.floor(((battery - 3700) / (4100 - 3700)) * 100);
  401. if (perBat < 0) {
  402. perBat = 0;
  403. } else if (perBat > 100) {
  404. perBat = 100;
  405. }
  406. return perBat;
  407. };
  408. const genMarker = (id, x, y, isCharging) => {
  409. const marker = new AMap.Marker({
  410. extData: {
  411. id: id,
  412. isOnline: 1
  413. },
  414. position: [x, y],
  415. icon: PicCar,
  416. offset: new AMap.Pixel(-18, -10),
  417. label: {
  418. direction: "bottom",
  419. //设置文本标注偏移量
  420. offset: new AMap.Pixel(-4, 0),
  421. //设置文本标注内容
  422. content: isCharging
  423. ? `<div> A[#${id}](充电中)</div>`
  424. : `<div> A[#${id}]</div>`
  425. }
  426. // visible: v.isOnline
  427. });
  428. marker.on("click", ({ lnglat }) => {
  429. // map.setZoom(13); //设置地图层级
  430. // map.setCenter(lnglat);
  431. const extData = marker.getExtData();
  432. const id = extData["id"];
  433. const x = extData["x"];
  434. const y = extData["y"];
  435. const battery = extData["battery"];
  436. const isCharging = battery > 4400;
  437. //信息窗体的内容
  438. var content = [
  439. `<div style="font-size: 14px; "><b style="font-size: 16px; ">A[#${id}]</b>`,
  440. `<b>经度:</b> ${x} <b>纬度:</b> ${y}`,
  441. `<b>电池${isCharging ? "状态" : "电量"}:</b> ${isCharging ? "充电中" : genBatStr(battery) + "%"}`,
  442. "</div>"
  443. ];
  444. //创建 infoWindow 实例
  445. var infoWindow = new AMap.InfoWindow({
  446. content: content.join("<br>"), //传入字符串拼接的 DOM 元素
  447. anchor: "top-left"
  448. });
  449. //打开信息窗体
  450. infoWindow.open(map, marker.getPosition()); //map 为当前地图的实例,map.getCenter() 用于获取地图中心点坐标。
  451. });
  452. marker.setMap(map);
  453. markerAnchors.push(marker);
  454. return marker;
  455. };
  456. const schedule = () => {
  457. if (!isPlaying.value) {
  458. return;
  459. }
  460. let path = null;
  461. if (loop < paths.length) {
  462. path = paths[loop];
  463. }
  464. // 计算播放时间
  465. playTime.value += playSpeed.value * 1000;
  466. console.log("playTime", parseTime(playTime.value), loop, playTime.value);
  467. console.log("path", path ? Number(path["tm"]) : "000-000");
  468. // 在播放时间内
  469. while (playTime.value >= Number(path["tm"]) && loop < paths.length) {
  470. path = paths[loop];
  471. const ancAddr = Number(path["addr"]);
  472. let marker = null;
  473. let extData = null;
  474. for (const m of markerAnchors) {
  475. // 查找 anchor
  476. extData = m.getExtData();
  477. const id = Number(extData["id"]);
  478. if (id == ancAddr) {
  479. marker = m;
  480. break;
  481. }
  482. }
  483. if (marker) {
  484. // console.log("marker find", marker);
  485. // 找到 anchor
  486. marker.setPosition([Number(path["x"]), Number(path["y"])]);
  487. } else {
  488. // 未找到 anchor
  489. // console.log("marker find", marker);
  490. marker = genMarker(ancAddr, Number(path["x"]), Number(path["y"]), 0);
  491. extData = marker.getExtData();
  492. }
  493. let isOnline = Number(path["status"]) || 0;
  494. extData["isOnline"] = isOnline;
  495. extData["battery"] = Number(path["battery"]);
  496. extData["x"] = Number(path["x"]);
  497. extData["y"] = Number(path["y"]);
  498. const isCharging = extData["battery"] > 4400;
  499. marker.setExtData(extData);
  500. const label = {
  501. direction: "bottom",
  502. //设置文本标注偏移量
  503. offset: new AMap.Pixel(-4, 0),
  504. //设置文本标注内容
  505. content: isCharging
  506. ? `<div style="color: red; font-weight: bold; font-size: 14px; " > A[#${extData["id"]}](充电中)</div>`
  507. : `<div style="font-size: 14px; font-weight: bold; "> A[#${extData["id"]}](${genBatStr(extData["battery"])}%)</div>`
  508. };
  509. marker.setLabel(label);
  510. if (isOnline) {
  511. marker.setIcon(PicCar);
  512. } else {
  513. marker.setIcon(PicCarDisable);
  514. }
  515. loop++;
  516. console.log("playTime", parseTime(playTime.value), loop, playTime.value);
  517. console.log("path", path ? Number(path["tm"]) : "000-000");
  518. } // end while 在播放时间内
  519. // 播放时间结束
  520. if (loop >= paths.length) {
  521. isPlaying.value = false;
  522. ElNotification.success({
  523. message: "查询时间内已有数据回放结束"
  524. });
  525. }
  526. };
  527. onBeforeMount(() => {
  528. if (!instance) return;
  529. const { MapConfigure } = instance.appContext.config.globalProperties.$config;
  530. const { options } = MapConfigure;
  531. AMapLoader.load({
  532. key: MapConfigure.amapKey,
  533. version: "2.0",
  534. plugins: ["AMap.MarkerCluster", "AMap.MoveAnimation"]
  535. })
  536. .then(aMap => {
  537. AMap = aMap;
  538. // 创建地图实例
  539. map = new AMap.Map(instance.refs.mapview, options);
  540. //地图中添加地图操作ToolBar插件
  541. map.plugin(["AMap.ToolBar", "AMap.MapType", "AMap.ControlBar"], () => {
  542. map.addControl(new AMap.ToolBar());
  543. //地图类型切换
  544. map.addControl(
  545. new AMap.MapType({
  546. defaultType: 0,
  547. position: {
  548. right: "10px",
  549. top: "10px"
  550. }
  551. })
  552. );
  553. map.addControl(
  554. new AMap.ControlBar({
  555. position: {
  556. right: "10px",
  557. top: "120px"
  558. }
  559. })
  560. );
  561. });
  562. // MarkerCluster = new AMap.MarkerCluster(map, [], {
  563. // // 聚合网格像素大小
  564. // gridSize: 80,
  565. // maxZoom: 14,
  566. // renderMarker(ctx) {
  567. // const { marker, data } = ctx;
  568. // if (Array.isArray(data) && data[0]) {
  569. // const { driver, plateNumber, orientation } = data[0];
  570. // const content = `<img style="transform: scale(1) rotate(${
  571. // 360 - Number(orientation)
  572. // }deg);" src='${car}' />`;
  573. // marker.setContent(content);
  574. // marker.setLabel({
  575. // direction: "bottom",
  576. // //设置文本标注偏移量
  577. // offset: new AMap.Pixel(-4, 0),
  578. // //设置文本标注内容
  579. // content: `<div> ${plateNumber}(${driver})</div>`
  580. // });
  581. // marker.setOffset(new AMap.Pixel(-18, -10));
  582. // marker.on("click", ({ lnglat }) => {
  583. // map.setZoom(13); //设置地图层级
  584. // map.setCenter(lnglat);
  585. // });
  586. // }
  587. // }
  588. // });
  589. complete();
  590. })
  591. .catch(() => {
  592. mapSet.loading = false;
  593. throw "地图加载失败,请重新加载";
  594. });
  595. });
  596. const handleResize = () => {
  597. if (map) {
  598. map.resize(); // 调用高德地图的resize方法重新计算尺寸
  599. }
  600. };
  601. onMounted(() => {
  602. const ci = setInterval(() => {
  603. if (vGlobal.isInited()) {
  604. clearInterval(ci);
  605. createMenus();
  606. handleMenuSelect(firstMenuId);
  607. window.addEventListener("resize", handleResize);
  608. }
  609. }, 100);
  610. console.log("onMounted");
  611. scheduleId = setInterval(() => {
  612. schedule();
  613. }, 1000);
  614. });
  615. onUnmounted(() => {
  616. window.removeEventListener("resize", handleResize);
  617. if (map) {
  618. // 销毁地图实例
  619. map.destroy() && map.clearEvents("click");
  620. }
  621. clearInterval(scheduleId);
  622. console.log("onUnmounted");
  623. scheduleId = null;
  624. });
  625. // 地图创建完成(动画关闭)
  626. const complete = (): void => {
  627. if (map) {
  628. map.on("complete", () => {
  629. mapSet.loading = false;
  630. });
  631. }
  632. };
  633. </script>
  634. <style scoped>
  635. .custom-menu {
  636. width: 100%;
  637. }
  638. :deep(.el-sub-menu .el-menu-item.is-active) {
  639. color: #0557aa !important;
  640. background-color: #e6f7ff !important;
  641. }
  642. /* 子菜单选中样式 */
  643. :deep(.el-menu-item.is-active) {
  644. color: #0557aa !important;
  645. background-color: #e6f7ff !important;
  646. font-weight: 600;
  647. }
  648. /* 父菜单激活样式 - 当子菜单选中时 */
  649. :deep(.parent-active .el-sub-menu__title) {
  650. color: #0557aa !important;
  651. font-weight: bold;
  652. }
  653. /* 父菜单 hover 样式保持一致 */
  654. :deep(.el-sub-menu__title:hover) {
  655. color: #0557aa !important;
  656. }
  657. #mapview {
  658. height: calc(100vh - 48px);
  659. width: 100%;
  660. grid-area: 1 / 1 / -1 / -1;
  661. }
  662. /* 新增根元素高度设置 */
  663. :root {
  664. height: 100%;
  665. }
  666. /* 确保父级容器高度正确传递 */
  667. :deep(html),
  668. :deep(body) {
  669. height: 100%;
  670. margin: 0;
  671. padding: 0;
  672. }
  673. /* 调整网格容器样式 */
  674. :deep(.grid-container) {
  675. /* 假设地图外层容器添加class="grid-container" */
  676. display: grid;
  677. height: 100%;
  678. }
  679. .float-stat {
  680. padding: 10px 10px 10px 10px;
  681. width: fit-content;
  682. height: fit-content;
  683. background: #ffffff;
  684. grid-area: 1 / 1 / -1 / -1;
  685. z-index: 10;
  686. border-radius: 2px;
  687. opacity: 0.95;
  688. box-shadow: rgba(0, 0, 0, 0.6) 0 2px 2px;
  689. display: flex;
  690. flex-direction: column;
  691. margin-top: 10px;
  692. justify-self: center;
  693. }
  694. .float-stat-item {
  695. /* width: 100%; */
  696. width: fit-content;
  697. display: flex;
  698. flex-direction: row;
  699. }
  700. .float-stat-item-key {
  701. font-weight: 900;
  702. width: 80px;
  703. }
  704. .float-stat-item-value {
  705. font-weight: 600;
  706. width: fit-content;
  707. text-align: right;
  708. }
  709. :deep(.amap-marker-label) {
  710. border: none !important;
  711. }
  712. :deep(.amap-copyright) {
  713. display: none !important;
  714. }
  715. :deep(.amap-logo) {
  716. display: none !important;
  717. }
  718. </style>