瀏覽代碼

Merge remote-tracking branch 'origin/zzx'

zouzexu 12 小時之前
父節點
當前提交
f4dc167e97

+ 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==}

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

@@ -1471,5 +1471,506 @@ namespace Api {
     spec?: string
     [property: string]: any
   }
+  // ===============充电模块================
+  interface chargeStationInfoPage {
+    /**
+     * 充电站id
+     */
+    stationId?: number
+    /**
+     * 充电站名称
+     */
+    stationName?: string
+    /**
+     * 提示语
+     */
+    tips?: string
+    /**
+     * 快充
+     */
+    fastCharging?: string
+    /**
+     * 慢充
+     */
+    slowCharging?: string
+    /**
+     * 距离
+     */
+    distance?: number
+    /**
+     * 企业价格
+     */
+    enterprisePrice?: number
+    /**
+     * 平台价格
+     */
+    platformPrice?: number
+    /**
+     * 当前峰值
+     */
+    peakValue?: string
+    /**
+     * 时间段
+     */
+    peakTime?: string
+  }
+
+  interface chargeStationDetail {
+    /**
+     * 详细地址
+     */
+    address?: string
+    /**
+     * 营业时间(如:周一至周日 00:00-24:00)
+     */
+    businessHours?: string
+    /**
+     * 充电终端列表
+     */
+    connectorList?: ConnectorInfoVO[]
+    /**
+     * 当前时段(如:峰10:00-13:00)
+     */
+    currentPeriod?: string
+    /**
+     * 当前价(元/度)
+     */
+    currentPrice?: number
+    /**
+     * 客服热线
+     */
+    customerServiceHotline?: string
+    /**
+     * 距离(km)
+     */
+    distance?: number
+    /**
+     * 企业专享价(元/度)
+     */
+    enterprisePrice?: number
+    /**
+     * 单笔预计省(元)
+     */
+    estimatedSaving?: number
+    /**
+     * 是否企业用户
+     */
+    firmUser?: boolean
+    /**
+     * 空闲终端数
+     */
+    idleCount?: number
+    /**
+     * 发票提供方
+     */
+    invoiceProvider?: string
+    /**
+     * 纬度
+     */
+    latitude?: number
+    /**
+     * 经度
+     */
+    longitude?: number
+    /**
+     * 占用终端数
+     */
+    occupiedCount?: number
+    /**
+     * 离线终端数
+     */
+    offlineCount?: number
+    /**
+     * 原价/划线价(元/度)
+     */
+    originalPrice?: number
+    /**
+     * 站点图片列表(JSON数组)
+     */
+    pictures?: string
+    /**
+     * 服务提供方
+     */
+    serviceProvider?: string
+    /**
+     * 是否显示特惠价标签
+     */
+    showSpecialPriceTag?: boolean
+    /**
+     * 站点ID
+     */
+    stationId?: number
+    /**
+     * 站点名称
+     */
+    stationName?: string
+    /**
+     * 提示语/标签(如:充电减免2小时停车费,超出部分按每小时3元计费)
+     */
+    tips?: string
+  }
+
+  interface chargeStationPrices {
+    /**
+     * 是否有企业价格
+     */
+    hasEnterprisePrice?: boolean
+    /**
+     * 价格列表
+     */
+    priceList?: PriceItemVO[]
+    /**
+     * 站点ID
+     */
+    stationId?: number
+    /**
+     * 站点名称
+     */
+    stationName?: string
+    /**
+     * 提示语/标签(如:充电减免2小时停车费,超出部分按每小时3元计费)
+     */
+    tips?: string
+  }
+
+  interface chargeStationConnectors {
+    /**
+     * 充电终端列表
+     */
+    connectorList?: ConnectorItemVO[]
+    /**
+     * 空闲终端数
+     */
+    idleCount?: number
+    /**
+     * 占用终端数
+     */
+    occupiedCount?: number
+    /**
+     * 离线终端数
+     */
+    offlineCount?: number
+    /**
+     * 站点ID
+     */
+    stationId?: number
+    /**
+     * 站点名称
+     */
+    stationName?: string
+    /**
+     * 提示语/标签(如:充电减免2小时停车费,超出部分按每小时3元计费)
+     */
+    tips?: string
+  }
+
+  interface chargeConnectorDetail {
+    /**
+     * 用户可用余额(元)
+     */
+    availableBalance?: number
+    /**
+     * 充电设备接口编码
+     */
+    connectorCode?: string
+    /**
+     * 充电设备接口ID
+     */
+    connectorId?: number
+    /**
+     * 充电设备接口名称
+     */
+    connectorName?: string
+    /**
+     * 接口类型:1-家用插座 2-交流接口插座 3-交流接口插头 4-直流接口枪头 5-无线充电座 6-其他
+     */
+    connectorType?: number
+    /**
+     * 接口类型名称
+     */
+    connectorTypeName?: string
+    /**
+     * 额定电流(A)
+     */
+    current?: number
+    /**
+     * 当前时段描述
+     */
+    currentPeriodDesc?: string
+    /**
+     * 常规价格(元/度),电价+服务费+常规运营费+增值费用
+     */
+    currentPrice?: number
+    /**
+     * 企业价格(元/度),电价+服务费+企业运营费+增值费用,仅企业用户有值
+     */
+    enterprisePrice?: number
+    /**
+     * 设备编码
+     */
+    equipmentCode?: string
+    /**
+     * 设备ID
+     */
+    equipmentId?: number
+    /**
+     * 设备名称
+     */
+    equipmentName?: string
+    /**
+     * 设备类型:1-直流设备 2-交流设备 3-交直流一体设备 4-无线设备 5-其他
+     */
+    equipmentType?: number
+    /**
+     * 设备类型名称
+     */
+    equipmentTypeName?: string
+    /**
+     * 是否企业用户
+     */
+    isFirmUser?: boolean
+    /**
+     * 最后更新时间
+     */
+    lastUpdateTime?: string
+    /**
+     * 国家标准:1-2011 2-2015
+     */
+    nationalStandard?: number
+    /**
+     * 国家标准名称
+     */
+    nationalStandardName?: string
+    /**
+     * 新用户首单优惠金额(元),仅新用户且有优惠活动时返回
+     */
+    newUserDiscount?: number
+    /**
+     * 提示语/停车费说明
+     */
+    parkingTips?: string
+    /**
+     * 车位号
+     */
+    parkNo?: string
+    /**
+     * 额定功率(kW)
+     */
+    power?: number
+    /**
+     * 充电站地址
+     */
+    stationAddress?: string
+    /**
+     * 充电站ID
+     */
+    stationId?: number
+    /**
+     * 充电站名称
+     */
+    stationName?: string
+    /**
+     * 终端状态:0-离网 1-空闲 2-占用(未充电) 3-占用(充电中) 4-占用(预约锁定) 255-故障
+     */
+    status?: number
+    /**
+     * 终端状态名称
+     */
+    statusName?: string
+    /**
+     * 额定电压下限(V)
+     */
+    voltageLowerLimits?: number
+    /**
+     * 额定电压上限(V)
+     */
+    voltageUpperLimits?: number
+    currentVoltage?: number
+  }
+
+  interface invokeChargeList {
+    /**
+     * 充电桩编号
+     */
+    equipmentId?: number
+    /**
+     * 充电桩id
+     */
+    stationId?: number
+    /**
+     * 充电设备接口编码
+     */
+    connectorId?: number
+    /**
+     * 用户昵称
+     */
+    consigneeName?: string
+    /**
+     * 用户手机号
+     */
+    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
+  }
 
