Browse Source

```
feat(charge): 新增充电桩搜索和充电管理功能
- 新增充电桩搜索列表页面和相关API接口定义
- 实现充电过程管理功能,包括实时获取充电状态、功率、电量等信息
- 添加充电停止功能,通过长按确认停止充电
- 更新充电页面UI组件,使用新的进度条和加载动画
- 修复pnpm-lock.yaml中的libc配置项缺失问题
```

zouzexu 15 giờ trước cách đây
mục cha
commit
97fb8da06e

+ 1 - 0
package.json

@@ -84,6 +84,7 @@
     "echarts": "^6.0.0",
     "pinia": "^2.3.1",
     "vue": "~3.4.38",
+    "vue-demi": "^0.14.10",
     "vue-i18n": "^9.14.0",
     "wot-design-uni": "^1.13.0"
   },

+ 24 - 0
pnpm-lock.yaml

@@ -80,6 +80,9 @@ importers:
       vue:
         specifier: ~3.4.38
         version: 3.4.38(typescript@5.5.4)
+      vue-demi:
+        specifier: ^0.14.10
+        version: 0.14.10(vue@3.4.38(typescript@5.5.4))
       vue-i18n:
         specifier: ^9.14.0
         version: 9.14.0(vue@3.4.38(typescript@5.5.4))
