map.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  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.stationLatDistance"
  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].stationLatDistance"
  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. ? "加载中"
  244. : token
  245. ? "暂无充电站信息"
  246. : "登录后查看更多电站信息"
  247. }}</view>
  248. </view>
  249. </block>
  250. </view>
  251. <view
  252. class="charging flex-align-center"
  253. v-if="charging"
  254. @click="toCharging"
  255. >
  256. <image
  257. class="width-64 ml-12"
  258. src="/static/images/map-charging.png"
  259. mode="widthFix"
  260. ></image>
  261. <view class="fs-26 color-fff ml-12">{{
  262. charging.chargeStatus === 0 ? "已预约" : "充电中"
  263. }}</view>
  264. </view>
  265. </view>
  266. <view class="login-mask" v-if="!token">
  267. <button open-type="getPhoneNumber" @getphonenumber="loginMask" class="full">
  268. 登录按钮
  269. </button>
  270. </view>
  271. </template>
  272. <script setup lang="ts">
  273. import {debounce, throttle} from "@/utils/util";
  274. const defaulDistance = 3;
  275. const defaultScale = 12;
  276. const pointSize = {
  277. width: 30,
  278. height: 52,
  279. fontSize: 7,
  280. iconPath: "/static/images/map-point.png",
  281. currentWidth: 42,
  282. currentHeight: 72,
  283. currentFontSize: 10,
  284. currentIconPath: "/static/images/map-point-current.png",
  285. androidX: -14,
  286. androidCurrentX: -50,
  287. };
  288. import { fetchHomeBanner } from "@/api";
  289. import { deCode } from "../../utils/code";
  290. import { rpxToPx } from "../../utils/device";
  291. import { fetchToken, login, onLogin } from "@/api/auth";
  292. import { fetchStations, fetchChargeStatus } from "@/api/charge";
  293. import { fetchCollectList } from "@/api/user";
  294. import { fetchLocation } from "@/utils/location";
  295. import { to } from "@/utils/navigate";
  296. import { onLoad, onShow } from "@dcloudio/uni-app";
  297. import { ref } from "vue";
  298. const currentLocation = ref(null)
  299. const isIOS = ref(false);
  300. const token = ref<string>();
  301. const ready = ref(false);
  302. const loading = ref(false);
  303. const styleData = ref({
  304. dialog: "",
  305. dialogHeight: 0,
  306. dialogPlaceHolderHeight: 0,
  307. cardHeight: 0,
  308. navHeight: 0,
  309. });
  310. const filterDialog = ref({
  311. discounts: false,
  312. distanceSelector: false,
  313. distanceRange: [
  314. {
  315. value: 1,
  316. scale: defaultScale + 3,
  317. },
  318. {
  319. value: 2,
  320. scale: defaultScale + 1,
  321. },
  322. {
  323. value: 3,
  324. scale: defaultScale,
  325. },
  326. {
  327. value: 5,
  328. scale: defaultScale - 0.2,
  329. },
  330. {
  331. value: 10,
  332. scale: defaultScale - 0.5,
  333. },
  334. {
  335. value: 20,
  336. scale: defaultScale - 1.5,
  337. },
  338. {
  339. value: 30,
  340. scale: defaultScale - 2,
  341. },
  342. {
  343. value: 50,
  344. scale: defaultScale - 2.5,
  345. },
  346. {
  347. value: 100,
  348. scale: defaultScale - 3.5,
  349. },
  350. {
  351. value: 200,
  352. scale: defaultScale - 4.5,
  353. },
  354. ],
  355. options: {
  356. distance: defaulDistance,
  357. status: 0,
  358. },
  359. status: [
  360. {
  361. title: "全部",
  362. },
  363. {
  364. title: "空闲",
  365. },
  366. {
  367. title: "忙碌",
  368. },
  369. ],
  370. });
  371. const stationPage = ref({
  372. page: 1,
  373. pageSize: 6,
  374. hasNext: false,
  375. });
  376. const station = ref<any[]>([]);
  377. const charging = ref<any>();
  378. const mapMode = ref(true);
  379. const mapProps = ref({
  380. latitude: 23.098994,
  381. longitude: 113.32252,
  382. selflatitude: 23.098994,
  383. selflongitude: 113.32252,
  384. scale: defaultScale,
  385. });
  386. const markersIndex = ref(-1);
  387. const markers = ref<any[]>([]);
  388. const mapBanner = ref<any[]>([]);
  389. let isIgnoreChangeLocation = false;
  390. const refreshStation = (location: any) => {
  391. let length = 0;
  392. let available = 0;
  393. const { latitude, longitude } = location;
  394. if (!token.value) {
  395. return;
  396. }
  397. return fetchStations(
  398. stationPage.value.page,
  399. stationPage.value.pageSize,
  400. latitude,
  401. longitude,
  402. mapProps.value.selflatitude,
  403. mapProps.value.selflongitude,
  404. {
  405. distance: filterDialog.value.options.distance,
  406. status: filterDialog.value.options.status,
  407. }
  408. ).then((res) => {
  409. const _markersIndex = stationPage.value.page === 1 ? 0 : markersIndex.value;
  410. // console.log("xxxxx",res)
  411. const _markers: any[] = res.map((item, index) => {
  412. item.totalFee =(Number(item.serviceFee)+Number(item.electricityFee)).toFixed(2)
  413. length = 0;
  414. available = 0;
  415. // item.equipmentInfos &&
  416. // item.equipmentInfos.forEach((eq: any) => {
  417. // eq.connectorInfos &&
  418. // eq.connectorInfos.forEach((co: any) => {
  419. // length += 1;
  420. // if (
  421. // co.connectorStatusInfo &&
  422. // co.connectorStatusInfo.status === 1
  423. // ) {
  424. // available += 1;
  425. // }
  426. // });
  427. // });
  428. return {
  429. id: Number(item.StationID),
  430. // (Number()+Number(item.electricityFee)||0).toFixed(2),
  431. latitude: item.location.stationLat,
  432. longitude: item.location.stationLng,
  433. iconPath:
  434. index === _markersIndex
  435. ? pointSize.currentIconPath
  436. : pointSize.iconPath,
  437. width:
  438. index === _markersIndex ? pointSize.currentWidth : pointSize.width,
  439. height:
  440. index === _markersIndex ? pointSize.currentHeight : pointSize.height,
  441. label: {
  442. content: `${item.availableParkingNum||0}/${item.parkingNum||0}`,
  443. color: "#ffffff",
  444. fontSize:
  445. index === _markersIndex
  446. ? pointSize.currentFontSize
  447. : pointSize.fontSize,
  448. textAlign: "center" ,
  449. // textAlign: isIOS.value ? "center" : "left",
  450. anchorX: isIOS.value
  451. ? 0
  452. : index === _markersIndex
  453. ? pointSize.androidCurrentX
  454. : pointSize.androidX,
  455. anchorY: -(
  456. (index === _markersIndex
  457. ? pointSize.currentHeight
  458. : pointSize.height) - 3
  459. ),
  460. },
  461. };
  462. });
  463. if (stationPage.value.page === 1) {
  464. _markers.push({
  465. id: -1,
  466. latitude: mapProps.value.selflatitude,
  467. longitude: mapProps.value.selflongitude,
  468. iconPath: "/static/images/map-current.png",
  469. width: 34,
  470. height: 34,
  471. });
  472. }
  473. isIgnoreChangeLocation = markers.value.length !== _markers.length;
  474. stationPage.value.hasNext = res.length >= stationPage.value.pageSize;
  475. // empty.value = stationPage.value.page === 1 ? res.length <= 0 : false;
  476. station.value =
  477. stationPage.value.page === 1 ? res : [...station.value, ...res];
  478. markersIndex.value = _markersIndex;
  479. markers.value = _markers;
  480. return res;
  481. });
  482. };
  483. const refresh = () => {
  484. if (loading.value) {
  485. return;
  486. }
  487. console.log("刷新电站");
  488. uni.showLoading({
  489. title: "加载中",
  490. });
  491. loading.value = true;
  492. stationPage.value.page = 1;
  493. stationPage.value.hasNext = false;
  494. station.value = [];
  495. markers.value = [];
  496. markersIndex.value = 0;
  497. fetchLocation()
  498. .then((res: any) => {
  499. mapProps.value.latitude = res.latitude;
  500. mapProps.value.longitude = res.longitude;
  501. mapProps.value.selflatitude = res.latitude;
  502. mapProps.value.selflongitude = res.longitude;
  503. isIgnoreChangeLocation = true;
  504. return refreshStation(res);
  505. })
  506. .then(() => {
  507. uni.hideLoading();
  508. loading.value = false;
  509. ready.value = true;
  510. })
  511. .catch((err) => {
  512. console.log(err);
  513. uni.hideLoading();
  514. loading.value = false;
  515. uni.showModal({
  516. content: `${err.errMsg},请重试`,
  517. });
  518. });
  519. };
  520. const handleNavReady = (e: any) => {
  521. console.log(e);
  522. styleData.value.dialog = `padding-top:${e.detail.statusBarHeight - 6}px;`;
  523. const searchHeight = rpxToPx(88);
  524. const filterHeight = 0; // rpxToPx(72);
  525. styleData.value.dialogHeight =
  526. e.detail.statusBarHeight - 6 + searchHeight + filterHeight;
  527. styleData.value.dialogPlaceHolderHeight =
  528. styleData.value.dialogHeight - e.detail.navigationBarHeight;
  529. styleData.value.cardHeight = rpxToPx(402) + e.detail.statusBarHeight;
  530. styleData.value.navHeight = e.detail.navigationBarHeight;
  531. };
  532. const toSearch = () => {
  533. if (!ready.value) {
  534. return;
  535. }
  536. uni.navigateTo({
  537. url: "/pages-charge/search/search",
  538. });
  539. };
  540. onLoad((query: any) => {
  541. // 只为了打包进tab-bar使用
  542. console.log(fetchToken, login, onLogin);
  543. // 扫普通码
  544. if (query.q) {
  545. console.log("扫普通码", decodeURIComponent(query.q));
  546. getApp<any>().globalData.normalCode = decodeURIComponent(query.q); // 获取到二维码原始链接内容
  547. }
  548. const device = uni.getSystemInfoSync();
  549. isIOS.value = device.osName === "ios";
  550. setTimeout(() => {
  551. token.value = getApp<any>().globalData.token || "";
  552. if (!token.value) {
  553. isIgnoreChangeLocation = true;
  554. fetchLocation().then((res: any) => {
  555. mapProps.value.latitude = res.latitude;
  556. mapProps.value.longitude = res.longitude;
  557. mapProps.value.selflatitude = res.latitude;
  558. mapProps.value.selflongitude = res.longitude;
  559. markers.value = [
  560. {
  561. id: -1,
  562. latitude: res.latitude,
  563. longitude: res.longitude,
  564. iconPath: "/static/images/map-current.png",
  565. width: 34,
  566. height: 34,
  567. },
  568. ];
  569. });
  570. onLogin((_token) => {
  571. if (getApp<any>().globalData.normalCode) {
  572. const code: string = getApp<any>().globalData.normalCode;
  573. getApp<any>().globalData.normalCode = "";
  574. deCode(code);
  575. }
  576. token.value = _token;
  577. fetchCollectList().then(() => {
  578. refresh();
  579. fetchCharging();
  580. fetchBanner();
  581. });
  582. });
  583. return;
  584. }
  585. fetchCollectList().then(() => {
  586. if (getApp<any>().globalData.normalCode) {
  587. const code: string = getApp<any>().globalData.normalCode;
  588. getApp<any>().globalData.normalCode = "";
  589. deCode(code);
  590. }
  591. refresh();
  592. fetchCharging();
  593. fetchBanner();
  594. });
  595. }, 300);
  596. });
  597. onShow(() => {
  598. if (token.value) {
  599. fetchBanner();
  600. charging.value = undefined;
  601. setTimeout(() => {
  602. fetchCharging();
  603. }, 2000);
  604. }
  605. });
  606. const fetchCharging = () => {
  607. fetchChargeStatus().then((res) => {
  608. if (res && [0, 1, 2, 3].includes(res.chargeStatus)) {
  609. charging.value = res;
  610. }
  611. });
  612. };
  613. const fetchBanner = () => {
  614. fetchHomeBanner().then((res) => {
  615. mapBanner.value = res.filter((item: any) => item.status === 1);
  616. });
  617. };
  618. // const checkDiscounts = () => {
  619. // filterDialog.value.discounts = !filterDialog.value.discounts;
  620. // };
  621. const checkFilterDistance = () => {
  622. if (isIgnoreChangeLocation) {
  623. return;
  624. }
  625. if (!mapMode.value) {
  626. mapMode.value = true;
  627. }
  628. filterDialog.value.distanceSelector = !filterDialog.value.distanceSelector;
  629. };
  630. const changeFilterDistance = (index: number) => {
  631. filterDialog.value.options.distance =
  632. filterDialog.value.distanceRange[index].value;
  633. if (mapProps.value.scale === filterDialog.value.distanceRange[index].scale) {
  634. refresh();
  635. return;
  636. }
  637. mapProps.value.scale = filterDialog.value.distanceRange[index].scale;
  638. checkFilterDistance();
  639. };
  640. const resetLocation = () => {
  641. if (loading.value) {
  642. return;
  643. }
  644. // eslint-disable-next-line promise/catch-or-return
  645. fetchLocation().then((res: any) => {
  646. const mapCtx = uni.createMapContext("map");
  647. const { latitude, longitude } = res;
  648. mapCtx.moveToLocation({
  649. latitude,
  650. longitude,
  651. });
  652. mapProps.value.scale = defaultScale;
  653. });
  654. };
  655. const mapUpdated = (e: any) => {
  656. // console.log('map updated', isIgnoreChangeLocation)
  657. setTimeout(() => {
  658. isIgnoreChangeLocation = false;
  659. }, 500);
  660. };
  661. const mapChange = (e: any) => {
  662. if (isIgnoreChangeLocation) {
  663. return;
  664. }
  665. if (e.type === "end" && markers.value.length) {
  666. //TODO 防抖500ms触发一次
  667. console.log("throttle")
  668. currentLocation.value = e.target.centerLocation
  669. // throttle(()=>{
  670. //
  671. // },500)
  672. // console.log("map change end", {
  673. // ...e.detail.centerLocation,
  674. // });
  675. debounceRefreshStation()
  676. }
  677. };
  678. const debounceRefreshStation = debounce(() => {
  679. const { latitude, longitude } = currentLocation.value;
  680. stationPage.value.page = 1;
  681. console.log("debounceRefreshStation run...")
  682. refreshStation({
  683. latitude,
  684. longitude,
  685. });
  686. }, 500)
  687. const _changeMarker = (current: number) => {
  688. const _markers = JSON.parse(JSON.stringify(markers.value));
  689. const markersNewIndex = current;
  690. _markers[markersIndex.value].iconPath = pointSize.iconPath;
  691. _markers[markersIndex.value].width = pointSize.width;
  692. _markers[markersIndex.value].height = pointSize.height;
  693. _markers[markersIndex.value].label.fontSize = pointSize.fontSize;
  694. _markers[markersIndex.value].label.anchorY = -(pointSize.height - 3);
  695. if (!isIOS) {
  696. _markers[markersIndex.value].label.anchorX = pointSize.androidX;
  697. }
  698. _markers[markersNewIndex].iconPath = pointSize.currentIconPath;
  699. _markers[markersNewIndex].width = pointSize.currentWidth;
  700. _markers[markersNewIndex].height = pointSize.currentHeight;
  701. _markers[markersNewIndex].label.fontSize = pointSize.currentFontSize;
  702. _markers[markersNewIndex].label.anchorY = -(pointSize.currentHeight - 3);
  703. if (!isIOS) {
  704. _markers[markersNewIndex].label.anchorX = pointSize.androidCurrentX;
  705. }
  706. isIgnoreChangeLocation = true;
  707. markers.value = _markers;
  708. markersIndex.value = markersNewIndex;
  709. if (stationPage.value.hasNext && markersNewIndex >= _markers.length - 2) {
  710. stationPage.value.page += 1;
  711. refreshStation({
  712. latitude: mapProps.value.selflatitude,
  713. longitude: mapProps.value.selflongitude,
  714. });
  715. }
  716. };
  717. const tapMarker = (e: any) => {
  718. if (e.detail.markerId === -1) {
  719. return;
  720. }
  721. const findIndex = station.value.findIndex(
  722. (item) => Number(item.StationID) === Number(e.detail.markerId)
  723. );
  724. if (findIndex >= 0) {
  725. _changeMarker(findIndex);
  726. }
  727. };
  728. const loginMask = (e: any) => {
  729. login(e);
  730. };
  731. let startpageY = 0;
  732. const touchCardStart = (e: any) => {
  733. if (loading.value || station.value.length <= 0) {
  734. return;
  735. }
  736. if (e.touches && e.touches.length) {
  737. startpageY = e.touches[0].pageY;
  738. }
  739. };
  740. const touchCardMove = (e: any) => {
  741. if (!startpageY) {
  742. return;
  743. }
  744. const threshold = 200;
  745. if (e.touches && e.touches.length) {
  746. if (mapMode.value && startpageY - e.touches[0].pageY > threshold) {
  747. mapMode.value = false;
  748. }
  749. if (!mapMode.value && e.touches[0].pageY - startpageY > threshold) {
  750. mapMode.value = true;
  751. }
  752. }
  753. };
  754. const toCharging = () => {
  755. to(
  756. charging.value.chargeStatus === 0
  757. ? `/pages-charge/appointment/appointment?sn=${charging.value.connectorId}`
  758. : `/pages-charge/ordering/ordering?sn=${charging.value.connectorId}&start=1`
  759. );
  760. };
  761. </script>
  762. <style lang="scss">
  763. page {
  764. background-color: #ffffff;
  765. }
  766. .container {
  767. position: relative;
  768. height: 100vh;
  769. width: 100vw;
  770. background-color: #ffffff;
  771. }
  772. .mock-map {
  773. position: relative;
  774. width: 100vw;
  775. background-color: #ffffff;
  776. overflow: hidden;
  777. }
  778. .login-mask {
  779. position: fixed;
  780. left: 0;
  781. top: 0;
  782. width: 100%;
  783. height: 100%;
  784. z-index: 999999;
  785. opacity: 0;
  786. .full {
  787. width: 100%;
  788. height: 100%;
  789. }
  790. }
  791. .dialog {
  792. position: fixed;
  793. top: 0;
  794. left: 0;
  795. width: 100%;
  796. z-index: 1000;
  797. &_logo {
  798. width: 156rpx;
  799. height: 100%;
  800. background-position: center;
  801. background-size: 100% auto;
  802. background-repeat: no-repeat;
  803. background-image: url("/static/images/map-logo.png");
  804. }
  805. &_search {
  806. width: 352rpx;
  807. height: 100%;
  808. background-position: center;
  809. background-size: 100% auto;
  810. background-repeat: no-repeat;
  811. background-image: url("/static/images/map-input.png");
  812. }
  813. &_event {
  814. margin-top: 20rpx;
  815. padding: 0rpx 32rpx;
  816. .swiper {
  817. height: 160rpx;
  818. width: 100%;
  819. border-radius: 16rpx;
  820. overflow: hidden;
  821. .swiper-item {
  822. border-radius: 16rpx;
  823. overflow: hidden;
  824. }
  825. view {
  826. background-position: center;
  827. background-repeat: no-repeat;
  828. background-size: 100% 100%;
  829. }
  830. }
  831. }
  832. &_selector {
  833. background-color: #f9f9f9;
  834. transition: all 0.3s;
  835. overflow: hidden;
  836. .type {
  837. width: 160rpx;
  838. height: 60rpx;
  839. background: var(--color-sec);
  840. border-radius: 4rpx;
  841. color: #666;
  842. font-size: 26rpx;
  843. border: 1px solid var(--color-sec);
  844. }
  845. .type-active {
  846. border: 1px solid var(--color-primary);
  847. color: var(--color-primary);
  848. }
  849. }
  850. /* .slider {
  851. position: relative;
  852. height: 12rpx;
  853. width: 100%;
  854. border-radius: 8rpx;
  855. background-color: var(--color-sec);
  856. margin-top: 10rpx;
  857. &_active {
  858. position: absolute;
  859. left: 0;
  860. top: 0;
  861. background-color: var(--color-primary);
  862. border-radius: 8rpx;
  863. height: 12rpx;
  864. width: 0%;
  865. }
  866. &_block {
  867. position: absolute;
  868. width: 40rpx;
  869. height: 40rpx;
  870. border-radius: 50%;
  871. top: -15rpx;
  872. left: 0;
  873. background: rgba(255, 255, 255, 1);
  874. box-shadow: 0px 4rpx 6rpx rgba(52, 125, 255, 0.4);
  875. }
  876. &_wx {
  877. position: absolute;
  878. width: 100%;
  879. left: 0px;
  880. top: -5px;
  881. margin: 0;
  882. opacity: 0;
  883. }
  884. } */
  885. }
  886. .card {
  887. position: fixed;
  888. width: 100%;
  889. left: 0px;
  890. z-index: 1000;
  891. transition: all 0.3s;
  892. box-shadow: 0px 8rpx 20rpx rgba(0, 0, 0, 0.2);
  893. background-color: #fff;
  894. &_location {
  895. position: absolute;
  896. border-radius: 16rpx;
  897. box-shadow: 0px 8rpx 20rpx 0px rgba(0, 0, 0, 0.2);
  898. right: 20rpx;
  899. top: -104rpx;
  900. z-index: 99;
  901. }
  902. &_line {
  903. height: 1rpx;
  904. background-color: rgba(0, 0, 0, 0.1);
  905. margin-left: 30rpx;
  906. margin-right: 30rpx;
  907. }
  908. &_empty {
  909. width: 100%;
  910. height: 100%;
  911. .image {
  912. width: 160rpx;
  913. }
  914. }
  915. .station-placeholder {
  916. min-height: 104rpx;
  917. }
  918. .station-iphonex {
  919. box-sizing: content-box;
  920. padding-bottom: constant(safe-area-inset-bottom);
  921. padding-bottom: env(safe-area-inset-bottom);
  922. }
  923. }
  924. .charging {
  925. position: fixed;
  926. z-index: 1000;
  927. right: 0;
  928. bottom: 164rpx;
  929. width: 186rpx;
  930. height: 88rpx;
  931. border-radius: 88rpx 0px 0px 88rpx;
  932. background: #2d9e95;
  933. margin-bottom: constant(safe-area-inset-bottom);
  934. margin-bottom: env(safe-area-inset-bottom);
  935. }
  936. </style>