fix:修复bug

This commit is contained in:
2026-02-06 18:09:23 +08:00
parent f476cee76d
commit bb5a6dd100
51 changed files with 4491 additions and 2630 deletions
+2 -2
View File
@@ -379,7 +379,7 @@ const handleSearch = () => {
const handleService = () => { const handleService = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/help/index' url: '/subPackages/service/help/index'
}) })
} }
@@ -389,7 +389,7 @@ const handleSearch = () => {
const handleJoinTap = () => { const handleJoinTap = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/join/index' url: '/subPackages/business/join/index'
}) })
} }
+1 -1
View File
@@ -65,7 +65,7 @@ const request = (option) => {
const query = current && current.options ? Object.keys(current.options).map(k => `${k}=${encodeURIComponent(current.options[k])}`).join('&') : '' const query = current && current.options ? Object.keys(current.options).map(k => `${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
const redirect = encodeURIComponent(query ? `${route}?${query}` : route) const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
// 跳转到登录页 // 跳转到登录页
uni.reLaunch({ url: `/pages/login/index?redirect=${redirect}` }) uni.reLaunch({ url: `/subPackages/user/login/index?redirect=${redirect}` })
} catch (e) {} } catch (e) {}
} }
+2 -2
View File
@@ -1,6 +1,6 @@
// export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用 // export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用
// export const URL = "https://manager.fdzpower.com/api" //正式服务器 export const URL = "https://manager.fdzpower.com/api" //正式服务器
export const URL = "https://fansdev.gxfs123.com/api" //测试服务器 // export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
// export const URL = "http://192.168.5.123:8080" //本地调试 // export const URL = "http://192.168.5.123:8080" //本地调试
// export const URL = "http://127.0.0.1:8080" //本地调试 // export const URL = "http://127.0.0.1:8080" //本地调试
+8 -2
View File
@@ -55,7 +55,12 @@ export default {
pull: 'Pull to refresh', pull: 'Pull to refresh',
release: 'Release to refresh', release: 'Release to refresh',
noMore: 'No more', noMore: 'No more',
functionDeveloping: 'Function under development' functionDeveloping: 'Function under development',
saveImage: 'Save to Phone',
saveSuccess: 'Saved successfully',
saving: 'Saving...',
saveFailed: 'Save failed',
downloadFailed: 'Download failed'
}, },
nav: { nav: {
@@ -272,7 +277,8 @@ export default {
deviceNoEjectSuccess: 'Feedback received, will be handled within 5 minutes', deviceNoEjectSuccess: 'Feedback received, will be handled within 5 minutes',
deviceNoEjectFailed: 'Feedback submission failed, please try again', deviceNoEjectFailed: 'Feedback submission failed, please try again',
returnProblemTip: 'After returning, if the order is still active, please go to ', returnProblemTip: 'After returning, if the order is still active, please go to ',
contactStaff: ' to contact staff.' contactStaff: ' to contact staff.',
returnLocationMap: 'Return Location Map',
}, },
user: { user: {
+8 -2
View File
@@ -55,7 +55,12 @@ export default {
pull: 'Tarik untuk muat ulang', pull: 'Tarik untuk muat ulang',
release: 'Lepas untuk muat ulang', release: 'Lepas untuk muat ulang',
noMore: 'Tidak ada lagi', noMore: 'Tidak ada lagi',
functionDeveloping: 'Fungsi sedang dikembangkan' functionDeveloping: 'Fungsi sedang dikembangkan',
saveImage: 'Simpan ke Ponsel',
saveSuccess: 'Berhasil disimpan',
saving: 'Menyimpan...',
saveFailed: 'Gagal menyimpan',
downloadFailed: 'Gagal mengunduh'
}, },
nav: { nav: {
@@ -272,7 +277,8 @@ export default {
deviceNoEjectSuccess: 'Umpan balik telah diterima, akan diproses dalam 5 menit', deviceNoEjectSuccess: 'Umpan balik telah diterima, akan diproses dalam 5 menit',
deviceNoEjectFailed: 'Pengiriman umpan balik gagal, harap coba lagi nanti', deviceNoEjectFailed: 'Pengiriman umpan balik gagal, harap coba lagi nanti',
returnProblemTip: 'Setelah produk dikembalikan ke gudang, pesanan masih belum berakhir, harap pergi ke', returnProblemTip: 'Setelah produk dikembalikan ke gudang, pesanan masih belum berakhir, harap pergi ke',
contactStaff: 'Hubungi staf.' contactStaff: 'Hubungi staf.',
returnLocationMap: 'Peta Lokasi Pengembalian',
}, },
user: { user: {
+8 -3
View File
@@ -55,7 +55,11 @@ export default {
pull: '下拉刷新', pull: '下拉刷新',
release: '释放刷新', release: '释放刷新',
noMore: '没有更多了', noMore: '没有更多了',
functionDeveloping: '功能开发中' functionDeveloping: '功能开发中',
saveImage:'保存到手机',
saveSuccess:'保存成功',
saving:'保存中...',
saveFailed:'保存失败'
}, },
nav: { nav: {
@@ -180,7 +184,7 @@ export default {
myCoupons: '优惠券优惠', myCoupons: '优惠券优惠',
payNow: '立即支付', payNow: '立即支付',
cancelOrder: '取消订单', cancelOrder: '取消订单',
quickReturn: '快速归还', quickReturn: '附近可归还',
returnDevice: '归还设备', returnDevice: '归还设备',
viewDetails: '查看详情', viewDetails: '查看详情',
orderCompleted: '订单已完成', orderCompleted: '订单已完成',
@@ -272,7 +276,8 @@ export default {
deviceNoEjectSuccess: '反馈已受理,将在5分钟内处理', deviceNoEjectSuccess: '反馈已受理,将在5分钟内处理',
deviceNoEjectFailed: '反馈提交失败,请稍后重试', deviceNoEjectFailed: '反馈提交失败,请稍后重试',
returnProblemTip: '产品归还入仓后,订单仍未结束,请前往', returnProblemTip: '产品归还入仓后,订单仍未结束,请前往',
contactStaff: '联系工作人员。' contactStaff: '联系工作人员。',
returnLocationMap:'归还点地图',
}, },
user: { user: {
+290 -249
View File
@@ -5,7 +5,8 @@
"^uv-(.*)": "@climblee/uv-ui/components/uv-$1/uv-$1.vue" "^uv-(.*)": "@climblee/uv-ui/components/uv-$1/uv-$1.vue"
} }
}, },
"pages": [{ "pages": [
{
"path": "pages/index/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
@@ -15,6 +16,12 @@
"enableShareTimeline": true "enableShareTimeline": true
} }
}, },
{
"path": "pages/serve/bagCheck/index",
"style": {
"navigationBarTitleText": ""
}
},
{ {
"path": "pages/scan/index", "path": "pages/scan/index",
"style": { "style": {
@@ -23,168 +30,6 @@
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
} }
}, },
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#C8F4D9",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/login/phone",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#C8F4D9",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/agreement",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/agreement-zh",
"style": {
"navigationBarTitleText": "用户协议",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/agreement-en",
"style": {
"navigationBarTitleText": "User Agreement",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/privacy",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/privacy-zh",
"style": {
"navigationBarTitleText": "隐私政策",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/privacy-en",
"style": {
"navigationBarTitleText": "Privacy Policy",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/legal/terms",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#D1FFE1",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/userProfile/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#D1FFE1",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/setting/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/deposit/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/device/orderList",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/device/orderDetail",
"style": {
"navigationBarTitleText": "定制详情",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/order/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/order/payment",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/expressReturn/addExpressReturn",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/feedback/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/feedback/list",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/feedback/detail",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/help/index",
"style": {
"navigationBarTitleText": ""
}
},
{ {
"path": "pages/device/detail", "path": "pages/device/detail",
"style": { "style": {
@@ -193,37 +38,6 @@
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black"
} }
}, },
{
"path": "pages/serve/bagCheck/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/return/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/order/success",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/order/return-success",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{ {
"path": "pages/order/detail", "path": "pages/order/detail",
"style": { "style": {
@@ -232,21 +46,6 @@
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black"
} }
}, },
{
"path": "pages/expressReturn/index",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "default"
}
},
{
"path": "pages/expressReturn/detail",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{ {
"path": "pages/search/index", "path": "pages/search/index",
"style": { "style": {
@@ -255,22 +54,6 @@
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black"
} }
}, },
{
"path": "pages/position/detail",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#D1FFE1",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/join/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{ {
"path": "pages/waiting/index", "path": "pages/waiting/index",
"style": { "style": {
@@ -278,9 +61,285 @@
"navigationBarBackgroundColor": "#ffffff", "navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black"
} }
}
],
"subPackages": [
{
"root": "subPackages/user",
"pages": [
{
"path": "login/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#C8F4D9",
"navigationBarTextStyle": "black"
}
}, },
{ {
"path": "pages/webview/index", "path": "login/phone",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#C8F4D9",
"navigationBarTextStyle": "black"
}
},
{
"path": "my/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#D1FFE1",
"navigationBarTextStyle": "black"
}
},
{
"path": "userProfile/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#D1FFE1",
"navigationBarTextStyle": "black"
}
},
{
"path": "setting/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "user/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
]
},
{
"root": "subPackages/order",
"pages": [
{
"path": "index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "payment",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "success",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "return-success",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
]
},
{
"root": "subPackages/service",
"pages": [
{
"path": "feedback/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "feedback/list",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "feedback/detail",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "help/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "expressReturn/index",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "default"
}
},
{
"path": "expressReturn/addExpressReturn",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "expressReturn/detail",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "return/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
]
},
{
"root": "subPackages/business",
"pages": [
{
"path": "purchase/index",
"style": {
"navigationBarTitleText": "优惠专区",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "my-card",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "my-coupon",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "position/detail",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#D1FFE1",
"navigationBarTextStyle": "black"
}
},
{
"path": "device-goods",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "device-orderList",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "device-orderDetail",
"style": {
"navigationBarTitleText": "定制详情",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
]
},
{
"root": "subPackages/other",
"pages": [
{
"path": "legal/agreement",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "legal/agreement-zh",
"style": {
"navigationBarTitleText": "用户协议",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "legal/agreement-en",
"style": {
"navigationBarTitleText": "User Agreement",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "legal/privacy",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "legal/privacy-zh",
"style": {
"navigationBarTitleText": "隐私政策",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "legal/privacy-en",
"style": {
"navigationBarTitleText": "Privacy Policy",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "legal/terms",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "join/index",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "webview/index",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff", "navigationBarBackgroundColor": "#ffffff",
@@ -289,36 +348,18 @@
} }
}, },
{ {
"path": "pages/purchase/index", "path": "serve/bagCheck/index",
"style": { "style": {
"navigationBarTitleText": "优惠专区", "navigationBarTitleText": ""
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
} }
}, },
{ {
"path": "pages/my/card", "path": "deposit/index",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": ""
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
} }
},
{
"path": "pages/my/coupon",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/device/goods",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
} }
]
} }
], ],
"globalStyle": { "globalStyle": {
+25 -7
View File
@@ -81,11 +81,12 @@
<!-- 底部操作区 --> <!-- 底部操作区 -->
<view class="footer"> <view class="footer">
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }" <view class="rent-button" :class="{ 'return-button': hasActiveOrder }"
@click="handleRent('wx-pay')"> @click="handleRent(isWechatMiniProgram ? 'wx-score-pay' : 'wx-pay')">
<text>{{ hasActiveOrder ? $t('order.returnDevice') : $t('device.rentDepositFree') }}</text> <text>{{ hasActiveOrder ? $t('order.returnDevice') : getRentButtonText() }}</text>
</button> </view>
<view class="wechat-credit"> <!-- 微信支付分标识仅在微信小程序环境显示 -->
<view class="wechat-credit" v-if="isWechatMiniProgram">
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="wx-icon"></image> <image src="/static/images/wxpayflag.png" mode="aspectFit" class="wx-icon"></image>
<text class="credit-text">{{ $t('device.wxPayScoreDesc') }}</text> <text class="credit-text">{{ $t('device.wxPayScoreDesc') }}</text>
</view> </view>
@@ -162,6 +163,7 @@
const isLoggedIn = ref(true) const isLoggedIn = ref(true)
const phoneNumber = ref('') const phoneNumber = ref('')
const showPhoneAuthPopup = ref(false) const showPhoneAuthPopup = ref(false)
const isWechatMiniProgram = ref(false)
// 生命周期 onLoad 钩子 // 生命周期 onLoad 钩子
onLoad(async (options) => { onLoad(async (options) => {
@@ -179,6 +181,13 @@
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: t('device.deviceInfo') title: t('device.deviceInfo')
}) })
// 检测当前运行环境
// #ifdef MP-WEIXIN
isWechatMiniProgram.value = true
// #endif
// #ifdef H5
isWechatMiniProgram.value = false
// #endif
await checkUserPhone() await checkUserPhone()
await fetchDeviceInfo() await fetchDeviceInfo()
}) })
@@ -368,7 +377,7 @@
const goToPurchase = () => { const goToPurchase = () => {
const positionId = positionInfo.value?.positionId || positionInfo.value?.id const positionId = positionInfo.value?.positionId || positionInfo.value?.id
uni.navigateTo({ uni.navigateTo({
url: `/pages/purchase/index?positionId=${positionId}` url: `/subPackages/business/purchase/index?positionId=${positionId}`
}) })
} }
@@ -381,7 +390,7 @@
const order = inUseRes.data const order = inUseRes.data
// 如果有正在进行的订单,跳转到归还页面,带上设备ID // 如果有正在进行的订单,跳转到归还页面,带上设备ID
uni.redirectTo({ uni.redirectTo({
url: `/pages/device/return?deviceId=${deviceId.value}` url: `/subPackages/service/return/index?deviceId=${deviceId.value}`
}) })
return return
} }
@@ -499,6 +508,15 @@
return `不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断` return `不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
} }
// 获取租借按钮文本
const getRentButtonText = () => {
if (isWechatMiniProgram.value) {
return t('device.rentDepositFree')
} else {
return '立即租借'
}
}
// 提交租借订单 // 提交租借订单
const submitRentOrder = async (payWay) => { const submitRentOrder = async (payWay) => {
try { try {
+10 -10
View File
@@ -287,11 +287,11 @@
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : '' `${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
const redirect = encodeURIComponent(query ? `${route}?${query}` : route) const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
uni.reLaunch({ uni.reLaunch({
url: `/pages/login/index?redirect=${redirect}` url: `/subPackages/user/login/index?redirect=${redirect}`
}) })
} catch (e) { } catch (e) {
uni.reLaunch({ uni.reLaunch({
url: '/pages/login/index' url: '/subPackages/user/login/index'
}) })
} }
} }
@@ -407,7 +407,7 @@
} else if (config.linkType === 'external' && config.linkUrl) { } else if (config.linkType === 'external' && config.linkUrl) {
// 跳转到外部链接(H5页面) // 跳转到外部链接(H5页面)
uni.navigateTo({ uni.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(config.linkUrl)}` url: `/subPackages/other/webview/index?url=${encodeURIComponent(config.linkUrl)}`
}) })
} else if (config.linkType === 'internal' && config.linkUrl) { } else if (config.linkType === 'internal' && config.linkUrl) {
// 跳转到内部页面 // 跳转到内部页面
@@ -875,7 +875,7 @@
const goMy = () => { const goMy = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/my/index' url: '/subPackages/user/my/index'
}) })
} }
@@ -906,13 +906,13 @@
// 跳转到商品详情页面,传递商品ID // 跳转到商品详情页面,传递商品ID
uni.navigateTo({ uni.navigateTo({
url: `/pages/device/goods?productId=${productId}` url: `/subPackages/business/device-goods?productId=${productId}`
}) })
} else { } else {
console.warn('没有查询到商品数据') console.warn('没有查询到商品数据')
// 如果没有商品数据,仍然跳转到商品页面(显示空状态) // 如果没有商品数据,仍然跳转到商品页面(显示空状态)
uni.navigateTo({ uni.navigateTo({
url: '/pages/device/goods' url: '/subPackages/business/device-goods'
}) })
} }
} catch (error) { } catch (error) {
@@ -921,7 +921,7 @@
// 即使查询失败,也跳转到商品页面 // 即使查询失败,也跳转到商品页面
uni.navigateTo({ uni.navigateTo({
url: '/pages/device/goods' url: '/subPackages/business/device-goods'
}) })
} }
} }
@@ -1061,14 +1061,14 @@
success: () => { success: () => {
setTimeout(() => { setTimeout(() => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}` url: `/subPackages/order/payment?orderId=${unpaidOrder.orderId}`
}); });
}, 100); }, 100);
} }
}); });
} else { } else {
uni.navigateTo({ uni.navigateTo({
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}` url: `/subPackages/order/payment?orderId=${unpaidOrder.orderId}`
}); });
} }
} else { } else {
@@ -1171,7 +1171,7 @@
// 使用指南弹窗控制 // 使用指南弹窗控制
const openPopup = () => { const openPopup = () => {
uni.navigateTo({ uni.navigateTo({
url:'/pages/device/goods' url:'/subPackages/business/device-goods'
}) })
// try { // try {
// showGuidePopup.value = true // showGuidePopup.value = true
+243 -31
View File
@@ -48,9 +48,10 @@
</view> </view>
</view> </view>
</view> </view>
<!-- <view class="fee-rule"> <!-- 计费规则图片 -->
{{ feeRuleText }} <view class="fee-rule-image">
</view> --> <image :src="getFeeRuleImageUrl()" mode="widthFix" class="rule-image"></image>
</view>
</view> </view>
<!-- 租借信息卡片 --> <!-- 租借信息卡片 -->
@@ -209,6 +210,27 @@
</view> </view>
</view> </view>
</uv-popup> </uv-popup>
<!-- 归还地图弹窗 -->
<view class="return-map-popup" v-if="showReturnMapPopup" @click.self="closeReturnMapPopup">
<view class="popup-mask"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">{{ $t('order.returnLocationMap') }}</text>
<view class="close-btn" @click="closeReturnMapPopup">
<text class="close-icon">×</text>
</view>
</view>
<view class="popup-body">
<image :src="returnMapImageUrl" mode="widthFix" class="map-image"></image>
</view>
<view class="popup-footer">
<view class="save-btn" @click="saveReturnMapImage">
{{ $t('common.saveImage') }}
</view>
</view>
</view>
</view>
</view> </view>
</template> </template>
@@ -288,7 +310,8 @@
userPurchaseId: '', userPurchaseId: '',
discountTypeName: '', discountTypeName: '',
originalFee:'', originalFee:'',
discountAmount:'' discountAmount:'',
returnMapImage: ''
}) })
const timer = ref(null) const timer = ref(null)
const statusCheckTimer = ref(null) const statusCheckTimer = ref(null)
@@ -303,6 +326,8 @@
const countdownTimer = ref(null) const countdownTimer = ref(null)
const feeRuleText = ref('') const feeRuleText = ref('')
const convertToOwnPopup = ref(null) const convertToOwnPopup = ref(null)
const showReturnMapPopup = ref(false)
const returnMapImageUrl = ref('')
// 计算属性:是否显示优惠券/会员卡可用提示 // 计算属性:是否显示优惠券/会员卡可用提示
const canUsePromotionTag = computed(() => { const canUsePromotionTag = computed(() => {
@@ -400,16 +425,88 @@
// 联系客服 // 联系客服
const contactService = () => { const contactService = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/help/index' url: '/subPackages/service/help/index'
}) })
} }
// 获取计费规则图片URL
const getFeeRuleImageUrl = () => {
const locale = instance?.proxy?.$i18n?.locale || 'en_US'
// 如果是中文环境,显示中文图片,否则显示英文图片
if (locale === 'zh_CN' || locale === 'zh-CN' || locale === 'zh') {
return 'https://static.fdzpower.com/order_notice/notice_CN.png'
}
return 'https://static.fdzpower.com/order_notice/notice_EN.png'
}
// 快速归还 // 快速归还
const quickReturn = () => { const quickReturn = () => {
// 检查是否有 returnMapImage 字段
console.log(orderInfo.value.returnMapImage);
if (orderInfo.value.returnMapImage) {
// 有值则弹窗显示图片
returnMapImageUrl.value = orderInfo.value.returnMapImage
showReturnMapPopup.value = true
} else {
// 没有值则继续执行跳转流程
uni.navigateTo({ uni.navigateTo({
url: '/pages/search/index' url: '/pages/search/index'
}) })
} }
}
// 关闭归还地图弹窗
const closeReturnMapPopup = () => {
showReturnMapPopup.value = false
}
// 保存归还地图图片
const saveReturnMapImage = () => {
uni.showLoading({
title: t('common.saving')
})
uni.downloadFile({
url: returnMapImageUrl.value,
success: (res) => {
if (res.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.hideLoading()
uni.showToast({
title: t('common.saveSuccess'),
icon: 'success'
})
},
fail: (err) => {
uni.hideLoading()
console.error('保存图片失败:', err)
uni.showToast({
title: t('common.saveFailed'),
icon: 'none'
})
}
})
} else {
uni.hideLoading()
uni.showToast({
title: t('common.downloadFailed'),
icon: 'none'
})
}
},
fail: (err) => {
uni.hideLoading()
console.error('下载图片失败:', err)
uni.showToast({
title: t('common.downloadFailed'),
icon: 'none'
})
}
})
}
// 再次租借 // 再次租借
const rentAgain = () => { const rentAgain = () => {
@@ -781,6 +878,7 @@
orderInfo.value.userPurchaseId = orderData.userPurchaseId || '' orderInfo.value.userPurchaseId = orderData.userPurchaseId || ''
orderInfo.value.discountTypeName = orderData.discountTypeName || '' orderInfo.value.discountTypeName = orderData.discountTypeName || ''
orderInfo.value.originalFee = orderData.originalFee||'' orderInfo.value.originalFee = orderData.originalFee||''
orderInfo.value.returnMapImage = orderData.returnMapImage||''
// 保存快递归还开始时间(小时为单位) // 保存快递归还开始时间(小时为单位)
orderInfo.value.expressReturnStart = orderData.expressReturnStart || null orderInfo.value.expressReturnStart = orderData.expressReturnStart || null
@@ -997,37 +1095,40 @@
// 申请退款 // 申请退款
const handleWithdraw = async () => { const handleWithdraw = async () => {
try { uni.navigateTo({
uni.showLoading({ url:'/subPackages/service/feedback/index'
title: t('common.processing')
}) })
// try {
// uni.showLoading({
// title: t('common.processing')
// })
const res = await withdrawDeposit(orderInfo.value.orderNo) // const res = await withdrawDeposit(orderInfo.value.orderNo)
if (res && res.code === 200) { // if (res && res.code === 200) {
uni.showToast({ // uni.showToast({
title: t('order.refundSuccess'), // title: t('order.refundSuccess'),
icon: 'success' // icon: 'success'
}) // })
orderInfo.value.withdrawStatus = 'processing' // orderInfo.value.withdrawStatus = 'processing'
orderInfo.value.isWithdrawn = true // orderInfo.value.isWithdrawn = true
setTimeout(() => { // setTimeout(() => {
getOrderDetails() // getOrderDetails()
}, 1500) // }, 1500)
} else { // } else {
throw new Error(res?.msg || t('order.refundFailed')) // throw new Error(res?.msg || t('order.refundFailed'))
} // }
} catch (error) { // } catch (error) {
console.error('退款申请失败:', error) // console.error('退款申请失败:', error)
uni.showToast({ // uni.showToast({
title: error.message || t('order.refundFailed'), // title: error.message || t('order.refundFailed'),
icon: 'none' // icon: 'none'
}) // })
} finally { // } finally {
uni.hideLoading() // uni.hideLoading()
} // }
} }
// 返回首页 // 返回首页
@@ -1381,6 +1482,18 @@
color: #4CAF50; color: #4CAF50;
line-height: 1.6; line-height: 1.6;
} }
.fee-rule-image {
margin-top: 20rpx;
width: 100%;
.rule-image {
// height: 100%;
width: 100%;
display: block;
border-radius: 12rpx;
}
}
} }
// 转为自用提示 // 转为自用提示
@@ -1655,4 +1768,103 @@
} }
} }
} }
/* 归还地图弹窗样式 */
.return-map-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
.popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
}
.popup-content {
position: relative;
width: 90%;
max-width: 600rpx;
background: #ffffff;
border-radius: 24rpx;
overflow: hidden;
z-index: 10000;
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.close-btn {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f5f5;
.close-icon {
font-size: 40rpx;
color: #666;
line-height: 1;
}
&:active {
opacity: 0.7;
}
}
}
.popup-body {
padding: 30rpx;
max-height: 60vh;
overflow-y: auto;
.map-image {
width: 100%;
display: block;
border-radius: 12rpx;
}
}
.popup-footer {
padding: 20rpx 30rpx 30rpx;
.save-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #07c160, #10d673);
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 600;
color: #fff;
&:active {
opacity: 0.8;
}
}
}
}
}
</style> </style>
+1 -1
View File
@@ -303,7 +303,7 @@ const init = async () => {
return return
} }
uni.navigateTo({ uni.navigateTo({
url: `/pages/position/detail?positionId=${position.positionId}` url: `/subPackages/business/position/detail?positionId=${position.positionId}`
}) })
} }
</script> </script>
+6 -6
View File
@@ -7,15 +7,15 @@
<script> <script>
import { import {
wxLogin, wxLogin,
} from '../../../util/index' } from '@/util/index'
import { import {
getMyIndexInfo getMyIndexInfo
} from "../../../config/api/user.js"; } from "@/config/api/user.js";
import { import {
queryHasOrder, queryHasOrder,
checkOrdersByStatus checkOrdersByStatus
} from "../../../config/api/order.js"; } from "@/config/api/order.js";
export default { export default {
data() { data() {
@@ -56,7 +56,7 @@
// 如果是使用中的订单,跳转到归还页面 // 如果是使用中的订单,跳转到归还页面
console.log('检测到使用中订单,跳转归还页:', latestOrder.orderId); console.log('检测到使用中订单,跳转归还页:', latestOrder.orderId);
uni.redirectTo({ uni.redirectTo({
url: `/pages/device/return?orderId=${latestOrder.orderId}` url: `/subPackages/service/return/index?orderId=${latestOrder.orderId}`
}); });
} else if (latestOrder.orderStatus === 'waiting_for_payment') { } else if (latestOrder.orderStatus === 'waiting_for_payment') {
// 如果是待支付订单,跳转到支付页面,并传递必要信息 // 如果是待支付订单,跳转到支付页面,并传递必要信息
@@ -81,13 +81,13 @@
const totalAmount = (parseFloat(depositAmount) + parseFloat(packagePrice)).toFixed(2); const totalAmount = (parseFloat(depositAmount) + parseFloat(packagePrice)).toFixed(2);
uni.redirectTo({ uni.redirectTo({
url: `/pages/order/payment?orderId=${latestOrder.orderId}&packageTimeHours=${packageTimeHours}&packagePrice=${packagePrice}&hourlyRate=${hourlyRate}&totalAmount=${totalAmount}&depositAmount=${depositAmount}` url: `/subPackages/order/payment?orderId=${latestOrder.orderId}&packageTimeHours=${packageTimeHours}&packagePrice=${packagePrice}&hourlyRate=${hourlyRate}&totalAmount=${totalAmount}&depositAmount=${depositAmount}`
}); });
} else { } else {
// 其他状态(理论上不应该到这里,除非statusesToCheck配置错误),默认到详情页 // 其他状态(理论上不应该到这里,除非statusesToCheck配置错误),默认到详情页
console.log('检测到其他状态订单,跳转详情页:', latestOrder.orderId); console.log('检测到其他状态订单,跳转详情页:', latestOrder.orderId);
uni.redirectTo({ uni.redirectTo({
url: `/pages/device/detail?deviceNo=${deviceNo}` url: `/pages/order/detail?deviceNo=${deviceNo}`
}); });
} }
} else { } else {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 26 KiB

@@ -763,7 +763,7 @@
const GotoList = ()=>{ const GotoList = ()=>{
uni.navigateTo({ uni.navigateTo({
url:'/pages/device/orderList' url:'/subPackages/business/device-orderList'
}) })
} }
@@ -825,7 +825,7 @@
// //
setTimeout(() => { setTimeout(() => {
uni.switchTab({ uni.switchTab({
url: '/pages/order/index' url: '/subPackages/business/device-orderList'
}) })
}, 2000) }, 2000)
}, },
@@ -599,7 +599,7 @@
const onReorder = (order) => { const onReorder = (order) => {
if(order){ if(order){
uni.navigateTo({ uni.navigateTo({
url: `/pages/device/goods?productId=${order}` url: `/subPackages/business/device-goods?productId=${order}`
}); });
} }
// console.log(order); // console.log(order);
@@ -466,7 +466,7 @@
// //
const navigateToDeviceOrderDetail = (order) => { const navigateToDeviceOrderDetail = (order) => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/device/orderDetail?orderId=${order.orderId || order.orderNo}` url: `/subPackages/business/device-orderDetail?orderId=${order.orderId || order.orderNo}`
}); });
}; };
@@ -71,8 +71,8 @@ import {
import { import {
getNearbyDevices, getNearbyDevices,
transformDeviceData transformDeviceData
} from '../../config/api/device.js' } from '@/config/api/device.js'
import { useI18n } from '../../utils/i18n.js' import { useI18n } from '@/utils/i18n.js'
const { t } = useI18n() const { t } = useI18n()
@@ -391,7 +391,7 @@ const handleBuy = async () => {
// //
setTimeout(() => { setTimeout(() => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/my/card' url: '/subPackages/business/my-card'
}) })
}, 1500) }, 1500)
}, },
@@ -463,7 +463,7 @@ const handleBuy = async () => {
// //
setTimeout(() => { setTimeout(() => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/my/coupon' url: '/subPackages/business/my-coupon'
}) })
}, 1500) }, 1500)
}, },
@@ -514,11 +514,11 @@ const handleBuy = async () => {
const goToMyProducts = () => { const goToMyProducts = () => {
if (currentTab.value === 'card') { if (currentTab.value === 'card') {
uni.navigateTo({ uni.navigateTo({
url: '/pages/my/card' url: '/subPackages/business/my-card'
}) })
} else if (currentTab.value === 'coupon') { } else if (currentTab.value === 'coupon') {
uni.navigateTo({ uni.navigateTo({
url: '/pages/my/coupon' url: '/subPackages/business/my-coupon'
}) })
} }
} }
+399
View File
@@ -0,0 +1,399 @@
<template>
<view class="success-container">
<!-- 支付成功状态和订单信息 -->
<view class="status-order-card">
<!-- 支付成功状态 -->
<view class="status-section">
<view class="status-icon success"></view>
<view class="status-text">{{ $t('success.paymentSuccess') }}</view>
<view class="status-desc">{{ $t('success.paymentSuccessDesc') }}</view>
</view>
<!-- 分割线 -->
<view class="section-divider"></view>
<!-- 订单信息 -->
<view class="order-section">
<view class="card-title">{{ $t('success.orderInfo') }}</view>
<view class="info-item">
<text class="label">{{ $t('order.orderNo') }}</text>
<text class="value">{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">{{ $t('order.deviceNo') }}</text>
<text class="value">{{ orderInfo.deviceNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">{{ $t('success.paymentAmount') }}</text>
<text class="value">{{ orderInfo.amount || '0.00' }}</text>
</view>
<view class="info-item">
<text class="label">{{ $t('success.paymentTime') }}</text>
<text class="value">{{ orderInfo.payTime || '-' }}</text>
</view>
</view>
</view>
<!-- 设备状态 -->
<view class="device-status">
<view class="status-message">{{ deviceMessage }}</view>
<view class="loading-animation" v-if="isLoading">
<view class="loading-circle"></view>
</view>
</view>
<!-- 操作按钮 -->
<view class="button-group">
<view class="secondary-btn" @click="goToHome">{{ $t('success.backToHome') }}</view>
<view class="primary-btn" @click="goToOrderList">{{ $t('success.viewOrder') }}</view>
</view>
</view>
</template>
<script setup>
import {
ref,
getCurrentInstance
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import {
queryById,
getOrderByOrderNo
} from '@/config/api/order.js'
// 获取当前实例以访问 $t 方法
const {
proxy
} = getCurrentInstance()
// 响应式数据
const orderId = ref('')
const orderInfo = ref({})
const isLoading = ref(true)
const deviceMessage = ref('')
const hasTriggeredDevice = ref(false)
// 页面加载
onLoad((options) => {
// 设置页面标题
uni.setNavigationBarTitle({
title: proxy.$t('success.paymentSuccess')
})
deviceMessage.value = proxy.$t('success.preparingDevice')
// #ifdef H5
if (uni.getStorageSync('pendingPaymentNo')) {
orderId.value = options.orderId
loadOrderInfo()
// 添加页面显示监听,防止页面切换后重复触发弹出
uni.$once('orderSuccess:' + orderId.value, () => {
console.log('已经触发过弹出逻辑,不再重复触发')
hasTriggeredDevice.value = true
})
} else {
uni.showToast({
title: proxy.$t('order.orderNotExist'),
icon: 'none'
})
setTimeout(() => {
goToHome()
}, 1500)
}
// #endif
// #ifndef H5
if (options && options.orderId) {
orderId.value = options.orderId
loadOrderInfo()
// 添加页面显示监听,防止页面切换后重复触发弹出
uni.$once('orderSuccess:' + orderId.value, () => {
console.log('已经触发过弹出逻辑,不再重复触发')
hasTriggeredDevice.value = true
})
} else {
uni.showToast({
title: proxy.$t('order.orderNotExist'),
icon: 'none'
})
setTimeout(() => {
goToHome()
}, 1500)
}
// #endif
})
// 加载订单信息
const loadOrderInfo = async () => {
try {
uni.showLoading({
title: proxy.$t('common.loading')
})
const res = await queryById(orderId.value)
if (res.code === 200 && res.data) {
const orderData = res.data
orderInfo.value = {
orderNo: orderData.orderNo || orderData.orderId,
deviceNo: orderData.deviceNo,
amount: orderData.payAmount || orderData.amount,
payTime: orderData.payTime || formatTime(new Date())
}
// 检查订单状态
if (orderData.orderStatus === 'IN_USED') {
// 如果已经是使用中状态,可能说明开锁已经完成
deviceMessage.value = '设备已弹出,请取走您的风扇'
isLoading.value = false
// 如果是第一次加载页面且设备已弹出,记录状态,避免重复弹出
if (!hasTriggeredDevice.value) {
uni.$emit('orderSuccess:' + orderId.value)
hasTriggeredDevice.value = true
}
} else {
// 在此页面不再触发设备弹出操作,仅更新展示文案和加载状态
deviceMessage.value = proxy.$t('success.paymentSuccessDesc')
isLoading.value = false
}
} else {
throw new Error('获取订单信息失败')
}
uni.hideLoading()
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || proxy.$t('order.getOrderFailed'),
icon: 'none'
})
}
}
// 格式化时间
const formatTime = (date) => {
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const hour = date.getHours().toString().padStart(2, '0')
const minute = date.getMinutes().toString().padStart(2, '0')
const second = date.getSeconds().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}
// 返回首页
const goToHome = () => {
uni.reLaunch({
url: '/pages/index/index'
})
}
// 查看订单列表
const goToOrderList = () => {
uni.redirectTo({
url: '/pages/order/index'
})
}
</script>
<style lang="scss" scoped>
.success-container {
padding: 20px;
padding-bottom: 180rpx;
background-color: #f5f5f5;
min-height: 100vh;
box-sizing: border-box;
}
.status-order-card {
background-color: #fff;
border-radius: 12px;
margin-bottom: 20px;
overflow: hidden;
.status-section {
padding: 30px;
text-align: center;
.status-icon {
width: 60px;
height: 60px;
margin: 0 auto 16px;
background-color: #07c160;
border-radius: 50%;
position: relative;
&::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 20px;
border: 3px solid #fff;
border-top: none;
border-right: none;
transform-origin: center;
transform: translate(-50%, -70%) rotate(-45deg);
}
}
.status-text {
font-size: 24px;
font-weight: bold;
color: #07c160;
margin-bottom: 8px;
}
.status-desc {
font-size: 14px;
color: #666;
}
}
.section-divider {
height: 1px;
background-color: #f0f0f0;
margin: 0 20px;
}
.order-section {
padding: 20px;
.card-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 16px;
color: #333;
padding-left: 12px;
position: relative;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 16px;
background-color: #07c160;
border-radius: 2px;
}
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
.label {
color: #666;
font-size: 14px;
}
.value {
color: #333;
font-size: 14px;
font-weight: 500;
}
}
}
}
.device-status {
background-color: #fff;
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
text-align: center;
.status-message {
font-size: 16px;
color: #333;
margin-bottom: 12px;
}
.loading-animation {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
.loading-circle {
width: 30px;
height: 30px;
border-radius: 50%;
border: 3px solid #f0f0f0;
border-top-color: #07c160;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}
}
.button-group {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
z-index: 10;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 20rpx;
.primary-btn {
background-color: #07c160;
color: #fff;
border: none;
border-radius: 32rpx;
padding: 0 32rpx;
font-size: 24rpx;
height: 64rpx;
line-height: 64rpx;
white-space: nowrap;
&:active {
opacity: 0.8;
}
}
.secondary-btn {
background-color: #fff;
color: #07c160;
border: 2rpx solid #07c160;
border-radius: 32rpx;
padding: 0 32rpx;
font-size: 24rpx;
height: 64rpx;
line-height: 64rpx;
white-space: nowrap;
&:active {
background-color: #f5f5f5;
}
}
}
</style>
@@ -41,9 +41,9 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app' import { onShow } from '@dcloudio/uni-app'
import { getUserInfo } from '../../util/index.js' import { getUserInfo } from '@/util/index.js'
import { withdrawDeposit } from '../../config/api/user.js' import { withdrawDeposit } from '@/config/api/user.js'
import { queryById } from '../../config/api/order.js' import { queryById } from '@/config/api/order.js'
import { useI18n } from '@/utils/i18n.js' import { useI18n } from '@/utils/i18n.js'
const { t } = useI18n() const { t } = useI18n()
+144
View File
@@ -0,0 +1,144 @@
<template>
<view>
</view>
</template>
<script>
import {
wxLogin,
} from '@/util/index'
import {
getMyIndexInfo
} from "@/config/api/user.js";
import {
queryHasOrder,
checkOrdersByStatus
} from "@/config/api/order.js";
export default {
data() {
return {
}
},
async onLoad(option) {
console.log('bagCheck onLoad option:', option);
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('device.checking')
})
try {
uni.showLoading({
title: this.$t('common.processing'),
mask: true
});
// 检查是否传入设备编号
if (!option || !option.deviceNo) {
throw new Error(this.$t('device.deviceNoNotRecognized'));
}
const deviceNo = option.deviceNo;
// 检查用户是否有进行中(in_used)或待支付(waiting_for_payment)的订单
const statusesToCheck = ['in_used', 'waiting_for_payment'];
const res = await checkOrdersByStatus(deviceNo, statusesToCheck);
if (res.code === 200 && res.data && res.data.length > 0) {
// 找到相关订单,取最新的一条
const latestOrder = res.data[0]; // 假设返回结果按时间倒序
if (latestOrder.orderStatus === 'in_used') {
// 如果是使用中的订单,跳转到归还页面
console.log('检测到使用中订单,跳转归还页:', latestOrder.orderId);
uni.redirectTo({
url: `/pages/device/return?orderId=${latestOrder.orderId}`
});
} else if (latestOrder.orderStatus === 'waiting_for_payment') {
// 如果是待支付订单,跳转到支付页面,并传递必要信息
console.log('检测到待支付订单,跳转支付页:', latestOrder.orderId);
// 获取套餐时间(分钟)
const packageTimeMinutes = latestOrder.packageTime || 60;
// 套餐小时数
const packageTimeHours = (packageTimeMinutes / 60).toFixed(1);
// 套餐价格
const packagePrice = latestOrder.packagePrice || '0.00';
// 计算每小时费率
const hourlyRate = (parseFloat(packagePrice) / (packageTimeMinutes / 60)).toFixed(2);
// 押金金额
const depositAmount = latestOrder.depositAmount || '99.00';
// 计算总金额(套餐+押金)
const totalAmount = (parseFloat(depositAmount) + parseFloat(packagePrice)).toFixed(2);
uni.redirectTo({
url: `/pages/order/payment?orderId=${latestOrder.orderId}&packageTimeHours=${packageTimeHours}&packagePrice=${packagePrice}&hourlyRate=${hourlyRate}&totalAmount=${totalAmount}&depositAmount=${depositAmount}`
});
} else {
// 其他状态(理论上不应该到这里,除非statusesToCheck配置错误),默认到详情页
console.log('检测到其他状态订单,跳转详情页:', latestOrder.orderId);
uni.redirectTo({
url: `/pages/device/detail?deviceNo=${deviceNo}`
});
}
} else {
// 没有找到相关状态的订单,跳转到设备详情页进行租借
console.log('未检测到相关订单,跳转详情页');
uni.redirectTo({
url: `/pages/device/detail?deviceNo=${deviceNo}`
});
}
} catch (error) {
// 只处理真正的错误,不是"没有订单"的情况
if (error.message && (
error.message.includes('未识别到设备编号') ||
error.message.includes('网络请求失败') ||
error.message.includes('服务器错误')
) ) {
console.error('扫码检查订单失败:', error);
uni.showToast({
title: error.message || this.$t('device.processFailed'),
icon: 'none',
duration: 2000
});
} else {
// 对于其他错误,包括"没有找到订单",直接跳转到详情页,不显示错误消息
console.log('没有找到符合条件的订单或发生其他错误,直接跳转详情页');
}
// 无论什么情况,都跳转到一个可用页面
setTimeout(() => {
if (option && option.deviceNo) {
uni.redirectTo({
url: `/pages/device/detail?deviceNo=${option.deviceNo}`
});
} else {
// uni.switchTab({
// url:'/pages/index/index'
// })
// 如果连deviceNo都没有,则返回首页
uni.reLaunch({ url: '/pages/index/index' });
}
}, 2000);
} finally {
uni.hideLoading();
}
},
methods: {
}
}
</script>
<style>
</style>
@@ -92,7 +92,7 @@
getFeedbackDetail, getFeedbackDetail,
getFeedbackMessages, getFeedbackMessages,
sendFeedbackMessage sendFeedbackMessage
} from '../../config/api/feedback.js'; } from '@/config/api/feedback.js';
import { import {
useI18n useI18n
} from '@/utils/i18n.js' } from '@/utils/i18n.js'
@@ -72,10 +72,10 @@
} from "@dcloudio/uni-app" } from "@dcloudio/uni-app"
import { import {
addUserFeedback addUserFeedback
} from '../../config/api/feedback' } from '@/config/api/feedback'
import { import {
uploadOssResource uploadOssResource
} from '../../config/api/user' } from '@/config/api/user'
import { import {
useI18n useI18n
} from '@/utils/i18n.js' } from '@/utils/i18n.js'
@@ -72,7 +72,7 @@
} from '@dcloudio/uni-app'; } from '@dcloudio/uni-app';
import { import {
getFeedbackList getFeedbackList
} from '../../config/api/feedback.js'; } from '@/config/api/feedback.js';
import { import {
useI18n useI18n
} from '@/utils/i18n.js' } from '@/utils/i18n.js'
@@ -27,9 +27,9 @@
<checkbox value="agreed" :checked="isAgreed" color="#07c160" class="agreement-checkbox" /> <checkbox value="agreed" :checked="isAgreed" color="#07c160" class="agreement-checkbox" />
<text class="agreement-text"> <text class="agreement-text">
{{ $t('auth.agreeToTerms') }} {{ $t('auth.agreeToTerms') }}
<text class="link" @tap.stop="go('/pages/legal/agreement')">{{ $t('user.userAgreement') }}</text> <text class="link" @tap.stop="go('/subPackages/other/legal/agreement')">{{ $t('user.userAgreement') }}</text>
{{ $t('common.and') }} {{ $t('common.and') }}
<text class="link" @tap.stop="go('/pages/legal/privacy')">{{ $t('user.privacyPolicy') }}</text> <text class="link" @tap.stop="go('/subPackages/other/legal/privacy')">{{ $t('user.privacyPolicy') }}</text>
</text> </text>
</label> </label>
</checkbox-group> </checkbox-group>
@@ -40,7 +40,7 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { wxLogin, getUserPhoneNumber, getUserInfo } from '../../util/index.js' import { wxLogin, getUserPhoneNumber, getUserInfo } from '@/util/index.js'
import { useI18n } from '@/utils/i18n.js' import { useI18n } from '@/utils/i18n.js'
const { t } = useI18n() const { t } = useI18n()
@@ -166,7 +166,7 @@
// //
const goToPhoneLogin = () => { const goToPhoneLogin = () => {
uni.navigateTo({ url: '/pages/login/phone' }) uni.navigateTo({ url: '/subPackages/user/login/phone' })
} }
</script> </script>
+479
View File
@@ -0,0 +1,479 @@
<template>
<view class="my-card-page">
<!-- 会员卡列表 -->
<view class="card-list" v-if="cardList.length > 0">
<view v-for="card in cardList" :key="card.id" style="position: relative;background-color: #f5f5f5;"
@click="viewCardDetail(card)" :style="card.cardType==='COUNT'?'height: 240rpx;':'height: 240rpx;'">
<view
style="height: 120rpx;background-color: #ffffff;z-index: 999;border-radius: 25rpx;padding: 32rpx;position: absolute;top: 0;left: 0;right: 0;">
<!-- 卡片头部标题和日期 -->
<view class="card-header">
<text class="card-name">{{ card.name }}</text>
<view class="card-date">
<text class="date-text" v-if="card.status !== 'expired'">{{ card.endDate }}{{
$t('myCard.expire') }}</text>
<text class="date-text expired" v-else>{{ $t('myCard.expiredOn') }}{{ card.endDate }}</text>
</view>
</view>
<!-- 地区信息 -->
<view class="card-region">
<text
class="region-text">{{ $t('myCard.onlyForRegionBefore') }}{{ card.positionName }}{{ $t('myCard.onlyForRegionAfter') }}</text>
<!-- 状态标签 / 去使用按钮 -->
<view v-if="card.status !== 'expired'" class="status-tag active"
style="display: flex; align-items: center; gap: 4rpx; background-color: #FFE2B8; border-radius: 20rpx; padding: 6rpx 20rpx;"
@click.stop="handleUseCard(card)">
<text class="status-text" style="color: #A16300;">{{ $t('myCard.toUse') }}</text>
<!-- <uv-icon name="scan" size="12" color="#D4A574"></uv-icon> -->
</view>
<view v-else class="status-tag" :class="getStatusClass(card.status)">
<text class="status-text">{{ getStatusText(card.status) }}</text>
</view>
</view>
</view>
<!-- 使用情况和操作按钮 -->
<view style="position: absolute; bottom: -20rpx; left: 0; right: 0; padding: 20rpx;z-index:1;">
<view class="card-footer">
<!-- 次卡信息 -->
<view v-if="card.cardType === 'COUNT'" class="card-usage-info">
<text class="usage-text">{{ $t('myCard.remainingTimes') }}{{ card.remainingCount }}{{
$t('myCard.times') }}</text>
</view>
<!-- 时长卡信息 -->
<view v-else class="card-usage-info">
<text class="usage-text">每日限用次数{{card.dailyLimitCount}}</text>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<!-- 续卡按钮仅次卡显示 -->
<view v-if="card.cardType === 'COUNT'" class="renew-btn" @click.stop="renewCard(card)">
<text class="renew-text">{{ $t('myCard.renew') }}</text>
<uv-icon name="arrow-right" size="14" color="#D4A574"></uv-icon>
</view>
<view v-else class="renew-btn">
<text class="renew-text">单次限时{{card.singleLimitMinutes}}分钟</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else>
<image class="empty-icon" src="/static/empty-card.png" mode="aspectFit"></image>
<text class="empty-text">{{ $t('myCard.noCards') }}</text>
<view class="buy-btn" @click="goToBuy">
<text class="buy-text">{{ $t('myCard.buyNow') }}</text>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted
} from 'vue'
import {
useI18n
} from '@/utils/i18n.js'
import {
getMemberCardsByStatus
} from '@/config/api/member.js'
import {
getQueryString
} from '@/util/index.js'
import {
getDeviceInfo
} from '@/config/api/device.js'
import {
getInUseOrder,
getUnpaidOrder
} from '@/config/api/order.js'
const {
t
} = useI18n()
// 会员卡列表
const cardList = ref([])
// 获取会员卡列表
const getCardList = async () => {
try {
const response = await getMemberCardsByStatus()
// 处理API返回的数据,转换为模板需要的格式
if (response.code === 200 && response.data) {
cardList.value = response.data.map(item => ({
id: item.id,
name: item.cardName,
cardType: item.cardType, // TIME 或 COUNT
// 次卡相关
totalCount: item.totalCount,
remainingCount: item.remainingCount,
singleLimitMinutesForCount: item.singleLimitMinutesForCount,
// 时长卡相关
cycleDays: item.cycleDays,
dailyLimitCount: item.dailyLimitCount,
singleLimitMinutes: item.singleLimitMinutes,
currentCycleStartTime: item.currentCycleStartTime,
currentCycleUsedCount: item.currentCycleUsedCount,
// 通用信息
status: item.status,
startDate: item.startTime?.split(' ')[0] || item.startTime,
endDate: item.endTime?.split(' ')[0] || item.endTime,
positionId: item.positionId,
positionName: item.positionName,
purchasePrice: item.purchasePrice,
remark: item.remark
}))
} else {
cardList.value = []
}
} catch (error) {
console.error('获取会员卡列表失败:', error)
uni.showToast({
title: t('myCard.getListFailed'),
icon: 'none'
})
}
}
// 计算进度条宽度
const getProgressWidth = (used, total) => {
if (!total || total === 0) return '0%'
const percentage = (used / total) * 100
return `${Math.min(percentage, 100)}%`
}
// 获取状态类名
const getStatusClass = (status) => {
const statusMap = {
'unused': 'active',
'expired': 'expired',
'used': 'used',
'active': 'active' // 兼容原始状态
}
return statusMap[status] || 'active' // 默认为active
}
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
'unused': t('myCard.active'), // unused表示未使用,即活跃状态
'expired': t('myCard.expired'),
'used': t('myCard.used'),
'active': t('myCard.active') // 兼容原始状态
}
return statusMap[status] || t('myCard.active') // 默认为active
}
// 查看卡详情
const viewCardDetail = (card) => {
// TODO: 跳转到卡详情页面
// uni.showToast({
// title: t('common.functionDeveloping'),
// icon: 'none'
// })
}
// 续卡
const renewCard = (card) => {
uni.navigateTo({
url: `/pages/purchase/index?positionId=${card.positionId}`
})
}
// 去使用会员卡
const handleUseCard = async (card) => {
try {
const scanResult = await new Promise((resolve, reject) => {
uni.scanCode({
success: resolve,
fail: reject
})
})
console.log('扫码结果:', scanResult);
let deviceNo;
// 兼容不同平台的扫码结果
if (scanResult.scanType === 'QR_CODE' || scanResult.scanType === 'qrCode') {
deviceNo = getQueryString(scanResult.result, 'deviceNo')
} else if (scanResult.path) {
deviceNo = getQueryString(scanResult.path, 'deviceNo')
} else {
deviceNo = scanResult.result
}
if (!deviceNo) {
uni.showToast({
title: t('home.invalidQRCode'),
icon: 'none'
})
return
}
uni.showLoading({
title: t('common.getting')
})
// 检查是否有使用中的订单
const inUseRes = await getInUseOrder()
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
uni.hideLoading()
const inUseOrder = inUseRes.data
uni.reLaunch({
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
})
return
}
// 检查是否有待支付订单
const orderRes = await getUnpaidOrder()
if (orderRes && orderRes.code === 200 && orderRes.data) {
uni.hideLoading()
const unpaidOrder = orderRes.data
uni.navigateTo({
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}`
})
return
}
// 获取设备信息并跳转详情
const deviceInfoRes = await getDeviceInfo(deviceNo)
uni.hideLoading()
if (deviceInfoRes.code === 200 && deviceInfoRes.data && deviceInfoRes.data.device) {
const deviceInfo = deviceInfoRes.data.device
let url = `/pages/device/detail?deviceNo=${deviceNo}`
if (deviceInfo.feeConfig) {
url += `&feeConfig=${encodeURIComponent(deviceInfo.feeConfig)}`
}
uni.navigateTo({
url
})
} else {
uni.navigateTo({
url: `/pages/device/detail?deviceNo=${deviceNo}`
})
}
} catch (error) {
console.error('扫码处理失败:', error)
if (error && error.errMsg !== 'scanCode:fail cancel') {
uni.showToast({
title: t('home.scanFailed'),
icon: 'none'
})
}
}
}
// 去购买
const goToBuy = () => {
uni.navigateTo({
url: '/pages/purchase/index'
})
}
onMounted(() => {
uni.setNavigationBarTitle({
title: t('user.myCards')
})
getCardList()
})
</script>
<style lang="scss" scoped>
.my-card-page {
min-height: 100vh;
background-color: #f5f5f5;
padding: 24rpx;
}
.card-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.card-item {
// background-color: #ffffff;
border-radius: 25rpx;
padding: 32rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
position: relative;
z-index: 5;
}
// 卡片头部
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16rpx;
}
.card-name {
font-size: 36rpx;
font-weight: 600;
color: #333333;
line-height: 50rpx;
}
.card-date {
.date-text {
font-size: 24rpx;
color: #999999;
line-height: 34rpx;
&.expired {
color: #999999;
}
}
}
// 地区信息
.card-region {
margin-bottom: 24rpx;
align-items: center;
display: flex;
justify-content: space-between;
gap: 16rpx;
.region-text {
font-size: 26rpx;
color: #666666;
line-height: 36rpx;
}
}
// 卡片底部
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
padding-top: 50rpx;
position: absolute;
background: rgba(255, 244, 227, 1);
z-index: 1;
right: 0;
bottom: 0;
left: 0;
border-radius: 0 0 25rpx 25rpx;
/* text-align: 30rpx ; */
}
.card-usage-info {
flex: 1;
.usage-text {
font-size: 26rpx;
color: #D4A574;
font-weight: 500;
line-height: 36rpx;
}
}
.card-actions {
display: flex;
align-items: center;
gap: 16rpx;
}
// 续卡按钮
.renew-btn {
display: flex;
align-items: center;
gap: 4rpx;
padding: 8rpx 16rpx;
// background-color: #FFF9F0;
border-radius: 8rpx;
.renew-text {
font-size: 24rpx;
color: #D4A574;
line-height: 34rpx;
}
.arrow {
font-size: 24rpx;
color: #D4A574;
}
}
// 状态标签
.status-tag {
padding: 8rpx 20rpx;
border-radius: 8rpx;
.status-text {
font-size: 24rpx;
line-height: 34rpx;
}
&.active {
// background-color: #FFF9F0;
.status-text {
color: #D4A574;
}
}
&.expired {
// background-color: #F5F5F5;
.status-text {
color: #999999;
}
}
&.used {
// background-color: #F5F5F5;
.status-text {
color: #999999;
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 40rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 40rpx;
}
.buy-btn {
padding: 20rpx 60rpx;
background-color: #B8741A;
border-radius: 48rpx;
.buy-text {
font-size: 28rpx;
color: #ffffff;
font-weight: 500;
}
}
}
</style>
+542
View File
@@ -0,0 +1,542 @@
<template>
<view class="my-coupon-page">
<!-- Tab 切换 -->
<!-- <view class="tab-container">
<view class="tab-item" :class="{ active: currentTab === 'available' }" @click="switchTab('available')">
<text class="tab-text">{{ $t('myCoupon.available') }}</text>
</view>
<view class="tab-item" :class="{ active: currentTab === 'used' }" @click="switchTab('used')">
<text class="tab-text">{{ $t('myCoupon.used') }}</text>
</view>
<view class="tab-item" :class="{ active: currentTab === 'expired' }" @click="switchTab('expired')">
<text class="tab-text">{{ $t('myCoupon.expired') }}</text>
</view>
</view> -->
<!-- 优惠券列表 -->
<view class="coupon-list" v-if="filteredCoupons.length > 0">
<view v-for="coupon in filteredCoupons" :key="coupon.id" class="coupon-item-wrapper">
<view class="coupon-item" :class="getCouponClass(coupon.status)">
<!-- 虚线上下圆形缺口 -->
<view class="coupon-circle-top"></view>
<view class="coupon-circle-bottom"></view>
<view class="coupon-left">
<view class="coupon-value">
<text v-if="coupon.type === 'cash'" class="coupon-unit">¥</text>
<text class="coupon-amount">{{ coupon.type === 'discount' ? coupon.discount : coupon.value }}</text>
<text v-if="coupon.type === 'discount'" class="coupon-unit"></text>
</view>
<view style="display: flex;flex-direction: column;">
<text class="coupon-condition">{{ coupon.condition }}</text>
<text class="coupon-validity-left">{{ coupon.validity }}</text>
</view>
</view>
<view class="coupon-divider"></view>
<view class="coupon-right">
<!-- <text class="coupon-name">{{ coupon.name }}</text> -->
<!-- <text class="coupon-region" v-if="coupon.positionName"
style="font-size: 22rpx; color: #999; margin-top: 4rpx;">
{{ $t('myCoupon.onlyForRegionBefore') }}{{ coupon.positionName }}{{ $t('myCoupon.onlyForRegionAfter') }}
</text> -->
<view class="use-btn" v-if="coupon.status == 'unused'" @click="useCoupon(coupon)">
<text class="use-text">{{ $t('myCoupon.useNow') }}</text>
</view>
<text class="coupon-status" v-else>{{ getStatusText(coupon.status) }}</text>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else>
<image class="empty-icon" src="/static/empty-coupon.png" mode="aspectFit"></image>
<text class="empty-text">{{ getEmptyText() }}</text>
<view class="buy-btn" @click="goToBuy" v-if="currentTab === 'available'">
<text class="buy-text">{{ $t('myCoupon.buyNow') }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useI18n } from '@/utils/i18n.js'
import { getUserCoupons } from '@/config/api/coupon.js'
import { getQueryString } from '@/util/index.js'
import { getDeviceInfo } from '@/config/api/device.js'
import { getInUseOrder, getUnpaidOrder } from '@/config/api/order.js'
const { t } = useI18n()
// 当前选中的 Tab
const currentTab = ref('available')
// // Tab 与 API 状态的映射
// const tabToStatusMap = {
// available: 'unused',
// used: 'used',
// expired: 'expired'
// }
// 优惠券列表
const couponList = ref([])
// 过滤后的优惠券
const filteredCoupons = computed(() => {
return couponList.value;
})
// 获取优惠券列表
const getCouponList = async () => {
try {
// const apiStatus = tabToStatusMap[currentTab.value]
const res = await getUserCoupons('')
if (res.code === 200 && res.data) {
// 将后端数据转换为前端需要的格式
couponList.value = (res.data || []).map(item => {
// 判断优惠券类型:discount_coupon 折扣券,deduction_coupon 抵扣券
const isCashCoupon = item.couponType === 'deduction_coupon'
// 格式化使用条件
let condition = '无门槛'
if (item.usableCondition && item.usableCondition > 0) {
condition = `${item.usableCondition}可用`
}
// 格式化有效期
let validity = ''
if (currentTab.value === 'used') {
// 已使用显示使用时间
validity = item.couponStartTime ? `使用时间 ${item.couponStartTime.split(' ')[0]}` : ''
} else if (item.couponEndTime) {
validity = `${item.couponEndTime.split(' ')[0]} 过期`
}
console.log(item.status);
return {
id: item.id,
name: item.couponName || '优惠券',
type: isCashCoupon ? 'cash' : 'discount',
value: item.deductAmount ? parseFloat(item.deductAmount) : 0,
discount: item.discountRate ? parseFloat(item.discountRate) * 10 : null,
condition: condition,
validity: validity,
status: item.status,
positionName: item.positionName
}
})
} else {
couponList.value = []
}
} catch (error) {
console.error('获取优惠券列表失败:', error)
couponList.value = []
uni.showToast({
title: t('myCoupon.getListFailed'),
icon: 'none'
})
}
}
// 切换 Tab
const switchTab = (tab) => {
currentTab.value = tab
getCouponList()
}
// 获取优惠券样式类名
const getCouponClass = (status) => {
return status === 'unused' ? '' : 'disabled'
}
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
'used': t('myCoupon.usedStatus'),
'expired': t('myCoupon.expiredStatus'),
'refunded':t('myCoupon.refundedStatus')
}
console.log("获取状态文本:"+statusMap[status]);
return statusMap[status] || ''
}
// 获取空状态文本
const getEmptyText = () => {
const textMap = {
'available': t('myCoupon.noAvailableCoupons'),
'used': t('myCoupon.noUsedCoupons'),
'expired': t('myCoupon.noExpiredCoupons')
}
return textMap[currentTab.value] || ''
}
// 使用优惠券
const useCoupon = async (coupon) => {
try {
const scanResult = await new Promise((resolve, reject) => {
uni.scanCode({
success: resolve,
fail: reject
})
})
console.log('扫码结果:', scanResult);
let deviceNo;
// 兼容不同平台的扫码结果
if (scanResult.scanType === 'QR_CODE' || scanResult.scanType === 'qrCode') {
deviceNo = getQueryString(scanResult.result, 'deviceNo')
} else if (scanResult.path) {
deviceNo = getQueryString(scanResult.path, 'deviceNo')
} else {
deviceNo = scanResult.result
}
if (!deviceNo) {
uni.showToast({
title: t('home.invalidQRCode'),
icon: 'none'
})
return
}
uni.showLoading({
title: t('common.getting')
})
// 检查是否有使用中的订单
const inUseRes = await getInUseOrder()
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
uni.hideLoading()
const inUseOrder = inUseRes.data
uni.reLaunch({
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
})
return
}
// 检查是否有待支付订单
const orderRes = await getUnpaidOrder()
if (orderRes && orderRes.code === 200 && orderRes.data) {
uni.hideLoading()
const unpaidOrder = orderRes.data
uni.navigateTo({
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}`
})
return
}
// 获取设备信息并跳转详情
const deviceInfoRes = await getDeviceInfo(deviceNo)
uni.hideLoading()
if (deviceInfoRes.code === 200 && deviceInfoRes.data && deviceInfoRes.data.device) {
const deviceInfo = deviceInfoRes.data.device
let url = `/pages/device/detail?deviceNo=${deviceNo}`
if (deviceInfo.feeConfig) {
url += `&feeConfig=${encodeURIComponent(deviceInfo.feeConfig)}`
}
uni.navigateTo({
url
})
} else {
uni.navigateTo({
url: `/pages/device/detail?deviceNo=${deviceNo}`
})
}
} catch (error) {
console.error('扫码处理失败:', error)
if (error && error.errMsg !== 'scanCode:fail cancel') {
uni.showToast({
title: t('home.scanFailed'),
icon: 'none'
})
}
}
}
// 去购买
const goToBuy = () => {
uni.navigateTo({
url: '/pages/purchase/index?tab=coupon'
})
}
onMounted(() => {
uni.setNavigationBarTitle({
title: t('user.myCoupons')
})
getCouponList()
})
</script>
<style lang="scss" scoped>
// 优惠券样式变量封装
$coupon-theme-color: #A16300;
$coupon-divider-color: #B8741A;
$coupon-bg-faded: #f5f5f5;
$coupon-active-bg-start: #FFF4E6;
$coupon-active-bg-end: #FFE8CC;
$coupon-divider-left: 65%;
$coupon-circle-radius: 16rpx;
.my-coupon-page {
min-height: 100vh;
background-color: #f5f5f5;
}
/* Tab 切换 */
.tab-container {
position: sticky;
top: 0;
display: flex;
background-color: #ffffff;
z-index: 999;
padding: 20rpx 0;
}
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
position: relative;
.tab-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
&.active {
.tab-text {
color: #333;
font-weight: 600;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 6rpx;
background-color: #FFA928;
border-radius: 3rpx;
}
}
}
.coupon-list {
padding: 20rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.coupon-item-wrapper {
position: relative;
}
.coupon-item {
background: #FFF4E3;
border-radius: 20rpx;
padding: 40rpx 30rpx;
display: flex;
align-items: stretch;
position: relative;
overflow: visible;
min-height: 180rpx;
box-sizing: border-box;
border: 2rpx solid transparent;
transition: all 0.3s;
&.selected {
border-color: $coupon-divider-color;
box-shadow: 0 4rpx 20rpx rgba(184, 116, 26, 0.2);
.coupon-circle-top,
.coupon-circle-bottom {
background-color: $coupon-active-bg-start;
border: 2rpx solid $coupon-divider-color;
}
}
&.disabled {
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
opacity: 0.6;
.coupon-value,
.coupon-condition,
.coupon-name {
color: #999;
}
.coupon-circle-top,
.coupon-circle-bottom {
background-color: #f5f5f5;
}
}
}
/* 虚线顶部圆形缺口 */
.coupon-circle-top {
position: absolute;
left: $coupon-divider-left+4%;
top: -$coupon-circle-radius;
transform: translateX(-50%);
width: $coupon-circle-radius * 2;
height: $coupon-circle-radius * 2;
border-radius: 50%;
background-color: $coupon-bg-faded;
z-index: 10;
}
/* 虚线底部圆形缺口 */
.coupon-circle-bottom {
position: absolute;
left: $coupon-divider-left+4%;
bottom: -$coupon-circle-radius;
transform: translateX(-50%);
width: $coupon-circle-radius * 2;
height: $coupon-circle-radius * 2;
border-radius: 50%;
background-color: $coupon-bg-faded;
z-index: 10;
}
.coupon-left {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
gap: 8rpx;
}
.coupon-value {
display: flex;
align-items: flex-end; // 单位在脚
color: $coupon-theme-color;
line-height: 1;
width:120rpx;
}
.coupon-amount {
font-size: 56rpx; // 值要大
font-weight: 700;
}
.coupon-unit {
font-size: 24rpx; // 单位小
font-weight: 500;
margin-bottom: 6rpx; // 微调单位垂直位置,使其更贴合“脚”
margin-left: 4rpx;
margin-right: 4rpx;
}
.coupon-condition {
font-size: 24rpx;
color: #000;
font-weight: 600;
}
.coupon-validity-left {
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
}
.coupon-divider {
width: 2rpx;
height: 100%;
position: absolute;
left: $coupon-divider-left;
transform: translateX(-50%);
top: 0;
bottom: 0;
align-self: stretch;
background: repeating-linear-gradient(to bottom,
$coupon-divider-color 0rpx,
$coupon-divider-color 8rpx,
transparent 8rpx,
transparent 16rpx);
margin: 0 30rpx;
}
.coupon-right {
// flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
align-items: center;
margin: auto 0;
width: 160rpx;
// align-content: center;
// transform: translateY(50%);
}
.coupon-name {
font-size: 28rpx;
font-weight: 600;
color: #333;
}
.coupon-validity {
font-size: 22rpx;
color: #999;
}
.use-btn {
// margin-top: 10rpx;
// padding: 12rpx 28rpx;
// background-color: #B8741A;
// border-radius: 40rpx;
.use-text {
font-size: 28rpx;
color: #A16300;
font-weight: 600;
}
}
.coupon-status {
margin-top: 10rpx;
font-size: 24rpx;
color: #999;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 40rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 40rpx;
}
.buy-btn {
padding: 20rpx 60rpx;
background-color: #B8741A;
border-radius: 48rpx;
.buy-text {
font-size: 28rpx;
color: #ffffff;
font-weight: 500;
}
}
}
</style>
@@ -29,8 +29,8 @@
<swiper class="banner-swiper" :indicator-dots="bannerImages.length > 1" <swiper class="banner-swiper" :indicator-dots="bannerImages.length > 1"
:autoplay="bannerImages.length > 1" :circular="true" :interval="3000"> :autoplay="bannerImages.length > 1" :circular="true" :interval="3000">
<swiper-item v-for="(image, index) in bannerImages" :key="index"> <swiper-item v-for="(image, index) in bannerImages" :key="index">
<image class="banner-image" :src="image" mode="aspectFill" <image class="banner-image" :src="image" mode="aspectFill" @click="handleBannerClick(index)">
@click="handleBannerClick(index)"></image> </image>
</swiper-item> </swiper-item>
</swiper> </swiper>
</view> </view>
@@ -43,7 +43,8 @@
<view class="list-item" @click="handleQuickReturn"> <view class="list-item" @click="handleQuickReturn">
<view class="left"> <view class="left">
<image class="icon" src="/static/express_return.png" mode="aspectFit"></image> <image class="icon" src="/static/express_return.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.quickReturn') }}<text style="font-size: 18rpx;">{{ $t('user.quickReturnDesc') }}</text></text> <text class="title">{{ $t('user.quickReturn') }}<text
style="font-size: 18rpx;">{{ $t('user.quickReturnDesc') }}</text></text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
@@ -54,35 +55,35 @@
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="list-item" @click="navigateTo('/pages/order/index')"> <view class="list-item" @click="navigateTo('/subPackages/order/index')">
<view class="left"> <view class="left">
<image class="icon" src="/static/orderList.png" mode="aspectFit"></image> <image class="icon" src="/static/orderList.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.myOrders') }}</text> <text class="title">{{ $t('user.myOrders') }}</text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="list-item" @click="navigateTo('/pages/my/card')"> <view class="list-item" @click="navigateTo('/subPackages/user/my/card')">
<view class="left"> <view class="left">
<image class="icon" src="/static/my_member.png" mode="aspectFit"></image> <image class="icon" src="/static/my_member.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.myCards') }}</text> <text class="title">{{ $t('user.myCards') }}</text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="list-item" @click="navigateTo('/pages/my/coupon')"> <view class="list-item" @click="navigateTo('/subPackages/user/my/coupon')">
<view class="left"> <view class="left">
<image class="icon" src="/static/my_coupon.png" mode="aspectFit"></image> <image class="icon" src="/static/my_coupon.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.myCoupons') }}</text> <text class="title">{{ $t('user.myCoupons') }}</text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="list-item" @click="navigateTo('/pages/help/index')"> <view class="list-item" @click="navigateTo('/subPackages/service/help/index')">
<view class="left"> <view class="left">
<image class="icon" src="/static/customer-service.png" mode="aspectFit"></image> <image class="icon" src="/static/customer-service.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.customerService') }}</text> <text class="title">{{ $t('user.customerService') }}</text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="list-item" @click="navigateTo('/pages/feedback/index')"> <view class="list-item" @click="navigateTo('/subPackages/service/feedback/index')">
<view class="left"> <view class="left">
<image class="icon" src="/static/suggess.png" mode="aspectFit"></image> <image class="icon" src="/static/suggess.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.feedback') }}</text> <text class="title">{{ $t('user.feedback') }}</text>
@@ -96,14 +97,14 @@
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> --> </view> -->
<view class="list-item" @click="navigateTo('/pages/join/index')"> <view class="list-item" @click="navigateTo('/subPackages/other/join/index')">
<view class="left"> <view class="left">
<image class="icon" src="/static/peopleInWork.png" mode="aspectFit"></image> <image class="icon" src="/static/peopleInWork.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.cooperation') }}</text> <text class="title">{{ $t('user.cooperation') }}</text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="list-item" @click="navigateTo('/pages/setting/index')"> <view class="list-item" @click="navigateTo('/subPackages/user/setting/index')">
<view class="left"> <view class="left">
<image class="icon" src="/static/setting.png" mode="aspectFit"></image> <image class="icon" src="/static/setting.png" mode="aspectFit"></image>
<text class="title">{{ $t('user.settings') }}</text> <text class="title">{{ $t('user.settings') }}</text>
@@ -115,9 +116,9 @@
<view class="footer-agreements"> <view class="footer-agreements">
<view class="link-box"> <view class="link-box">
<text class="link" @click="navigateTo('/pages/legal/agreement')">{{ $t('user.userAgreement') }}</text> <text class="link" @click="navigateTo('/subPackages/other/legal/agreement')">{{ $t('user.userAgreement') }}</text>
<text class="sep"></text> <text class="sep"></text>
<text class="link" @click="navigateTo('/pages/legal/privacy')">{{ $t('user.privacyPolicy') }}</text> <text class="link" @click="navigateTo('/subPackages/other/legal/privacy')">{{ $t('user.privacyPolicy') }}</text>
</view> </view>
<view class="version">{{ $t('user.version') }}{{ appVersion }}</view> <view class="version">{{ $t('user.version') }}{{ appVersion }}</view>
</view> </view>
@@ -151,16 +152,24 @@
import { import {
wxLogin, wxLogin,
getUserInfo getUserInfo
} from '../../util/index.js'; } from '@/util/index.js';
import { import {
uploadUserAvatar uploadUserAvatar
} from '../../config/api/user.js' } from '@/config/api/user.js'
import { getCurrentAdvertisement } from '@/config/api/system.js' import {
import { getInUseOrder } from '@/config/api/order.js' getCurrentAdvertisement
import { useI18n } from '@/utils/i18n.js' } from '@/config/api/system.js'
import {
getInUseOrder
} from '@/config/api/order.js'
import {
useI18n
} from '@/utils/i18n.js'
// 退 // 退
const { t } = useI18n() const {
t
} = useI18n()
// //
const userInfo = ref({}); const userInfo = ref({});
@@ -180,7 +189,8 @@ import {
// 广 // 广
const res = await getCurrentAdvertisement({ const res = await getCurrentAdvertisement({
appPlatform: 'wechat', // appPlatform: 'wechat', //
appType: 'user' // appType: 'user', //
pictureLocation:'userProfile_banner'
}) })
if (res && res.code === 200 && res.data) { if (res && res.code === 200 && res.data) {
@@ -214,8 +224,7 @@ import {
uni.navigateToMiniProgram({ uni.navigateToMiniProgram({
appId: config.appId, appId: config.appId,
path: config.linkUrl || '', path: config.linkUrl || '',
success: () => { success: () => {},
},
fail: (err) => { fail: (err) => {
console.error('跳转小程序失败:', err) console.error('跳转小程序失败:', err)
uni.showToast({ uni.showToast({
@@ -234,7 +243,7 @@ import {
} else if (config.linkType === 'external' && config.linkUrl) { } else if (config.linkType === 'external' && config.linkUrl) {
// H5 // H5
uni.navigateTo({ uni.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(config.linkUrl)}` url: `subPackages/other/webview/index?url=${encodeURIComponent(config.linkUrl)}`
}) })
} else if (config.linkType === 'internal' && config.linkUrl) { } else if (config.linkType === 'internal' && config.linkUrl) {
// //
@@ -10,15 +10,15 @@
</view> </view>
</view> </view>
<view class="group"> <view class="group">
<view class="item" @click="navigateTo('/pages/legal/agreement')"> <view class="item" @click="navigateTo('/subPackages/other/legal/agreement')">
<text class="label">{{ $t('user.userAgreement') }}</text> <text class="label">{{ $t('user.userAgreement') }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="item" @click="navigateTo('/pages/legal/privacy')"> <view class="item" @click="navigateTo('/subPackages/other/legal/privacy')">
<text class="label">{{ $t('user.privacyPolicy') }}</text> <text class="label">{{ $t('user.privacyPolicy') }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
<view class="item" @click="navigateTo('/pages/legal/terms')"> <view class="item" @click="navigateTo('/subPackages/other/legal/terms')">
<text class="label">{{ $t('legal.termsAndConditions') }}</text> <text class="label">{{ $t('legal.termsAndConditions') }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> </view>
@@ -123,7 +123,7 @@ const handleLogout = async () => {
setTimeout(() => { setTimeout(() => {
uni.removeStorageSync('token') uni.removeStorageSync('token')
uni.removeStorageSync('userInfo') uni.removeStorageSync('userInfo')
uni.reLaunch({ url: '/pages/login/index' }) uni.reLaunch({ url: '/subPackages/user/login/index' })
}, 1200) }, 1200)
} }
} }
@@ -45,7 +45,7 @@
</template> </template>
<script> <script>
import { getUserInfo } from '@/api/user' import { getUserInfo } from '@/util/index.js'
import { URL } from '@/config/url' import { URL } from '@/config/url'
export default { export default {
@@ -56,7 +56,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import { getMyIndexInfo, uploadUserAvatar, updateUserInfo } from '../../config/api/user.js'; import { getMyIndexInfo, uploadUserAvatar, updateUserInfo } from '@/config/api/user.js';
import { useI18n } from '@/utils/i18n.js' import { useI18n } from '@/utils/i18n.js'
const { t } = useI18n() const { t } = useI18n()