|
@ -15,7 +15,7 @@ |
|
|
<!-- 订单卡片 --> |
|
|
<!-- 订单卡片 --> |
|
|
<view class="order-card" v-for="(order, index) in orderList" :key="index"> |
|
|
<view class="order-card" v-for="(order, index) in orderList" :key="index"> |
|
|
<view class="order-header"> |
|
|
<view class="order-header"> |
|
|
<text>{{ tabs[order.status - 1].name }}</text> |
|
|
|
|
|
|
|
|
<text>{{ tabs[order.status + 1].name }}</text> |
|
|
<view class="order-status"> |
|
|
<view class="order-status"> |
|
|
<text v-if="order.teacherId">指定伴宠师下单</text> |
|
|
<text v-if="order.teacherId">指定伴宠师下单</text> |
|
|
<text v-else>系统下单</text> |
|
|
<text v-else>系统下单</text> |
|
@ -85,7 +85,8 @@ |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
|
<!-- 加载更多 --> |
|
|
<!-- 加载更多 --> |
|
|
<view class="loading-more" v-if="orderList.length > 0 && hasMore"> |
|
|
|
|
|
|
|
|
<view class="loading-more" v-if="orderList.length > 0 && hasMore && loading"> |
|
|
|
|
|
<view class="loading-spinner"></view> |
|
|
<text>加载中...</text> |
|
|
<text>加载中...</text> |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
@ -99,8 +100,22 @@ |
|
|
<image src="/static/images/personal/no-data.png" mode="aspectFit" class="no-data-image"></image> |
|
|
<image src="/static/images/personal/no-data.png" mode="aspectFit" class="no-data-image"></image> |
|
|
<text>暂无订单</text> |
|
|
<text>暂无订单</text> |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 首次加载状态 --> |
|
|
|
|
|
<view class="initial-loading" v-if="orderList.length === 0 && loading"> |
|
|
|
|
|
<view class="loading-spinner large"></view> |
|
|
|
|
|
<text>正在加载订单...</text> |
|
|
|
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</scroll-view> |
|
|
</scroll-view> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 全屏加载遮罩 (仅首次加载显示) --> |
|
|
|
|
|
<!-- <view class="loading-overlay" v-if="loading && page === 1 && orderList.length === 0 && isFirstLoad"> |
|
|
|
|
|
<view class="loading-content"> |
|
|
|
|
|
<view class="loading-spinner large"></view> |
|
|
|
|
|
<text class="loading-text">正在加载订单数据...</text> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> --> |
|
|
|
|
|
|
|
|
<!-- 取消订单弹窗 --> |
|
|
<!-- 取消订单弹窗 --> |
|
|
<cancel-order-popup ref="cancelOrderPopup" @cancel="handleCancelOrder"></cancel-order-popup> |
|
|
<cancel-order-popup ref="cancelOrderPopup" @cancel="handleCancelOrder"></cancel-order-popup> |
|
@ -110,6 +125,14 @@ |
|
|
|
|
|
|
|
|
<!-- 伴宠师选择弹窗 --> |
|
|
<!-- 伴宠师选择弹窗 --> |
|
|
<companion-select-popup ref="companionSelectPopup" /> |
|
|
<companion-select-popup ref="companionSelectPopup" /> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 支付加载遮罩 --> |
|
|
|
|
|
<view class="loading-overlay" v-if="isPaying"> |
|
|
|
|
|
<view class="loading-content"> |
|
|
|
|
|
<view class="loading-spinner large"></view> |
|
|
|
|
|
<text class="loading-text">正在处理支付...</text> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
@ -166,6 +189,10 @@ |
|
|
refreshing: false, |
|
|
refreshing: false, |
|
|
currentOrder: null, |
|
|
currentOrder: null, |
|
|
isPaying : false, |
|
|
isPaying : false, |
|
|
|
|
|
requestId: 0, // 请求标识,确保只处理最后一个请求 |
|
|
|
|
|
switchTabTimer: null, // 切换标签防抖定时器 |
|
|
|
|
|
loadMoreTimer: null, // 加载更多防抖定时器 |
|
|
|
|
|
isFirstLoad: true, // 是否首次加载 |
|
|
// 示例数据,实际应从API获取 |
|
|
// 示例数据,实际应从API获取 |
|
|
exampleData: [{ |
|
|
exampleData: [{ |
|
|
id: '1', |
|
|
id: '1', |
|
@ -196,12 +223,23 @@ |
|
|
methods: { |
|
|
methods: { |
|
|
// 切换标签 |
|
|
// 切换标签 |
|
|
switchTab(tabValue) { |
|
|
switchTab(tabValue) { |
|
|
// if (this.currentTab === tabValue) return; |
|
|
|
|
|
|
|
|
if (this.currentTab === tabValue) return; // 如果是相同标签,不重复请求 |
|
|
|
|
|
|
|
|
this.currentTab = tabValue; |
|
|
this.currentTab = tabValue; |
|
|
this.orderList = []; |
|
|
this.orderList = []; |
|
|
this.page = 1; |
|
|
this.page = 1; |
|
|
this.hasMore = true; |
|
|
this.hasMore = true; |
|
|
this.getOrderList(); |
|
|
|
|
|
|
|
|
this.isFirstLoad = false; // 标记为非首次加载 |
|
|
|
|
|
|
|
|
|
|
|
// 清除之前的定时器 |
|
|
|
|
|
if (this.switchTabTimer) { |
|
|
|
|
|
clearTimeout(this.switchTabTimer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 添加防抖,避免快速点击 |
|
|
|
|
|
this.switchTabTimer = setTimeout(() => { |
|
|
|
|
|
this.getOrderList(); |
|
|
|
|
|
}, 100); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 下拉刷新 |
|
|
// 下拉刷新 |
|
@ -210,6 +248,7 @@ |
|
|
this.page = 1; |
|
|
this.page = 1; |
|
|
this.orderList = []; |
|
|
this.orderList = []; |
|
|
this.hasMore = true; |
|
|
this.hasMore = true; |
|
|
|
|
|
this.isFirstLoad = false; // 标记为非首次加载 |
|
|
this.getOrderList().then(() => { |
|
|
this.getOrderList().then(() => { |
|
|
this.refreshing = false; |
|
|
this.refreshing = false; |
|
|
}).catch(() => { |
|
|
}).catch(() => { |
|
@ -224,8 +263,16 @@ |
|
|
// 加载更多 |
|
|
// 加载更多 |
|
|
loadMore() { |
|
|
loadMore() { |
|
|
if (this.loading || !this.hasMore) return; |
|
|
if (this.loading || !this.hasMore) return; |
|
|
this.page++; |
|
|
|
|
|
this.getOrderList(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 添加防抖,避免快速滚动触发多次加载 |
|
|
|
|
|
if (this.loadMoreTimer) { |
|
|
|
|
|
clearTimeout(this.loadMoreTimer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.loadMoreTimer = setTimeout(() => { |
|
|
|
|
|
this.page++; |
|
|
|
|
|
this.getOrderList(); |
|
|
|
|
|
}, 200); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 查看订单详情 |
|
|
// 查看订单详情 |
|
@ -284,6 +331,9 @@ |
|
|
getOrderList() { |
|
|
getOrderList() { |
|
|
if (this.loading) return Promise.reject(); |
|
|
if (this.loading) return Promise.reject(); |
|
|
this.loading = true; |
|
|
this.loading = true; |
|
|
|
|
|
|
|
|
|
|
|
// 生成新的请求ID,确保只处理最后一个请求 |
|
|
|
|
|
const currentRequestId = ++this.requestId; |
|
|
|
|
|
|
|
|
// 构建请求参数 |
|
|
// 构建请求参数 |
|
|
const params = { |
|
|
const params = { |
|
@ -317,6 +367,12 @@ |
|
|
// }); |
|
|
// }); |
|
|
// 实际API调用应该是这样: |
|
|
// 实际API调用应该是这样: |
|
|
return getOrderList(params).then(res => { |
|
|
return getOrderList(params).then(res => { |
|
|
|
|
|
// 检查是否是最后一个请求,如果不是则忽略结果 |
|
|
|
|
|
if (currentRequestId !== this.requestId) { |
|
|
|
|
|
console.log('忽略过期的请求结果'); |
|
|
|
|
|
return Promise.reject('过期的请求'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (res.content) { |
|
|
if (res.content) { |
|
|
const newList = res.content || []; |
|
|
const newList = res.content || []; |
|
|
if (this.page === 1) { |
|
|
if (this.page === 1) { |
|
@ -342,9 +398,13 @@ |
|
|
this.hasMore = false; |
|
|
this.hasMore = false; |
|
|
} |
|
|
} |
|
|
this.loading = false; |
|
|
this.loading = false; |
|
|
resolve(); |
|
|
|
|
|
|
|
|
this.isFirstLoad = false; // 标记首次加载完成 |
|
|
}).catch(err => { |
|
|
}).catch(err => { |
|
|
this.loading = false; |
|
|
|
|
|
|
|
|
// 只有当前请求才更新loading状态 |
|
|
|
|
|
if (currentRequestId === this.requestId) { |
|
|
|
|
|
this.loading = false; |
|
|
|
|
|
this.isFirstLoad = false; // 标记首次加载完成 |
|
|
|
|
|
} |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
@ -369,6 +429,18 @@ |
|
|
onLoad() { |
|
|
onLoad() { |
|
|
// 页面加载时获取订单列表 |
|
|
// 页面加载时获取订单列表 |
|
|
this.getOrderList(); |
|
|
this.getOrderList(); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
onUnload() { |
|
|
|
|
|
// 页面销毁时清理所有定时器 |
|
|
|
|
|
if (this.switchTabTimer) { |
|
|
|
|
|
clearTimeout(this.switchTabTimer); |
|
|
|
|
|
this.switchTabTimer = null; |
|
|
|
|
|
} |
|
|
|
|
|
if (this.loadMoreTimer) { |
|
|
|
|
|
clearTimeout(this.loadMoreTimer); |
|
|
|
|
|
this.loadMoreTimer = null; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</script> |
|
@ -569,6 +641,72 @@ |
|
|
padding: 20rpx 0; |
|
|
padding: 20rpx 0; |
|
|
color: #999; |
|
|
color: #999; |
|
|
font-size: 24rpx; |
|
|
font-size: 24rpx; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.initial-loading { |
|
|
|
|
|
text-align: center; |
|
|
|
|
|
padding: 100rpx 0; |
|
|
|
|
|
color: #999; |
|
|
|
|
|
font-size: 28rpx; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.loading-overlay { |
|
|
|
|
|
position: fixed; |
|
|
|
|
|
top: 0; |
|
|
|
|
|
left: 0; |
|
|
|
|
|
right: 0; |
|
|
|
|
|
bottom: 0; |
|
|
|
|
|
// background-color: rgba(255, 255, 255, 0.8); |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
z-index: 999; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.loading-content { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
// background-color: #FFFFFF; |
|
|
|
|
|
padding: 40rpx; |
|
|
|
|
|
border-radius: 20rpx; |
|
|
|
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.loading-text { |
|
|
|
|
|
color: #666; |
|
|
|
|
|
font-size: 28rpx; |
|
|
|
|
|
margin-top: 20rpx; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.loading-spinner { |
|
|
|
|
|
width: 40rpx; |
|
|
|
|
|
height: 40rpx; |
|
|
|
|
|
border: 4rpx solid #f3f3f3; |
|
|
|
|
|
border-top: 4rpx solid #FFAA48; |
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
animation: spin 1s linear infinite; |
|
|
|
|
|
margin-bottom: 16rpx; |
|
|
|
|
|
|
|
|
|
|
|
&.large { |
|
|
|
|
|
width: 60rpx; |
|
|
|
|
|
height: 60rpx; |
|
|
|
|
|
border-width: 6rpx; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@keyframes spin { |
|
|
|
|
|
0% { transform: rotate(0deg); } |
|
|
|
|
|
100% { transform: rotate(360deg); } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.no-order { |
|
|
.no-order { |
|
|