Files
integral-shop/single_uniapp22miao/pages/integral/orders.vue

1014 lines
25 KiB
Vue
Raw Normal View History

<template>
<view class="orders-page">
<!-- 橙色头部 -->
<view class="header-section">
<view class="header-content">
<view class="header-icon" @click="goBack">
<text class="iconfont icon-fanhui"></text>
</view>
<text class="header-title">我的订单</text>
<text class="header-menu"></text>
</view>
</view>
<!-- 状态标签栏 -->
<view class="stats-bar">
<view
v-for="(tab, index) in tabs"
:key="index"
class="stat-item"
:class="{ 'active': currentTab === index }"
@click="switchTab(index)"
>
<view class="stat-number-wrapper">
<text class="stat-number" :class="{ 'active': currentTab === index }">{{ tab.count || 0 }}</text>
<view v-if="currentTab === index" class="stat-underline"></view>
</view>
<text class="stat-text" :class="{ 'active': currentTab === index }">{{ tab.name }}</text>
</view>
</view>
<!-- 订单列表 -->
<scroll-view
class="orders-list"
scroll-y
@scrolltolower="loadMore"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
>
<view v-if="orderList.length > 0" class="list-container">
<view
v-for="(order, index) in orderList"
:key="order.id"
class="order-item"
@click="goToDetail(order.orderId)"
>
<!-- 订单头部日期和状态 -->
<view class="order-header">
<text class="order-time">{{ order.createTime }}</text>
<text class="order-status" :style="{ color: getStatusColor(order.status, order.paid) }">
{{ order.status_text }}
</text>
</view>
<!-- 商品信息列表 -->
<view class="order-content">
<view
v-for="(goods, gIndex) in order.orderInfoList"
:key="gIndex"
class="goods-row"
>
<image v-if="goods.image" class="goods-image" :src="goods.image" mode="aspectFill"></image>
<view class="goods-info">
<text class="goods-name">{{ goods.storeName || goods.productName }}</text>
<view class="goods-price-row">
<text class="goods-points">{{ formatPoints(goods.integral || goods.vipPrice || goods.price) }}积分</text>
<text class="goods-quantity">x{{ goods.cartNum }}</text>
</view>
</view>
</view>
</view>
<!-- 订单汇总 -->
<view class="order-summary">
<text class="summary-text">{{ order.quantity }}件商品总金额</text>
<text class="summary-points">{{ formatPoints(order.points) }}积分</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-else-if="!loading && isShow" class="empty-state">
<image class="empty-image" src="/static/images/empty.png" mode="aspectFit"></image>
<text class="empty-text">暂无订单</text>
<button class="empty-btn" @click="goToShop">去逛逛</button>
</view>
<!-- 加载更多 -->
<view v-if="orderList.length > 0" class="load-more">
<text v-if="loading" class="load-text">加载中...</text>
<text v-else-if="noMore" class="load-text">我也是有底线的</text>
</view>
</scroll-view>
<!-- 底部导航栏 -->
<view class="bottom-tabbar safe-area-inset-bottom">
<view class="tab-item" @click="goToShop">
<image class="tab-icon" src="/static/tabBar/home.png" mode="aspectFit"></image>
<text class="tab-text">积分兑换</text>
</view>
<view class="tab-item" @click="goToCart">
<view class="tab-icon-wrap">
<image class="tab-icon" src="/static/tabBar/cart.png" mode="aspectFit"></image>
<view class="cart-badge" v-if="cartCount > 0">
<text>{{ cartCount > 99 ? '99+' : cartCount }}</text>
</view>
</view>
<text class="tab-text">购物车</text>
</view>
<view class="tab-item active">
<image class="tab-icon" src="/static/tabBar/order-active.png" mode="aspectFit"></image>
<text class="tab-text active">订单</text>
</view>
</view>
</view>
</template>
<script>
import { getOrderList, orderData, orderCancel, orderTake, orderDel, getCartCounts } from '@/api/order.js';
import { mapGetters } from 'vuex';
import { toLogin } from '@/libs/login.js';
export default {
data() {
return {
// 状态标签: type对应后端参数
// -99-全部 0-待兑换(待付款) 1-待发货 2-待收货 3-已完成
tabs: [
{ name: '全部', type: -99, count: 0 },
{ name: '待兑换', type: 0, count: 0 },
{ name: '待发货', type: 1, count: 0 },
{ name: '待收货', type: 2, count: 0 },
{ name: '已完成', type: 4, count: 0 }
],
currentTab: 0, // 默认显示"全部"
orderList: [],
orderData: {}, // 订单统计数据
cartCount: 0, // 购物车数量
page: 1,
limit: 10,
loading: false,
refreshing: false,
noMore: false,
isShow: false
}
},
computed: {
...mapGetters(['isLogin'])
},
onLoad(options) {
if (options.status !== undefined) {
const status = parseInt(options.status);
// 根据状态找到对应的标签索引
const tabIndex = this.tabs.findIndex(tab => tab.type === status);
if (tabIndex >= 0) {
this.currentTab = tabIndex;
}
} else {
// 默认显示"全部"标签索引0
this.currentTab = 0;
}
},
onShow() {
// 页面显示时刷新订单统计和列表
if (this.isLogin) {
this.noMore = false;
this.page = 1;
this.orderList = [];
this.getOrderData();
this.loadOrderList();
this.getCartNum();
} else {
toLogin();
}
},
onReachBottom() {
this.loadMore();
},
methods: {
// 初始化页面
async initPage() {
// 检查登录状态
if (!this.isLogin) {
toLogin();
return;
}
// 加载订单统计
await this.getOrderData();
// 加载订单列表
this.loadOrderList();
},
// 获取订单统计数据
async getOrderData() {
try {
const res = await orderData();
if (res.data) {
this.orderData = res.data;
// 更新各个状态的订单数量
// 当前标签: 全部(-99), 待兑换(0), 待发货(1), 待收货(2), 已完成(4)
const unPaidCount = res.data.unPaidCount || 0; // 待兑换(待付款)
const unShippedCount = res.data.unShippedCount || 0;
const receivedCount = res.data.receivedCount || 0;
const evaluateCount = res.data.evaluateCount || 0; // 待评价
const completeCount = res.data.completeCount || 0; // 已完成
// "已完成"标签:包含待评价(type:3) + 已完成(type:4)
const finishedCount = evaluateCount + completeCount;
// 计算全部订单数量
const totalCount = res.data.orderCount || (unPaidCount + unShippedCount + receivedCount + finishedCount);
this.tabs[0].count = totalCount; // 全部
this.tabs[1].count = unPaidCount; // 待兑换
this.tabs[2].count = unShippedCount; // 待发货
this.tabs[3].count = receivedCount; // 待收货
this.tabs[4].count = finishedCount; // 已完成(包含待评价)
}
} catch (error) {
console.error('加载订单统计失败:', error);
}
},
// 获取购物车数量
async getCartNum() {
try {
const res = await getCartCounts(true, 'total');
if (res.data) {
this.cartCount = res.data.count || 0;
}
} catch (error) {
console.error('获取购物车数量失败:', error);
}
},
// 返回上一页
goBack() {
uni.navigateBack()
},
// 切换标签
switchTab(index) {
if (this.currentTab === index) return
this.currentTab = index
this.resetList()
this.loadOrderList()
},
// 重置列表
resetList() {
this.orderList = []
this.page = 1
this.noMore = false
},
// 加载订单列表
async loadOrderList() {
if (this.noMore) return
if (this.loading) return
// 检查登录状态
if (!this.isLogin) {
toLogin();
return;
}
this.loading = true
try {
const tab = this.tabs[this.currentTab];
const params = {
page: this.page,
limit: this.limit
};
let res;
// "全部"时传空字符串其他传具体type值
if (tab.type === -99) {
params.type = '-99'; // 空字符串表示查询所有状态
res = await getOrderList(params);
} else if (tab.type === 4) {
// "已完成"标签:同时获取 type=3(待评价) 和 type=4(已完成) 的订单
const [res3, res4] = await Promise.all([
getOrderList({ ...params, type: 3 }),
getOrderList({ ...params, type: 4 })
]);
// 合并两个结果
const list3 = (res3.data && res3.data.list) || [];
const list4 = (res4.data && res4.data.list) || [];
const mergedList = [...list3, ...list4];
// 按创建时间倒序排序
mergedList.sort((a, b) => {
const timeA = new Date(a.createTime || 0).getTime();
const timeB = new Date(b.createTime || 0).getTime();
return timeB - timeA;
});
// 构造合并后的响应
res = {
data: {
list: mergedList
}
};
} else {
params.type = tab.type;
res = await getOrderList(params);
}
if (res.data) {
const list = res.data.list || [];
// 格式化订单数据
const formattedList = list.map(order => this.formatOrderData(order));
if (this.page === 1) {
this.orderList = formattedList;
} else {
this.orderList = [...this.orderList, ...formattedList];
}
if (list.length < this.limit) {
this.noMore = true;
}
this.page++;
this.isShow = true;
} else {
uni.showToast({
title: res.msg || '加载失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载订单失败:', error)
uni.showToast({
title: error.msg || '加载失败,请重试',
icon: 'none'
})
} finally {
this.loading = false
this.refreshing = false
}
},
// 格式化订单数据
formatOrderData(order) {
// 获取商品列表
const orderInfoList = order.orderInfoList || [];
// 计算总商品数量
let totalNum = order.totalNum || 1;
// 计算总积分优先使用payIntegral/useIntegral否则从商品列表计算
let totalPoints = 0;
// 优先使用订单级别的积分字段
if (order.payIntegral !== undefined && order.payIntegral !== null) {
totalPoints = parseFloat(order.payIntegral) || 0;
} else if (order.useIntegral !== undefined && order.useIntegral !== null) {
totalPoints = parseFloat(order.useIntegral) || 0;
} else if (order.integral !== undefined && order.integral !== null) {
totalPoints = parseFloat(order.integral) || 0;
} else {
// 从商品列表计算总积分
totalPoints = orderInfoList.reduce((sum, item) => {
// 优先使用integral字段否则使用vipPrice或price
const itemPoints = parseFloat(item.integral) || parseFloat(item.vipPrice) || parseFloat(item.price) || 0;
const quantity = parseInt(item.cartNum) || 1;
return sum + (itemPoints * quantity);
}, 0);
// 如果计算结果为0尝试使用payPrice或totalPrice
if (totalPoints === 0) {
totalPoints = parseFloat(order.payPrice) || parseFloat(order.totalPrice) || 0;
}
}
// 获取第一个商品的信息用于列表显示
let goodsName = '商品名称';
let goodsImage = '/static/images/default-goods.png';
let goodsPrice = 0;
let goodsNum = 1;
if (orderInfoList.length > 0) {
const firstGoods = orderInfoList[0];
goodsName = firstGoods.storeName || firstGoods.productName || goodsName;
goodsImage = firstGoods.image || goodsImage;
// 商品积分优先使用integral否则使用vipPrice或price
goodsPrice = parseFloat(firstGoods.integral) || parseFloat(firstGoods.vipPrice) || parseFloat(firstGoods.price) || 0;
goodsNum = firstGoods.cartNum || 1;
}
console.log('订单积分计算:', {
orderId: order.orderId,
payIntegral: order.payIntegral,
useIntegral: order.useIntegral,
integral: order.integral,
payPrice: order.payPrice,
totalPrice: order.totalPrice,
calculatedPoints: totalPoints,
orderInfoList: orderInfoList.map(item => ({
name: item.storeName,
integral: item.integral,
vipPrice: item.vipPrice,
price: item.price,
cartNum: item.cartNum
}))
});
return {
id: order.id,
orderId: order.orderId || order.orderNo || '',
order_no: order.orderId || order.orderNo || '',
goods_name: goodsName,
goods_image: goodsImage,
quantity: totalNum,
points: totalPoints,
goods_points: goodsPrice,
goods_quantity: goodsNum,
status: order.status,
paid: order.paid,
status_text: order.orderStatus || this.getStatusText(order.status, order.paid),
orderInfoList: orderInfoList,
createTime: order.createTime || '',
created_at: order.createTime || '',
activityType: order.activityType || '普通'
};
},
// 获取状态文本
getStatusText(status, paid) {
if (!paid) return '待兑换';
// status: 0-待发货 1-待收货 2-待评价 3-已完成
const statusMap = {
0: '待发货',
1: '待收货',
2: '待评价',
3: '已完成'
};
return statusMap[status] || '未知状态';
},
// 下拉刷新
onRefresh() {
this.refreshing = true
this.resetList()
this.getOrderData()
this.loadOrderList()
},
// 加载更多
loadMore() {
if (!this.noMore && !this.loading) {
this.loadOrderList()
}
},
// 取消订单
cancelOrder(orderId, index) {
uni.showModal({
title: '提示',
content: '确定要取消这个订单吗?',
confirmColor: '#f55850',
success: async (res) => {
if (res.confirm) {
if (!this.isLogin) {
toLogin();
return;
}
uni.showLoading({ title: '取消中...' })
try {
const result = await orderCancel(orderId);
uni.hideLoading()
uni.showToast({
title: '取消成功',
icon: 'success'
})
// 从列表中移除该订单
if (index !== undefined) {
this.orderList.splice(index, 1);
}
// 刷新统计
this.getOrderData();
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.msg || error.message || '取消失败,请重试',
icon: 'none'
})
}
}
}
})
},
// 确认收货
confirmReceipt(orderId, index) {
uni.showModal({
title: '确认收货',
content: '确认已收到商品?',
confirmColor: '#f55850',
success: async (res) => {
if (res.confirm) {
if (!this.isLogin) {
toLogin();
return;
}
uni.showLoading({ title: '确认中...' })
try {
const result = await orderTake(orderId);
uni.hideLoading()
uni.showToast({
title: '确认收货成功',
icon: 'success'
})
// 刷新统计和列表
this.getOrderData();
this.resetList();
this.loadOrderList();
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.msg || error.message || '操作失败,请重试',
icon: 'none'
})
}
}
}
})
},
// 删除订单
delOrder(orderId, index) {
uni.showModal({
title: '提示',
content: '确定要删除这个订单吗?',
confirmColor: '#f55850',
success: async (res) => {
if (res.confirm) {
try {
await orderDel(orderId);
uni.showToast({
title: '删除成功',
icon: 'success'
})
// 从列表中移除该订单
if (index !== undefined) {
this.orderList.splice(index, 1);
}
// 刷新统计
this.getOrderData();
} catch (error) {
uni.showToast({
title: error.msg || '删除失败',
icon: 'none'
})
}
}
}
})
},
// 查看物流
viewLogistics(order) {
if (!order.express_no) {
uni.showToast({
title: '暂无物流信息',
icon: 'none'
})
return
}
// 跳转到积分订单详情页面查看物流
uni.navigateTo({
url: `/pages/integral/order-detail?id=${order.orderId}`
})
},
// 跳转订单详情
goToDetail(orderId) {
if (!orderId) return;
uni.navigateTo({
url: `/pages/integral/order-detail?id=${orderId}`
})
},
// 去评价
goComment(order) {
if (!order.orderInfoList || order.orderInfoList.length === 0) {
uni.showToast({
title: '商品信息不存在',
icon: 'none'
})
return
}
const firstGoods = order.orderInfoList[0]
uni.navigateTo({
url: `/pages/goods/goods_comment_con/index?unique=${firstGoods.attrId}&orderId=${order.orderId}&id=${order.id}`
})
},
// 去商城
goToShop() {
uni.navigateTo({
url: '/pages/integral/index'
})
},
// 去购物车
goToCart() {
uni.navigateTo({
url: '/pages/integral/cart'
})
},
// 去支付
goPay(order) {
uni.navigateTo({
url: `/pages/order/order_payment/index?orderNo=${order.orderId}&payPrice=${order.points}`
})
},
// 格式化积分
formatPoints(price) {
if (!price && price !== 0) return '0'
// 积分显示为整数,添加千分位
return Number(price).toFixed(0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
},
// 获取状态颜色
getStatusColor(status, paid) {
if (!paid) return '#F54900'; // 待付款 - 橙色
// status: 0-待发货 1-待收货 2-待评价 3-已完成
const colors = {
0: '#F54900', // 待发货 - 橙色
1: '#F54900', // 待收货 - 橙色
2: '#F54900', // 待评价 - 橙色
3: '#52c41a', // 已完成 - 绿色
}
return colors[status] || '#F54900'
},
}
}
</script>
<style lang="scss" scoped>
.orders-page {
min-height: 100vh;
background-color: #F9FAFB;
display: flex;
flex-direction: column;
padding-bottom: 120rpx;
}
// 橙色头部
.header-section {
background-color: #F54900;
padding: 0;
}
.header-content {
height: 112rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32rpx;
}
.header-icon {
width: 72rpx;
display: flex;
align-items: center;
justify-content: center;
.iconfont {
font-size: 36rpx;
color: #FFFFFF;
}
}
.header-title {
flex: 1;
font-size: 36rpx;
color: #FFFFFF;
font-weight: normal;
text-align: center;
line-height: 56rpx;
}
.header-menu {
font-size: 32rpx;
color: #FFFFFF;
font-weight: bold;
letter-spacing: 2rpx;
width: 72rpx;
text-align: right;
}
// 状态统计栏
.stats-bar {
background-color: #FFFFFF;
display: flex;
padding: 32rpx 0;
align-items: center;
justify-content: space-around;
}
.stat-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
}
.stat-number-wrapper {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.stat-number {
font-size: 40rpx;
color: #333333;
font-weight: 500;
line-height: 1.2;
&.active {
color: #F54900;
}
}
.stat-underline {
position: absolute;
bottom: -4rpx;
left: 50%;
transform: translateX(-50%);
width: 24rpx;
height: 4rpx;
background-color: #F54900;
}
.stat-text {
font-size: 26rpx;
color: #666666;
font-weight: normal;
line-height: 1.2;
&.active {
color: #F54900;
}
}
.orders-list {
flex: 1;
padding: 24rpx 32rpx;
background-color: #F9FAFB;
}
.list-container {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.order-item {
background-color: #FFFFFF;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 32rpx;
border-bottom: 1rpx solid #F5F5F5;
}
.order-time {
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
}
.order-status {
font-size: 28rpx;
font-weight: normal;
color: #F54900;
line-height: 40rpx;
}
.order-content {
padding: 0 32rpx;
}
.goods-row {
display: flex;
padding: 24rpx 0;
gap: 20rpx;
border-bottom: 1rpx solid #F5F5F5;
&:last-child {
border-bottom: none;
}
}
.goods-image {
width: 100rpx;
height: 100rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
flex-shrink: 0;
}
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 12rpx;
}
.goods-name {
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
font-weight: normal;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
line-clamp: 1;
overflow: hidden;
}
.goods-price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.goods-points {
font-size: 28rpx;
color: #F54900;
font-weight: normal;
line-height: 40rpx;
}
.goods-quantity {
font-size: 26rpx;
color: #999999;
line-height: 40rpx;
}
.order-summary {
padding: 24rpx 32rpx;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 8rpx;
}
.summary-text {
font-size: 26rpx;
color: #333333;
line-height: 40rpx;
}
.summary-points {
font-size: 28rpx;
color: #F54900;
font-weight: 500;
line-height: 40rpx;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
}
.empty-image {
width: 300rpx;
height: 300rpx;
margin-bottom: 40rpx;
}
.empty-text {
font-size: 28rpx;
color: #999999;
margin-bottom: 40rpx;
}
.empty-btn {
width: 200rpx;
height: 80rpx;
line-height: 80rpx;
background-color: #F54900;
color: #FFFFFF;
border-radius: 40rpx;
font-size: 28rpx;
border: none;
&::after {
border: none;
}
}
.load-more {
padding: 40rpx 0;
text-align: center;
}
.load-text {
font-size: 28rpx;
color: #999999;
line-height: 40rpx;
}
// 底部导航栏
.bottom-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #FFFFFF;
display: flex;
border-top: 1rpx solid #EEEEEE;
z-index: 100;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4rpx;
}
.tab-icon-wrap {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.tab-icon {
width: 44rpx;
height: 44rpx;
}
.cart-badge {
position: absolute;
top: -8rpx;
right: -16rpx;
min-width: 32rpx;
height: 32rpx;
background-color: #F54900;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 8rpx;
text {
font-size: 20rpx;
color: #FFFFFF;
font-weight: 500;
}
}
.tab-text {
font-size: 22rpx;
color: #999999;
&.active {
color: #F54900;
}
}
</style>