Compare commits

...

2 Commits

Author SHA1 Message Date
格调main 075054a6c1 删除跳转获取的信息弹出提示 2026-03-06 10:00:27 +08:00
wk bf7cb7ec01 feat: 订单加二维码icon和弹窗 2026-03-05 19:04:23 +08:00
7 changed files with 407 additions and 103 deletions

112
App.vue
View File

@ -19,52 +19,52 @@
console.log(options) console.log(options)
// 2. scene '1038' // 2. scene '1038'
if (options.scene === 1038 && // if (options.scene === 1038 &&
options.referrerInfo?.appId === 'wxef277996acc166c3') { // options.referrerInfo?.appId === 'wxef277996acc166c3') {
// // //
const extraData = options.referrerInfo.extraData; // const extraData = options.referrerInfo.extraData;
console.log("extraData",extraData) // console.log("extraData",extraData)
if (!extraData) { // if (!extraData) {
uni.showToast({ // uni.showToast({
title: '当前通过物理按键返回,未接收到返参,建议自行查询交易结果', // title: '',
icon: 'none', // icon: 'none',
duration: 3000 // duration: 3000
}); // });
} else { // } else {
if (extraData.code === 'success') { // if (extraData.code === 'success') {
// // //
// 1. 使 // // 1. 使
const orderNumberFromExtra = // const orderNumberFromExtra =
extraData.orderNumber || extraData.reqsn || extraData.orderNo; // extraData.orderNumber || extraData.reqsn || extraData.orderNo;
// 2. 使 // // 2. 使
const storedOrderNumber = uni.getStorageSync("lastOrderNumber"); // const storedOrderNumber = uni.getStorageSync("lastOrderNumber");
const orderNumber = orderNumberFromExtra || storedOrderNumber; // const orderNumber = orderNumberFromExtra || storedOrderNumber;
if (orderNumber) { // if (orderNumber) {
this.startOrderStatusPolling(orderNumber); // this.startOrderStatusPolling(orderNumber);
} else { // } else {
uni.showToast({ // uni.showToast({
title: '支付返回缺少订单号,请稍后在服务记录中查看', // title: '',
icon: 'none', // icon: 'none',
duration: 3000 // duration: 3000
}); // });
} // }
} else if (extraData.code === 'cancel') { // } else if (extraData.code === 'cancel') {
uni.showToast({ // uni.showToast({
title: '支付已取消', // title: '',
icon: 'none' // icon: 'none'
}); // });
} else { // } else {
uni.showToast({ // uni.showToast({
title: `支付失败:${extraData.errmsg || '未知错误'}`, // title: `${extraData.errmsg || ''}`,
icon: 'none', // icon: 'none',
duration: 3000 // duration: 3000
}); // });
} // }
} // }
} // }
}, },
onHide: function() { onHide: function() {
console.log('App Hide') console.log('App Hide')
@ -102,38 +102,31 @@
}, 100) }, 100)
} }
}, },
// // 3 3 4
startOrderStatusPolling(orderNumber) { startOrderStatusPolling(orderNumber) {
//
if (this._orderStatusTimer) { if (this._orderStatusTimer) {
clearInterval(this._orderStatusTimer); clearInterval(this._orderStatusTimer);
this._orderStatusTimer = null; this._orderStatusTimer = null;
} }
let times = 0; let times = 0;
const maxTimes = 5; // 20 1 const maxTimes = 4; // 1 + 3
this._orderStatusTimer = setInterval(async () => { const doCheck = async () => {
times++; times++;
try { try {
const res = await updateLuOrderPayStatus({ orderNumber }); const res = await updateLuOrderPayStatus({ orderNumber });
//
if (res) { if (res) {
clearInterval(this._orderStatusTimer); clearInterval(this._orderStatusTimer);
this._orderStatusTimer = null; this._orderStatusTimer = null;
//
uni.removeStorageSync("lastOrderNumber"); uni.removeStorageSync("lastOrderNumber");
uni.showToast({ title: '支付成功', icon: 'success' });
uni.showToast({
title: '支付成功',
icon: 'success'
});
// tab
uni.navigateTo({ uni.navigateTo({
url: '/pages/profileSub/serviceRecords?tab=pending_verification' url: '/pages/profileSub/serviceRecords?tab=pending_verification'
}); });
} else if (times >= maxTimes) { return;
}
if (times >= maxTimes) {
clearInterval(this._orderStatusTimer); clearInterval(this._orderStatusTimer);
this._orderStatusTimer = null; this._orderStatusTimer = null;
uni.showToast({ uni.showToast({
@ -149,7 +142,10 @@
this._orderStatusTimer = null; this._orderStatusTimer = null;
} }
} }
}, 3000); };
doCheck(); //
this._orderStatusTimer = setInterval(doCheck, 3000); // 3 3
} }
} }
} }

