소스 검색

feat:电站详情调整

needcode 2 년 전
부모
커밋
434e42ebae

+ 1 - 1
README.md

@@ -1 +1 @@
-## apifox 待调试接口
+## 

+ 16 - 0
src/App.vue

@@ -39,6 +39,10 @@ image {
   height: 20rpx;
 }
 
+.relative {
+  position: relative;
+}
+
 .color-fff {
   color: #fff;
 }
@@ -47,6 +51,18 @@ image {
   color: #000;
 }
 
+.color-333 {
+  color: #333;
+}
+
+.color-666 {
+  color: #666;
+}
+
+.color-999 {
+  color: #999;
+}
+
 .color-primary {
   color: var(--color-primary);
 }

+ 49 - 38
src/api/charge.ts

@@ -15,45 +15,56 @@ export function startCharge(sn: string) {
 }
 
 export function fetchStationPriceDesc(ConnectorID: string, StationID?: string) {
-  return cHttp
-    .get(`/charge/businessPolicy/${ConnectorID}`)
-    .then((res) => {
-      const nowHour = new Date().getHours();
-      let currentPrice = 0;
-      let currentTime = "00:00~24:00";
-      if (res && res.policyInfoss && res.policyInfoss.length) {
-        res.policyInfoss.forEach((item: any, index: number) => {
-          const hour = item.startTime.substring(0, 2);
-          const min = item.startTime.substring(3, 5);
-          if (index >= res.policyInfoss.length - 1) {
-            // 最后一个
-            item.startTimeFormat = `${hour}:${min}~24:00`;
-            if (Number(hour) <= nowHour) {
-              currentPrice = Number(
-                Number(item.elecPrice + item.servicePrice).toFixed(2)
-              );
-              currentTime = `${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);
-            item.startTimeFormat = `${hour}:${min}~${nhour}:${nmin}`;
-            if (nowHour >= Number(hour) && nowHour < Number(nhour)) {
-              currentPrice = Number(
-                Number(item.elecPrice + item.servicePrice).toFixed(2)
-              );
-              currentTime = `${hour}:${min}~${nhour}:${nmin}`;
-            }
+  return cHttp.get(`/charge/businessPolicy/${ConnectorID}`).then((res) => {
+    const nowHour = new Date().getHours();
+    let maxPrice = 0;
+    let minPrice = 9;
+    let currentPrice = 0;
+    let currentTime = "00:00~24:00";
+    res.useTime = "";
+    if (res && res.policyInfoss && res.policyInfoss.length) {
+      res.policyInfoss.forEach((item: any, index: number) => {
+        const hour = item.startTime.substring(0, 2);
+        const min = item.startTime.substring(3, 5);
+        if (index === 0) {
+          res.useTime = `${hour}:${min}~24:00`;
+        }
+        let tempPrice = Number(
+          Number(item.elecPrice + item.servicePrice).toFixed(2)
+        );
+        if (tempPrice > maxPrice) {
+          maxPrice = tempPrice;
+        }
+        if (tempPrice < minPrice) {
+          minPrice = tempPrice;
+        }
+        if (index >= res.policyInfoss.length - 1) {
+          // 最后一个
+          item.startTimeFormat = `${hour}:${min}~24:00`;
+          if (Number(hour) <= nowHour) {
+            currentPrice = tempPrice;
+            currentTime = `${hour}:${min}~24:00`;
           }
-        });
-      }
-      res.currentPrice = currentPrice;
-      res.currentTime = currentTime;
-      if (StationID) {
-        res.StationID = StationID;
-      }
-      return res;
-    });
+        } else {
+          const nhour = res.policyInfoss[index + 1].startTime.substring(0, 2);
+          const nmin = res.policyInfoss[index + 1].startTime.substring(3, 5);
+          item.startTimeFormat = `${hour}:${min}~${nhour}:${nmin}`;
+          if (nowHour >= Number(hour) && nowHour < Number(nhour)) {
+            currentPrice = tempPrice;
+            currentTime = `${hour}:${min}~${nhour}:${nmin}`;
+          }
+        }
+      });
+    }
+    res.maxPrice = maxPrice;
+    res.minPrice = minPrice;
+    res.currentPrice = currentPrice;
+    res.currentTime = currentTime;
+    if (StationID) {
+      res.StationID = StationID;
+    }
+    return res;
+  });
 }
 
 export function cancelCharge(sn: string) {

+ 77 - 70
src/pages-charge/machines/charge-machine/charge-machine.vue

@@ -1,47 +1,55 @@
 <template>
-  <view :class="['charge-point', `charge-point-${open ? 'open' : 'close'}`]">
-    <view class="flex-align-center flex-between">
-      <view>
-        <view class="fs-32 fw-600">{{ title }}</view>
-        <view class="fs-24 mt-6 color-666">{{ time }}</view>
+  <view
+    class="charge-point flex-align-center mt-20"
+    v-for="(item, index) in list"
+    :key="index"
+    @click="toOrdering(index)"
+  >
+    <view
+      :class="[
+        'charge-point_icon',
+        `charge-point_icon-${item.connectorStatusInfo.status}`,
+      ]"
+    >
+      <!-- TODO 图标 -->
+    </view>
+    <view class="charge-point_title ml-16">
+      <view class="flex-align-center">
+        <view class="fs-32 fw-600 color-000 lh-32">{{ title }}</view>
+        <view
+          :class="[
+            'tag',
+            'fs-24',
+            'flex-center',
+            'ml-8',
+            item.connectorStatusInfo.status === 255 ||
+            item.connectorStatusInfo.status === 0
+              ? 'tag-disabled'
+              : 'tag-warning',
+          ]"
+          v-if="item.connectorStatusInfo.status !== 1"
+          >{{ (statusMap as any)[item.connectorStatusInfo.status] }}</view
+        >
       </view>
-      <view class="ml-auto color-warning" catchtap="toggleStatus">
-        <text class="fs-22 mr-6">¥</text>
-        <text class="fs-32 fw-600">{{ price }}</text>
+      <view class="mt-10 flex-align-center">
+        <view class="fs-24 color-666"
+          >{{ item.connectorType === 1 ? "直流" : "交流"
+          }}{{ item.power }}kw</view
+        >
+        <view class="fs-24 color-666 mr-6 ml-6">|</view>
+        <view class="fs-24 color-666">车位号TODO</view>
       </view>
+    </view>
+    <view class="ml-auto">
       <view
-        class="ml-14"
-        :style="{
-          transform: `rotate(${open ? '270deg' : '90deg'})`,
-          'transform-origin': 'center',
-        }"
-        @click="toggleStatus"
+        class="lh-32 color-warning"
+        style="text-align: right; margin-top: -8rpx"
       >
-        <uni-icons type="right" size="12" color="rgba(0,0,0,0.4)"></uni-icons>
+        <text class="fs-22 mr-4">¥</text>
+        <text class="fs-32">{{ price }}</text>
+        <text class="fs-22 ml-4" style="vertical-align: text-top">元</text>
       </view>
-    </view>
-    <view
-      class="mt-30 pr-20 pl-20 flex-align-center"
-      :style="{
-        height: '76rpx',
-        'background-color': 'var(--color-sec)',
-        'border-radius': '10rpx',
-      }"
-      v-for="(item, index) in list"
-      :key="index"
-      @click="toOrdering(index)"
-    >
-      <charge-icon
-        :type="item.connectorType === 1 ? 'fast' : 'slow'"
-      ></charge-icon>
-      <view class="ml-16 fs-26 color-000">{{ item.connectorName }}</view>
-      <view class="ml-10 fs-26 color-0004"
-        >{{ item.connectorType === 1 ? "直流" : "交流"
-        }}{{ item.power }}kw</view
-      >
-      <view class="ml-auto fs-22 flex-center pl-12 pr-12 status-tag">{{
-        (statusMap as any)[item.connectorStatusInfo.status]
-      }}</view>
+      <view class="fs-24 lh-24 color-999 mt-10">预计费用</view>
     </view>
   </view>
 </template>
@@ -56,7 +64,6 @@ export default {
   },
   data() {
     return {
-      open: true,
       statusMap: {
         255: "故障",
         0: "离网",
@@ -68,9 +75,6 @@ export default {
     };
   },
   methods: {
-    toggleStatus() {
-      this.open = !this.open;
-    },
     toOrdering(index: number) {
       const { list } = this;
       if (
@@ -106,33 +110,36 @@ export default {
   transition: all 0.3s;
   overflow: hidden;
   padding: 30rpx;
-}
-
-.charge-point-close {
-  height: 148rpx;
-}
-
-.charge-point-open {
-  height: auto;
-}
-
-.color-000 {
-  color: #000;
-}
-
-.color-0004 {
-  color: rgba(0, 0, 0, 0.4);
-}
-
-.color-666 {
-  color: #666;
-}
-
-.status-tag {
-  height: 42rpx;
-  border-radius: 40rpx;
-  color: var(--color-primary);
-  background-color: rgba(52, 125, 255, 0.1);
-  white-space: nowrap;
+  &_icon {
+    height: 56rpx;
+    width: 56rpx;
+    border-radius: 50%;
+    &-0,
+    &-255 {
+      background-color: #b1b1b1;
+    }
+    &-1 {
+      background-color: #00dab3;
+    }
+    &-2,
+    &-3,
+    &-4 {
+      background-color: var(--color-warning);
+    }
+  }
+  .tag {
+    height: 38rpx;
+    padding: 0rpx 8rpx;
+    box-sizing: border-box;
+    border-radius: 8rpx;
+  }
+  .tag-warning {
+    color: var(--color-warning);
+    background: rgba(255, 153, 0, 0.1);
+  }
+  .tag-disabled {
+    color: #b1b1b1;
+    background: rgba(177, 177, 177, 0.1);
+  }
 }
 </style>

+ 219 - 68
src/pages-charge/machines/machines.vue

@@ -1,50 +1,94 @@
 <template>
+  <!-- TODO 图片 -->
   <view
     :class="[
       'pl-20',
       'pr-20',
       'pb-40',
-      `container-${descDialog ? 'hidden' : ''}`,
+      `container-${dialogVisible ? 'hidden' : ''}`,
     ]"
   >
-    <view class="banner flex-column mt-20" v-if="canUseCount >= 0">
-      <view class="flex-align-center">
-        <view class="fs-40 fw-500 color-fff">{{ title }}</view>
-        <view class="tag flex-center pl-15 pr-15 ml-20 fs-24 color-fff"
-          >{{ canUseCount }}可用</view
-        >
-      </view>
-      <view class="flex mt-20">
+    <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"
+          style="width: 16px; display: block; flex-shrink: 0"
         />
-        <view class="ml-12 fs-26 color-fff">{{ location.address }}</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 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>
+      </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="foot mt-30 flex-justify-end flex-align-center"
-        @click="openDesc"
+        class="flex-align-center"
+        v-if="
+          station &&
+          station.equipmentInfos &&
+          station.equipmentInfos.length &&
+          station.equipmentInfos[0].connectorInfos
+        "
       >
-        <view class="fs-28 mr-4 color-fff">计费说明</view>
-        <view style="margin-top: -2px">
-          <uni-icons type="right" size="8" color="#fff"></uni-icons>
-        </view>
-        <view style="margin-left: -4px; margin-top: -2px">
-          <uni-icons type="right" size="8" color="#fff"></uni-icons>
-        </view>
+        <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="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>
-      <image
-        src="/pages-charge/static/machines-banner-nav.png"
-        mode="widthFix"
-        class="nav"
-        @click="openAddress"
-      />
     </view>
-    <view
-      class="mt-20"
+    <template
       v-if="station"
-      v-for="(item, index) in station.equipmentInfos"
+      v-for="(item, index) in stationEquipmentInfos"
       :key="index"
     >
       <ChargeMachine
@@ -53,19 +97,19 @@
         :time="currentTime"
         :list="item.connectorInfos"
       ></ChargeMachine>
-    </view>
-    <view class="dialog flex-center" v-if="descDialog">
+    </template>
+    <view class="dialog flex-align-end" v-if="dialogVisible">
       <view class="desc-dialog">
-        <view
-          @click.stop="closeDesc"
-          class="desc-dialog_head flex-center fs-32 fw-500 color-fff"
-          >计费说明<image
-            src="/static/images/icon-close.png"
-            mode="widthFix"
-          ></image>
+        <view class="desc-dialog_head flex-center">
+          <view class="fw-500 color-000" style="font-size: 16px">{{
+            dialogType === "desc" ? "充电费用说明" : "选择充电桩状态"
+          }}</view>
+          <view class="close" @click="closeDialog">
+            <uni-icons type="closeempty" size="24" color="#2D284B"></uni-icons>
+          </view>
         </view>
         <view class="desc-dialog_body">
-          <view class="table">
+          <view class="table" v-if="dialogType === 'desc'">
             <view class="tr flex">
               <view class="th flex-center fs-28">时间</view>
               <view class="th flex-center flex-column">
@@ -96,6 +140,17 @@
               <view class="td flex-center">{{ item.servicePrice }}</view>
             </view>
           </view>
+          <view class="status" v-if="dialogType === 'status'">
+            <view
+              :class="['fs-32', 'flex-align-center']"
+              :style="{ color: status === index ? '#347DFF' : '#2d284b' }"
+              v-for="(statusItem, index) in statusList"
+              :key="index"
+              @click="changeStatus(index)"
+            >
+              {{ statusItem.title }}
+            </view>
+          </view>
         </view>
       </view>
     </view>
@@ -107,17 +162,37 @@ import { ref } from "vue";
 import { fetchStation, fetchStationPriceDesc } from "../../api/charge";
 import { onLoad } from "@dcloudio/uni-app";
 import ChargeMachine from "./charge-machine/charge-machine.vue";
-const descDialog = ref(false);
+const dialogVisible = ref(false);
+const dialogType = ref("");
 const desc = ref<any[]>([]);
+const status = ref(0);
+const statusList = ref([
+  {
+    title: "全部",
+  },
+  {
+    title: "空闲",
+  },
+  {
+    title: "充电中",
+  },
+]);
+
+const totalCount = ref(0);
 const canUseCount = ref(0);
+const canUseTime = ref("");
+const canUsePrice = ref("");
 const title = ref("");
 const location = ref({
   address: "",
   latitude: "",
   longitude: "",
 });
+
 const station = ref();
+const stationEquipmentInfos = ref();
 const currentTime = ref();
+
 onLoad((options: any) => {
   const _title = options.title || "";
   uni.setNavigationBarTitle({
@@ -140,11 +215,13 @@ onLoad((options: any) => {
   });
   fetchStation(Number(options.id))
     .then((res) => {
+      console.log(res);
       let ConnectorID = "";
       if (res.equipmentInfos && res.equipmentInfos.length) {
         res.equipmentInfos.forEach((item: any) => {
           if (item.connectorInfos && item.connectorInfos.length) {
             item.connectorInfos.forEach((con: any) => {
+              totalCount.value++;
               if (!ConnectorID) {
                 ConnectorID = con.connectorId;
               }
@@ -159,6 +236,11 @@ onLoad((options: any) => {
         });
       }
       station.value = res;
+      stationEquipmentInfos.value = res.equipmentInfos.map((item: any) => {
+        return {
+          ...item,
+        };
+      });
       if (ConnectorID) {
         return fetchStationPriceDesc(ConnectorID);
       } else {
@@ -171,6 +253,8 @@ onLoad((options: any) => {
     .then((res) => {
       uni.hideLoading();
       currentTime.value = res.currentTime;
+      canUseTime.value = res.useTime;
+      canUsePrice.value = `${res.minPrice}~${res.maxPrice}`;
       desc.value = res.policyInfoss || [];
     })
     .catch((err) => {
@@ -193,10 +277,56 @@ const openAddress = function () {
   });
 };
 const openDesc = function () {
-  descDialog.value = true;
+  dialogVisible.value = true;
+  dialogType.value = "desc";
+};
+const openStatus = function () {
+  if (!stationEquipmentInfos.value || stationEquipmentInfos.value.length <= 0) {
+    return;
+  }
+  dialogVisible.value = true;
+  dialogType.value = "status";
 };
-const closeDesc = function () {
-  descDialog.value = false;
+const closeDialog = function () {
+  dialogVisible.value = false;
+};
+const changeStatus = function (index: number) {
+  if (status.value === index) {
+    closeDialog();
+    return;
+  }
+  status.value = index;
+  if (index === 0) {
+    stationEquipmentInfos.value = station.value.equipmentInfos.map(
+      (item: any) => {
+        return {
+          ...item,
+        };
+      }
+    );
+    closeDialog();
+    return;
+  }
+  const STATUS_MAP = [-1, 1, 3];
+  let newStationEquipmentInfos: any[] = [];
+  station.value.equipmentInfos.forEach((item: any) => {
+    let check = false;
+    if (item.connectorInfos && item.connectorInfos.length) {
+      item.connectorInfos.forEach((con: any) => {
+        if (
+          con.connectorStatusInfo &&
+          Number(con.connectorStatusInfo.status) === STATUS_MAP[status.value]
+        ) {
+          check = true;
+        }
+      });
+    }
+    if (check) {
+      newStationEquipmentInfos.push(item);
+    }
+  });
+  stationEquipmentInfos.value = newStationEquipmentInfos;
+  closeDialog();
 };
 </script>
 
@@ -213,57 +343,68 @@ page {
 }
 
 .banner {
+  margin-top: 380rpx;
   border-radius: 20rpx;
   overflow: hidden;
   position: relative;
-  background: linear-gradient(180deg, #347dff 0%, #4faaff 100%);
-  min-height: 280rpx;
+  background-color: #fff;
+  min-height: 264rpx;
   padding: 0 40rpx;
-  padding-top: 40rpx;
+  padding-top: 30rpx;
 
-  .tag {
-    border-radius: 10px;
-    background: rgba(255, 255, 255, 0.2);
-    height: 20px;
+  .nav {
+    flex-shrink: 0;
+    width: 120rpx;
+    height: 48rpx;
+    border-radius: 40rpx;
+    background: rgba(52, 125, 255, 0.1);
+    image {
+      width: 36rpx;
+    }
   }
 
   .foot {
-    flex-shrink: 0;
-    border-top: 1px dashed rgba(255, 255, 255, 0.3);
-    height: 88rpx;
+    border-top: 1px dashed rgba(0, 0, 0, 0.1);
+    height: 96rpx;
   }
+}
 
-  .nav {
-    position: absolute;
-    top: 42rpx;
-    right: 40rpx;
-    width: 120rpx;
+.desc {
+  box-sizing: border-box;
+  padding: 0rpx 30rpx;
+  border-radius: 20rpx;
+  background-color: #fff;
+  & > view {
+    height: 90rpx;
+    border-top: 1px solid rgba(0, 0, 0, 0.1);
+    &:first-child {
+      border-top: none;
+    }
   }
 }
 
 .desc-dialog {
   background-color: transparent;
-  width: 690rpx;
+  width: 100%;
   overflow: hidden;
-  border-radius: 10px;
+  border-radius: 20rpx;
 
   &_head {
-    background: linear-gradient(180deg, #347dff 0%, #4faaff 100%);
-    height: 104rpx;
+    padding: 24rpx 0px;
     position: relative;
-
-    image {
+    background-color: #fff;
+    .close {
       position: absolute;
-      width: 12px;
-      right: 40rpx;
-      top: 40rpx;
+      right: 30rpx;
+      top: 50%;
+      transform: translateY(-50%);
     }
   }
 
   &_body {
     max-height: 860rpx;
     box-sizing: border-box;
-    padding: 30rpx;
+    padding: 0rpx 30rpx 80rpx 30rpx;
     background-color: #fff;
     overflow-y: auto;
 
@@ -311,6 +452,16 @@ page {
       font-size: 26rpx;
       height: 76rpx;
     }
+
+    .status {
+      & > view {
+        height: 92rpx;
+        border-top: 1px solid rgba(0, 0, 0, 0.1);
+        &:first-child {
+          border-top: none;
+        }
+      }
+    }
   }
 }
 </style>

BIN
src/pages-charge/static/machines-banner-address.png


BIN
src/pages-charge/static/machines-banner-nav.png


BIN
src/static/images/custom-tab-bar/2.png


BIN
src/static/images/icon-close.png


+ 4 - 0
src/styles/layout.scss

@@ -36,4 +36,8 @@ $position: top, right, bottom, left;
 
 .mt-auto {
   margin-top: auto;
+}
+
+.width-168 {
+  width: 168rpx;
 }

+ 1 - 1
src/utils/constant.ts

@@ -12,5 +12,5 @@ console.log("env", env);
 isDevelopment = env === "develop" || env === "trial";
 // #endif
 
-export const domain = isDevelopment ? "dev.kuaiyuman.cn" : "www.kuaiyuman.cn";
+export const domain = !isDevelopment ? "dev.kuaiyuman.cn" : "www.kuaiyuman.cn";
 export const host = `https://${domain}/api`;