zuy 1 anno fa
parent
commit
43b8f43d24

+ 23 - 0
src/api/user.ts

@@ -163,3 +163,26 @@ export function listRefund(param: any) {
     },
   });
 }
+
+
+/**
+ * 获取站点可以领取的优惠券
+ * @param stationId
+ */
+export function listAvailableCoupons(stationId: any) {
+  return userHttp.get(`/coupon/listAvailableCoupons?stationId=${stationId}`, {
+  });
+}
+
+
+/**
+ * 领取优惠券
+ * @param couponId
+ */
+export function collectCoupon (couponId: number) {
+  return userHttp.get(`/coupon/collectCoupon?couponId=${couponId}`, {
+  });
+}
+
+
+

+ 139 - 42
src/components/charge-station/charge-station.vue

@@ -1,31 +1,45 @@
 <template>
   <view
-    class="charge-station"
-    :style="{
+      class="charge-station"
+      :style="{
       border: border ? '1rpx solid rgba(0, 0, 0, 0.1)' : 'none',
       'border-radius': fromMap ? '20rpx 20rpx 0 0' : '20rpx',
     }"
-    @touchstart="touchstart"
-    @touchend="touchend"
-    data-method="detail"
+      @touchstart="touchstart"
+      @touchend="touchend"
+      data-method="detail"
   >
     <view class="flex-between">
       <view>
-        <view class="fs-32 fw-bold">{{ title }}</view>
+        <view class="flex flex-align-center">
+          <view class="fs-32 fw-bold">{{ title }}</view>
+          <view class="coupon-box ml-5"
+                v-if="activityList&&activityList.length>0"
+                @touchstart="touchstart"
+                @touchend.stop="touchend"
+                data-method="toCouponCenter"
+          >
+            <!--            <image src="/static/images/coupon-center.png" mode="widthFix" style="width:42rpx"/>-->
+            <text>领券</text>
+          </view>
+        </view>
+
+
         <view v-if="tag">
           <view class="flex-center tag">{{ tagMap[tag] }}</view>
         </view>
         <view v-else class="fs-22" style="color: rgba(0, 0, 0, 0.5)"
-          >{{ address }} | {{ distance }}km</view
+        >{{ address }} | {{ distance }}km
+        </view
         >
       </view>
       <view
-        class="nav flex-center flex-shrink"
-        @touchstart="touchstart"
-        @touchend.stop="touchend"
-        data-method="nav"
+          class="nav flex-center flex-shrink"
+          @touchstart="touchstart"
+          @touchend.stop="touchend"
+          data-method="nav"
       >
-        <image src="/static/images/icon-nav.png" mode="widthFix" />
+        <image src="/static/images/icon-nav.png" mode="widthFix"/>
         <text>导航</text>
       </view>
     </view>
@@ -33,63 +47,70 @@
       <view class="status flex-column flex-grow">
         <view class="flex-align-center" v-if="price">
           <text class="fs-32 fw-600" style="color: var(--color-warning)">{{
-            price
-          }}</text>
+              price
+            }}
+          </text>
           <text class="fs-22 ml-6 color-666">元/度</text>
         </view>
         <view class="flex-align-center lh-0 mt-auto" v-if="!fromMap">
           <charge-icon type="fast"></charge-icon>
           <view class="fs-22 ml-8 color-666" v-if="fast"
-            >空闲:{{ freeLength(fast) }}|共:{{ fast.length }}</view
+          >空闲:{{ freeLength(fast) }}|共:{{ fast.length }}
+          </view
           >
           <view class="ml-60">
             <charge-icon type="slow"></charge-icon>
           </view>
           <view class="fs-22 ml-8 color-666" v-if="slow"
-            >空闲:{{ freeLength(slow) }}|共:{{ slow.length }}</view
+          >空闲:{{ freeLength(slow) }}|共:{{ slow.length }}
+          </view
           >
         </view>
         <block v-else>
           <view class="lh-0 mt-18 fs-22" style="color: #999"
-            >以场地方公告为准!</view
+          >以场地方公告为准!
+          </view
           >
           <view
-            class="top-right-bound flex-align-center lh-32"
-            v-if="slow && slow.length"
+              class="top-right-bound flex-align-center lh-32"
+              v-if="slow && slow.length"
           >
             <charge-icon type="slow"></charge-icon>
             <view class="ml-6 fs-22 color-666"
-              >{{ freeLength(slow) }} / {{ slow.length }}</view
+            >{{ freeLength(slow) }} / {{ slow.length }}
+            </view
             >
           </view>
           <view
-            class="top-right-bound flex-align-center lh-32"
-            v-if="fast && fast.length"
+              class="top-right-bound flex-align-center lh-32"
+              v-if="fast && fast.length"
           >
             <charge-icon type="fast"></charge-icon>
             <view class="ml-6 fs-22 color-666"
-              >{{ freeLength(fast) }} / {{ fast.length }}</view
+            >{{ freeLength(fast) }} / {{ fast.length }}
+            </view
             >
           </view>
         </block>
       </view>
       <view
-        class="btn flex-shrink flex-column flex-center"
-        @touchstart="touchstart"
-        @touchend.stop="touchend"
-        data-method="collect"
+          class="btn flex-shrink flex-column flex-center"
+          @touchstart="touchstart"
+          @touchend.stop="touchend"
+          data-method="collect"
       >
         <uni-icons
-          :type="collected ? 'star-filled' : 'star'"
-          size="20"
-          :color="collected ? 'var(--color-warning)' : '#999999'"
+            :type="collected ? 'star-filled' : 'star'"
+            size="20"
+            :color="collected ? 'var(--color-warning)' : '#999999'"
         ></uni-icons>
         <view
-          class="fs-22 mt-6"
-          :style="{
+            class="fs-22 mt-6"
+            :style="{
             color: collected ? 'var(--color-warning)' : '#999999',
           }"
-          >收藏</view
+        >收藏
+        </view
         >
       </view>
     </view>
@@ -97,7 +118,8 @@
 </template>
 
 <script lang="ts">
