Files
solosw 99b11b04e4 1.0
2026-01-05 14:11:34 +08:00

694 lines
15 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<!-- 状态切换标签 -->
<view class="status-tabs">
<view class="tab-item"
:class="{ active: currentStatus === 'all' }"
@click="switchStatus('all')">
<text class="tab-text">全部</text>
</view>
<view class="tab-item"
:class="{ active: currentStatus === '0' }"
@click="switchStatus('0')">
<text class="tab-text">待付款</text>
</view>
<view class="tab-item"
:class="{ active: currentStatus === '1' }"
@click="switchStatus('1')">
<text class="tab-text">待确认</text>
</view>
<view class="tab-item"
:class="{ active: currentStatus === '2' }"
@click="switchStatus('2')">
<text class="tab-text">已完成</text>
</view>
</view>
<!-- 筛选条件 -->
<view class="filter-bar">
<view class="filter-item" @click="showDatePicker('start')">
<text class="filter-label">开始日期</text>
<text class="filter-value">{{ startDate || '选择日期' }}</text>
</view>
<view class="filter-item" @click="showDatePicker('end')">
<text class="filter-label">结束日期</text>
<text class="filter-value">{{ endDate || '选择日期' }}</text>
</view>
<view class="filter-item" @click="showAmountFilter">
<text class="filter-label">按日期筛选</text>
<text class="filter-value">总金额</text>
</view>
</view>
<!-- 总金额显示 -->
<view class="amount-summary" v-if="totalAmount > 0">
<text class="summary-label">重置</text>
<text class="summary-amount">{{ totalAmount }}</text>
</view>
<!-- 订单列表 -->
<view class="order-list">
<view class="order-item" v-for="(item, index) in orderList" :key="index" @click="goToOrderDetail(item)">
<view class="order-header">
<text class="order-number">订单编号{{ item.order_no }}</text>
<view class="order-status" :class="getStatusClass(item.order_status)">
<text class="status-text">{{ getStatusText(item.order_status) }}</text>
</view>
</view>
<view class="order-content">
<view class="product-info">
<image :src="getFullImageUrl(getFirstImage(item.product_images))"
class="product-image"
mode="aspectFill">
</image>
<view class="product-details">
<text class="product-name">{{ item.product_name }}</text>
<text class="product-price">¥{{ item.unit_price }}</text>
<text class="product-quantity">×{{ item.quantity }}</text>
</view>
</view>
</view>
<view class="order-footer">
<text class="order-time">下单时间{{ formatTime(item.created_at) }}</text>
<view class="order-summary">
<!-- <text class="summary-text">{{ item.quantity }}件商品 运费 ¥0.00 合计</text> -->
<text class="summary-price">¥{{ item.total_amount }}</text>
</view>
</view>
<view class="order-actions">
<!-- <button class="action-btn detail-btn" @click.stop="goToDetail(item)">
查看详情
</button> -->
<button v-if="item.payment_proof_image"
class="action-btn proof-btn"
@click.stop="viewPaymentProof(item)">
查看凭证
</button>
<button v-if="item.order_status === 0"
class="action-btn pay-btn"
@click.stop="payOrder(item)">
立即付款
</button>
<button v-if="item.order_status === 0"
class="action-btn cancel-btn"
@click.stop="cancelOrder(item)">
取消订单
</button>
<!-- <button v-if="item.order_status === 1"
class="action-btn confirm-btn"
@click.stop="confirmOrder(item)">
确认收货
</button> -->
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="orderList.length === 0 && !loading">
<uni-icons type="list" size="100" color="#ccc"></uni-icons>
<text class="empty-text">暂无订单</text>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="hasMore && orderList.length > 0">
<text>加载更多...</text>
</view>
</view>
</template>
<script>
import request from '@/utils/request.js'
export default {
data() {
return {
currentStatus: 'all',
startDate: '',
endDate: '',
totalAmount: 0,
orderList: [],
currentPage: 1,
pageSize: 10,
hasMore: true,
loading: false
}
},
onLoad(options) {
// 检查登录状态
const token = uni.getStorageSync('token')
if (!token) {
uni.reLaunch({
url: '/pages/login/login'
})
return
}
if (options.status) {
this.currentStatus = options.status
}
this.loadOrders()
},
onReachBottom() {
if (this.hasMore && !this.loading) {
this.loadMore()
}
},
methods: {
// 获取完整的图片URL
getFullImageUrl(url) {
return request.getImageUrl(url)
},
// 获取商品的第一张图片
getFirstImage(images) {
if (!images) return ''
if (typeof images === 'string') {
return images.split(',')[0].trim()
}
if (Array.isArray(images) && images.length > 0) {
return images[0]
}
return ''
},
// 切换状态
switchStatus(status) {
this.currentStatus = status
this.currentPage = 1
this.loadOrders()
},
// 显示日期选择器
showDatePicker(type) {
uni.showActionSheet({
itemList: ['今天', '最近7天', '最近30天', '最近90天'],
success: (res) => {
const now = new Date()
let startDate, endDate
switch (res.tapIndex) {
case 0: // 今天
startDate = endDate = this.formatDate(now)
break
case 1: // 最近7天
startDate = this.formatDate(new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000))
endDate = this.formatDate(now)
break
case 2: // 最近30天
startDate = this.formatDate(new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000))
endDate = this.formatDate(now)
break
case 3: // 自定义
startDate = this.formatDate(new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000))
endDate = this.formatDate(now)
break
}
if (type === 'start') {
this.startDate = startDate
} else {
this.endDate = endDate
}
this.currentPage = 1
this.loadOrders()
}
})
},
// 显示金额筛选
showAmountFilter() {
this.startDate = ''
this.endDate = ''
this.currentPage = 1
this.loadOrders()
},
// 加载订单列表
async loadOrders() {
this.loading = true
try {
const params = {
page: this.currentPage,
page_size: this.pageSize
}
if (this.currentStatus !== 'all') {
// 状态映射:前端显示状态 -> 后端查询参数
const statusMap = {
'0': 'pending',
'1': 'confirming',
'2': 'completed',
'3': 'cancelled'
}
params.status = statusMap[this.currentStatus] || this.currentStatus
}
if (this.startDate) {
params.start_date = this.startDate
}
if (this.endDate) {
params.end_date = this.endDate
}
const res = await request.get('/back/user/orders/purchase', params)
if (res.success) {
const data = res.data
if (this.currentPage === 1) {
this.orderList = data.list || []
} else {
this.orderList = [...this.orderList, ...(data.list || [])]
}
this.hasMore = this.orderList.length < data.total
this.totalAmount = data.total_amount || 0
}
} catch (error) {
console.error('加载订单失败:', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
// 加载更多
loadMore() {
this.currentPage++
this.loadOrders()
},
// 获取状态样式类
getStatusClass(status) {
const classMap = {
0: 'status-pending',
1: 'status-confirming',
2: 'status-completed',
3: 'status-cancelled'
}
return classMap[status] || ''
},
// 获取状态文本
getStatusText(status) {
const textMap = {
0: '待付款',
1: '待确认',
2: '已完成',
3: '已取消'
}
return textMap[status] || '未知状态'
},
// 格式化时间
formatTime(time) {
if (!time) return ''
const date = new Date(time)
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`
},
// 格式化日期
formatDate(date) {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
},
// 跳转到订单详情
goToDetail(order) {
uni.navigateTo({
url: `/pages/order/detail?id=${order.id}&type=purchase`
})
},
// 跳转到订单详情页面
goToOrderDetail(order) {
uni.navigateTo({
url: `/pages/orders/detail?id=${order.id}&type=purchase`
})
},
// 查看付款凭证
viewPaymentProof(order) {
if (!order.payment_proof_image) {
uni.showToast({
title: '暂无付款凭证',
icon: 'none'
})
return
}
uni.previewImage({
urls: [this.getFullImageUrl(order.payment_proof_image)],
current: 0
})
},
// 支付订单 - 跳转到付款页面
payOrder(order) {
var isSecond=order.product_type == 1 ? false : true
uni.navigateTo({
url: `/pages/payment/purchase?order_id=${order.id}&order_no=${order.order_no}&is_second=${isSecond}`
})
},
// 取消订单
async cancelOrder(order) {
uni.showModal({
title: '确认取消',
content: '确定要取消这个订单吗?',
success: async (res) => {
if (res.confirm) {
try {
const result = await request.put(`/back/user/orders/purchase/${order.id}/cancel`)
if (result.success) {
uni.showToast({
title: '订单已取消',
icon: 'success'
})
this.loadOrders()
} else {
throw new Error(result.message || '取消失败')
}
} catch (error) {
console.error('取消订单失败:', error)
uni.showToast({
title: error.message || '取消失败',
icon: 'none'
})
}
}
}
})
},
// 确认收货
async confirmOrder(order) {
uni.showModal({
title: '确认收货',
content: '确认已收到商品吗?确认后商品将进入您的仓库。',
success: async (res) => {
if (res.confirm) {
try {
const result = await request.put(`/back/user/orders/purchase/${order.id}/confirm`)
if (result.success) {
uni.showToast({
title: '确认收货成功',
icon: 'success'
})
this.loadOrders()
} else {
throw new Error(result.message || '确认失败')
}
} catch (error) {
console.error('确认收货失败:', error)
uni.showToast({
title: error.message || '确认失败',
icon: 'none'
})
}
}
}
})
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
/* 状态标签 */
.status-tabs {
background-color: #fff;
display: flex;
padding: 0 30rpx;
}
.tab-item {
flex: 1;
text-align: center;
padding: 30rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666;
}
.tab-item.active .tab-text {
color: #FF4444;
font-weight: bold;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background-color: #FF4444;
}
/* 筛选条件 */
.filter-bar {
background-color: #fff;
display: flex;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #eee;
}
.filter-item {
flex: 1;
text-align: center;
}
.filter-label {
display: block;
font-size: 24rpx;
color: #999;
margin-bottom: 10rpx;
}
.filter-value {
font-size: 28rpx;
color: #333;
}
/* 总金额 */
.amount-summary {
background-color: #fff;
padding: 20rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #eee;
}
.summary-label {
font-size: 28rpx;
color: #007AFF;
}
.summary-amount {
font-size: 32rpx;
font-weight: bold;
color: #FF4444;
}
/* 订单列表 */
.order-list {
padding: 20rpx;
}
.order-item {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.order-number {
font-size: 24rpx;
color: #666;
}
.order-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
}
.status-pending {
background-color: #FFF2E6;
color: #FF8800;
}
.status-confirming {
background-color: #E6F7FF;
color: #1890FF;
}
.status-completed {
background-color: #F6FFED;
color: #52C41A;
}
.status-cancelled {
background-color: #FFF1F0;
color: #FF4D4F;
}
.order-content {
padding: 30rpx;
}
.product-info {
display: flex;
}
.product-image {
width: 160rpx;
height: 120rpx;
border-radius: 8rpx;
margin-right: 20rpx;
background-color: #f5f5f5;
}
.product-details {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.product-name {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.product-price {
font-size: 32rpx;
color: #FF4444;
font-weight: bold;
}
.product-quantity {
font-size: 24rpx;
color: #999;
}
.order-footer {
padding: 30rpx;
border-top: 1rpx solid #f5f5f5;
}
.order-time {
display: block;
font-size: 24rpx;
color: #999;
margin-bottom: 20rpx;
}
.order-summary {
display: flex;
justify-content: space-between;
align-items: center;
}
.summary-text {
font-size: 24rpx;
color: #666;
}
.summary-price {
font-size: 32rpx;
color: #FF4444;
font-weight: bold;
}
.order-actions {
display: flex;
justify-content: flex-end;
padding: 30rpx;
gap: 20rpx;
border-top: 1rpx solid #f5f5f5;
}
.action-btn {
padding: 16rpx 32rpx;
border-radius: 8rpx;
font-size: 24rpx;
border: 1rpx solid #ddd;
}
.detail-btn {
background-color: #fff;
color: #666;
}
.proof-btn {
background-color: #1890FF;
color: #fff;
border-color: #1890FF;
}
.pay-btn {
background-color: #FF4444;
color: #fff;
border-color: #FF4444;
}
.cancel-btn {
background-color: #fff;
color: #999;
}
.confirm-btn {
background-color: #52C41A;
color: #fff;
border-color: #52C41A;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 200rpx 0;
}
.empty-state uni-icons {
margin-bottom: 40rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 加载更多 */
.load-more {
text-align: center;
padding: 40rpx;
color: #999;
font-size: 24rpx;
}
</style>