first
This commit is contained in:
@@ -0,0 +1,525 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user