chuangte_bike_newxcx/page_user/yongche/orderxq.vue
2025-04-30 18:03:27 +08:00

831 lines
22 KiB
Vue
Raw 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>
<view class="page">
<u-navbar title="订单详情" :border-bottom="false" :background="bgc" title-color='#000' title-size='36' height='45' back-icon-color='#000'></u-navbar>
<map class="map" id="map" ref="map" :scale="zoomSize" :latitude="latitude" :longitude="longitude"
:show-location="true" :markers="markers" :polyline="polyline" :polygons="polygon" :enable-zoom="true"
:enable-scroll="true">
<!-- <cover-view class="park" @click="toggleIconAndCallout">
<cover-image class="img" src="https://lxnapi.ccttiot.com/bike/img/static/uRiYQZQEb3l2LsltEsyW"
mode=""></cover-image>
</cover-view> -->
</map>
<view class="jiaoyi">
<view class="pic">
<image src="https://api.ccttiot.com/smartmeter/img/static/uiXte3LpSV30jyvTUR2R" mode=""></image>
<text v-if="info.status == 'PROCESSING'">进行中</text>
<text v-if="info.status == 'WAIT_PAY'">待支付</text>
<text v-if="info.status == 'CANCELED'">已取消</text>
<text v-if="info.status == 'FINISHED'">交易完成</text>
<text v-if="info.status == 'WAIT_VERIFY'">待审核</text>
</view>
<view class="gx">
<text v-if="info.status == 'PROCESSING'">订单进行中</text>
<text v-if="info.status == 'WAIT_PAY'">订单待支付</text>
<text v-if="info.status == 'CANCELED'">订单已取消</text>
<text v-if="info.status == 'FINISHED'">感谢您的支持,欢迎再次光临</text>
<text v-if="info.status == 'WAIT_VERIFY'">订单待审核</text>
</view>
</view>
<view class="ordermx">
<view class="top">
订单明细
</view>
<view class="xuxian">
<view class="one">
<view class="qian">
订单编号
</view>
<view class="shen">
{{info.no == null ? '--' : info.no}} <image src="https://api.ccttiot.com/smartmeter/img/static/uimYURcxkn9ItLquwPpM" mode=""></image>
</view>
</view>
<view class="one">
<view class="qian">
下单时间
</view>
<view class="shen">
{{info.createTime == null ? '--' : info.createTime}}
</view>
</view>
<view class="one">
<view class="qian">
结束时间
</view>
<view class="shen">
{{info.endTime == null ? '--' : info.endTime}}
</view>
</view>
</view>
<view class="xuxian">
<view class="one">
<view class="qian">
骑行费
</view>
<view class="shen">
{{info.ridingFee == null ? '0.00' : info.ridingFee.toFixed(2)}}元
</view>
</view>
<view class="one">
<view class="qian">
停车点外调度费
</view>
<view class="shen">
{{info.manageFee == null ? '0.00' : info.manageFee.toFixed(2)}}元
</view>
</view>
<view class="one">
<view class="qian">
运营区外调度费
</view>
<view class="shen">
{{info.dispatchFee == null ? '0.00' : info.dispatchFee.toFixed(2)}}元
</view>
</view>
<view class="one">
<view class="qian">
合计
</view>
<view class="shen">
{{info.totalFee == null ? '0.00' : info.totalFee.toFixed(2)}}元
</view>
</view>
</view>
<view class="xuxian">
<view class="one">
<view class="qian">
预存金额
</view>
<view class="shen">
{{info.depositFee == null ? '0.00' : info.depositFee.toFixed(2)}}元
</view>
</view>
<view class="one">
<view class="qian">
退款总额
</view>
<view class="shen">
{{info.payRefunded == null ? '0.00' : info.payRefunded.toFixed(2)}}元
</view>
</view>
<view class="one">
<view class="qian">
还车退款
</view>
<view class="shen">
{{info.payAutoRefund == null ? '0.00' : info.payAutoRefund.toFixed(2)}}元
</view>
</view>
<view class="one">
<view class="qian">
商家退款
</view>
<view class="shen">
{{info.payAdminRefund == null ? '0.00' : info.payAdminRefund.toFixed(2)}}元
</view>
</view>
</view>
<view class="xuxian" style="border: 0;">
<view class="one" v-if="info.status == 'PROCESSING'">
<view class="qian">
预估金额
</view>
<view class="shen">
<span style="color: #E7612E;font-size: 40rpx;">{{yugumoney.toFixed(2)}}</span>元
</view>
</view>
<view class="one">
<view class="qian">
实付金额
</view>
<view class="shen">
<span style="color: #E7612E;font-size: 40rpx;">{{formatPayedAmount(info.payedAmount, info.payRefunded, info.payRefunding)}}</span>元
</view>
</view>
</view>
</view>
<view class="qixing">
<view class="top">
骑行信息
</view>
<view class="xuxian">
<view class="one">
<view class="qian">
骑行时长
</view>
<view class="shen">
{{ rideDuration == '' ? '--' : rideDuration}}
</view>
</view>
<view class="one">
<view class="qian">
骑行距离
</view>
<view class="shen" v-if="info.status != 'PROCESSING'">
{{info.distance > 1000 ? (info.distance / 1000).toFixed(2) + 'km' : info.distance.toFixed(2) + 'm'}}
</view>
<view class="shen" v-else>
订单结束后展示
</view>
</view>
<view class="one">
<view class="qian">
运营区
</view>
<view class="shen">
{{info.areaName == null ? '--' : info.areaName}}
</view>
</view>
<view class="one">
<view class="qian">
车牌号
</view>
<view class="shen">
{{info.deviceVehicleNum == null ? '--' : info.deviceVehicleNum}}
</view>
</view>
<view class="one">
<view class="qian">
车辆编号SN
</view>
<view class="shen">
{{info.deviceSn == null ? '--' : info.deviceSn}}
</view>
</view>
</view>
</view>
<view class="kefu" @click="btntel">
联系客服
</view>
<!-- 平台客服弹窗 -->
<view class="kefutc" v-if="kefuflag">
<image src="https://api.ccttiot.com/smartmeter/img/static/umtjJg2CJLiOS6hfAEzo" mode="" @click="btnkefu"></image>
<view class="box">
<view class="" style="max-height: 280rpx;overflow: scroll;">
<view class="top" v-for="(item,index) in kefulist" :key="index">
<view class="dianhua">
{{item.name == null ? '--' : item.name}}{{item.contact == null ? '--' : item.contact}}
<view class="wz">
工作时间{{item.startTime}}~{{item.endTime}}
</view>
</view>
<view class="boda" @click.stop="btnptkf(item.contact)">
<u-icon name="phone-fill" color="#4297F3" size="28"></u-icon>
<text>拨打</text>
</view>
</view>
</view>
<view class="bot">
<view class="wzs">
客服电话高峰期可能遇忙请耐心等待
</view>
</view>
</view>
</view>
<view class="mask" v-if="kefuflag"></view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
backgroundColor: "#fff",
},
id: 0,
info: {},
rideDuration: '' ,// 新增属性用于存储骑行时间
kefulist:[],
kefuflag:false,
yugumoney:'',
zoomSize: 15,
markers: [], // 标记点数组
polyline: [], // 轨迹折线数组
polygon: [], // 用于存储区域多边形
latitude: '',
longitude: '',
isMap: false,
trackPoints: [], // 存储轨迹点数据
}
},
onLoad(e) {
this.id = e.id
this.updateTrackData()
this.orderInfo()
this.getyugu()
},
methods: {
getParking() {
let data = {
areaId: this.info.areaId
};
this.$u.get('/bst/areaSub/listByAreaId', data).then((res) => {
if (res.code === 200) {
const type1Data = [];
const type2Data = [];
const type3Data = [];
res.data.forEach(row => {
if (row.type == 1) type1Data.push(row);
else if (row.type == 2) type2Data.push(row);
else if (row.type == 3) type3Data.push(row);
});
// 处理 type1 数据
const validBoundaries = type1Data.map(row => row.boundaryStr).filter(boundary =>
typeof boundary === 'string' && boundary.trim() !== '');
const polygons = this.convertBoundaryToPolygons(validBoundaries, 1);
if (polygons && polygons.length > 0) {
this.polygon = this.polygon.concat(polygons);
}
// 处理 type2 数据
const validBoundaries1 = type2Data.map(row => row.boundaryStr).filter(boundary =>
typeof boundary === 'string' && boundary.trim() !== '');
const polygons1 = this.convertBoundaryToPolygons(validBoundaries1, 2);
if (polygons1 && polygons1.length > 0) {
this.polygon = this.polygon.concat(polygons1);
}
// 处理 type3 数据
const validBoundaries2 = type3Data.map(row => row.boundaryStr).filter(boundary =>
typeof boundary === 'string' && boundary.trim() !== '');
const polygons2 = this.convertBoundaryToPolygons(validBoundaries2, 3);
if (polygons2 && polygons2.length > 0) {
this.polygon = this.polygon.concat(polygons2);
}
this.parkingList = res.data;
this.$forceUpdate();
this.toggleIconAndCallout()
}
}).catch(error => {
console.error("Error fetching parking data:", error);
});
},
toggleIconAndCallout() {
this.showIconAndCallout = !this.showIconAndCallout
if (this.showIconAndCallout) {
const newMarkers = []
this.parkingList.forEach(item => {
newMarkers.push({
id: parseFloat(item.id),
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude),
width: 20,
height: 28.95,
iconPath: item.type == 1 ?
'https://lxnapi.ccttiot.com/bike/img/static/up2xXqAgwCX5iER600k3' : item
.type == 2 ?
'https://lxnapi.ccttiot.com/bike/img/static/uDNY5Q4zOiZTCBTA2Jdq' :
'https://lxnapi.ccttiot.com/bike/img/static/u53BAQcFIX3vxsCzEZ7t',
callout: {
content: item.name,
color: '#ffffff',
fontSize: 14,
borderRadius: 10,
bgColor: item.type == 1 ? '#3A7EDB' : item.type == 2 ? '#FF473E' : '#FFC107',
padding: 6,
display: 'ALWAYS'
},
isCalloutVisible: true // 添加标记
})
})
this.$set(this, 'markers', [...this.markers, ...newMarkers])
} else {
// 过滤掉所有气泡显示的标记
this.$set(this, 'markers', this.markers.filter(marker => !marker.isCalloutVisible))
}
},
getArea() {
let id = this.info.areaId;
if (!id) {
console.error("Area ID is missing");
return;
}
this.$u.get(`/bst/area/${id}`).then((res) => {
if(res.code == 200){
this.latitude = res.data.latitude
this.longitude = res.data.longitude
}
if (res.data && res.data.boundaryStr) {
const polygon = this.convertBoundaryToPolygon(res.data.boundaryStr);
if (polygon) {
this.polygon = [polygon];
console.log('Area boundary added:', polygon);
this.getParking();
}
}
}).catch(error => {
console.error("Error fetching area data:", error);
});
},
convertBoundaryToPolygon(boundary) {
if (!boundary) return null;
try {
const points = JSON.parse(boundary).map(coord => ({
latitude: coord[1],
longitude: coord[0]
}));
return {
points: points,
fillColor: "#55888840",
strokeColor: "#22FF0080",
strokeWidth: 1,
zIndex: 1
};
} catch (error) {
console.error("Error converting boundary to polygon:", error);
return null;
}
},
convertBoundaryToPolygons(boundaries, num) {
if (!boundaries || !boundaries.length) return [];
const colors = {
1: { fill: "#3A7EDB40", stroke: "#3A7EDB" },
2: { fill: "#FFF5D640", stroke: "#FFC107" },
3: { fill: "#FFE0E040", stroke: "#FF473E" }
};
return boundaries.map(boundary => {
if (!boundary) return null;
try {
const coords = JSON.parse(boundary);
if (!Array.isArray(coords)) return null;
const points = coords.map(coord => ({
latitude: coord[1],
longitude: coord[0]
}));
return {
points: points,
fillColor: colors[num].fill,
strokeColor: colors[num].stroke,
strokeWidth: 1,
zIndex: 1
};
} catch (error) {
console.error("Error converting boundary to polygons:", error);
return null;
}
}).filter(Boolean);
},
updateTrackData() {
this.$u.get('/app/locationLog/allByOrder?orderId=' + this.id).then((res) => {
if (res.code === 200) {
if (!res.data || res.data.length === 0) {
uni.showToast({
title: '该时间段内无轨迹数据',
icon: 'none',
duration: 2000
});
return
}
// 清空之前的轨迹数据
this.polyline = []
this.markers = []
// 处理轨迹点数据并按时间排序
this.trackPoints = res.data
.map(point => ({
latitude: parseFloat(point.latitude),
longitude: parseFloat(point.longitude),
time: point.at,
status: point.status,
onlineStatus: point.onlineStatus,
remainingPower: point.bat || 0
}))
.sort((a, b) => new Date(a.time) - new Date(b.time)); // 按时间排序
// 添加起点和终点标记
if (this.trackPoints.length > 0) {
const startPoint = this.trackPoints[0]
const endPoint = this.trackPoints[this.trackPoints.length - 1]
// 添加起点标记
this.markers.push({
id: 'start',
latitude: startPoint.latitude,
longitude: startPoint.longitude,
width: 22,
height: 32,
iconPath: 'https://api.ccttiot.com/smartmeter/img/static/uoORewceRbnD0jcNHhns'
})
// 添加终点标记
this.markers.push({
id: 'end',
latitude: endPoint.latitude,
longitude: endPoint.longitude,
width: 22,
height: 32,
iconPath: 'https://api.ccttiot.com/smartmeter/img/static/u5HZcdTVcyNGkHDUeT4h'
})
// 添加轨迹折线
this.polyline.push({
points: this.trackPoints.map(point => ({
latitude: point.latitude,
longitude: point.longitude
})),
color: '#00AF99',
width: 4,
dottedLine: false,
arrowLine: true,
borderWidth: 2,
borderColor: '#ffffff'
})
// 更新地图显示范围
this.$nextTick(() => {
if (this.$refs.map) {
this.$refs.map.includePoints({
points: this.trackPoints,
padding: [80, 80, 80, 80]
})
}
})
}
}
})
},
// 计算实付金额
formatPayedAmount(payedAmount, payRefunded, payRefunding) {
if (!payedAmount) return '0.00';
const amount = parseFloat(payedAmount) || 0;
const refunded = parseFloat(payRefunded) || 0;
const refunding = parseFloat(payRefunding) || 0;
const actualAmount = amount - (refunded + refunding);
return `${actualAmount.toFixed(2)}`;
},
// 点击隐藏客服
btnkefu(){
console.log(211);
this.kefuflag = false
},
// 请求预估金额
getyugu() {
let data = {
orderId:this.id,
checkLocation:false
}
this.$u.post(`/app/order/calcFee`,data).then((res) => {
if (res.code == 200) {
this.yugumoney = res.data.ridingFee
}
})
},
// 请求订单详情
orderInfo() {
this.$u.get("/app/order/mineDetail?id=" + this.id).then((res) => {
if (res.code == 200) {
this.info = res.data
this.getArea()
this.calculateRideDuration() // 计算骑行时间
}
})
},
// 计算骑行时间
calculateRideDuration() {
if(this.info.startTime != null){
const createTime = new Date(this.info.startTime)
let returnTime = ''
if(this.info.status === 'PROCESSING'){
// 进行中订单,计算到当前时间
returnTime = new Date()
const duration = (returnTime - createTime) / 1000 // 时间差,单位秒
const hours = Math.floor(duration / 3600)
const minutes = Math.floor((duration % 3600) / 60)
if (hours > 0) {
this.rideDuration = `${hours}小时${minutes}`
} else {
this.rideDuration = `${minutes}`
}
} else if(this.info.endTime != null){
// 非进行中订单,计算到结束时间
returnTime = new Date(this.info.endTime)
const duration = (returnTime - createTime) / 1000 // 时间差,单位秒
const hours = Math.floor(duration / 3600)
const minutes = Math.floor((duration % 3600) / 60)
const seconds = Math.floor(duration % 60)
if (hours > 0) {
this.rideDuration = `${hours}小时${minutes}${seconds}`
} else {
this.rideDuration = `${minutes}${seconds}`
}
}
}
},
// 点击拨打平台客服电话
btnptkf(tel){
uni.makePhoneCall({
phoneNumber: tel,
success: function(res) {
console.log('拨打电话成功', res)
},
fail: function(err) {
console.error('拨打电话失败', err)
}
})
},
btntel(){
this.$u.get(`/app/customerService/list?pageNum=1&pageSize=999&areaId=${this.info.areaId}`).then(res => {
if(res.code == 200){
if(res.rows.length > 0){
this.kefulist = res.rows
this.kefuflag = true
}else{
uni.showToast({
title: '暂无客服电话',
icon: 'none',
duration:2000
})
}
}
})
}
}
}
</script>
<style lang="scss" >
page{
background-color: #F7FAFE;
}
.map {
position: relative;
width: 750rpx;
height: 752rpx;
.track {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
left: 30rpx;
bottom: 40rpx;
// background-color: #fff;
border-radius: 50%;
width: 82rpx;
height: 82rpx;
// z-index: 1;
.img {
width: 82rpx;
height: 82rpx;
}
}
.park {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
right: 30rpx;
bottom: 40rpx;
// background-color: #fff;
border-radius: 50%;
width: 82rpx;
height: 82rpx;
// z-index: 1;
.img {
width: 82rpx;
height: 82rpx;
}
}
}
.kefutc{
animation: fadeIn 0.5s ease-in-out forwards;
position: fixed;
top: 660rpx;
left: 50%;
transform: translateX(-50%);
z-index: 999;
.bot{
margin-top: 30rpx;
.wz{
margin-top: 10rpx;
font-weight: 600;
font-size: 28rpx;
color: #3D3D3D;
}
.wzs{
margin-top: 10rpx;
font-size: 24rpx;
color: #7C7C7C;
}
}
.top{
width: 538rpx;
height: 122rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 10rpx 0rpx rgba(0,0,0,0.1);
border-radius: 14rpx 14rpx 14rpx 14rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 14rpx;
box-sizing: border-box;
margin-top: 20rpx;
.dianhua{
font-weight: 600;
font-size: 28rpx;
color: #3D3D3D;
padding-left: 26rpx;
box-sizing: border-box;
}
.boda{
width: 94rpx;
height: 94rpx;
background: #DCEDFF;
border-radius: 8rpx 8rpx 8rpx 8rpx;
text-align: center;
padding-top: 8rpx;
box-sizing: border-box;
text{
display: block;
}
}
}
image{
position: absolute;
top: -280rpx;
z-index: -1;
left: 50%;
transform: translateX(-50%);
width: 614rpx;
height: 748rpx;
}
}
.mask{
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background-color: rgba(0,0,0,0.3);
z-index: 9;
}
.page{
width: 750rpx;
background: #F7F7F7;
border-radius: 0rpx 0rpx 0rpx 0rpx;
padding-bottom: 200rpx;
box-sizing: border-box;
.kefu{
width: 680rpx;
height: 100rpx;
line-height: 100rpx;
background-color: #4C97E7;
color: #fff;
font-size: 36rpx;
font-weight: 600;
text-align: center;
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 50rpx;
border-radius: 20rpx;
}
.qixing{
width: 702rpx;
height: 466rpx;
background: #FFFFFF;
border-radius: 16rpx 16rpx 16rpx 16rpx;
padding: 0 42rpx;
box-sizing: border-box;
margin: auto;
margin-top: 32rpx;
.top{
font-weight: 600;
font-size: 32rpx;
color: #3D3D3D;
padding: 36rpx 0 24rpx 0;
box-sizing: border-box;
border-bottom: 1px dashed #D8D8D8;
}
.xuxian{
.one{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30rpx;
.qian{
color: #808080;
font-size: 28rpx;
}
.shen{
color: #3D3D3D;
}
}
}
}
.ordermx{
width: 702rpx;
max-height: 1862rpx;
background: #FFFFFF;
border-radius: 16rpx 16rpx 16rpx 16rpx;
padding: 0 42rpx;
box-sizing: border-box;
margin: auto;
margin-top: 32rpx;
.xuxian{
padding-bottom: 24rpx;
box-sizing: border-box;
border-bottom: 1px dashed #D8D8D8;
.one{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30rpx;
.qian{
color: #808080;
font-size: 28rpx;
}
.shen{
color: #3D3D3D;
image{
width: 23rpx;
height: 28rpx;
vertical-align: baseline;
margin-left: 10rpx;
}
}
}
}
.top{
font-weight: 600;
font-size: 32rpx;
color: #3D3D3D;
padding: 36rpx 0 24rpx 0;
box-sizing: border-box;
border-bottom: 1px dashed #D8D8D8;
}
}
.jiaoyi{
width: 702rpx;
height: 182rpx;
background: #FFFFFF;
border-radius: 16rpx 16rpx 16rpx 16rpx;
margin: auto;
text-align: center;
margin-top: 26rpx;
.pic{
padding-top: 34rpx;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
image{
width: 38rpx;
height: 38rpx;
margin-right: 10rpx;
}
font-weight: 600;
font-size: 40rpx;
color: #4C97E7;
}
.gx{
font-size: 28rpx;
color: #808080;
margin-top: 20rpx;
}
}
}
</style>