瀏覽代碼

补充钱包相关页面

zuy 1 年之前
父節點
當前提交
3e1c144b66

文件差異過大導致無法顯示
+ 15 - 10
src/pages-order/detail/index.vue


+ 326 - 0
src/pages-user/wallet/index.vue

@@ -0,0 +1,326 @@
+<template>
+  <view class="page flex-column">
+    <view class="head flex-shrink">
+      <view>
+        <image class="bg" src="/static/wallet-banner.png"></image>
+        <view class="fg pt-48 pl-48 pr-48">
+          <view class="flex-align-center">
+            <uv-icon type="wallet" size="24" color="#ffffff"></uv-icon>
+            <view class="fs-36 color-fff fw-500 ml-16">我的钱包</view>
+            <view
+                class="btn flex-center fs-28 ml-auto"
+                @click="to(`/pages-user/wallet/refund`)">退款
+            </view>
+          </view>
+          <view class="price mt-50" v-if="user">
+            <text class="color-fff fw-600 fs-48" style="vertical-align: top">¥</text>
+            <text
+                class="color-fff fw-600 ml-12"
+                style="font-size: 46px; line-height: 46px">{{ user?.balance }}
+            </text>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="body flex-grow flex-column">
+      <view class="tabs flex-shrink flex-align-center">
+        <view
+            v-for="(item, index) in tabs"
+            :key="index"
+            class="flex-align-center fs-30 mr-60"
+            :style="{
+            color:
+              tab === item.value ? 'rgba(0, 0, 0, 1);' : 'rgba(0, 0, 0, 0.6);',
+          }"
+            @click="changeTab(index)">
+          {{ item.label }}
+          <view
+              :style="{ opacity: tab === item.value ? '1' : '0' }"
+              class="active"></view>
+        </view>
+      </view>
+      <view class="list pl-30 pr-30">
+        <view
+            class="item flex-align-center"
+            v-for="(item, index) in dataList"
+            :key="index"
+            @click="detail(index)">
+          <view>
+            <view class="fs-30 fw-500" key="title" duration="300">{{
+                typeMap[item.type - 1]
+              }}
+            </view>
+            <view v-if="item.type!==2">
+              <view class="fs-24" style="color: rgba(0, 0, 0, 0.4)">余额</view>
+            </view>
+            <view v-else>
+              <text class="refund-status" :style="refundStyle(item.status)">{{ refundLabel(item.status) }}</text>
+            </view>
+
+          </view>
+          <view class="ml-auto" style="text-align: right">
+            <view class="fs-30 fw-500">
+              <text>{{ item.type == 3 ? "- " : "" }}{{ item.amount }}</text>
+              <text class="fs-24 ml-6">元</text>
+            </view>
+            <view class="fs-24" style="color: rgba(0, 0, 0, 0.4)">
+              {{ item?.transactionTime }}
+            </view>
+          </view>
+          <view class="ml-32" v-if="item.type === 3">
+            <uv-icon type="right" size="12" color="rgba(0,0,0,0.4)"></uv-icon>
+          </view>
+          <view class="ml-32" v-else>&nbsp;&nbsp;&nbsp;</view>
+        </view>
+      </view>
+
+      <view style="height: 170rpx"></view>
+    </view>
+    <view>
+      <view class="pl-40 pr-40 pb-30 pt-30">
+        <uv-button
+            type="primary"
+            @click="to(`/pages-user/wallet-recharge/wallet-recharge`)">充值
+        </uv-button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import {onPullDownRefresh, onReachBottom, onShow} from "@dcloudio/uni-app";
+import {ref} from "vue";
+import {rpxToPx} from "@/utils/device";
+import {body, get} from "@/utils/https";
+
+const user = ref();
+
+const dataList = ref([])
+const tab = ref(0);
+const tabs = ref([
+  {
+    label: "全部",
+    value: 0,
+  },
+  {
+    label: "充值",
+    value: 1,
+  },
+  {
+    label: "消费",
+    value: 3,
+  },
+  {
+    label: "退款",
+    value: 2,
+  },
+]);
+
+const refundStatusMap = ref([
+  {label: '退款成功 ', value: 'SUCCESS', color: '#4cd964'},
+  {label: '退款关闭  ', value: 'CLOSED', color: '#000000'},
+  {label: '退款处理中 ', value: 'PROCESSING', color: '#007aff'},
+  {label: '退款异常  ', value: 'ABNORMAL', color: '#dd524d'},
+  {label: '退款已申请  ', value: 'NEW', color: '#f0ad4e'},
+])
+
+const typeMap = ref(["充值", "退款", "消费"]);
+/*const infiniteScroller = useInfiniteScroll(10, (page) => {
+  return fetchWallet(tab.value, page, 10).then((res: any) => {
+    if (res && res.length) {
+      res.forEach((item: any) => {
+        item.amount = (Number(item.amount) / 100).toFixed(2);
+      });
+    }
+    return res;
+  });
+});*/
+const scrollViewHeight = ref(0);
+
+onShow(() => {
+  // infiniteScroller.refresh();
+  loadData();
+  if (getApp<any>().globalData.user) {
+    user.value = getApp<any>().globalData.user;
+  }
+})
+
+onReachBottom(() => {
+  // if(tab.value!==2){
+  //   infiniteScroller.next();
+  // }
+  loadData();
+});
+
+onPullDownRefresh(() => {
+  // if(tab.value!==2){
+  //   infiniteScroller.refresh();
+  // }
+  loadData();
+});
+
+const to = (url: string) => {
+  uni.navigateTo({
+    url
+  })
+}
+
+const ready = (e: any) => {
+  scrollViewHeight.value =
+      getApp<any>().globalData.device.windowHeight -
+      (e.detail.navigationBarHeight + rpxToPx(380));
+};
+
+const refundLabel = (value: string) => {
+  return refundStatusMap.value.find((item: any) => item.value === value)?.label
+}
+
+const refundStyle = (value: string) => {
+  let rf = refundStatusMap.value.find((item: any) => item.value === value);
+  return setupLabelColorStyle(rf?.color)
+}
+
+const setupLabelColorStyle = (hex: string = "#000000", opacity = 0.2) => {
+  if (!hex) {
+    hex = "#000000"
+  }
+  hex = hex.replace("#", "");
+  // Convert the hex value to RGB values
+  const r = parseInt(hex.substring(0, 2), 16);
+  const g = parseInt(hex.substring(2, 4), 16);
+  const b = parseInt(hex.substring(4, 6), 16);
+
+  return {
+    /*    'text-shadow': `2px 2px 3px rgba(${r},${g},${b},${opacity})`,
+        'background-color': `rgba(${r},${g},${b},${opacity})`,*/
+    'color': `rgb(${r},${g},${b})`
+  }
+}
+
+const changeTab = (index: number) => {
+  tab.value = tabs.value[index].value;
+  loadData();
+}
+
+const loadData = () => {
+  if (!user) return;
+  if (tab.value !== 2) {
+    get(`user/wallet`).then((res: any) => {
+      if (res && res.length) {
+        res.forEach((item: any) => {
+          item.amount = (Number(item.amount) / 100).toFixed(2);
+        });
+      }
+      dataList.value = res;
+    })
+  } else {
+    body((`user/refundList`)).then((res: any) => {
+      console.log(res)
+      let list = (res.list || []).map(item => {
+        item.amount = (Number(item.refund) / 100).toFixed(2);
+        item.type = 2;
+        if (item.successTime) {
+          item.transactionTime = item.successTime.slice(0, 3).join("-") + " " + item.successTime.slice(3, 6).join(":")
+        }
+        return item;
+      })
+      dataList.value = list;
+    })
+  }
+}
+
+const detail = (index: number) => {
+  if (!dataList.value) {
+    return;
+  }
+  if (dataList.value[index].type === 3) {
+    uni.navigateTo({
+      url: `/pages-order/detail/index?id=${dataList.value[index]?.orderNo}`,
+    });
+  }
+};
+</script>
+
+<style lang="scss">
+.page {
+  height: 100vh;
+  width: 100vw;
+  background: linear-gradient(
+          180deg,
+          #e0ebff 0%,
+          rgba(255, 255, 255, 0) 60.13%
+  );
+
+  .head {
+    padding: 30rpx 40rpx;
+
+    & > view {
+      position: relative;
+      height: 320rpx;
+      border-radius: 40rpx;
+      overflow: hidden;
+
+      .bg {
+        height: 100%;
+        width: 100%;
+      }
+
+      .fg {
+        position: absolute;
+        left: 0px;
+        top: 0px;
+        height: 100%;
+        width: 100%;
+
+        .btn {
+          width: 120rpx;
+          height: 64rpx;
+          border-radius: 40rpx;
+          background: rgba(255, 255, 255, 0.3);
+          color: #076370;
+        }
+      }
+    }
+  }
+
+  .body {
+    .tabs {
+      padding: 0 40rpx;
+
+      & > view {
+        position: relative;
+        height: 72rpx;
+
+        .active {
+          position: absolute;
+          left: 50%;
+          bottom: 0px;
+          transform: translateX(-50%);
+          width: 40rpx;
+          height: 4rpx;
+          border-radius: 4rpx;
+          background-color: var(--color-primary);
+        }
+      }
+    }
+
+    .list {
+      .item {
+        height: 130rpx;
+        border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
+
+        &:last-child {
+          border-bottom: none;
+        }
+      }
+    }
+  }
+}
+
+.refund-status {
+  display: inline-block;
+  //padding: 8rpx;
+  font-size: 22rpx;
+  font-weight: 500;
+}
+</style>

