|
|
|
@ -154,7 +154,7 @@ |
|
|
|
</view> |
|
|
|
<view class="status-fu" v-if="orderDetail.status == 0"> |
|
|
|
<view class=""> |
|
|
|
创建时间 <text style="color:red;font-weight: 700;">{{orderDetail.createTime | formatTime}}</text></view> |
|
|
|
剩余支付时间 <text style="color:red;font-weight: 700;">{{payCountdownText}}</text></view> |
|
|
|
</view> |
|
|
|
<view class="status-fu" v-else-if="orderDetail.deliveryInfo"> |
|
|
|
<view class=""> |
|
|
|
@ -700,7 +700,7 @@ |
|
|
|
<uni-icons type="info" size="14" color="#ff5722"></uni-icons> |
|
|
|
<text |
|
|
|
v-if="(((payData.orderType == 2 || payData.orderType == 3) && payData.status == 2) || ((payData.orderType == 2 || payData.orderType == 3) && payData.status == 3) || (payData.deliveryType == 1 && payData.status == 3) || payData.status == 4) && payData.otherOrder == null"> |
|
|
|
取消订单需要商家同意 |
|
|
|
请确认责任方,取消订单需要责任方同意! |
|
|
|
</text> |
|
|
|
<text v-else>请选择退款原因</text> |
|
|
|
</view> |
|
|
|
@ -783,6 +783,23 @@ |
|
|
|
</view> |
|
|
|
</uni-popup> |
|
|
|
|
|
|
|
<uni-popup ref="noResponsibilityCancelPopup" type="center" background-color="transparent"> |
|
|
|
<view class="no-responsibility-cancel-popup"> |
|
|
|
<view class="no-responsibility-cancel-title">确认取消订单吗</view> |
|
|
|
<view class="no-responsibility-cancel-desc">符合无责取消条件,订单金额将按平台退款处理</view> |
|
|
|
<view class="no-responsibility-cancel-actions"> |
|
|
|
<view class="no-responsibility-cancel-btn no-responsibility-cancel-btn-muted" |
|
|
|
@tap="$refs.noResponsibilityCancelPopup.close()"> |
|
|
|
再想想 |
|
|
|
</view> |
|
|
|
<view class="no-responsibility-cancel-btn no-responsibility-cancel-btn-confirm" |
|
|
|
@tap="confirmNoResponsibilityCancel()"> |
|
|
|
确认取消 |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</uni-popup> |
|
|
|
|
|
|
|
<!-- 待支付订单支付弹出层 --> |
|
|
|
<uni-popup ref="payPopup" background-color="#fff"> |
|
|
|
<view class="pay-popup"> |
|
|
|
@ -905,7 +922,7 @@ |
|
|
|
return { |
|
|
|
newQRList: '', |
|
|
|
pintuan: false, |
|
|
|
sellTime: 2, |
|
|
|
sellTime: '', |
|
|
|
menuButtonInfo: {}, |
|
|
|
refundType: 3, |
|
|
|
refundTypeStatus: 1, |
|
|
|
@ -931,7 +948,10 @@ |
|
|
|
isRefreshing: false, |
|
|
|
groupBuyCancelPopupType: 'buy', |
|
|
|
assignWorkerModalVisible: false, |
|
|
|
assignWorkerSelectionKey: '' |
|
|
|
assignWorkerSelectionKey: '', |
|
|
|
payCountdownText: '60:00', |
|
|
|
payCountdownSeconds: 0, |
|
|
|
payCountdownTimer: null |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
@ -1047,11 +1067,66 @@ |
|
|
|
onPullDownRefresh() { |
|
|
|
this.getOrderDetail(this.orderId) |
|
|
|
}, |
|
|
|
onHide() { |
|
|
|
this.stopPayCountdown(); |
|
|
|
}, |
|
|
|
onUnload() { |
|
|
|
uni.$off('updateDeliveryWorker'); |
|
|
|
this.stopPayCountdown(); |
|
|
|
}, |
|
|
|
|
|
|
|
methods: { |
|
|
|
startPayCountdown() { |
|
|
|
this.stopPayCountdown(); |
|
|
|
if (this.orderDetail.status != 0 || !this.orderDetail.createTime) { |
|
|
|
this.setPayCountdown(0); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.setPayCountdown(this.getPayCountdownSeconds()); |
|
|
|
this.payCountdownTimer = setInterval(() => { |
|
|
|
if (this.orderDetail.status != 0) { |
|
|
|
this.stopPayCountdown(); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.setPayCountdown(Math.max(this.payCountdownSeconds - 1, 0)); |
|
|
|
if (this.payCountdownSeconds <= 0) { |
|
|
|
this.stopPayCountdown(); |
|
|
|
} |
|
|
|
}, 1000); |
|
|
|
}, |
|
|
|
stopPayCountdown() { |
|
|
|
if (this.payCountdownTimer) { |
|
|
|
clearInterval(this.payCountdownTimer); |
|
|
|
this.payCountdownTimer = null; |
|
|
|
} |
|
|
|
}, |
|
|
|
getPayCountdownSeconds() { |
|
|
|
const createTime = this.parseOrderTime(this.orderDetail.createTime); |
|
|
|
if (!createTime) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
const expireTime = createTime.getTime() + 60 * 60 * 1000; |
|
|
|
const remainMs = expireTime - Date.now(); |
|
|
|
return Math.max(Math.floor(remainMs / 1000), 0); |
|
|
|
}, |
|
|
|
setPayCountdown(totalSeconds) { |
|
|
|
this.payCountdownSeconds = totalSeconds; |
|
|
|
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0'); |
|
|
|
const seconds = String(totalSeconds % 60).padStart(2, '0'); |
|
|
|
this.payCountdownText = `${minutes}:${seconds}`; |
|
|
|
this.$forceUpdate(); |
|
|
|
}, |
|
|
|
parseOrderTime(value) { |
|
|
|
if (!value) return null; |
|
|
|
if (value instanceof Date) return isNaN(value.getTime()) ? null : value; |
|
|
|
let date = new Date(value); |
|
|
|
if (!isNaN(date.getTime())) { |
|
|
|
return date; |
|
|
|
} |
|
|
|
const normalized = String(value).replace(/-/g, '/').replace('T', ' ').split('.')[0]; |
|
|
|
date = new Date(normalized); |
|
|
|
return isNaN(date.getTime()) ? null : date; |
|
|
|
}, |
|
|
|
formatTimeText(value) { |
|
|
|
if (!value) return ''; |
|
|
|
if (value == '尽快送达') return '尽快送达'; |
|
|
|
@ -1229,12 +1304,62 @@ |
|
|
|
} |
|
|
|
return false; |
|
|
|
}, |
|
|
|
hasNoDeliveryWorker(item) { |
|
|
|
return !item || !item.deliveryInfo || !item.deliveryInfo.workerId; |
|
|
|
}, |
|
|
|
isGroupSuccess(item) { |
|
|
|
return item && item.groupInfo && (item.groupInfo.status == 1 || item.groupInfo.successTime); |
|
|
|
}, |
|
|
|
isAfterGroupSuccessMinutes(item, minutes) { |
|
|
|
if (!item || !item.groupInfo || !item.groupInfo.successTime) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
const successTime = this.parseOrderTime(item.groupInfo.successTime); |
|
|
|
if (!successTime) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
return Date.now() - successTime.getTime() >= minutes * 60 * 1000; |
|
|
|
}, |
|
|
|
isNoResponsibilityCancel(item) { |
|
|
|
if (!item) return false; |
|
|
|
|
|
|
|
const isNormalOrder = item.orderType == 1 && item.otherOrder == null; |
|
|
|
const isGroupDeliveryOrder = item.deliveryType == 1 && item.otherOrder == null && this.isGroupSuccess(item); |
|
|
|
const isSelfPickupNoMake = item.deliveryType == 2 && item.status == 3 && isNormalOrder && |
|
|
|
(item.userRequireMake == null || item.userRequireMake == 0) && !item.shopMakeTime; |
|
|
|
|
|
|
|
if (isNormalOrder && item.deliveryType == 1 && this.hasNoDeliveryWorker(item)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (isSelfPickupNoMake) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (isGroupDeliveryOrder && item.orderType == 3 && item.groupInfo.headOrderId == item.id && |
|
|
|
this.hasNoDeliveryWorker(item)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (isGroupDeliveryOrder && item.orderType == 2 && this.isAfterGroupSuccessMinutes(item, 10) && |
|
|
|
this.hasNoDeliveryWorker(item)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (item.otherOrder == 2 && item.status == 3 && !item.shopMakeTime) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (item.otherOrder == 1 && this.hasNoDeliveryWorker(item)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
}, |
|
|
|
returnPopupProp(item) { |
|
|
|
this.payData = item; |
|
|
|
if (item.status == 0) { |
|
|
|
this.cancelUnpaidOrder(item); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.isNoResponsibilityCancel(item)) { |
|
|
|
this.$refs.noResponsibilityCancelPopup.open('center'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.payData.otherOrder == 1) { |
|
|
|
this.refundType = 3 |
|
|
|
@ -1302,6 +1427,27 @@ |
|
|
|
} |
|
|
|
}).catch((res) => {}); |
|
|
|
}, |
|
|
|
confirmNoResponsibilityCancel() { |
|
|
|
this.$refs.noResponsibilityCancelPopup.close(); |
|
|
|
let item = this.payData; |
|
|
|
let that = this; |
|
|
|
that.tui.request("/mall/order/cancel", "POST", { |
|
|
|
status: 1, |
|
|
|
refundType: 3, |
|
|
|
refundTypeStatus: 4, |
|
|
|
orderId: item.id, |
|
|
|
userId: uni.getStorageSync('id') |
|
|
|
}, false, true).then((res) => { |
|
|
|
uni.hideLoading(); |
|
|
|
if (res.code == 200) { |
|
|
|
that.tui.toast("操作成功"); |
|
|
|
that.getOrderDetail(item.id); |
|
|
|
that.$forceUpdate(); |
|
|
|
} else { |
|
|
|
that.tui.toast(res.message, 2000); |
|
|
|
} |
|
|
|
}).catch((res) => {}); |
|
|
|
}, |
|
|
|
returnPopupPropBuy(item) { |
|
|
|
this.payData = item; |
|
|
|
this.groupBuyCancelPopupType = 'buy'; |
|
|
|
@ -1343,10 +1489,10 @@ |
|
|
|
this.$refs.returnPopupBuy.open('bottom'); |
|
|
|
}, |
|
|
|
returnOrder() { |
|
|
|
/* if(this.sellTime == 5 && this.remark == ""){ |
|
|
|
that.tui.toast("请填写备注"); |
|
|
|
if(this.sellTime == ''){ |
|
|
|
this.tui.toast("请选择退款责任方"); |
|
|
|
return |
|
|
|
} */ |
|
|
|
} |
|
|
|
this.$refs.returnPopup.close(); |
|
|
|
let item = this.payData |
|
|
|
let that = this |
|
|
|
@ -1412,22 +1558,52 @@ |
|
|
|
id: that.payData.shopId, |
|
|
|
regionId: JSON.parse(uni.getStorageSync('area')).id |
|
|
|
}, false, true).then((res1) => { |
|
|
|
if (res1.code == 200) { |
|
|
|
uni.hideLoading(); |
|
|
|
// 类似普通订单结算逻辑 |
|
|
|
let cartItems = []; |
|
|
|
for (let i = 0; i < that.payData.goodsList.length; i++) { |
|
|
|
let productPrice = that.payData.goodsList[i].price |
|
|
|
for (let a = 0; a < res1.result.products.length; a++) { |
|
|
|
if (res1.result.products[a].id == that.payData.goodsList[i] |
|
|
|
.productId) { |
|
|
|
let priceToUse = 0; |
|
|
|
if (res1.result.products[a].attributeListPrice) { |
|
|
|
try { |
|
|
|
let pObj = typeof res1.result.products[a] |
|
|
|
.attributeListPrice === 'string' ? JSON.parse(res1 |
|
|
|
.result.products[a].attributeListPrice) : |
|
|
|
res1.result.products[a].attributeListPrice; |
|
|
|
try { |
|
|
|
if (res1.code == 200) { |
|
|
|
uni.hideLoading(); |
|
|
|
|
|
|
|
// 1. 定义一个安全的 JSON 解析函数(防止遇到 undefined, null 或空字符串报错) |
|
|
|
const safeJsonParse = (str, fallback = null) => { |
|
|
|
if (!str || typeof str !== 'string') return fallback; |
|
|
|
try { |
|
|
|
return JSON.parse(str); |
|
|
|
} catch (e) { |
|
|
|
console.warn('JSON解析失败,原始数据:', str, '错误:', e.message); |
|
|
|
return fallback; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let cartItems = []; |
|
|
|
for (let i = 0; i < that.payData.goodsList.length; i++) { |
|
|
|
let productPrice = that.payData.goodsList[i].price; |
|
|
|
|
|
|
|
for (let a = 0; a < res1.result.products.length; a++) { |
|
|
|
if (res1.result.products[a].id == that.payData.goodsList[i].productId) { |
|
|
|
let priceToUse = 0; |
|
|
|
let rawAttrPrice = res1.result.products[a].attributeListPrice; |
|
|
|
|
|
|
|
// 2. 安全解析 attributeListPrice(兼容未转义的双引号问题) |
|
|
|
let pObj = null; |
|
|
|
if (typeof rawAttrPrice === 'string') { |
|
|
|
pObj = safeJsonParse(rawAttrPrice); // 先尝试正常解析 |
|
|
|
if (!pObj) { |
|
|
|
// 如果正常解析失败,说明可能是后端没转义内部的双引号,尝试修复 |
|
|
|
try { |
|
|
|
// 简单修复:把最外层括号内部的双引号转义 |
|
|
|
let fixedStr = rawAttrPrice.replace(/(\[{)(.*?)(\]})/g, (match, p1, p2, p3) => { |
|
|
|
return p1 + p2.replace(/"/g, '\\"') + p3; |
|
|
|
}); |
|
|
|
pObj = JSON.parse(fixedStr); |
|
|
|
} catch (err) { |
|
|
|
console.warn('attributeListPrice 修复解析依然失败:', err.message); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
pObj = rawAttrPrice; // 如果后端已经返回了对象,直接使用 |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 提取价格 |
|
|
|
if (pObj) { |
|
|
|
if (Array.isArray(pObj) && pObj.length > 0) { |
|
|
|
priceToUse = parseFloat(pObj[0].specPrice); |
|
|
|
} else { |
|
|
|
@ -1436,34 +1612,42 @@ |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) {} |
|
|
|
} |
|
|
|
productPrice = priceToUse; |
|
|
|
} |
|
|
|
productPrice = priceToUse |
|
|
|
} |
|
|
|
|
|
|
|
that.payData.goodsList[i].id = that.payData.goodsList[i].productId; |
|
|
|
let specStr = that.payData.goodsList[i].specs; |
|
|
|
let cartId = that.payData.goodsList[i].productId + '_' + specStr; |
|
|
|
|
|
|
|
// 4. 安全解析 specs |
|
|
|
let parsedSpecs = safeJsonParse(specStr, []); |
|
|
|
|
|
|
|
cartItems.push({ |
|
|
|
cartId: cartId, |
|
|
|
item: that.payData.goodsList[i], |
|
|
|
specs: parsedSpecs, // 使用安全解析后的值 |
|
|
|
quantity: that.payData.goodsList[i].quantity, |
|
|
|
price: productPrice.toFixed(2) |
|
|
|
}); |
|
|
|
} |
|
|
|
that.payData.goodsList[i].id = that.payData.goodsList[i].productId |
|
|
|
let specStr = that.payData.goodsList[i].specs; |
|
|
|
let cartId = that.payData.goodsList[i].productId + '_' + specStr; |
|
|
|
cartItems.push({ |
|
|
|
cartId: cartId, |
|
|
|
item: that.payData.goodsList[i], |
|
|
|
specs: JSON.parse(that.payData.goodsList[i].specs), |
|
|
|
quantity: that.payData.goodsList[i].quantity, |
|
|
|
price: productPrice.toFixed(2) |
|
|
|
|
|
|
|
uni.navigateTo({ |
|
|
|
url: '/package1/buyFood/buyFood?cart=' + encodeURIComponent(JSON.stringify(cartItems)) + |
|
|
|
'&shopItem=' + encodeURIComponent(JSON.stringify(res1.result.shop)) + |
|
|
|
'&packageFee=' + that.payData.packageFee |
|
|
|
}); |
|
|
|
} else { |
|
|
|
that.tui.toast(res.message, 2000); |
|
|
|
return; |
|
|
|
} |
|
|
|
uni.navigateTo({ |
|
|
|
url: '/package1/buyFood/buyFood?cart=' + encodeURIComponent( |
|
|
|
JSON.stringify(cartItems)) + |
|
|
|
'&shopItem=' + encodeURIComponent(JSON.stringify(res1 |
|
|
|
.result.shop)) + '&packageFee=' + |
|
|
|
that.payData.packageFee |
|
|
|
}); |
|
|
|
} else { |
|
|
|
that.tui.toast(res.message,2000); |
|
|
|
return; |
|
|
|
} catch (e) { |
|
|
|
console.error('结算逻辑发生异常:', e.message); |
|
|
|
} finally { |
|
|
|
// 5. 无论成功失败,最后都隐藏 Loading(优化了原来在 if 里和外面重复调用的问题) |
|
|
|
uni.hideLoading(); |
|
|
|
} |
|
|
|
uni.hideLoading(); |
|
|
|
}).catch((res1) => {}); |
|
|
|
} else { |
|
|
|
that.tui.toast(res.message); |
|
|
|
@ -1498,6 +1682,7 @@ |
|
|
|
uni.stopPullDownRefresh() |
|
|
|
if (res.code == 200) { |
|
|
|
that.orderDetail = res.result; |
|
|
|
that.startPayCountdown(); |
|
|
|
if (that.orderDetail.mallRefundRecord != null && that.orderDetail.mallRefundRecord.length > |
|
|
|
0) { |
|
|
|
for (let i = 0; i < that.orderDetail.mallRefundRecord.length; i++) { |
|
|
|
@ -2463,6 +2648,59 @@ |
|
|
|
box-shadow: 0 28rpx 70rpx rgba(0, 35, 28, 0.18); |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-popup { |
|
|
|
width: 620rpx; |
|
|
|
padding: 46rpx 36rpx 34rpx; |
|
|
|
box-sizing: border-box; |
|
|
|
border-radius: 32rpx; |
|
|
|
background: #fff; |
|
|
|
box-shadow: 0 28rpx 70rpx rgba(0, 35, 28, 0.18); |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-title { |
|
|
|
color: #243f38; |
|
|
|
font-size: 36rpx; |
|
|
|
font-weight: 900; |
|
|
|
line-height: 52rpx; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-desc { |
|
|
|
margin-top: 16rpx; |
|
|
|
color: #7b8883; |
|
|
|
font-size: 26rpx; |
|
|
|
font-weight: 700; |
|
|
|
line-height: 38rpx; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-actions { |
|
|
|
display: flex; |
|
|
|
gap: 18rpx; |
|
|
|
margin-top: 42rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-btn { |
|
|
|
flex: 1; |
|
|
|
height: 82rpx; |
|
|
|
border-radius: 999rpx; |
|
|
|
font-size: 28rpx; |
|
|
|
font-weight: 900; |
|
|
|
line-height: 82rpx; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-btn-muted { |
|
|
|
background: #f3f6f4; |
|
|
|
color: #87938f; |
|
|
|
} |
|
|
|
|
|
|
|
.no-responsibility-cancel-btn-confirm { |
|
|
|
background: linear-gradient(135deg, #fff7d7 0%, #a6ffea 100%); |
|
|
|
color: #243f38; |
|
|
|
box-shadow: 0 12rpx 26rpx rgba(0, 191, 160, 0.12); |
|
|
|
} |
|
|
|
|
|
|
|
.commission-popup-title { |
|
|
|
color: #243f38; |
|
|
|
font-size: 34rpx; |
|
|
|
|