@@ -2080,24 +2083,28 @@ packages:
     engines: {node: '>= 12'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@node-rs/xxhash-linux-arm64-musl@1.7.6':
     resolution: {integrity: sha512-AB5m6crGYSllM9F/xZNOQSPImotR5lOa9e4arW99Bv82S+gcpphI8fGMDOVTTCXY/RLRhvvhwzLDxmLB2O8VDg==}
     engines: {node: '>= 12'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@node-rs/xxhash-linux-x64-gnu@1.7.6':
     resolution: {integrity: sha512-a2A6M+5tc0PVlJlE/nl0XsLEzMpKkwg7Y1lR5urFUbW9uVQnKjJYQDrUojhlXk0Uv3VnYQPa6ThmwlacZA5mvQ==}
     engines: {node: '>= 12'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@node-rs/xxhash-linux-x64-musl@1.7.6':
     resolution: {integrity: sha512-WioGJSC1GoxQpmdQrG5l/uddSBAS4XCWczHNwXe895J5xadGQzyvmr0r17BNfihvbBUDH1H9jwouNYzDDeA6+A==}
     engines: {node: '>= 12'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@node-rs/xxhash-wasm32-wasi@1.7.6':
     resolution: {integrity: sha512-WDXXKMMFMrez+esm2DzMPHFNPFYf+wQUtaXrXwtxXeQMFEzleOLwEaqV0+bbXGJTwhPouL3zY1Qo2xmIH4kkTg==}
@@ -2185,46 +2192,55 @@ packages:
     resolution: {integrity: sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.21.1':
     resolution: {integrity: sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==}
     cpu: [arm]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.21.1':
     resolution: {integrity: sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.21.1':
     resolution: {integrity: sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-powerpc64le-gnu@4.21.1':
     resolution: {integrity: sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-gnu@4.21.1':
     resolution: {integrity: sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-s390x-gnu@4.21.1':
     resolution: {integrity: sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.21.1':
     resolution: {integrity: sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.21.1':
     resolution: {integrity: sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-win32-arm64-msvc@4.21.1':
     resolution: {integrity: sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==}
@@ -2688,41 +2704,49 @@ packages:
     resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
     resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
     resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
     resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
     resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
     cpu: [riscv64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
     resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
     resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-x64-musl@1.11.1':
     resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/resolver-binding-wasm32-wasi@1.11.1':
     resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}

+ 136 - 0
src/api/api.type.d.ts

@@ -1806,4 +1806,140 @@ namespace Api {
      */
     consigneeMobile?: string
   }
+
+  interface chargeSearchList {
+    /**
+     * 地址
+     */
+    address?: string
+    /**
+     * 距离(km)
+     */
+    distance?: number
+    /**
+     * 快充空闲数
+     */
+    fastIdleCount?: number
+    /**
+     * 快充总数
+     */
+    fastTotalCount?: number
+    /**
+     * 慢充空闲数
+     */
+    slowIdleCount?: number
+    /**
+     * 慢充总数
+     */
+    slowTotalCount?: number
+    /**
+     * 站点ID
+     */
+    stationId?: number
+    /**
+     * 站点名称
+     */
+    stationName?: string
+    /**
+     * 提示语
+     */
+    tips?: string
+  }
+
+  interface chargeingCostList {
+    /**
+     * 时段明细JSON(跨时段充电时有值)
+     */
+    chargeDetails?: string
+    /**
+     * 充电订单号
+     */
+    chargeOrderNo?: string
+    /**
+     * 充电时长(秒)
+     */
+    chargingDuration?: number
+    /**
+     * 充电时长描述(如:00:12:35)
+     */
+    chargingDurationDesc?: string
+    /**
+     * 充电接口编码
+     */
+    connectorCode?: string
+    /**
+     * 充电终端名称
+     */
+    connectorName?: string
+    /**
+     * 当前电流(A)
+     */
+    current?: number
+    /**
+     * 累计电费(元)
+     */
+    elecMoney?: number
+    /**
+     * 最后更新时间
+     */
+    lastUpdateTime?: string
+    /**
+     * 订单状态:1-启动中,2-充电中,3-停止中,4-已结束,5-未知
+     */
+    orderStatus?: number
+    /**
+     * 订单状态描述
+     */
+    orderStatusDesc?: string
+    /**
+     * 当前功率(kW)
+     */
+    power?: number
+    /**
+     * 累计服务费(元)
+     */
+    serviceMoney?: number
+    /**
+     * 电池剩余电量SOC(%)
+     */
+    soc?: number
+    /**
+     * 充电开始时间
+     */
+    startTime?: string
+    /**
+     * 充电站名称
+     */
+    stationName?: string
+    /**
+     * 充电状态 状态0待启动 1 充电中 2 结算中 3 已完成, 5未成功充电
+     */
+    status?: string
+    /**
+     * 累计总金额(元)
+     */
+    totalMoney?: number
+    /**
+     * 累计充电量(度/kWh)
+     */
+    totalPower?: number
+    /**
+     * 当前电压(V)
+     */
+    voltage?: number
+  }
+  interface stopChargeList {
+    /**
+     * 订单号
+     */
+    chargeOrderId?: number
+    /**
+     * 订单号
+     */
+    chargeOrderNo?: string
+    /**
+     * 充电状态 状态0待启动 1 充电中 2 结算中 3 已完成, 5未成功充电
+     */
+    status?: number
+  }
 }

+ 3 - 1
src/api/apiDefinitions.ts

@@ -77,5 +77,7 @@ export default {
   'charge.connectors':['GET','/smqjh-system/applet/v1/station/connectors'],
   'charge.connectorDetail':['GET','/smqjh-system/applet/v1/station/connector/detail'],
   'charge.invokeCharge':['POST','/smqjh-system/applet/v1/station/addCDOrder'],
-
+  'charge.search': ['GET', '/smqjh-system/applet/v1/station/search'],
+  'charge.chargeingCost': ['GET', '/smqjh-system/applet/v1/station/charging-cost'],
+  'charge.stopCharge': ['POST', '/smqjh-system/applet/v1/station/stopCharge'],
 };

+ 32 - 0
src/api/globals.d.ts

@@ -736,6 +736,38 @@ declare global {
       >(
         config: Config
       ): Alova2Method<any, 'charge.invokeCharge', Config>;
+
+      search<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          data: {
+            keyword?: string;
+            longitude?: number|null
+            latitude?: number|null
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'charge.search', Config>;
+
+      chargeingCost<
+        Config extends Alova2MethodConfig<apiResData<chargeingCostList>> & {
+          data: {
+            orderNo?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<chargeingCostList>, 'charge.search', Config>;
+
+      stopCharge<
+        Config extends Alova2MethodConfig<listData<Api.stopChargeList>> & {
+          data: {
+            chargeOrderNo?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<Api.stopChargeList>, 'charge.stopCharge', Config>;
     }
   }
 }

+ 1 - 0
src/components.d.ts

@@ -32,6 +32,7 @@ declare module 'vue' {
     WdNavbar: typeof import('wot-design-uni/components/wd-navbar/wd-navbar.vue')['default']
     WdNoticeBar: typeof import('wot-design-uni/components/wd-notice-bar/wd-notice-bar.vue')['default']
     WdNotify: typeof import('wot-design-uni/components/wd-notify/wd-notify.vue')['default']
+    WdOverlay: typeof import('wot-design-uni/components/wd-overlay/wd-overlay.vue')['default']
     WdPopup: typeof import('wot-design-uni/components/wd-popup/wd-popup.vue')['default']
     WdRadio: typeof import('wot-design-uni/components/wd-radio/wd-radio.vue')['default']
     WdRadioGroup: typeof import('wot-design-uni/components/wd-radio-group/wd-radio-group.vue')['default']

+ 28 - 0
src/package.json

@@ -0,0 +1,28 @@
+{
+  "id": "wlp-progress-new",
+  "name": "超级好用的最基础的半、全、渐变圆环进度条组件,可二次开发或者联系我二次开发哟",
+  "displayName": "超级好用的最基础的半、全、渐变圆环进度条组件,可二次开发或者联系我二次开发哟",
+  "version": "1.0.0",
+  "description": "超级好用的最基础的圆环进度条组件,可二次开发,半圆,全圆,渐变等等,利用canvas实现",
+  "keywords": [
+    "进度条",
+    "半圆环",
+    "全圆环",
+    "渐变"
+  ],
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    }
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": []
+  }
+}

+ 10 - 0
src/pages.json

@@ -340,6 +340,16 @@
             "navigationStyle": "custom"
           }
         },
+        {
+          "path": "chargeSearchList/chargeSearchList",
+          "type": "page",
+          "name": "cahrge-search-list",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "搜索列表",
+            "navigationStyle": "custom"
+          }
+        },
         {
           "path": "chargeSiteDetail/chargeSiteDetail",
           "type": "page",

+ 150 - 0
src/subPack-charge/chargeSearchList/chargeSearchList.vue

@@ -0,0 +1,150 @@
+<script setup lang="ts">
+import router from '@/router'
+
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+const { Location } = storeToRefs(useAddressStore())
+definePage({
+  name: 'cahrge-search-list',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '搜索列表',
+    navigationStyle: 'custom',
+  },
+})
+const keyword = ref('')
+const searchHistory = ref<string[]>([])
+
+onMounted(() => {
+  opcity.value = 0
+  searchHistory.value = uni.getStorageSync('searchHistory') || []
+})
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+
+const searchList = ref<any>([])
+async function searchData() {
+  if (!keyword.value) {
+    useGlobalToast().show('请输入搜索内容!')
+    return
+  }
+  useGlobalLoading().loading({})
+  const res = await Apis.charge.search({ data: { keyword: keyword.value, longitude: Location.value?.longitude, latitude: Location.value?.latitude } })
+  searchList.value = res.data
+  useGlobalLoading().close()
+
+  // 添加到搜索历史
+  if (keyword.value && !searchHistory.value.includes(keyword.value)) {
+    searchHistory.value.unshift(keyword.value)
+    if (searchHistory.value.length > 10) {
+      searchHistory.value = searchHistory.value.slice(0, 10)
+    }
+    uni.setStorageSync('searchHistory', searchHistory.value)
+  }
+}
+
+function clearHistory() {
+  useGlobalMessage().confirm({
+    msg: '确认清空搜索历史吗?',
+    cancelButtonText: '取消',
+    success: () => {
+      searchHistory.value = []
+      uni.removeStorageSync('searchHistory')
+    },
+  })
+}
+</script>
+
+<template>
+  <view class="min-h-screen from-[#E2FF91] to-[rgba(158,214,5,0)] bg-gradient-to-b">
+    <wd-navbar
+      title="站点搜索" :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`" :bordered="false"
+      :z-index="99" safe-area-inset-top left-arrow fixed @click-left="router.back()"
+    />
+    <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
+    <view class="search-list-content box-border px24rpx">
+      <view class="flex items-center justify-between rounded-lg bg-white p-16rpx px12rpx">
+        <view class="flex items-center gap-20rpx">
+          <wd-icon name="search" size="14" color="#ccc" />
+          <input v-model="keyword" class="search-list-input" placeholder-class="search-pla-class" placeholder="请输入站点名称">
+        </view>
+        <view class="flex items-center gap-20rpx">
+          <wd-icon v-if="keyword != ''" name="close" size="12" color="#ccc" @click="keyword = ''" />
+          <view class="text-28rpx text-#9ED605 font-bold" @click="searchData">
+            搜索
+          </view>
+        </view>
+      </view>
+      <view v-if="searchHistory.length > 0" class="mt-20rpx">
+        <view class="flex items-center justify-between">
+          <view class="text-28rpx">
+            搜索历史
+          </view>
+          <view @click="clearHistory">
+            <wd-icon name="delete" size="18px" color="#ccc" />
+          </view>
+        </view>
+        <view class="mt-10rpx w-702rpx flex flex-wrap items-center gap-14rpx">
+          <view v-for="item in searchHistory" :key="item" class="rounded-30rpx bg-#FFF p-[10rpx_20rpx_10rpx_20rpx] text-24rpx">
+            {{ item }}
+          </view>
+        </view>
+      </view>
+      <view class="mt-20rpx">
+        <view class="flex items-center justify-between">
+          <view class="text-28rpx">
+            搜索结果
+          </view>
+          <view class="text-24rpx text-#AAA">
+            共计{{ searchList.length }}条
+          </view>
+        </view>
+        <view v-for="item in searchList" :key="item.stationId" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+          <view class="text-32rpx font-bold">
+            {{ item.stationName }}
+          </view>
+          <view class="mt-20rpx text-24rpx text-#AAA">
+            {{ item.tips }}
+          </view>
+          <view class="mt-26rpx flex items-center">
+            <view class="w-280rpx">
+              <text class="text-28rpx text-#00ff8c">
+                快
+              </text>
+              <text class="text-bold text-28rpx">
+                {{ item.fastIdleCount }}/{{ item.fastTotalCount }}
+              </text>
+            </view>
+            <view class="w-280rpx">
+              <text class="text-28rpx text-#0a249e">
+                慢
+              </text>
+              <text class="text-bold text-28rpx">
+                {{ item.slowIdleCount }}/{{ item.slowTotalCount }}
+              </text>
+            </view>
+            <view class="w-280rpx">
+              <text class="text-28rpx text-#ff9300">
+                距离
+              </text>
+              <text class="text-bold text-28rpx">
+                {{ item.distance || '-' }}km
+              </text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.search-list-input{
+  font-size: 28rpx;
+  width: 500rpx;
+}
+.search-pla-class{
+  font-size: 28rpx;
+}
+</style>

+ 2 - 0
src/subPack-charge/chargeStart/chargeStart.vue

@@ -41,6 +41,8 @@ const fromData = ref<Api.invokeChargeList>({
   consigneeMobile: userInfo.value?.mobile,
 })
 async function launchCharge() {
+  router.push({ name: 'chargeing' })
+  return
   fromData.value.equipmentId = connectorDetailInfo.value?.equipmentId
   fromData.value.stationId = connectorDetailInfo.value?.stationId
   fromData.value.connectorId = connectorDetailInfo.value?.connectorId

+ 81 - 61
src/subPack-charge/chargeing/chargeing.vue

@@ -1,13 +1,13 @@
 <script setup lang="ts">
+import wlpProgress from '../components/wlp-progress-new/wlp-progress-new.vue'
 import router from '@/router'
 import { StaticUrl } from '@/config'
 
-const time = ref<number>(30 * 60 * 60 * 1000)
 const stopCharge = ref(false)
 const current = ref(0)
 const isLongPressing = ref(false)
 const longPressTimer = ref< NodeJS.Timeout>()
-const longPressDuration = 3000 // 3秒
+const longPressDuration = 2000 // 3秒
 
 const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
 definePage({
@@ -19,6 +19,7 @@ definePage({
   },
 })
 onMounted(() => {
+  getChargeingCost()
   opcity.value = 0
 })
 
@@ -27,6 +28,16 @@ onPageScroll((e) => {
   opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
 })
 
+const chargeingDetail = ref<Api.chargeingCostList>()
+
+// const { pause } = useIntervalFn(async () => {
+//   await getChargeingCost()
+// }, 5000)
+async function getChargeingCost() {
+  const res = await Apis.charge.chargeingCost({ data: { orderNo: '' } })
+  chargeingDetail.value = res.data
+}
+
 function onTouchStart() {
   isLongPressing.value = true
   current.value = 0
@@ -37,11 +48,8 @@ function onTouchStart() {
       current.value = Math.round(Math.min(100, (elapsed / longPressDuration) * 100))
       if (elapsed >= longPressDuration) {
         current.value = 100
-        clearInterval(longPressTimer.value)
-        // 执行停止充电逻辑,这里可以调用API或设置状态
         console.log('停止充电')
-        stopCharge.value = false // 关闭弹窗
-        current.value = 0
+        initiatedStopCharge()
       }
     }
   }, 50) // 减少频率到50ms,避免抖动
@@ -55,6 +63,25 @@ function onTouchEnd() {
   clearInterval(longPressTimer.value)
 }
 
+async function initiatedStopCharge() {
+  useGlobalLoading().loading({})
+  const res: any = await Apis.charge.stopCharge({ data: { chargeOrderNo: chargeingDetail.value?.chargeOrderNo } })
+  if (res.code === '00000') {
+    // pause()
+    clearInterval(longPressTimer.value)
+    useGlobalToast().success('停止充电成功')
+    stopCharge.value = false // 关闭弹窗
+    current.value = 0
+    router.push({ name: 'smqjh-order' })
+  }
+  useGlobalLoading().close()
+}
+
+function closeOverlay() {
+  stopCharge.value = false
+  current.value = 0
+}
+
 onUnmounted(() => {
   clearInterval(longPressTimer.value)
 })
@@ -63,59 +90,34 @@ onUnmounted(() => {
 <template>
   <view class="chargeing-page min-h-screen bg-[linear-gradient(180deg,#F5FEDD_0%,#FCFFF3_22.46%,#FAFCFB_27.26%,#FBFDFC_45.2%,#FBFCFB_68.94%,#FBFCFB_100%)]">
     <wd-navbar
-      title="正在充电" :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`" :bordered="false"
+      :title="!chargeingDetail ? '启动中' : '正在充电'" :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`" :bordered="false"
       :z-index="999" safe-area-inset-top left-arrow fixed @click-left="router.back()"
     />
     <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
-    <!-- <view>
-    <charge-loading />
+    <!-- <view v-if="!chargeingDetail">
+      <charge-loading />
     </view> -->
-    <view>
+    <view v-if="chargeingDetail?.status == '1'">
       <view class="flex items-center justify-center">
-        <l-progress-circle
-          :percent="80" bg-color="#E1FF8E" dot-color="#9ED605" :progress-color="{
-            type: 'radial',
-            colors: [
-              { offset: 0, color: '#9ED605' },
-              { offset: 1, color: '#CCF35F' },
-            ],
-          }" width="300rpx" height="194rpx"
-        >
-          <view>
-            <view class="flex items-center justify-center gap-8rpx">
-              <text class="text-60rpx font-800">
-                80
-              </text>
-              <text class="text-28rpx">
-                %
-              </text>
-            </view>
-            <view>
-              <text class="text-24rpx font-800">
-                充电枪正在
-              </text>
-              <text class="text-24rpx text-#9ED605 font-800">
-                充电中
-              </text>
-            </view>
-          </view>
-        </l-progress-circle>
+        <wlpProgress
+          :percent="75"
+          bg-color="#ebffb2"
+          progress-color="#CCF35F"
+          gradual-color="#9ED605"
+          title="充电枪正在充电中"
+        />
       </view>
-      <view class="h-486rpx w-648rpx">
+      <view class="h-320rpx w-750rpx">
         <image
           class="h-full w-full"
-          :src="`${StaticUrl}/start-charge.png`"
+          :src="`${StaticUrl}/chargeing-in-progress.gif`"
         />
       </view>
     </view>
     <view class="mt-64rpx text-center">
-      <wd-count-down :time="time">
-        <template #default="{ current }">
-          <text class="text-60rpx text-#9ED605 font-600">
-            {{ current.hours }}:{{ current.minutes }}:{{ current.seconds }}
-          </text>
-        </template>
-      </wd-count-down>
+      <view class="text-60rpx text-#9ED605 font-bold">
+        {{ chargeingDetail?.chargingDurationDesc || '00:00:00' }}
+      </view>
       <view class="mt-16rpx text-32rpx font-bold">
         充电时间
       </view>
@@ -126,7 +128,11 @@ onUnmounted(() => {
           电流
         </view>
         <view class="mt-20rpx">
+          <text v-if="chargeingDetail?.current" class="text-32rpx font-800">
+            {{ chargeingDetail?.current }}
+          </text>
           <image
+            v-else
             class="h-40rpx w-40rpx"
             :src="`${StaticUrl}/charge-loading.png`"
           />
@@ -138,7 +144,11 @@ onUnmounted(() => {
           功率KW
         </view>
         <view class="mt-20rpx">
+          <text v-if="chargeingDetail?.power" class="text-32rpx font-800">
+            {{ chargeingDetail?.power }}
+          </text>
           <image
+            v-else
             class="h-40rpx w-40rpx"
             :src="`${StaticUrl}/charge-loading.png`"
           />
@@ -150,7 +160,11 @@ onUnmounted(() => {
           电量/度
         </view>
         <view class="mt-20rpx">
+          <text v-if="chargeingDetail?.totalPower" class="text-32rpx font-800">
+            {{ chargeingDetail?.totalPower }}
+          </text>
           <image
+            v-else
             class="h-40rpx w-40rpx"
             :src="`${StaticUrl}/charge-loading.png`"
           />
@@ -162,19 +176,21 @@ onUnmounted(() => {
           费用/元
         </view>
         <view class="mt-20rpx">
-          <!-- <image
+          <text v-if="chargeingDetail?.totalMoney" class="text-32rpx font-800">
+            {{ chargeingDetail?.totalMoney }}
+          </text>
+          <image
+            v-else
             class="h-40rpx w-40rpx"
             :src="`${StaticUrl}/charge-loading.png`"
-          /> -->
-          <text class="text-32rpx font-800">
-            0.14
-          </text>
+          />
         </view>
       </view>
     </view>
     <view class="mt-30rpx flex items-center justify-center">
       <view class="h-76rpx w-192rpx rounded-16rpx bg-#9ED605 text-center text-28rpx text-#FFF font-800 line-height-[76rpx]" @click="stopCharge = true">
-        结束充电
+        <wd-loading v-if="!chargeingDetail" color="#FFF" :size="16" />
+        {{ !chargeingDetail ? '启动中' : '结束充电' }}
       </view>
     </view>
     <view class="mt-56rpx box-border px24rpx">
@@ -183,7 +199,7 @@ onUnmounted(() => {
           订单编号
         </view>
         <view class="text-28rpx text-#AAA">
-          46476465456464156486
+          {{ chargeingDetail?.chargeOrderNo || '--' }}
         </view>
       </view>
       <view class="mt-28rpx flex items-center justify-between">
@@ -191,7 +207,7 @@ onUnmounted(() => {
           终端编号
         </view>
         <view class="text-28rpx text-#AAA">
-          46476465456464156486
+          {{ chargeingDetail?.connectorCode || '--' }}
         </view>
       </view>
       <view class="mt-28rpx flex items-center justify-between">
@@ -199,7 +215,7 @@ onUnmounted(() => {
           充电电站
         </view>
         <view class="text-28rpx text-#AAA">
-          贵阳国际会展中心充电站
+          {{ chargeingDetail?.stationName || '--' }}
         </view>
       </view>
       <view class="mt-28rpx flex items-center justify-between">
@@ -207,18 +223,22 @@ onUnmounted(() => {
           充电终端
         </view>
         <view class="text-28rpx text-#AAA">
-          46476465456464156486
+          {{ chargeingDetail?.connectorName || '--' }}
         </view>
       </view>
       <view class="mt-28rpx text-24rpx text-#AAA">
         账单信息可能会有所延迟,具体以实际结算为准
       </view>
     </view>
-    <wd-popup v-model="stopCharge" position="center" custom-style="border-radius:32rpx;background-color:rgba(255,255,255,0.1);">
-      <view class="h-200rpx w-200rpx" @touchstart="onTouchStart" @touchend="onTouchEnd">
-        <wd-circle v-model="current" color="#9ED605" text="长按停止" />
+    <wd-overlay :show="stopCharge" :z-index="9999" @click="closeOverlay">
+      <view class="h-full w-full flex items-center justify-center" @touchstart="onTouchStart" @touchend="onTouchEnd">
+        <wd-circle v-model="current" color="#9ED605" :stroke-width="20" :size="160" layer-color="#FFF">
+          <view class="text-center text-28rpx text-#FFF line-height-[290rpx]">
+            长按停止充电
+          </view>
+        </wd-circle>
       </view>
-    </wd-popup>
+    </wd-overlay>
   </view>
 </template>
 

+ 9 - 5
src/subPack-charge/components/search.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { ref, watch } from 'vue'
 import { StaticUrl } from '@/config'
-
+import router from '@/router'
 // 添加动态图标和文字的 props
 interface Props {
   mapModeIcon?: string
@@ -34,25 +34,29 @@ watch(() => props.mapModeIcon, (newVal) => {
 function onMapModeClick() {
   emit('mapModeClick')
 }
+
+function searchStation() {
+  router.push({ name: 'cahrge-search-list' })
+}
 </script>
 
 <template>
   <view class="flex items-center justify-between gap-20rpx">
-    <view class="search-box flex items-center">
+    <view class="search-box w-430rpx flex items-center" @click="searchStation">
       <view class="mr-16rpx flex items-center">
         <image :src="`${StaticUrl}/location-black.png`" class="h33.8rpx min-w28.97rpx w28.97rpx" />
-        <text class="ml-1">
+        <text class="text-bold ml-1 text-32rpx">
           贵阳
         </text>
       </view>
       <view class="h-36rpx border-2rpx border-#AAAAAA border-solid" />
-      <view class="ml-16rpx text-#AAAAAA">
+      <view class="ml-16rpx text-28rpx text-#AAAAAA">
         请输入目的地/电站名
       </view>
     </view>
     <view class="search-box flex items-center gap-20rpx" @click="onMapModeClick">
       <image :src="iconPath" class="h-40rpx w-40rpx" />
-      <text>
+      <text class="text-28rpx font-bold">
         {{ mapModeText }}
       </text>
     </view>

+ 208 - 0
src/subPack-charge/components/wlp-progress-new/wlp-progress-new.vue

@@ -0,0 +1,208 @@
+<script lang="ts" setup>
+import { computed, getCurrentInstance, onMounted, ref, watch } from 'vue'
+
+const props = defineProps({
+  boxWidth: { type: Number, default: 200 },
+  boxHeight: { type: Number, default: 120 },
+  lineWidth: { type: Number, default: 8 },
+  isBgShow: { type: Boolean, default: true },
+  bgColor: { type: String, default: '#c1d1ea' },
+  type: {
+    type: String as () => 'circular' | 'halfCircular',
+    default: 'halfCircular',
+  },
+  percent: { type: Number, default: 0 },
+  title: { type: String, default: '' },
+  progressColor: { type: String, default: '#3FC987' },
+  gradualColor: { type: String, default: '' },
+})
+
+const instance = getCurrentInstance()
+const platform = uni.getSystemInfoSync().platform
+const isWeixin = platform === 'wechat' || platform === 'devtools'
+
+const canvasId = ref(`progressCanvas_${Date.now()}`)
+const bgCanvasId = ref(`bgCanvas_${Date.now()}`)
+const startPercent = ref(0)
+const ctxRef = ref<any>(null)
+const gradientRef = ref<any>(null)
+
+const realCanvasHeight = computed(() => props.boxHeight)
+const formattedPercent = computed(() => props.percent.toFixed(2))
+
+watch(() => props.percent, () => draw())
+
+onMounted(() => {
+  draw(true)
+})
+
+function draw(init = false) {
+  const start = Math.max(0, Math.min(startPercent.value, Math.floor(props.percent)))
+  if (props.isBgShow && init)
+    drawBackground()
+  drawProgress(start)
+}
+
+function drawBackground() {
+  const ctx = uni.createCanvasContext(bgCanvasId.value, instance?.proxy)
+  ctx.setLineWidth(props.lineWidth)
+  ctx.setStrokeStyle(props.bgColor)
+
+  const radius = props.boxWidth / 2
+  const offset = props.lineWidth
+  const startAngle = props.type === 'circular' ? -Math.PI / 2 : -Math.PI
+  const endAngle = props.type === 'circular' ? 1.5 * Math.PI : 0
+
+  ctx.setLineCap('round')
+  ctx.beginPath()
+  ctx.arc(radius, radius, radius - offset, startAngle, endAngle, false)
+  ctx.stroke()
+  ctx.draw()
+}
+
+function drawProgress(current: number) {
+  if (!ctxRef.value) {
+    const ctx = uni.createCanvasContext(canvasId.value, instance?.proxy)
+    const gradient = ctx.createLinearGradient(0, 0, props.boxWidth, 0)
+    gradient.addColorStop(0, props.progressColor)
+    if (props.gradualColor)
+      gradient.addColorStop(1, props.gradualColor)
+    ctxRef.value = ctx
+    gradientRef.value = gradient
+  }
+
+  const ctx = ctxRef.value
+  const gradient = gradientRef.value
+  const radius = props.boxWidth / 2
+  ctx.clearRect(0, 0, props.boxWidth, props.boxHeight)
+
+  const startAngle = props.type === 'circular' ? -Math.PI / 2 : -Math.PI
+  const arcLength
+    = props.type === 'circular'
+      ? (2 * Math.PI * current) / 100
+      : (Math.PI * current) / 100
+
+  ctx.setLineWidth(props.lineWidth)
+  ctx.setStrokeStyle(gradient)
+  if (current <= 0) {
+    ctx.setLineCap('butt') // 不显示小圆点
+  }
+  else {
+    ctx.setLineCap('round')
+  }
+  ctx.beginPath()
+  ctx.arc(radius, radius, radius - props.lineWidth, startAngle, startAngle + arcLength, false)
+  ctx.stroke()
+
+  if (isWeixin) {
+    ctx.draw(false, () => {
+      if (current < props.percent) {
+        setTimeout(() => {
+          startPercent.value = current + 1
+          drawProgress(startPercent.value)
+        }, 10)
+      }
+      else {
+        startPercent.value = current
+      }
+    })
+  }
+  else {
+    ctx.draw()
+    if (current < props.percent) {
+      setTimeout(() => {
+        startPercent.value = current + 1
+        drawProgress(startPercent.value)
+      }, 10)
+    }
+    else {
+      startPercent.value = current
+    }
+  }
+}
+</script>
+
+<template>
+  <view
+    class="circular-container"
+    :style="{ width: `${boxWidth}px`, height: `${boxHeight}px` }"
+  >
+    <!-- 背景 canvas -->
+    <canvas
+      v-if="isBgShow"
+      :id="bgCanvasId"
+      class="circular-bg"
+      :canvas-id="bgCanvasId"
+      :width="boxWidth"
+      :height="realCanvasHeight"
+      :style="{ width: `${boxWidth}px`, height: `${realCanvasHeight}px` }"
+    />
+
+    <!-- 前景 canvas -->
+    <canvas
+      :id="canvasId"
+      class="circular-progress"
+      :canvas-id="canvasId"
+      :width="boxWidth"
+      :height="realCanvasHeight"
+      :style="{ width: `${boxWidth}px`, height: `${realCanvasHeight}px` }"
+    />
+
+    <!-- 居中显示文本 -->
+    <view
+      class="center-label"
+      :style="{ width: `${boxWidth}px`, height: `${realCanvasHeight}px` }"
+    >
+      <text class="percent">
+        <text class="text-60rpx font-800">
+          {{ formattedPercent }}
+        </text>
+        %
+      </text>
+      <text class="title">
+        {{ title }}
+      </text>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.circular-container {
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  .circular-bg,
+  .circular-progress {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 1;
+  }
+
+  .center-label {
+    position: absolute;
+    top: 24rpx;
+    left: 0;
+    z-index: 2;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+
+    .title {
+      font-size: 22rpx;
+      color: #000;
+    }
+
+    .percent {
+      font-size: 28rpx;
+      font-weight: bold;
+      color: #000;
+    }
+  }
+}
+</style>

+ 1 - 0
src/uni-pages.d.ts

@@ -32,6 +32,7 @@ type _LocationUrl =
   "/subPack-charge/chargeDetail/chargeDetail" |
   "/subPack-charge/chargeing/chargeing" |
   "/subPack-charge/chargeMap/chargeMap" |
+  "/subPack-charge/chargeSearchList/chargeSearchList" |
   "/subPack-charge/chargeSiteDetail/chargeSiteDetail" |
   "/subPack-charge/chargeStart/chargeStart" |
   "/subPack-charge/index/index";