needcode před 2 roky
rodič
revize
bc55e8ec60

+ 24 - 0
src/App.vue

@@ -80,6 +80,30 @@ image {
   color: var(--color-warning);
 }
 
+.bg-fff {
+  background-color: #fff;
+}
+
+.full-percent {
+  height: 100%;
+  width: 100%;
+}
+
+.absolute-full {
+  position: absolute;
+  left: 0px;
+  top: 0px;
+  width: 100%;
+  height: 100%;
+}
+
+.absolute-center {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+}
+
 .transition {
   transition: all 0.3s;
 }

+ 53 - 3
src/api/charge.ts

@@ -19,6 +19,7 @@ export function fetchStationPriceDesc(ConnectorID: string, StationID?: string) {
     const nowHour = new Date().getHours();
     let maxPrice = 0;
     let minPrice = 9;
+    let minPriceTime = "00:00";
     let currentPrice = 0;
     let currentTime = "00:00~24:00";
     res.useTime = "";
@@ -35,9 +36,6 @@ export function fetchStationPriceDesc(ConnectorID: string, StationID?: string) {
         if (tempPrice > maxPrice) {
           maxPrice = tempPrice;
         }
-        if (tempPrice < minPrice) {
-          minPrice = tempPrice;
-        }
         if (index >= res.policyInfoss.length - 1) {
           // 最后一个
           item.startTimeFormat = `${hour}:${min}~24:00`;
@@ -45,6 +43,10 @@ export function fetchStationPriceDesc(ConnectorID: string, StationID?: string) {
             currentPrice = tempPrice;
             currentTime = `${hour}:${min}~24:00`;
           }
+          if (tempPrice < minPrice) {
+            minPrice = tempPrice;
+            minPriceTime = `${hour}:${min}~24:00`;
+          }
         } else {
           const nhour = res.policyInfoss[index + 1].startTime.substring(0, 2);
           const nmin = res.policyInfoss[index + 1].startTime.substring(3, 5);
@@ -53,11 +55,16 @@ export function fetchStationPriceDesc(ConnectorID: string, StationID?: string) {
             currentPrice = tempPrice;
             currentTime = `${hour}:${min}~${nhour}:${nmin}`;
           }
+          if (tempPrice < minPrice) {
+            minPrice = tempPrice;
+            minPriceTime = `${hour}:${min}~${nhour}:${nmin}`;
+          }
         }
       });
     }
     res.maxPrice = maxPrice;
     res.minPrice = minPrice;
+    res.minPriceTime = minPriceTime;
     res.currentPrice = currentPrice;
     res.currentTime = currentTime;
     if (StationID) {
@@ -190,6 +197,49 @@ export function fetchStationByIds(ids: number[]) {
   });
 }
 
