| | |
| | | <template> |
| | | <view> |
| | | <view class="itinerary-page"> |
| | | <view class="top-fixed"> |
| | | <view class="top-gradient"></view> |
| | | <view class="top-inner"> |
| | | <view :style="{ height: statusbarHeight + 'px' }"></view> |
| | | <view class="header-bar" :style="{ height: navHeight + 'px' }"> |
| | | <text class="header-title">我的行程</text> |
| | | </view> |
| | | <scroll-view scroll-x class="tabs-row page-padding" style="padding: 0 0 0 30rpx !important;" show-scrollbar="false"> |
| | | <view class="tabs-inner"> |
| | | <view |
| | | v-for="item in filterTabs" |
| | | :key="item.value" |
| | | class="filter-tab" |
| | | :class="{ active: activeTab === item.value }" |
| | | @tap="activeTab = item.value" |
| | | > |
| | | {{ item.label }} |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="content-wrap"> |
| | | <view class="page-padding card-list"> |
| | | <view v-for="(item, index) in visibleOrders" :key="item.id" class="order-card"> |
| | | <view class="order-head" :class="item.mode === 'city' ? 'city-head-bg' : 'local-head-bg'"> |
| | | <view v-if="item.mode === 'local'" class="head-local"> |
| | | <view class="mode-tag local-tag">就地寄存</view> |
| | | <view class="head-copy single-copy"> |
| | | <text class="head-name text-ellipsis">{{ item.fromName }}</text> |
| | | <text class="head-user">{{ item.userName }}</text> |
| | | </view> |
| | | </view> |
| | | <view v-else class="head-city"> |
| | | <view class="head-copy city-left"> |
| | | <view class="mode-tag city-tag">同城寄送</view> |
| | | <text class="head-name text-ellipsis">{{ item.fromName }}</text> |
| | | <text class="head-user">{{ item.userName }}</text> |
| | | </view> |
| | | <view class="city-arrow"> |
| | | <view class="arrow-line"></view> |
| | | <view class="arrow-head"></view> |
| | | </view> |
| | | <view class="head-copy city-right align-right"> |
| | | <text class="status-text" v-if="index === 1">{{ item.status }}</text> |
| | | <text class="head-name text-ellipsis">{{ item.toName }}</text> |
| | | <text class="head-user">{{ item.userName }}</text> |
| | | </view> |
| | | </view> |
| | | <text class="status-text" v-if="index === 0">{{ item.status }}</text> |
| | | </view> |
| | | |
| | | <view class="goods-area"> |
| | | <view v-for="goods in item.goods" :key="goods.name" class="goods-row"> |
| | | <view class="goods-left"> |
| | | <text class="goods-name">{{ goods.name }}</text> |
| | | <text class="goods-size">{{ goods.size }}</text> |
| | | </view> |
| | | <view class="goods-right"> |
| | | <text class="goods-price">{{ goods.price }}</text> |
| | | <text class="goods-count">x1</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="amount-area"> |
| | | <view class="pay-row"> |
| | | <text class="pay-label">实付款:</text> |
| | | <text class="pay-value">{{ item.payAmount }}</text> |
| | | </view> |
| | | <view class="insurance-row"> |
| | | <text class="insurance-label">含行李保费:</text> |
| | | <text class="insurance-value">{{ item.insurance }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="card-footer"> |
| | | <view class="pickup-wrap" v-if="item.pickupTime"> |
| | | <text class="pickup-label">预计取件时间:</text> |
| | | <text class="pickup-value">{{ item.pickupTime }}</text> |
| | | </view> |
| | | <view class="pickup-wrap" v-else></view> |
| | | <view class="footer-actions"> |
| | | <view class="footer-btn contact-btn">联系门店</view> |
| | | <view class="footer-btn primary-btn">{{ item.actionText }}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="isLoadingMore" class="loading-text">加载中...</view> |
| | | <view v-else-if="!hasMore && visibleOrders.length" class="loading-text">没有更多了</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapState } from 'vuex' |
| | | |
| | | const baseOrders = [ |
| | | { |
| | | id: 1, |
| | | mode: 'local', |
| | | status: '待核验', |
| | | statusKey: 'verify', |
| | | fromName: '中铁快运南站旗舰店', |
| | | userName: '蔡子瑄', |
| | | goods: [ |
| | | { name: '大件行李箱', size: '24-28寸', price: '¥35' }, |
| | | { name: '中件行李箱', size: '24-28寸', price: '¥35' }, |
| | | { name: '小件行李箱', size: '24-28寸', price: '¥35' } |
| | | ], |
| | | payAmount: '¥125.00', |
| | | insurance: '¥20', |
| | | pickupTime: '2026-04-12 10:10', |
| | | actionText: '申请退款' |
| | | }, |
| | | { |
| | | id: 2, |
| | | mode: 'city', |
| | | status: '待核验', |
| | | statusKey: 'verify', |
| | | fromName: '中铁快运南站旗舰店', |
| | | toName: '丽枫酒店34...', |
| | | userName: '蔡子瑄', |
| | | goods: [ |
| | | { name: '大件行李箱', size: '24-28寸', price: '¥35' }, |
| | | { name: '中件行李箱', size: '24-28寸', price: '¥35' } |
| | | ], |
| | | payAmount: '¥90.00', |
| | | insurance: '¥20', |
| | | pickupTime: '', |
| | | actionText: '申请退款' |
| | | } |
| | | ] |
| | | |
| | | export default { |
| | | onReachBottom() { |
| | | this.loadMore() |
| | | }, |
| | | computed: { |
| | | ...mapState(['navHeight', 'statusbarHeight']), |
| | | filteredOrders() { |
| | | if (this.activeTab === 'all') { |
| | | return this.orders |
| | | } |
| | | return this.orders.filter(item => item.statusKey === this.activeTab) |
| | | }, |
| | | visibleOrders() { |
| | | return this.filteredOrders.slice(0, this.pageSize) |
| | | }, |
| | | hasMore() { |
| | | return this.pageSize < this.filteredOrders.length |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | |
| | | }; |
| | | activeTab: 'all', |
| | | pageSize: 4, |
| | | isLoadingMore: false, |
| | | filterTabs: [ |
| | | { label: '全部', value: 'all' }, |
| | | { label: '待支付', value: 'pending' }, |
| | | { label: '待核验', value: 'verify' }, |
| | | { label: '待配送', value: 'delivery' }, |
| | | { label: '待收货', value: 'receive' }, |
| | | { label: '待核验', value: 'verify' }, |
| | | { label: '待配送', value: 'delivery' }, |
| | | { label: '待收货', value: 'receive' } |
| | | ], |
| | | orders: [ |
| | | ...baseOrders, |
| | | ...baseOrders.map((item, index) => ({ ...item, id: item.id + (index + 1) * 10 })), |
| | | ...baseOrders.map((item, index) => ({ ...item, id: item.id + (index + 1) * 20 })) |
| | | ] |
| | | } |
| | | }, |
| | | watch: { |
| | | activeTab() { |
| | | this.pageSize = 4 |
| | | } |
| | | }, |
| | | methods: { |
| | | loadMore() { |
| | | if (!this.hasMore || this.isLoadingMore) { |
| | | return |
| | | } |
| | | this.isLoadingMore = true |
| | | setTimeout(() => { |
| | | this.pageSize += 2 |
| | | this.isLoadingMore = false |
| | | }, 300) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | <style lang="scss" scoped> |
| | | .itinerary-page { |
| | | min-height: 100vh; |
| | | background: #f7f7f7; |
| | | } |
| | | |
| | | .top-fixed { |
| | | position: sticky; |
| | | left: 0; |
| | | top: 0; |
| | | width: 100%; |
| | | z-index: 20; |
| | | } |
| | | |
| | | .top-gradient { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: linear-gradient(90deg, #1ba8fa 0%, #73e5cf 100%); |
| | | } |
| | | |
| | | .top-inner { |
| | | position: relative; |
| | | z-index: 1; |
| | | padding-bottom: 18rpx; |
| | | } |
| | | |
| | | .page-padding { |
| | | padding: 0 30rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .header-bar { |
| | | padding: 0 30rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .header-title { |
| | | font-size: 44rpx; |
| | | font-weight: 600; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .tabs-row { |
| | | margin-top: 16rpx; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .tabs-inner { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 12rpx; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .filter-tab { |
| | | flex-shrink: 0; |
| | | height: 50rpx; |
| | | padding: 0 20rpx; |
| | | border-radius: 25rpx; |
| | | background: #ffffff; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 24rpx; |
| | | font-weight: 500; |
| | | color: #81858d; |
| | | } |
| | | |
| | | .filter-tab.active { |
| | | color: #19adf8; |
| | | } |
| | | |
| | | .content-wrap { |
| | | position: relative; |
| | | z-index: 1; |
| | | padding: 30rpx 0; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .order-card { |
| | | margin-bottom: 20rpx; |
| | | background: #ffffff; |
| | | border-radius: 14rpx; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .order-head { |
| | | padding: 24rpx 30rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .local-head-bg { |
| | | background: #eff9ff; |
| | | } |
| | | |
| | | .city-head-bg { |
| | | background: #fff4e8; |
| | | } |
| | | |
| | | .head-local, |
| | | .head-city { |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .head-copy { |
| | | min-width: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .single-copy, |
| | | .city-left, |
| | | .city-right { |
| | | flex: 1; |
| | | } |
| | | |
| | | .align-right { |
| | | align-items: flex-end; |
| | | text-align: right; |
| | | } |
| | | |
| | | .mode-tag { |
| | | width: 112rpx; |
| | | height: 38rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-weight: 400; |
| | | font-size: 24rpx; |
| | | color: #FFFFFF; |
| | | margin-right: 14rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .local-tag { |
| | | background: linear-gradient(90deg, #18abf8 0%, #39c5ff 100%); |
| | | } |
| | | |
| | | .city-tag { |
| | | background: linear-gradient(90deg, #ff8b28 0%, #ffb14f 100%); |
| | | margin-right: 0; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .head-name { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-weight: 600; |
| | | font-size: 30rpx; |
| | | color: #222222; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .head-user { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-top: 10rpx; |
| | | font-weight: 400; |
| | | font-size: 24rpx; |
| | | color: #666666; |
| | | } |
| | | |
| | | .text-ellipsis { |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | .city-arrow { |
| | | width: 82rpx; |
| | | margin: 0 54rpx; |
| | | position: relative; |
| | | height: 24rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .arrow-line { |
| | | position: absolute; |
| | | left: 8rpx; |
| | | top: 8rpx; |
| | | width: 42rpx; |
| | | height: 0; |
| | | border-top: 3rpx solid #ff8c1f; |
| | | } |
| | | |
| | | .arrow-head { |
| | | position: absolute; |
| | | right: 8rpx; |
| | | top: 3rpx; |
| | | width: 12rpx; |
| | | height: 12rpx; |
| | | border-top: 3rpx solid #ff8c1f; |
| | | border-right: 3rpx solid #ff8c1f; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .status-text { |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #10B2FA; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .goods-area { |
| | | padding: 22rpx 30rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .goods-row { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | justify-content: space-between; |
| | | margin-bottom: 26rpx; |
| | | &:last-child { |
| | | margin: 0 !important; |
| | | } |
| | | } |
| | | |
| | | .goods-left { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .goods-name { |
| | | display: block; |
| | | font-weight: 600; |
| | | font-size: 28rpx; |
| | | color: #333333; |
| | | } |
| | | |
| | | .goods-size { |
| | | display: block; |
| | | margin-top: 12rpx; |
| | | font-weight: 400; |
| | | font-size: 24rpx; |
| | | color: #8C939F; |
| | | } |
| | | |
| | | .goods-right { |
| | | width: 92rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-end; |
| | | } |
| | | |
| | | .goods-price { |
| | | font-weight: 400; |
| | | font-size: 30rpx; |
| | | color: #333333; |
| | | } |
| | | |
| | | .goods-count { |
| | | margin-top: 12rpx; |
| | | font-weight: 400; |
| | | font-size: 24rpx; |
| | | color: #8C939F; |
| | | } |
| | | |
| | | .amount-area { |
| | | padding: 0 30rpx; |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-end; |
| | | } |
| | | |
| | | .pay-row, |
| | | .insurance-row { |
| | | display: flex; |
| | | align-items: baseline; |
| | | } |
| | | |
| | | .pay-label, |
| | | .insurance-label { |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #333333; |
| | | } |
| | | |
| | | .pay-value { |
| | | font-weight: 600; |
| | | font-size: 26rpx; |
| | | color: #222222; |
| | | } |
| | | |
| | | .insurance-row { |
| | | margin-top: 12rpx; |
| | | } |
| | | |
| | | .insurance-value { |
| | | font-size: 20rpx; |
| | | color: #999999; |
| | | } |
| | | |
| | | .card-footer { |
| | | padding: 0 30rpx 30rpx 30rpx; |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-top: 30rpx; |
| | | } |
| | | |
| | | .pickup-wrap { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .pickup-label { |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #333333; |
| | | } |
| | | |
| | | .pickup-value { |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #10B2FA; |
| | | } |
| | | |
| | | .footer-actions { |
| | | margin-left: auto; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | .footer-btn { |
| | | height: 64rpx; |
| | | border-radius: 34rpx; |
| | | padding: 0 18rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 28rpx; |
| | | box-sizing: border-box; |
| | | margin-right: 20rpx; |
| | | &:last-child { |
| | | margin: 0 !important; |
| | | } |
| | | } |
| | | |
| | | .contact-btn { |
| | | border: 1rpx solid #B2B2B2; |
| | | background: #ffffff; |
| | | color: #666666; |
| | | } |
| | | |
| | | .primary-btn { |
| | | background: #10B2FA; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .loading-text { |
| | | padding: 18rpx 0 8rpx; |
| | | text-align: center; |
| | | font-size: 22rpx; |
| | | color: #a5aab3; |
| | | } |
| | | </style> |