+ 184 - 0
src/pages-user/wallet/refund.vue

@@ -0,0 +1,184 @@
+<template>
+  <view class="page">
+    <view class="block">
+      <view class="fs-28 color-000 fw-500">退款金额</view>
+      <view
+          class="pt-24 pb-24 box-content flex-inline"
+          style="border-bottom: 1rpx solid rgba(0, 0, 0, 0.1)">
+        <text style="font-size: 32px; line-height: 32px">¥</text>
+        <input
+            style="height: 46px; font-size: 46px; line-height: 46px"
+            class="fw-600 color-000 ml-18"
+            :value="value"
+            :disabled="true"/>
+      </view>
+      <view class="height-94 flex-align-center">
+        <view class="fs-28 color-999">可退金额 {{ user ? user.refundableAmount : 0 }} 元,优惠金额
+          {{ user ? user.discountAmount : 0 }} 元
+        </view>
+        <!-- <view class="fs-28 color-primary" @click="allRefund">全部提现</view> -->
+      </view>
+    </view>
+    <view class="block mt-16">
+      <view class="fs-28 color-000 fw-500 pb-8">退款原因</view>
+      <view class="pb-40 flex-wrap">
+        <view
+            :class="[
+            'reason',
+            'mt-16',
+            'mr-20',
+            'fs-26',
+            reason === i ? 'reason-active' : '',
+          ]"
+            v-for="(r, i) in reasons"
+            :key="i"
+            @click="changeReason(i)"
+        >{{ r }}
+        </view>
+      </view>
+    </view>
+    <view class="block mt-16 relative">
+      <view class="fs-28 color-000 fw-500 pb-16">退款说明</view>
+      <view class="pb-40 reason-text">
+        <textarea
+            class="fs-28"
+            style="height: 100%"
+            placeholder="请您详细填写申请说明"
+            maxlength="200"
+            @input="inputReasonText"></textarea>
+      </view>
+      <view class="reason-text-length lh-28 fs-28 color-999">{{ reasonText.length }}/200</view>
+    </view>
+    <view>
+      <view class="pl-40 pr-40 pb-30 pt-30" style="background-color: #fff">
+        <uv-button type="primary" @click="submit">提交申请</uv-button>
+      </view>
+    </view>
+
+    <uv-gap height="80" bgColor="#bbb"></uv-gap>
+
+    <uv-alert
+        v-if="success"
+        type="success"
+        :show-icon="true"
+        description="提交成功,预计需要5个工作日内审核完成请及时查收">
+      <view class="pt-64" style="width: 280rpx">
+        <uv-button height="80" type="primary" @click="close">完成</uv-button>
+      </view>
+    </uv-alert>
+  </view>
+</template>
+
+<script setup lang="ts">
+import {onLoad, onShow} from "@dcloudio/uni-app";
+import {ref} from "vue";
+import {body} from "@/utils/https";
+
+const user = ref();
+const value = ref(0);
+const reasonText = ref("");
+const reason = ref(-1);
+const reasons = ref([
+  "不常住该小区",
+  "充电故障",
+  "下次使用再充值",
+  "充电费用贵",
+  "很难充上电",
+  "其他原因",
+]);
+const success = ref(false);
+
+
+onShow(() => {
+  if (getApp<any>().globalData.user) {
+    user.value = getApp<any>().globalData.user;
+    allRefund();
+  }
+});
+
+const allRefund = () => {
+  if (user.value) {
+    value.value = Number(user.value.balance);
+  }
+};
+
+const changeReason = (i: number) => {
+  reason.value = i;
+};
+
+const inputReasonText = (e: any) => {
+  reasonText.value = e.detail.value;
+};
+
+
+const submit = () => {
+  uni.showLoading({
+    title: "提交中",
+  });
+  let r = "";
+  if (reason.value >= 0) {
+    r += `${reasons.value[reason.value]}`;
+    if (reasonText.value) {
+      r += "|";
+    }
+  }
+  if (reasonText.value) {
+    r += reasonText.value;
+  }
+  body(`refund/apply`, r).then((res: any) => {
+    uni.hideLoading();
+    success.value = true;
+  }).catch((err) => {
+    console.log(err);
+    uni.hideLoading();
+    uni.showModal({
+      content: err.errMsg,
+      showCancel: false
+    })
+  });
+};
+
+
+const close = () => {
+  success.value = false;
+  uni.navigateBack();
+};
+</script>
+
+<style lang="scss">
+.page {
+  height: 100vh;
+  background-color: #f6f7fa;
+  padding: 20rpx;
+
+  .block {
+    border-radius: 24rpx;
+    background: #fff;
+    padding: 40rpx 40rpx 0px 40rpx;
+  }
+
+  .reason {
+    color: #666;
+    background-color: #f7f7f7;
+    border-radius: 4rpx;
+    padding: 12rpx 16rpx;
+    border: 1px solid #f7f7f7;
+  }
+
+  .reason-active {
+    color: var(--color-primary);
+    border: 1px solid var(--color-primary);
+    background: rgba(52, 125, 255, 0.1);
+  }
+
+  .reason-text {
+    height: 224rpx;
+  }
+
+  .reason-text-length {
+    position: absolute;
+    top: 40rpx;
+    right: 40rpx;
+  }
+}
+</style>

