Files
venue-miniapp/miniprogram/src/pages/comprehensive/profile/orders.vue
T
gqt 6a18d3de61 feat: 页面重写与流程优化
- 门票Tab展示已购门票记录(可核销/已核销/已退款)
- 购买次数去掉合计金额行
- 登录后直接跳profile/detail,合并setup页(不区分模式)
- profile/detail增加返回首页按钮
- 约课页移除UnauthLoginPanel
- 401跳转改为reLaunch到login页,不显示toast
- membership/cards仅展示可用卡,恢复点击进详情
- available-sessions改为只读展示,移除预约逻辑
- profile/orders重写样式,单行状态筛选
- profile/balance重写样式,onShow自动刷新
- profile/index移除冗余菜单项(篮球门票/充值/我的预约/个人信息/切换场馆)
- profile/index移除AgreementLinks,我的次卡改为我的会员卡
- pages.json移除setup页注册
2026-07-13 21:43:59 +08:00

246 lines
5.5 KiB
Vue

<template>
<view class="orders-page">
<scroll-view scroll-x class="filter-bar" :show-scrollbar="false">
<view class="filter-row">
<view v-for="item in statusOptions" :key="item.value" class="filter-chip" :class="{ active: orderStatus === item.value }" @tap="selectStatus(item.value)">
{{ item.label }}
</view>
</view>
</scroll-view>
<view v-if="isLoading" class="load-more-tip">加载中...</view>
<view v-else-if="orders.length" class="order-list">
<view v-for="order in orders" :key="order.id" class="card order-card" @tap="goDetail(order.id)">
<view class="order-top">
<text class="business">{{ order.business_type_label }}</text>
<text class="status-tag" :class="order.status">{{ order.status_label }}</text>
</view>
<text class="order-no">{{ order.order_no }}</text>
<view class="order-bottom">
<text class="time">{{ formatDateTime(order.created_at) }}</text>
<text class="amount">¥{{ order.amount }}</text>
</view>
<view v-if="order.status === 'pending_pay'" class="actions" @tap.stop>
<button class="ghost-button small-btn" @tap="closeOrder(order.id)">取消</button>
<button class="primary-button small-btn" @tap="goPay(order.id)">去支付</button>
</view>
</view>
<view v-if="!hasMore && orders.length >= pageSize" class="load-more-tip">已加载全部</view>
</view>
<view v-else class="empty-state">暂无订单</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { orderApi } from '@/modules/comprehensive/api/order'
import { formatDateTime } from '@/modules/comprehensive/utils/date'
const statusOptions = [
{ label: '全部', value: '' },
{ label: '待支付', value: 'pending_pay' },
{ label: '已支付', value: 'paid' },
{ label: '已关闭', value: 'closed' },
{ label: '已退款', value: 'refunded' }
]
const orderStatus = ref('')
const orders = ref([])
const page = ref(1)
const pageSize = 20
const total = ref(0)
const isLoading = ref(false)
const isLoadingMore = ref(false)
const hasMore = computed(() => orders.value.length < total.value)
const loadOrders = async (reset) => {
if (reset) {
page.value = 1
isLoading.value = true
} else {
isLoadingMore.value = true
}
try {
const data = await orderApi.getOrders({
page: page.value,
pageSize,
status: orderStatus.value
})
orders.value = reset ? data.items : [...orders.value, ...data.items]
total.value = data.total
} finally {
isLoading.value = false
isLoadingMore.value = false
}
}
const selectStatus = (value) => {
orderStatus.value = value
loadOrders(true)
}
const goDetail = (id) => {
uni.navigateTo({ url: `/pages/comprehensive/orders/detail?id=${id}` })
}
const goPay = (id) => {
uni.navigateTo({ url: `/pages/comprehensive/pay/index?order_id=${id}` })
}
const closeOrder = async (id) => {
await orderApi.closeOrder(id)
uni.showToast({ title: '订单已取消', icon: 'none' })
loadOrders(true)
}
onPullDownRefresh(() => {
loadOrders(true).finally(() => uni.stopPullDownRefresh())
})
onReachBottom(() => {
if (!hasMore.value || isLoadingMore.value) return
page.value += 1
loadOrders(false)
})
loadOrders(true)
</script>
<style lang="scss" scoped>
.orders-page {
min-height: 100vh;
background: $bg-color-soft;
}
.filter-bar {
position: sticky;
top: 0;
z-index: 10;
background: $bg-color-main;
border-bottom: 1rpx solid $border-color;
white-space: nowrap;
}
.filter-row {
display: inline-flex;
padding: $spacing-sm $spacing-xs;
gap: $spacing-xs;
}
.filter-chip {
min-width: 100rpx;
height: 64rpx;
padding: 0 $spacing-md;
border-radius: $border-radius-sm;
background: $bg-color-hover;
color: $text-color-muted;
font-size: 24rpx;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
}
.filter-chip.active {
background: $text-color-main;
color: #FFFFFF;
}
.order-list {
display: flex;
flex-direction: column;
gap: $spacing-md;
padding: $spacing-md;
}
.order-card {
display: flex;
flex-direction: column;
}
.order-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: $spacing-sm;
}
.business {
color: $text-color-main;
font-size: 30rpx;
font-weight: 900;
}
.status-tag {
padding: 4rpx 16rpx;
border-radius: $border-radius-sm;
font-size: 22rpx;
font-weight: 800;
}
.status-tag.paid {
background: $color-success-light;
color: $color-success;
}
.status-tag.pending_pay {
background: $color-warning-light;
color: $color-warning;
}
.status-tag.closed {
background: $bg-color-hover;
color: $text-color-muted;
}
.status-tag.refunded {
background: $color-danger-light;
color: $color-danger;
}
.order-no {
display: block;
margin-top: $spacing-sm;
color: $text-color-muted;
font-size: 24rpx;
}
.order-bottom {
display: flex;
align-items: center;
justify-content: space-between;
gap: $spacing-md;
margin-top: $spacing-md;
}
.time {
color: $text-color-muted;
font-size: 24rpx;
}
.amount {
color: $text-color-main;
font-size: 34rpx;
font-weight: 900;
}
.actions {
display: flex;
justify-content: flex-end;
gap: $spacing-sm;
margin-top: $spacing-md;
padding-top: $spacing-md;
border-top: 1rpx solid $bg-color-hover;
}
.small-btn {
width: 160rpx;
height: 64rpx;
font-size: 24rpx;
}
</style>