map.vue 26 KB

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