diff --git a/App.vue b/App.vue index 7609ea9..e58073c 100644 --- a/App.vue +++ b/App.vue @@ -102,38 +102,31 @@ }, 100) } }, - // 启动轮询支付状态 + // 启动轮询支付状态:回到小程序立马查一次,失败则每隔 3 秒再查,最多再查 3 次(共 4 次) startOrderStatusPolling(orderNumber) { - // 避免重复轮询 if (this._orderStatusTimer) { clearInterval(this._orderStatusTimer); this._orderStatusTimer = null; } let times = 0; - const maxTimes = 5; // 最多轮询 20 次,大约 1 分钟 + const maxTimes = 4; // 立即 1 次 + 最多再查 3 次 - this._orderStatusTimer = setInterval(async () => { + const doCheck = async () => { times++; try { const res = await updateLuOrderPayStatus({ orderNumber }); - // 约定:接口有返回数据即认为支付成功 if (res) { clearInterval(this._orderStatusTimer); this._orderStatusTimer = null; - // 成功后清理本地缓存的订单号 uni.removeStorageSync("lastOrderNumber"); - - uni.showToast({ - title: '支付成功', - icon: 'success' - }); - - // 跳转到服务记录页面,默认展示「待核销」tab + uni.showToast({ title: '支付成功', icon: 'success' }); uni.navigateTo({ url: '/pages/profileSub/serviceRecords?tab=pending_verification' }); - } else if (times >= maxTimes) { + return; + } + if (times >= maxTimes) { clearInterval(this._orderStatusTimer); this._orderStatusTimer = null; uni.showToast({ @@ -149,7 +142,10 @@ this._orderStatusTimer = null; } } - }, 3000); + }; + + doCheck(); // 回到小程序立马查一次 + this._orderStatusTimer = setInterval(doCheck, 3000); // 失败则每 3 秒再查,最多再查 3 次 } } } diff --git a/api/index.js b/api/index.js index 72dee5f..6dbefdf 100644 --- a/api/index.js +++ b/api/index.js @@ -4,8 +4,8 @@ */ // 基础URL配置(注意:末尾不要加斜杠) -// const BASE_URL = 'https://guangsh.manage.hschengtai.com' -const BASE_URL = 'http://192.168.5.134:48085' +const BASE_URL = 'https://guangsh.manage.hschengtai.com' +// const BASE_URL = 'http://192.168.5.134:48085' // 是否正在刷新token(防止并发刷新) let isRefreshing = false // 等待刷新完成的请求队列 @@ -366,12 +366,15 @@ export function request(options = {}) { uni.hideLoading() } - // 网络错误处理 + // 网络错误处理(真机/预览时域名未配置会报 domain 相关错误) let errorMsg = '网络请求失败' if (err.errMsg) { - if (err.errMsg.includes('timeout')) { + const em = err.errMsg + if (em.includes('timeout')) { errorMsg = '请求超时,请检查网络' - } else if (err.errMsg.includes('fail')) { + } else if (em.includes('domain') || em.includes('url not in')) { + errorMsg = '请在小程序后台配置服务器域名' + } else if (em.includes('fail')) { errorMsg = '网络连接失败,请检查网络设置' } } diff --git a/api/service.js b/api/service.js index 5175dae..eafd498 100644 --- a/api/service.js +++ b/api/service.js @@ -161,11 +161,16 @@ export function getMyOrderPage(params = {}){ }) } -// 删除订单 -export function deleteOrder(id){ +// 删除订单(id 为 Query 参数,拼在 URL 上) +export function deleteOrder(params = {}) { + const id = params.id + const url = id !== undefined && id !== '' + ? `/app-api/member/lu-order/delete?id=${encodeURIComponent(id)}` + : '/app-api/member/lu-order/delete' return request({ - url: '/app-api/member/lu-order/delete?id='+id, + url, method: 'DELETE', + data: {}, }) } diff --git a/pages/detail/serviceDetail.vue b/pages/detail/serviceDetail.vue index d8fac4a..5da61c3 100644 --- a/pages/detail/serviceDetail.vue +++ b/pages/detail/serviceDetail.vue @@ -61,7 +61,7 @@ {{ item.discount }}折 - {{ item.description }} + 现价¥ diff --git a/pages/profile/profile.vue b/pages/profile/profile.vue index d8fa8c9..1746854 100644 --- a/pages/profile/profile.vue +++ b/pages/profile/profile.vue @@ -7,11 +7,7 @@ - + @@ -128,6 +136,22 @@ export default { uploading: false, // 是否正在上传头像 }; }, + computed: { + avatarSrc() { + const v = this.userInfo.avatar; + return (v && typeof v === "string") ? v : "/static/tabbar/profile.png"; + }, + memberLevelIcon() { + const icon = this.userInfo.level && this.userInfo.level.icon; + return (icon && typeof icon === "string") ? icon : "/static/tabbar/profile.png"; + }, + userCardBgImage() { + const url = this.userInfo.level && this.userInfo.level.backgroundUrl; + return (url && typeof url === "string") + ? `url(${url})` + : "url(/static/profile/member-card-bg.png)"; + }, + }, onLoad() { this.getSystemInfo(); this.updateTime(); @@ -264,12 +288,10 @@ export default { // 选择头像(微信小程序使用 chooseAvatar,其他平台使用 chooseImage) handleChooseAvatar(e) { // #ifdef MP-WEIXIN - // 微信小程序新版本使用 chooseAvatar if (e.detail && e.detail.avatarUrl) { - const avatarUrl = e.detail.avatarUrl; - // 先显示本地预览 + const avatarUrl = typeof e.detail.avatarUrl === "string" ? e.detail.avatarUrl : String(e.detail.avatarUrl || ""); + if (!avatarUrl) return; this.userInfo.avatar = avatarUrl; - // 上传头像 this.uploadAvatar(avatarUrl); } // #endif @@ -282,22 +304,43 @@ export default { sourceType: ["album", "camera"], success: (res) => { const tempFilePath = res.tempFilePaths[0]; - // 先显示本地预览 this.userInfo.avatar = tempFilePath; - // 上传头像 this.uploadAvatar(tempFilePath); }, fail: (err) => { console.error("选择图片失败:", err); - uni.showToast({ - title: "选择图片失败", - icon: "none", - }); + uni.showToast({ title: "选择图片失败", icon: "none" }); }, }); // #endif }, + // 微信内:昵称输入框失焦时保存昵称(input type="nickname" 会弹出微信昵称选项) + handleNicknameBlur(e) { + const nickname = (e.detail && e.detail.value) ? String(e.detail.value).trim() : ""; + if (nickname) { + this.userInfo.nickname = nickname; + uni.setStorageSync("userInfo", this.userInfo); + this.updateUserNickname(nickname); + uni.showToast({ title: "昵称已更新", icon: "success" }); + } + }, + // 非微信环境点击昵称提示 + handleNicknameClickNonWeixin() { + uni.showToast({ + title: "请在微信中打开以修改昵称", + icon: "none", + }); + }, + // 同步昵称到后端(若后端有更新接口可在此调用) + async updateUserNickname(nickname) { + try { + // 若有更新用户信息接口可在此调用,例如:await updateUserInfo({ nickname }); + } catch (err) { + console.error("更新昵称失败:", err); + } + }, + // 上传头像 uploadAvatar(filePath) { if (this.uploading) { @@ -365,11 +408,10 @@ export default { fail: (err) => { uni.hideLoading(); console.error("上传头像失败:", err); - uni.showToast({ - title: "上传失败,请检查网络", - icon: "none", - }); - // 上传失败,恢复原头像 + const msg = (err && err.errMsg && err.errMsg.indexOf("ENOENT") !== -1) + ? "模拟器暂不支持更换头像,请使用真机预览" + : "上传失败,请检查网络"; + uni.showToast({ title: msg, icon: "none", duration: 2500 }); this.loadUserInfo(); }, complete: () => { @@ -501,6 +543,19 @@ export default { color: #333333; } + .nickname-input { + font-family: PingFang-SC, PingFang-SC; + font-weight: bold; + font-size: 32rpx; + color: #333333; + padding: 0; + margin: 0; + height: 44rpx; + line-height: 44rpx; + background: transparent; + width: 100%; + } + .user-id { font-family: PingFang-SC, PingFang-SC; font-weight: 500; diff --git a/pages/profileSub/serviceRecords.vue b/pages/profileSub/serviceRecords.vue index 68af6fa..a9306b0 100644 --- a/pages/profileSub/serviceRecords.vue +++ b/pages/profileSub/serviceRecords.vue @@ -90,20 +90,37 @@ mode="aspectFill" > - {{ goods.couponName || goods.name }} + {{ + goods.couponName || goods.name + }} - ¥{{ formatFen(goods.salePrice) }} + ¥{{ formatFen(goods.salePrice) }} ×{{ goods.num || goods.count || goods.quantity || goods.qty }} + + + @@ -111,7 +128,9 @@ 实付款: - ¥{{ formatFen(item.payableAmount) }} + ¥{{ formatFen(item.payableAmount) }} @@ -151,7 +170,7 @@ @@ -166,12 +185,44 @@ 上拉加载更多 + + + + + + + 核销码 + + {{ getQrUseStatusText(qrModalData.useStatus) }} + + × + + + + + + + 暂无券码 + + + @@ -717,6 +834,16 @@ export default { } } } + + .qr-code-icon-wrapper { + flex-shrink: 0; + padding: 12rpx; + margin-left: 8rpx; + } + .qr-code-icon { + width: 44rpx; + height: 44rpx; + } } } @@ -787,4 +914,122 @@ export default { } } } + +/* 券码二维码弹窗 */ +.qr-modal-mask { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 900; +} +.qr-modal-wrap { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 901; + display: flex; + align-items: center; + justify-content: center; + padding: 36rpx; + box-sizing: border-box; +} +.qr-modal { + width: 100%; + max-width: 720rpx; + background: #ffffff; + border-radius: 28rpx; + overflow: hidden; + box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.15); +} +.qr-modal-header { + position: relative; + padding: 40rpx 32rpx 28rpx; + border-bottom: 1rpx solid #f0f0f0; + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 16rpx; +} +.qr-modal-title { + font-family: PingFang-SC, PingFang-SC; + font-weight: 600; + font-size: 36rpx; + color: #1a1819; +} +.qr-modal-status { + padding: 8rpx 20rpx; + border-radius: 24rpx; + font-family: PingFang-SC, PingFang-SC; + font-weight: 500; + font-size: 24rpx; + &.status-unused { + background: rgba(0, 66, 148, 0.1); + color: #004294; + } + &.status-used { + background: rgba(158, 158, 158, 0.15); + color: #9e9e9e; + } + &.status-refund { + background: rgba(76, 175, 80, 0.1); + color: #4caf50; + } + &.status-cancelled { + background: rgba(158, 158, 158, 0.15); + color: #9e9e9e; + } +} +.qr-modal-close { + position: absolute; + right: 20rpx; + top: 50%; + transform: translateY(-50%); + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 44rpx; + color: #999999; + line-height: 1; +} +.qr-modal-body { + padding: 48rpx 32rpx 56rpx; + display: flex; + flex-direction: column; + align-items: center; +} +.qr-code-box { + display: flex; + flex-direction: column; + align-items: center; +} +.qr-code-image { + width: 520rpx; + height: 520rpx; + background: #fff; + border: 2rpx solid #e8e8e8; + border-radius: 20rpx; + margin-bottom: 28rpx; +} +.qr-code-text { + font-family: PingFang-SC, PingFang-SC; + font-weight: 500; + font-size: 26rpx; + color: #333333; + word-break: break-all; + text-align: center; + padding: 0 20rpx; +} +.qr-code-empty { + font-family: PingFang-SC, PingFang-SC; + font-size: 28rpx; + color: #999999; +} diff --git a/static/home/qr_code_icon.png b/static/home/qr_code_icon.png new file mode 100644 index 0000000..0fa839c Binary files /dev/null and b/static/home/qr_code_icon.png differ