+ 868 - 216
src/pages-wash/detail/index.vue

@@ -1,294 +1,946 @@
 <template>
-  <view class="pt-60 pb-20 flex-center">
-    <button
-        class="avatar"
-        open-type="chooseAvatar"
-        @chooseavatar="chooseAvatar"
-    >
-      <image class="avatar_image" :src="avatar" @error="errorHandle"></image>
-      <view class="avatar_text flex-center">编辑</view>
-    </button>
-  </view>
+  <view :class="['page']">
+    <view v-if="data && priceInfo">
+      <view class="block">
+        <view class="station">
+          <ChargeMachine
+              :title="'NO.' + data.equipment.shortId"
+              :list="data.equipment.connectorInfos"
+              :time="priceInfo.useTime"
+              :parkingNo="data.equipment.parkingNo"
+          ></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 ml-30 color-999 fs-32 fw-500">费用说明</view>-->
+      <view
+          class="mt-20 block height-96 flex-align-center pl-30 pr-30"
+          @click="openPriceDesc"
+      >
+        <view class="fs-28 color-000">充电费用</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>
+      <template v-if="appointmentData">
+        <view class="mt-40 color-999 fs-32 fw-500">已预约充电</view>
+        <view class="mt-20 block flex-align-center" style="height: 180rpx">
+          <image
+              class="width-56 ml-30"
+              src="/pages-charge/static/icon-alarm.png"
+              mode="widthFix"
+          ></image>
+          <view class="ml-24">
+            <view class="flex" v-if="appointmentCountDown">
+              <view class="fs-28 color-000">将在</view>
+              <view class="fs-44 lh-36 color-primary ml-8 mr-8">{{
+                  appointmentCountDown
+                }}
+              </view>
+              <view class="fs-28 color-000">后开始充电</view>
+            </view>
+            <view class="fs-24 color-999 mt-8"
+            >注意:启动充电前请勿拔插充电枪!
+            </view
+            >
+          </view>
+        </view>
+        <view class="mt-40 flex-center">
+          <view
+              class="flex-center height-68 br-68 fs-26 color-666"
+              style="width: 184rpx; border: 1px solid rgba(0, 0, 0, 0.3)"
+              @click="cancelAppointment"
+          >取消预约
+          </view
+          >
+        </view>
+      </template>
+      <template v-else>
+        <!--        <view class="mt-40 ml-30 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 lh-24 mt-16"
+                  style="color: #f8386a"
+                  v-if="chargeType === index"
+              >{{ type.checkTip }}
+              </view
+              >
+              <view class="fs-24 color-999 lh-24 mt-16" v-else>{{
+                  type.tip
+                }}
+              </view>
+            </view>
+            <view class="ml-auto">
+              <style-checkbox :checked="chargeType === index"></style-checkbox>
+            </view>
+          </view>
+        </view>
+        <view class="mt-20 block pl-30 pr-30" v-if="chargeType === 0">
+          <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">
+              <block v-if="chargeTime.isPowerSaving">
+                <view class="fs-28 color-333 fw-500 mr-16">00:00 - 08:00</view>
+              </block>
+              <block v-else>
+                <view
+                    v-if="chargeTime.format"
+                    class="fs-28 color-333 fw-500 mr-16"
+                >{{ chargeTime.format }}
+                </view
+                >
+                <view v-else class="fs-26 mr-16" style="color: #cacaca"
+                >请选择
+                </view
+                >
+                <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
+              </block>
+            </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">
+              <block v-if="chargeTime.isPowerSaving">
+                <view class="fs-28 color-333 fw-500"
+                >低至{{ priceInfo.minPrice }}元/kwh
+                </view
+                >
+              </block>
+              <block v-else>
+                <view
+                    v-if="chargeTime.formatPrice"
+                    class="fs-28 color-333 fw-500"
+                >{{ chargeTime.formatPrice }}
+                </view
+                >
+                <view v-else class="fs-26 mr-16" style="color: #cacaca"
+                >选择时间后显示
+                </view
+                >
+              </block>
+            </view>
+          </view>
+        </view>
+
+        <view
+            class="mt-20 block flex-align-center pl-30 pr-30 pt-30 pb-30"
+            @click="handleShowStationChargeCoupon"
+        >
+          <!--          to(-->
+          <!--          `/pages-charge/discount/discountV2?rightsId=${userRechargeRightsId}&couponId=${userCouponId}&stationId=${stationId}`-->
+          <!--          )-->
+          <view class="fs-28 lh-28 color-000 fw-500">充电服务费优惠</view>
+          <block v-if="checkedUserRightsOrCoupon&&checkedUserRightsOrCoupon.rightsId">
+            <view class="fs-28 lh-28 ml-auto" style="color: #f43636"
+            >{{ ((checkedUserRightsOrCoupon?.userRights?.discount || 0) / 10).toFixed(1) }}折
+            </view
+            >
+            <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
+          </block>
+          <block v-else-if="checkedUserRightsOrCoupon&&checkedUserRightsOrCoupon.couponId">
+            <view class="fs-28 lh-28 ml-auto" style="color: #f43636" v-if="checkedUserRightsOrCoupon.userCoupon.couponType=='FullDiscount'">
+              {{ ((checkedUserRightsOrCoupon?.userCoupon?.discount || 0) / 100).toFixed(2) }}元
+            </view>
+            <view class="fs-28 lh-28 ml-auto" style="color: #f43636" v-else>
+              {{ ((checkedUserRightsOrCoupon?.userCoupon?.discount || 0) / 10).toFixed(1) }}折
+            </view>
+
+            <!--            <view class="fs-28 lh-28 color-666 ml-8">权益待领取</view>-->
+            <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
+          </block>
+          <view v-else class="fs-28 lh-28 color-666 ml-auto">无</view>
+        </view>
+
+        <view
+            class="mt-20 block flex-align-center pl-30 pr-30 pt-30 pb-30"
+            v-if="chargeType === 0"
+        >
+          <view class="flex-column">
+            <view class="fs-28 lh-28 color-000 fw-500">省钱模式</view>
+            <view class="fs-24 lh-30 color-999 mt-8"
+            >处于00:00-08:00时,电费低至{{ priceInfo.minPrice }}元/kwh
+            </view
+            >
+          </view>
+          <view class="ml-auto">
+            <switch
+                :checked="chargeTime.isPowerSaving"
+                color="#2d9e95"
+                @change="changePowerSaving"
+            />
+          </view>
+        </view>
+      </template>
+      <view class="pt-60 pb-80"></view>
+      <style-bottom-view>
+        <view class="pt-20 pl-40 pr-40 pb-20 bg-fff flex">
+          <template v-if="appointmentData">
+            <view class="mr-10" style="width: 50%">
+              <style-button size="small" @click="selectTime"
+              >修改时间
+              </style-button
+              >
+            </view
+            >
+            <view class="ml-10" style="width: 50%">
+              <style-button type="primary" size="small" @click="submitNow"
+              >立即开始
+              </style-button
+              >
+            </view>
+          </template>
+          <template v-else>
+            <style-button v-if="balance>0" type="primary" size="small" @click="submit">{{
+                chargeType === 0 ? "提交预约" : "立即充电"
+              }}
+            </style-button>
 