+  interface markers {
+    /**
+     * 标点id
+     */
+    id?: number
+    /**
+     * 维度
+     */
+    latitude?: number
+    /**
+     * 经度
+     */
+    longitude?: number
+    /**
+     * 标点图标
+     */
+    iconPath?: string
+    /**
+     * 标点图标宽度
+     */
+    width?: number
+    /**
+     * 标点图标高度
+     */
+    height?: number
+  }
 }

+ 13 - 1
src/api/apiDefinitions.ts

@@ -68,5 +68,17 @@ export default {
   'common.shoppingCartOrderConfirm':['GET', '/smqjh-oms/app-api/v1/shoppingCart/shoppingCartOrderConfirm/{ids}'],
   'common.addOrder':['POST', '/smqjh-oms/api/v1/order/addOrder'],
   'common.hybridPayment':['POST', '/smqjh-oms/service/pay/hybridPayment'],
-  'smqjh.getCategoryList':['POST', '/smqjh-pms/app-api/v1/categories/getCategoryList'],
+  'smqjh.getCategoryList': ['POST', '/smqjh-pms/app-api/v1/categories/getCategoryList'],
+
+  'charge.getStationInfoPage':['POST','/smqjh-system/applet/v1/homePage/getStationInfoPage'],
+  'charge.getMemberInfo': ['GET', '/smqjh-system/applet/v1/homePage/getMemberInfo'],
+  'charge.detail': ['GET', '/smqjh-system/applet/v1/station/detail'],
+  'charge.prices':['GET','/smqjh-system/applet/v1/station/prices'],
+  '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'],
+  'charge.stationInfoMapList': ['GET', '/smqjh-system/applet/v1/homePage/getStationInfoMapList'],
 };

+ 117 - 4
src/api/globals.d.ts

