Files
uni-fans-score/subPackages/business/device-orderDetail.vue
T
2026-02-06 18:09:23 +08:00

784 lines
18 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="order-detail-container">
<!-- 状态标题 -->
<!-- <view class="status-header">
<view class="status-icon">
<uv-icon name="checkbox-mark" size="40" color="#07c160"></uv-icon>
</view>
<text class="status-text">{{ statusText }}</text>
</view> -->
<!-- 产品信息卡片 -->
<view class="product-card">
<image
:src="orderDetail.pictureUrl || orderDetail.productImage || '/static/default-product.png'"
mode="aspectFill"
class="product-image"
></image>
<view class="product-info">
<view class="product-name">{{ orderDetail.productName || orderDetail.deviceName || '风电者2026新款风扇、充电宝、暖手宝三合一' }}</view>
<view class="product-style">款式{{ orderDetail.optionName || orderDetail.style || '标准' }}</view>
<view class="product-price">¥{{ orderDetail.price || orderDetail.totalAmount }}</view>
</view>
</view>
<!-- 订单信息 -->
<view class="info-section">
<view class="section-title">订单信息</view>
<view class="info-item">
<text class="label">订单号</text>
<text class="value">{{ orderDetail.orderNo || '-' }}</text>
</view>
<view class="info-item" v-if="orderDetail.outTradeNo">
<text class="label">交易单号</text>
<text class="value">{{ orderDetail.outTradeNo }}</text>
</view>
<view class="info-item">
<text class="label">订单状态</text>
<text class="value" :style="{color: statusColor}">{{ statusText }}</text>
</view>
<view class="info-item">
<text class="label">创建时间</text>
<text class="value">{{ orderDetail.createTime || '-' }}</text>
</view>
<view class="info-item">
<text class="label">支付方式</text>
<text class="value">{{ paymentMethodText }}</text>
</view>
<view class="info-item">
<text class="label">支付时间</text>
<text class="value">{{ orderDetail.payTime || '未支付' }}</text>
</view>
<view class="info-item">
<text class="label">收货人</text>
<text class="value">{{ receiverInfo }}</text>
</view>
<view class="info-item">
<text class="label">收货地址</text>
<text class="value address">{{ orderDetail.receiverAddress || '-' }}</text>
</view>
<view class="info-item" v-if="orderDetail.expressageNo">
<text class="label">快递单号</text>
<text class="value">{{ orderDetail.expressageNo }}</text>
</view>
<view class="info-item" v-if="orderDetail.remark">
<text class="label">备注</text>
<text class="value address">{{ orderDetail.remark }}</text>
</view>
</view>
<!-- 费用信息 -->
<view class="info-section">
<view class="section-title">费用信息</view>
<view class="info-item" v-if="orderDetail.quantity">
<text class="label">数量</text>
<text class="value">x{{ orderDetail.quantity }}</text>
</view>
<view class="info-item">
<text class="label">单价</text>
<text class="value">¥ {{ orderDetail.price || orderDetail.totalAmount }}</text>
</view>
<view class="info-item total">
<text class="label">合计</text>
<text class="value highlight">¥ {{ totalAmount }}</text>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-actions">
<!-- 待付款状态显示取消订单和立即支付 -->
<template v-if="orderDetail.status === 0 || orderDetail.status === '0'">
<view class="action-btn secondary" @click="onCancelOrder">
取消订单
</view>
<view class="action-btn primary" @click="onPayNow">
立即支付
</view>
</template>
<!-- 已完成/已取消状态显示删除订单和再次定制 -->
<template v-else-if="orderDetail.status === 3 || orderDetail.status === '3' || orderDetail.status === 4 || orderDetail.status === '4'">
<view class="action-btn secondary" @click="onDeleteOrder">
删除订单
</view>
<view class="action-btn primary" @click="onReorder(orderDetail.productId)">
再次定制
</view>
</template>
<!-- 其他状态显示联系客服和再次定制 -->
<template v-else>
<view class="action-btn secondary" @click="onContactService">
联系客服
</view>
<view class="action-btn primary" @click="onReorder(orderDetail.productId)">
再次定制
</view>
</template>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import {
queryById,
getProductOrderDetail,
deleteProductOrder,
cancelProductOrder,
createWxPayment
} from '../../config/api/order.js';
// import { getSystemParamByKey } from '../../config/api/system.js';
import { useI18n } from '@/utils/i18n.js'
const { t } = useI18n()
const orderDetail = ref({});
const orderId = ref('');
const countdownText = ref('');
let countdownTimer = null;
// 订单状态文本
const statusText = computed(() => {
const status = orderDetail.value.status;
if (status === 0 || status === '0') {
return '待付款';
}
if (status === 1 || status === '1') {
return '待发货';
}
if (status === 2 || status === '2') {
return '待收货';
}
if (status === 3 || status === '3') {
return '已完成';
}
if (status === 4 || status === '4') {
return '已取消';
}
if (status === 5 || status === '5') {
return '退款中';
}
return '待付款';
});
// 订单状态颜色
const statusColor = computed(() => {
const status = orderDetail.value.status;
if (status === 0 || status === '0') {
return '#ff976a'; // 待付款 - 橙色
}
if (status === 1 || status === '1') {
return '#1989fa'; // 待发货 - 蓝色
}
if (status === 2 || status === '2') {
return '#1989fa'; // 待收货 - 蓝色
}
if (status === 3 || status === '3') {
return '#07c160'; // 已完成 - 绿色
}
if (status === 4 || status === '4') {
return '#999'; // 已取消 - 灰色
}
if (status === 5 || status === '5') {
return '#ff6b6b'; // 退款中 - 红色
}
return '#ff976a';
});
// 支付方式文本
const paymentMethodText = computed(() => {
const payWay = orderDetail.value.payWay;
if (payWay === 'wx_score_pay') return '微信支付';
if (payWay === 'wx_global_pay') return '微信支付';
if (payWay === 'wx_member_pay') return '微信支付';
return '微信支付';
});
// 收货人信息
const receiverInfo = computed(() => {
const name = orderDetail.value.receiverName || '-';
const phone = orderDetail.value.receiverPhone || '-';
return `${name} ${phone}`;
});
// 总金额
const totalAmount = computed(() => {
return orderDetail.value.totalAmount || orderDetail.value.amount || orderDetail.value.payAmount || '99.0';
});
// 页面加载
onLoad(async (options) => {
if (options && options.orderId) {
orderId.value = options.orderId;
await loadOrderDetail();
}
});
// 根据订单状态设置页面标题
const updatePageTitle = () => {
const status = orderDetail.value.status;
let title = '订单详情';
if (status === 0 || status === '0') {
title = '待付款';
// 如果有倒计时文本,添加到标题中
if (countdownText.value) {
title = `${title} ${countdownText.value}`;
}
} else if (status === 1 || status === '1') {
title = '待发货';
} else if (status === 2 || status === '2') {
title = '待收货';
} else if (status === 3 || status === '3') {
title = '已完成';
} else if (status === 4 || status === '4') {
title = '已取消';
} else if (status === 5 || status === '5') {
title = '退款中';
}
uni.setNavigationBarTitle({
title: title
});
};
// 启动倒计时
const startCountdown = () => {
// 清除之前的定时器
if (countdownTimer) {
clearInterval(countdownTimer);
countdownTimer = null;
}
const status = orderDetail.value.status;
const expireTime = orderDetail.value.expireTime;
// 只有待付款状态且有过期时间才显示倒计时
if ((status === 0 || status === '0') && expireTime) {
// 计算倒计时
const updateCountdown = () => {
const now = new Date().getTime();
const expireTimestamp = new Date(expireTime).getTime();
const diff = expireTimestamp - now;
if (diff > 0) {
// 计算总分钟数和秒数
const totalMinutes = Math.floor(diff / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
// 格式化为 MM:SS
const minutesStr = String(totalMinutes).padStart(2, '0');
const secondsStr = String(seconds).padStart(2, '0');
countdownText.value = `${minutesStr}:${secondsStr}`;
updatePageTitle();
} else {
// 倒计时结束
countdownText.value = '';
if (countdownTimer) {
clearInterval(countdownTimer);
countdownTimer = null;
}
// 重新加载订单详情(订单可能已自动取消)
loadOrderDetail();
}
};
// 立即执行一次
updateCountdown();
// 每秒更新一次
countdownTimer = setInterval(updateCountdown, 1000);
} else {
countdownText.value = '';
updatePageTitle();
}
};
// 组件卸载时清除定时器
onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer);
countdownTimer = null;
}
});
// 加载订单详情
const loadOrderDetail = async () => {
try {
uni.showLoading({
title: '加载中...'
});
const res = await getProductOrderDetail(orderId.value);
if (res.code === 200 && res.data) {
const data = res.data;
orderDetail.value = {
// 基础信息
id: data.id,
orderNo: data.orderNo,
outTradeNo: data.outTradeNo,
userId: data.userId,
// 状态信息
status: data.status,
payStatus: data.payStatus,
// 金额信息
totalAmount: data.totalAmount,
payAmount: data.payAmount,
price: data.price,
// 时间信息
createTime: data.createTime,
updateTime: data.updateTime,
payTime: data.payTime,
expireTime: data.expireTime, // 订单自动取消时间
// 收货信息
receiverName: data.receiverName,
receiverPhone: data.receiverPhone,
receiverAddress: data.receiverAddress,
// 物流信息
expressageNo: data.expressageNo,
// 备注
remark: data.remark,
// 商品信息
productName: data.productName,
optionName: data.optionName,
pictureUrl: data.pictureUrl,
color: data.color,
quantity: data.quantity,
productId:data.productId,
// 兼容旧字段
orderId: data.id,
deviceId: data.deviceNo || '',
deviceName: data.deviceName || '',
style: data.optionName || data.style || data.deviceStyle || '',
payWay: data.payWay || 'wx_global_pay',
phone: data.receiverPhone,
address: data.receiverAddress,
deposit: data.deposit || '0',
package: data.package || '',
amount: data.payAmount,
productImage: data.pictureUrl || data.productImage || ''
};
// 启动倒计时
startCountdown();
}
uni.hideLoading();
} catch (error) {
uni.hideLoading();
console.error('加载订单详情失败:', error);
uni.showToast({
title: '加载失败',
icon: 'none'
});
}
};
// 退款/售后
const onRefund = () => {
uni.showToast({
title: '退款/售后功能开发中',
icon: 'none'
});
};
// 取消订单
const onCancelOrder = () => {
uni.showModal({
title: '提示',
content: '确定要取消这个订单吗?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '取消中...',
mask: true
});
const result = await cancelProductOrder(orderDetail.value.id);
uni.hideLoading();
if (result && result.code === 200) {
uni.showToast({
title: '订单已取消',
icon: 'success'
});
// 重新加载订单详情
await loadOrderDetail();
} else {
throw new Error(result?.msg || '取消失败');
}
} catch (error) {
uni.hideLoading();
console.error('取消订单失败:', error);
uni.showToast({
title: error.message || '取消失败',
icon: 'none'
});
}
}
}
});
};
// 删除订单
const onDeleteOrder = () => {
uni.showModal({
title: '提示',
content: '确定要删除这个订单吗?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '删除中...',
mask: true
});
const result = await deleteProductOrder(orderDetail.value.id);
uni.hideLoading();
if (result && result.code === 200) {
uni.showToast({
title: '删除成功',
icon: 'success'
});
// 返回订单列表
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
throw new Error(result?.msg || '删除失败');
}
} catch (error) {
uni.hideLoading();
console.error('删除订单失败:', error);
uni.showToast({
title: error.message || '删除失败',
icon: 'none'
});
}
}
}
});
};
// 立即支付
const onPayNow = async () => {
try {
uni.showLoading({
title: '正在创建支付...',
mask: true
});
const res = await createWxPayment(orderDetail.value.orderNo);
if (res && res.code === 200 && res.data) {
uni.hideLoading();
const payParams = res.data;
// 调用微信支付
uni.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: async (payRes) => {
console.log('支付成功:', payRes);
uni.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
});
// 重新加载订单详情
await loadOrderDetail();
},
fail: async (err) => {
console.error('支付失败:', err);
// 判断是用户取消还是支付失败
if (err.errMsg && err.errMsg.includes('cancel')) {
// 用户取消支付,调用取消订单接口
try {
await cancelProductOrder(orderDetail.value.id);
uni.showToast({
title: '支付已取消',
icon: 'none'
});
// 重新加载订单详情
await loadOrderDetail();
} catch (cancelError) {
console.error('取消订单失败:', cancelError);
uni.showToast({
title: '支付已取消',
icon: 'none'
});
}
} else {
// 支付失败
uni.showToast({
title: '支付失败,请重试',
icon: 'none'
});
}
}
});
} else {
uni.hideLoading();
uni.showToast({
title: res?.msg || '创建支付订单失败',
icon: 'none'
});
}
} catch (error) {
console.error('支付异常:', error);
uni.hideLoading();
uni.showToast({
title: error.message || '支付失败,请重试',
icon: 'none'
});
}
};
// 联系客服
const onContactService = async () => {
const phoneNumber = uni.getStorageSync('customerPhone');
// 拨打客服电话
uni.makePhoneCall({
phoneNumber: phoneNumber,
success: () => {
console.log('拨打电话成功');
},
fail: (err) => {
console.error('拨打电话失败:', err);
uni.showToast({
title: '拨打电话失败',
icon: 'none'
});
}
});
};
// 再次定制
const onReorder = (order) => {
if(order){
uni.navigateTo({
url: `/subPackages/business/device-goods?productId=${order}`
});
}
// console.log(order);
};
</script>
<style lang="scss" scoped>
.order-detail-container {
min-height: 100vh;
background: #f7f8fa;
padding-bottom: 120rpx;
// 状态头部
.status-header {
background: #fff;
padding: 60rpx 0 40rpx;
text-align: center;
.status-icon {
margin-bottom: 20rpx;
}
.status-text {
font-size: 36rpx;
color: #333;
font-weight: 600;
}
}
// 产品卡片
.product-card {
background: #fff;
margin: 20rpx;
padding: 24rpx;
border-radius: 16rpx;
display: flex;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.product-image {
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
background: #f5f5f5;
flex-shrink: 0;
}
.product-info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.product-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
line-height: 1.4;
margin-bottom: 12rpx;
}
.product-style {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.product-price {
font-size: 32rpx;
color: #07c160;
font-weight: 600;
}
}
}
// 信息区块
.info-section {
background: #fff;
margin: 20rpx;
padding: 24rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.section-title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
font-size: 28rpx;
height: 50rpx;
&:last-child {
margin-bottom: 0;
}
.label {
color: #999;
flex-shrink: 0;
width: 140rpx;
}
.value {
color: #333;
flex: 1;
text-align: right;
word-break: break-all;
&.address {
text-align: right;
}
}
&.total {
margin-top: 12rpx;
padding-top: 20rpx;
border-top: 1rpx dashed #e5e5e5;
.label {
color: #333;
font-weight: 500;
}
.value.highlight {
color: #07c160;
font-size: 36rpx;
font-weight: 600;
}
}
}
}
// 底部按钮
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx;
display: flex;
justify-content: flex-end;
gap: 20rpx;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.04);
z-index: 100;
.action-btn {
width: 180rpx;
height: 64rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
font-weight: 500;
&.secondary {
background: #fff;
color: #07c160;
border: 2rpx solid #07c160;
}
&.primary {
background: #07c160;
color: #fff;
}
&:active {
opacity: 0.8;
}
}
}
}
</style>