|
|
@@ -1,16 +1,35 @@
|
|
|
<template>
|
|
|
<view class="container">
|
|
|
- <view class="status-tabs">
|
|
|
+ <!-- 搜索栏 -->
|
|
|
+ <view class="search-bar">
|
|
|
+ <view class="search-input-wrap">
|
|
|
+ <text class="search-icon">🔍</text>
|
|
|
+ <input
|
|
|
+ class="search-input"
|
|
|
+ v-model="keyword"
|
|
|
+ placeholder="搜索商品名称/订单号"
|
|
|
+ placeholder-class="search-placeholder"
|
|
|
+ @confirm="onSearch"
|
|
|
+ confirm-type="search"
|
|
|
+ />
|
|
|
+ <text v-if="keyword" class="search-clear" @click="clearSearch">✕</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 状态 Tabs -->
|
|
|
+ <scroll-view scroll-x class="status-tabs" enhanced :show-scrollbar="false">
|
|
|
<view
|
|
|
v-for="item in statusList"
|
|
|
:key="item.value"
|
|
|
:class="['tab', currentStatus === item.value ? 'active' : '']"
|
|
|
@click="onStatusChange(item.value)"
|
|
|
>
|
|
|
- {{ item.label }}
|
|
|
+ <text class="tab-label">{{ item.label }}</text>
|
|
|
+ <text v-if="statusCounts[item.value] > 0" class="tab-badge">{{ formatCount(statusCounts[item.value]) }}</text>
|
|
|
</view>
|
|
|
- </view>
|
|
|
+ </scroll-view>
|
|
|
|
|
|
+ <!-- 订单列表 -->
|
|
|
<scroll-view
|
|
|
scroll-y
|
|
|
class="order-list"
|
|
|
@@ -24,7 +43,8 @@
|
|
|
</view>
|
|
|
<view v-else-if="orders.length === 0" class="empty-wrap">
|
|
|
<text class="empty-icon">📦</text>
|
|
|
- <text class="empty-text">暂无订单</text>
|
|
|
+ <text class="empty-text">{{ keyword ? '未找到相关订单' : '暂无订单' }}</text>
|
|
|
+ <text v-if="keyword" class="empty-hint">试试其他关键词</text>
|
|
|
</view>
|
|
|
<view v-else>
|
|
|
<view
|
|
|
@@ -33,47 +53,49 @@
|
|
|
class="order-card"
|
|
|
@click="goDetail(item.orderNo)"
|
|
|
>
|
|
|
- <view class="order-header">
|
|
|
- <text class="order-no">{{ item.orderNo }}</text>
|
|
|
- <text :class="['status-badge', 'status-' + item.status]">{{ statusLabel(item.status) }}</text>
|
|
|
- </view>
|
|
|
- <view class="order-body">
|
|
|
- <view class="product-info">
|
|
|
- <text class="product-name">{{ item.productName }}</text>
|
|
|
- <text class="product-type">{{ typeLabel(item.productType) }}</text>
|
|
|
- </view>
|
|
|
- <view class="order-meta">
|
|
|
- <text class="order-amount">{{ formatPriceWithSymbol(item.totalAmount) }}</text>
|
|
|
- <text class="order-qty">x{{ item.quantity }}</text>
|
|
|
+ <view class="card-body">
|
|
|
+ <image
|
|
|
+ class="product-img"
|
|
|
+ :src="item.coverImage || defaultCover(item.productType)"
|
|
|
+ mode="aspectFill"
|
|
|
+ @error="onImgError($event, item)"
|
|
|
+ />
|
|
|
+ <view class="product-section">
|
|
|
+ <view class="product-header">
|
|
|
+ <text class="product-name">{{ item.productName }}</text>
|
|
|
+ <text :class="['status-badge', 'status-' + getDisplayStatus(item)]">
|
|
|
+ {{ getDisplayStatusText(item) }}
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
+ <view class="product-meta">
|
|
|
+ <view :class="['type-tag', 'type-' + item.productType]">
|
|
|
+ <text class="type-icon">{{ typeIcon(item.productType) }}</text>
|
|
|
+ <text class="type-text">{{ typeLabel(item.productType) }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="order-no">No.{{ item.orderNo }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="price-row">
|
|
|
+ <text class="price">{{ formatPriceWithSymbol(item.totalAmount) }}</text>
|
|
|
+ <text class="qty">x{{ item.quantity }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <view class="order-footer">
|
|
|
- <text class="order-time">{{ formatTime(item.createdAt) }}</text>
|
|
|
- <view class="order-actions">
|
|
|
- <button
|
|
|
- v-if="item.status === 'pending'"
|
|
|
- class="btn-small btn-cancel"
|
|
|
- @click.stop="onCancel(item.orderNo)"
|
|
|
- >取消</button>
|
|
|
- <button
|
|
|
- v-if="item.status === 'pending'"
|
|
|
- class="btn-small btn-pay"
|
|
|
- @click.stop="onPay(item.orderNo, item.totalAmount / 100)"
|
|
|
- >支付</button>
|
|
|
- <button
|
|
|
- v-if="item.status === 'shipped'"
|
|
|
- class="btn-small btn-confirm"
|
|
|
- @click.stop="onConfirm(item.orderNo)"
|
|
|
- >确认收货</button>
|
|
|
- <button
|
|
|
- v-if="(item.status === 'paid' || item.status === 'shipped') && item.refundStatus !== 1 && item.refundStatus !== 2"
|
|
|
- class="btn-small btn-refund"
|
|
|
- @click.stop="goAfterSales(item.id, item.orderNo)"
|
|
|
- >申请退款</button>
|
|
|
+
|
|
|
+ <view class="card-footer">
|
|
|
+ <text class="order-time" :title="formatFullTime(item.createdAt)">
|
|
|
+ {{ formatRelativeTime(item.createdAt) }}
|
|
|
+ </text>
|
|
|
+ <view class="order-actions" @click.stop>
|
|
|
+ <button v-if="item.status === 'pending'" class="btn-sm btn-cancel" @click="onCancel(item.orderNo)">取消</button>
|
|
|
+ <button v-if="item.status === 'pending'" class="btn-sm btn-pay" @click="onPay(item.orderNo, item.totalAmount / 100)">去支付</button>
|
|
|
+ <button v-if="item.status === 'paid'" class="btn-sm btn-refund" @click="goAfterSales(item.id, item.orderNo)">申请退款</button>
|
|
|
+ <button v-if="item.status === 'shipped'" class="btn-sm btn-confirm" @click="onConfirm(item.orderNo)">确认收货</button>
|
|
|
+ <button v-if="item.status === 'completed'" class="btn-sm btn-rebuy" @click="goDetail(item.orderNo)">再次购买</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+
|
|
|
<view v-if="loadingMore" class="loading-more">
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
</view>
|
|
|
@@ -85,11 +107,21 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { productOrderMy, productOrderCancel, productOrderConfirm, productOrderRefundApply } from '@/utils/api.js'
|
|
|
+import { productOrderMy, productOrderCounts, productOrderCancel, productOrderConfirm } from '@/utils/api.js'
|
|
|
+
|
|
|
+var TYPE_ICONS = { activity: '🎯', course: '📖', physical: '📦', digital: '💎', service: '🎓' }
|
|
|
+var DEFAULT_COVER = {
|
|
|
+ activity: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 160 160%22%3E%3Crect fill=%22%23FFF7E6%22 width=%22160%22 height=%22160%22/%3E%3Ctext x=%2280%22 y=%2290%22 text-anchor=%22middle%22 font-size=%2260%22%3E🎯%3C/text%3E%3C/svg%3E',
|
|
|
+ course: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 160 160%22%3E%3Crect fill=%22%23EDE9FE%22 width=%22160%22 height=%22160%22/%3E%3Ctext x=%2280%22 y=%2290%22 text-anchor=%22middle%22 font-size=%2260%22%3E📖%3C/text%3E%3C/svg%3E',
|
|
|
+ physical: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 160 160%22%3E%3Crect fill=%22%23E0F2FE%22 width=%22160%22 height=%22160%22/%3E%3Ctext x=%2280%22 y=%2290%22 text-anchor=%22middle%22 font-size=%2260%22%3E📦%3C/text%3E%3C/svg%3E',
|
|
|
+ digital: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 160 160%22%3E%3Crect fill=%22%23F0FDF4%22 width=%22160%22 height=%22160%22/%3E%3Ctext x=%2280%22 y=%2290%22 text-anchor=%22middle%22 font-size=%2260%22%3E💎%3C/text%3E%3C/svg%3E',
|
|
|
+ service: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 160 160%22%3E%3Crect fill=%22%23FEF3C7%22 width=%22160%22 height=%22160%22/%3E%3Ctext x=%2280%22 y=%2290%22 text-anchor=%22middle%22 font-size=%2260%22%3E🎓%3C/text%3E%3C/svg%3E'
|
|
|
+}
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
+ keyword: '',
|
|
|
statusList: [
|
|
|
{ label: '全部', value: '' },
|
|
|
{ label: '待支付', value: 'pending' },
|
|
|
@@ -99,93 +131,130 @@ export default {
|
|
|
{ label: '退款中', value: 'refunding' }
|
|
|
],
|
|
|
currentStatus: '',
|
|
|
+ statusCounts: { total: 0, pending: 0, paid: 0, shipped: 0, completed: 0, refunding: 0 },
|
|
|
orders: [],
|
|
|
page: 1,
|
|
|
size: 20,
|
|
|
loading: false,
|
|
|
loadingMore: false,
|
|
|
refreshing: false,
|
|
|
- noMore: false
|
|
|
+ noMore: false,
|
|
|
+ searchTimer: null
|
|
|
}
|
|
|
},
|
|
|
onLoad() {
|
|
|
+ this.loadCounts()
|
|
|
this.loadOrders()
|
|
|
},
|
|
|
methods: {
|
|
|
- onStatusChange(value) {
|
|
|
- this.currentStatus = value
|
|
|
- this.page = 1
|
|
|
- this.orders = []
|
|
|
- this.noMore = false
|
|
|
- this.loadOrders()
|
|
|
+ loadCounts() {
|
|
|
+ var self = this
|
|
|
+ productOrderCounts().then(function(res) {
|
|
|
+ if (res.code === 200 && res.data) {
|
|
|
+ self.statusCounts = res.data
|
|
|
+ }
|
|
|
+ }).catch(function() {})
|
|
|
},
|
|
|
loadOrders() {
|
|
|
if (this.loading) return
|
|
|
this.loading = true
|
|
|
- const params = { page: this.page, size: this.size }
|
|
|
+ var params = { page: this.page, size: this.size }
|
|
|
if (this.currentStatus) {
|
|
|
params.status = this.currentStatus
|
|
|
}
|
|
|
- productOrderMy(params).then(res => {
|
|
|
- this.loading = false
|
|
|
- this.refreshing = false
|
|
|
+ if (this.keyword && this.keyword.trim()) {
|
|
|
+ params.keyword = this.keyword.trim()
|
|
|
+ }
|
|
|
+ var self = this
|
|
|
+ productOrderMy(params).then(function(res) {
|
|
|
+ self.loading = false
|
|
|
+ self.refreshing = false
|
|
|
if (res.code === 200 && res.data) {
|
|
|
- const list = res.data || []
|
|
|
- if (this.page === 1) {
|
|
|
- this.orders = list
|
|
|
+ var list = res.data || []
|
|
|
+ if (self.page === 1) {
|
|
|
+ self.orders = list
|
|
|
} else {
|
|
|
- this.orders = this.orders.concat(list)
|
|
|
+ self.orders = self.orders.concat(list)
|
|
|
}
|
|
|
- this.noMore = list.length < this.size
|
|
|
+ self.noMore = list.length < self.size
|
|
|
}
|
|
|
- }).catch(() => {
|
|
|
- this.loading = false
|
|
|
- this.refreshing = false
|
|
|
+ }).catch(function() {
|
|
|
+ self.loading = false
|
|
|
+ self.refreshing = false
|
|
|
})
|
|
|
},
|
|
|
onRefresh() {
|
|
|
this.refreshing = true
|
|
|
this.page = 1
|
|
|
this.noMore = false
|
|
|
+ this.loadCounts()
|
|
|
this.loadOrders()
|
|
|
},
|
|
|
onLoadMore() {
|
|
|
if (this.noMore || this.loadingMore) return
|
|
|
this.page = this.page + 1
|
|
|
this.loadingMore = true
|
|
|
- const params = { page: this.page, size: this.size }
|
|
|
+ var params = { page: this.page, size: this.size }
|
|
|
if (this.currentStatus) {
|
|
|
params.status = this.currentStatus
|
|
|
}
|
|
|
- productOrderMy(params).then(res => {
|
|
|
- this.loadingMore = false
|
|
|
+ if (this.keyword && this.keyword.trim()) {
|
|
|
+ params.keyword = this.keyword.trim()
|
|
|
+ }
|
|
|
+ var self = this
|
|
|
+ productOrderMy(params).then(function(res) {
|
|
|
+ self.loadingMore = false
|
|
|
if (res.code === 200 && res.data) {
|
|
|
- const list = res.data || []
|
|
|
- this.orders = this.orders.concat(list)
|
|
|
- this.noMore = list.length < this.size
|
|
|
+ var list = res.data || []
|
|
|
+ self.orders = self.orders.concat(list)
|
|
|
+ self.noMore = list.length < self.size
|
|
|
}
|
|
|
- }).catch(() => {
|
|
|
- this.loadingMore = false
|
|
|
+ }).catch(function() {
|
|
|
+ self.loadingMore = false
|
|
|
})
|
|
|
},
|
|
|
+ onStatusChange(value) {
|
|
|
+ this.currentStatus = value
|
|
|
+ this.page = 1
|
|
|
+ this.orders = []
|
|
|
+ this.noMore = false
|
|
|
+ this.loadOrders()
|
|
|
+ },
|
|
|
+ onSearch() {
|
|
|
+ var self = this
|
|
|
+ if (this.searchTimer) clearTimeout(this.searchTimer)
|
|
|
+ this.searchTimer = setTimeout(function() {
|
|
|
+ self.page = 1
|
|
|
+ self.orders = []
|
|
|
+ self.noMore = false
|
|
|
+ self.loadOrders()
|
|
|
+ }, 300)
|
|
|
+ },
|
|
|
+ clearSearch() {
|
|
|
+ this.keyword = ''
|
|
|
+ this.page = 1
|
|
|
+ this.orders = []
|
|
|
+ this.noMore = false
|
|
|
+ this.loadOrders()
|
|
|
+ },
|
|
|
goDetail(orderNo) {
|
|
|
uni.navigateTo({ url: '/pages/shop/order-detail/order-detail?orderNo=' + orderNo })
|
|
|
},
|
|
|
onPay(orderNo, amount) {
|
|
|
- uni.navigateTo({
|
|
|
- url: '/pages/shop/payment/payment?orderNo=' + orderNo + '&amount=' + (amount || 0)
|
|
|
- })
|
|
|
+ uni.navigateTo({ url: '/pages/shop/payment/payment?orderNo=' + orderNo + '&amount=' + (amount || 0) })
|
|
|
},
|
|
|
onCancel(orderNo) {
|
|
|
+ var self = this
|
|
|
uni.showModal({
|
|
|
title: '确认取消',
|
|
|
content: '确定取消该订单吗?',
|
|
|
- success: (confirm) => {
|
|
|
+ success: function(confirm) {
|
|
|
if (confirm.confirm) {
|
|
|
- productOrderCancel({ orderNo }).then(res => {
|
|
|
+ productOrderCancel({ orderNo: orderNo }).then(function(res) {
|
|
|
if (res.code === 200) {
|
|
|
uni.showToast({ title: '订单已取消', icon: 'success' })
|
|
|
- this.onRefresh()
|
|
|
+ self.loadCounts()
|
|
|
+ self.onRefresh()
|
|
|
} else {
|
|
|
uni.showToast({ title: res.message || '取消失败', icon: 'none' })
|
|
|
}
|
|
|
@@ -195,33 +264,17 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
onConfirm(orderNo) {
|
|
|
- productOrderConfirm({ orderNo }).then(res => {
|
|
|
+ var self = this
|
|
|
+ productOrderConfirm({ orderNo: orderNo }).then(function(res) {
|
|
|
if (res.code === 200) {
|
|
|
uni.showToast({ title: '收货成功', icon: 'success' })
|
|
|
- this.onRefresh()
|
|
|
+ self.loadCounts()
|
|
|
+ self.onRefresh()
|
|
|
} else {
|
|
|
uni.showToast({ title: res.message || '操作失败', icon: 'none' })
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
- onApplyRefund(orderNo) {
|
|
|
- uni.showModal({
|
|
|
- title: '申请退款',
|
|
|
- content: '确定要申请退款吗?',
|
|
|
- success: (confirm) => {
|
|
|
- if (confirm.confirm) {
|
|
|
- productOrderRefundApply({ orderNo: orderNo }).then(res => {
|
|
|
- if (res.code === 200) {
|
|
|
- uni.showToast({ title: '退款申请已提交', icon: 'success' })
|
|
|
- this.onRefresh()
|
|
|
- } else {
|
|
|
- uni.showToast({ title: res.message || '申请失败', icon: 'none' })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
goAfterSales(orderId, orderNo) {
|
|
|
var url = '/pages/shop/after-sales-apply/after-sales-apply?orderId=' + orderId
|
|
|
if (orderNo) {
|
|
|
@@ -229,39 +282,71 @@ export default {
|
|
|
}
|
|
|
uni.navigateTo({ url: url })
|
|
|
},
|
|
|
- statusLabel(status) {
|
|
|
- const map = {
|
|
|
+ getDisplayStatus(item) {
|
|
|
+ if (item.refundStatus === 1) return 'refunding'
|
|
|
+ if (item.refundStatus === 2) return 'refunded'
|
|
|
+ if (item.refundStatus === 3) return 'refund-rejected'
|
|
|
+ return item.status
|
|
|
+ },
|
|
|
+ getDisplayStatusText(item) {
|
|
|
+ var map = {
|
|
|
pending: '待支付',
|
|
|
paid: '待发货',
|
|
|
- pending_shipment: '待发货',
|
|
|
shipped: '待收货',
|
|
|
- pending_receipt: '待收货',
|
|
|
completed: '已完成',
|
|
|
- refunded: '已退款',
|
|
|
refunding: '退款中',
|
|
|
+ refunded: '已退款',
|
|
|
cancelled: '已取消',
|
|
|
- abnormal: '异常'
|
|
|
+ 'refund-rejected': '退款驳回'
|
|
|
}
|
|
|
- return map[status] || status
|
|
|
+ return map[this.getDisplayStatus(item)] || item.status
|
|
|
},
|
|
|
typeLabel(type) {
|
|
|
- const map = {
|
|
|
- activity: '活动',
|
|
|
- course: '课程',
|
|
|
- physical: '实物',
|
|
|
- digital: '数字',
|
|
|
- service: '服务'
|
|
|
- }
|
|
|
- return map[type] || type
|
|
|
+ var map = { activity: '活动', course: '课程', physical: '实物', digital: '数字', service: '服务' }
|
|
|
+ return map[type] || '商品'
|
|
|
+ },
|
|
|
+ typeIcon(type) {
|
|
|
+ return TYPE_ICONS[type] || '📦'
|
|
|
},
|
|
|
formatPriceWithSymbol(amount) {
|
|
|
if (!amount && amount !== 0) return ''
|
|
|
return '¥' + (amount / 100).toFixed(2)
|
|
|
},
|
|
|
- formatTime(time) {
|
|
|
+ formatRelativeTime(time) {
|
|
|
if (!time) return ''
|
|
|
- const d = new Date(time)
|
|
|
+ var now = Date.now()
|
|
|
+ var ts = new Date(time).getTime()
|
|
|
+ var diff = now - ts
|
|
|
+ var minute = 60 * 1000
|
|
|
+ var hour = 60 * minute
|
|
|
+ var day = 24 * hour
|
|
|
+ if (diff < minute) return '刚刚'
|
|
|
+ if (diff < hour) return Math.floor(diff / minute) + '分钟前'
|
|
|
+ if (diff < day) return Math.floor(diff / hour) + '小时前'
|
|
|
+ if (diff < 3 * day) return Math.floor(diff / day) + '天前'
|
|
|
+ var d = new Date(time)
|
|
|
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0')
|
|
|
+ },
|
|
|
+ formatFullTime(time) {
|
|
|
+ if (!time) return ''
|
|
|
+ var d = new Date(time)
|
|
|
+ var y = d.getFullYear()
|
|
|
+ var mo = String(d.getMonth() + 1).padStart(2, '0')
|
|
|
+ var da = String(d.getDate()).padStart(2, '0')
|
|
|
+ var h = String(d.getHours()).padStart(2, '0')
|
|
|
+ var mi = String(d.getMinutes()).padStart(2, '0')
|
|
|
+ var s = String(d.getSeconds()).padStart(2, '0')
|
|
|
+ return y + '-' + mo + '-' + da + ' ' + h + ':' + mi + ':' + s
|
|
|
+ },
|
|
|
+ formatCount(n) {
|
|
|
+ if (n > 99) return '99+'
|
|
|
+ return String(n)
|
|
|
+ },
|
|
|
+ defaultCover(type) {
|
|
|
+ return DEFAULT_COVER[type] || DEFAULT_COVER.physical
|
|
|
+ },
|
|
|
+ onImgError(e, item) {
|
|
|
+ item.coverImage = ''
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -274,37 +359,92 @@ export default {
|
|
|
height: 100vh;
|
|
|
background: #f5f5f5;
|
|
|
}
|
|
|
+.search-bar {
|
|
|
+ background: #fff;
|
|
|
+ padding: 16rpx 24rpx;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+}
|
|
|
+.search-input-wrap {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 40rpx;
|
|
|
+ padding: 12rpx 24rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+.search-icon {
|
|
|
+ font-size: 28rpx;
|
|
|
+ margin-right: 12rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.search-input {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+ height: 48rpx;
|
|
|
+ line-height: 48rpx;
|
|
|
+}
|
|
|
+.search-placeholder {
|
|
|
+ color: #bbb;
|
|
|
+ font-size: 26rpx;
|
|
|
+}
|
|
|
+.search-clear {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #ccc;
|
|
|
+ padding: 8rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
.status-tabs {
|
|
|
display: flex;
|
|
|
background: #fff;
|
|
|
- padding: 20rpx 0;
|
|
|
+ padding: 0;
|
|
|
border-bottom: 1rpx solid #eee;
|
|
|
- position: sticky;
|
|
|
- top: 0;
|
|
|
- z-index: 10;
|
|
|
+ white-space: nowrap;
|
|
|
+ height: 88rpx;
|
|
|
}
|
|
|
.tab {
|
|
|
- flex: 1;
|
|
|
- text-align: center;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-direction: column;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ height: 88rpx;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+.tab-label {
|
|
|
font-size: 26rpx;
|
|
|
color: #666;
|
|
|
- padding: 10rpx 0;
|
|
|
+ transition: color 0.2s;
|
|
|
}
|
|
|
-.tab.active {
|
|
|
+.tab.active .tab-label {
|
|
|
color: #F97316;
|
|
|
font-weight: bold;
|
|
|
- border-bottom: 4rpx solid #F97316;
|
|
|
+}
|
|
|
+.tab-badge {
|
|
|
+ font-size: 18rpx;
|
|
|
+ background: #EF4444;
|
|
|
+ color: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ padding: 2rpx 10rpx;
|
|
|
+ margin-top: 4rpx;
|
|
|
+ min-width: 32rpx;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+.tab.active .tab-badge {
|
|
|
+ background: #F97316;
|
|
|
}
|
|
|
.order-list {
|
|
|
flex: 1;
|
|
|
- height: calc(100vh - 100rpx);
|
|
|
+ height: calc(100vh - 88rpx - 104rpx);
|
|
|
}
|
|
|
.loading-wrap,
|
|
|
.empty-wrap {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
- padding-top: 200rpx;
|
|
|
+ padding-top: 160rpx;
|
|
|
}
|
|
|
.loading-text {
|
|
|
font-size: 26rpx;
|
|
|
@@ -318,6 +458,11 @@ export default {
|
|
|
font-size: 28rpx;
|
|
|
color: #999;
|
|
|
}
|
|
|
+.empty-hint {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #bbb;
|
|
|
+ margin-top: 12rpx;
|
|
|
+}
|
|
|
.order-card {
|
|
|
background: #fff;
|
|
|
margin: 20rpx;
|
|
|
@@ -325,96 +470,126 @@ export default {
|
|
|
padding: 24rpx;
|
|
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
|
|
|
}
|
|
|
-.order-header {
|
|
|
+.card-body {
|
|
|
+ display: flex;
|
|
|
+ gap: 20rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+.product-img {
|
|
|
+ width: 160rpx;
|
|
|
+ height: 160rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ background: #f5f5f5;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.product-section {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
display: flex;
|
|
|
+ flex-direction: column;
|
|
|
justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 16rpx;
|
|
|
}
|
|
|
-.order-no {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #999;
|
|
|
+.product-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 12rpx;
|
|
|
+}
|
|
|
+.product-name {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #1a1a1a;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1.4;
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
}
|
|
|
.status-badge {
|
|
|
- font-size: 22rpx;
|
|
|
- padding: 4rpx 16rpx;
|
|
|
+ font-size: 20rpx;
|
|
|
+ padding: 4rpx 14rpx;
|
|
|
border-radius: 20rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+ line-height: 1.5;
|
|
|
}
|
|
|
-.status-pending {
|
|
|
- background: #fff7e6;
|
|
|
- color: #fa8c16;
|
|
|
+.status-pending { background: #FFF7E6; color: #F97316; }
|
|
|
+.status-paid { background: #E0F2FE; color: #0284C7; }
|
|
|
+.status-shipped { background: #E0F2FE; color: #0284C7; }
|
|
|
+.status-completed { background: #F0FDF4; color: #16A34A; }
|
|
|
+.status-refunding { background: #FEF3C7; color: #D97706; }
|
|
|
+.status-refunded { background: #f5f5f5; color: #999; }
|
|
|
+.status-cancelled { background: #f5f5f5; color: #999; }
|
|
|
+.status-refund-rejected { background: #FEE2E2; color: #DC2626; }
|
|
|
+.product-meta {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12rpx;
|
|
|
+ margin-top: 10rpx;
|
|
|
}
|
|
|
-.status-paid {
|
|
|
- background: #e6f7ff;
|
|
|
- color: #1890ff;
|
|
|
+.type-tag {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 3rpx 12rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ font-size: 20rpx;
|
|
|
}
|
|
|
-.status-shipped {
|
|
|
- background: #e6f7ff;
|
|
|
- color: #1890ff;
|
|
|
+.type-icon {
|
|
|
+ font-size: 20rpx;
|
|
|
+ margin-right: 4rpx;
|
|
|
}
|
|
|
-.status-completed {
|
|
|
- background: #f6ffed;
|
|
|
- color: #52c41a;
|
|
|
+.type-text {
|
|
|
+ font-size: 20rpx;
|
|
|
}
|
|
|
-.status-cancelled,
|
|
|
-.status-refunded {
|
|
|
- background: #f5f5f5;
|
|
|
- color: #999;
|
|
|
+.type-activity { background: #FFF7E6; color: #F97316; }
|
|
|
+.type-course { background: #EDE9FE; color: #7C3AED; }
|
|
|
+.type-physical { background: #E0F2FE; color: #0284C7; }
|
|
|
+.type-digital { background: #F0FDF4; color: #16A34A; }
|
|
|
+.type-service { background: #FEF3C7; color: #D97706; }
|
|
|
+.order-no {
|
|
|
+ font-size: 20rpx;
|
|
|
+ color: #bbb;
|
|
|
}
|
|
|
-.order-body {
|
|
|
+.price-row {
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- margin-bottom: 16rpx;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 8rpx;
|
|
|
+ margin-top: 8rpx;
|
|
|
}
|
|
|
-.product-info {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-.product-name {
|
|
|
- display: block;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 6rpx;
|
|
|
-}
|
|
|
-.product-type {
|
|
|
- font-size: 22rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-.order-meta {
|
|
|
- text-align: right;
|
|
|
-}
|
|
|
-.order-amount {
|
|
|
- display: block;
|
|
|
- font-size: 30rpx;
|
|
|
+.price {
|
|
|
+ font-size: 32rpx;
|
|
|
color: #F97316;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
-.order-qty {
|
|
|
+.qty {
|
|
|
font-size: 22rpx;
|
|
|
color: #999;
|
|
|
}
|
|
|
-.order-footer {
|
|
|
+.card-footer {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
- border-top: 1rpx solid #f0f0f0;
|
|
|
+ border-top: 1rpx solid #f5f5f5;
|
|
|
padding-top: 16rpx;
|
|
|
}
|
|
|
.order-time {
|
|
|
font-size: 22rpx;
|
|
|
- color: #999;
|
|
|
+ color: #bbb;
|
|
|
}
|
|
|
.order-actions {
|
|
|
display: flex;
|
|
|
gap: 12rpx;
|
|
|
+ align-items: center;
|
|
|
}
|
|
|
-.btn-small {
|
|
|
+.btn-sm {
|
|
|
font-size: 22rpx;
|
|
|
padding: 8rpx 24rpx;
|
|
|
border-radius: 30rpx;
|
|
|
border: none;
|
|
|
line-height: 1.5;
|
|
|
}
|
|
|
-.btn-small::after {
|
|
|
+.btn-sm::after {
|
|
|
border: none;
|
|
|
}
|
|
|
.btn-cancel {
|
|
|
@@ -426,8 +601,16 @@ export default {
|
|
|
color: #fff;
|
|
|
}
|
|
|
.btn-confirm {
|
|
|
- background: #e6f7ff;
|
|
|
- color: #1890ff;
|
|
|
+ background: #E0F2FE;
|
|
|
+ color: #0284C7;
|
|
|
+}
|
|
|
+.btn-refund {
|
|
|
+ background: #FEF3C7;
|
|
|
+ color: #D97706;
|
|
|
+}
|
|
|
+.btn-rebuy {
|
|
|
+ background: #f5f5f5;
|
|
|
+ color: #666;
|
|
|
}
|
|
|
.loading-more,
|
|
|
.no-more {
|