合并merchant文件

This commit is contained in:
2026-03-20 15:43:33 +08:00
parent 29f588a2b2
commit 620ae742df
12 changed files with 3477 additions and 0 deletions

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
<!-- 商家端首页 - UTS Android 兼容 -->
<template>
<view class="merchant-container">
@@ -134,6 +135,245 @@
</view>
</view>
</view>
=======
<!-- 商家端首页 -->
<template>
<view class="merchant-container">
<scroll-view scroll-y class="main-scroll" :refresher-enabled="true" :refresher-triggered="refreshing" @refresherrefresh="onRefresh">
<!-- 头部区域 -->
<view class="header">
<view class="header-bg"></view>
<view class="header-content">
<view class="shop-info">
<image :src="shopInfo.shop_logo || '/static/images/default-shop.png'" class="shop-logo" mode="aspectFill" @click="goToSettings" />
<view class="shop-details">
<text class="shop-name">{{ shopInfo.shop_name || '我的店铺' }}</text>
<view class="shop-meta">
<view class="meta-item">
<text class="meta-icon">⭐</text>
<text class="meta-value">{{ shopInfo.rating_avg || 5.0 }}</text>
</view>
<view class="meta-divider"></view>
<view class="meta-item">
<text class="meta-icon">📦</text>
<text class="meta-value">{{ shopInfo.total_sales || 0 }}销量</text>
</view>
</view>
</view>
<view class="header-actions">
<view class="action-btn" @click="goToMessages">
<text class="action-icon">🔔</text>
<view v-if="unreadCount > 0" class="action-badge"><text>{{ unreadCount > 99 ? '99+' : unreadCount }}</text></view>
</view>
<view class="action-btn" @click="goToSettings">
<text class="action-icon">⚙️</text>
</view>
</view>
</view>
</view>
</view>
<view class="content-area">
<!-- 今日数据卡片 -->
<view class="stats-card">
<view class="stats-header">
<view class="stats-title-row">
<text class="stats-title">📊 今日数据</text>
<text class="stats-date">{{ currentDate }}</text>
</view>
</view>
<view class="stats-grid">
<view class="stats-item">
<view class="stats-icon-wrap blue">
<text class="stats-icon">📋</text>
</view>
<text class="stats-value">{{ todayStats.orders || 0 }}</text>
<text class="stats-label">订单数</text>
</view>
<view class="stats-item">
<view class="stats-icon-wrap green">
<text class="stats-icon">💰</text>
</view>
<text class="stats-value">¥{{ formatNumber(todayStats.sales) }}</text>
<text class="stats-label">销售额</text>
</view>
<view class="stats-item">
<view class="stats-icon-wrap orange">
<text class="stats-icon">👥</text>
</view>
<text class="stats-value">{{ todayStats.visitors || 0 }}</text>
<text class="stats-label">访客数</text>
</view>
<view class="stats-item">
<view class="stats-icon-wrap purple">
<text class="stats-icon">📈</text>
</view>
<text class="stats-value">{{ todayStats.conversion || 0 }}%</text>
<text class="stats-label">转化率</text>
</view>
</view>
</view>
<!-- 待处理事项 -->
<view class="section-card">
<view class="section-header">
<text class="section-title">🔔 待处理事项</text>
</view>
<view class="pending-grid">
<view class="pending-item" @click="goToOrders('pending')">
<view class="pending-icon-wrap orange">
<text class="pending-icon">📦</text>
</view>
<view class="pending-info">
<text class="pending-count" v-if="pendingCounts.pending_shipment > 0">{{ pendingCounts.pending_shipment }}</text>
<text class="pending-text">待发货</text>
</view>
</view>
<view class="pending-item" @click="goToOrders('refund')">
<view class="pending-icon-wrap red">
<text class="pending-icon">↩️</text>
</view>
<view class="pending-info">
<text class="pending-count" v-if="pendingCounts.refund_requests > 0">{{ pendingCounts.refund_requests }}</text>
<text class="pending-text">退款</text>
</view>
</view>
<view class="pending-item" @click="goToInventory">
<view class="pending-icon-wrap yellow">
<text class="pending-icon">⚠️</text>
</view>
<view class="pending-info">
<text class="pending-count" v-if="pendingCounts.low_stock > 0">{{ pendingCounts.low_stock }}</text>
<text class="pending-text">库存预警</text>
</view>
</view>
<view class="pending-item" @click="goToReviews">
<view class="pending-icon-wrap blue">
<text class="pending-icon">💬</text>
</view>
<view class="pending-info">
<text class="pending-count" v-if="pendingCounts.pending_reviews > 0">{{ pendingCounts.pending_reviews }}</text>
<text class="pending-text">待回复</text>
</view>
</view>
</view>
</view>
<!-- 常用功能 -->
<view class="section-card">
<view class="section-header">
<text class="section-title">🚀 常用功能</text>
</view>
<view class="shortcuts-grid">
<view class="shortcut-item" @click="goToProducts('add')">
<view class="shortcut-icon-wrap gradient-blue">
<text class="shortcut-icon"></text>
</view>
<text class="shortcut-text">发布商品</text>
</view>
<view class="shortcut-item" @click="goToOrders('all')">
<view class="shortcut-icon-wrap gradient-orange">
<text class="shortcut-icon">📋</text>
</view>
<text class="shortcut-text">订单管理</text>
</view>
<view class="shortcut-item" @click="goToProducts('manage')">
<view class="shortcut-icon-wrap gradient-green">
<text class="shortcut-icon">📦</text>
</view>
<text class="shortcut-text">商品管理</text>
</view>
<view class="shortcut-item" @click="goToInventory">
<view class="shortcut-icon-wrap gradient-purple">
<text class="shortcut-icon">📊</text>
</view>
<text class="shortcut-text">库存管理</text>
</view>
<view class="shortcut-item" @click="goToPromotions">
<view class="shortcut-icon-wrap gradient-red">
<text class="shortcut-icon">🎯</text>
</view>
<text class="shortcut-text">营销活动</text>
</view>
<view class="shortcut-item" @click="goToStatistics">
<view class="shortcut-icon-wrap gradient-cyan">
<text class="shortcut-icon">📈</text>
</view>
<text class="shortcut-text">数据统计</text>
</view>
<view class="shortcut-item" @click="goToFinance">
<view class="shortcut-icon-wrap gradient-yellow">
<text class="shortcut-icon">💰</text>
</view>
<text class="shortcut-text">财务结算</text>
</view>
<view class="shortcut-item" @click="goToMembers">
<view class="shortcut-icon-wrap gradient-pink">
<text class="shortcut-icon">VIP</text>
</view>
<text class="shortcut-text">会员管理</text>
</view>
<view class="shortcut-item" @click="goToSettings">
<view class="shortcut-icon-wrap gradient-pink">
<text class="shortcut-icon">🏪</text>
</view>
<text class="shortcut-text">店铺设置</text>
</view>
</view>
</view>
<!-- 最新订单 -->
<view class="section-card">
<view class="section-header">
<text class="section-title">🛒 最新订单</text>
<text class="section-more" @click="goToOrders('all')">查看全部 </text>
</view>
<view v-if="recentOrders.length === 0" class="empty-orders">
<text class="empty-icon">📭</text>
<text class="empty-text">暂无订单</text>
<text class="empty-hint">有新订单时会在这里显示</text>
</view>
<view v-else class="orders-list">
<view v-for="order in recentOrders" :key="order.id" class="order-card" @click="goToOrderDetail(order.id)">
<view class="order-header">
<view class="order-no-wrap">
<text class="order-label">订单号</text>
<text class="order-no">{{ order.order_no }}</text>
</view>
<text class="order-status" :class="getOrderStatusClass(order.order_status)">{{ getOrderStatusText(order.order_status) }}</text>
</view>
<view class="order-goods">
<view v-for="item in order.items.slice(0, 3)" :key="item.id" class="goods-item">
<image :src="item.image_url || '/static/images/default-product.png'" class="goods-image" mode="aspectFill" />
<view class="goods-info">
<text class="goods-name">{{ item.product_name }}</text>
<view class="goods-bottom">
<text class="goods-spec" v-if="item.sku_name">{{ item.sku_name }}</text>
<text class="goods-qty">×{{ item.quantity }}</text>
</view>
</view>
<text class="goods-price">¥{{ item.price }}</text>
</view>
<view v-if="order.items.length > 3" class="goods-more">
<text>还有{{ order.items.length - 3 }}件商品</text>
</view>
</view>
<view class="order-footer">
<text class="order-time">{{ formatTime(order.created_at) }}</text>
<view class="order-amount-wrap">
<text class="amount-label">合计</text>
<text class="amount-value">¥{{ order.total_amount.toFixed(2) }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 底部安全区域 -->
<view class="safe-bottom"></view>
</view>
</scroll-view>
>>>>>>> local-backup-root-cyj
</view>
</template>
@@ -219,7 +459,20 @@
low_stock: 0,
pending_reviews: 0
} as PendingCountsType,
<<<<<<< HEAD
recentOrders: [] as OrderType[]
=======
recentOrders: [] as OrderType[],
unreadCount: 0,
refreshing: false
}
},
computed: {
currentDate(): string {
const now = new Date()
return `${now.getMonth() + 1}月${now.getDate()}日`
>>>>>>> local-backup-root-cyj
}
},
@@ -229,6 +482,7 @@
onShow() {
if (this.merchantId) {
<<<<<<< HEAD
this.loadMerchantData()
this.loadTodayStats()
this.loadPendingCounts()
@@ -239,16 +493,36 @@
this.loadTodayStats()
this.loadPendingCounts()
this.loadRecentOrders()
=======
this.loadAllData()
this.startRealtimeSubscription()
} else {
setTimeout(() => {
this.loadAllData()
this.startRealtimeSubscription()
>>>>>>> local-backup-root-cyj
}, 500)
}
},
<<<<<<< HEAD
methods: {
formatNumber(value: number | null): string {
if (value == null) return '0.00'
return value.toFixed(2)
},
=======
onHide() {
this.stopRealtimeSubscription()
},
onUnload() {
this.stopRealtimeSubscription()
},
methods: {
>>>>>>> local-backup-root-cyj
async initMerchantId() {
try {
const session = supa.getSession()
@@ -263,6 +537,56 @@
}
},
<<<<<<< HEAD
=======
startRealtimeSubscription() {
if (!this.merchantId) return
// 监听订单表的变化
try {
supa.channel('ml_orders_realtime')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'ml_orders',
filter: `merchant_id=eq.${this.merchantId}`
}, (payload) => {
console.log('收到订单实时更新:', payload)
// 延迟一下再刷新,避免连续变动导致频繁请求
setTimeout(() => {
this.loadTodayStats()
this.loadPendingCounts()
this.loadRecentOrders()
}, 500)
})
.subscribe()
} catch (e) {
console.error('订阅实时更新失败:', e)
}
},
stopRealtimeSubscription() {
try {
supa.channel('ml_orders_realtime').unsubscribe()
} catch (e) {
console.error('取消订阅失败:', e)
}
},
async loadAllData() {
await this.loadMerchantData()
await this.loadTodayStats()
await this.loadPendingCounts()
await this.loadRecentOrders()
await this.loadUnreadCount()
},
formatNumber(value: number | null): string {
if (value == null) return '0.00'
return value.toFixed(2)
},
>>>>>>> local-backup-root-cyj
async loadMerchantData() {
try {
const response = await supa
@@ -272,7 +596,12 @@
.limit(1)
.execute()
<<<<<<< HEAD
if (response.error != null || !response.data || (response.data as any[]).length === 0) {
=======
if (response.error != null) { console.error('ml_shops请求500报错', response.error) }
if (response.error != null || !response.data || (response.data as any[]).length === 0) {
>>>>>>> local-backup-root-cyj
this.shopInfo = {
id: null,
merchant_id: this.merchantId,
@@ -291,7 +620,11 @@
const rawData = (response.data as any[])[0] as UTSJSONObject
this.shopInfo = {
<<<<<<< HEAD
id: rawData.getString('id') || null,
=======
id: rawData.getString('id') || null,
>>>>>>> local-backup-root-cyj
merchant_id: rawData.getString('merchant_id') || null,
shop_name: rawData.getString('shop_name') || '我的店铺',
shop_logo: rawData.getString('shop_logo') || null,
@@ -303,6 +636,43 @@
total_sales: rawData.getNumber('total_sales') || 0,
status: rawData.getNumber('status') || 1
}
<<<<<<< HEAD
=======
// 重新动态查询并计算该店铺下所有商品的真实销量总和
try {
const salesRes = await supa
.from('ml_products')
.select('sale_count')
.eq('merchant_id', this.merchantId)
.execute()
if (salesRes.error != null) { console.error('ml_products sale_count报错', salesRes.error) }
if (salesRes.data != null) {
let calcTotalSales: number = 0
const salesData = salesRes.data as any[]
for (let i = 0; i < salesData.length; i++) {
const productInfo = salesData[i] as UTSJSONObject
const currentSale = productInfo.getNumber('sale_count')
if (currentSale != null) {
calcTotalSales += currentSale
}
}
let baseSales: number = 0
if (this.shopInfo.total_sales != null) {
baseSales = Number(this.shopInfo.total_sales)
}
if (calcTotalSales > baseSales) {
this.shopInfo.total_sales = calcTotalSales
}
}
} catch (e) {
console.error('获取店铺真实销量失败:', e)
}
>>>>>>> local-backup-root-cyj
} catch (e) {
console.error('加载店铺信息失败:', e)
}
@@ -310,6 +680,7 @@
async loadTodayStats() {
try {
<<<<<<< HEAD
const response = await supa
.from('ml_orders')
.select('total_amount, order_status', { count: 'exact' })
@@ -323,24 +694,89 @@
let totalOrders = 0
let totalSales = 0
=======
// 1. 获取所有订单
const response = await supa
.from('ml_orders')
.select(`
total_amount,
order_status,
created_at,
order_items (quantity)
`)
.eq('merchant_id', this.merchantId)
.execute()
if (response.error != null) { console.error('ml_orders stats报错', response.error); return }
let todayOrders = 0
let todaySales = 0
let allTimeSalesVolume = 0 // 总销量(件数)
const now = new Date()
// 获取今日0点的毫秒数 (本地时间)
const todayStartMs = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
>>>>>>> local-backup-root-cyj
const rawData = response.data as any[]
if (rawData != null) {
for (let i = 0; i < rawData.length; i++) {
const item = rawData[i] as UTSJSONObject
const status = item.getNumber('order_status')
<<<<<<< HEAD
if (status >= 2) {
totalOrders++
totalSales += item.getNumber('total_amount') || 0
=======
// 有效订单(已支付、已发货、已完成) >= 2
// 如果是退款(0)或取消(5),可能不计入今日销售额,这里按需调整
if (status != null && status >= 2 && status < 5) {
// 计算总销量(即售出的商品总件数)
const itemsObj = item.get('order_items')
if (itemsObj != null && Array.isArray(itemsObj)) {
const itemsArr = itemsObj as any[]
for (let j = 0; j < itemsArr.length; j++) {
const orderItem = itemsArr[j] as UTSJSONObject
allTimeSalesVolume += Math.floor(orderItem.getNumber('quantity') || 1)
}
} else {
allTimeSalesVolume += 1
}
// 判断是否是今日数据
const createdAtStr = item.getString('created_at') || ''
if (createdAtStr.length > 0) {
const orderDateMs = new Date(createdAtStr).getTime()
if (orderDateMs >= todayStartMs) {
todayOrders++
todaySales += item.getNumber('total_amount') || 0
}
}
>>>>>>> local-backup-root-cyj
}
}
}
<<<<<<< HEAD
this.todayStats = {
orders: totalOrders,
sales: totalSales,
visitors: Math.floor(totalOrders * 3),
conversion: totalOrders > 0 ? 15 : 0
=======
// 更新店铺总销量显示
let currentShopSales = Number(this.shopInfo.total_sales || 0)
if (allTimeSalesVolume > currentShopSales) {
this.shopInfo.total_sales = allTimeSalesVolume
}
this.todayStats = {
orders: todayOrders,
sales: todaySales,
visitors: Math.floor(todayOrders * (2.5 + Math.random())) + 5, // 模拟访客数
conversion: todayOrders > 0 ? (12 + Math.floor(Math.random() * 8)) : 0 // 模拟转化率
>>>>>>> local-backup-root-cyj
}
} catch (e) {
console.error('获取今日统计异常:', e)
@@ -356,22 +792,39 @@
.eq('order_status', 2)
.execute()
<<<<<<< HEAD
const refundRes = await supa
=======
if (pendingShipmentRes.error != null) { console.error('pendingShipment报错', pendingShipmentRes.error) }
const refundRes = await supa
>>>>>>> local-backup-root-cyj
.from('ml_orders')
.select('id', { count: 'exact' })
.eq('merchant_id', this.merchantId)
.eq('order_status', 0)
.execute()
<<<<<<< HEAD
const lowStockRes = await supa
=======
if (refundRes.error != null) { console.error('refundRes报错', refundRes.error) }
const lowStockRes = await supa
>>>>>>> local-backup-root-cyj
.from('ml_products')
.select('id', { count: 'exact' })
.eq('merchant_id', this.merchantId)
.lte('total_stock', 10)
<<<<<<< HEAD
.gte('total_stock', 0)
.execute()
this.pendingCounts = {
=======
.execute()
if (lowStockRes.error != null) { console.error('lowStockRes报错', lowStockRes.error) }
this.pendingCounts = {
>>>>>>> local-backup-root-cyj
pending_shipment: pendingShipmentRes.total || 0,
refund_requests: refundRes.total || 0,
low_stock: lowStockRes.total || 0,
@@ -388,7 +841,11 @@
.from('ml_orders')
.select(`
*,
<<<<<<< HEAD
order_items!inner (
=======
order_items (
>>>>>>> local-backup-root-cyj
id,
product_id,
product_name,
@@ -403,10 +860,15 @@
.limit(5)
.execute()
<<<<<<< HEAD
if (response.error != null || !response.data) {
this.recentOrders = []
return
}
=======
if (response.error != null) { console.error('recentOrders报错', response.error) }
if (response.error != null || !response.data) { this.recentOrders = []; return; }
>>>>>>> local-backup-root-cyj
const rawData = response.data as any[]
const ordersData: OrderType[] = []
@@ -447,6 +909,7 @@
this.recentOrders = ordersData
} catch (e) {
<<<<<<< HEAD
console.error('加载最新订单异常:', e)
}
},
@@ -472,6 +935,51 @@
case 0: return '退款中'
default: return '未知状态'
}
=======
console.error('加载最新订单异常:', e); uni.showModal({title: '最新订单报错', content: e.toString()})
}
},
async loadUnreadCount() {
try {
const response = await supa
.from('ml_chat_messages')
.select('id', { count: 'exact' })
.eq('receiver_id', this.merchantId)
.eq('is_read', false)
.execute()
if (response.error != null) { uni.showModal({title: 'ml_chat_messages报错', content: JSON.stringify(response.error)}) }
this.unreadCount = response.total || 0
} catch (e) {
console.error('获取未读消息数失败:', e)
}
},
onRefresh() {
this.refreshing = true
this.loadAllData().then(() => {
this.refreshing = false
})
},
getOrderStatusClass(status: number): string {
if (status === 1) return 'status-pending'
if (status === 2) return 'status-paid'
if (status === 3) return 'status-shipped'
if (status === 4) return 'status-completed'
if (status === 0) return 'status-refund'
return 'status-default'
},
getOrderStatusText(status: number): string {
if (status === 1) return '待付款'
if (status === 2) return '待发货'
if (status === 3) return '已发货'
if (status === 4) return '已完成'
if (status === 0) return '退款中'
return '未知'
>>>>>>> local-backup-root-cyj
},
formatTime(timeStr: string): string {
@@ -480,6 +988,7 @@
const now = new Date()
const diff = now.getTime() - date.getTime()
const minutes = Math.floor(diff / (1000 * 60))
<<<<<<< HEAD
if (minutes < 60) {
return `${minutes}分钟前`
@@ -542,12 +1051,67 @@
uni.navigateTo({
url: `/pages/mall/merchant/order-detail?id=${orderId}`
})
=======
if (minutes < 60) return `${minutes}分钟前`
if (minutes < 1440) return `${Math.floor(minutes / 60)}小时前`
return `${date.getMonth() + 1}-${date.getDate()}`
},
goToMessages() {
uni.navigateTo({ url: '/pages/mall/merchant/messages' })
},
goToSettings() {
uni.navigateTo({ url: '/pages/mall/merchant/shop-edit' })
},
goToOrders(type: string) {
uni.navigateTo({ url: `/pages/mall/merchant/orders?type=${type}` })
},
goToProducts(type: string) {
if (type === 'add') {
uni.navigateTo({ url: '/pages/mall/merchant/product-edit' })
} else {
uni.navigateTo({ url: '/pages/mall/merchant/products' })
}
},
goToPromotions() {
uni.navigateTo({ url: '/pages/mall/merchant/promotions' })
},
goToStatistics() {
uni.navigateTo({ url: '/pages/mall/merchant/statistics' })
},
goToFinance() {
uni.navigateTo({ url: '/pages/mall/merchant/finance' })
},
goToReviews() {
uni.navigateTo({ url: '/pages/mall/merchant/reviews' })
},
goToInventory() {
uni.navigateTo({ url: '/pages/mall/merchant/inventory' })
},
goToMembers() {
uni.navigateTo({ url: '/pages/mall/merchant/members' })
},
goToOrderDetail(orderId: string) {
uni.navigateTo({ url: `/pages/mall/merchant/order-detail?id=${orderId}` })
>>>>>>> local-backup-root-cyj
}
}
}
</script>
<style>
<<<<<<< HEAD
.merchant-container {
background-color: #f5f5f5;
min-height: 100vh;
@@ -872,3 +1436,115 @@
color: #999;
}
</style>
=======
.merchant-container { background-color: #f5f7fa; min-height: 100vh; }
.main-scroll { height: 100vh; }
.header { position: relative; padding-bottom: 30rpx; }
.header-bg { position: absolute; top: 0; left: 0; right: 0; height: 300rpx; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 0 0 40rpx 40rpx; }
.header-content { position: relative; padding: 40rpx 30rpx 0; }
.shop-info { display: flex; flex-direction: row; align-items: center; }
.shop-logo { width: 110rpx; height: 110rpx; border-radius: 20rpx; border-width: 4rpx; border-style: solid; border-color: rgba(255,255,255,0.8); margin-right: 24rpx; background-color: #fff; }
.shop-details { flex: 1; }
.shop-name { font-size: 40rpx; font-weight: bold; color: #fff; margin-bottom: 12rpx; }
.shop-meta { display: flex; flex-direction: row; align-items: center; }
.meta-item { display: flex; flex-direction: row; align-items: center; }
.meta-icon { font-size: 24rpx; margin-right: 6rpx; }
.meta-value { font-size: 26rpx; color: rgba(255,255,255,0.9); }
.meta-divider { width: 2rpx; height: 24rpx; background-color: rgba(255,255,255,0.3); margin-left: 20rpx; margin-right: 20rpx; }
.header-actions { display: flex; flex-direction: row; }
.action-btn { position: relative; margin-left: 24rpx; width: 72rpx; height: 72rpx; background-color: rgba(255,255,255,0.2); border-radius: 36rpx; display: flex; align-items: center; justify-content: center; }
.action-icon { font-size: 36rpx; }
.action-badge { position: absolute; top: -4rpx; right: -4rpx; min-width: 36rpx; height: 36rpx; background-color: #FF3B30; border-radius: 18rpx; display: flex; align-items: center; justify-content: center; padding-left: 8rpx; padding-right: 8rpx; border-width: 2rpx; border-style: solid; border-color: #fff; }
.action-badge-text { font-size: 20rpx; color: #fff; font-weight: bold; }
.content-area { padding-left: 24rpx; padding-right: 24rpx; padding-bottom: 30rpx; margin-top: 10rpx; }
.stats-card { background-color: #fff; border-radius: 24rpx; padding: 28rpx; margin-bottom: 24rpx; }
.stats-header { margin-bottom: 24rpx; }
.stats-title-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
.stats-title { font-size: 32rpx; font-weight: bold; color: #333; }
.stats-date { font-size: 24rpx; color: #999; background-color: #f5f7fa; padding-top: 6rpx; padding-bottom: 6rpx; padding-left: 16rpx; padding-right: 16rpx; border-radius: 12rpx; }
.stats-grid { display: flex; flex-direction: row; justify-content: space-between; }
.stats-item { width: 160rpx; display: flex; flex-direction: column; align-items: center; }
.stats-icon-wrap { width: 64rpx; height: 64rpx; border-radius: 16rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 12rpx; }
.stats-icon-wrap.blue { background-color: #E3F2FD; }
.stats-icon-wrap.green { background-color: #E8F5E9; }
.stats-icon-wrap.orange { background-color: #FFF3E0; }
.stats-icon-wrap.purple { background-color: #F3E5F5; }
.stats-icon { font-size: 28rpx; }
.stats-value { font-size: 36rpx; font-weight: bold; color: #333; margin-bottom: 4rpx; }
.stats-label { font-size: 24rpx; color: #999; }
.section-card { background-color: #fff; border-radius: 24rpx; padding: 28rpx; margin-bottom: 24rpx; }
.section-header { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 24rpx; }
.section-title { font-size: 32rpx; font-weight: bold; color: #333; }
.section-more { font-size: 26rpx; color: #007AFF; padding-top: 8rpx; padding-bottom: 8rpx; padding-left: 16rpx; padding-right: 16rpx; background-color: #E3F2FD; border-radius: 12rpx; }
.pending-grid { display: flex; flex-direction: row; justify-content: space-between; }
.pending-item { width: 160rpx; display: flex; flex-direction: column; align-items: center; padding-top: 16rpx; padding-bottom: 16rpx; }
.pending-icon-wrap { width: 88rpx; height: 88rpx; border-radius: 24rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 12rpx; }
.pending-icon-wrap.orange { background-color: #FFF3E0; }
.pending-icon-wrap.red { background-color: #FFEBEE; }
.pending-icon-wrap.yellow { background-color: #FFFDE7; }
.pending-icon-wrap.blue { background-color: #E3F2FD; }
.pending-icon { font-size: 40rpx; }
.pending-info { display: flex; flex-direction: column; align-items: center; }
.pending-count { font-size: 32rpx; font-weight: bold; color: #FF6B35; margin-bottom: 4rpx; }
.pending-text { font-size: 24rpx; color: #666; }
.shortcuts-grid { display: flex; flex-direction: row; flex-wrap: wrap; }
.shortcut-item { width: 25%; display: flex; flex-direction: column; align-items: center; padding-top: 20rpx; padding-bottom: 20rpx; }
.shortcut-icon-wrap { width: 88rpx; height: 88rpx; border-radius: 24rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 12rpx; }
.shortcut-icon-wrap.gradient-blue { background-color: #667eea; }
.shortcut-icon-wrap.gradient-orange { background-color: #f093fb; }
.shortcut-icon-wrap.gradient-green { background-color: #4facfe; }
.shortcut-icon-wrap.gradient-purple { background-color: #a18cd1; }
.shortcut-icon-wrap.gradient-red { background-color: #ff9a9e; }
.shortcut-icon-wrap.gradient-cyan { background-color: #a1c4fd; }
.shortcut-icon-wrap.gradient-yellow { background-color: #f6d365; }
.shortcut-icon-wrap.gradient-pink { background-color: #ffecd2; }
.shortcut-icon { font-size: 40rpx; }
.shortcut-text { font-size: 24rpx; color: #666; }
.empty-orders { display: flex; flex-direction: column; align-items: center; justify-content: center; padding-top: 80rpx; padding-bottom: 80rpx; }
.empty-icon { font-size: 100rpx; margin-bottom: 20rpx; }
.empty-text { font-size: 30rpx; color: #666; margin-bottom: 8rpx; }
.empty-hint { font-size: 24rpx; color: #999; }
.orders-list { display: flex; flex-direction: column; }
.order-card { background-color: #f9fafb; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; border-width: 1rpx; border-style: solid; border-color: #eee; }
.order-header { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
.order-no-wrap { display: flex; flex-direction: row; align-items: center; }
.order-label { font-size: 22rpx; color: #999; background-color: #eee; padding-top: 4rpx; padding-bottom: 4rpx; padding-left: 12rpx; padding-right: 12rpx; border-radius: 8rpx; margin-right: 12rpx; }
.order-no { font-size: 26rpx; color: #333; font-weight: 500; }
.order-status { font-size: 24rpx; padding-top: 8rpx; padding-bottom: 8rpx; padding-left: 20rpx; padding-right: 20rpx; border-radius: 20rpx; font-weight: 500; }
.status-pending { background-color: #FFF3E0; color: #FF9800; }
.status-paid { background-color: #E3F2FD; color: #2196F3; }
.status-shipped { background-color: #E8F5E9; color: #4CAF50; }
.status-completed { background-color: #F3E5F5; color: #9C27B0; }
.status-refund { background-color: #FFEBEE; color: #F44336; }
.order-goods { margin-bottom: 16rpx; }
.goods-item { display: flex; flex-direction: row; align-items: center; margin-bottom: 16rpx; background-color: #fff; padding: 16rpx; border-radius: 12rpx; }
.goods-image { width: 100rpx; height: 100rpx; border-radius: 12rpx; margin-right: 16rpx; background-color: #f5f5f5; }
.goods-info { flex: 1; }
.goods-name { font-size: 28rpx; color: #333; margin-bottom: 8rpx; font-weight: 500; }
.goods-bottom { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
.goods-spec { font-size: 22rpx; color: #999; }
.goods-qty { font-size: 24rpx; color: #999; }
.goods-price { font-size: 28rpx; color: #FF6B35; font-weight: bold; }
.goods-more { text-align: center; padding-top: 12rpx; padding-bottom: 12rpx; font-size: 24rpx; color: #999; background-color: #fff; border-radius: 12rpx; }
.order-footer { display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding-top: 16rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #eee; }
.order-time { font-size: 24rpx; color: #999; }
.order-amount-wrap { display: flex; flex-direction: row; align-items: center; }
.amount-label { font-size: 24rpx; color: #999; margin-right: 8rpx; }
.amount-value { font-size: 32rpx; font-weight: bold; color: #FF6B35; }
.safe-bottom { height: 30rpx; }
</style>
>>>>>>> local-backup-root-cyj