| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828 |
- <template>
- <view class="container">
- <navigation-bar @ready="handleNavReady"></navigation-bar>
- <view class="dialog" v-if="styleData.dialog" :style="styleData.dialog">
- <view class="height-88 flex-align-center bg-fff">
- <image
- mode="widthFix"
- src="/static/images/map-logo.png"
- style="width: 156rpx"
- class="ml-24"
- ></image>
- <image
- mode="widthFix"
- src="/static/images/map-input.png"
- class="ml-24"
- style="width: 352rpx"
- @click="toSearch"
- ></image>
- </view>
- <view class="height-72 flex" style="background-color: #f9f9f9">
- <view class="width-half flex-center" @click.stop="checkFilterDistance">
- <view class="fs-26 color-000-6 mr-8">距离</view>
- <view
- class="width-20 height-20 br-round bg-000-08 lh-10 text-center transition"
- :style="{
- transform: filterDialog.distanceSelector ? 'rotate(180deg)' : '',
- }"
- >
- <uni-icons
- type="bottom"
- size="8"
- color="rgba(0,0,0,0.4)"
- ></uni-icons>
- </view>
- </view>
- <view class="width-half flex-center" @click.stop="checkDiscounts">
- <view
- class="width-32 height-32 br-round lh-20 text-center"
- :style="{
- border: filterDialog.discounts
- ? '1px solid var(--color-primary)'
- : '1px solid rgba(0, 0, 0, 0.4)',
- backgroundColor: filterDialog.discounts
- ? 'var(--color-primary)'
- : '#fff',
- }"
- >
- <uni-icons
- type="checkmarkempty"
- size="10"
- color="#ffffff"
- ></uni-icons>
- </view>
- <view
- :class="[
- 'fs-26',
- 'ml-8',
- filterDialog.discounts ? 'color-primary' : 'color-000-6',
- ]"
- >支持省钱充电</view
- >
- </view>
- </view>
- <view
- class="dialog_selector"
- :style="{
- height: filterDialog.distanceSelector ? '190rpx' : '0rpx',
- borderTop: `1rpx solid rgba(0, 0, 0, ${
- filterDialog.distanceSelector ? '0.1' : '0'
- })`,
- }"
- >
- <view class="flex-center flex-wrap pb-14">
- <view
- :class="[
- 'type',
- 'flex-shrink',
- 'flex-center',
- 'mt-20',
- (index + 1) % 5 === 0 ? 'mr-0' : 'mr-20',
- `type-${
- item.value === filterDialog.options.distance ? 'active' : ''
- }`,
- ]"
- v-for="(item, index) in filterDialog.distanceRange"
- :key="index"
- @click.stop="changeFilterDistance(index)"
- style="width: 117rpx"
- >{{ item.value }}km</view
- >
- </view>
- </view>
- <!-- <view class="dialog_event" v-if="ready && !filterDialog.distanceSelector">
- // TODO 接入活动接口
- <swiper
- class="swiper"
- circular
- :indicator-dots="true"
- :autoplay="true"
- :interval="3000"
- >
- <swiper-item class="full-percent">
- <view class="full-percent bg-666"></view>
- </swiper-item>
- <swiper-item class="full-percent">
- <view class="full-percent bg-fff"></view>
- </swiper-item>
- </swiper>
- </view> -->
- </view>
- <view
- v-if="styleData.dialogPlaceHolderHeight"
- :style="{ height: `${styleData.dialogPlaceHolderHeight}px` }"
- ></view>
- <map
- id="map"
- :style="{
- width: '100%',
- height: `calc(100vh - ${styleData.dialogHeight}px - ${
- styleData.cardHeight - 24
- }px)`,
- zIndex: 1,
- pointerEvents: loading || !mapMode ? 'none' : 'auto',
- }"
- :latitude="mapProps.latitude"
- :longitude="mapProps.longitude"
- :markers="markers"
- min-scale="1"
- :scale="mapProps.scale"
- @regionchange="mapChange"
- @updated="mapUpdated"
- @labeltap="tapMarker"
- @markertap="tapMarker"
- :show-scale="true"
- ></map>
- <view
- class="card"
- :style="{
- height: mapMode
- ? `${styleData.cardHeight}px`
- : `calc(100vh - ${styleData.dialogHeight}px)`,
- bottom: '0px',
- borderRadius: mapMode ? '16rpx 16rpx 0px 0px' : '0px',
- }"
- @touchstart.stop="touchCardStart"
- @touchmove.stop="touchCardMove"
- >
- <view
- v-if="mapMode"
- class="card_location height-64 width-64 bg-fff flex-center"
- @click="resetLocation"
- >
- <image
- src="/static/images/map-re-location.png"
- mode="widthFix"
- class="width-48"
- ></image>
- </view>
- <block v-if="station.length">
- <!-- 列表 -->
- <block v-if="!mapMode">
- <view class="station" v-for="(item, index) in station" :key="index">
- <view v-if="index !== 0" class="card_line"></view>
- <charge-station
- :title="item.stationName"
- :address="item.address"
- :price="item.totalFee"
- :fast="item.fastEquipmentInfos"
- :slow="item.slowEquipmentInfos"
- :sId="item.StationID"
- :distance="item.stationLatDistance"
- :latitude="item.location.stationLat"
- :longitude="item.location.stationLng"
- :fromMap="true"
- ></charge-station>
- </view>
- </block>
- <block v-else>
- <!-- 单个 -->
- <view class="station">
- <charge-station
- :title="station[markersIndex].stationName"
- :address="station[markersIndex].address"
- :price="station[markersIndex].totalFee"
- :fast="station[markersIndex].fastEquipmentInfos"
- :slow="station[markersIndex].slowEquipmentInfos"
- :sId="station[markersIndex].StationID"
- :distance="station[markersIndex].stationLatDistance"
- :latitude="station[markersIndex].location.stationLat"
- :longitude="station[markersIndex].location.stationLng"
- :fromMap="true"
- ></charge-station>
- </view>
- </block>
- </block>
- <block v-else>
- <view class="card_empty flex-column flex-align-center pt-20">
- <view v-if="loading" class="mt-60 animation-loading"
- ><uni-icons
- type="spinner-cycle"
- size="20"
- color="rgba(0, 0, 0, 0.5)"
- ></uni-icons
- ></view>
- <image
- class="image"
- v-else
- src="/static/images/map-empty.png"
- mode="widthFix"
- />
- <view class="fs-22 mt-14 color-000-5">{{
- loading ? "加载中" : "暂无充电站信息"
- }}</view>
- </view>
- </block>
- </view>
- </view>
- <view class="login-mask" v-if="!token">
- <button open-type="getPhoneNumber" @getphonenumber="loginMask" class="full">
- 登录按钮
- </button>
- </view>
- </template>
- <script setup lang="ts">
- const defaulDistance = 3;
- const defaultScale = 12;
- const pointSize = {
- width: 34,
- height: 58,
- fontSize: 10,
- iconPath: "/static/images/map-point.png",
- currentWidth: 52,
- currentHeight: 86,
- currentFontSize: 11,
- currentIconPath: "/static/images/map-point-current.png",
- androidX: -14,
- androidCurrentX: -20,
- };
- import { deCode } from "../../utils/code";
- import { rpxToPx } from "../../utils/device";
- import { fetchToken, login, onLogin } from "@/api/auth";
- import { fetchStations } from "@/api/charge";
- import { fetchCollectList } from "@/api/user";
- import { fetchLocation } from "@/utils/location";
- import { onLoad } from "@dcloudio/uni-app";
- import { ref } from "vue";
- // TODO 支持省钱充电
- // TODO 充电中
- const isIOS = ref(false);
- const token = ref<string>();
- const ready = ref(false);
- const loading = ref(false);
- const styleData = ref({
- dialog: "",
- dialogHeight: 0,
- dialogPlaceHolderHeight: 0,
- cardHeight: 0,
- });
- const filterDialog = ref({
- discounts: false,
- distanceSelector: false,
- distanceRange: [
- {
- value: 1,
- scale: defaultScale + 3,
- },
- {
- value: 2,
- scale: defaultScale + 1,
- },
- {
- value: 3,
- scale: defaultScale,
- },
- {
- value: 5,
- scale: defaultScale - 0.2,
- },
- {
- value: 10,
- scale: defaultScale - 0.5,
- },
- {
- value: 20,
- scale: defaultScale - 1.5,
- },
- {
- value: 30,
- scale: defaultScale - 2,
- },
- {
- value: 50,
- scale: defaultScale - 2.5,
- },
- {
- value: 100,
- scale: defaultScale - 3.5,
- },
- {
- value: 200,
- scale: defaultScale - 4.5,
- },
- ],
- options: {
- distance: defaulDistance,
- status: 0,
- },
- status: [
- {
- title: "全部",
- },
- {
- title: "空闲",
- },
- {
- title: "忙碌",
- },
- ],
- });
- const stationPage = ref({
- page: 1,
- pageSize: 6,
- hasNext: false,
- });
- const station = ref<any[]>([]);
- const mapMode = ref(true);
- const mapProps = ref({
- latitude: 23.098994,
- longitude: 113.32252,
- selflatitude: 23.098994,
- selflongitude: 113.32252,
- scale: defaultScale,
- });
- const markersIndex = ref(-1);
- const markers = ref<any[]>([]);
- let isIgnoreChangeLocation = false;
- const refreshStation = (location: any) => {
- let length = 0;
- let available = 0;
- const { latitude, longitude } = location;
- if (!token.value) {
- return;
- }
- return fetchStations(
- stationPage.value.page,
- stationPage.value.pageSize,
- latitude,
- longitude,
- mapProps.value.selflatitude,
- mapProps.value.selflongitude,
- {
- distance: filterDialog.value.options.distance,
- status: filterDialog.value.options.status,
- }
- ).then((res) => {
- const _markersIndex = stationPage.value.page === 1 ? 0 : markersIndex.value;
- const _markers: any[] = res.map((item, index) => {
- length = 0;
- available = 0;
- item.equipmentInfos &&
- item.equipmentInfos.forEach((eq: any) => {
- eq.connectorInfos &&
- eq.connectorInfos.forEach((co: any) => {
- length += 1;
- if (
- co.connectorStatusInfo &&
- co.connectorStatusInfo.status === 1
- ) {
- available += 1;
- }
- });
- });
- return {
- id: Number(item.StationID),
- latitude: item.location.stationLat,
- longitude: item.location.stationLng,
- iconPath:
- index === _markersIndex
- ? pointSize.currentIconPath
- : pointSize.iconPath,
- width:
- index === _markersIndex ? pointSize.currentWidth : pointSize.width,
- height:
- index === _markersIndex ? pointSize.currentHeight : pointSize.height,
- label: {
- content: `${available}/${length}`,
- color: "#ffffff",
- fontSize:
- index === _markersIndex
- ? pointSize.currentFontSize
- : pointSize.fontSize,
- textAlign: isIOS.value ? "center" : "left",
- anchorX: isIOS.value
- ? 0
- : index === _markersIndex
- ? pointSize.androidCurrentX
- : pointSize.androidX,
- anchorY: -(
- (index === _markersIndex
- ? pointSize.currentHeight
- : pointSize.height) - 3
- ),
- },
- };
- });
- if (stationPage.value.page === 1) {
- _markers.push({
- id: -1,
- latitude: mapProps.value.selflatitude,
- longitude: mapProps.value.selflongitude,
- iconPath: "/static/images/map-current.png",
- width: 34,
- height: 34,
- });
- }
- isIgnoreChangeLocation = markers.value.length !== _markers.length;
- stationPage.value.hasNext = res.length >= stationPage.value.pageSize;
- // empty.value = stationPage.value.page === 1 ? res.length <= 0 : false;
- station.value =
- stationPage.value.page === 1 ? res : [...station.value, ...res];
- markersIndex.value = _markersIndex;
- markers.value = _markers;
- return res;
- });
- };
- const refresh = () => {
- if (loading.value) {
- return;
- }
- console.log("刷新电站");
- loading.value = true;
- stationPage.value.page = 1;
- stationPage.value.hasNext = false;
- station.value = [];
- markers.value = [];
- markersIndex.value = 0;
- fetchLocation()
- .then((res: any) => {
- mapProps.value.latitude = res.latitude;
- mapProps.value.longitude = res.longitude;
- mapProps.value.selflatitude = res.latitude;
- mapProps.value.selflongitude = res.longitude;
- isIgnoreChangeLocation = true;
- return refreshStation(res);
- })
- .then(() => {
- loading.value = false;
- ready.value = true;
- })
- .catch((err) => {
- console.log(err);
- loading.value = false;
- uni.showModal({
- content: `${err.errMsg},请重试`,
- });
- });
- };
- const handleNavReady = (e: any) => {
- styleData.value.dialog = `padding-top:${e.detail.statusBarHeight - 6}px;`;
- const searchHeight = rpxToPx(88);
- const filterHeight = rpxToPx(72);
- styleData.value.dialogHeight =
- e.detail.statusBarHeight - 6 + searchHeight + filterHeight;
- styleData.value.dialogPlaceHolderHeight =
- styleData.value.dialogHeight - e.detail.navigationBarHeight;
- styleData.value.cardHeight = rpxToPx(410);
- };
- const toSearch = () => {
- uni.navigateTo({
- url: "/pages-charge/search/search",
- });
- };
- onLoad((query: any) => {
- // 只为了打包进tab-bar使用
- console.log(fetchToken, login, onLogin);
- // 扫普通码
- if (query.q) {
- console.log("扫普通码", decodeURIComponent(query.q));
- getApp<any>().globalData.normalCode = decodeURIComponent(query.q); // 获取到二维码原始链接内容
- }
- const device = uni.getSystemInfoSync();
- isIOS.value = device.osName === "ios";
- setTimeout(() => {
- token.value = getApp<any>().globalData.token || "";
- if (!token.value) {
- isIgnoreChangeLocation = true;
- fetchLocation().then((res: any) => {
- mapProps.value.latitude = res.latitude;
- mapProps.value.longitude = res.longitude;
- mapProps.value.selflatitude = res.latitude;
- mapProps.value.selflongitude = res.longitude;
- markers.value = [
- {
- id: -1,
- latitude: res.latitude,
- longitude: res.longitude,
- iconPath: "/static/images/map-current.png",
- width: 34,
- height: 34,
- },
- ];
- });
- onLogin((_token) => {
- if (getApp<any>().globalData.normalCode) {
- const code: string = getApp<any>().globalData.normalCode;
- getApp<any>().globalData.normalCode = "";
- deCode(code);
- }
- token.value = _token;
- fetchCollectList().then(() => {
- refresh();
- });
- });
- return;
- }
- fetchCollectList().then(() => {
- if (getApp<any>().globalData.normalCode) {
- const code: string = getApp<any>().globalData.normalCode;
- getApp<any>().globalData.normalCode = "";
- deCode(code);
- }
- refresh();
- });
- }, 300);
- });
- const checkDiscounts = () => {
- filterDialog.value.discounts = !filterDialog.value.discounts;
- };
- const checkFilterDistance = () => {
- if (!mapMode.value) {
- mapMode.value = true
- }
- filterDialog.value.distanceSelector = !filterDialog.value.distanceSelector;
- };
- const changeFilterDistance = (index: number) => {
- filterDialog.value.options.distance =
- filterDialog.value.distanceRange[index].value;
- if (mapProps.value.scale === filterDialog.value.distanceRange[index].scale) {
- refresh();
- return;
- }
- mapProps.value.scale = filterDialog.value.distanceRange[index].scale;
- checkFilterDistance();
- };
- const resetLocation = () => {
- if (loading.value) {
- return;
- }
- // eslint-disable-next-line promise/catch-or-return
- fetchLocation().then((res: any) => {
- const mapCtx = uni.createMapContext("map");
- const { latitude, longitude } = res;
- mapCtx.moveToLocation({
- latitude,
- longitude,
- });
- mapProps.value.scale = defaultScale;
- });
- };
- const mapUpdated = (e: any) => {
- // console.log('map updated', isIgnoreChangeLocation)
- setTimeout(() => {
- isIgnoreChangeLocation = false;
- }, 500);
- };
- const mapChange = (e: any) => {
- if (isIgnoreChangeLocation) {
- return;
- }
- if (e.type === "end" && markers.value.length) {
- console.log("map change end", {
- ...e.detail.centerLocation,
- });
- const current = e.target.centerLocation;
- const { latitude, longitude } = current;
- stationPage.value.page = 1;
- refreshStation({
- latitude,
- longitude,
- });
- }
- };
- const _changeMarker = (current: number) => {
- const _markers = JSON.parse(JSON.stringify(markers.value));
- const markersNewIndex = current;
- _markers[markersIndex.value].iconPath = pointSize.iconPath;
- _markers[markersIndex.value].width = pointSize.width;
- _markers[markersIndex.value].height = pointSize.height;
- _markers[markersIndex.value].label.fontSize = pointSize.fontSize;
- _markers[markersIndex.value].label.anchorY = -(pointSize.height - 3);
- if (!isIOS) {
- _markers[markersIndex.value].label.anchorX = pointSize.androidX;
- }
- _markers[markersNewIndex].iconPath = pointSize.currentIconPath;
- _markers[markersNewIndex].width = pointSize.currentWidth;
- _markers[markersNewIndex].height = pointSize.currentHeight;
- _markers[markersNewIndex].label.fontSize = pointSize.currentFontSize;
- _markers[markersNewIndex].label.anchorY = -(pointSize.currentHeight - 3);
- if (!isIOS) {
- _markers[markersNewIndex].label.anchorX = pointSize.androidCurrentX;
- }
- isIgnoreChangeLocation = true;
- markers.value = _markers;
- markersIndex.value = markersNewIndex;
- if (stationPage.value.hasNext && markersNewIndex >= _markers.length - 2) {
- stationPage.value.page += 1;
- refreshStation({
- latitude: mapProps.value.selflatitude,
- longitude: mapProps.value.selflongitude,
- });
- }
- };
- const tapMarker = (e: any) => {
- if (e.detail.markerId === -1) {
- return;
- }
- const findIndex = station.value.findIndex(
- (item) => Number(item.StationID) === Number(e.detail.markerId)
- );
- if (findIndex >= 0) {
- _changeMarker(findIndex);
- }
- };
- const loginMask = (e: any) => {
- login(e);
- };
- let startpageY = 0;
- const touchCardStart = (e: any) => {
- if (loading.value || station.value.length <= 0) {
- return;
- }
- if (e.touches && e.touches.length) {
- startpageY = e.touches[0].pageY;
- }
- };
- const touchCardMove = (e: any) => {
- if (!startpageY) {
- return;
- }
- const threshold = 150;
- if (e.touches && e.touches.length) {
- if (mapMode.value && startpageY - e.touches[0].pageY > threshold) {
- mapMode.value = false;
- }
- if (!mapMode.value && e.touches[0].pageY - startpageY > threshold) {
- mapMode.value = true;
- }
- }
- };
- // TORM
- // const changeMarker = (e: any) => {
- // _changeMarker(e.detail.current);
- // };
- // const emptyTap = () => {};
- // const changeFilterStatus = (index: number) => {
- // filterDialog.value.options.status = index;
- // };
- // const resetFilter = () => {
- // filterDialog.value.options.distance = defaulDistance;
- // filterDialog.value.options.status = 0;
- // refresh();
- // };
- </script>
- <style lang="scss">
- page {
- background-color: #ffffff;
- }
- .container {
- position: relative;
- height: 100vh;
- width: 100vw;
- background-color: #ffffff;
- }
- .login-mask {
- position: fixed;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- z-index: 999999;
- opacity: 0;
- .full {
- width: 100%;
- height: 100%;
- }
- }
- .dialog {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- z-index: 999;
- &_event {
- margin-top: 20rpx;
- padding: 0rpx 32rpx;
- .swiper {
- height: 176rpx;
- width: 100%;
- border-radius: 16rpx;
- overflow: hidden;
- }
- }
- &_selector {
- background-color: #f9f9f9;
- transition: all 0.3s;
- overflow: hidden;
- .type {
- width: 160rpx;
- height: 60rpx;
- background: var(--color-sec);
- border-radius: 4rpx;
- color: #666;
- font-size: 26rpx;
- border: 1px solid var(--color-sec);
- }
- .type-active {
- border: 1px solid var(--color-primary);
- color: var(--color-primary);
- }
- }
- /* .slider {
- position: relative;
- height: 12rpx;
- width: 100%;
- border-radius: 8rpx;
- background-color: var(--color-sec);
- margin-top: 10rpx;
- &_active {
- position: absolute;
- left: 0;
- top: 0;
- background-color: var(--color-primary);
- border-radius: 8rpx;
- height: 12rpx;
- width: 0%;
- }
- &_block {
- position: absolute;
- width: 40rpx;
- height: 40rpx;
- border-radius: 50%;
- top: -15rpx;
- left: 0;
- background: rgba(255, 255, 255, 1);
- box-shadow: 0px 4rpx 6rpx rgba(52, 125, 255, 0.4);
- }
- &_wx {
- position: absolute;
- width: 100%;
- left: 0px;
- top: -5px;
- margin: 0;
- opacity: 0;
- }
- } */
- }
- .card {
- position: fixed;
- width: 100%;
- left: 0px;
- z-index: 999;
- transition: all 0.3s;
- box-shadow: 0px 8rpx 20rpx rgba(0, 0, 0, 0.2);
- background-color: #fff;
- margin-bottom: constant(safe-area-inset-bottom);
- margin-bottom: env(safe-area-inset-bottom);
- overflow-y: auto;
- &_location {
- position: absolute;
- border-radius: 16rpx;
- box-shadow: 0px 8rpx 20rpx 0px rgba(0, 0, 0, 0.2);
- right: 20rpx;
- top: -104rpx;
- }
- &_line {
- height: 1rpx;
- background-color: rgba(0, 0, 0, 0.1);
- margin-left: 30rpx;
- margin-right: 30rpx;
- }
- &_empty {
- width: 100%;
- height: 100%;
- .image {
- width: 160rpx;
- }
- }
- }
- </style>
|