View File

@ -4,8 +4,8 @@
*/ */
// 基础URL配置注意末尾不要加斜杠 // 基础URL配置注意末尾不要加斜杠
// const BASE_URL = 'https://guangsh.manage.hschengtai.com' const BASE_URL = 'https://guangsh.manage.hschengtai.com'
const BASE_URL = 'http://192.168.5.134:48085' // const BASE_URL = 'http://192.168.5.134:48085'
// 是否正在刷新token防止并发刷新 // 是否正在刷新token防止并发刷新
let isRefreshing = false let isRefreshing = false
// 等待刷新完成的请求队列 // 等待刷新完成的请求队列
@ -366,12 +366,15 @@ export function request(options = {}) {
uni.hideLoading() uni.hideLoading()
} }
// 网络错误处理 // 网络错误处理(真机/预览时域名未配置会报 domain 相关错误)
let errorMsg = '网络请求失败' let errorMsg = '网络请求失败'
if (err.errMsg) { if (err.errMsg) {
if (err.errMsg.includes('timeout')) { const em = err.errMsg
if (em.includes('timeout')) {
errorMsg = '请求超时,请检查网络' errorMsg = '请求超时,请检查网络'
} else if (err.errMsg.includes('fail')) { } else if (em.includes('domain') || em.includes('url not in')) {
errorMsg = '请在小程序后台配置服务器域名'
} else if (em.includes('fail')) {
errorMsg = '网络连接失败,请检查网络设置' errorMsg = '网络连接失败,请检查网络设置'
} }
} }

View File

@ -161,11 +161,16 @@ export function getMyOrderPage(params = {}){
}) })
} }
// 删除订单 // 删除订单id 为 Query 参数,拼在 URL 上)
export function deleteOrder(id){ 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({ return request({
url: '/app-api/member/lu-order/delete?id='+id, url,
method: 'DELETE', method: 'DELETE',
data: {},
}) })
} }

View File

@ -61,7 +61,7 @@
{{ item.discount }} {{ item.discount }}
</view> </view>
</view> </view>
<text class="menu-desc">{{ item.description }}</text> <!-- <text class="menu-desc">{{ item.description }}</text> -->
<view class="menu-price-row"> <view class="menu-price-row">
<view class="current-price"> <view class="current-price">
<view class="price-label">现价¥</view> <view class="price-label">现价¥</view>

View File