-  <view class="pl-50 pr-50">
-    <view
-        class="menu flex-align-center flex-between"
-        v-for="(item, index) in state.menuList"
-        :key="index"
-        @click="edit(index)"
+            <style-button v-else type="primary" size="small" @click="to('/pages-user/wallet-recharge/wallet-recharge')">
+              余额不足,请充值
+            </style-button>
+          </template>
+
+        </view>
+      </style-bottom-view>
+    </view>
+    <style-dialog
+        v-if="chargeTimeDialog"
+        @close="closeTime"
+        :title="
+        '请选择' + (chargeTime.isPowerSaving ? '省电模式' : '') + '充电时间'
+      "
     >
-      <view class="fs-30">{{ item.title }}</view>
-      <view class="flex">
+      <view class="flex" style="border-top: 1rpx solid rgba(0, 0, 0, 0.1)">
         <view
-            :class="['fs-30', 'fw-500', `mr-${item.disabled ? '0' : '20'}`]"
-            style="color: rgba(0, 0, 0, 0.8)"
-        >{{ item.value }}
-        </view
+            class="flex-shrink"
+            style="height: 840rpx; width: 256rpx; background-color: #f0f0f0"
+        >
+          <view
+              class="flex-align-center pl-32 height-90"
+              v-for="(day, index) in timesInfo.day"
+              :key="index"
+              :style="{
+              backgroundColor:
+                day.format === chargeTime.day ? '#fff' : 'transparent',
+            }"
+              @click="changeDay(index)"
+          >
+            <view
+                :class="[
+                'fs-32',
+                'fw-500',
+                day.format === chargeTime.day ? 'color-primary' : 'color-000',
+              ]"
+            >{{ day.format }}
+            </view
+            >
+            <view
+                class="fs-24 color-666 ml-12 br-8 width-60 height-40 flex-center"
+                style="background-color: rgba(0, 0, 0, 0.07)"
+                v-if="day.year && day.format !== chargeTime.day"
+            >{{ day.year }}年
+            </view
+            >
+          </view>
+        </view>
+        <scroll-view
+            scroll-y
+            class="flex-grow"
+            style="height: 840rpx"
+            :scroll-into-view="chargeTime.scrollId"
         >
-        <uni-icons
-            type="right"
-            size="12"
-            color="rgba(0,0,0,0.4)"
-            v-if="!item.disabled"
-        ></uni-icons>
+          <view
+              :id="`hour-${hour.format}`"
+              class="flex-align-center pl-40 height-90 fs-32"
+              v-for="(hour, index) in timesInfo.hour"
+              :key="index"
+              :style="{
+              color: hour.disabled
+                ? '#999'
+                : hour.format === chargeTime.hour
+                ? 'var(--color-primary)'
+                : '#000',
+            }"
+              @click="changeHour(index)"
+          >
+            {{ hour.format }}
+          </view>
+          <view class="hour-placeholder"></view>
+        </scroll-view>
       </view>
-    </view>
-  </view>
+    </style-dialog>
+    <PriceDesc
+        v-if="priceDescVisible"
+        :desc="priceInfo.policyInfos || []"
+        @close="closePriceDesc"
+    ></PriceDesc>
 
-  <style-bottom-view>
-    <view class="pl-60 pr-60 pb-40">
-      <style-button type="primary" @click="logoutUser">退出登录</style-button>
-    </view>
-  </style-bottom-view>
+    <StationChargeCoupon ref="station_charge_coupon_ref" @on-change="handleCheckedUserRightsAndCoupon"></StationChargeCoupon>
+  </view>
 </template>
 
 <script setup lang="ts">
-import {onHide, onShow} from "@dcloudio/uni-app";
+import {cancelAppointmentCharge, changeAppointmentTime, fetchChargeStatus, fetchStationByConnectorIdOrShortId, fetchStationPriceDesc, startAppointmentCharge, startCharge,} from "@/api/charge";
+import {fetchProfile, listStationAvailableRightsAndCoupons} from "@/api/user";
+import {onHide, onLoad, onShow} from "@dcloudio/uni-app";
 import {reactive, ref} from "vue";
-import {body, get, upload} from "@/utils/https"
+import ChargeMachine from "../machines/charge-machine/charge-machine.vue";
+import PriceDesc from "../machines/price-desc/price-desc.vue";
+import StationChargeCoupon from "@/components/station-charge-coupon/station-charge-coupon.vue";
+import {format} from "@/utils/date";
+import {redirect, to} from "../../utils/navigate";
 