-import { fetchCollectList, addCollectList } from "../../api/user";
+import {addCollectList, fetchCollectList} from "../../api/user";
+
 export default {
   props: {
     sId: {
@@ -114,6 +136,7 @@ export default {
     distance: String,
     border: Boolean,
     fromMap: Boolean,
+    activityList: Array
   },
   watch: {
     sId: {
@@ -148,12 +171,25 @@ export default {
     };
   },
   methods: {
+    toCouponCenter() {
+      const {address, latitude, longitude, activityList} = this;
+      getApp<any>().globalData.lastData.station = {
+        address,
+        latitude,
+        longitude,
+        activityList
+      };
+      // uni.navigateTo({
+      //   url: `/pages-charge/coupon-center/coupon-center?title=${this.title}&id=${this.sId}`,
+      // });
+    },
     detail() {
-      const { address, latitude, longitude } = this;
+      const {address, latitude, longitude, activityList} = this;
       getApp<any>().globalData.lastData.station = {
         address,
         latitude,
         longitude,
+        activityList
       };
       uni.navigateTo({
         url: `/pages-charge/machines/machines?title=${this.title}&id=${this.sId}`,
@@ -162,15 +198,15 @@ export default {
     collect() {
       addCollectList(Number(this.sId)).then((collected) => {
         collected &&
-          uni.vibrateShort &&
-          uni.vibrateShort({
-            type: "light",
-          });
+        uni.vibrateShort &&
+        uni.vibrateShort({
+          type: "light",
+        });
         this.collected = collected;
       });
     },
     nav() {
-      const { latitude, longitude, title, address } = this;
+      const {latitude, longitude, title, address} = this;
       uni.openLocation({
         latitude,
         longitude,
@@ -184,8 +220,8 @@ export default {
       infos.forEach(function (item) {
         item.connectorInfos.forEach(function (conItem: any) {
           if (
-            conItem.connectorStatusInfo &&
-            conItem.connectorStatusInfo.status === 1
+              conItem.connectorStatusInfo &&
+              conItem.connectorStatusInfo.status === 1
           ) {
             length += 1;
           }
@@ -263,4 +299,65 @@ export default {
     transition: all 0.3s;
   }
 }
+
+.coupon-box {
+  display: inline-flex;
+  justify-content: space-between;
+  align-items: center;
+  color: #fff;
+  background-color: #dd524d;
+  font-size: 18rpx;
+  border-radius: 4rpx;
+  padding: 0rpx 6rpx;
+
+  /*  background: linear-gradient(
+            -135deg,
+            transparent 12rpx,
+            #fff 22rpx,
+            #dd524d 100%
+    )
+    top right,
+    linear-gradient(
+            -45deg,
+            transparent 12rpx,
+            #fff 22rpx,
+            #dd524d 100%
+    )
+    bottom right;
+    background-size: 100% 100%;
+    background-repeat: no-repeat;*/
+
+  /*flex-shrink: 0;
+  background-size: 70% 70%;
+  background-image: radial-gradient(
+          circle at 100% 100%,
+          transparent 0,
+          transparent 12px,
+          #2179f5 13px
+  ),
+  radial-gradient(
+          circle at 0 0,
+          transparent 0,
+          transparent 12px,
+          #2179f5 13px
+  ),
+  radial-gradient(
+          circle at 100% 0,
+          transparent 0,
+          transparent 12px,
+          #2179f5 13px
+  ),
+  radial-gradient(
+          circle at 0 100%,
+          transparent 0,
+          transparent 12px,
+          #2179f5 13px
+  );
+  background-repeat: no-repeat;
+  background-position: right bottom, left top, right top, left bottom;
+*/
+  text {
+    font-size: 24rpx;
+  }
+}
 </style>

+ 221 - 0
src/pages-charge/coupon-center/coupon-center.vue

@@ -0,0 +1,221 @@
+<template>
+  <style-dialog
+      v-if="visible"
+      @close="handleClose"
+      title="">
+
+    <view class="page pt-20 pr-40 pl-40">
+      <view class="tab-list flex-align-end">
+        <view class="fs-28 color-666 mt-20 w50" :class="mode===0?'active':''" @click="changeTab(0)">权益卡</view>
+        <view class="fs-28 color-666 mt-20 w50" :class="mode===1?'active':''" @click="changeTab(1)">优惠券</view>
+      </view>
+      <view>
+        <view
+            class="item mt-20 "
+            v-for="(item, index) in list"
+            :class="item.status==0?'inactive':''"
+            :key="index"
+        >
+          <image class="full-percent" src="/pages-charge/static/discount-bg.png"></image>
+          <view class="absolute-full flex pt-8 pb-8">
+            <view class="left  flex-justify-center pl-48">
+
+              <view class="item-content_left">
+                <view class="flex-align-end">
+                  <view class="item-header__left">
+                    <image class="width-84" src="/pages-charge/static/discount-rights-card.png" mode="widthFix"></image>
+                    <view class="fs-48 lh-58 color-000 fw-500 ml-16">{{ item.discountAmount }}折
+                    </view>
+                    <view class="fs-26 color-999 ml-16" v-if="mode === 0"
+                    >用于充电服务费抵扣
+                    </view>
+                  </view>
+
+                  <!--                <view class="item-header__right flex-grow">
+                                    <view class="item-status ml-10">{{ item.status == 0 ? '已失效' : '生效中' }}</view>
+                                  </view>-->
+                </view>
+
+
+                <block>
+                  <view class="fs-26 color-999 mt-8"
+                  >
+                    <view class="fs-26 color-999 mt-24">到期时间:{{ item.endTime }}</view>
+                    <!--                  <view class="flex-align-center" v-if="mode === 0">
+                                        <view class="fs-26 color-999 mt-24">权益余额:{{ ((item.rightsBalance || 0) / 100).toFixed(2) }}元
+                                          <text class="color-primary">详细规则</text>
+                                          <text class="fs-24 ml-4 color-primary">>></text>
+                                        </view>
+                                      </view>-->
+                  </view>
+                </block>
+              </view>
+              <view class="item-content_right">
+                <view class="item-status ml-10" @click.stop="handleCollectCoupon(item)">领券</view>
+              </view>
+
+            </view>
+          </view>
+        </view>
+      </view>
+
+
+    </view>
+    >
+  </style-dialog>
+
+</template>
+
+<script setup lang="ts">
+import {onPullDownRefresh} from "@dcloudio/uni-app";
+import {ref} from "vue";
+import {collectCoupon, listAvailableCoupons} from "@/api/user";
+
+const mode = ref(0);
+const list = ref<any[]>([]);
+const couponList = ref<any[]>([]);
+const stationId = ref(-1);
+const visible = ref(false);
+
+const handleClose = () => {
+
+}
+
+onPullDownRefresh(() => {
+  loadData();
+});
+
+const changeTab = (tab: number) => {
+  mode.value = tab;
+}
+
+const handleCollectCoupon = (coupon: any) => {
+  collectCoupon(coupon.id).then((res) => {
+    console.log(res)
+    uni.showToast({
+      title: "领券成功",
+      icon: "success"
+    });
+  });
+}
+
+
+const loadData = () => {
+  uni.showLoading({
+    title: "加载中",
+  });
+  listAvailableCoupons(stationId.value).then((res) => {
+    console.log(res)
+    uni.hideLoading();
+    couponList.value = res.list;
+  });
+}
+const open = (stationId: any) => {
+  visible.value = true;
+  stationId.value = stationId;
+  loadData()
+}
+
+
+defineExpose({
+  open
+})
+</script>
+
+<style lang="scss" scoped>
+.page {
+  min-height: 100vh;
+  width: 100%;
+  background-color: #f6f7fa;
+
+  .item {
+    position: relative;
+    min-height: 220rpx;
+    border-radius: 20rpx;
+    overflow: hidden;
+    background: #fff;
+
+    .left {
+      flex-grow: 1;
+    }
+
+    .right {
+      flex-shrink: 0;
+
+      .btn {
+        border-radius: 8rpx;
+        border: 1px solid var(--color-primary);
+        color: var(--color-primary);
+        width: 144rpx;
+        height: 56rpx;
+      }
+    }
+  }
+
+  .tab-list {
+    width: 100%;
+
+    .w50 {
+      width: 50%;
+      text-align: center;
+    }
+
+    .active {
+      color: var(--color-primary);
+    }
+  }
+}
+
+.item-status {
+  font-size: 24rpx;
+  padding: 0rpx 2rpx;
+  color: var(--color-primary);
+  border: 2rpx solid var(--color-primary);
+  border-radius: 6rpx;
+
+}
+
+.item-header__left {
+  display: inline-flex;
+  align-items: flex-end;
+}
+
+.item-header__right {
+  padding: 5rpx;
+  display: flex;
+  //justify-content: flex-end;
+}
+
+.inactive {
+  cursor: not-allowed;
+  pointer-events: none;
+
+  .item-status {
+    font-size: 24rpx;
+    padding: 0rpx 2rpx;
+    color: #ccc !important;
+    border: 2rpx solid #ccc;
+    border-radius: 6rpx;
+
+  }
+
+  .color-primary {
+    color: #ccc !important;
+  }
+}
+
+.item-content_left {
+  flex-grow: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-around;
+
+}
+
+.item-content_right {
+  width: 120rpx;
+  display: flex;
+  align-items: center;
+  align-content: center;
+}
+</style>

+ 164 - 117
src/pages-charge/machines/machines.vue

@@ -1,52 +1,62 @@
 <template>
   <!--  #ifdef MP-WEIXIN -->
-  <page-meta page-style="overflow: {{y ? 'hidden' : 'visible'}}" />
+  <page-meta page-style="overflow: {{y ? 'hidden' : 'visible'}}"/>
   <!--  #endif -->
   <view :class="['pb-40']" v-if="station">
     <view class="banner">
       <swiper
-        class="full-percent"
-        circular
-        :indicator-dots="true"
-        :autoplay="true"
-        :interval="3000"
+          class="full-percent"
+          circular
+          :indicator-dots="true"
+          :autoplay="true"
+          :interval="3000"
       >
         <swiper-item
-          class="full-percent"
-          v-for="(item, index) in station.bannerImages"
-          :key="index"
+            class="full-percent"
+            v-for="(item, index) in station.bannerImages"
+            :key="index"
         >
           <view
-            class="full-percent banner-image"
-            :style="{
+              class="full-percent banner-image"
+              :style="{
               backgroundImage: `url(${item})`,
             }"
-            @click="previewImage(item)"
+              @click="previewImage(item)"
           ></view>
         </swiper-item>
       </swiper>
     </view>
     <view class="pl-20 pr-20">
       <view class="station flex-column" v-if="canUseCount >= 0">
-        <view class="fs-40 fw-600 color-000">{{ title }}</view>
+        <view style="display: inline-flex;align-items: center;">
+          <view class="fs-40 fw-600 color-000">{{ title }}</view>
+          <view class="coupon-box ml-5"
+                v-if="location.activityList&&location.activityList.length>0"
+                @tap.stop="toCouponCenter"
+          >
+            <image src="/static/images/coupon-center.png" mode="widthFix" style="width:42rpx"/>
+            <text>领取优惠</text>
+          </view>
+        </view>
+
         <view class="flex-align-center mt-10 height-48 relative flex-shrink">
           <image
-            src="/pages-charge/static/machines-banner-address.png"
-            mode="widthFix"
-            style="width: 16px; display: block; flex-shrink: 0"
+              src="/pages-charge/static/machines-banner-address.png"
+              mode="widthFix"
+              style="width: 16px; display: block; flex-shrink: 0"
           />
           <view class="ml-12 fs-26 color-666">{{ location.address }}</view>
           <view class="flex-center ml-auto nav" @click="openAddress">
             <image
-              src="/pages-charge/static/machines-banner-nav.png"
-              mode="widthFix"
+                src="/pages-charge/static/machines-banner-nav.png"
+                mode="widthFix"
             />
             <view class="fs-26" style="color: #347dff">导航</view>
           </view>
         </view>
         <view
-          class="foot mt-30 flex-align-center flex-shrink"
-          @click="openDesc"
+            class="foot mt-30 flex-align-center flex-shrink"
+            @click="openDesc"
         >
           <view class="fs-28 color-666">可用充电桩:</view>
           <view class="fs-36 fw-500 color-primary">{{ canUseCount }}</view>
@@ -61,23 +71,24 @@
           <view class="fs-26 color-000">{{ canUsePrice }}元/度</view>
           <view class="ml-auto lh-0">
             <uni-icons
-              type="right"
-              size="18"
-              color="rgba(0,0,0,0.4)"
+                type="right"
+                size="18"
+                color="rgba(0,0,0,0.4)"
             ></uni-icons>
           </view>
         </view>
         <view class="flex-align-center">
           <view class="width-168 fs-26 color-666">停车费用</view>
           <view class="fs-26 color-000">{{
-            Number(station.parkFee) > 0
-              ? station.parkFee
-              : "以停车场公示信息为准"
-          }}</view>
+              Number(station.parkFee) > 0
+                  ? station.parkFee
+                  : "以停车场公示信息为准"
+            }}
+          </view>
         </view>
         <view
-          class="flex-align-center"
-          v-if="
+            class="flex-align-center"
+            v-if="
             station &&
             station.equipmentInfos &&
             station.equipmentInfos.length &&
@@ -86,25 +97,28 @@
         >
           <view class="width-168 fs-26 color-666">充电桩类型</view>
           <view class="fs-26 color-000"
-            >{{
+          >{{
               station.equipmentInfos[0].connectorInfos[0].connectorType === 1
-                ? "直流"
-                : "交流"
-            }}{{ station.equipmentInfos[0].connectorInfos[0].power }}kw</view
+                  ? "直流"
+                  : "交流"
+            }}{{ station.equipmentInfos[0].connectorInfos[0].power }}kw
+          </view
           >
         </view>
       </view>
       <view class="pt-40 flex-align-center">
         <view class="fs-32 fw-500 color-999">充电桩</view>
         <view class="fs-26 color-999" v-if="station"
-          >(共{{ totalCount }}个)</view
+        >(共{{ totalCount }}个)
+        </view
         >
         <view class="ml-auto flex-align-center" @click="openStatus">
           <view class="fs-28 color-333 mr-16">{{
-            statusList[status].title
-          }}</view>
+              statusList[status].title
+            }}
+          </view>
           <view
-            style="
+              style="
               width: 0;
               height: 0;
               border: 8rpx solid;
@@ -115,31 +129,32 @@
         </view>
       </view>
       <template
-        v-if="station"
-        v-for="(item, index) in stationEquipmentInfos"
-        :key="index"
+          v-if="station"
+          v-for="(item, index) in stationEquipmentInfos"
+          :key="index"
       >
         <ChargeMachine
-          :title="'NO.' + item.shortId"
-          :price="station.totalFee"
-          :list="item.connectorInfos"
-          :parkingNo="item.parkingNo"
+            :title="'NO.' + item.shortId"
+            :price="station.totalFee"
+            :list="item.connectorInfos"
+            :parkingNo="item.parkingNo"
         ></ChargeMachine>
       </template>
     </view>
     <PriceDesc
-      v-if="dialogVisible && dialogType === 'desc'"
-      :desc="desc"
-      @close="closeDialog"
+        v-if="dialogVisible && dialogType === 'desc'"
+        :desc="desc"
+        @close="closeDialog"
     ></PriceDesc>
     <view
-      class="dialog flex-align-end"
-      v-if="dialogVisible && dialogType === 'status'"
+        class="dialog flex-align-end"
+        v-if="dialogVisible && dialogType === 'status'"
     >
       <view class="status-dialog">
         <view class="status-dialog_head flex-center">
           <view class="fw-500 color-000" style="font-size: 16px"
-            >选择充电桩状态</view
+          >选择充电桩状态
+          </view
           >
           <view class="close" @click="closeDialog">
             <uni-icons type="closeempty" size="24" color="#2D284B"></uni-icons>
@@ -148,11 +163,11 @@
         <view class="status-dialog_body">
           <view class="status">
             <view
-              :class="['fs-32', 'flex-align-center']"
-              :style="{ color: status === index ? '#347DFF' : '#2d284b' }"
-              v-for="(statusItem, index) in statusList"
-              :key="index"
-              @click="changeStatus(index)"
+                :class="['fs-32', 'flex-align-center']"
+                :style="{ color: status === index ? '#347DFF' : '#2d284b' }"
+                v-for="(statusItem, index) in statusList"
+                :key="index"
+                @click="changeStatus(index)"
             >
               {{ statusItem.title }}
             </view>
@@ -164,11 +179,12 @@
 </template>
 
 <script setup lang="ts">
-import { ref } from "vue";
-import { fetchStation, fetchStationPriceDesc } from "../../api/charge";
-import { onLoad } from "@dcloudio/uni-app";
+import {ref} from "vue";
+import {fetchStation, fetchStationPriceDesc} from "../../api/charge";
+import {onLoad} from "@dcloudio/uni-app";
 import ChargeMachine from "./charge-machine/charge-machine.vue";
 import PriceDesc from "./price-desc/price-desc.vue";
+
 const dialogVisible = ref(false);
 const dialogType = ref("");
 const desc = ref<any[]>([]);
@@ -194,6 +210,7 @@ const location = ref({
   address: "",
   latitude: "",
   longitude: "",
+  activityList: Array
 });
 
 const station = ref();
@@ -207,12 +224,13 @@ onLoad((options: any) => {
   });
   title.value = _title;
   if (getApp<any>().globalData.lastData.station) {
-    const { address, latitude, longitude } =
-      getApp<any>().globalData.lastData.station;
+    const {address, latitude, longitude, activityList} =
+        getApp<any>().globalData.lastData.station;
     location.value = {
       address,
       latitude,
       longitude,
+      activityList
     };
     getApp<any>().globalData.lastData.station = undefined;
   }
@@ -221,59 +239,59 @@ onLoad((options: any) => {
     mask: true,
   });
   fetchStation(Number(options.id))
-    .then((res) => {
-      console.log(res);
-      let ConnectorID = "";
-      if (res.equipmentInfos && res.equipmentInfos.length) {
-        res.equipmentInfos.forEach((item: any) => {
-          if (item.connectorInfos && item.connectorInfos.length) {
-            item.connectorInfos.forEach((con: any) => {
-              totalCount.value++;
-              if (!ConnectorID) {
-                ConnectorID = con.connectorId;
-              }
-              if (
-                con.connectorStatusInfo &&
-                con.connectorStatusInfo.status === 1
-              ) {
-                canUseCount.value++;
-              }
-            });
-          }
+      .then((res) => {
+        console.log(res);
+        let ConnectorID = "";
+        if (res.equipmentInfos && res.equipmentInfos.length) {
+          res.equipmentInfos.forEach((item: any) => {
+            if (item.connectorInfos && item.connectorInfos.length) {
+              item.connectorInfos.forEach((con: any) => {
+                totalCount.value++;
+                if (!ConnectorID) {
+                  ConnectorID = con.connectorId;
+                }
+                if (
+                    con.connectorStatusInfo &&
+                    con.connectorStatusInfo.status === 1
+                ) {
+                  canUseCount.value++;
+                }
+              });
+            }
+          });
+        }
+        res.bannerImages = res.pictures.split(",");
+        station.value = res;
+        stationEquipmentInfos.value = res.equipmentInfos.map((item: any) => {
+          return {
+            ...item,
+          };
         });
-      }
-      res.bannerImages = res.pictures.split(",");
-      station.value = res;
-      stationEquipmentInfos.value = res.equipmentInfos.map((item: any) => {
-        return {
-          ...item,
-        };
-      });
-      if (ConnectorID) {
-        return fetchStationPriceDesc(ConnectorID);
-      } else {
-        // eslint-disable-next-line promise/no-return-wrap
-        return Promise.resolve({
-          policyInfos: [],
+        if (ConnectorID) {
+          return fetchStationPriceDesc(ConnectorID);
+        } else {
+          // eslint-disable-next-line promise/no-return-wrap
+          return Promise.resolve({
+            policyInfos: [],
+          });
+        }
+      })
+      .then((res) => {
+        uni.hideLoading();
+        currentTime.value = res.currentTime;
+        canUseTime.value = res.useTime;
+        canUsePrice.value = `${res.minPrice}~${res.maxPrice}`;
+        desc.value = res.policyInfos || [];
+      })
+      .catch((err) => {
+        // eslint-disable-next-line no-console
+        console.log(err);
+        uni.hideLoading();
+        uni.showToast({
+          title: "加载失败,请重试",
+          icon: "none",
         });
-      }
-    })
-    .then((res) => {
-      uni.hideLoading();
-      currentTime.value = res.currentTime;
-      canUseTime.value = res.useTime;
-      canUsePrice.value = `${res.minPrice}~${res.maxPrice}`;
-      desc.value = res.policyInfos || [];
-    })
-    .catch((err) => {
-      // eslint-disable-next-line no-console
-      console.log(err);
-      uni.hideLoading();
-      uni.showToast({
-        title: "加载失败,请重试",
-        icon: "none",
       });
-    });
 });
 const openAddress = function () {
   uni.openLocation({
@@ -303,11 +321,11 @@ const changeStatus = function (index: number) {
   status.value = index;
   if (index === 0) {
     stationEquipmentInfos.value = station.value.equipmentInfos.map(
-      (item: any) => {
-        return {
-          ...item,
-        };
-      }
+        (item: any) => {
+          return {
+            ...item,
+          };
+        }
     );
     closeDialog();
     return;
@@ -319,8 +337,8 @@ const changeStatus = function (index: number) {
     if (item.connectorInfos && item.connectorInfos.length) {
       item.connectorInfos.forEach((con: any) => {
         if (
-          con.connectorStatusInfo &&
-          Number(con.connectorStatusInfo.status) === STATUS_MAP[status.value]
+            con.connectorStatusInfo &&
+            Number(con.connectorStatusInfo.status) === STATUS_MAP[status.value]
         ) {
           check = true;
         }
@@ -339,10 +357,18 @@ const previewImage = (url: string) => {
     current: url,
   });
 };
+
+const toCouponCenter = () => {
+  // uni.navigateTo({
+  //   url: `/pages-charge/coupon-center/coupon-center?title=${station.value.title}&id=${station.value.StationID}`,
+  // });
+
+}
 </script>
 
 <style lang="scss">
 @import "../../styles/dialog.scss";
+
 page {
   background-color: #f5f5f5;
 }
@@ -350,6 +376,7 @@ page {
 .banner {
   height: 440rpx;
   background-color: transparent;
+
   .banner-image {
     background-position: center;
     background-size: cover;
@@ -373,6 +400,7 @@ page {
     height: 48rpx;
     border-radius: 40rpx;
     background: rgba(52, 125, 255, 0.1);
+
     image {
       width: 36rpx;
     }
@@ -389,9 +417,11 @@ page {
   padding: 0rpx 30rpx;
   border-radius: 20rpx;
   background-color: #fff;
+
   & > view {
     height: 90rpx;
     border-top: 1px solid rgba(0, 0, 0, 0.1);
+
     &:first-child {
       border-top: none;
     }
@@ -408,6 +438,7 @@ page {
     padding: 24rpx 0px;
     position: relative;
     background-color: #fff;
+
     .close {
       position: absolute;
       right: 30rpx;
@@ -427,6 +458,7 @@ page {
       & > view {
         height: 92rpx;
         border-top: 1px solid rgba(0, 0, 0, 0.1);
+
         &:first-child {
           border-top: none;
         }
@@ -434,4 +466,19 @@ page {
     }
   }
 }
+
+.coupon-box {
+  display: inline-flex;
+  justify-content: space-between;
+  align-items: center;
+  color: #dd524d;
+  font-size: 24rpx;
+  border-radius: 20rpx;
+  border: 3rpx solid #dd524d;
+  padding: 2rpx 6rpx;
+
+  text {
+    font-size: 24rpx;
+  }
+}
 </style>

+ 7 - 0
src/pages.json

@@ -92,6 +92,13 @@
             "navigationBarTitleText": "我的卡包",
             "enablePullDownRefresh": true
           }
+        },
+        {
+          "path": "coupon-center/coupon-center",
+          "style": {
+            "navigationStyle": "default",
+            "navigationBarTitleText": "领券中心"
+          }
         }
       ]
     },

+ 963 - 961
src/pages/map/map.vue

@@ -1,961 +1,963 @@
-<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">
-        <view class="dialog_logo ml-24"></view>
-        <view class="dialog_search ml-24" @click.stop="toSearch"></view>
-      </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">
-          <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 && mapBanner.length > 0">
-        <swiper
-          class="swiper"
-          circular
-          :indicator-dots="true"
-          :autoplay="mapBanner.length > 1"
-          :interval="3000"
-        >
-          <swiper-item
-            class="swiper-item full-percent"
-            v-for="(item, index) in mapBanner"
-            :key="index"
-            @click="to(item.linkUrl)"
-          >
-            <view
-              class="full-percent"
-              :style="{
-                backgroundImage: `url(${item.bannerUrl})`,
-              }"
-            ></view>
-          </swiper-item>
-        </swiper>
-      </view>
-    </view>
-    <view
-      v-if="styleData.dialogPlaceHolderHeight"
-      :style="{ height: `${styleData.dialogPlaceHolderHeight}px` }"
-    ></view>
-    <map
-      v-if="ready"
-      id="map"
-      :style="{
-        width: '100%',
-        height: styleData.dialogHeight
-          ? `calc(100vh - ${styleData.dialogHeight}px - ${
-              styleData.cardHeight - 24
-            }px)`
-          : '50vh',
-        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>
-    <block v-else>
-      <view
-        v-if="styleData.dialogHeight"
-        class="mock-map flex-center"
-        :style="{
-          height: `calc(100vh - ${styleData.dialogHeight}px - ${
-            styleData.cardHeight - 24
-          }px)`,
-        }"
-      >
-        <image
-          src="/static/images/map-bg.jpg"
-          mode="widthFix"
-          style="width: 100%"
-        ></image>
-        <image
-          src="/static/images/map-current.png"
-          mode="widthFix"
-          class="absolute-center"
-          style="width: 34px"
-        ></image>
-      </view>
-    </block>
-    <view
-      class="card"
-      :style="{
-        height: mapMode
-          ? `${styleData.cardHeight}px`
-          : `calc(100vh - ${styleData.navHeight}px)`,
-        bottom: '0px',
-        borderRadius: mapMode ? '16rpx 16rpx 0px 0px' : '0px',
-        overflowY: mapMode ? 'visible' : 'auto',
-      }"
-      @touchstart="touchCardStart"
-      @touchmove="touchCardMove"
-    >
-      <view
-        v-if="mapMode"
-        class="card_location height-64 width-64 bg-fff flex-center"
-        @touchstart.stop="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>
-          <view class="station-placeholder"></view>
-          <view class="station-iphonex"></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
-              ? "加载中"
-              : token
-              ? "暂无充电站信息"
-              : "登录后查看更多电站信息"
-          }}</view>
-        </view>
-      </block>
-    </view>
-    <view
-      class="charging flex-align-center"
-      v-if="charging"
-      @click="toCharging"
-    >
-      <image
-        class="width-64 ml-12"
-        src="/static/images/map-charging.png"
-        mode="widthFix"
-      ></image>
-      <view class="fs-26 color-fff ml-12">{{
-        charging.chargeStatus === 0 ? "已预约" : "充电中"
-      }}</view>
-    </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 { fetchHomeBanner } from "@/api";
-import { deCode } from "../../utils/code";
-import { rpxToPx } from "../../utils/device";
-import { fetchToken, login, onLogin } from "@/api/auth";
-import { fetchStations, fetchChargeStatus } from "@/api/charge";
-import { fetchCollectList } from "@/api/user";
-import { fetchLocation } from "@/utils/location";
-import { to } from "@/utils/navigate";
-import { onLoad, onShow } from "@dcloudio/uni-app";
-import { ref } from "vue";
-
-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,
-  navHeight: 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 charging = 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[]>([]);
-const mapBanner = 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("刷新电站");
-  uni.showLoading({
-    title: "加载中",
-  });
-  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(() => {
-      uni.hideLoading();
-      loading.value = false;
-      ready.value = true;
-    })
-    .catch((err) => {
-      console.log(err);
-      uni.hideLoading();
-      loading.value = false;
-      uni.showModal({
-        content: `${err.errMsg},请重试`,
-      });
-    });
-};
-
-const handleNavReady = (e: any) => {
-  console.log(e);
-  styleData.value.dialog = `padding-top:${e.detail.statusBarHeight - 6}px;`;
-  const searchHeight = rpxToPx(88);
-  const filterHeight = 0; // rpxToPx(72);
-  styleData.value.dialogHeight =
-    e.detail.statusBarHeight - 6 + searchHeight + filterHeight;
-  styleData.value.dialogPlaceHolderHeight =
-    styleData.value.dialogHeight - e.detail.navigationBarHeight;
-  styleData.value.cardHeight = rpxToPx(402) + e.detail.statusBarHeight;
-  styleData.value.navHeight = e.detail.navigationBarHeight;
-};
-
-const toSearch = () => {
-  if (!ready.value) {
-    return;
-  }
-  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();
-          fetchCharging();
-          fetchBanner();
-        });
-      });
-      return;
-    }
-    fetchCollectList().then(() => {
-      if (getApp<any>().globalData.normalCode) {
-        const code: string = getApp<any>().globalData.normalCode;
-        getApp<any>().globalData.normalCode = "";
-        deCode(code);
-      }
-      refresh();
-      fetchCharging();
-      fetchBanner();
-    });
-  }, 300);
-});
-
-onShow(() => {
-  if (token.value) {
-    fetchBanner();
-    charging.value = undefined;
-    setTimeout(() => {
-      fetchCharging();
-    }, 2000);
-  }
-});
-
-const fetchCharging = () => {
-  fetchChargeStatus().then((res) => {
-    if (res && [0, 1, 2, 3].includes(res.chargeStatus)) {
-      charging.value = res;
-    }
-  });
-};
-
-const fetchBanner = () => {
-  fetchHomeBanner().then((res) => {
-    mapBanner.value = res.filter((item: any) => item.status === 1);
-  });
-};
-
-// const checkDiscounts = () => {
-//   filterDialog.value.discounts = !filterDialog.value.discounts;
-// };
-
-const checkFilterDistance = () => {
-  if (isIgnoreChangeLocation) {
-    return;
-  }
-  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 = 200;
-  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;
-    }
-  }
-};
-
-const toCharging = () => {
-  to(
-    charging.value.chargeStatus === 0
-      ? `/pages-charge/appointment/appointment?sn=${charging.value.connectorId}`
-      : `/pages-charge/ordering/ordering?sn=${charging.value.connectorId}&start=1`
-  );
-};
-</script>
-
-<style lang="scss">
-page {
-  background-color: #ffffff;
-}
-
-.container {
-  position: relative;
-  height: 100vh;
-  width: 100vw;
-  background-color: #ffffff;
-}
-
-.mock-map {
-  position: relative;
-  width: 100vw;
-  background-color: #ffffff;
-  overflow: hidden;
-}
-
-.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: 1000;
-
-  &_logo {
-    width: 156rpx;
-    height: 100%;
-    background-position: center;
-    background-size: 100% auto;
-    background-repeat: no-repeat;
-    background-image: url("/static/images/map-logo.png");
-  }
-
-  &_search {
-    width: 352rpx;
-    height: 100%;
-    background-position: center;
-    background-size: 100% auto;
-    background-repeat: no-repeat;
-    background-image: url("/static/images/map-input.png");
-  }
-
-  &_event {
-    margin-top: 20rpx;
-    padding: 0rpx 32rpx;
-    .swiper {
-      height: 160rpx;
-      width: 100%;
-      border-radius: 16rpx;
-      overflow: hidden;
-      .swiper-item {
-        border-radius: 16rpx;
-        overflow: hidden;
-      }
-      view {
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: 100% 100%;
-      }
-    }
-  }
-
-  &_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: 1000;
-  transition: all 0.3s;
-  box-shadow: 0px 8rpx 20rpx rgba(0, 0, 0, 0.2);
-  background-color: #fff;
-
-  &_location {
-    position: absolute;
-    border-radius: 16rpx;
-    box-shadow: 0px 8rpx 20rpx 0px rgba(0, 0, 0, 0.2);
-    right: 20rpx;
-    top: -104rpx;
-    z-index: 99;
-  }
-
-  &_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;
-    }
-  }
-
-  .station-placeholder {
-    min-height: 104rpx;
-  }
-  .station-iphonex {
-    box-sizing: content-box;
-    padding-bottom: constant(safe-area-inset-bottom);
-    padding-bottom: env(safe-area-inset-bottom);
-  }
-}
-
-.charging {
-  position: fixed;
-  z-index: 1000;
-  right: 0;
-  bottom: 164rpx;
-  width: 186rpx;
-  height: 88rpx;
-  border-radius: 88rpx 0px 0px 88rpx;
-  background: linear-gradient(106deg, #34b6ff 15.97%, #347dff 72.38%);
-  margin-bottom: constant(safe-area-inset-bottom);
-  margin-bottom: env(safe-area-inset-bottom);
-}
-</style>
+<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">
+        <view class="dialog_logo ml-24"></view>
+        <view class="dialog_search ml-24" @click.stop="toSearch"></view>
+      </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">
+          <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 && mapBanner.length > 0">
+        <swiper
+          class="swiper"
+          circular
+          :indicator-dots="true"
+          :autoplay="mapBanner.length > 1"
+          :interval="3000"
+        >
+          <swiper-item
+            class="swiper-item full-percent"
+            v-for="(item, index) in mapBanner"
+            :key="index"
+            @click="to(item.linkUrl)"
+          >
+            <view
+              class="full-percent"
+              :style="{
+                backgroundImage: `url(${item.bannerUrl})`,
+              }"
+            ></view>
+          </swiper-item>
+        </swiper>
+      </view>
+    </view>
+    <view
+      v-if="styleData.dialogPlaceHolderHeight"
+      :style="{ height: `${styleData.dialogPlaceHolderHeight}px` }"
+    ></view>
+    <map
+      v-if="ready"
+      id="map"
+      :style="{
+        width: '100%',
+        height: styleData.dialogHeight
+          ? `calc(100vh - ${styleData.dialogHeight}px - ${
+              styleData.cardHeight - 24
+            }px)`
+          : '50vh',
+        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>
+    <block v-else>
+      <view
+        v-if="styleData.dialogHeight"
+        class="mock-map flex-center"
+        :style="{
+          height: `calc(100vh - ${styleData.dialogHeight}px - ${
+            styleData.cardHeight - 24
+          }px)`,
+        }"
+      >
+        <image
+          src="/static/images/map-bg.jpg"
+          mode="widthFix"
+          style="width: 100%"
+        ></image>
+        <image
+          src="/static/images/map-current.png"
+          mode="widthFix"
+          class="absolute-center"
+          style="width: 34px"
+        ></image>
+      </view>
+    </block>
+    <view
+      class="card"
+      :style="{
+        height: mapMode
+          ? `${styleData.cardHeight}px`
+          : `calc(100vh - ${styleData.navHeight}px)`,
+        bottom: '0px',
+        borderRadius: mapMode ? '16rpx 16rpx 0px 0px' : '0px',
+        overflowY: mapMode ? 'visible' : 'auto',
+      }"
+      @touchstart="touchCardStart"
+      @touchmove="touchCardMove"
+    >
+      <view
+        v-if="mapMode"
+        class="card_location height-64 width-64 bg-fff flex-center"
+        @touchstart.stop="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"
+              :activity-list="item.activityList"
+              :fromMap="true"
+            ></charge-station>
+          </view>
+          <view class="station-placeholder"></view>
+          <view class="station-iphonex"></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"
+              :activity-list="station[markersIndex].activityList"
+              :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
+              ? "加载中"
+              : token
+              ? "暂无充电站信息"
+              : "登录后查看更多电站信息"
+          }}</view>
+        </view>
+      </block>
+    </view>
+    <view
+      class="charging flex-align-center"
+      v-if="charging"
+      @click="toCharging"
+    >
+      <image
+        class="width-64 ml-12"
+        src="/static/images/map-charging.png"
+        mode="widthFix"
+      ></image>
+      <view class="fs-26 color-fff ml-12">{{
+        charging.chargeStatus === 0 ? "已预约" : "充电中"
+      }}</view>
+    </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 { fetchHomeBanner } from "@/api";
+import { deCode } from "../../utils/code";
+import { rpxToPx } from "../../utils/device";
+import { fetchToken, login, onLogin } from "@/api/auth";
+import { fetchStations, fetchChargeStatus } from "@/api/charge";
+import { fetchCollectList } from "@/api/user";
+import { fetchLocation } from "@/utils/location";
+import { to } from "@/utils/navigate";
+import { onLoad, onShow } from "@dcloudio/uni-app";
+import { ref } from "vue";
+
+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,
+  navHeight: 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 charging = 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[]>([]);
+const mapBanner = 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("刷新电站");
+  uni.showLoading({
+    title: "加载中",
+  });
+  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(() => {
+      uni.hideLoading();
+      loading.value = false;
+      ready.value = true;
+    })
+    .catch((err) => {
+      console.log(err);
+      uni.hideLoading();
+      loading.value = false;
+      uni.showModal({
+        content: `${err.errMsg},请重试`,
+      });
+    });
+};
+
+const handleNavReady = (e: any) => {
+  console.log(e);
+  styleData.value.dialog = `padding-top:${e.detail.statusBarHeight - 6}px;`;
+  const searchHeight = rpxToPx(88);
+  const filterHeight = 0; // rpxToPx(72);
+  styleData.value.dialogHeight =
+    e.detail.statusBarHeight - 6 + searchHeight + filterHeight;
+  styleData.value.dialogPlaceHolderHeight =
+    styleData.value.dialogHeight - e.detail.navigationBarHeight;
+  styleData.value.cardHeight = rpxToPx(402) + e.detail.statusBarHeight;
+  styleData.value.navHeight = e.detail.navigationBarHeight;
+};
+
+const toSearch = () => {
+  if (!ready.value) {
+    return;
+  }
+  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();
+          fetchCharging();
+          fetchBanner();
+        });
+      });
+      return;
+    }
+    fetchCollectList().then(() => {
+      if (getApp<any>().globalData.normalCode) {
+        const code: string = getApp<any>().globalData.normalCode;
+        getApp<any>().globalData.normalCode = "";
+        deCode(code);
+      }
+      refresh();
+      fetchCharging();
+      fetchBanner();
+    });
+  }, 300);
+});
+
+onShow(() => {
+  if (token.value) {
+    fetchBanner();
+    charging.value = undefined;
+    setTimeout(() => {
+      fetchCharging();
+    }, 2000);
+  }
+});
+
+const fetchCharging = () => {
+  fetchChargeStatus().then((res) => {
+    if (res && [0, 1, 2, 3].includes(res.chargeStatus)) {
+      charging.value = res;
+    }
+  });
+};
+
+const fetchBanner = () => {
+  fetchHomeBanner().then((res) => {
+    mapBanner.value = res.filter((item: any) => item.status === 1);
+  });
+};
+
+// const checkDiscounts = () => {
+//   filterDialog.value.discounts = !filterDialog.value.discounts;
+// };
+
+const checkFilterDistance = () => {
+  if (isIgnoreChangeLocation) {
+    return;
+  }
+  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 = 200;
+  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;
+    }
+  }
+};
+
+const toCharging = () => {
+  to(
+    charging.value.chargeStatus === 0
+      ? `/pages-charge/appointment/appointment?sn=${charging.value.connectorId}`
+      : `/pages-charge/ordering/ordering?sn=${charging.value.connectorId}&start=1`
+  );
+};
+</script>
+
+<style lang="scss">
+page {
+  background-color: #ffffff;
+}
+
+.container {
+  position: relative;
+  height: 100vh;
+  width: 100vw;
+  background-color: #ffffff;
+}
+
+.mock-map {
+  position: relative;
+  width: 100vw;
+  background-color: #ffffff;
+  overflow: hidden;
+}
+
+.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: 1000;
+
+  &_logo {
+    width: 156rpx;
+    height: 100%;
+    background-position: center;
+    background-size: 100% auto;
+    background-repeat: no-repeat;
+    background-image: url("/static/images/map-logo.png");
+  }
+
+  &_search {
+    width: 352rpx;
+    height: 100%;
+    background-position: center;
+    background-size: 100% auto;
+    background-repeat: no-repeat;
+    background-image: url("/static/images/map-input.png");
+  }
+
+  &_event {
+    margin-top: 20rpx;
+    padding: 0rpx 32rpx;
+    .swiper {
+      height: 160rpx;
+      width: 100%;
+      border-radius: 16rpx;
+      overflow: hidden;
+      .swiper-item {
+        border-radius: 16rpx;
+        overflow: hidden;
+      }
+      view {
+        background-position: center;
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+  }
+
+  &_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: 1000;
+  transition: all 0.3s;
+  box-shadow: 0px 8rpx 20rpx rgba(0, 0, 0, 0.2);
+  background-color: #fff;
+
+  &_location {
+    position: absolute;
+    border-radius: 16rpx;
+    box-shadow: 0px 8rpx 20rpx 0px rgba(0, 0, 0, 0.2);
+    right: 20rpx;
+    top: -104rpx;
+    z-index: 99;
+  }
+
+  &_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;
+    }
+  }
+
+  .station-placeholder {
+    min-height: 104rpx;
+  }
+  .station-iphonex {
+    box-sizing: content-box;
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+  }
+}
+
+.charging {
+  position: fixed;
+  z-index: 1000;
+  right: 0;
+  bottom: 164rpx;
+  width: 186rpx;
+  height: 88rpx;
+  border-radius: 88rpx 0px 0px 88rpx;
+  background: linear-gradient(106deg, #34b6ff 15.97%, #347dff 72.38%);
+  margin-bottom: constant(safe-area-inset-bottom);
+  margin-bottom: env(safe-area-inset-bottom);
+}
+</style>

BIN
src/static/images/coupon-center.png