Files
2025-10-28 09:25:46 +08:00

525 lines
12 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<s-layout title="正在充电" navbar="normal" onShareAppMessage @clickLeft="clearTimer">
<view class="container">
<view class="charging-percentage">
<view class="percentage-container">
<image src="https://kxcharge.oss-cn-beijing.aliyuncs.com/147.png" class="circle"></image>
<view class="percentage">
<text class="value">{{ data.soc?data.soc:0 }}</text>
<text>%</text>
</view>
<view class="status">
充电中...
</view>
</view>
<view class="name">
<text>{{data.stationName}}</text>
<image src="https://kxcharge.oss-cn-beijing.aliyuncs.com/27574.png"></image>
</view>
</view>
<view class="detail-info">
<view class="rest-time">
<image src="https://kxcharge.oss-cn-beijing.aliyuncs.com/27698 (2).png"></image>
<text>预计充满时间{{ estimatedFullTime }}</text>
</view>
<view class="cost-info">
<view class="item">
<view class="name">
已充电量
</view>
<view class="value">
{{data.chargedPower}}
</view>
</view>
<view class="line"></view>
<view class="item">
<view class="name">
充电时长
</view>
<view class="value">
{{chargingDuration}}
</view>
</view>
<view class="line"></view>
<view class="item">
<view class="name">
费用总计
</view>
<view class="value">
{{data.chargedAmount}}
</view>
</view>
</view>
<view class="cable-info">
<view class="item">
<text>实时电流</text>
<text class="value">{{data.outputCurrent}}</text>
</view>
<view class="item">
<text>实时电压</text>
<text class="value">{{data.outputVoltage}}</text>
</view>
<view class="item">
<text>实时功率</text>
<text class="value">{{ (data.outputCurrent * data.outputVoltage).toFixed(2) }}KW</text>
</view>
</view>
<view class="split-line"></view>
<view style="width:100%;">
<view class="header">
<view class="line"></view>
<view>
订单信息
</view>
</view>
</view>
<view style="width: 100%;">
<view class="list-item">
<text>订单编号</text>
<text class="value">{{data.seqNumber}}</text>
</view>
<view class="list-item">
<text>绑定车牌</text>
<text class="value">{{data.carNumber}}</text>
</view>
<view class="list-item">
<text>充电时间</text>
<text class="value">{{ formatTime }}</text>
</view>
<view class="list-item">
<text>充电桩编号</text>
<text class="value">{{data.equipmentConnectorCode}}</text>
</view>
</view>
</view>
<ljs-loading v-if="progress>0">{{progress}}%</ljs-loading>
</view>
<template v-slot:kxTabBar>
<kx-tabbar-wrapper>
<view class="tab-bar">
<button class="left item" @click="toPage('/pages/pay/recharge')">
续缴
</button>
<button class="right item" :disabled="interval>0" @click="endCharge()">
{{interval>0?`(${interval}秒)`:''}}结束
</button>
</view>
</kx-tabbar-wrapper>
</template>
</s-layout>
</template>
<script setup>
import {
ref,
onMounted,
onUnmounted,
computed
} from 'vue'
import {
getOrder as getOrderApi
} from '@/sheep/api/charge/operations';
import * as OrderApi from "@/sheep/api/charge/order";
const interval = ref(0) // 剩余秒数
const data = ref({})
let timer = null
let countdownTimer = null // 新增倒计时定时器
let durationTimer = null // 新增充电时长定时器
let hasInitializedTimers = false // 新增标志变量
const chargeStatus = ref([5, 6, 7, 8, 9])
const progress = ref(0)
let progressTimer = null
const toPage = (url) => {
uni.navigateTo({
url
});
};
// 清除所有定时器
const clearTimers = () => {
if (timer) {
clearTimeout(timer)
timer = null
}
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
if (durationTimer) {
clearInterval(durationTimer)
durationTimer = null
}
if (progressTimer) {
clearTimeout(progressTimer)
progressTimer = null
}
hasInitializedTimers = false // 重置标志
}
// 格式化时间戳
const formatTime = computed(() => {
if (!data.value.startTime) return '--'
const date = new Date(parseInt(data.value.startTime))
// 格式化为 YYYY-MM-DD HH:mm:ss
return `${date.getFullYear()}-${padZero(date.getMonth()+1)}-${padZero(date.getDate())} ${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
})
// 计算预计充满时间(响应式)
const estimatedFullTime = computed(() => {
if (!data.value.soc) return '计算中...'
const remainingPercent = 100 - data.value.soc
const totalMinutes = Math.ceil(remainingPercent * 1.5) // 假设每1%需要1.5分钟
const hours = Math.floor(totalMinutes / 60)
const minutes = totalMinutes % 60
if (hours > 0) {
return `${hours}小时${minutes}分钟`
} else {
return `${minutes}分钟`
}
})
const endCharge = async () => {
const response = await OrderApi.endCharge(data.value.seqNumber);
if (response.code === 0) {
clearTimers()
progress.value = 90
afterEndCharge()
}
};
const afterEndCharge = async () => {
progress.value--
const orderResponse = await OrderApi.getOrderInfo(data.value.seqNumber);
if (chargeStatus.value.includes(orderResponse.data.chargeStatus) || progress.value <= 0) {
uni.navigateTo({
url: `/pages/order-info/order-info?id=` + data.value.seqNumber
})
} else {
progressTimer = setTimeout(() => {
afterEndCharge()
}, 5000)
}
}
// 补零函数
const padZero = (num) => {
return num < 10 ? `0${num}` : num
}
// 格式化时间为 HH:mm:ss
const formatDuration = (seconds) => {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
return [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
secs.toString().padStart(2, '0')
].join(':')
}
// 计算充电时长(响应式)
const chargingDuration = computed(() => {
if (!data.value.startTime) return '00:00:00'
const now = Date.now()
const startTimestamp = parseInt(data.value.startTime)
const diffInSeconds = Math.floor((now - startTimestamp) / 1000)
return formatDuration(diffInSeconds)
})
// 计算剩余秒数并开始倒计时
const startCountdown = (startTime) => {
const now = Date.now()
const startTimestamp = parseInt(startTime)
const diffInSeconds = Math.floor((now - startTimestamp) / 1000)
// 如果超过60秒,不显示倒计时
if (diffInSeconds >= 60) {
interval.value = 0
return
}
// 计算剩余秒数
interval.value = 60 - diffInSeconds
// 开始倒计时
countdownTimer = setInterval(() => {
interval.value -= 1
// 倒计时结束
if (interval.value <= 0) {
clearInterval(countdownTimer)
countdownTimer = null
}
}, 1000)
}
// 开始充电时长计时
const startDurationTimer = () => {
// 先清除已有的定时器
if (durationTimer) clearInterval(durationTimer)
// 每秒更新一次
durationTimer = setInterval(() => {
// computed属性会自动更新
}, 1000)
}
onLoad(({
seqNumber
}) => {
getOrder(seqNumber)
})
onHide(() => {
clearTimers()
})
onUnmounted(() => {
clearTimers()
})
onBackPress(() => {
clearTimers()
})
const getOrder = async seqNumber => {
const res = await getOrderApi(seqNumber)
data.value = res.data
// 修改后的计时器初始化逻辑
if (res.data.startTime && !hasInitializedTimers) {
startCountdown(res.data.startTime)
startDurationTimer()
hasInitializedTimers = true // 设置标志为true
}
if (res.data.orderStatus === 4) {
clearTimers()
uni.navigateTo({
url: `/pages/order-info/order-info?id=` + res.data.seqNumber
})
return
}
timer = setTimeout(() => {
getOrder(seqNumber)
}, 10000)
}
</script>
<style scoped lang="scss">
.container {
padding-top: 54rpx;
background: url('https://kxcharge.oss-cn-beijing.aliyuncs.com/mini/box.png') 0 0/100% 812rpx no-repeat;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.charging-percentage {
display: flex;
flex-direction: column;
align-items: center;
.percentage-container {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
background: url('https://kxcharge.oss-cn-beijing.aliyuncs.com/mini/percentage.png') 0 0/100% 100% no-repeat;
width: 416rpx;
height: 416rpx;
justify-content: center;
}
.circle {
width: 347rpx;
height: 347rpx;
position: absolute;
transform-origin: center center;
animation: spin 1s linear infinite;
}
.percentage {
font-weight: bold;
font-size: 42rpx;
color: #000000;
}
.value {
font-size: 99rpx;
}
.status {
font-size: 33rpx;
color: #000000;
}
.name {
background: rgba(255, 255, 255, 0.19);
border-radius: 45rpx;
border: 1rpx solid #FFFFFF;
padding: 8rpx 38rpx;
margin: 38rpx 0 64rpx;
font-weight: bold;
font-size: 30rpx;
color: #FFFFFF;
image {
width: 26rpx;
height: 26rpx;
margin-left: 10rpx;
}
}
}
.detail-info {
display: flex;
flex-direction: column;
align-items: center;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 20rpx 1rpx rgba(53, 127, 251, 0.1);
border-radius: 36rpx 36rpx 0rpx 0rpx;
padding: 30rpx;
}
.rest-time {
font-weight: 800;
font-size: 32rpx;
color: #333333;
margin-bottom: 24rpx;
image {
width: 20rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
.cost-info {
background: rgba(48, 114, 246, .1);
border-radius: 25rpx;
display: flex;
align-items: center;
justify-content: space-around;
width: 690rpx;
padding: 30rpx 0;
font-size: 28rpx;
color: #000000;
.line {
width: 1rpx;
height: 61rpx;
background: #DCDCDC;
}
.item {
text-align: center;
}
.value {
font-weight: bold;
font-size: 32rpx;
margin-top: 10rpx;
}
}
.cable-info {
display: flex;
justify-content: space-evenly;
margin: 30rpx 0;
font-size: 24rpx;
color: #000000;
width: 100%;
.value {
font-weight: bold;
margin-left: 10rpx;
}
}
.split-line {
width: 693rpx;
height: 0rpx;
border: 1rpx solid #E3E3E3;
margin-bottom: 24rpx;
}
.header {
font-weight: 800;
font-size: 32rpx;
color: #333333;
display: flex;
align-items: center;
margin-bottom: 20rpx;
.line {
width: 7rpx;
height: 32rpx;
margin-right: 16rpx;
background: #3072F6;
}
}
.list-item {
font-size: 28rpx;
color: #999999;
.value {
color: #333333;
margin-left: 20rpx;
}
}
.list-item+.list-item {
margin-top: 20rpx;
}
.tab-bar {
display: flex;
justify-content: space-evenly;
width: 100%;
.item {
width: 334rpx;
// padding: 24rpx 0;
font-weight: bold;
font-size: 30rpx;
color: #FFFFFF;
background: linear-gradient(132deg, #4A8EFC 0%, #50C3FD 100%);
border-radius: 45rpx;
text-align: center;
}
.right {
background: linear-gradient(131deg, #FC754A 0%, #FD5050 100%);
}
}
</style>