-const avatar = ref<string>();
-const menu = ref<any[]>([]);
-const MENU_TEMPLATE = [
-  {
-    title: "昵称",
-    key: "nickname",
-    value: "",
-  },
-  {
-    title: "电话",
-    key: "",
-    disabled: true,
-  },
+
+const DAY = 24 * 60 * 60 * 1000;
+const options = ref<any>();
+const data = ref<any>();
+const priceInfo = ref();
+const priceDescVisible = ref(false);
+const timesInfo = ref<any>({
+  day: [],
+  time: [],
+});
+
+
+const checkedUserRightsOrCoupon = ref<any>({
+  rightsId: null,
+  userRechargeRightsId: null,
+  couponId: null,
+  userCouponId: null,
+  stationId: null,
+  userRights: null,
+  userCoupon: null
+});
+
+const station_charge_coupon_ref = ref();
+
+const chargeType = ref(1);
+const chargeTypes = ref([
   {
-    title: "车牌号",
-    key: "defaultPlateNo",
-    value: "",
+    title: "预约充电",
+    tip: "预约特定时间点开始充电",
+    checkTip: "部分车辆可能进入休眠状态无法唤醒,将导致充电失败",
   },
   {
-    title: "VIN码",
-    key: "vin",
-    value: "",
+    title: "立即充电",
   },
-  // {
-  //   title: "充电卡",
-  //   key: "",
-  //   value: "",
-  // },
-];
+]);
+const chargeTimeDialog = ref(false);
+const chargeTime = ref({
+  time: 0, // 时间戳
+  day: "",
+  hour: "",
+  format: "",
+  formatPrice: "",
+  scrollId: "",
+  isPowerSaving: false, // 省电模式
+});
+const appointmentData = ref();
+const appointmentCountDown = ref();
+
+const discountIndex = ref(-1);
+const discountList = ref<any[]>([]);
+const activity = ref();
+
+const stationId = ref();
+const balance = ref(0);
+
 
 
 const initState = () => ({
-  user: {},
-  menuList: []
+  couponList: [] as any[],
 })
 
 const state = reactive(initState())
 