+export function fetchStationByConnectorId(connectorId: string) {
+  let equipment = -1;
+  return fetchAllStations()
+    .then((res) => {
+      let station: any = undefined;
+      res.forEach((item) => {
+        if (!station && item.equipmentInfos && item.equipmentInfos) {
+          item.equipmentInfos.forEach(
+            (equipmentInfo: any, equipmentIndex: number) => {
+              if (
+                equipmentInfo.connectorInfos &&
+                equipmentInfo.connectorInfos
+              ) {
+                // connectorId
+                equipmentInfo.connectorInfos.forEach((connectorInfo: any) => {
+                  if (connectorInfo.connectorId === connectorId) {
+                    station = item;
+                    equipment = equipmentIndex;
+                  }
+                });
+              }
+            }
+          );
+        }
+      });
+      if (station) {
+        return _fetchStationStatus([station]);
+      } else {
+        return Promise.reject({});
+      }
+    })
+    .then((res) => {
+      if (res && res.length) {
+        return {
+          station: res[0],
+          equipment: res[0].equipmentInfos[equipment],
+        };
+      } else {
+        return Promise.reject({});
+      }
+    });
+}
+
 export function searchStation(keyword: string) {
   return fetchAllStations().then((res) => {
     const reg = new RegExp(keyword, "ig");

+ 1 - 1
src/components/style-bottom-view/style-bottom-view.vue

@@ -26,7 +26,7 @@ export default {
     },
     zIndex: {
       type: Number,
-      default: 999999,
+      default: 999,
     },
   },
 };

+ 187 - 3
src/pages-charge/appointment/appointment.vue

@@ -1,7 +1,191 @@
 <template>
-  <view class="list"> </view>
+  <view class="page">
+    <template v-if="data && priceInfo">
+      <view class="block">
+        <view class="station">
+          <ChargeMachine
+            :title="'NO.' + data.equipment.shortId"
+            :list="data.equipment.connectorInfos"
+            :time="priceInfo.useTime"
+          ></ChargeMachine>
+        </view>
+        <view class="pt-20 pb-20 pl-30 pr-30 flex-align-center">
+          <image
+            src="/pages-charge/static/machines-banner-address.png"
+            mode="widthFix"
+            class="flex-shrink mr-12 width-40"
+          />
+          <view class="fs-26 color-666">{{ data.station.address }}</view>
+        </view>
+      </view>
+      <view class="mt-40 color-999 fs-32 fw-500">费用说明</view>
+      <view class="mt-20 block height-96 flex-align-center pl-30 pr-30">
+        <view class="fs-26 color-666">充电费用</view>
+        <view class="ml-64 fs-26 color-333"
+          >{{ priceInfo.minPrice }}~{{ priceInfo.maxPrice }}元/度</view
+        >
+        <view class="ml-auto">
+          <uni-icons type="right" size="16" color="rgba(0,0,0,0.4)"></uni-icons>
+        </view>
+      </view>
+      <view class="mt-40 color-999 fs-32 fw-500">选择充电方式</view>
+      <view class="mt-20 block pl-30 pr-30">
+        <view
+          v-for="(type, index) in chargeTypes"
+          :key="index"
+          :class="['pt-40', 'pb-40', 'flex-align-center']"
+          :style="{
+            borderTop: index === 0 ? '' : '1rpx solid rgba(0, 0, 0, 0.10)',
+          }"
+          @click="changeType(index)"
+        >
+          <view>
+            <view class="fs-28 lh-28 color-000 fw-500">{{ type.title }}</view>
+            <view class="fs-24 color-999 lh-24 mt-16" v-if="type.tip">{{
+              type.tip
+            }}</view>
+          </view>
+          <view class="ml-auto">
+            <view
+              class="height-32 width-32 lh-24 br-round flex-justify-center"
+              :style="{
+                border:
+                  chargeType === index
+                    ? '1rpx solid var(--color-primary)'
+                    : '1rpx solid rgba(0,0,0,0.15)',
+                backgroundColor:
+                  chargeType === index
+                    ? 'var(--color-primary)'
+                    : 'rgba(0,0,0,0.1)',
+              }"
+            >
+              <uni-icons
+                v-if="chargeType === index"
+                type="checkmarkempty"
+                size="10"
+                color="#ffffff"
+              ></uni-icons>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view class="mt-20 block pl-30 pr-30">
+        <view
+          class="pt-40 pb-40 flex-between"
+          style="border-bottom: 1rpx solid rgba(0, 0, 0, 0.1)"
+          @click="selectTime"
+        >
+          <view class="fs-28 color-000 fw-500">充电时间</view>
+          <view class="flex-align-center lh-28">
+            <view v-if="chargeTime" class="fs-28 color-333 fw-500 mr-16"
+              >TODO</view
+            >
+            <view v-else class="fs-26 mr-16" style="color: #cacaca"
+              >请选择</view
+            >
+            <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
+          </view>
+        </view>
+        <view class="pt-40 pb-40 flex-between">
+          <view class="fs-28 color-000 fw-500">预计费用</view>
+          <view class="flex-align-center lh-28">
+            <view v-if="chargeTime" class="fs-28 color-333 fw-500">TODO</view>
+            <view v-else class="fs-26 mr-16" style="color: #cacaca"
+              >选择时间后显示</view
+            >
+          </view>
+        </view>
+        <view class="pb-24 fs-24 color-primary"
+          >{{ priceInfo.minPriceTime }}充电,电费低至{{
+            priceInfo.minPrice
+          }}元/kwh</view
+        >
+      </view>
+      <view class="pt-60 pb-80"></view>
+    </template>
+    <style-bottom-view>
+      <view class="pt-20 pl-40 pr-40 pb-20 bg-fff">
+        <style-button type="primary" size="small" @click="submit">{{
+          chargeType === 0 ? "提交申请" : "马上充电"
+        }}</style-button>
+      </view>
+    </style-bottom-view>
+  </view>
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { fetchStationByConnectorId, fetchStationPriceDesc } from "@/api/charge";
+import { onLoad } from "@dcloudio/uni-app";
+import { ref } from "vue";
+import ChargeMachine from "../machines/charge-machine/charge-machine.vue";
 
-<style></style>
+const options = ref<any>();
+const data = ref<any>();
+const priceInfo = ref();
+
+const chargeType = ref(1);
+const chargeTypes = ref([
+  {
+    title: "预约省钱充电",
+    tip: "预约特定时间点开始充电",
+  },
+  {
+    title: "立即充电",
+  },
+]);
+const chargeTime = ref();
+
+const changeType = (index: number) => {
+  chargeType.value = index;
+};
+
+const selectTime = () => {
+  if (chargeType.value === 1) {
+    return
+  }
+  // TODO 选择时间
+}
+
+const submit = () => {};
+
+onLoad((_options: any) => {
+  // sn=SN100523051916091
+  const sn = _options.sn || "SN100523051916091";
+  uni.showLoading({
+    title: "加载中",
+  });
+  fetchStationByConnectorId(sn)
+    .then((res) => {
+      console.log(res);
+      options.value = _options;
+      data.value = res;
+      return fetchStationPriceDesc(sn);
+    })
+    .then((res) => {
+      console.log(res);
+      uni.hideLoading();
+      priceInfo.value = res;
+    })
+    .catch(() => {
+      uni.hideLoading();
+      uni.navigateBack();
+    });
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+  min-height: 100vh;
+  width: 100%;
+  padding: 20rpx;
+  background-color: #f6f7fa;
+}
+.block {
+  border-radius: 20rpx;
+  background: #fff;
+}
+.station {
+  height: 148rpx;
+  border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
+}
+</style>

+ 58 - 1
src/pages-charge/camera/camera.vue

@@ -5,6 +5,7 @@
         v-if="!isDevTool"
         device-position="back"
         mode="scanCode"
+        :flash="cameraFlash"
         @scancode="handleScancode"
         @ready="handleReady"
         @error="handleError"
@@ -23,8 +24,34 @@
       ></view>
     </view>
     <view class="page_action flex-shrink" id="page_action">
+      <cover-view
+        @click="toggleFlash"
+        style="
+          text-align: center;
+          position: absolute;
+          width: 100%;
+          left: 0;
+          top: -96px;
+        "
+      >
+        <cover-image
+          style="width: 28px; height: 28px; margin: 0 auto"
+          src="/pages-charge/static/scan-code-light.png"
+        ></cover-image>
+        <cover-view
+          style="
+            color: rgba(255, 255, 255, 0.7);
+            font-size: 13px;
+            margin-top: 10px;
+          "
+          >{{ cameraFlash === "off" ? "轻触照亮" : "轻触关闭" }}</cover-view
+        >
+      </cover-view>
       <view class="flex">
-        <view class="flex-center flex-column" @click="redirect('/pages-charge/codeing/codeing')">
+        <view
+          class="flex-center flex-column"
+          @click="redirect('/pages-charge/codeing/codeing')"
+        >
           <image
             class="width-96"
             mode="widthFix"
@@ -51,13 +78,34 @@ import { isDevTool } from "../../utils/device";
 import { deCode } from "../../utils/code";
 import { redirect } from "../../utils/navigate";
 import { onLoad, onReady } from "@dcloudio/uni-app";
+import { fetchChargeStatus } from "../../api/charge";
 
 const cameraHeight = ref(0);
+const cameraFlash = ref("off");
 
 onLoad(() => {
   uni.showLoading({
     title: "加载中",
   });
+  fetchChargeStatus().then((res) => {
+    if ([1, 2].includes(res.chargeStatus)) {
+      uni.hideLoading();
+      uni.showModal({
+        title: "温馨提示",
+        content: "当前已有进行中的订单,请结束订单再扫码充电",
+        showCancel: false,
+        confirmText: "查看当前",
+        confirmColor: "#347DFF",
+        success(modal) {
+          if (modal.confirm) {
+            uni.navigateTo({
+              url: `/pages-charge/appointment/appointment?sn=${res.connectorId}&start=1`,
+            });
+          }
+        },
+      });
+    }
+  });
 });
 
 onReady(() => {
@@ -100,6 +148,14 @@ const handleError = (e: any) => {
     content: e.detail.errMsg,
   });
 };
+
+const toggleFlash = () => {
+  if (cameraFlash.value === "off") {
+    cameraFlash.value = "on";
+    return;
+  }
+  cameraFlash.value = "off";
+};
 </script>
 
 <style lang="scss">
@@ -113,6 +169,7 @@ const handleError = (e: any) => {
     }
   }
   &_action {
+    position: relative;
     box-sizing: content-box;
     padding-bottom: constant(safe-area-inset-bottom);
     padding-bottom: env(safe-area-inset-bottom);

+ 10 - 4
src/pages-charge/machines/charge-machine/charge-machine.vue

@@ -14,7 +14,10 @@
       ></image>
       <image
         style="width: 100%"
-        v-else-if="item.connectorStatusInfo.status === 0 || item.connectorStatusInfo.status === 255"
+        v-else-if="
+          item.connectorStatusInfo.status === 0 ||
+          item.connectorStatusInfo.status === 255
+        "
         src="/pages-charge/static/icon-status-3.png"
         mode="widthFix"
       ></image>
@@ -49,10 +52,10 @@
           }}{{ item.power }}kw</view
         >
         <view class="fs-24 color-666 mr-6 ml-6">|</view>
-        <view class="fs-24 color-666">车位号TODO</view>
+        <view class="fs-24 color-666">车位号{{ item.parkNo }}</view>
       </view>
     </view>
-    <view class="ml-auto">
+    <view class="ml-auto" v-if="price">
       <view
         class="lh-32 color-warning"
         style="text-align: right; margin-top: -8rpx"
@@ -63,6 +66,9 @@
       </view>
       <view class="fs-24 lh-24 color-999 mt-10">预计费用</view>
     </view>
+    <view class="ml-auto height-60 flex-align-end" v-if="time">
+      <view class="fs-24 lh-24 color-999">{{ time }}</view>
+    </view>
   </view>
 </template>
 
@@ -70,7 +76,7 @@
 export default {
   props: {
     title: String,
-    price: String,
+    price: [String, Number],
     time: String,
     list: Array<any>,
   },

+ 110 - 89
src/pages-charge/machines/machines.vue

@@ -1,103 +1,119 @@
 <template>
-  <!-- TODO 图片 -->
-  <view
-    :class="[
-      'pl-20',
-      'pr-20',
-      'pb-40',
-      `container-${dialogVisible ? 'hidden' : ''}`,
-    ]"
-  >
-    <view class="banner flex-column" v-if="canUseCount >= 0">
-      <view class="fs-40 fw-600 color-000">{{ title }}</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"
-        />
-        <view class="ml-12 fs-26 color-666">{{ location.address }}</view>
-        <view class="flex-center ml-auto nav" @click="openAddress">
+  <view :class="['pb-40', `container-${dialogVisible ? 'hidden' : ''}`]" v-if="station">
+    <view class="banner">
+      <swiper
+        class="full-percent"
+        circular
+        :indicator-dots="true"
+        :autoplay="true"
+        :interval="3000"
+      >
+        <swiper-item class="full-percent">
+          <view class="full-percent"></view>
+        </swiper-item>
+        <swiper-item class="full-percent">
+          <view class="full-percent bg-fff"></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 class="flex-align-center mt-10 height-48 relative flex-shrink">
           <image
-            src="/pages-charge/static/machines-banner-nav.png"
+            src="/pages-charge/static/machines-banner-address.png"
             mode="widthFix"
+            style="width: 16px; display: block; flex-shrink: 0"
           />
-          <view class="fs-26" style="color: #347dff">导航</view>
+          <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"
+            />
+            <view class="fs-26" style="color: #347dff">导航</view>
+          </view>
         </view>
-      </view>
-      <view 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>
-        <view class="fs-28 color-primary ml-6">个</view>
-        <view class="fs-28 color-666 ml-auto">{{ canUseTime }}</view>
-      </view>
-    </view>
-    <view class="pt-40 pb-20 fs-32 fw-500 color-999">详细说明</view>
-    <view class="desc" v-if="station">
-      <view class="flex-align-center" @click="openDesc">
-        <view class="width-168 fs-26 color-666">充电费用</view>
-        <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)"></uni-icons>
+        <view
+          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>
+          <view class="fs-28 color-primary ml-6">个</view>
+          <view class="fs-28 color-666 ml-auto">{{ canUseTime }}</view>
         </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>
