| | |
| | | <template> |
| | | <view> |
| | | |
| | | <view class="order-page"> |
| | | <view class="order-page__nav" :style="{ paddingTop: statusBarHeight + 'px' }"> |
| | | <view class="order-page__nav-inner"> |
| | | <text class="order-page__nav-title">我的订单</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-page__tabs" :style="{ top: navHeight + 'px' }"> |
| | | <view v-for="tab in displayTabs" :key="tab.value" class="order-page__tab" :class="{ 'order-page__tab--active': activeTab === tab.value }" @click="activeTab = tab.value"> |
| | | <text class="order-page__tab-text">{{ tab.label }}</text> |
| | | <view v-if="activeTab === tab.value" class="order-page__tab-line"></view> |
| | | </view> |
| | | </view> |
| | | |
| | | <scroll-view class="order-page__body" scroll-y :style="bodyStyle"> |
| | | <view class="order-page__list"> |
| | | <view v-for="item in currentOrders" :key="item.id" class="order-card" @click="goToOrderDetail(item)"> |
| | | <view class="order-card__head"> |
| | | <view class="order-card__head-left"> |
| | | <image class="order-card__badge-icon" :src="getBadgeIcon(item.badge)" mode="widthFix"></image> |
| | | <text class="order-card__time-text">下单时间: {{ item.orderTime }}</text> |
| | | </view> |
| | | <text class="order-card__status" :class="{ 'order-card__status--highlight': item.actions && item.actions.length }">{{ item.statusText }}</text> |
| | | </view> |
| | | |
| | | <view class="order-card__route-item"> |
| | | <view class="order-card__point order-card__point--pickup">取</view> |
| | | <view class="order-card__route-texts"> |
| | | <text class="order-card__route-title">{{ item.pickupName }}</text> |
| | | <text class="order-card__route-desc">{{ item.pickupAddress }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-card__route-item order-card__route-item--delivery"> |
| | | <view class="order-card__point order-card__point--delivery">送</view> |
| | | <view class="order-card__route-texts"> |
| | | <text class="order-card__route-title">{{ item.deliveryName }}</text> |
| | | <text class="order-card__route-desc">{{ item.deliveryAddress }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-card__footer"> |
| | | <view class="order-card__arrival"> |
| | | <image class="order-card__clock" src="/static/image/ic_clock@2x.png" mode="aspectFit"></image> |
| | | <text class="order-card__arrival-text">{{ item.arriveLabel || '送达时间:' }}{{ item.arriveTime }}</text> |
| | | </view> |
| | | <view class="order-card__price-wrap"> |
| | | <text v-if="item.priceTag" class="order-card__price-tag">{{ item.priceTag }}</text> |
| | | <text class="order-card__price">{{ item.price }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="item.actions && item.actions.length" class="order-card__actions"> |
| | | <button |
| | | v-for="action in item.actions" |
| | | :key="action.text" |
| | | class="order-card__action-btn" |
| | | :class="['order-card__action-btn--' + action.type, { 'order-card__action-btn--primary-fill': action.fill }]" |
| | | hover-class="order-card__action-btn--hover" |
| | | > |
| | | {{ action.text }} |
| | | </button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | export default { |
| | | data() { |
| | | return { |
| | | |
| | | }; |
| | | statusBarHeight: 0, |
| | | navHeight: 0, |
| | | activeTab: 'all', |
| | | tabs: [ |
| | | { label: '全部', value: 'all' }, |
| | | { label: '待取货', value: 'pickup' }, |
| | | { label: '配送中', value: 'delivering' }, |
| | | { label: '已完成', value: 'finished' } |
| | | ], |
| | | orders: [ |
| | | { |
| | | id: 1, |
| | | type: 'pickup', |
| | | badge: '标速达', |
| | | badgeType: 'blue', |
| | | orderTime: '2026-04-12 12:09', |
| | | statusText: '待取货', |
| | | pickupName: '中铁快运南站旗舰店', |
| | | pickupAddress: '莲花路200号莲花产业园F栋401', |
| | | deliveryName: '佳苑巴黎都市3期10栋301室', |
| | | deliveryAddress: '洞庭湖路与湖北路交叉口西150米', |
| | | arriveLabel: '', |
| | | arriveTime: '45分钟内送达', |
| | | priceTag: '', |
| | | price: '¥20.5', |
| | | actions: [ |
| | | { text: '取消订单', type: 'light', fill: false }, |
| | | { text: '取货码', type: 'primary', fill: false }, |
| | | { text: '拍照取货', type: 'primary', fill: true } |
| | | ] |
| | | }, |
| | | { |
| | | id: 4, |
| | | type: 'pickup', |
| | | badge: '极速达', |
| | | badgeType: 'red', |
| | | orderTime: '2026-04-12 12:33', |
| | | statusText: '待取货', |
| | | pickupName: '中铁快运南站旗舰店', |
| | | pickupAddress: '莲花路200号莲花产业园F栋401', |
| | | deliveryName: '佳苑巴黎都市3期10栋301室', |
| | | deliveryAddress: '洞庭湖路与湖北路交叉口西150米', |
| | | arriveLabel: '', |
| | | arriveTime: '50分钟内送达', |
| | | priceTag: '', |
| | | price: '¥20.5', |
| | | actions: [ |
| | | { text: '取消订单', type: 'light', fill: false }, |
| | | { text: '取货码', type: 'primary', fill: false }, |
| | | { text: '拍照取货', type: 'primary', fill: true } |
| | | ] |
| | | }, |
| | | { |
| | | id: 2, |
| | | type: 'delivering', |
| | | badge: '极速达', |
| | | badgeType: 'red', |
| | | orderTime: '2026-04-12 12:33', |
| | | statusText: '配送中', |
| | | pickupName: '中铁快运南站旗舰店', |
| | | pickupAddress: '莲花路200号莲花产业园F栋401', |
| | | deliveryName: '佳苑巴黎都市3期10栋301室', |
| | | deliveryAddress: '洞庭湖路与湖北路交叉口西150米', |
| | | arriveLabel: '送达时间:', |
| | | arriveTime: '04-12 12:58', |
| | | priceTag: '', |
| | | price: '¥20.5' |
| | | }, |
| | | { |
| | | id: 5, |
| | | type: 'rated', |
| | | badge: '极速达', |
| | | badgeType: 'red', |
| | | orderTime: '2026-04-12 13:08', |
| | | statusText: '已评价', |
| | | pickupName: '中铁快运南站旗舰店', |
| | | pickupAddress: '莲花路200号莲花产业园F栋401', |
| | | deliveryName: '佳苑巴黎都市3期10栋301室', |
| | | deliveryAddress: '洞庭湖路与湖北路交叉口西150米', |
| | | arriveLabel: '送达时间:', |
| | | arriveTime: '04-12 13:36', |
| | | priceTag: '', |
| | | price: '¥18.8' |
| | | }, |
| | | { |
| | | id: 6, |
| | | type: 'cancelled', |
| | | badge: '标速达', |
| | | badgeType: 'blue', |
| | | orderTime: '2026-04-12 13:18', |
| | | statusText: '已取消', |
| | | pickupName: '中铁快运南站旗舰店', |
| | | pickupAddress: '莲花路200号莲花产业园F栋401', |
| | | deliveryName: '佳苑巴黎都市3期10栋301室', |
| | | deliveryAddress: '洞庭湖路与湖北路交叉口西150米', |
| | | arriveLabel: '送达时间:', |
| | | arriveTime: '04-12 13:52', |
| | | priceTag: '', |
| | | price: '¥16.5' |
| | | }, |
| | | { |
| | | id: 3, |
| | | type: 'finished', |
| | | badge: '极速达', |
| | | badgeType: 'red', |
| | | orderTime: '2026-04-12 12:33', |
| | | statusText: '已完成', |
| | | pickupName: '中铁快运南站旗舰店', |
| | | pickupAddress: '莲花路200号莲花产业园F栋401', |
| | | deliveryName: '佳苑巴黎都市3期10栋301室', |
| | | deliveryAddress: '洞庭湖路与湖北路交叉口西150米', |
| | | arriveLabel: '送达时间:', |
| | | arriveTime: '04-12 12:58', |
| | | priceTag: '已结算', |
| | | price: '¥20.5' |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | computed: { |
| | | displayTabs() { |
| | | const countMap = { |
| | | pickup: this.orders.filter((item) => item.type === 'pickup').length, |
| | | delivering: this.orders.filter((item) => item.type === 'delivering').length, |
| | | finished: this.orders.filter((item) => item.type === 'finished').length |
| | | } |
| | | |
| | | return this.tabs.map((tab) => { |
| | | if (!countMap[tab.value]) { |
| | | return tab |
| | | } |
| | | |
| | | return { |
| | | ...tab, |
| | | label: `${tab.label} ${countMap[tab.value]}` |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | bodyStyle() { |
| | | return { |
| | | marginTop: this.navHeight + uni.upx2px(88) + 'px', |
| | | height: `calc(100vh - ${this.navHeight + uni.upx2px(88)}px)` |
| | | } |
| | | }, |
| | | currentOrders() { |
| | | if (this.activeTab === 'all') { |
| | | return this.orders |
| | | } |
| | | |
| | | return this.orders.filter((item) => item.type === this.activeTab) |
| | | } |
| | | }, |
| | | onLoad() { |
| | | const systemInfo = uni.getSystemInfoSync() |
| | | this.statusBarHeight = systemInfo.statusBarHeight || 0 |
| | | this.navHeight = this.statusBarHeight + uni.upx2px(88) |
| | | }, |
| | | methods: { |
| | | getBadgeIcon(badge) { |
| | | const badgeMap = { |
| | | '极速达': '/static/image/ic_jisuda@2x.png', |
| | | '标速达': '/static/image/ic_biaosuda@2x.png' |
| | | } |
| | | |
| | | return badgeMap[badge] || '' |
| | | }, |
| | | goToOrderDetail(item) { |
| | | uni.navigateTo({ |
| | | url: `/pages/order-detail/order-detail?id=${item.id}&status=${item.type === 'delivering' ? 'delivering' : item.type === 'finished' ? 'finished' : item.type === 'cancelled' ? 'cancelled' : item.type === 'rated' ? 'rated' : 'pickup'}` |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | <style lang="scss" scoped> |
| | | .order-page { |
| | | height: 100vh; |
| | | background: #f5f7fb; |
| | | overflow: hidden; |
| | | |
| | | </style> |
| | | &__nav { |
| | | position: fixed; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | z-index: 10; |
| | | background: linear-gradient(180deg, #1f73f6 0%, #1b6cf2 100%); |
| | | } |
| | | |
| | | &__nav-inner { |
| | | height: 88rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 28rpx; |
| | | } |
| | | |
| | | &__nav-title { |
| | | font-size: 38rpx; |
| | | font-weight: 700; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | &__tabs { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | z-index: 9; |
| | | height: 88rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | background: #ffffff; |
| | | box-shadow: 0 10rpx 20rpx rgba(40, 72, 128, 0.04); |
| | | } |
| | | |
| | | &__tab { |
| | | position: relative; |
| | | flex: 1; |
| | | height: 100%; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | &--active { |
| | | .order-page__tab-text { |
| | | color: #272b33; |
| | | font-weight: 700; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &__tab-text { |
| | | font-size: 30rpx; |
| | | color: #8f96a3; |
| | | } |
| | | |
| | | &__tab-line { |
| | | position: absolute; |
| | | left: 26rpx; |
| | | right: 26rpx; |
| | | bottom: 0; |
| | | height: 4rpx; |
| | | border-radius: 999rpx; |
| | | background: #1a73f8; |
| | | } |
| | | |
| | | &__body { |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | &__list { |
| | | padding: 18rpx 22rpx calc(env(safe-area-inset-bottom) + 26rpx); |
| | | } |
| | | } |
| | | |
| | | .order-card { |
| | | margin-bottom: 18rpx; |
| | | padding: 20rpx 18rpx 18rpx; |
| | | border-radius: 20rpx; |
| | | background: #ffffff; |
| | | box-shadow: 0 8rpx 20rpx rgba(43, 65, 106, 0.05); |
| | | |
| | | &__head { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | &__head-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12rpx; |
| | | min-width: 0; |
| | | } |
| | | |
| | | &__badge { |
| | | padding: 4rpx 10rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 22rpx; |
| | | line-height: 1.2; |
| | | font-weight: 600; |
| | | |
| | | &--blue { |
| | | border: 1rpx solid #75cfff; |
| | | color: #27a8f8; |
| | | background: #eefaff; |
| | | } |
| | | |
| | | &--red { |
| | | border: 1rpx solid #ff8f8f; |
| | | color: #ff5d5d; |
| | | background: #fff1f1; |
| | | } |
| | | } |
| | | |
| | | &__badge-icon { |
| | | width: 108rpx; |
| | | height: 40rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | &__time-text, |
| | | &__status, |
| | | &__route-desc, |
| | | &__arrival-text { |
| | | font-size: 24rpx; |
| | | color: #a1a7b2; |
| | | } |
| | | |
| | | &__status { |
| | | flex-shrink: 0; |
| | | |
| | | &--highlight { |
| | | color: #ff4a3d; |
| | | font-weight: 700; |
| | | } |
| | | } |
| | | |
| | | &__route-item { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | margin-top: 22rpx; |
| | | |
| | | &--delivery { |
| | | margin-top: 20rpx; |
| | | } |
| | | } |
| | | |
| | | &__point { |
| | | width: 34rpx; |
| | | height: 34rpx; |
| | | line-height: 34rpx; |
| | | text-align: center; |
| | | border-radius: 50%; |
| | | font-size: 22rpx; |
| | | font-weight: 700; |
| | | color: #ffffff; |
| | | flex-shrink: 0; |
| | | margin-right: 16rpx; |
| | | |
| | | &--pickup { |
| | | background: #2ab8ff; |
| | | } |
| | | |
| | | &--delivery { |
| | | background: #ff9d2e; |
| | | } |
| | | } |
| | | |
| | | &__route-texts { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | &__route-title { |
| | | display: block; |
| | | font-size: 34rpx; |
| | | font-weight: 700; |
| | | color: #2d3139; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | &__route-desc { |
| | | display: block; |
| | | margin-top: 8rpx; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | &__footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-top: 22rpx; |
| | | padding-top: 16rpx; |
| | | border-top: 1rpx solid #f0f2f6; |
| | | } |
| | | |
| | | &__arrival { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | } |
| | | |
| | | &__clock { |
| | | width: 24rpx; |
| | | height: 24rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | &__price-wrap { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | } |
| | | |
| | | &__price-tag { |
| | | padding: 3rpx 8rpx; |
| | | border-radius: 8rpx; |
| | | border: 1rpx solid #ff8f8f; |
| | | font-size: 22rpx; |
| | | font-weight: 600; |
| | | color: #ff6a6a; |
| | | background: #fff4f4; |
| | | } |
| | | |
| | | &__price { |
| | | font-size: 40rpx; |
| | | font-weight: 700; |
| | | color: #ff4030; |
| | | } |
| | | |
| | | &__actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | margin-top: 18rpx; |
| | | padding-top: 18rpx; |
| | | border-top: 1rpx solid #f0f2f6; |
| | | } |
| | | |
| | | &__action-btn { |
| | | width: 160rpx; |
| | | height: 64rpx; |
| | | line-height: 64rpx; |
| | | padding: 0; |
| | | border-radius: 34rpx; |
| | | font-size: 28rpx; |
| | | font-weight: 500; |
| | | border: 1rpx solid transparent; |
| | | background: #ffffff; |
| | | box-sizing: border-box; |
| | | |
| | | &::after { |
| | | border: 0; |
| | | } |
| | | |
| | | &--light { |
| | | border-color: #d7dbe3; |
| | | color: #8f96a3; |
| | | } |
| | | |
| | | &--primary { |
| | | border-color: #2c7cff; |
| | | color: #2c7cff; |
| | | } |
| | | |
| | | &--primary-fill { |
| | | background: #2c7cff; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | &--hover { |
| | | opacity: 0.92; |
| | | } |
| | | } |
| | | } |
| | | </style> |