-const refresh = () => {
-  const _menu = [...MENU_TEMPLATE];
-  get(`user/profile`).then(res => {
-    getApp<any>().globalData.user = res;
-  })
-  // fetchProfile().then(() => {
-  //   const user = getApp<any>().globalData.user;
-  //   if (user) {
-  //     _menu[0].value = user.nickname;
-  //     _menu[1].value = user.mobilePhone;
-  //     _menu[2].value = user.defaultPlateNo;
-  //     _menu[3].value = user.vin;
-  //     // _menu[4].value = user.card_no;
-  //     avatar.value =
-  //         user.avatar ||
-  //         "https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0";
-  //     menu.value = _menu;
-  //   }
-  // });
+onHide(() => {
+  Object.assign(state, initState());
+})
+
+
+
+const refreshUserBalance = () => {
+  let user = getApp<any>().globalData.user
+  console.log(user)
+  balance.value = user?.balance || 0;
+}
+
+const handleShowStationChargeCoupon = () => {
+  let {rightsId, couponId} = checkedUserRightsOrCoupon.value;
+  station_charge_coupon_ref.value?.open(stationId.value, rightsId, couponId);
+}
+
+const handleCheckedUserRightsAndCoupon = (data: any) => {
+  // console.log("handleCheckedUserRightsAndCoupon", data);
+  checkedUserRightsOrCoupon.value = data;
+}
+
+const changeType = (index: number) => {
+  chargeType.value = index;
 };
 
-const save = (form: Record<string, any>) => {
-  uni.showLoading({
-    title: "保存中",
-  });
-  return body(`user/updateProfile`, form)
-      .then((res) => {
-        uni.hideLoading();
-        uni.showToast({
-          icon: "success",
-          title: "保存成功",
-        });
-        refresh();
-        return res;
-      })
-      .catch((err) => {
-        uni.hideLoading();
-        uni.showModal({
-          content: `${err.errMsg},请重试`,
-        });
-      });
+const isPassTime = (hour: string, day: number) => {
+  const now = new Date();
+  const time = new Date(format("y/M/d", day) + " " + hour + ":00");
+  return now > time;
 };
 
-const chooseAvatar = (e: any) => {
-  if (e.detail.avatarUrl) {
-    uni.showLoading({
-      title: "上传中",
-    });
-    let params = {
-      url: `file/upload`,
-      filePath: e.detail.avatarUrl,
-      success: (res) => {
-        body(`user/updateAvatar`, {
-          avatar: res.url,
-        })
-            .then(() => {
-              uni.hideLoading();
-              uni.showToast({
-                title: "已更新",
-                icon: "success",
-              });
-              avatar.value = res.url;
-            })
-            .catch((err) => {
-              uni.hideLoading();
-              uni.showModal({
-                content: `${err.errMsg},请重试`,
-              });
-            });
-      }
+const getHourPrice = (hour: string) => {
+  let price = "";
+  priceInfo.value.policyInfos.forEach((item: any) => {
+    if (Number(hour) >= Number(item.startTime.substring(0, 2))) {
+      price = item.totalPrice;
     }
-    upload(params)
-  } else {
-    uni.showModal({
-      content: `${e.detail.errMsg},请重试`,
+  });
+  return price;
+};
+
+const rebuildHours = (day: Date) => {
+  timesInfo.value.hour = [];
+  const nowHour = format("h");
+  const isToday = format("d") === format("d", day.getTime());
+  // 当前时间的24小时内
+  let startHour = isToday ? Number(nowHour) : 0;
+  let endHour = isToday ? 24 : Number(nowHour) + 1;
+  if (chargeTime.value.isPowerSaving) {
+    startHour = 0;
+    endHour = 8;
+  }
+  for (let index = startHour; index < endHour; index++) {
+    timesInfo.value.hour = [...timesInfo.value.hour, ...buildHours(index, day)];
+  }
+};
+
+const buildHours = (hour: number, day: Date) => {
+  let res: any[] = [];
+  let hourTemp = hour >= 10 ? `${hour}` : `0${hour}`;
+  let priceTemp = getHourPrice(hourTemp);
+  for (let index = 0; index < 12; index++) {
+    res.push({
+      format: `${hourTemp}:${index * 5 >= 10 ? index * 5 : `0${index * 5}`}`,
+      formatPrice: priceTemp,
+      disabled: isPassTime(
+          `${hourTemp}:${index * 5 >= 10 ? index * 5 : `0${index * 5}`}`,
+          chargeTime.value.time ? chargeTime.value.time : day.getTime()
+      ),
     });
   }
+  return res;
 };
-const edit = (index: number) => {
-  const menuItem = menu.value[index];
-  if (menuItem.disabled) {
+
+const selectTime = () => {
+  if (chargeTime.value.isPowerSaving) {
+    uni.showToast({
+      title: "省电模式将在00:00-08:00进行充电",
+      icon: "none",
+    });
     return;
   }
-  if (!menuItem.key) {
+  if (!appointmentData.value && chargeType.value === 1) {
     uni.showToast({
+      title: "请选择预约模式",
       icon: "none",
-      title: "暂不支持修改",
     });
     return;
   }
-  if (/车牌/.test(menuItem.title)) {
-    uni.chooseLicensePlate({
-      success: (res) => {
-        save({
-          defaultPlateNo: res.plateNumber,
-        });
-      },
-      fail: (err) => {
-        console.log(err);
+  const now = appointmentData.value
+      ? new Date(appointmentData.value.startTime.replace(/-/g, "/"))
+      : new Date();
+  const year = format("y", now.getTime());
+  const nextyear = format("y", now.getTime() + DAY);
+
+  // 天
+  timesInfo.value.day = [
+    {
+      time: now.getTime(),
+      format: format("M月d日", now.getTime()),
+      year: "",
+    },
+    {
+      time: now.getTime() + DAY,
+      format: format("M月d日", now.getTime() + DAY),
+      year: year === nextyear ? "" : nextyear.slice(2),
+    },
+  ];
+  rebuildHours(now);
+
+  // 预约时间
+  if (!appointmentData.value) {
+    if (!chargeTime.value.day) {
+      chargeTime.value.time = timesInfo.value.day[0].time;
+      chargeTime.value.day = timesInfo.value.day[0].format;
+    }
+    // 滚动到特定位置
+    if (chargeTime.value.hour) {
+      chargeTime.value.scrollId = `hour-${chargeTime.value.hour}`;
+    } else {
+      let hourNow = now.getHours();
+      chargeTime.value.scrollId = `hour-${hourNow}:00`;
+    }
+  }
+  // 修改时间
+  if (appointmentData.value) {
+    chargeTime.value.time = timesInfo.value.day[0].time;
+    chargeTime.value.day = timesInfo.value.day[0].format;
+    const findIndex = timesInfo.value.hour.findIndex(
+        (item: any) => item.format === format("h:m:s", now.getTime())
+    );
+    if (findIndex >= 0) {
+      chargeTime.value.hour = timesInfo.value.hour[findIndex].format;
+      chargeTime.value.formatPrice =
+          timesInfo.value.hour[findIndex].formatPrice;
+    }
+  }
+  chargeTimeDialog.value = true;
+};
+
+const changeDay = (index: number) => {
+  chargeTime.value.time = timesInfo.value.day[index].time;
+  chargeTime.value.day = timesInfo.value.day[index].format;
+  if (chargeTime.value.hour) {
+    chargeTime.value.hour = "";
+    chargeTime.value.formatPrice = "";
+  }
+  const now = new Date(chargeTime.value.time);
+  rebuildHours(now);
+  setTimeout(() => {
+    chargeTime.value.scrollId = `hour-${timesInfo.value.hour[0].format}`;
+  }, 0);
+};
+
+const changeHour = (index: number) => {
+  if (timesInfo.value.hour[index].disabled) {
+    uni.showToast({
+      title: "无法选择过去的时间",
+      icon: "none",
+    });
+    return;
+  }
+  if (appointmentData.value) {
+    // 修改时间
+    uni.showModal({
+      title: "温馨提示",
+      content: `确认修改为${timesInfo.value.hour[index].format}吗?`,
+      confirmText: "确定",
+      confirmColor: "#2d9e95",
+      success(modal) {
+        if (modal.confirm) {
+          uni.showLoading({
+            title: "提交中",
+            mask: true,
+          });
+          changeAppointmentTime(
+              appointmentData.value.startChargeSeq,
+              `${format("y-M-d", chargeTime.value.time)} ${
+                  timesInfo.value.hour[index].format
+              }:00`
+          )
+              .then(() => {
+                return fetchChargeStatus();
+              })
+              .then((res) => {
+                uni.hideLoading();
+                if (res && res.isAppointment) {
+                  uni.showToast({
+                    icon: "success",
+                    title: "修改成功",
+                  });
+                  closeTime();
+                  appointmentData.value = res;
+                  startAppointmentCountDown();
+                }
+              });
+        }
       },
     });
     return;
   }
-  uni.navigateTo({
-    url: `/pages-user/profile-edit/profile-edit?key=${menuItem.key}&title=${
-        menuItem.title
-    }${menuItem.value ? `&value=${encodeURIComponent(menuItem.value)}` : ""}`,
-  });
+  chargeTime.value.hour = timesInfo.value.hour[index].format;
+  chargeTime.value.format = `${format("y年M月d日", chargeTime.value.time)} ${
+      chargeTime.value.hour
+  }`;
+  chargeTime.value.formatPrice = timesInfo.value.hour[index].formatPrice;
+  closeTime();
+};
+
+const changePowerSaving = (e: any) => {
+  chargeTime.value = {
+    time: 0, // 时间戳
+    day: "",
+    hour: "",
+    isPowerSaving: e.detail.value,
+    format: "",
+    formatPrice: "",
+    scrollId: "",
+  };
+};
+
+const closeTime = () => {
+  chargeTimeDialog.value = false;
+  if (appointmentData.value) {
+    return;
+  }
+  if (!chargeTime.value.hour) {
+    chargeTime.value = {
+      time: 0, // 时间戳
+      day: "",
+      hour: "",
+      isPowerSaving: chargeTime.value.isPowerSaving, // 省电模式
+      format: "",
+      formatPrice: "",
+      scrollId: "",
+    };
+  }
+};
+
+const openPriceDesc = () => {
+  priceDescVisible.value = true;
+};
+
+const closePriceDesc = () => {
+  priceDescVisible.value = false;
 };
 
-const logoutUser = () => {
+const cancelAppointment = () => {
   uni.showModal({
-    title: "温馨提示",
-    content: "确定退出登录吗?",
+    title: "取消预约",
+    content: "确定取消预约吗?",
+    cancelText: "取消预约",
+    cancelColor: "#999999",
+    confirmText: "点错了",
     confirmColor: "#2d9e95",
-    confirmText: "确定退出",
-    cancelText: "手滑了",
     success: (res) => {
-      if (res.confirm) {
+      if (res.cancel) {
         uni.showLoading({
-          title: "退出中",
+          title: "提交中",
+          mask: true,
         });
-        logout()
+        cancelAppointmentCharge()
             .then(() => {
               uni.hideLoading();
               uni.showToast({
                 icon: "success",
-                title: "已退出",
+                title: "已取消",
               });
-              clearToken();
-              setTimeout(() => {
-                uni.reLaunch({
-                  url: "/pages/map/map",
-                });
-              }, 1500);
+              appointmentData.value = undefined;
+              startAppointmentCountDown();
             })
             .catch((err) => {
               uni.hideLoading();
-              uni.showModal({
-                content: `${err.errMsg},请重试`,
-              });
             });
       }
     },
   });
 };
 
-const errorHandle = (e: any) => {
-  console.log(e);
+let appointmentCountDownTimer = 0;
+
+const startAppointmentCountDown = () => {
+  appointmentCountDownTimer && clearTimeout(appointmentCountDownTimer);
+  if (!appointmentData.value) {
+    appointmentCountDown.value = "";
+    return;
+  }
+  appointmentCountDown.value = formatAppointmentCountDown(
+      appointmentData.value.startTime
+  );
+  if (appointmentCountDown.value) {
+    appointmentCountDownTimer = setTimeout(() => {
+      startAppointmentCountDown();
+    }, 1000);
+  } else {
+    uni.showModal({
+      content: "当前预约时间已到,将开始进行充电",
+      showCancel: false,
+      success() {
+        uni.switchTab({
+          url: "/pages/map/map",
+        });
+      },
+    });
+  }
+};
+
+const formatAppointmentCountDown = (time: string) => {
+  const now = new Date();
+  const date = new Date(time.replace(/-/g, "/"));
+  let secondTime = parseInt(`${(date.getTime() - now.getTime()) / 1000}`);
+  if (secondTime <= 0) {
+    return "";
+  }
+  let hourTime = 0;
+  let minuteTime = 0; // 分
+  if (secondTime >= 60) {
+    minuteTime = parseInt(`${secondTime / 60}`);
+    secondTime = parseInt(`${secondTime % 60}`);
+    if (minuteTime >= 60) {
+      hourTime = parseInt(`${minuteTime / 60}`);
+      minuteTime = parseInt(`${minuteTime % 60}`);
+    }
+  }
+  return `${hourTime < 10 ? "0" + hourTime : hourTime}:${
+      minuteTime < 10 ? "0" + minuteTime : minuteTime
+  }:${secondTime < 10 ? "0" + secondTime : secondTime}`;
 };
 
-onShow(() => {
-  if (getApp<any>().globalData.lastData.profile) {
-    const {key, value} = getApp<any>().globalData.lastData.profile;
-    save({
-      [key]: value,
-    }).then(() => {
-      getApp<any>().globalData.lastData.profile = undefined;
+const submit = () => {
+  if (
+      chargeType.value === 0 &&
+      !chargeTime.value.isPowerSaving &&
+      !chargeTime.value.hour
+  ) {
+    uni.showToast({
+      title: "请选择充电时间",
+      icon: "none",
     });
+    return;
+  }
+  const now = new Date();
+  let startTime = chargeTime.value.time;
+  if (chargeTime.value.isPowerSaving) {
+    startTime = now.getTime();
+    // 省电模式
+    if (
+        now.getHours() >= 8 &&
+        now.getDate() === Number(format("d", chargeTime.value.time))
+    ) {
+      startTime += DAY;
+    }
+  }
+  let query = "";
+  if (chargeType.value === 0) {
+    query += "isBooking=true";
+    query += `&startTime=${format("y-M-d", startTime)} ${
+        chargeTime.value.isPowerSaving
+            ? "00:00:00"
+            : `${chargeTime.value.hour}:00`
+    }`;
+    if (chargeTime.value.isPowerSaving) {
+      query += `&endTime=${format("y-M-d", startTime)} 08:00:00`;
+    }
   }
+  if (chargeType.value === 1) {
+    query += "isBooking=false";
+  }
+  let {userRechargeRightsId, userCouponId} = checkedUserRightsOrCoupon.value;
+  console.log("userRechargeRightsId")
+  if (userRechargeRightsId && userRechargeRightsId >= 0) {
+    query += `&userRechargeRightsId=${userRechargeRightsId}`;
+  }
+  if (userCouponId && userCouponId >= 0) {
+    query += `&userCouponId=${userCouponId}`;
+  }
+  console.log(query)
+  uni.showLoading({
+    title: "提交中",
+    mask: true,
+  });
+  startCharge(options.value.sn, query)
+      .then(() => {
+        if (chargeType.value === 0) {
+          fetchChargeStatus()
+              .then((res) => {
+                if (res && res.isAppointment) {
+                  uni.hideLoading();
+                  uni.showToast({
+                    icon: "success",
+                    title: "预约成功",
+                  });
+                  appointmentData.value = res;
+                  startAppointmentCountDown();
+                }
+              })
+              .catch(() => {
+                uni.navigateBack();
+              });
+        }
+        if (chargeType.value === 1) {
+          redirect(`/pages-charge/ordering/ordering?sn=${options.value.sn}&start=1`)
+        }
+      })
+      .catch((err) => {
+        uni.hideLoading();
+        uni.showModal({
+          content: `${err.errMsg}`,
+          showCancel: false,
+        });
+      });
+};
+
+const submitNow = () => {
+  uni.showLoading({
+    title: "提交中",
+    mask: true,
+  });
+  startAppointmentCharge(options.value.sn)
+      .then(() => {
+        _checkStartCharge();
+      })
+      .catch((err) => {
+        uni.hideLoading();
+        uni.showModal({
+          content: `${err.errMsg}`,
+          showCancel: false,
+        });
+      });
+};
+
+const _checkStartCharge = () => {
+  fetchChargeStatus().then((res) => {
+    if (res && [2].includes(res.chargeStatus)) {
+      uni.hideLoading();
+      redirect(`/pages-charge/ordering/ordering?sn=${options.value.sn}&start=1`)
+    } else {
+      _checkStartCharge();
+    }
+  });
+};
+
+/**
+ * 设置当前用户在该站点的默认充电权益或优惠券
+ */
+const fetchUserStationDefaultRightsAndCoupon = () => {
+  listStationAvailableRightsAndCoupons(stationId.value).then((res) => {
+    let {userRechargeRightsList, userCouponList} = res;
+    let couponList = (userCouponList || []).sort((a: any, b: any) => new Date(a.endTime).getTime() - new Date(b.endTime).getTime());
+    let rightsList = (userRechargeRightsList || []).sort((a: any, b: any) => new Date(a.endTime).getTime() - new Date(b.endTime).getTime());
+    checkedUserRightsOrCoupon.value.stationId = stationId.value;
+    if (rightsList && rightsList.length > 0) {
+      checkedUserRightsOrCoupon.value.userRights = rightsList[0];
+      checkedUserRightsOrCoupon.value.rightsId = rightsList[0].rightsId;
+      checkedUserRightsOrCoupon.value.userRechargeRightsId = rightsList[0].id;
+    } else if (couponList && couponList.length > 0) {
+      checkedUserRightsOrCoupon.value.userCoupon = couponList[0];
+      checkedUserRightsOrCoupon.value.couponId = couponList[0].couponId;
+      checkedUserRightsOrCoupon.value.userCouponId = couponList[0].id;
+    }
+    console.log(res)
+  });
+}
+
+onLoad((_options: any) => {
+  // sn=SN100523042860091 测试环境
+  console.log("options", _options);
+  let sn = _options.sn;
+  uni.showLoading({
+    title: "加载中",
+  });
+  fetchStationByConnectorIdOrShortId(sn)
+      .then((res) => {
+        console.log(res);
+        let {station, equipment} = res;
+        if (station) {
+          stationId.value = station.StationID;
+          fetchUserStationDefaultRightsAndCoupon();
+        }
+        if (equipment && equipment.connectorInfos && equipment.connectorInfos.length) {
+          sn = equipment.connectorInfos[0].connectorId;
+          _options.sn = equipment.connectorInfos[0].connectorId;
+        }
+        options.value = _options;
+        data.value = res;
+        return fetchStationPriceDesc(sn);
+      })
+      .then((res) => {
+        // console.log(res);
+        uni.hideLoading();
+        priceInfo.value = res;
+        return fetchChargeStatus(false, true);
+      })
+      .then((res) => {
+        if (res && res.isAppointment) {
+          appointmentData.value = res;
+          startAppointmentCountDown();
+        }
+      })
+      .catch((err) => {
+        console.log(err);
+        uni.hideLoading();
+      });
+
+
+  fetchProfile().then((res) => {
+    if (res.userRechargeRightsList.length) {
+      res.userRechargeRightsList.forEach((item: any) => {
+        item.discountFormat = Number((Number(item.discount) / 10).toFixed(2));
+      });
+      discountList.value = res.userRechargeRightsList;
+      discountIndex.value = 0;
+    }
+
+    if (res.activityList && res.activityList.length) {
+      res.activityList[0].rechargeRightsList.sort((a: any, b: any) => {
+        return a.discount - b.discount;
+      });
+      activity.value = res.activityList[0];
+      activity.value.minDiscount = Number(
+          (
+              Number(res.activityList[0].rechargeRightsList[0]?.discount) / 10
+          ).toFixed(1)
+      );
+    }
+
+    refreshUserBalance();
+  });
+  refreshUserBalance();
 });
 
-onHide(() => {
-  Object.assign(state, initState());
-})
+onShow(() => {
+  if (getApp<any>().globalData.lastData.discount) {
+    discountIndex.value = getApp<any>().globalData.lastData.discount.index;
+    getApp<any>().globalData.lastData.discount = undefined;
+  }
+  refreshUserBalance();
+});
 </script>
 
-<style lang="scss">
-.avatar {
-  position: relative;
-  height: 116rpx !important;
-  width: 116rpx !important;
-  border-radius: 50%;
-  border: 2rpx solid rgba(0, 0, 0, 0.15);
-  overflow: hidden;
-
-  &_image {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    left: 0;
-    top: 0;
-    border-radius: 50%;
-  }
+<style lang="scss" scoped>
+.page {
+  min-height: 100vh;
+  width: 100%;
+  padding: 20rpx;
+  background-color: #f6f7fa;
+}
 
-  &_text {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-    height: 40rpx;
-    background: rgba(0, 0, 0, 0.5);
-    color: #fff;
-    font-size: 24rpx;
-  }
+.block {
+  border-radius: 20rpx;
+  background: #fff;
 }
 
-.menu {
-  background-color: #fff;
-  height: 120rpx;
+.station {
+  height: 148rpx;
   border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
+}
 
-  &:last-child {
-    border-bottom: none;
-  }
+.hour-placeholder {
+  box-sizing: content-box;
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
 }
 </style>

+ 16 - 0
src/pages.json

@@ -78,6 +78,22 @@
             "navigationBarTitleText": "纠错上报",
             "navigationBarBackgroundColor": "#ffffff"
           }
+        },
+        {
+          "path": "wallet/index",
+          "style": {
+            "navigationStyle": "default",
+            "navigationBarTitleText": "我的钱包",
+            "navigationBarBackgroundColor": "#ffffff"
+          }
+        },
+        {
+          "path": "wallet/refund",
+          "style": {
+            "navigationStyle": "default",
+            "navigationBarTitleText": "我的退款",
+            "navigationBarBackgroundColor": "#ffffff"
+          }
         }
       ]
     }

+ 93 - 0
src/pages/coupon/fetch.vue

@@ -0,0 +1,93 @@
+<template>
+  <view>
+    <uv-popup ref="popup_ref" mode="bottom" custom-style="min-height: 800rpx;padding:15rpx;" closeable round="10">
+      <view class="w100 flex-center">优惠领取</view>
+      <view class="coupon-list">
+        <view class="coupon-card" v-for="item in state.couponList" :key="item.id">
+
+          <view class="coupon-card-left">
+            <view class="card-content">
+              <view class="amount-content">
+                <view class="amount">
+                  <view class="price">{{ item.discountPrice }}</view>
+                  {{ item.unit }}
+                </view>
+                <view class="amount-text">
+                  {{ item.couponAmountText }}
+                </view>
+              </view>
+              <view class="coupon-info">
+                <view class="info-name">
+                  {{ item.couponName }}
+                </view>
+                <view class="info-time">
+                  {{ item.couponTime }}
+                </view>
+              </view>
+            </view>
+
+            <view class="coupon-desc">
+              {{ item.couponDesc }}
+            </view>
+          </view>
+          <view class="coupon-card-right">
+            <view class="ml10">
+              <uv-button plain type="error" size="mini" class="ml10" style="margin-left: 10px;" @click="handleFetchCoupon(item)">领取</uv-button>
+            </view>
+          </view>
+
+        </view>
+        <uv-empty v-if="state.couponList.length==0" mode="coupon" text="无可领取的优惠券"></uv-empty>
+      </view>
+    </uv-popup>
+
+  </view>
+</template>
+<script setup lang="ts">
+import {reactive, ref} from "vue";
+import {onHide} from "@dcloudio/uni-app";
+import {body, get} from "@/utils/https";
+
+const popup_ref = ref()
+
+const initState = () => ({
+  couponList: [] as any[],
+})
+
+const state = reactive(initState())
+
+onHide(() => {
+  Object.assign(state, initState());
+})
+
+/**
+ * 获取优惠券列表
+ */
+const loadFetchCouponList = () => {
+  body(`coupon/available`).then((res: any) => {
+    state.couponList = res.list;
+  })
+}
+
+const handleFetchCoupon = async (coupon: any) => {
+  let res = await get(`coupon/fetch`, coupon)
+}
+
+const close = () => {
+  Object.assign(state, initState());
+  popup_ref.value.close()
+}
+
+const open = () => {
+  popup_ref.value.open();
+  loadFetchCouponList();
+}
+
+defineExpose({
+  open
+})
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 5 - 7
src/pages/user/index.vue

@@ -9,8 +9,7 @@
           <image
               src="/static/user/round.png"
               mode="heightFix"
-              style="width: 12orpx;height: 120rpx"
-          />
+              style="width: 12orpx;height: 120rpx"/>
         </view>
         <view class="main flex-shrink">
           <view
@@ -18,11 +17,10 @@
               @click="toPage(2)"
               :style="{
               'background-image': `url(${user.avatar})`,
-            }"
-          ></view>
+            }"></view>
           <view class="phone fs-40 fw-500">{{ user.mobilePhoneFormat }}</view>
 
-          <view class="money" @click="toPage({path: '/pages-user/account/index'})">
+          <view class="money" @click="toPage({path: '/pages-user/wallet/index'})">
             <uv-icon name="red-packet" size="24"></uv-icon>
             <view>¥{{ user.balance }}</view>
           </view>
@@ -109,9 +107,9 @@ const toPage = (item: any) => {
       }
     });
     return;
-  }else if(path.includes("order")||path.includes("coupon")){
+  } else if (path.includes("order") || path.includes("coupon")) {
     uni.switchTab({
-      url:path
+      url: path
     })
   }
   uni.navigateTo({

二進制
src/static/wallet-banner.png


部分文件因文件數量過多而無法顯示