+      <view class="pt-40 pb-20 fs-32 fw-500 color-999">详细说明</view>
+      <view class="desc" v-if="station">
+        <view class="flex-align-center" @click="openDesc">
+          <view class="width-168 fs-26 color-666">充电费用</view>
+          <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)"
+            ></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>
+        </view>
+        <view
+          class="flex-align-center"
+          v-if="
+            station &&
+            station.equipmentInfos &&
+            station.equipmentInfos.length &&
+            station.equipmentInfos[0].connectorInfos
+          "
+        >
+          <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
+          >
+        </view>
       </view>
-      <view
-        class="flex-align-center"
-        v-if="
-          station &&
-          station.equipmentInfos &&
-          station.equipmentInfos.length &&
-          station.equipmentInfos[0].connectorInfos
-        "
-      >
-        <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
+      <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
         >
+        <view class="ml-auto flex-align-center" @click="openStatus">
+          <view class="fs-28 color-333 mr-16">{{
+            statusList[status].title
+          }}</view>
+          <view
+            style="
+              width: 0;
+              height: 0;
+              border: 8rpx solid;
+              border-color: #333 transparent transparent transparent;
+              margin-top: 8rpx;
+            "
+          ></view>
+        </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
+      <template
+        v-if="station"
+        v-for="(item, index) in stationEquipmentInfos"
+        :key="index"
       >
-      <view class="ml-auto flex-align-center" @click="openStatus">
-        <view class="fs-28 color-333 mr-16">{{
-          statusList[status].title
-        }}</view>
-        <view
-          style="
-            width: 0;
-            height: 0;
-            border: 8rpx solid;
-            border-color: #333 transparent transparent transparent;
-            margin-top: 8rpx;
-          "
-        ></view>
-      </view>
+        <ChargeMachine
+          :title="'NO.' + item.shortId"
+          :price="station.totalFee"
+          :list="item.connectorInfos"
+        ></ChargeMachine>
+      </template>
     </view>
-    <template
-      v-if="station"
-      v-for="(item, index) in stationEquipmentInfos"
-      :key="index"
-    >
-      <ChargeMachine
-        :title="'NO.' + item.shortId"
-        :price="station.totalFee"
-        :time="currentTime"
-        :list="item.connectorInfos"
-      ></ChargeMachine>
-    </template>
     <view class="dialog flex-align-end" v-if="dialogVisible">
       <view class="desc-dialog">
         <view class="desc-dialog_head flex-center">
@@ -343,7 +359,12 @@ page {
 }
 
 .banner {
-  margin-top: 380rpx;
+  height: 440rpx;
+  background-color: #000;
+}
+
+.station {
+  margin-top: -60rpx;
   border-radius: 20rpx;
   overflow: hidden;
   position: relative;

binární
src/pages-charge/static/scan-code-light.png


+ 2 - 2
src/pages-user/wallet/wallet.vue

@@ -84,7 +84,7 @@
           </view>
         </view>
       </view>
-      <view style="height: 170rpx;"></view>
+      <view style="height: 170rpx"></view>
     </view>
     <style-bottom-view background="#ffffff">
       <view class="pl-40 pr-40 pb-30 pt-30">
@@ -123,7 +123,7 @@ const tabs = ref([
     value: 3,
   },
   {
-    label: "退款", // TODO
+    label: "退款",
     value: 2,
   },
 ]);

+ 6 - 3
src/pages.json

@@ -1,9 +1,6 @@
 {
   "entryPagePath": "pages/map/map",
   "pages": [
-    {
-      "path": "pages/index/index"
-    },
     {
       "path": "pages/map/map",
       "style": {
@@ -140,6 +137,12 @@
       ]
     }
   ],
+  "preloadRule": {
+    "pages/map/map": {
+      "network": "all",
+      "packages": ["pages-charge"]
+    }
+  },
   "globalStyle": {
     "navigationBarTextStyle": "black",
     "navigationBarTitleText": "uni-app",

binární
src/static/images/custom-tab-bar/bg.png


+ 7 - 0
src/styles/layout.scss

@@ -24,6 +24,13 @@ $position: top, right, bottom, left;
   .width-#{$i} {
     width: #{$i}rpx;
   }
+  .br-#{$i} {
+    border-radius: #{$i}rpx;
+  }
+}
+
+.br-round {
+  border-radius: 50%;
 }
 
 .mb-0 {