@ -7,11 +7,7 @@
<!-- 用户资料卡片 --> <!-- 用户资料卡片 -->
<view <view
class="user-card" class="user-card"
:style="{ :style="{ backgroundImage: userCardBgImage }"
backgroundImage: userInfo.level.backgroundUrl
? `url(${userInfo.level.backgroundUrl})`
: 'url(/static/profile/member-card-bg.png)',
}"
> >
<view class="user-info"> <view class="user-info">
<view class="user-level">{{ userInfo.level.name }}</view> <view class="user-level">{{ userInfo.level.name }}</view>
@ -23,7 +19,7 @@
> >
<image <image
class="avatar" class="avatar"
:src="userInfo.avatar || '/static/tabbar/profile.png'" :src="avatarSrc"
mode="aspectFill" mode="aspectFill"
></image> ></image>
</button> </button>
@ -32,7 +28,7 @@
<view class="avatar-wrapper" @click="handleChooseAvatar"> <view class="avatar-wrapper" @click="handleChooseAvatar">
<image <image
class="avatar" class="avatar"
:src="userInfo.avatar || '/static/tabbar/profile.png'" :src="avatarSrc"
mode="aspectFill" mode="aspectFill"
></image> ></image>
</view> </view>
@ -43,14 +39,26 @@
class="avatar-hg" class="avatar-hg"
></image> ></image>
<view class="user-details"> <view class="user-details">
<text class="login-method">{{ <!-- #ifdef MP-WEIXIN -->
userInfo.nickname || "微信登录" <input
}}</text> type="nickname"
class="nickname-input"
:value="userInfo.nickname || ''"
placeholder="点击设置微信昵称"
@blur="handleNicknameBlur"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<text
class="login-method"
@click="handleNicknameClickNonWeixin"
>{{ userInfo.nickname || "微信登录" }}</text>
<!-- #endif -->
<text class="user-id">NO.{{ userInfo.id }}</text> <text class="user-id">NO.{{ userInfo.id }}</text>
</view> </view>
</view> </view>
<view class="member-benefits-btn" @click="goToMemberBenefits"> <view class="member-benefits-btn" @click="goToMemberBenefits">
<image :src="userInfo.level.icon" mode="aspectFill"></image> <image :src="memberLevelIcon" mode="aspectFill"></image>
</view> </view>
<view class="user-notice"> <view class="user-notice">
<!-- <text class="user-notice-text">{{ noticeNum }}</text> --> <!-- <text class="user-notice-text">{{ noticeNum }}</text> -->
@ -128,6 +136,22 @@ export default {
uploading: false, // 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() { onLoad() {
this.getSystemInfo(); this.getSystemInfo();
this.updateTime(); this.updateTime();
@ -264,12 +288,10 @@ export default {
// 使 chooseAvatar使 chooseImage // 使 chooseAvatar使 chooseImage
handleChooseAvatar(e) { handleChooseAvatar(e) {
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// 使 chooseAvatar
if (e.detail && e.detail.avatarUrl) { 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.userInfo.avatar = avatarUrl;
//
this.uploadAvatar(avatarUrl); this.uploadAvatar(avatarUrl);
} }
// #endif // #endif
@ -282,22 +304,43 @@ export default {
sourceType: ["album", "camera"], sourceType: ["album", "camera"],
success: (res) => { success: (res) => {
const tempFilePath = res.tempFilePaths[0]; const tempFilePath = res.tempFilePaths[0];
//
this.userInfo.avatar = tempFilePath; this.userInfo.avatar = tempFilePath;
//
this.uploadAvatar(tempFilePath); this.uploadAvatar(tempFilePath);
}, },
fail: (err) => { fail: (err) => {
console.error("选择图片失败:", err); console.error("选择图片失败:", err);
uni.showToast({ uni.showToast({ title: "选择图片失败", icon: "none" });
title: "选择图片失败",
icon: "none",
});
}, },
}); });
// #endif // #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) { uploadAvatar(filePath) {
if (this.uploading) { if (this.uploading) {
@ -365,11 +408,10 @@ export default {
fail: (err) => { fail: (err) => {
uni.hideLoading(); uni.hideLoading();
console.error("上传头像失败:", err); console.error("上传头像失败:", err);
uni.showToast({ const msg = (err && err.errMsg && err.errMsg.indexOf("ENOENT") !== -1)
title: "上传失败,请检查网络", ? "模拟器暂不支持更换头像,请使用真机预览"
icon: "none", : "上传失败,请检查网络";
}); uni.showToast({ title: msg, icon: "none", duration: 2500 });
//
this.loadUserInfo(); this.loadUserInfo();
}, },
complete: () => { complete: () => {
@ -501,6 +543,19 @@ export default {
color: #333333; 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 { .user-id {
font-family: PingFang-SC, PingFang-SC; font-family: PingFang-SC, PingFang-SC;
font-weight: 500; font-weight: 500;

View File

@ -90,20 +90,37 @@
mode="aspectFill" mode="aspectFill"
></image> ></image>
<view class="goods-info"> <view class="goods-info">
<text class="goods-title">{{ goods.couponName || goods.name }}</text> <text class="goods-title">{{
goods.couponName || goods.name
}}</text>
<!-- <text class="goods-subtitle"> <!-- <text class="goods-subtitle">
订单号{{ item.orderNumber }} 订单号{{ item.orderNumber }}
</text> --> </text> -->
<view class="goods-meta-row"> <view class="goods-meta-row">
<text class="goods-price">¥{{ formatFen(goods.salePrice) }}</text> <text class="goods-price"
>¥{{ formatFen(goods.salePrice) }}</text
>
<text <text
class="goods-count" class="goods-count"
v-if="(goods.num || goods.count || goods.quantity || goods.qty) > 1" v-if="
(goods.num || goods.count || goods.quantity || goods.qty) >
1
"
> >
×{{ goods.num || goods.count || goods.quantity || goods.qty }} ×{{ goods.num || goods.count || goods.quantity || goods.qty }}
</text> </text>
</view> </view>
</view> </view>
<view
class="qr-code-icon-wrapper"
@click.stop="handleQrCode(item, goods)"
>
<image
src="/static/home/qr_code_icon.png"
mode="aspectFill"
class="qr-code-icon"
></image>
</view>
</view> </view>
</view> </view>
@ -111,7 +128,9 @@
<view class="record-footer"> <view class="record-footer">
<view class="total-info"> <view class="total-info">
<text class="total-label">实付款</text> <text class="total-label">实付款</text>
<text class="total-amount">¥{{ formatFen(item.payableAmount) }}</text> <text class="total-amount"
>¥{{ formatFen(item.payableAmount) }}</text
>
</view> </view>
<!-- 操作按钮区域 --> <!-- 操作按钮区域 -->
@ -151,7 +170,7 @@
<view class="record-actions" v-else-if="item.status === 4"> <view class="record-actions" v-else-if="item.status === 4">
<button <button
class="action-btn detail-btn" class="action-btn detail-btn"
@click.stop="handleViewDetail(item)" @click.stop="handleDelete(item)"
> >
删除 删除
</button> </button>
@ -166,12 +185,44 @@
<text v-else class="load-more-text">上拉加载更多</text> <text v-else class="load-more-text">上拉加载更多</text>
</view> </view>
</scroll-view> </scroll-view>
<!-- 券码二维码弹窗 -->
<view
class="qr-modal-mask"
v-if="qrModalVisible"
@click="closeQrModal"
></view>
<view class="qr-modal-wrap" v-if="qrModalVisible">
<view class="qr-modal" @click.stop>
<view class="qr-modal-header">
<text class="qr-modal-title">核销码</text>
<view
class="qr-modal-status"
:class="getQrUseStatusClass(qrModalData.useStatus)"
>
{{ getQrUseStatusText(qrModalData.useStatus) }}
</view>
<view class="qr-modal-close" @click="closeQrModal">×</view>
</view>
<view class="qr-modal-body">
<view class="qr-code-box" v-if="qrModalData.couponCode">
<image
class="qr-code-image"
:src="qrCodeImageUrl"
mode="aspectFit"
></image>
<!-- <text class="qr-code-text">{{ qrModalData.couponCode }}</text> -->
</view>
<text v-else class="qr-code-empty">暂无券码</text>
</view>
</view>
</view>
</view> </view>
</template> </template>
<script> <script>
import NavHeader from "@/components/NavHeader/NavHeader.vue"; import NavHeader from "@/components/NavHeader/NavHeader.vue";
import { getLuMyOrderPage, cancelOrder, getPaySign } from "@/api/service"; import { getLuMyOrderPage, cancelOrder, getPaySign,deleteOrder } from "@/api/service";
export default { export default {
components: { components: {
@ -193,12 +244,22 @@ export default {
completed: [], completed: [],
cancelled: [], cancelled: [],
}, },
qrModalVisible: false,
qrModalData: {
useStatus: 0,
couponCode: "",
},
}; };
}, },
computed: { computed: {
currentList() { currentList() {
return this.recordsMap[this.currentTab] || []; return this.recordsMap[this.currentTab] || [];
}, },
qrCodeImageUrl() {
const code = this.qrModalData.couponCode || "";
if (!code) return "";
return `https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${encodeURIComponent(code)}`;
},
}, },
onLoad(options) { onLoad(options) {
// tab使 tab // tab使 tab
@ -238,7 +299,7 @@ export default {
getStatusValue() { getStatusValue() {
const map = { const map = {
pending_payment: 0, // pending_payment: 0, //
pending_verification: 1, // pending_verification: 1, //
chargeback: 3, // 退 chargeback: 3, // 退
cancelled: 4, // cancelled: 4, //
}; };
@ -286,7 +347,7 @@ export default {
? "status-cancelled" ? "status-cancelled"
: ""; : "";
}, },
// // /1. 2.
async loadData(append = false) { async loadData(append = false) {
if (this.loading) return; if (this.loading) return;
this.loading = true; this.loading = true;
@ -297,7 +358,13 @@ export default {
pageSize: this.pageSize, pageSize: this.pageSize,
status, status,
}); });
const list = res.list || []; // list / records / data.list
const rawList =
res.list ||
res.records ||
(res.data && (res.data.list || res.data.records)) ||
[];
const list = Array.isArray(rawList) ? rawList : [];
const currentList = this.recordsMap[this.currentTab] || []; const currentList = this.recordsMap[this.currentTab] || [];
this.recordsMap = { this.recordsMap = {
@ -308,10 +375,12 @@ export default {
// //
this.hasMore = list.length >= this.pageSize; this.hasMore = list.length >= this.pageSize;
} catch (e) { } catch (e) {
const msg = (e && e.message) ? String(e.message) : "";
console.error("加载服务记录失败:", e); console.error("加载服务记录失败:", e);
uni.showToast({ uni.showToast({
title: "加载服务记录失败", title: msg ? `加载失败:${msg}` : "加载服务记录失败",
icon: "none", icon: "none",
duration: 2500,
}); });
} finally { } finally {
this.loading = false; this.loading = false;
@ -361,6 +430,27 @@ export default {
}, },
}); });
}, },
//
handleDelete(item) {
uni.showModal({
title: "提示",
content: "确定要删除该订单吗?",
success: async (res) => {
if (res.confirm) {
const resData = await deleteOrder({
id: item.id,
});
if (resData) {
uni.showToast({
title: "订单已删除",
icon: "success",
});
this.loadData();
}
}
},
});
},
// handlePay // handlePay
async handlePay(item) { async handlePay(item) {
if (!item || !item.orderNumber) { if (!item || !item.orderNumber) {
@ -448,6 +538,33 @@ export default {
icon: "none", icon: "none",
}); });
}, },
// + couponCode
handleQrCode(item, goods) {
const useStatus = goods?.useStatus ?? item?.useStatus ?? 0;
const couponCode =
(goods && (goods.couponCode || goods.couponNo)) ||
item?.couponCode ||
item?.couponNo ||
"";
this.qrModalData = { useStatus, couponCode };
this.qrModalVisible = true;
},
getQrUseStatusText(useStatus) {
const map = { 0: "未使用", 1: "已使用", 3: "已退款", 4: "已取消" };
return map[useStatus] ?? "未使用";
},
getQrUseStatusClass(useStatus) {
const map = {
0: "status-unused",
1: "status-used",
3: "status-refund",
4: "status-cancelled",
};
return map[useStatus] ?? "status-unused";
},
closeQrModal() {
this.qrModalVisible = false;
},
}, },
}; };
</script> </script>
@ -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;
}
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB