From dbf7fa0c950223f163fa28dbc74e12193eee6be0 Mon Sep 17 00:00:00 2001 From: ISFP_T <68358856@qq.com> Date: Mon, 12 Jan 2026 10:30:40 +0800 Subject: [PATCH] =?UTF-8?q?add:=E6=96=B0=E5=A2=9E=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 26 +- config/api/order.js | 9 + config/url.js | 4 +- locale/en-US.js | 10 +- locale/zh-CN.js | 10 +- pages/feedback/index.vue | 5 +- pages/order/detail.vue | 530 +++++++++++++++++++++++++++---------- static/power_no_popout.png | Bin 0 -> 2522 bytes 8 files changed, 426 insertions(+), 168 deletions(-) create mode 100644 static/power_no_popout.png diff --git a/App.vue b/App.vue index 4b83a77..cb38d24 100644 --- a/App.vue +++ b/App.vue @@ -28,33 +28,23 @@ // 规范化路径格式进行比较 const normalizedLaunchPath = launchPath.replace(/\.html$/, '') - // 只在「单页面栈」时生效,避免影响正常多页面跳转(如下单后跳详情) - if (pages.length === 1 && currentRoute !== normalizedLaunchPath) { + // 只要当前不在目标页面,就进行一次跳转 + // 下单流程不会受影响,因为在下单离开设备详情页时不会写入 launchPath + if (currentRoute !== normalizedLaunchPath) { console.log('当前页面:', currentRoute, '目标页面:', normalizedLaunchPath) // 清除启动路径标记(在跳转前清除,避免重复触发) uni.removeStorageSync('launchPath') - // 跳转到启动路径(首页) - // 先尝试 switchTab(如果首页是 tabBar 页面) + // 使用 reLaunch 跳转到启动路径(首页) uni.reLaunch({ url: normalizedLaunchPath, success: () => { - console.log('成功跳转到启动路径(switchTab)') + console.log('成功跳转到启动路径(reLaunch)') }, - fail: (err) => { - console.log('switchTab 失败,使用 reLaunch:', err) - // 如果 switchTab 失败,使用 reLaunch(适用于非 tabBar 页面) - uni.reLaunch({ - url: normalizedLaunchPath, - success: () => { - console.log('成功跳转到启动路径(reLaunch)') - }, - fail: (reLaunchErr) => { - console.error('跳转到启动路径失败:', reLaunchErr) - } - }) + fail: (reLaunchErr) => { + console.error('跳转到启动路径失败:', reLaunchErr) } }) - } else if (currentRoute === normalizedLaunchPath) { + } else { // 如果已经在目标页面,清除启动路径标记 console.log('当前已在目标页面,清除启动路径标记') uni.removeStorageSync('launchPath') diff --git a/config/api/order.js b/config/api/order.js index 47282fd..2c65395 100644 --- a/config/api/order.js +++ b/config/api/order.js @@ -95,6 +95,15 @@ export const convertToOwned = (orderId) => { }) } +// 不想还了转为自用(按最高费用) +export const closeWithMaxFee = (orderNo) => { + console.log('不想还了转为自用,订单号:', orderNo) + return request({ + url: `/app/order/closeWithMaxFee/${orderNo}`, + method: 'post' + }) +} + // 通过订单号获取支付分订单信息 export const getOrderByOrderNoScore = (orderNo) => { console.log('通过订单号获取支付分订单信息', orderNo); diff --git a/config/url.js b/config/url.js index e0d18e3..914ef91 100644 --- a/config/url.js +++ b/config/url.js @@ -1,6 +1,6 @@ // export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用 -// export const URL = "https://manager.fdzpower.com/api" //正式服务器 -export const URL = "https://fansdev.gxfs123.com/api" //测试服务器 +export const URL = "https://manager.fdzpower.com/api" //正式服务器 +// export const URL = "https://fansdev.gxfs123.com/api" //测试服务器 // export const URL = "http://192.168.5.30:8080" //本地调试 // export const URL = "http://127.0.0.1:8080" //本地调试 diff --git a/locale/en-US.js b/locale/en-US.js index 400bed4..de3a1c4 100644 --- a/locale/en-US.js +++ b/locale/en-US.js @@ -242,11 +242,19 @@ export default { perMinute: 'per minute', perHalfHour: 'per half hour', deviceNoEject: 'Not Ejected', + returnReminder: 'Return Reminder', convertToOwn: 'Don\'t want to return? Convert to own', convertToOwnTitle: 'Convert to Own', - convertToOwnConfirm: 'Converting to own costs ¥99. The power bank will be yours. Confirm?', + convertToOwnConfirm: 'Only ¥99 to convert to own. The power bank will be yours. Confirm?', convertToOwnSuccess: 'Successfully converted to own', convertToOwnFailed: 'Operation failed, please try again', + convertToOwnConfirmBtn: 'Own It', + convertToOwnCancelBtn: 'Keep Rent', + convertToOwnWithMaxFee: 'Don\'t want to return? Convert to own', + convertToOwnWithMaxFeeTitle: 'Take It Home!', + convertToOwnWithMaxFeeConfirm: 'Since you love using it, buy it out and take it home! Only ¥99, the device is yours forever – no return needed~\n✅Type-C charging supported, perfect for home use~\n✅No usage limits after purchase, use it freely!', + convertToOwnWithMaxFeeSuccess: 'Purchase successful', + convertToOwnWithMaxFeeFailed: 'Purchase failed, please try again', deviceNoEjectTitle: 'Device Not Ejected', deviceNoEjectConfirm: 'Your power bank didn\'t eject? We will handle it immediately, expected to resolve within 5 minutes.', deviceNoEjectSuccess: 'Feedback received, will be handled within 5 minutes', diff --git a/locale/zh-CN.js b/locale/zh-CN.js index 48bc012..deded74 100644 --- a/locale/zh-CN.js +++ b/locale/zh-CN.js @@ -242,11 +242,19 @@ export default { perMinute: '每分钟', perHalfHour: '每半小时', deviceNoEject: '宝未弹出', + returnReminder: '归还提醒', convertToOwn: '不想还了?点击转为自用', convertToOwnTitle: '转为自用', - convertToOwnConfirm: '转为自用需扣除99元,充电宝将归您所有,确认操作吗?', + convertToOwnConfirm: '仅需花费99元,即可转为自用,充电宝将归您所有,确认操作吗?', convertToOwnSuccess: '已成功转为自用', convertToOwnFailed: '操作失败,请稍后重试', + convertToOwnConfirmBtn: '买断自用', + convertToOwnCancelBtn: '继续租借', + convertToOwnWithMaxFee: '不想还了?转为自用', + convertToOwnWithMaxFeeTitle: '买断带回家!', + convertToOwnWithMaxFeeConfirm: '既然用得顺手,直接买断带回家!仅需99元,设备永久归你,无需归还~\n✅支持Type-C充电,居家使用超方便~\n✅买断后无任何使用限制,随心用!', + convertToOwnWithMaxFeeSuccess: '买断成功', + convertToOwnWithMaxFeeFailed: '买断失败,请稍后重试', deviceNoEjectTitle: '充电宝未弹出', deviceNoEjectConfirm: '您的充电宝未弹出吗?我们将立即为您处理,预计5分钟内解决问题。', deviceNoEjectSuccess: '反馈已受理,将在5分钟内处理', diff --git a/pages/feedback/index.vue b/pages/feedback/index.vue index 5962dfc..5b16dc3 100644 --- a/pages/feedback/index.vue +++ b/pages/feedback/index.vue @@ -96,10 +96,13 @@ }) }) - onLoad(() => { + onLoad((options) => { if (uni.getStorageSync("userInfo").phone) { contact.value = uni.getStorageSync('userInfo').phone; } + if(options.selectedType) { + selectedType.value = parseInt(options.selectedType); + } }) // 响应式数据 diff --git a/pages/order/detail.vue b/pages/order/detail.vue index aef321f..0285a42 100644 --- a/pages/order/detail.vue +++ b/pages/order/detail.vue @@ -2,26 +2,44 @@ - {{ getOrderStatusText() }} - {{ getStatusDesc() }} + + + {{ getOrderStatusText() }} + {{ getStatusDesc() }} + + + + + {{ $t('order.deviceNoEject') }} + + + - - - {{ getUsedTimeDisplay().number }} - {{ getUsedTimeDisplay().unit }} + + + + {{ getUsedTimeDisplay().number }} + {{ getUsedTimeDisplay().unit }} + + {{ getUsedTimeLabel() }} + + + + {{ getOrderFee() }} + {{ $t('unit.yuan') }} + + {{ $t('order.totalAmount') }} - {{ getUsedTimeLabel() }} - - - {{ getOrderFee() }} - {{ $t('unit.yuan') }} + + + + {{ $t('order.returnReminder') }} - {{ $t('order.totalAmount') }} - + @@ -121,10 +139,9 @@ {{ $t('order.quickReturn') }} - - - - {{ $t('order.deviceNoEject') }} + + + {{ $t('order.convertToOwnWithMaxFee') }} @@ -154,6 +171,33 @@ {{ $t('order.backToHome') }} + + + + + + {{ $t('order.convertToOwnWithMaxFeeTitle') }} + + + {{ $t('order.convertToOwnWithMaxFeeConfirm') }} + + + + {{ $t('order.convertToOwnCancelBtn') }} + + + {{ $t('order.convertToOwnConfirmBtn') }} + + + + @@ -164,7 +208,8 @@ queryById, cancelOrder, reportDeviceNoEject, - convertToOwned + convertToOwned, + closeWithMaxFee } from '@/config/api/order.js' import { addUserFeedback @@ -220,6 +265,7 @@ const showExpressAction = ref(false) const countdownTimer = ref(null) const feeRuleText = ref('5.0元/60分钟 前15分钟内归还免费 不足60分钟按60分钟计费 封顶99元 持续计费至99元视为买断') + const convertToOwnPopup = ref(null) // 判断订单是否已完成 const isOrderCompleted = () => { return orderInfo.value.orderStatus === 'used_done' || @@ -524,11 +570,25 @@ } } - // 从订单详情页角度不再主动移除订单监控 - // 订单监控的生命周期完全交给全局 orderMonitor,在订单真正完成时自动移除 + // 从订单监控中移除订单 const removeFromOrderMonitor = () => { - // 保留函数以兼容已有调用,但不再从全局监控中删除订单 - console.log('removeFromOrderMonitor 调用:已不再从全局监控中移除订单,交由 orderMonitor 在订单完成时处理') + if (orderInfo.value.orderId && $orderMonitor) { + try { + $orderMonitor.removeOrder({ + orderId: orderInfo.value.orderId + }) + console.log('订单已从监控队列移除:', orderInfo.value.orderId) + } catch (error) { + console.error('从监控队列移除订单失败:', error) + } + } + // 清除本地缓存的 activeOrderId + try { + uni.removeStorageSync('activeOrderId') + console.log('已清除本地缓存的 activeOrderId') + } catch (error) { + console.error('清除 activeOrderId 缓存失败:', error) + } } // 处理订单完成事件 @@ -665,13 +725,18 @@ deviceId.value = orderData.deviceNo } + // 如果订单已完成,从监控中移除 + if (isOrderCompleted()) { + removeFromOrderMonitor() + console.log('订单已完成,已从监控队列移除') + } + // 如果订单状态从 'in_used' 变为其他状态,清理所有定时器 if (oldStatus === 'in_used' && orderInfo.value.orderStatus !== 'in_used') { console.log('订单状态已从使用中变为:', orderInfo.value.orderStatus, ',清理所有定时器') clearTimer() clearStatusCheckTimer() clearExpressCountdown() - removeFromOrderMonitor() // 显示订单完成提示 if (orderInfo.value.orderStatus === 'used_done' || orderInfo.value.orderStatus === 'used_down') { @@ -721,25 +786,30 @@ } } - // 无论当前订单状态如何,只要进入订单详情页,就把当前订单交给全局订单监控 + // 根据订单状态决定是否添加到监控队列 if (orderInfo.value.orderId) { - uni.setStorageSync('activeOrderId', orderInfo.value.orderId) - - // 添加/更新到监控队列 - try { - if ($orderMonitor) { - $orderMonitor.removeOrder({ - orderId: orderInfo.value.orderId - }) - $orderMonitor.addOrder({ - orderId: orderInfo.value.orderId - }, 'detail') - console.log('订单已添加到监控队列(无论当前状态):', orderInfo.value.orderId, '当前状态:', orderInfo.value.orderStatus) - } else { - console.warn('$orderMonitor 未定义,无法添加订单到监控队列') + // 如果订单已完成,从监控中移除 + if (isOrderCompleted()) { + removeFromOrderMonitor() + console.log('订单已完成,已从监控队列移除:', orderInfo.value.orderId, '当前状态:', orderInfo.value.orderStatus) + } else { + // 订单未完成,添加到监控队列 + uni.setStorageSync('activeOrderId', orderInfo.value.orderId) + try { + if ($orderMonitor) { + $orderMonitor.removeOrder({ + orderId: orderInfo.value.orderId + }) + $orderMonitor.addOrder({ + orderId: orderInfo.value.orderId + }, 'detail') + console.log('订单已添加到监控队列:', orderInfo.value.orderId, '当前状态:', orderInfo.value.orderStatus) + } else { + console.warn('$orderMonitor 未定义,无法添加订单到监控队列') + } + } catch (error) { + console.error('添加订单到监控队列失败:', error) } - } catch (error) { - console.error('添加订单到监控队列失败:', error) } } } else { @@ -905,96 +975,99 @@ }) } - // 处理"宝未弹出"反馈 + // 处理"宝未弹出" - 跳转到反馈页面 const handleDeviceNoEject = () => { - uni.showModal({ - title: $t('order.deviceNoEjectTitle'), - content: $t('order.deviceNoEjectConfirm'), - confirmText: $t('common.confirm'), - cancelText: $t('common.cancel'), - success: async (res) => { - if (res.confirm) { - try { - uni.showLoading({ - title: $t('common.submitting') - }) - - // 调用反馈API提交工单 - const feedbackData = { - type: 'device_issue', - content: `订单号:${orderInfo.value.orderNo}\n设备号:${deviceId.value}\n问题:充电宝未弹出`, - phone: orderInfo.value.phone || '', - orderId: orderInfo.value.orderId, - orderNo: orderInfo.value.orderNo - } - - const result = await addUserFeedback(feedbackData) - - if (result.code === 200) { - uni.hideLoading() - uni.showToast({ - title: $t('order.deviceNoEjectSuccess'), - icon: 'success', - duration: 3000 - }) - } else { - throw new Error(result.msg || $t('order.deviceNoEjectFailed')) - } - } catch (error) { - uni.hideLoading() - uni.showToast({ - title: error.message || $t('order.deviceNoEjectFailed'), - icon: 'none', - duration: 2000 - }) - } - } - } + uni.navigateTo({ + url: '/pages/feedback/index?selectedType=0' }) } - // 处理"转为自用" - const handleConvertToOwned = () => { - uni.showModal({ - title: $t('order.convertToOwnTitle'), - content: $t('order.convertToOwnConfirm'), - confirmText: $t('common.confirm'), - cancelText: $t('common.cancel'), - success: async (res) => { - if (res.confirm) { - try { - uni.showLoading({ - title: $t('common.processing') - }) - - const result = await convertToOwned(orderInfo.value.orderId) - - if (result.code === 200) { - uni.hideLoading() - uni.showToast({ - title: $t('order.convertToOwnSuccess'), - icon: 'success', - duration: 2000 - }) - - // 刷新订单详情 - setTimeout(() => { - getOrderDetails() - }, 2000) - } else { - throw new Error(result.msg || $t('order.convertToOwnFailed')) - } - } catch (error) { - uni.hideLoading() - uni.showToast({ - title: error.message || $t('order.convertToOwnFailed'), - icon: 'none', - duration: 2000 - }) + // 处理"归还提醒" - 授权微信通知 + const handleReturnReminderSubscribe = async () => { + try { + // 订阅微信通知 + await new Promise((resolve, reject) => { + uni.requestSubscribeMessage({ + tmplIds: ['euJmQA2FjRFPZVSM4Z6U96i2eLRiGTLy3E3qMjIoYYY'], + success: (subscribeRes) => { + console.log('订阅消息success回调', subscribeRes) + resolve(subscribeRes) + }, + fail: (subscribeErr) => { + console.log('订阅消息fail回调', subscribeErr) + // 订阅失败不影响主流程 + resolve(subscribeErr) } - } + }) + }) + + uni.showToast({ + title: '订阅成功', + icon: 'success', + duration: 2000 + }) + } catch (error) { + console.log('订阅消息异常', error) + uni.showToast({ + title: '订阅失败,请稍后重试', + icon: 'none', + duration: 2000 + }) + } + } + + + // 处理"不想还了转为自用"(按最高费用) + const handleConvertToOwnedWithMaxFee = () => { + try { + convertToOwnPopup.value && typeof convertToOwnPopup.value.open === 'function' && convertToOwnPopup.value.open() + } catch (e) { + console.error('打开弹窗失败', e) + } + } + + // 关闭转为自用弹窗 + const closeConvertToOwnPopup = () => { + try { + convertToOwnPopup.value && typeof convertToOwnPopup.value.close === 'function' && convertToOwnPopup.value.close() + } catch (e) { + console.error('关闭弹窗失败', e) + } + } + + // 确认转为自用 + const confirmConvertToOwn = async () => { + try { + closeConvertToOwnPopup() + uni.showLoading({ + title: $t('common.processing') + }) + + const result = await closeWithMaxFee(orderInfo.value.orderNo) + + if (result.code === 200) { + uni.hideLoading() + uni.showToast({ + title: $t('order.convertToOwnWithMaxFeeSuccess'), + icon: 'success', + duration: 2000 + }) + + // 刷新订单详情 + setTimeout(() => { + getOrderDetails() + }, 2000) + } else { + throw new Error(result.msg || $t('order.convertToOwnWithMaxFeeFailed')) } - }) + } catch (error) { + uni.hideLoading() + uni.showToast({ + title: error.message || $t('order.convertToOwnWithMaxFeeFailed'), + icon: 'none', + duration: 2000 + }) + } } // 生命周期钩子 @@ -1079,6 +1152,16 @@ .page-header { margin-bottom: 30rpx; + .header-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + } + + .header-left { + flex: 1; + } + .header-title { font-size: 48rpx; font-weight: bold; @@ -1090,6 +1173,42 @@ font-size: 28rpx; color: #999; } + + .header-right { + display: flex; + align-items: center; + height:100%; + text-align: center; + } + + .device-no-eject-btn { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + // padding: 12rpx 20rpx; + // background: #E8F5E9; + // border-radius: 12rpx; + // border: 2rpx solid #07c160; + min-width: 120rpx; + + .device-no-eject-icon { + width: 68rpx; + height: 68rpx; + margin-bottom: 4rpx; + } + + .device-no-eject-text { + font-size: 26rpx; + color: #07c160; + font-weight: 500; + } + + &:active { + opacity: 0.8; + } + } } // 订单信息卡片 @@ -1102,36 +1221,72 @@ .info-row { display: flex; - justify-content: space-between; - // margin-bottom: 20rpx; + // justify-content: space-between; + align-items: center; + // gap: 20rpx; - .info-col { + .info-left { flex: 1; - text-align: center; + display: flex; + // justify-content: space-between; + align-items: center; - .info-value-wrapper { - display: flex; - align-items: baseline; - justify-content: center; - margin-bottom: 8rpx; + .info-col { + width: 200rpx; + // flex: 1; + // text-align: center; - .info-value-large { - font-size: 48rpx; - font-weight: bold; - color: #333; - line-height: 1; + .info-value-wrapper { + display: flex; + align-items: baseline; + // justify-content: center; + margin-bottom: 8rpx; + + .info-value-large { + font-size: 36rpx; + font-weight: bold; + color: #333; + line-height: 1; + } + + .info-value-unit { + font-size: 24rpx; + color: #666; + margin-left: 4rpx; + } } - .info-value-unit { + .info-label { font-size: 24rpx; - color: #666; - margin-left: 4rpx; + color: #999; } } + } - .info-label { - font-size: 24rpx; - color: #999; + .info-right { + display: flex; + align-items: center; + justify-content: flex-end; + flex-shrink: 0; + + .return-reminder-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 12rpx 24rpx; + background: #3EAB64; + border-radius: 50rpx; + border: 2rpx solid #3EAB64; + + .return-reminder-text { + font-size: 24rpx; + color: #fff; + font-weight: 500; + } + + &:active { + opacity: 0.8; + } } } } @@ -1316,6 +1471,91 @@ opacity: 0.8; } } + + &.convert-btn { + background: #fff; + color: #07c160; + border: 2rpx solid #07c160; + flex: 1; + + &:active { + opacity: 0.8; + } + } + } + } + } + + /* 转为自用确认弹窗样式 */ + .convert-to-own-popup { + width: 640rpx; + max-width: 86vw; + background: #ffffff; + border-radius: 24rpx; + padding: 40rpx 40rpx 0; + box-sizing: border-box; + overflow: hidden; + + .popup-header { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 24rpx; + + .popup-title { + font-size: 36rpx; + font-weight: 600; + color: #333; + } + } + + .popup-content { + padding: 0 0 40rpx; + box-sizing: border-box; + text-align: left; + + .popup-text { + font-size: 28rpx; + color: #666; + line-height: 1.8; + text-align: left; + display: block; + width: 100%; + } + } + + .popup-actions { + display: flex; + border-top: 1rpx solid #f0f0f0; + margin-left: -40rpx; + margin-right: -40rpx; + + .popup-btn { + flex: 1; + height: 88rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + box-sizing: border-box; + + &.cancel-btn { + color: #666; + border-right: 1rpx solid #f0f0f0; + + &:active { + background-color: #f5f5f5; + } + } + + &.confirm-btn { + color: #07c160; + font-weight: 600; + + &:active { + background-color: #f0f9f4; + } + } } } } diff --git a/static/power_no_popout.png b/static/power_no_popout.png new file mode 100644 index 0000000000000000000000000000000000000000..4c11fb918343af15d6379db701e14f2965a267d7 GIT binary patch literal 2522 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 literal 0 HcmV?d00001