needcode 2 år sedan
förälder
incheckning
8d44547e63

+ 51 - 0
src/components/shadow-card/shadow-card.vue

@@ -0,0 +1,51 @@
+<template>
+  <view class="shadow-card">
+    <view v-for="(item, index) in list" :key="index">
+      <view class="label">{{ item.label }}</view>
+      <view class="value">{{ item.value }}</view>
+    </view>
+  </view>
+</template>
+
+<script lang="ts">
+export default {
+  props: {
+    list: {
+      type: Array<any>,
+      defalut: [],
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.shadow-card {
+  background: #ffffff;
+  border: 1rpx solid rgba(0, 0, 0, 0.1);
+  box-shadow: 0px 4rpx 20rpx rgba(0, 0, 0, 0.1);
+  border-radius: 20rpx;
+  padding: 40rpx;
+
+  & > view {
+    margin-top: 40rpx;
+    display: flex;
+
+    &:first-child {
+      margin-top: 0rpx;
+    }
+
+    .label {
+      width: 156rpx;
+      flex-shrink: 0;
+      color: rgba(0, 0, 0, 0.4);
+      font-size: 26rpx;
+    }
+
+    .value {
+      flex-grow: 1;
+      color: #000;
+      font-size: 26rpx;
+    }
+  }
+}
+</style>

+ 12 - 13
src/custom-tab-bar/index.js

@@ -1,7 +1,6 @@
 import { fetchToken, login, onLogin } from "../api/auth";
-// import {
-//   fetchChargeStatus
-// } from '../api/charge'
+import { fetchChargeStatus } from "../api/charge";
+import { scanCode } from "../utils/code";
 
 Component({
   data: {
@@ -52,15 +51,15 @@ Component({
   },
   methods: {
     switchTab(e) {
-      // fetchChargeStatus().then(res => {
-      //   this.setData({
-      //     charging: [1, 2].includes(res.StartChargeSeqStat) ? res.ConnectorID : ''
-      //   })
-      // }).catch(() => {
-      //   this.setData({
-      //     charging: ''
-      //   })
-      // })
+      fetchChargeStatus().then(res => {
+        this.setData({
+          charging: [1, 2].includes(res.StartChargeSeqStat) ? res.ConnectorID : ''
+        })
+      }).catch(() => {
+        this.setData({
+          charging: ''
+        })
+      })
       const data = e.currentTarget.dataset;
       const url = data.path;
       if (!url) {
@@ -97,7 +96,7 @@ Component({
       login(e);
     },
     scanCode() {
-      getApp().scanCode();
+      scanCode()
     },
     inputCode() {
       wx.navigateTo({

+ 60 - 2
src/pages-charge/order/order.vue

@@ -1,9 +1,67 @@
 <template>
-  <view></view>
+  <view class="body" v-if="list">
+    <shadow-card :list="list"></shadow-card>
+  </view>
 </template>
 
 <script setup lang="ts">
+import { fetchOrder } from "../../api/user";
+import { onLoad } from "@dcloudio/uni-app";
+import { ref } from "vue";
+
+const list = ref<any[]>();
+onLoad((options: any) => {
+  fetchOrder(options.id)
+    .then((res) => {
+      // console.log(res);
+      const total_money = (res.total_money / 100).toFixed(2);
+      const reg = new RegExp("\B(?=(\d{3})+(?!\d))", "g");
+      const start = new Date(res.start_time.replace(/-/g, "/"));
+      const end = new Date(res.end_time.replace(/-/g, "/"));
+      const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
+      const min = parseInt(`${diff / 60}`);
+      const time =
+        min >= 60
+          ? `${parseInt(`${min / 60}`)}小时${parseInt(
+              `${min - parseInt(`${min / 60}`) * 60}`
+            )}分钟`
+          : `${parseInt(`${diff / 60}`)}分钟`;
+      list.value = [
+        {
+          label: "累计充电量",
+          value: `${res.total_power}度`,
+        },
+        {
+          label: "累计费用",
+          value: `${total_money.replace(reg, ",")}元`,
+        },
+        {
+          label: "开始时间",
+          value: res.start_time,
+        },
+        {
+          label: "结束时间",
+          value: res.end_time,
+        },
+        {
+          label: "累计用时",
+          value: time,
+        },
+      ];
+    })
+    .catch(() => {
+      uni.showModal({
+        content: "出现错误,请退出重进",
+        success() {
+          uni.navigateBack();
+        },
+      });
+    });
+});
 </script>
 
-<style>
+<style lang="scss">
+.body {
+  padding: 30rpx 60rpx;
+}
 </style>

+ 451 - 2
src/pages-charge/ordering/ordering.vue

@@ -1,9 +1,458 @@
 <template>
-  <view></view>
+  <view class="container">
+    <image class="bg" src="../images/charge-ordering-bg.png" mode="widthFix" />
+    <view class="body">
+      <view class="iphonex-placeholder"></view>
+      <view
+        class="status flex-center transition"
+        :style="{
+          opacity: step >= 1 ? '1' : '0',
+        }"
+      >
+        <image
+          :class="['border', `${step <= 2 ? 'border-animation' : ''}`]"
+          src="../images/charge-ordering-border.png"
+          @load="onImgLoad"
+        />
+        <view class="timer flex-column flex-center">
+          <block v-if="step === 1">
+            <view>
+              <text class="fw-600" style="font-size: 100rpx">{{
+                status.time
+              }}</text>
+              <text class="fs-40 fw-500 ml-10">s</text>
+            </view>
+            <view class="fs-26">充电启动中</view>
+          </block>
+          <block v-if="step === 2">
+            <image class="icon" src="../images/charge-ordering-icon.png" />
+            <view class="fs-26 mt-10">充电中...</view>
+          </block>
+          <block v-if="step === 3">
+            <image class="icon" src="../images/charge-ordering-finish.png" />
+            <view class="fs-26 mt-10">充电结束</view>
+          </block>
+        </view>
+      </view>
+    </view>
+  </view>
+
+  <style-bottom-view>
+    <view
+      class="transition pl-60 pr-60 pb-40"
+      :style="{
+        opacity: step >= 1 ? '1' : '0',
+      }"
+    >
+      <view class="mb-40" v-if="step === 2 || step === 3">
+        <shadow-card :list="chargeInfo"></shadow-card>
+      </view>
+      <style-button
+        v-if="step === 1 || step === 2"
+        type="warning"
+        size="small"
+        @click="cancel"
+        >取消充电</style-button
+      >
+      <style-button v-if="step === 3" type="primary" size="small" @click="back"
+        >返回首页</style-button
+      >
+    </view>
+  </style-bottom-view>
 </template>
 
 <script setup lang="ts">
+import { onHide, onLoad, onShow } from "@dcloudio/uni-app";
+import { cancelCharge, fetchChargeStatus, startCharge } from "../../api/charge";
+import { ref } from "vue";
+import { format } from "../../utils/date";
+
+let timer: any;
+const step = ref(0);
+const options = ref<any>();
+const status = ref({
+  time: 30,
+  start: false,
+  cancel: false,
+  error: false
+});
+const chargeInfo = ref<any[]>([]);
+const startStartTimer = () => {
+  if (status.value.start || status.value.cancel || status.value.error) {
+    timer && clearTimeout(timer);
+    return;
+  }
+  timer = setTimeout(() => {
+    if (status.value.time <= 1 && !status.value.start) {
+      uni.showModal({
+        title: "充电错误",
+        content: "出现问题,请重试",
+        showCancel: false,
+        confirmText: "知道了",
+        confirmColor: "#347DFF",
+        success() {
+          uni.navigateBack();
+        },
+      });
+      return;
+    }
+    status.value.time = status.value.time - 1;
+    startStartTimer();
+  }, 1000);
+};
+const startStatusTimer = () => {
+  setTimeout(() => {
+    fetchChargeStatus()
+      .then((res) => {
+        if (status.value.cancel) {
+          return;
+        }
+        if ([1, 2].includes(res.StartChargeSeqStat)) {
+          setChargeData(res);
+        }
+        startStatusTimer();
+      })
+      .catch(() => {
+        startStatusTimer();
+      });
+  }, 30000);
+};
+const start = () => {
+  step.value = 1;
+  startStartTimer();
+  startCharge(options.value && options.value.sn ? options.value.sn : "")
+    .then(() => {
+      fetchStatus();
+    })
+    .catch((err) => {
+      setTimeout(() => {
+        status.value.error = true
+        timer && clearTimeout(timer);
+        uni.showModal({
+          content: `${err.errMsg}`,
+          showCancel: false,
+          success: () => {
+            uni.navigateBack();
+          },
+        });
+      }, 300);
+    });
+};
+const back = () => {
+  uni.navigateBack();
+};
+const cancel = () => {
+  uni.showModal({
+    title: "停止充电",
+    content: "是否停止充电",
+    cancelText: "停止",
+    cancelColor: "#999999",
+    confirmText: "点错了",
+    confirmColor: "#347DFF",
+    success: (res) => {
+      if (res.cancel) {
+        clearTimeout(timer);
+        status.value.cancel = true;
+        if ([1, 2].includes(step.value)) {
+          if (status.value.start) {
+            finish();
+          } else {
+            uni.showLoading({
+              title: "正在取消",
+            });
+            cancelCharge(
+              options.value && options.value.sn ? options.value.sn : ""
+            )
+              .then(() => {
+                // console.log(res)
+                uni.hideLoading();
+                uni.showToast({
+                  title: "已取消充电",
+                  icon: "none",
+                });
+                setTimeout(() => {
+                  uni.navigateBack();
+                }, 2000);
+              })
+              .catch(() => {
+                uni.hideLoading();
+                uni.showToast({
+                  title: "已取消充电",
+                  icon: "none",
+                });
+                setTimeout(() => {
+                  uni.navigateBack();
+                }, 2000);
+              });
+          }
+        }
+      }
+    },
+  });
+};
+const finish = () => {
+  uni.showLoading({
+    title: "正在结束中",
+  });
+  fetchChargeStatus()
+    .then((res) => {
+      if ([1, 2].includes(res.StartChargeSeqStat)) {
+        // 当前充电中
+        const start = new Date(res.StartTime.replace(/-/g, "/"));
+        const end = new Date();
+        const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
+        const min = parseInt(`${diff / 60}`);
+        const time =
+          min >= 60
+            ? `${parseInt(`${min / 60}`)}小时${parseInt(
+                `${min - parseInt(`${min / 60}`) * 60}`
+              )}分钟`
+            : `${parseInt(`${diff / 60}`)}分钟`;
+        const endFormat = format("y-M-d h:m:s");
+        const _chargeInfo = [
+          {
+            label: "累计充电量",
+            value: `${res.TotalPower}度`,
+          },
+          {
+            label: "累计费用",
+            value: `${res.ElecMoney}元`,
+          },
+          {
+            label: "开始时间",
+            value: res.StartTime,
+          },
+          {
+            label: "结束时间",
+            value: endFormat,
+          },
+          {
+            label: "累计用时",
+            value: time,
+          },
+        ];
+        if (res.Soc) {
+          _chargeInfo.unshift({
+            label: "剩余电量",
+            value: `${res.Soc}%`,
+          });
+        }
+        chargeInfo.value = _chargeInfo;
+        cancelCharge(options.value && options.value.sn ? options.value.sn : "")
+          .then(() => {
+            uni.hideLoading();
+            if (res.FailReason) {
+              uni.showModal({
+                content: res.FailReason,
+                showCancel: false,
+                success() {
+                  uni.navigateBack();
+                },
+              });
+              return;
+            }
+            step.value = 3;
+          })
+          .catch((err) => {
+            console.log(err);
+            uni.hideLoading();
+            uni.showToast({
+              title: "已取消充电",
+              icon: "none",
+            });
+            setTimeout(() => {
+              uni.navigateBack();
+            }, 2000);
+          });
+      }
+    })
+    .catch((err) => {
+      console.log(err);
+      uni.hideLoading();
+      uni.showModal({
+        title: "温馨提示",
+        content: "出现错误,请重试",
+        showCancel: false,
+        confirmText: "重试",
+        confirmColor: "#347DFF",
+        success: () => {
+          finish();
+        },
+      });
+    });
+};
+const setChargeData = (res: any) => {
+  const start = new Date(res.StartTime.replace(/-/g, "/"));
+  const end = new Date();
+  const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
+  const min = parseInt(`${diff / 60}`);
+  const time =
+    min >= 60
+      ? `${parseInt(`${min / 60}`)}小时${parseInt(
+          `${min - parseInt(`${min / 60}`) * 60}`
+        )}分钟`
+      : `${parseInt(`${diff / 60}`)}分钟`;
+  const _chargeInfo = [
+    {
+      label: "累计充电量",
+      value: `${res.TotalPower}度`,
+    },
+    {
+      label: "累计费用",
+      value: `${res.ElecMoney}元`,
+    },
+    {
+      label: "开始时间",
+      value: res.StartTime,
+    },
+    {
+      label: "累计用时",
+      value: time,
+    },
+  ];
+  if (res.Soc) {
+    _chargeInfo.unshift({
+      label: "剩余电量",
+      value: `${res.Soc}%`,
+    });
+  }
+  chargeInfo.value = _chargeInfo;
+};
+const fetchStatus = (data?: any) => {
+  if (status.value.cancel) {
+    return;
+  }
+  const promise = status ? Promise.resolve(data) : fetchChargeStatus();
+  promise
+    .then((res) => {
+      if ([1, 2].includes(res.StartChargeSeqStat)) {
+        // 当前充电中
+        status.value.start = true;
+        step.value = 2;
+        setChargeData(res);
+        startStatusTimer();
+      }
+    })
+    .catch((err) => {
+      console.log(err);
+      setTimeout(() => {
+        fetchStatus(data);
+      }, 1000);
+    });
+};
+const onImgLoad = () => {
+  fetchChargeStatus()
+    .then((res) => {
+      if ([1, 2].includes(res.StartChargeSeqStat)) {
+        if (options.value && options.value.sn === res.ConnectorID) {
+          fetchStatus(res);
+          return;
+        }
+        // 当前充电中
+        uni.showModal({
+          title: "温馨提示",
+          content: "当前有正在充电中的订单",
+          showCancel: false,
+          confirmText: "查看订单",
+          confirmColor: "#347DFF",
+          success: () => {
+            fetchStatus(res);
+          },
+        });
+      } else {
+        start();
+      }
+    })
+    .catch((err) => {
+      console.log(err);
+      start();
+    });
+};
+
+onLoad((_options: any) => {
+  options.value = _options;
+});
+onHide(() => {
+  timer && clearTimeout(timer);
+});
+onShow(() => {
+  startStartTimer();
+});
 </script>
 
-<style>
+<style lang="scss">
+.container {
+  height: 100vh;
+  width: 100%;
+  background-color: #fff;
+  position: relative;
+  overflow: hidden;
+
+  .bg {
+    width: 100%;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    transform-origin: center;
+  }
+
+  .body {
+    position: absolute;
+    left: 0;
+    top: 0;
+    height: 100%;
+    width: 100%;
+
+    .iphonex-placeholder {
+      box-sizing: content-box;
+      padding-bottom: constant(safe-area-inset-bottom);
+      padding-bottom: env(safe-area-inset-bottom);
+    }
+
+    .status {
+      width: 420rpx;
+      height: 420rpx;
+      border-radius: 50%;
+      position: relative;
+      margin: 0 auto;
+      margin-top: 130rpx;
+
+      .border {
+        width: 100%;
+        height: 100%;
+      }
+
+      .border-animation {
+        animation: rot 3s linear infinite;
+      }
+
+      .timer {
+        position: absolute;
+        width: 340rpx;
+        height: 340rpx;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%, -50%);
+        background: linear-gradient(180deg, #ffffff 0%, #ecf3ff 100%);
+        box-shadow: 0px 18rpx 30rpx rgba(0, 0, 0, 0.1);
+        border-radius: 50%;
+        .icon {
+          width: 152rpx;
+          height: 152rpx;
+        }
+      }
+    }
+  }
+}
+
+@keyframes rot {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  100% {
+    transform: rotate(360deg);
+  }
+}
 </style>

+ 69 - 2
src/pages-charge/orders/orders.vue

@@ -1,9 +1,76 @@
 <template>
-  <view></view>
+  <view
+    class="pl-30 pr-30"
+    v-if="infiniteScroller.list && infiniteScroller.list.length"
+  >
+    <view
+      class="item flex-align-center"
+      v-for="(item, index) in infiniteScroller.list"
+      :key="index"
+      @click="detail(index)"
+    >
+      <view>
+        <view class="fs-30 fw-500" key="title" duration="300">充电费用</view>
+        <view class="fs-24 mt-10" style="color: rgba(0, 0, 0, 0.4)">{{
+          item.transaction_time
+        }}</view>
+      </view>
+      <view class="ml-auto" style="text-align: right">
+        <view class="fs-36 fw-500">
+          <text>{{ item.amount }}</text>
+          <text class="fs-24 ml-6">元</text>
+        </view>
+      </view>
+      <view class="ml-20">
+        <uni-icons type="right" size="12" color="rgba(0,0,0,0.4)"></uni-icons>
+      </view>
+    </view>
+  </view>
+
+  <view
+    class="pt-40 flex-center fs-30"
+    style="color: rgba(0, 0, 0, 0.6)"
+    v-if="infiniteScroller.list && infiniteScroller.list.length <= 0"
+    >暂无数据</view
+  >
 </template>
 
 <script setup lang="ts">
+import { onLoad, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
+import { fetchWallet } from "../../api/user";
+import { useInfiniteScroll } from "../../utils/infinite-scroll";
+const infiniteScroller = useInfiniteScroll(10, (page) => {
+  return fetchWallet(3, page, 10).then((res: any) => {
+    if (res && res.length) {
+      res.forEach((item: any) => {
+        item.amount = (Number(item.amount) / 100).toFixed(2);
+      });
+    }
+    return res;
+  });
+});
+const detail = (index: number) => {
+  if (!infiniteScroller.list) {
+    return;
+  }
+  uni.navigateTo({
+    url: `/pages-charge/order/order?id=${infiniteScroller.list[index].transaction_id}`,
+  });
+};
+onLoad(() => {
+  infiniteScroller.refresh();
+});
+onPullDownRefresh(() => {
+  infiniteScroller.refresh();
+});
+onReachBottom(() => {
+  infiniteScroller.next();
+});
 </script>
 
-<style>
+<style lang="scss">
+.item {
+  height: 170rpx;
+  border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
+}
 </style>

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

@@ -88,7 +88,7 @@ const confirm = () => {
   const params = payValue.value
     ? Number(payValue.value)
     : payOptions.value[payOption.value];
-  if (params > 10000) {
+  if (params > 10000 || params <= 0) {
     uni.showModal({
       title: "温馨提示",
       content: "每次最大充值金额10000,请修改金额",

+ 17 - 3
src/pages.json

@@ -33,13 +33,27 @@
           }
         },
         {
-          "path": "order/order"
+          "path": "order/order",
+          "style": {
+            "navigationStyle": "default",
+            "navigationBarTitleText": "充电费用"
+          }
         },
         {
-          "path": "ordering/ordering"
+          "path": "ordering/ordering",
+          "style": {
+            "navigationBarTitleText": "充电",
+            "navigationStyle": "default",
+            "disableScroll": true
+          }
         },
         {
-          "path": "orders/orders"
+          "path": "orders/orders",
+          "style": {
+            "navigationStyle": "default",
+            "navigationBarTitleText": "充电订单",
+            "enablePullDownRefresh": true
+          }
         },
         {
           "path": "search/search"

+ 1 - 31
src/pages/index/index.vue

@@ -1,10 +1,5 @@
 <template>
-  <view class="content">
-    <image class="logo" src="/static/logo.png" />
-    <view class="text-area">
-      <text class="title">{{ title }}</text>
-    </view>
-  </view>
+  <view></view>
 </template>
 
 <script setup lang="ts">
@@ -13,29 +8,4 @@ const title = ref('Hello')
 </script>
 
 <style>
-.content {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-}
-
-.logo {
-  height: 200rpx;
-  width: 200rpx;
-  margin-top: 200rpx;
-  margin-left: auto;
-  margin-right: auto;
-  margin-bottom: 50rpx;
-}
-
-.text-area {
-  display: flex;
-  justify-content: center;
-}
-
-.title {
-  font-size: 36rpx;
-  color: #8f8f94;
-}
 </style>

+ 20 - 0
src/utils/date.ts

@@ -0,0 +1,20 @@
+export function format(format: string, time?: number) {
+  const now = time ? new Date(time) : new Date()
+  const map: Record<string, string | number> = {
+    y: now.getFullYear(),
+    M: now.getMonth() + 1,
+    d: now.getDate(),
+    h: now.getHours() > 9 ? now.getHours() : `0${now.getHours()}`,
+    m: now.getMinutes() > 9 ? now.getMinutes() : `0${now.getMinutes()}`,
+    s: now.getSeconds() > 9 ? now.getSeconds() : `0${now.getSeconds()}`
+  }
+  const res = []
+  for (let i = 0; i < format.length; i++) {
+    if (map[format.charAt(i)]) {
+      res.push(map[format.charAt(i)])
+    } else {
+      res.push(format.charAt(i))
+    }
+  }
+  return res.join('')
+}