map.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. <template>
  2. <view class="container">
  3. <navigation-bar @ready="handleNavReady"></navigation-bar>
  4. <view class="dialog" v-if="styleData.dialog" :style="styleData.dialog">
  5. <view class="height-88 flex-align-center bg-fff">
  6. <view class="dialog_logo ml-24"></view>
  7. <view class="dialog_search ml-24" @click.stop="toSearch"></view>
  8. </view>
  9. <!-- <view class="height-72 flex" style="background-color: #f9f9f9">
  10. <view class="width-half flex-center" @click.stop="checkFilterDistance">
  11. <view class="fs-26 color-000-6 mr-8">距离</view>
  12. <view
  13. class="width-20 height-20 br-round bg-000-08 lh-10 text-center transition"
  14. :style="{
  15. transform: filterDialog.distanceSelector ? 'rotate(180deg)' : '',
  16. }"
  17. >
  18. <uni-icons
  19. type="bottom"
  20. size="8"
  21. color="rgba(0,0,0,0.4)"
  22. ></uni-icons>
  23. </view>
  24. </view>
  25. <view class="width-half flex-center">
  26. <view
  27. class="width-32 height-32 br-round lh-20 text-center"
  28. :style="{
  29. border: filterDialog.discounts
  30. ? '1px solid var(--color-primary)'
  31. : '1px solid rgba(0, 0, 0, 0.4)',
  32. backgroundColor: filterDialog.discounts
  33. ? 'var(--color-primary)'
  34. : '#fff',
  35. }"
  36. >
  37. <uni-icons
  38. type="checkmarkempty"
  39. size="10"
  40. color="#ffffff"
  41. ></uni-icons>
  42. </view>
  43. <view
  44. :class="[
  45. 'fs-26',
  46. 'ml-8',
  47. filterDialog.discounts ? 'color-primary' : 'color-000-6',
  48. ]"
  49. >支持省钱充电</view
  50. >
  51. </view>
  52. </view> -->
  53. <view
  54. class="dialog_selector"
  55. :style="{
  56. height: filterDialog.distanceSelector ? '190rpx' : '0rpx',
  57. borderTop: `1rpx solid rgba(0, 0, 0, ${
  58. filterDialog.distanceSelector ? '0.1' : '0'
  59. })`,
  60. }"
  61. >
  62. <view class="flex-center flex-wrap pb-14">
  63. <view
  64. :class="[
  65. 'type',
  66. 'flex-shrink',
  67. 'flex-center',
  68. 'mt-20',
  69. (index + 1) % 5 === 0 ? 'mr-0' : 'mr-20',
  70. `type-${
  71. item.value === filterDialog.options.distance ? 'active' : ''
  72. }`,
  73. ]"
  74. v-for="(item, index) in filterDialog.distanceRange"
  75. :key="index"
  76. @click.stop="changeFilterDistance(index)"
  77. style="width: 117rpx"
  78. >{{ item.value }}km</view
  79. >
  80. </view>
  81. </view>
  82. <view class="dialog_event" v-if="ready && mapBanner.length > 0">
  83. <swiper
  84. class="swiper"
  85. circular
  86. :indicator-dots="true"
  87. :autoplay="mapBanner.length > 1"
  88. :interval="3000"
  89. >
  90. <swiper-item
  91. class="swiper-item full-percent"
  92. v-for="(item, index) in mapBanner"
  93. :key="index"
  94. @click="to(item.linkUrl)"
  95. >
  96. <view
  97. class="full-percent"
  98. :style="{
  99. backgroundImage: `url(${item.bannerUrl})`,
  100. }"
  101. ></view>
  102. </swiper-item>
  103. </swiper>
  104. </view>
  105. </view>
  106. <view
  107. v-if="styleData.dialogPlaceHolderHeight"
  108. :style="{ height: `${styleData.dialogPlaceHolderHeight}px` }"
  109. ></view>
  110. <map
  111. v-if="ready"
  112. id="map"
  113. :style="{
  114. width: '100%',
  115. height: styleData.dialogHeight
  116. ? `calc(100vh - ${styleData.dialogHeight}px - ${
  117. styleData.cardHeight - 24
  118. }px)`
  119. : '50vh',
  120. zIndex: 1,
  121. pointerEvents: loading || !mapMode ? 'none' : 'auto',
  122. }"
  123. :latitude="mapProps.latitude"
  124. :longitude="mapProps.longitude"
  125. :markers="markers"
  126. min-scale="1"
  127. :scale="mapProps.scale"
  128. @regionchange="mapChange"
  129. @updated="mapUpdated"
  130. @labeltap="tapMarker"
  131. @markertap="tapMarker"
  132. :show-scale="true"
  133. ></map>
  134. <block v-else>
  135. <view
  136. v-if="styleData.dialogHeight"
  137. class="mock-map flex-center"
  138. :style="{
  139. height: `calc(100vh - ${styleData.dialogHeight}px - ${
  140. styleData.cardHeight - 24
  141. }px)`,
  142. }"
  143. >
  144. <image
  145. src="/static/images/map-bg.jpg"
  146. mode="widthFix"
  147. style="width: 100%"
  148. ></image>
  149. <image
  150. src="/static/images/map-current.png"
  151. mode="widthFix"
  152. class="absolute-center"
  153. style="width: 34px"
  154. ></image>
  155. </view>
  156. </block>
  157. <view
  158. class="card"
  159. :style="{
  160. height: mapMode
  161. ? `${styleData.cardHeight}px`
  162. : `calc(100vh - ${styleData.navHeight}px)`,
  163. bottom: '0px',
  164. borderRadius: mapMode ? '16rpx 16rpx 0px 0px' : '0px',
  165. overflowY: mapMode ? 'visible' : 'auto',
  166. }"
  167. @touchstart="touchCardStart"
  168. @touchmove="touchCardMove"
  169. >
  170. <view
  171. v-if="mapMode"
  172. class="card_location height-64 width-64 bg-fff flex-center"
  173. @touchstart.stop="resetLocation"
  174. >
  175. <image
  176. src="/static/images/map-re-location.png"
  177. mode="widthFix"
  178. class="width-48"
  179. ></image>
  180. </view>
  181. <block v-if="station.length">
  182. <!-- 列表 -->
  183. <block v-if="!mapMode">
  184. <view class="station" v-for="(item, index) in station" :key="index">
  185. <view v-if="index !== 0" class="card_line"></view>
  186. <charge-station
  187. :title="item.stationName"
  188. :address="item.address"
  189. :price="item.totalFee"
  190. :fast="item.fastEquipmentInfos"
  191. :slow="item.slowEquipmentInfos"
  192. :sId="item.StationID"
  193. :distance="item.distance"
  194. :latitude="item.location.stationLat"
  195. :longitude="item.location.stationLng"
  196. :availableParkingNum="item.availableParkingNum"
  197. :parkingNum="item.parkingNum"
  198. :activity-list="item.activityList"
  199. :fromMap="true"
  200. ></charge-station>
  201. </view>
  202. <view class="station-placeholder"></view>
  203. <view class="station-iphonex"></view>
  204. </block>
  205. <block v-else>
  206. <!-- 单个 -->
  207. <view class="station">
  208. <charge-station
  209. :title="station[markersIndex].stationName"
  210. :address="station[markersIndex].address"
  211. :price="station[markersIndex].totalFee"
  212. :fast="station[markersIndex].fastEquipmentInfos"
  213. :slow="station[markersIndex].slowEquipmentInfos"
  214. :sId="station[markersIndex].StationID"
  215. :distance="station[markersIndex].distance"
  216. :latitude="station[markersIndex].location.stationLat"
  217. :longitude="station[markersIndex].location.stationLng"
  218. :availableParkingNum="station[markersIndex].availableParkingNum"
  219. :parkingNum="station[markersIndex].parkingNum"
  220. :activity-list="station[markersIndex].activityList"
  221. :fromMap="true"
  222. ></charge-station>
  223. </view>
  224. </block>
  225. </block>
  226. <block v-else>
  227. <view class="card_empty flex-column flex-align-center pt-20">
  228. <view v-if="loading" class="mt-60 animation-loading"
  229. ><uni-icons
  230. type="spinner-cycle"
  231. size="20"
  232. color="rgba(0, 0, 0, 0.5)"
  233. ></uni-icons
  234. ></view>
  235. <image
  236. class="image"
  237. v-else
  238. src="/static/images/map-empty.png"
  239. mode="widthFix"
  240. />
  241. <view class="fs-22 mt-14 color-000-5">{{
  242. loading ? "加载中" : "暂无充电站信息"
  243. }}</view>
  244. </view>
  245. </block>
  246. </view>
  247. <view
  248. class="charging flex-align-center"
  249. v-if="charging"
  250. @click="toCharging"
  251. >
  252. <image
  253. class="width-64 ml-12"
  254. src="/static/images/map-charging.png"
  255. mode="widthFix"
  256. ></image>
  257. <view class="fs-26 color-fff ml-12">{{
  258. charging.chargeStatus === 0 ? "已预约" : "充电中"
  259. }}</view>
  260. </view>
  261. </view>
  262. </template>
  263. <script setup lang="ts">
  264. import {debounce, throttle} from "@/utils/util";
  265. const defaulDistance = 3;
  266. const defaultScale = 12;
  267. const pointSize = {
  268. width: 30,
  269. height: 55,
  270. fontSize: 7,
  271. iconPath: "/static/images/map-point.png",
  272. currentWidth: 42,
  273. currentHeight: 72,
  274. currentFontSize: 10,
  275. currentIconPath: "/static/images/map-point-current.png",
  276. androidX:-4,
  277. androidCurrentX: -7,
  278. };
  279. import { fetchHomeBanner } from "@/api";
  280. import { deCode } from "../../utils/code";
  281. import { rpxToPx } from "../../utils/device";
  282. import { fetchToken, onLogin } from "@/api/auth";
  283. import { fetchStations, fetchChargeStatus } from "@/api/charge";
  284. import { fetchCollectList } from "@/api/user";
  285. import { fetchLocation } from "@/utils/location";
  286. import { to } from "@/utils/navigate";
  287. import { onLoad, onShow } from "@dcloudio/uni-app";
  288. import { ref } from "vue";
  289. const currentLocation = ref(null)
  290. const isIOS = ref(false);
  291. const token = ref<string>();
  292. const ready = ref(false);
  293. const loading = ref(false);
  294. const styleData = ref({
  295. dialog: "",
  296. dialogHeight: 0,
  297. dialogPlaceHolderHeight: 0,
  298. cardHeight: 0,
  299. navHeight: 0,
  300. });
  301. const filterDialog = ref({
  302. discounts: false,
  303. distanceSelector: false,
  304. distanceRange: [
  305. {
  306. value: 1,
  307. scale: defaultScale + 3,
  308. },
  309. {
  310. value: 2,
  311. scale: defaultScale + 1,
  312. },
  313. {
  314. value: 3,
  315. scale: defaultScale,
  316. },
  317. {
  318. value: 5,
  319. scale: defaultScale - 0.2,
  320. },
  321. {
  322. value: 10,
  323. scale: defaultScale - 0.5,
  324. },
  325. {
  326. value: 20,
  327. scale: defaultScale - 1.5,
  328. },
  329. {
  330. value: 30,
  331. scale: defaultScale - 2,
  332. },
  333. {
  334. value: 50,
  335. scale: defaultScale - 2.5,
  336. },
  337. {
  338. value: 100,
  339. scale: defaultScale - 3.5,
  340. },
  341. {
  342. value: 200,
  343. scale: defaultScale - 4.5,
  344. },
  345. ],
  346. options: {
  347. distance: defaulDistance,
  348. status: 0,
  349. },
  350. status: [
  351. {
  352. title: "全部",
  353. },
  354. {
  355. title: "空闲",
  356. },
  357. {
  358. title: "忙碌",
  359. },
  360. ],
  361. });
  362. const stationPage = ref({
  363. page: 1,
  364. pageSize: 6,
  365. hasNext: false,
  366. });
  367. const station = ref<any[]>([]);
  368. const charging = ref<any>();
  369. const mapMode = ref(true);
  370. const mapProps = ref({
  371. latitude: 23.098994,
  372. longitude: 113.32252,
  373. selflatitude: 23.098994,
  374. selflongitude: 113.32252,
  375. scale: defaultScale,
  376. });
  377. const markersIndex = ref(-1);
  378. const markers = ref<any[]>([]);
  379. const mapBanner = ref<any[]>([]);
  380. let isIgnoreChangeLocation = false;
  381. const refreshStation = (location: any) => {
  382. let length = 0;
  383. let available = 0;
  384. const { latitude, longitude } = location;
  385. return fetchStations(
  386. stationPage.value.page,
  387. stationPage.value.pageSize,
  388. latitude,
  389. longitude,
  390. mapProps.value.selflatitude,
  391. mapProps.value.selflongitude,
  392. {
  393. distance: filterDialog.value.options.distance,
  394. status: filterDialog.value.options.status,
  395. }
  396. ).then((res) => {
  397. const _markersIndex = stationPage.value.page === 1 ? 0 : markersIndex.value;
  398. // console.log("xxxxx",res)
  399. const _markers: any[] = res.map((item, index) => {
  400. item.totalFee =(Number(item.currentPolicyInfo?.servicePrice||0)+Number(item.currentPolicyInfo?.elecPrice||0)).toFixed(2)
  401. length = 0;
  402. available = 0;
  403. // item.equipmentInfos &&
  404. // item.equipmentInfos.forEach((eq: any) => {
  405. // eq.connectorInfos &&
  406. // eq.connectorInfos.forEach((co: any) => {
  407. // length += 1;
  408. // if (
  409. // co.connectorStatusInfo &&
  410. // co.connectorStatusInfo.status === 1
  411. // ) {
  412. // available += 1;
  413. // }
  414. // });
  415. // });
  416. return {
  417. id: Number(item.StationID),
  418. // (Number()+Number(item.electricityFee)||0).toFixed(2),
  419. latitude: item.location.stationLat,
  420. longitude: item.location.stationLng,
  421. iconPath:
  422. index === _markersIndex
  423. ? pointSize.currentIconPath
  424. : pointSize.iconPath,
  425. width:
  426. index === _markersIndex ? pointSize.currentWidth : pointSize.width,
  427. height:
  428. index === _markersIndex ? pointSize.currentHeight : pointSize.height,
  429. label: {
  430. content: `${item.availableParkingNum||0}/${item.parkingNum||0}`,
  431. // content: `1/10`,
  432. color: "#ffffff",
  433. fontSize:
  434. index === _markersIndex
  435. ? pointSize.currentFontSize
  436. : pointSize.fontSize,
  437. textAlign: isIOS.value ? "center" : "left",
  438. anchorX: isIOS.value
  439. ? 0
  440. : index === _markersIndex
  441. ? -17 // 针对安卓设备调整 anchorX 实现居中
  442. : -10,
  443. anchorY: -(
  444. (index === _markersIndex
  445. ? pointSize.currentHeight
  446. : pointSize.height- 3)
  447. ),
  448. },
  449. };
  450. });
  451. if (stationPage.value.page === 1) {
  452. _markers.push({
  453. id: -1,
  454. latitude: mapProps.value.selflatitude,
  455. longitude: mapProps.value.selflongitude,
  456. iconPath: "/static/images/map-current.png",
  457. width: 34,
  458. height: 34,
  459. });
  460. }
  461. isIgnoreChangeLocation = markers.value.length !== _markers.length;
  462. stationPage.value.hasNext = res.length >= stationPage.value.pageSize;
  463. // empty.value = stationPage.value.page === 1 ? res.length <= 0 : false;
  464. station.value =
  465. stationPage.value.page === 1 ? res : [...station.value, ...res];
  466. markersIndex.value = _markersIndex;
  467. markers.value = _markers;
  468. return res;
  469. });
  470. };
  471. const refresh = () => {
  472. if (loading.value) {
  473. return;
  474. }
  475. console.log("刷新电站");
  476. uni.showLoading({
  477. title: "加载中",
  478. });
  479. loading.value = true;
  480. stationPage.value.page = 1;
  481. stationPage.value.hasNext = false;
  482. station.value = [];
  483. markers.value = [];
  484. markersIndex.value = 0;
  485. fetchLocation()
  486. .then((res: any) => {
  487. mapProps.value.latitude = res.latitude;
  488. mapProps.value.longitude = res.longitude;
  489. mapProps.value.selflatitude = res.latitude;
  490. mapProps.value.selflongitude = res.longitude;
  491. isIgnoreChangeLocation = true;
  492. return refreshStation(res);
  493. })
  494. .then(() => {
  495. uni.hideLoading();
  496. loading.value = false;
  497. ready.value = true;
  498. })
  499. .catch((err) => {
  500. console.log(err);
  501. uni.hideLoading();
  502. loading.value = false;
  503. if (!token.value) {
  504. ready.value = true;
  505. return;
  506. }
  507. uni.showModal({
  508. content: `${err.errMsg},请重试`,
  509. });
  510. });
  511. };
  512. const handleNavReady = (e: any) => {
  513. console.log(e);
  514. styleData.value.dialog = `padding-top:${e.detail.statusBarHeight - 6}px;`;
  515. const searchHeight = rpxToPx(88);
  516. const filterHeight = 0; // rpxToPx(72);
  517. styleData.value.dialogHeight =
  518. e.detail.statusBarHeight - 6 + searchHeight + filterHeight;
  519. styleData.value.dialogPlaceHolderHeight =
  520. styleData.value.dialogHeight - e.detail.navigationBarHeight;
  521. styleData.value.cardHeight = rpxToPx(402) + e.detail.statusBarHeight;
  522. styleData.value.navHeight = e.detail.navigationBarHeight;
  523. };
  524. const toSearch = () => {
  525. if (!ready.value) {
  526. return;
  527. }
  528. uni.navigateTo({
  529. url: "/pages-charge/search/search",
  530. });
  531. };
  532. onLoad((query: any) => {
  533. // 只为了打包进tab-bar使用
  534. console.log(fetchToken, onLogin);
  535. // 扫普通码
  536. if (query.q) {
  537. console.log("扫普通码", decodeURIComponent(query.q));
  538. getApp<any>().globalData.normalCode = decodeURIComponent(query.q); // 获取到二维码原始链接内容
  539. }
  540. const device = uni.getSystemInfoSync();
  541. isIOS.value = device.osName === "ios";
  542. setTimeout(() => {
  543. token.value = getApp<any>().globalData.token || "";
  544. if (!token.value) {
  545. refresh();
  546. fetchBanner();
  547. onLogin((_token) => {
  548. if (getApp<any>().globalData.normalCode) {
  549. const code: string = getApp<any>().globalData.normalCode;
  550. getApp<any>().globalData.normalCode = "";
  551. deCode(code);
  552. }
  553. token.value = _token;
  554. fetchCollectList().then(() => {
  555. refresh();
  556. fetchCharging();
  557. fetchBanner();
  558. });
  559. });
  560. return;
  561. }
  562. fetchCollectList().then(() => {
  563. if (getApp<any>().globalData.normalCode) {
  564. const code: string = getApp<any>().globalData.normalCode;
  565. getApp<any>().globalData.normalCode = "";
  566. deCode(code);
  567. }
  568. refresh();
  569. fetchCharging();
  570. fetchBanner();
  571. });
  572. }, 300);
  573. });
  574. onShow(() => {
  575. if (token.value) {
  576. fetchBanner();
  577. charging.value = undefined;
  578. setTimeout(() => {
  579. fetchCharging();
  580. }, 2000);
  581. }
  582. });
  583. const fetchCharging = () => {
  584. //手工停止充电后,暂时不查询充电状态
  585. if (getApp<any>().globalData.manualStop) {
  586. return;
  587. }
  588. fetchChargeStatus().then((res) => {
  589. if (res && [0, 1, 2, 3].includes(res.chargeStatus)) {
  590. charging.value = res;
  591. } else {
  592. // 充电已结束或无充电数据,重置手工停止标识
  593. if (getApp<any>().globalData.manualStop) {
  594. getApp<any>().globalData.manualStop = false;
  595. }
  596. charging.value = undefined;
  597. }
  598. }).catch(() => {
  599. // 查询失败时不重置 manualStop,保持当前状态
  600. });
  601. };
  602. const fetchBanner = () => {
  603. fetchHomeBanner().then((res) => {
  604. mapBanner.value = res.filter((item: any) => item.status === 1);
  605. }).catch(() => {});
  606. };
  607. // const checkDiscounts = () => {
  608. // filterDialog.value.discounts = !filterDialog.value.discounts;
  609. // };
  610. const checkFilterDistance = () => {
  611. if (isIgnoreChangeLocation) {
  612. return;
  613. }
  614. if (!mapMode.value) {
  615. mapMode.value = true;
  616. }
  617. filterDialog.value.distanceSelector = !filterDialog.value.distanceSelector;
  618. };
  619. const changeFilterDistance = (index: number) => {
  620. filterDialog.value.options.distance =
  621. filterDialog.value.distanceRange[index].value;
  622. if (mapProps.value.scale === filterDialog.value.distanceRange[index].scale) {
  623. refresh();
  624. return;
  625. }
  626. mapProps.value.scale = filterDialog.value.distanceRange[index].scale;
  627. checkFilterDistance();
  628. };
  629. const resetLocation = () => {
  630. if (loading.value) {
  631. return;
  632. }
  633. // eslint-disable-next-line promise/catch-or-return
  634. fetchLocation().then((res: any) => {
  635. const mapCtx = uni.createMapContext("map");
  636. const { latitude, longitude } = res;
  637. mapCtx.moveToLocation({
  638. latitude,
  639. longitude,
  640. });
  641. mapProps.value.scale = defaultScale;
  642. });
  643. };
  644. const mapUpdated = (e: any) => {
  645. // console.log('map updated', isIgnoreChangeLocation)
  646. setTimeout(() => {
  647. isIgnoreChangeLocation = false;
  648. }, 500);
  649. };
  650. const mapChange = (e: any) => {
  651. if (isIgnoreChangeLocation) {
  652. return;
  653. }
  654. if (e.type === "end" && markers.value.length) {
  655. //TODO 防抖500ms触发一次
  656. console.log("throttle")
  657. currentLocation.value = e.target.centerLocation
  658. // throttle(()=>{
  659. //
  660. // },500)
  661. // console.log("map change end", {
  662. // ...e.detail.centerLocation,
  663. // });
  664. debounceRefreshStation()
  665. }
  666. };
  667. const debounceRefreshStation = throttle(() => {
  668. const { latitude, longitude } = currentLocation.value;
  669. stationPage.value.page = 1;
  670. console.log("debounceRefreshStation run...")
  671. refreshStation({
  672. latitude,
  673. longitude,
  674. });
  675. }, 500)
  676. const _changeMarker = (current: number) => {
  677. const _markers = JSON.parse(JSON.stringify(markers.value));
  678. const markersNewIndex = current;
  679. _markers[markersIndex.value].iconPath = pointSize.iconPath;
  680. _markers[markersIndex.value].width = pointSize.width;
  681. _markers[markersIndex.value].height = pointSize.height;
  682. _markers[markersIndex.value].label.fontSize = pointSize.fontSize;
  683. _markers[markersIndex.value].label.anchorY = -(pointSize.height - 3);
  684. _markers[markersIndex.value].label.textAlign = isIOS.value ? "center" : "left";
  685. _markers[markersIndex.value].label.anchorX = isIOS.value ? 0 : -10;
  686. _markers[markersNewIndex].iconPath = pointSize.currentIconPath;
  687. _markers[markersNewIndex].width = pointSize.currentWidth;
  688. _markers[markersNewIndex].height = pointSize.currentHeight;
  689. _markers[markersNewIndex].label.fontSize = pointSize.currentFontSize;
  690. _markers[markersNewIndex].label.textAlign = isIOS.value ? "center" : "left";
  691. _markers[markersNewIndex].label.anchorY = -(pointSize.currentHeight - 3);
  692. _markers[markersNewIndex].label.anchorX = isIOS.value ? 0 : -17;
  693. isIgnoreChangeLocation = true;
  694. markers.value = _markers;
  695. markersIndex.value = markersNewIndex;
  696. if (stationPage.value.hasNext && markersNewIndex >= _markers.length - 2) {
  697. stationPage.value.page += 1;
  698. refreshStation({
  699. latitude: mapProps.value.selflatitude,
  700. longitude: mapProps.value.selflongitude,
  701. });
  702. }
  703. };
  704. const tapMarker = (e: any) => {
  705. if (e.detail.markerId === -1) {
  706. return;
  707. }
  708. const findIndex = station.value.findIndex(
  709. (item) => Number(item.StationID) === Number(e.detail.markerId)
  710. );
  711. if (findIndex >= 0) {
  712. _changeMarker(findIndex);
  713. }
  714. };
  715. let startpageY = 0;
  716. const touchCardStart = (e: any) => {
  717. if (loading.value || station.value.length <= 0) {
  718. return;
  719. }
  720. if (e.touches && e.touches.length) {
  721. startpageY = e.touches[0].pageY;
  722. }
  723. };
  724. const touchCardMove = (e: any) => {
  725. if (!startpageY) {
  726. return;
  727. }
  728. const threshold = 200;
  729. if (e.touches && e.touches.length) {
  730. if (mapMode.value && startpageY - e.touches[0].pageY > threshold) {
  731. mapMode.value = false;
  732. }
  733. if (!mapMode.value && e.touches[0].pageY - startpageY > threshold) {
  734. mapMode.value = true;
  735. }
  736. }
  737. };
  738. const toCharging = () => {
  739. to(
  740. charging.value.chargeStatus === 0
  741. ? `/pages-charge/appointment/appointment?connectorId=${charging.value.connectorId}`
  742. : `/pages-charge/ordering/ordering?sn=${charging.value.connectorId}&start=1&connectorId=${charging.value.connectorId}`
  743. );
  744. };
  745. </script>
  746. <style lang="scss">
  747. page {
  748. background-color: #ffffff;
  749. }
  750. .container {
  751. position: relative;
  752. height: 100vh;
  753. width: 100vw;
  754. background-color: #ffffff;
  755. }
  756. .mock-map {
  757. position: relative;
  758. width: 100vw;
  759. background-color: #ffffff;
  760. overflow: hidden;
  761. }
  762. .dialog {
  763. position: fixed;
  764. top: 0;
  765. left: 0;
  766. width: 100%;
  767. z-index: 1000;
  768. &_logo {
  769. width: 156rpx;
  770. height: 100%;
  771. background-position: center;
  772. background-size: 100% auto;
  773. background-repeat: no-repeat;
  774. background-image: url("/static/images/map-logo.png");
  775. }
  776. &_search {
  777. width: 352rpx;
  778. height: 100%;
  779. background-position: center;
  780. background-size: 100% auto;
  781. background-repeat: no-repeat;
  782. background-image: url("/static/images/map-input.png");
  783. }
  784. &_event {
  785. margin-top: 20rpx;
  786. padding: 0rpx 32rpx;
  787. .swiper {
  788. height: 160rpx;
  789. width: 100%;
  790. border-radius: 16rpx;
  791. overflow: hidden;
  792. .swiper-item {
  793. border-radius: 16rpx;
  794. overflow: hidden;
  795. }
  796. view {
  797. background-position: center;
  798. background-repeat: no-repeat;
  799. background-size: 100% 100%;
  800. }
  801. }
  802. }
  803. &_selector {
  804. background-color: #f9f9f9;
  805. transition: all 0.3s;
  806. overflow: hidden;
  807. .type {
  808. width: 160rpx;
  809. height: 60rpx;
  810. background: var(--color-sec);
  811. border-radius: 4rpx;
  812. color: #666;
  813. font-size: 26rpx;
  814. border: 1px solid var(--color-sec);
  815. }
  816. .type-active {
  817. border: 1px solid var(--color-primary);
  818. color: var(--color-primary);
  819. }
  820. }
  821. /* .slider {
  822. position: relative;
  823. height: 12rpx;
  824. width: 100%;
  825. border-radius: 8rpx;
  826. background-color: var(--color-sec);
  827. margin-top: 10rpx;
  828. &_active {
  829. position: absolute;
  830. left: 0;
  831. top: 0;
  832. background-color: var(--color-primary);
  833. border-radius: 8rpx;
  834. height: 12rpx;
  835. width: 0%;
  836. }
  837. &_block {
  838. position: absolute;
  839. width: 40rpx;
  840. height: 40rpx;
  841. border-radius: 50%;
  842. top: -15rpx;
  843. left: 0;
  844. background: rgba(255, 255, 255, 1);
  845. box-shadow: 0px 4rpx 6rpx rgba(52, 125, 255, 0.4);
  846. }
  847. &_wx {
  848. position: absolute;
  849. width: 100%;
  850. left: 0px;
  851. top: -5px;
  852. margin: 0;
  853. opacity: 0;
  854. }
  855. } */
  856. }
  857. .card {
  858. position: fixed;
  859. width: 100%;
  860. left: 0px;
  861. z-index: 1000;
  862. transition: all 0.3s;
  863. box-shadow: 0px 8rpx 20rpx rgba(0, 0, 0, 0.2);
  864. background-color: #fff;
  865. &_location {
  866. position: absolute;
  867. border-radius: 16rpx;
  868. box-shadow: 0px 8rpx 20rpx 0px rgba(0, 0, 0, 0.2);
  869. right: 20rpx;
  870. top: -104rpx;
  871. z-index: 99;
  872. }
  873. &_line {
  874. height: 1rpx;
  875. background-color: rgba(0, 0, 0, 0.1);
  876. margin-left: 30rpx;
  877. margin-right: 30rpx;
  878. }
  879. &_empty {
  880. width: 100%;
  881. height: 100%;
  882. .image {
  883. width: 160rpx;
  884. }
  885. }
  886. .station-placeholder {
  887. min-height: 104rpx;
  888. }
  889. .station-iphonex {
  890. box-sizing: content-box;
  891. padding-bottom: constant(safe-area-inset-bottom);
  892. padding-bottom: env(safe-area-inset-bottom);
  893. }
  894. }
  895. .charging {
  896. position: fixed;
  897. z-index: 1000;
  898. right: 0;
  899. bottom: 164rpx;
  900. width: 186rpx;
  901. height: 88rpx;
  902. border-radius: 88rpx 0px 0px 88rpx;
  903. background: #2d9e95;
  904. margin-bottom: constant(safe-area-inset-bottom);
  905. margin-bottom: env(safe-area-inset-bottom);
  906. }
  907. </style>