@@ -195,7 +195,7 @@ declare global {
       refundDetails<
         Config extends Alova2MethodConfig<apiResData<Api.OrderRefundDetails>> & {
           data: {
-             refundNumber: string;
+            refundNumber: string;
           };
         }
       >(
@@ -214,7 +214,7 @@ declare global {
         Config extends Alova2MethodConfig<listData<Api.AppRefundOrderListVo>> & {
           data: {
             pageNum: number;
-            pageSize:number
+            pageSize: number
           };
         }
       >(
@@ -665,11 +665,124 @@ declare global {
         config: Config
       ): Alova2Method<apiResData<Api.smqjhCategoryList[]>, 'smqjh.getCategoryList', Config>;
     }
+    charge: {
+      getStationInfoPage<
+        Config extends Alova2MethodConfig<listData<Api.chargeStationInfoPage>> & {
+          data: {
+            pageNum?: number;
+            pageSize?: number;
+            sortType?: number;
+            longitude?: number;
+            latitude?: number;
+            userId?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<Api.chargeStationInfoPage>, 'charge.getStationInfoPage', Config>;
 
-  }
+      getMemberInfo<
+        Config extends Alova2MethodConfig<apiResData<Api.chargeMemberInfo>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.chargeMemberInfo>, 'charge.getMemberInfo', Config>;
+
+      detail<
+        Config extends Alova2MethodConfig<apiResData<Api.chargeStationDetail>> & {
+          data: {
+            stationId?: number;
+            longitude?: number | null;
+            latitude?: number | null;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.chargeStationDetail>, 'charge.detail', Config>;
 
-  var Apis: Apis;
+      prices<
+        Config extends Alova2MethodConfig<apiResData<Api.chargeStationPrices>> & {
+          data: {
+            stationId?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.chargeStationPrices>, 'charge.prices', Config>;
+
+      connectors<
+        Config extends Alova2MethodConfig<apiResData<Api.chargeStationConnectors>> & {
+          data: {
+            stationId?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.chargeStationConnectors>, 'charge.connectors', Config>;
+
+      connectorDetail<
+        Config extends Alova2MethodConfig<apiResData<Api.chargeConnectorDetail>> & {
+          data: {
+            connectorCode?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.chargeConnectorDetail>, 'charge.detail', Config>;
+
+      invokeCharge<
+        Config extends Alova2MethodConfig<any> & {
+          data: Api.invokeChargeList;
+        }
+      >(
+        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>;
+
+      stationInfoMapList<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          data: {
+            longitude?: number|null;
+            latitude?: number|null;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'charge.stationInfoMapList', Config>;
+    }
+  }
 }
+var Apis: Apis;
 
 
 export interface loginModel {

+ 2 - 0
src/config/index.ts

@@ -6,6 +6,8 @@ const mapEnvVersion = {
   // develop: 'http://192.168.1.101:8080',
   // develop: 'http://192.168.0.157:8080',
   // develop: 'http://192.168.1.253:8080',
+  develop: 'http://192.168.0.19:8080',
+  // develop: 'http://192.168.0.217:8080',
   // develop: 'http://192.168.1.89:8080', // 田
   // develop: 'http://47.109.84.152:8081',
   develop: 'https://smqjh.api.zswlgz.com',

+ 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": []
+  }
+}

+ 60 - 0
src/pages.json

@@ -310,6 +310,66 @@
     {
       "root": "subPack-charge",
       "pages": [
+        {
+          "path": "chargeDetail/chargeDetail",
+          "type": "page",
+          "name": "charge-detail",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "充电详情",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "chargeing/chargeing",
+          "type": "page",
+          "name": "chargeing",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "正在充电",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "chargeMap/chargeMap",
+          "type": "page",
+          "name": "charge-map",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "地图模式",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "chargeSearchList/chargeSearchList",
+          "type": "page",
+          "name": "cahrge-search-list",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "搜索列表",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "chargeSiteDetail/chargeSiteDetail",
+          "type": "page",
+          "name": "charge-site-detail",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "站点详情",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "chargeStart/chargeStart",
+          "type": "page",
+          "name": "charge-start",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "启动终端",
+            "navigationStyle": "custom"
+          }
+        },
         {
           "path": "index/index",
           "type": "page",

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

@@ -55,7 +55,7 @@ onReachBottom(() => {
 const navList = computed(() => {
   const list = [
     { icon: `${StaticUrl}/xsb.png`, title: '星闪豹', name: 'xsb-homeTabbar', show: true },
-    { icon: `${StaticUrl}/smqjh-index-cd.png`, title: '充电', name: '', show: !isOnlineAudit.value }, // !isOnlineAudit.value
+    { icon: `${StaticUrl}/smqjh-index-cd.png`, title: '充电', name: 'charge-index', show: true }, // !isOnlineAudit.value
     { icon: `${StaticUrl}/smqjh-sp.png`, title: '电影演出', name: '', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-vip.png`, title: '视频权益', name: '', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-diancan.png`, title: '大牌点餐', name: '', show: !isOnlineAudit.value },

+ 200 - 0
src/subPack-charge/chargeDetail/chargeDetail.vue

@@ -0,0 +1,200 @@
+<script setup lang="ts">
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
+const activeTab = ref('price')
+definePage({
+  name: 'charge-detail',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '充电详情',
+    navigationStyle: 'custom',
+  },
+})
+const stationId = ref()
+onLoad((options: any) => {
+  stationId.value = Number(options.stationId)
+  activeTab.value = options.type
+})
+onMounted(() => {
+  getPricesList()
+  getConnectorsList()
+})
+
+const priceDetail = ref<Api.chargeStationPrices>()
+async function getPricesList() {
+  const res = await Apis.charge.prices({ data: { stationId: stationId.value } })
+  priceDetail.value = res.data
+}
+
+const connectorsDetail = ref<Api.chargeStationConnectors>()
+async function getConnectorsList() {
+  const res = await Apis.charge.connectors({ data: { stationId: stationId.value } })
+  connectorsDetail.value = res.data
+}
+
+/**
+ * 处理站点设备状态
+ *status 状态:0-离线 1-空闲 2-占用
+ */
+function getStatusImageByStatus(deviceStatus: number) {
+  switch (deviceStatus) {
+    case 1: // 空闲
+      return 'kx'
+    case 2: // 占用
+      return 'zy'
+    case 0: // 离线
+      return 'lx'
+    default:
+      return 'unknown'
+  }
+}
+// 切换选项卡的方法
+function switchTab(tab: string) {
+  activeTab.value = tab
+}
+
+// 计算样式的方法
+function getTabStyle(tab: string) {
+  return activeTab.value === tab
+    ? { background: '#9ED605', color: '#FFF' }
+    : {}
+}
+</script>
+
+<template>
+  <view class="charge-detail-page min-h-screen bg-[linear-gradient(90deg,#F1FECC_0%,#EAFEFA_100%)]">
+    <wd-navbar
+      title="充电详情" custom-style="background: linear-gradient(90deg, #F1FECC 0%, #EAFEFA 100%);"
+      :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 class="content-page box-border px24rpx">
+      <view class="flex items-center gap-24rpx">
+        <view>
+          <view class="text-32rpx font-bold">
+            {{ priceDetail?.stationName }}
+          </view>
+          <view class="mt-16rpx text-24rpx text-#AAA">
+            {{ priceDetail?.tips || '--' }}
+          </view>
+        </view>
+        <view>
+          <image
+            class="h-132rpx w-140rpx"
+            :src="`${StaticUrl}/site-name-icon.png`"
+          />
+        </view>
+      </view>
+      <view class="items-centerrounded-16rpx mt-20rpx flex bg-#FFF p-20rpx">
+        <view class="w-230rpx text-center">
+          <view class="text-32rpx text-#9ED605 font-bold">
+            {{ connectorsDetail?.idleCount }}
+          </view>
+          <view class="text-24rpx font-500">
+            空闲
+          </view>
+        </view>
+        <view class="h-76rpx w-2rpx bg-#F0F0F0" />
+        <view class="w-230rpx text-center">
+          <view class="text-32rpx text-#9ED605 font-bold">
+            {{ connectorsDetail?.occupiedCount }}
+          </view>
+          <view class="text-24rpx font-500">
+            占用
+          </view>
+        </view>
+        <view class="h-76rpx w-2rpx bg-#F0F0F0" />
+        <view class="w-230rpx text-center">
+          <view class="text-32rpx text-#9ED605 font-bold">
+            {{ connectorsDetail?.offlineCount }}
+          </view>
+          <view class="text-24rpx font-500">
+            离线
+          </view>
+        </view>
+      </view>
+      <view class="mt-28rpx">
+        <view class="flex items-center justify-between rounded-42rpx bg-#FFF p-6rpx">
+          <view
+            class="h-80rpx w-348rpx rounded-60rpx text-center line-height-[80rpx]" :style="getTabStyle('price')"
+            @click="switchTab('price')"
+          >
+            电站价格
+          </view>
+          <view
+            class="h-80rpx w-348rpx rounded-60rpx text-center line-height-[80rpx]" :style="getTabStyle('terminal')"
+            @click="switchTab('terminal')"
+          >
+            充电终端
+          </view>
+        </view>
+      </view>
+      <view v-if="activeTab == 'price'">
+        <view v-for="item in priceDetail?.priceList" :key="item.timePeriod" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx" :style="{ border: item.currentPeriod ? '2rpx solid #9ED605' : '' }">
+          <view class="relative flex items-center justify-between">
+            <view class="flex items-center gap-20rpx">
+              <view
+                class="h-40rpx w-40rpx rounded-8rpx bg-#CCC text-center text-28rpx text-#FFF font-bold line-height-[40rpx]"
+              >
+                {{ item.periodFlagName }}
+              </view>
+              <view class="text-28rpx font-bold">
+                {{ item.timePeriod }}
+              </view>
+            </view>
+            <view
+              v-if="item.currentPeriod"
+              class="absolute h-40rpx w-152rpx rounded-[0_16rpx_0_16rpx] bg-[linear-gradient(99deg,#D2F670_0%,#9ED605_100%)] text-center text-28rpx text-#FFF font-600 -right-24rpx -top-24rpx"
+            >
+              当前时段
+            </view>
+          </view>
+          <view class="mt-24rpx rounded-16rpx bg-#F6F6F6 p-20rpx">
+            <view class="flex items-center justify-between">
+              <view class="text-24rpx text-#222222">
+                抵扣券电价
+              </view>
+              <view class="text-24rpx" :style="{ color: item.currentPeriod ? '#FF6464' : '', fontWeight: item.currentPeriod ? '800' : '' }">
+                <text>{{ item.totalPrice }}</text>
+                <text class="text-#AAA">
+                  元/度
+                </text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view v-if="activeTab == 'terminal'">
+        <view v-for="item in connectorsDetail?.connectorList" :key="item.connectorId" class="mt-20rpx flex items-center gap-20rpx rounded-16rpx bg-#FFF p-20rpx">
+          <view
+            class="h-116rpx w-116rpx text-center line-height-[116rpx]"
+            :style="{ backgroundImage: `url(${StaticUrl}/site-status-${getStatusImageByStatus(item.status)}.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }"
+          >
+            <view class="text-24rpx font-bold">
+              {{ item.statusName }}
+            </view>
+          </view>
+          <view>
+            <view class="text-bold text-28rpx">
+              {{ item.connectorName }}
+            </view>
+            <view class="mt-4rpx text-24rpx text-#AAA">
+              电类分类:{{ item.equipmentType }}
+            </view>
+            <view class="mt-4rpx w-400rpx overflow-hidden truncate whitespace-nowrap text-24rpx text-#AAA">
+              终端编号:{{ item.connectorCode }}
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="h-180rpx" />
+    <view class="fixed bottom-66rpx left-24rpx h-100rpx w-702rpx rounded-16rpx bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-28rpx font-800 line-height-[100rpx] shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]">
+      扫码充电
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 129 - 0
src/subPack-charge/chargeMap/chargeMap.vue

@@ -0,0 +1,129 @@
+<script setup lang="ts">
+import chargeSearch from '../components/search.vue'
+import chargeFooter from '../components/charge-tab.vue'
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+const { Location } = storeToRefs(useAddressStore())
+const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
+definePage({
+  name: 'charge-map',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '地图模式',
+    navigationStyle: 'custom',
+  },
+})
+onMounted(() => {
+  getStationInfoMapList()
+})
+const markersData = ref<any[]>([])
+async function getStationInfoMapList() {
+  const res = await Apis.charge.stationInfoMapList({ data: { longitude: Location.value.longitude, latitude: Location.value.latitude } })
+  res.data.forEach((item: any) => {
+    markersData.value.push({
+      id: item.stationId,
+      latitude: item.latitude,
+      longitude: item.longitude,
+      iconPath: '',
+      width: 30,
+      height: 30,
+    })
+  })
+}
+const markerShow = ref(true)
+function openMarkerTap(item: any) {
+  console.log(item, '点击标点')
+  markerShow.value = true
+}
+</script>
+
+<template>
+  <view class="map-page min-h-screen bg-#F6FAFF">
+    <wd-navbar
+      title="" custom-style="background:linear-gradient( 180deg, #E2FF91 0%, rgba(158,214,5,0) 100%)"
+      :bordered="false" :z-index="99" safe-area-inset-top left-arrow fixed @click-left="router.back()"
+    >
+      <template #left>
+        <view class="flex items-center">
+          <wd-icon name="arrow-left" size="22px" color="#000" />
+          <view class="relative z-10 h62rpx w-full w180rpx opacity-100">
+            <image class="absolute left-0 top-0 h62rpx w180rpx" :src="`${StaticUrl}/charge-logo.png`" />
+          </view>
+        </view>
+      </template>
+    </wd-navbar>
+    <!-- <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" /> -->
+    <view class="relative">
+      <view
+        class="absolute z-1 box-border px24rpx"
+        :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }"
+      >
+        <charge-search
+          map-mode-text-value="列表模式" :map-mode-icon="`${StaticUrl}/charge-list.png`"
+          @map-mode-click="router.push({ name: 'charge-index' })"
+        />
+      </view>
+      <view class="absolute left-0 top-0 z-0 h-full w-full">
+        <map
+          style="width: 100%;height: 100vh;" :longitude="Location.longitude || 113.264435"
+          :latitude="Location.latitude || 23.129163" :markers="markersData" @markertap.stop="openMarkerTap"
+        />
+      </view>
+      <view class="absolute top-900rpx px-24rpx">
+        <view class="h-432rpx w-702rpx rounded-32rpx bg-#FFF p-24rpx">
+          <view class="flex items-center justify-between">
+            <view class="text-32rpx font-800">
+              贵阳花果园购物中心充电站
+            </view>
+            <view
+              class="ml-150rpx h-44rpx w-148rpx flex items-center border-2rpx border-#9ED605 rounded-34rpx border-solid line-height-[44rpx]"
+            >
+              <view class="w-44rpx rounded-[34rpx_0rpx_0rpx_34rpx] bg-#9ED605 text-center">
+                <wd-icon name="location" size="16px" color="#FFF" />
+              </view>
+              <view class="text-24rpx text-#9ED605">
+                1.55km
+              </view>
+            </view>
+          </view>
+          <view class="mt-20rpx text-24rpx text-#AAA">
+            贵州省贵阳市观山湖区贵安新区群航新能源责任有限公司
+          </view>
+          <view class="flex items-center">
+            <view class="h-120rpx w-204rpx rounded-8rpx bg-[linear-gradient(180deg,#45E67D_0%,rgba(218,249,229,0)_100%)] text-center">
+              <view class="h-40rpx w-100rpx rounded-8rpx bg-[linear-gradient(180deg,#4FEF86_0%,#00AA3A_100%)]">
+                快充
+              </view>
+              <view>12/12</view>
+            </view>
+            <view>
+              <view>快充</view>
+              <view>12/12</view>
+            </view>
+            <view>
+              <view>快充</view>
+              <view>12/12</view>
+            </view>
+          </view>
+          <view>
+            <view>
+              <text>1.0026</text>
+              <text>元/度</text>
+            </view>
+            <view>峰:10:00-13:00</view>
+          </view>
+          <view>
+            <view>P</view>
+            <view>充电减免2小时停车费,超出部分按每小时3元计算</view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <chargeFooter />
+  </view>
+</template>
+
+<style lang="scss" scoped>
+
+</style>

+ 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>

+ 313 - 0
src/subPack-charge/chargeSiteDetail/chargeSiteDetail.vue

@@ -0,0 +1,313 @@
+<script setup lang="ts">
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+// const swiperList = ['https://www.keaitupian.cn/cjpic/frombd/2/253/1659552792/3869332496.jpg']
+const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
+const { Location } = storeToRefs(useAddressStore())
+const { opcity } = storeToRefs(useSysStore())
+const stationId = ref()
+onLoad((options: any) => {
+  console.log(options)
+  stationId.value = Number(options.stationId)
+})
+definePage({
+  name: 'charge-site-detail',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '站点详情',
+    navigationStyle: 'custom',
+  },
+})
+const activeFilter = ref('0')
+onMounted(() => {
+  getStationDetail()
+  opcity.value = 0
+})
+/**
+ * 获取电站详情
+ */
+const stationDetail = ref<Api.chargeStationDetail>()
+async function getStationDetail() {
+  useGlobalLoading().loading({})
+  const res = await Apis.charge.detail({ data: { stationId: stationId.value, latitude: Location.value.latitude, longitude: Location.value.longitude } })
+  stationDetail.value = res.data
+  useGlobalLoading().close()
+}
+// 使用计算属性动态计算筛选选项
+const filterOptions = computed(() => {
+  const counts = {
+    0: 0, // 离线
+    1: 0, // 空闲
+    2: 0, // 占用
+  }
+
+  // 遍历充电站连接器列表,统计各状态数量
+  if (stationDetail.value?.connectorList) {
+    stationDetail.value.connectorList.forEach((item) => {
+      // 检查status是否为有效键值
+      if (item.status === 0 || item.status === 1 || item.status === 2) {
+        counts[item.status as keyof typeof counts]++
+      }
+    })
+  }
+
+  return [
+    { key: '0', label: `离线(${counts['0']})` },
+    { key: '1', label: `空闲(${counts['1']})` },
+    { key: '2', label: `占用(${counts['2']})` },
+  ]
+})
+
+// 添加计算属性来过滤设备列表
+const filteredConnectors = computed(() => {
+  if (!stationDetail.value?.connectorList)
+    return []
+  // 根据当前选中的状态过滤设备,并限制为前3个
+  return stationDetail.value.connectorList.filter(
+    item => String(item.status) === activeFilter.value,
+  ).slice(0, 3)
+})
+/**
+ * 处理站点设备状态
+ *status 状态:0-离线 1-空闲 2-占用
+ */
+function getStatusImageByStatus(deviceStatus: number) {
+  switch (deviceStatus) {
+    case 1: // 空闲
+      return 'kx'
+    case 2: // 占用
+      return 'zy'
+    case 0: // 离线
+      return 'lx'
+    default:
+      return 'unknown'
+  }
+}
+
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+
+// 处理筛选项点击的方法
+function handleFilterClick(filterKey: string) {
+  activeFilter.value = filterKey
+  console.log(`选择了: ${filterKey}`)
+}
+
+function openMap() {
+  uni.openLocation({
+    latitude: Number(stationDetail.value?.latitude),
+    longitude: Number(stationDetail.value?.longitude),
+  })
+}
+</script>
+
+<template>
+  <view class="site-detail-page min-h-screen bg-#F6FAFF">
+    <wd-navbar
+      title="站点详情" :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 class="site-detail-content">
+      <!-- <view>
+        <wd-swiper :list="swiperList" :height="260" :indicator="{ type: 'fraction' }" value-key="advertImg" />
+      </view> -->
+      <view class="bg-#FFF p-20rpx">
+        <view class="mt-28rpx box-border px24rpx">
+          <view class="text-32rpx font-800">
+            {{ stationDetail?.stationName }}
+          </view>
+          <view class="mt-20rpx flex items-center gap-20rpx">
+            <view class="h-30rpx w-30rpx rounded-4rpx bg-#5BE7FF text-center text-24rpx text-#fff line-height-[30rpx]">
+              P
+            </view>
+            <view class="text-24rpx text-#AAA">
+              {{ stationDetail?.tips || '--' }}
+            </view>
+          </view>
+        </view>
+        <view class="ml-10rpx">
+          <view
+            class="cardBg-set mt-20rpx box-border h-138rpx w-680rpx flex items-center justify-between rounded-16rpx"
+            :style="{ backgroundImage: `url(${StaticUrl}/site-detail-navBg.png)` }"
+          >
+            <view class="ml-24rpx">
+              <view class="text-28rpx text-#2B303A font-bold">
+                距离您{{ stationDetail?.distance || '--' }}km
+              </view>
+              <view class="mt-8rpx w-408rpx overflow-hidden text-ellipsis whitespace-nowrap text-24rpx text-#AAA">
+                {{ stationDetail?.address }}
+              </view>
+            </view>
+            <view class="mr-24rpx" @click="openMap">
+              <view>
+                <image class="h-40rpx w-40rpx" :src="`${StaticUrl}/site-detail-nav.png`" />
+              </view>
+              <view class="mt-8rpx text-24rpx">
+                导航
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="mt20rpx box-border px24rpx">
+      <view class="rounded-24rpx bg-#FFF p-24rpx">
+        <view class="flex items-center justify-between">
+          <view class="text-32rpx font-bold">
+            费用信息
+          </view>
+          <view class="flex items-center" @click="router.push({ name: 'charge-detail', params: { stationId: String(stationDetail?.stationId), type: 'price' } })">
+            <view class="text-24rpx text-#AAA">
+              查看全部
+            </view>
+            <wd-icon name="chevron-right" size="22px" color="#AAAAAA" />
+          </view>
+        </view>
+        <view class="mt-24rpx rounded-16rpx bg-[linear-gradient(90deg,#FEE4C6_0%,rgba(251,235,198,0.23)_100%)] p-24rpx">
+          <view class="relative flex items-center justify-between">
+            <view class="text-28rpx font-bold">
+              当前价
+            </view>
+            <view class="absolute -right-20rpx -top-20rpx">
+              <image class="h-52rpx w-125rpx" :src="`${StaticUrl}/site-price-tag.png`" />
+            </view>
+          </view>
+          <view class="mt-24rpx flex items-center justify-between">
+            <view>
+              <view class="flex items-center">
+                <text class="text-48rpx text-#FF6464 font-800">
+                  {{ stationDetail?.currentPrice }}
+                </text>
+                <text class="text-24rpx">
+                  元/度
+                </text>
+              </view>
+              <view class="mt-12rpx text-28rpx">
+                当前时段:
+              </view>
+              <view class="text-bold mt-16rpx text-28rpx">
+                {{ stationDetail?.currentPeriod }}
+              </view>
+            </view>
+            <view>
+              <image class="h-182rpx w-88rpx" :src="`${StaticUrl}/site-price-icon.png`" />
+            </view>
+          </view>
+        </view>
+      </view>
+      <view class="mt-20rpx rounded-24rpx bg-#FFF p-24rpx">
+        <view class="flex items-center justify-between">
+          <view class="text-32rpx font-bold">
+            充电终端
+          </view>
+          <view class="flex items-center" @click="router.push({ name: 'charge-detail', params: { stationId: String(stationDetail?.stationId), type: 'terminal' } })">
+            <view class="text-24rpx text-#AAA">
+              查看全部
+            </view>
+            <wd-icon name="chevron-right" size="22px" color="#AAAAAA" />
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center gap-20rpx">
+          <view
+            v-for="item in filterOptions" :key="item.key"
+            class="select-item h-60rpx w-152rpx text-28rpx line-height-[60rpx]" :class="[{ 'select-item-active': activeFilter === item.key }]" @click="handleFilterClick(item.key)"
+          >
+            {{ item.label }}
+          </view>
+        </view>
+        <view class="mt-24rpx">
+          <view v-for="item in filteredConnectors" :key="item.connectorId" class="mb-20rpx flex items-center gap-20rpx rounded-16rpx bg-#F6F6F6 p-20rpx" @click="router.push({ name: 'charge-start', params: { connectorCode: item.connectorCode } })">
+            <view
+              class="h-116rpx w-116rpx text-center"
+              :style="{ backgroundImage: `url(${StaticUrl}/site-status-${getStatusImageByStatus(item.status)}.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }"
+            >
+              <image
+                class="mt-20rpx h-38rpx w-27.18rpx"
+                :src="`${StaticUrl}/terminal-icon.png`"
+              />
+              <view class="text-24rpx font-bold">
+                {{ item?.statusName }}
+              </view>
+            </view>
+            <view>
+              <view class="text-bold text-28rpx">
+                {{ item.connectorName }}
+              </view>
+              <view class="mt-4rpx text-24rpx text-#AAA">
+                电类分类:{{ item.equipmentType }}
+              </view>
+              <view class="mt-4rpx w-300rpx overflow-hidden truncate whitespace-nowrap text-24rpx text-#AAA">
+                终端编号:{{ item.connectorCode }}
+              </view>
+            </view>
+          </view>
+          <view v-if="filteredConnectors.length < 1" class="h-100rpx w-full text-center text-24rpx text-#AAA line-height-[100rpx]">
+            暂无终端信息
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="h-170rpx" />
+    <view class="fixed bottom-0 left-0 right-0 h-166rpx w-full rounded-[30rpx_30rpx_0_0] bg-#FFF">
+      <view
+        class="h-166rpx"
+        :style="{ backgroundImage: `url(${StaticUrl}/site-price-bg.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }"
+      >
+        <view class="h-166rpx flex items-center justify-center gap-32rpx">
+          <view class="ml-248rpx">
+            <text class="text-bold text-#F5531A">
+              ¥
+            </text>
+            <text class="text-bold text-40rpx text-#F5531A">
+              {{ stationDetail?.currentPrice }}
+            </text>
+            <text class="text-24rpx text-#AAA">
+              元/度
+            </text>
+          </view>
+          <view class="scan-qrcode">
+            扫码充电
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.cardBg-set {
+  background-size: cover;
+  background-position: center;
+}
+
+.select-item {
+  background: #f5f3f3;
+  border-radius: 16rpx;
+  color: #2B303A;
+  text-align: center;
+}
+
+.select-item-active {
+  background: #9ED605;
+  box-shadow: inset 0rpx 20rpx 40rpx 2rpx rgba(100, 255, 218, 0.26);
+  border-radius: 0rpx 16rpx 0rpx 16rpx;
+  font-weight: bold;
+  color: #FFFFFF;
+}
+
+.scan-qrcode {
+  width: 220rpx;
+  height: 100rpx;
+  background: linear-gradient(90deg, #DBFC81 0%, #9ED605 100%);
+  box-shadow: inset 0rpx 6rpx 20rpx 2rpx #FFFFFF;
+  border-radius: 16rpx;
+  font-weight: 800;
+  font-size: 28rpx;
+  line-height: 112rpx;
+  text-align: center;
+}
+</style>

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

@@ -0,0 +1,136 @@
+<script setup lang="ts">
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+const { userInfo } = storeToRefs(useUserStore())
+definePage({
+  name: 'charge-start',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '启动终端',
+    navigationStyle: 'custom',
+  },
+})
+const connectorCode = ref()
+onLoad((options: any) => {
+  connectorCode.value = options.connectorCode
+})
+
+onMounted(() => {
+  opcity.value = 0
+  getConnectorDetail()
+})
+
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+
+const connectorDetailInfo = ref<Api.chargeConnectorDetail>()
+async function getConnectorDetail() {
+  const res = await Apis.charge.connectorDetail({ data: { connectorCode: connectorCode.value } })
+  connectorDetailInfo.value = res.data
+}
+
+const fromData = ref<Api.invokeChargeList>({
+  equipmentId: 0,
+  stationId: 0,
+  connectorId: 0,
+  consigneeName: userInfo.value?.nickName,
+  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
+  await Apis.charge.invokeCharge({ data: fromData.value })
+}
+</script>
+
+<template>
+  <view class="charge-start-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"
+      :z-index="999" safe-area-inset-top left-arrow fixed @click-left="router.back()"
+    />
+    <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
+    <view>
+      <view class="h-486rpx w-648rpx">
+        <image
+          class="h-full w-full"
+          :src="`${StaticUrl}/start-charge.png`"
+        />
+      </view>
+      <view class="mt-94rpx flex items-center justify-center">
+        <view class="w-182rpx text-center">
+          <view class="text-28rpx text-#AAA">
+            电流A
+          </view>
+          <view class="mt-24rpx text-32rpx font-800">
+            {{ connectorDetailInfo?.current }}
+          </view>
+        </view>
+        <view class="h-40rpx w-2rpx bg-#DEDEDE" />
+        <view class="w-182rpx text-center">
+          <view class="text-28rpx text-#AAA">
+            电压V
+          </view>
+          <view class="mt-24rpx text-32rpx font-800">
+            {{ connectorDetailInfo?.currentVoltage || '0' }}
+          </view>
+        </view>
+        <view class="h-40rpx w-2rpx bg-#DEDEDE" />
+        <view class="w-182rpx text-center">
+          <view class="text-28rpx text-#AAA">
+            功率KW
+          </view>
+          <view class="mt-24rpx text-32rpx font-800">
+            {{ connectorDetailInfo?.power }}
+          </view>
+        </view>
+      </view>
+      <view class="mt-40rpx px24rpx">
+        <view class="flex items-center justify-between">
+          <view class="text-28rpx font-bold">
+            当前电价
+          </view>
+          <view class="flex items-center">
+            <text class="text-30rpx text-#FC5A5A font-bold">
+              {{ connectorDetailInfo?.currentPrice }}元
+            </text>
+            <text class="text-28rpx text-#AAA">
+              /度
+            </text>
+          </view>
+        </view>
+        <view class="mt-20rpx flex items-center justify-between">
+          <view class="text-28rpx font-bold">
+            当前时段
+          </view>
+          <view class="text-28rpx text-#AAA">
+            {{ connectorDetailInfo?.currentPeriodDesc }}
+          </view>
+        </view>
+        <view class="mt-20rpx flex items-center justify-between">
+          <view class="text-28rpx font-bold">
+            停车参考
+          </view>
+          <view class="text-28rpx text-#AAA">
+            {{ connectorDetailInfo?.parkingTips || '--' }}
+          </view>
+        </view>
+        <view class="mt-28rpx text-24rpx text-#AAA">
+          账单信息可能会有所延迟,具体以实际结算为准
+        </view>
+      </view>
+    </view>
+    <view class="fixed bottom-66rpx left-24rpx h-100rpx w-702rpx rounded-16rpx bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-28rpx font-800 line-height-[100rpx] shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]" @click="launchCharge">
+      启动终端充电
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 245 - 0
src/subPack-charge/chargeing/chargeing.vue

@@ -0,0 +1,245 @@
+<script setup lang="ts">
+import wlpProgress from '../components/wlp-progress-new/wlp-progress-new.vue'
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+const stopCharge = ref(false)
+const current = ref(0)
+const isLongPressing = ref(false)
+const longPressTimer = ref< NodeJS.Timeout>()
+const longPressDuration = 2000 // 3秒
+
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+definePage({
+  name: 'chargeing',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '正在充电',
+    navigationStyle: 'custom',
+  },
+})
+onMounted(() => {
+  getChargeingCost()
+  opcity.value = 0
+})
+
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  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
+  const startTime = Date.now()
+  longPressTimer.value = setInterval(() => {
+    if (isLongPressing.value) {
+      const elapsed = Date.now() - startTime
+      current.value = Math.round(Math.min(100, (elapsed / longPressDuration) * 100))
+      if (elapsed >= longPressDuration) {
+        current.value = 100
+        console.log('停止充电')
+        initiatedStopCharge()
+      }
+    }
+  }, 50) // 减少频率到50ms,避免抖动
+}
+
+function onTouchEnd() {
+  isLongPressing.value = false
+  if (current.value < 100) {
+    current.value = 0
+  }
+  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)
+})
+</script>
+
+<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="!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 v-if="!chargeingDetail">
+      <charge-loading />
+    </view> -->
+    <view v-if="chargeingDetail?.status == '1'">
+      <view class="flex items-center justify-center">
+        <wlpProgress
+          :percent="75"
+          bg-color="#ebffb2"
+          progress-color="#CCF35F"
+          gradual-color="#9ED605"
+          title="充电枪正在充电中"
+        />
+      </view>
+      <view class="h-320rpx w-750rpx">
+        <image
+          class="h-full w-full"
+          :src="`${StaticUrl}/chargeing-in-progress.gif`"
+        />
+      </view>
+    </view>
+    <view class="mt-64rpx text-center">
+      <view class="text-60rpx text-#9ED605 font-bold">
+        {{ chargeingDetail?.chargingDurationDesc || '00:00:00' }}
+      </view>
+      <view class="mt-16rpx text-32rpx font-bold">
+        充电时间
+      </view>
+    </view>
+    <view class="mt-32rpx flex items-center justify-center">
+      <view class="w-182rpx text-center">
+        <view class="text-28rpx text-#AAA">
+          电流
+        </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`"
+          />
+        </view>
+      </view>
+      <view class="h-40rpx w-2rpx bg-#DEDEDE" />
+      <view class="w-182rpx text-center">
+        <view class="text-28rpx text-#AAA">
+          功率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`"
+          />
+        </view>
+      </view>
+      <view class="h-40rpx w-2rpx bg-#DEDEDE" />
+      <view class="w-182rpx text-center">
+        <view class="text-28rpx text-#AAA">
+          电量/度
+        </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`"
+          />
+        </view>
+      </view>
+      <view class="h-40rpx w-2rpx bg-#DEDEDE" />
+      <view class="w-182rpx text-center">
+        <view class="text-28rpx text-#AAA">
+          费用/元
+        </view>
+        <view class="mt-20rpx">
+          <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`"
+          />
+        </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">
+      <view class="mt-28rpx flex items-center justify-between">
+        <view class="text-28rpx font-bold">
+          订单编号
+        </view>
+        <view class="text-28rpx text-#AAA">
+          {{ chargeingDetail?.chargeOrderNo || '--' }}
+        </view>
+      </view>
+      <view class="mt-28rpx flex items-center justify-between">
+        <view class="text-28rpx font-bold">
+          终端编号
+        </view>
+        <view class="text-28rpx text-#AAA">
+          {{ chargeingDetail?.connectorCode || '--' }}
+        </view>
+      </view>
+      <view class="mt-28rpx flex items-center justify-between">
+        <view class="text-28rpx font-bold">
+          充电电站
+        </view>
+        <view class="text-28rpx text-#AAA">
+          {{ chargeingDetail?.stationName || '--' }}
+        </view>
+      </view>
+      <view class="mt-28rpx flex items-center justify-between">
+        <view class="text-28rpx font-bold">
+          充电终端
+        </view>
+        <view class="text-28rpx text-#AAA">
+          {{ chargeingDetail?.connectorName || '--' }}
+        </view>
+      </view>
+      <view class="mt-28rpx text-24rpx text-#AAA">
+        账单信息可能会有所延迟,具体以实际结算为准
+      </view>
+    </view>
+    <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-overlay>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 86 - 0
src/subPack-charge/components/charge-loading.vue

@@ -0,0 +1,86 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <view class="">
+    <view class="circleBox">
+      <view class="circle" />
+      <view class="circle1" />
+      <view class="circle2" />
+      <view class="circle3" />
+    </view>
+    <view class="text-center">
+      <view class="text-32rpx text-#9ED605 font-800">
+        充电正在启动中
+      </view>
+      <view class="mt-20rpx text-24rpx text-#AAA">
+        长时间无法启动可“长按结束充电"按钮结束订单
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+  .circleBox {
+  position: relative;
+  width: 400rpx;
+  height: 400rpx;
+  margin:auto;
+}
+
+.circle,
+.circle1,
+.circle2,
+.circle3 {
+  width: 80rpx;
+  height: 80rpx;
+  background: rgba(255, 255, 255, 1);
+  /* border: 1px solid rgba(62, 182, 248, 0.7); */
+  box-shadow: inset 0rpx 6rpx 40rpx 2rpx rgba(186,239,42,0.8);
+  border-radius: 999px;
+  position: absolute;
+  top: 38%;
+  left: 41%;
+  /* transform: translate(-50%,-75%); */
+}
+.circle1,
+.circle2,
+.circle3 {
+  animation-name: circleChange;
+  animation-duration: 3s;
+  animation-iteration-count: infinite;
+  animation-timing-function: linear;
+}
+.circle1 {
+  animation-delay: 1s;
+}
+.circle2 {
+  animation-delay: 2s;
+}
+.circle3 {
+  animation-delay: 3s;
+}
+
+@keyframes circleChange {
+  0% {
+    transform: scale(1);
+    opacity: 0.95;
+  }
+  25% {
+    transform: scale(2);
+    opacity: 0.75;
+  }
+  50% {
+    transform: scale(3);
+    opacity: 0.5;
+  }
+  75% {
+    transform: scale(4);
+    opacity: 0.25;
+  }
+  100% {
+    transform: scale(5);
+    opacity: 0.05;
+  }
+}
+</style>

+ 41 - 21
src/subPack-charge/components/search.vue

@@ -1,42 +1,62 @@
 <script lang="ts" setup>
-import { ref } from 'vue'
+import { ref, watch } from 'vue'
 import { StaticUrl } from '@/config'
-// 如果需要从外部传入动态文本,可以使用 props
-defineProps<{
-  initialMapModeText?: string
-}>()
-// 如果需要从外部传入点击事件处理方法,也可以定义 emit
-defineEmits<{
-  (e: 'mapModeClick'): void
-}>()
-// 定义动态文本
-const mapModeText = ref('地图模式')
+import router from '@/router'
+// 添加动态图标和文字的 props
+interface Props {
+  mapModeIcon?: string
+  mapModeTextValue?: string
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  mapModeIcon: `${StaticUrl}/charge-map.png`,
+  mapModeTextValue: '',
+})
+
+const emit = defineEmits<Emits>()
+interface Emits {
+  (e: 'mapModeClick'): void // 简化 emit,不需要事件参数
+}
+// 定义动态文本和图标
+const mapModeText = ref(props.mapModeTextValue)
+const iconPath = ref(props.mapModeIcon)
+
+// 监听外部传入的文字和图标变化
+watch(() => props.mapModeTextValue, (newVal) => {
+  mapModeText.value = newVal
+})
+
+watch(() => props.mapModeIcon, (newVal) => {
+  iconPath.value = newVal
+})
+
 // 定义点击事件处理方法
 function onMapModeClick() {
-  // 在这里添加点击逻辑
-  console.log('地图模式被点击')
-  // 示例:切换显示文本
-  // mapModeText.value = mapModeText.value === '地图模式' ? '列表模式' : '地图模式'
+  emit('mapModeClick')
+}
+
+function searchStation() {
+  router.push({ name: 'cahrge-search-list' })
 }
 </script>
 
 <template>
-  <view class="flex items-center justify-between">
-    <view class="search-box flex items-center">
+  <view class="flex items-center justify-between gap-20rpx">
+    <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="`${StaticUrl}/charge-map.png`" class="h-40rpx w-40rpx" mode="" />
-      <text>
+      <image :src="iconPath" class="h-40rpx w-40rpx" />
+      <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>

+ 92 - 46
src/subPack-charge/index/index.vue

@@ -3,9 +3,11 @@ import chargeSearch from '../components/search.vue'
 import chargeFooter from '../components/charge-tab.vue'
 import router from '@/router'
 import { StaticUrl } from '@/config'
+import { createGlobalLoadingMiddleware } from '@/api/core/middleware'
 
-const { statusBarHeight, MenuButtonHeight, opcity, userInfo } = storeToRefs(useSysStore())
-const swiperList = ['https://www.keaitupian.cn/cjpic/frombd/2/253/1659552792/3869332496.jpg']
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+const { token, userInfo } = storeToRefs(useUserStore())
+const { Location } = storeToRefs(useAddressStore())
 definePage({
   name: 'charge-index',
   islogin: false,
@@ -15,29 +17,53 @@ definePage({
     backgroundColorBottom: '#fff',
   },
 })
+const activeFilter = ref<number>(1)
 const filterOptions = [
-  { key: 'nearest', label: '离我最近', widthClass: 'w-152rpx' },
-  { key: 'most-available', label: '空闲最多', widthClass: '' },
-  { key: 'lowest-price', label: '电费最低', widthClass: '' },
+  { key: 1, label: '离我最近', widthClass: 'w-152rpx' },
+  { key: 2, label: '空闲最多', widthClass: '' },
+  { key: 3, label: '电费最低', widthClass: '' },
 ]
-const activeFilter = ref('nearest')
 
+/**
+ * 获取充电站列表
+ */
+const { data: stationList, isLastPage, page, refresh, reload } = usePagination((pageNum, pageSize) =>
+  Apis.charge.getStationInfoPage({ data: { pageNum, pageSize, sortType: activeFilter.value, longitude: Location.value.longitude || 106.620256, latitude: Location.value.latitude || 26.648327, userId: userInfo.value.id } }), {
+  data: resp => resp.data?.list,
+  initialData: [],
+  initialPage: 1,
+  initialPageSize: 10,
+  append: true,
+  immediate: true,
+  middleware: createGlobalLoadingMiddleware(),
+})
+onMounted(() => {
+  getUserAccountInfo()
+  opcity.value = 0
+})
+onShow(() => refresh())
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+onReachBottom(() => {
+  if (!isLastPage.value) {
+    page.value++
+  }
+})
+
+/**
+ * 获取用户账户信息
+ */
+const userAccountInfo = ref()
+async function getUserAccountInfo() {
+  const res = await Apis.charge.getMemberInfo({})
+  userAccountInfo.value = res.data
+}
 // 处理筛选项点击的方法
-function handleFilterClick(filterKey: string) {
+function handleFilterClick(filterKey: number) {
   activeFilter.value = filterKey
-  console.log(`选择了: ${filterKey}`)
-  // 根据不同筛选类型执行相应操作
-  switch (filterKey) {
-    case 'nearest':
-      // 执行距离排序逻辑
-      break
-    case 'most-available':
-      // 执行空闲数量排序逻辑
-      break
-    case 'lowest-price':
-      // 执行价格排序逻辑
-      break
-  }
+  reload()
 }
 </script>
 
@@ -57,7 +83,7 @@ function handleFilterClick(filterKey: string) {
       </template>
     </wd-navbar>
     <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" class="px24rpx">
-      <charge-search />
+      <charge-search map-mode-text-value="地图模式" @map-mode-click="router.push({ name: 'charge-map' })" />
       <view class="userInfo-card">
         <view class="flex items-center gap-20rpx">
           <image
@@ -68,9 +94,12 @@ function handleFilterClick(filterKey: string) {
             <view class="text-32rpx text-#2B303A font-bold">
               {{ userInfo?.nickName || '游客' }}
             </view>
-            <view class="text-24rpx text-#9ED605">
+            <view v-if="!token" class="text-24rpx text-#9ED605" @click="router.replace({ name: 'smqjh-login' })">
               授权登录
             </view>
+            <view v-else class="rounded-8rpx bg-#9ED605 px12rpx py4rpx text-24rpx text-#FFF opacity-70">
+              {{ userInfo.channelName }}
+            </view>
           </view>
         </view>
         <view class="mt-20rpx flex items-center justify-between gap-18rpx">
@@ -79,16 +108,18 @@ function handleFilterClick(filterKey: string) {
               <view>我的积分</view>
               <view>可用充电余额</view>
             </view>
-            <view class="mt-24rpx flex items-center gap-30rpx text-40rpx text-#9ED605 font-bold">
+            <view class="mt-24rpx flex items-center gap-30rpx text-28rpx text-#9ED605 font-500">
               <view class="flex items-center gap-20rpx">
-                <text>896</text>
-                <image class="h-48rpx w-48rpx" :src="`${StaticUrl}/charge-acc.png`" />
+                <text class="w-120rpx overflow-hidden truncate whitespace-nowrap">
+                  {{ userAccountInfo?.availablePoints || '--' }}
+                </text>
+                <image class="h-30rpx w-30rpx" :src="`${StaticUrl}/charge-acc.png`" />
               </view>
-              <view>
-                <text class="text-28rpx">
+              <view class="w-120rpx overflow-hidden truncate whitespace-nowrap">
+                <text class="text-20rpx">
                 </text>
-                89.6
+                {{ userAccountInfo?.balance || '--' }}
               </view>
             </view>
           </view>
@@ -97,7 +128,7 @@ function handleFilterClick(filterKey: string) {
               <view class="text-28rpx font-bold">
                 充电订单
               </view>
-              <view class="mt-24rpx text-40rpx text-#9ED605 font-bold">
+              <view class="mt-24rpx text-28rpx text-#9ED605 font-500">
                 956
               </view>
             </view>
@@ -105,9 +136,9 @@ function handleFilterClick(filterKey: string) {
           </view>
         </view>
       </view>
-      <view class="mt-24rpx">
+      <!-- <view class="mt-24rpx">
         <wd-swiper :list="swiperList" :height="100" :indicator="false" value-key="advertImg" />
-      </view>
+      </view> -->
       <view class="mt-24rpx flex items-center gap-20rpx">
         <view
           v-for="option in filterOptions"
@@ -122,16 +153,16 @@ function handleFilterClick(filterKey: string) {
         </view>
       </view>
       <view>
-        <view class="relative mt-24rpx rounded-16rpx bg-#FFFFFF p-24rpx">
+        <view v-for="item in stationList" :key="item.stationId" class="relative mt-24rpx rounded-16rpx bg-#FFFFFF p-24rpx" @click="router.push({ name: 'charge-site-detail', params: { stationId: String(item.stationId) } })">
           <image
             class="absolute right-0 top-0 h-52rpx w-186rpx"
             :src="`${StaticUrl}/charge-firm-tag.png`"
           />
           <view class="text-32rpx text-#2B303A font-bold">
-            贵阳花果园购物中心充电站
+            {{ item.stationName }}
           </view>
           <view class="mt-20rpx text-24rpx text-#aaa">
-            充电减免2小时停车费,超出部分按每小时3元计算
+            {{ item.tips || '暂无提示' }}
           </view>
           <view class="mt-24rpx flex items-center rounded-16rpx bg-[linear-gradient(90deg,rgba(170,255,235,0.3)_0%,rgba(175,247,252,0.2)_43.57%,rgba(139,232,252,0)_100%)] p-20rpx">
             <view class="flex items-center gap-16rpx">
@@ -140,10 +171,7 @@ function handleFilterClick(filterKey: string) {
               </view>
               <view class="flex items-center">
                 <text class="text-32rpx font-bold">
-                  12
-                </text>
-                <text class="text-24rpx text-#AAAAAA">
-                  /12
+                  {{ item.fastCharging }}
                 </text>
               </view>
             </view>
@@ -153,10 +181,7 @@ function handleFilterClick(filterKey: string) {
               </view>
               <view class="flex items-center">
                 <text class="text-32rpx font-bold">
-                  12
-                </text>
-                <text class="text-24rpx text-#AAAAAA">
-                  /12
+                  {{ item.slowCharging }}
                 </text>
               </view>
             </view>
@@ -165,15 +190,15 @@ function handleFilterClick(filterKey: string) {
                 <wd-icon name="location" size="16px" color="#FFF" />
               </view>
               <view class="text-24rpx text-#3EB6F8">
-                1.55km
+                {{ item.distance }}km
               </view>
             </view>
           </view>
-          <view class="mt-28rpx flex items-center justify-between">
+          <view v-if="!item.enterprisePrice" class="mt-28rpx flex items-center justify-between">
             <view class="relative flex">
               <view class="absolute left-30rpx z-10 flex items-center -top-26rpx">
                 <text class="text-40rpx text-#FF6464 font-bold">
-                  1.0026
+                  {{ item.platformPrice }}
                 </text>
                 <text class="w-100rpx text-24rpx">
                   元/度
@@ -185,11 +210,32 @@ function handleFilterClick(filterKey: string) {
               />
             </view>
             <view class="text-24rpx text-#2B303A">
-              峰:10:00-13:00
+              {{ item.peakValue }}:{{ item.peakTime }}
+            </view>
+          </view>
+          <view v-else class="mt-20rpx flex items-center justify-between">
+            <view class="flex items-center">
+              <view class="flex items-center -mt-10rpx">
+                <text class="text-40rpx text-#FF6464 font-bold">
+                  {{ item.platformPrice }}
+                </text>
+                <text class="w-100rpx text-24rpx">
+                  元/度
+                </text>
+              </view>
+              <view class="h-52rpx w-212rpx text-center" :style="{ backgroundImage: `url(${StaticUrl}/charge-firm.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }">
+                <text class="ml-70rpx text-28rpx text-#FF6464">
+                  {{ item.enterprisePrice }}
+                </text>
+              </view>
+            </view>
+            <view class="text-24rpx text-#2B303A">
+              {{ item.peakValue }}:{{ item.peakTime }}
             </view>
           </view>
         </view>
       </view>
+      <view class="h-160rpx" />
     </view>
     <chargeFooter />
   </view>

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

@@ -29,6 +29,12 @@ type _LocationUrl =
   "/subPack-common/revalueSuccess/index" |
   "/subPack-common/user-center/index" |
   "/subPack-smqjh/order/index" |
+  "/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";
 
 interface NavigateToOptions {