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页注册
This commit is contained in:
@@ -1,27 +1,20 @@
|
||||
<template>
|
||||
<view class="orders-page">
|
||||
<view class="filter-panel">
|
||||
<scroll-view scroll-x class="filter-scroll">
|
||||
<view class="filter-row">
|
||||
<view v-for="item in businessOptions" :key="item.value" class="filter-chip" :class="{ active: businessType === item.value }" @tap="selectBusiness(item.value)">
|
||||
{{ item.label }}
|
||||
</view>
|
||||
<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>
|
||||
</scroll-view>
|
||||
<scroll-view scroll-x class="filter-scroll second">
|
||||
<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>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="list">
|
||||
<view v-for="order in orders" :key="order.id" class="order-card card" @tap="goDetail(order.id)">
|
||||
<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="badge" :class="statusBadgeClass(order.status)">{{ order.status_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">
|
||||
@@ -29,33 +22,22 @@
|
||||
<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="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="isLoading" class="load-more-tip">加载中...</view>
|
||||
<view v-else-if="orders.length === 0" class="empty-state">暂无订单</view>
|
||||
<view v-else-if="!hasMore" class="load-more-tip">已加载全部</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, onMounted, ref } from 'vue'
|
||||
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'
|
||||
import { statusBadgeClass } from '@/modules/comprehensive/utils/status'
|
||||
|
||||
const businessOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '课程', value: 'course' },
|
||||
{ label: '包场', value: 'private_booking' },
|
||||
{ label: '门票', value: 'ticket' },
|
||||
{ label: '会员卡', value: 'membership' },
|
||||
{ label: '储值卡', value: 'stored_value' }
|
||||
]
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
@@ -65,7 +47,6 @@ const statusOptions = [
|
||||
{ label: '已退款', value: 'refunded' }
|
||||
]
|
||||
|
||||
const businessType = ref('')
|
||||
const orderStatus = ref('')
|
||||
const orders = ref([])
|
||||
const page = ref(1)
|
||||
@@ -76,18 +57,6 @@ const isLoadingMore = ref(false)
|
||||
|
||||
const hasMore = computed(() => orders.value.length < total.value)
|
||||
|
||||
onMounted(() => loadOrders(true))
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
loadOrders(true).finally(() => uni.stopPullDownRefresh())
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
if (!hasMore.value || isLoadingMore.value) return
|
||||
page.value += 1
|
||||
loadOrders(false)
|
||||
})
|
||||
|
||||
const loadOrders = async (reset) => {
|
||||
if (reset) {
|
||||
page.value = 1
|
||||
@@ -99,7 +68,6 @@ const loadOrders = async (reset) => {
|
||||
const data = await orderApi.getOrders({
|
||||
page: page.value,
|
||||
pageSize,
|
||||
businessType: businessType.value,
|
||||
status: orderStatus.value
|
||||
})
|
||||
orders.value = reset ? data.items : [...orders.value, ...data.items]
|
||||
@@ -110,11 +78,6 @@ const loadOrders = async (reset) => {
|
||||
}
|
||||
}
|
||||
|
||||
const selectBusiness = (value) => {
|
||||
businessType.value = value
|
||||
loadOrders(true)
|
||||
}
|
||||
|
||||
const selectStatus = (value) => {
|
||||
orderStatus.value = value
|
||||
loadOrders(true)
|
||||
@@ -133,6 +96,18 @@ const closeOrder = async (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>
|
||||
@@ -141,34 +116,26 @@ const closeOrder = async (id) => {
|
||||
background: $bg-color-soft;
|
||||
}
|
||||
|
||||
.filter-panel {
|
||||
.filter-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
padding: $spacing-md;
|
||||
background: $bg-color-main;
|
||||
border-bottom: 1rpx solid $border-color;
|
||||
}
|
||||
|
||||
.filter-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-scroll.second {
|
||||
margin-top: $spacing-sm;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: inline-flex;
|
||||
gap: $spacing-sm;
|
||||
padding: $spacing-sm $spacing-xs;
|
||||
gap: $spacing-xs;
|
||||
}
|
||||
|
||||
.filter-chip {
|
||||
min-width: 118rpx;
|
||||
height: 62rpx;
|
||||
padding: 0 $spacing-sm;
|
||||
border-radius: $border-radius-base;
|
||||
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;
|
||||
@@ -183,21 +150,23 @@ const closeOrder = async (id) => {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.list {
|
||||
.order-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-md;
|
||||
padding: $spacing-md;
|
||||
}
|
||||
|
||||
.order-card {
|
||||
margin-bottom: $spacing-md;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.order-top,
|
||||
.order-bottom,
|
||||
.actions {
|
||||
.order-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $spacing-md;
|
||||
gap: $spacing-sm;
|
||||
}
|
||||
|
||||
.business {
|
||||
@@ -206,19 +175,51 @@ const closeOrder = async (id) => {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.order-no,
|
||||
.time {
|
||||
.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-no {
|
||||
margin-top: $spacing-sm;
|
||||
.order-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $spacing-md;
|
||||
margin-top: $spacing-md;
|
||||
}
|
||||
|
||||
.order-bottom {
|
||||
margin-top: $spacing-md;
|
||||
.time {
|
||||
color: $text-color-muted;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
@@ -228,14 +229,16 @@ const closeOrder = async (id) => {
|
||||
}
|
||||
|
||||
.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: 180rpx;
|
||||
width: 160rpx;
|
||||
height: 64rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user