1653 lines
47 KiB
Plaintext
1653 lines
47 KiB
Plaintext
<!-- pages/mall/consumer/orders.uvue -->
|
||
<template>
|
||
<view class="orders-page">
|
||
<!-- 顶部标题栏 -->
|
||
<view class="orders-header">
|
||
<view class="header-search full-width">
|
||
<input
|
||
class="search-input"
|
||
type="text"
|
||
placeholder="搜索订单号或商品名称"
|
||
:value="searchKeyword"
|
||
@input="onSearchInput"
|
||
@confirm="onSearchConfirm"
|
||
/>
|
||
<text v-if="searchKeyword" class="search-clear" @click="clearSearch">×</text>
|
||
<text v-else class="search-icon">🔍</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单状态筛选 -->
|
||
<view class="order-tabs-fixed-container">
|
||
<view
|
||
:class="['tab-item-fixed', { active: activeTab === 'all' }]"
|
||
@click="switchTab('all')"
|
||
>
|
||
<text class="tab-name">全部</text>
|
||
<view v-if="activeTab === 'all'" class="active-indicator"></view>
|
||
</view>
|
||
<scroll-view scroll-x="true" class="tab-scroll-mobile" :show-scrollbar="false" :scroll-with-animation="true">
|
||
<view class="tab-container-mobile">
|
||
<view
|
||
v-for="tab in orderTabsMobile"
|
||
:key="tab.id"
|
||
:class="['tab-item-mobile', { active: activeTab === tab.id }]"
|
||
@click="switchTab(tab.id)"
|
||
>
|
||
<text class="tab-name">{{ tab.name }}</text>
|
||
<text v-if="tab.count > 0" class="tab-count">{{ tab.count }}</text>
|
||
<view v-if="activeTab === tab.id" class="active-indicator"></view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 订单列表 -->
|
||
<scroll-view
|
||
scroll-y
|
||
class="orders-content"
|
||
refresher-enabled
|
||
:refresher-triggered="refreshing"
|
||
@refresherrefresh="onRefresh"
|
||
@scrolltolower="loadMore"
|
||
>
|
||
<!-- 空状态 -->
|
||
<view v-if="!loading && orders.length === 0" class="empty-orders">
|
||
<text class="empty-icon">📦</text>
|
||
<text class="empty-title">暂无订单</text>
|
||
<text class="empty-desc">去逛逛,发现心仪的商品</text>
|
||
<button class="go-shopping-btn" @click="goShopping">去逛逛</button>
|
||
</view>
|
||
|
||
<!-- 订单列表 -->
|
||
<view v-else class="order-list">
|
||
<view
|
||
v-for="order in orders"
|
||
:key="order.id"
|
||
class="order-card"
|
||
@click="viewOrderDetail(order.id)"
|
||
>
|
||
<!-- 订单头部:显示店铺名称 -->
|
||
<view class="order-card-header">
|
||
<view class="shop-info">
|
||
<text class="shop-icon">🏪</text>
|
||
<text class="shop-name">{{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '自营店铺' }}</text>
|
||
<text class="arrow-right">›</text>
|
||
</view>
|
||
<view class="status-row">
|
||
<text :class="['order-status', getStatusClass(order.status)]">
|
||
{{ getStatusText(order.status) }}
|
||
</text>
|
||
<text class="more-btn" @click.stop="showOrderMenu(order)">⋯</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单商品 -->
|
||
<view class="order-products">
|
||
<view
|
||
v-for="product in order.products"
|
||
:key="product.id"
|
||
class="order-product"
|
||
@click="navigateToProduct(product)"
|
||
>
|
||
<image
|
||
class="product-image"
|
||
:src="product.image"
|
||
mode="aspectFill"
|
||
/>
|
||
<view class="product-info">
|
||
<view class="product-top-info">
|
||
<text class="product-name">{{ product.name }}</text>
|
||
<text class="product-spec">{{ product.spec }}</text>
|
||
</view>
|
||
<view class="product-footer">
|
||
<text class="product-price">¥{{ product.price }}</text>
|
||
<text class="product-quantity">x{{ product.quantity }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单汇总信息 -->
|
||
<view class="order-summary">
|
||
<text class="order-time">{{ formatDate(order.create_time) }}</text>
|
||
<view class="summary-right">
|
||
<text class="summary-label">共{{ order.products.length }}件商品 实付:</text>
|
||
<text class="summary-price">¥{{ order.total_amount }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单操作 -->
|
||
<view class="order-actions" @click.stop="">
|
||
<view v-if="order.status === 1" class="action-buttons">
|
||
<button class="action-btn cancel" @click="cancelOrder(order.id)">取消订单</button>
|
||
<button class="action-btn pay" @click="payOrder(order.id)">立即支付</button>
|
||
</view>
|
||
|
||
<view v-if="order.status === 2" class="action-buttons">
|
||
<button class="action-btn remind" @click="remindShipping(order.id)">提醒发货</button>
|
||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
||
</view>
|
||
|
||
<view v-if="order.status === 3" class="action-buttons">
|
||
<button class="action-btn view" @click="viewLogistics(order.id)">查看物流</button>
|
||
<button class="action-btn confirm" @click="confirmReceipt(order.id)">确认收货</button>
|
||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
||
</view>
|
||
|
||
<view v-if="order.status === 4" class="action-buttons">
|
||
<button class="action-btn review" @click="goReview(order)">评价</button>
|
||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
||
<button class="action-btn repurchase" @click="repurchase(order)">再次购买</button>
|
||
</view>
|
||
|
||
<view v-if="order.status === 5" class="action-buttons">
|
||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||
</view>
|
||
|
||
<view v-if="order.status === 6" class="action-buttons">
|
||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||
<button class="action-btn refund" @click="viewRefundProgress(order.id)">退款进度</button>
|
||
</view>
|
||
|
||
<view v-if="order.status === 7" class="action-buttons">
|
||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||
<button class="action-btn repurchase" @click="repurchase(order)">再次购买</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view v-if="loadingMore" class="loading-more">
|
||
<view class="loading-spinner"></view>
|
||
<text>加载中...</text>
|
||
</view>
|
||
|
||
<view v-if="!hasMore && orders.length > 0" class="no-more">
|
||
<text>没有更多订单了</text>
|
||
</view>
|
||
|
||
<!-- 安全区域 -->
|
||
<view class="safe-area"></view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部导航 -->
|
||
<view class="tabbar-placeholder"></view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, reactive, onMounted, computed } from 'vue'
|
||
import { onShow, onLoad, onBackPress } from '@dcloudio/uni-app'
|
||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||
|
||
// 定义标签页类型
|
||
type OrderTabItem = {
|
||
id: string,
|
||
name: string,
|
||
count: number
|
||
}
|
||
|
||
// 定义订单产品类型
|
||
type OrderProduct = {
|
||
id: string,
|
||
name: string,
|
||
price: number,
|
||
image: string,
|
||
spec: string,
|
||
quantity: number
|
||
}
|
||
|
||
// 定义订单类型
|
||
type OrderItem = {
|
||
id: string,
|
||
order_no: string,
|
||
status: number,
|
||
create_time: string,
|
||
product_amount: number,
|
||
shipping_fee: number,
|
||
total_amount: number,
|
||
merchant_id: string,
|
||
shop_name: string,
|
||
products: OrderProduct[]
|
||
}
|
||
|
||
// 响应式数据
|
||
const orders = ref<OrderItem[]>([])
|
||
const allOrdersList = ref<OrderItem[]>([]) // Store all fetched orders for client-side filtering
|
||
const loading = ref<boolean>(false)
|
||
const loadingMore = ref<boolean>(false)
|
||
const hasMore = ref<boolean>(true)
|
||
const refreshing = ref<boolean>(false)
|
||
const page = ref<number>(1)
|
||
const activeTab = ref<string>('all')
|
||
const searchKeyword = ref<string>('')
|
||
|
||
// 订单标签页 - 使用 ref 以便整体替换
|
||
const orderTabs = ref<OrderTabItem[]>([
|
||
{ id: 'all', name: '全部', count: 0 },
|
||
{ id: 'pending', name: '待付款', count: 0 },
|
||
{ id: 'shipping', name: '待发货', count: 0 },
|
||
{ id: 'delivering', name: '待收货', count: 0 },
|
||
{ id: 'completed', name: '已完成', count: 0 },
|
||
{ id: 'aftersale', name: '售后', count: 0 },
|
||
{ id: 'cancelled', name: '已取消', count: 0 }
|
||
])
|
||
|
||
// 模拟状态筛选(除去"全部"后的其余标签)
|
||
const orderTabsMobile = computed((): OrderTabItem[] => {
|
||
return orderTabs.value.filter((tab: OrderTabItem) => tab.id !== 'all')
|
||
})
|
||
|
||
|
||
// 辅助函数:获取状态码
|
||
const getStatusByTab = (tabId: string): number => {
|
||
if (tabId == 'pending') return 1
|
||
if (tabId == 'shipping') return 2
|
||
if (tabId == 'delivering') return 3
|
||
if (tabId == 'completed') return 4
|
||
if (tabId == 'cancelled') return 5
|
||
if (tabId == 'aftersale') return 6
|
||
return 0
|
||
}
|
||
|
||
// 格式化规格对象为友好的文本 - 必须在 parseSpecText 之前定义
|
||
function formatSpecObj(obj: any): string {
|
||
if (obj == null) return ''
|
||
if (typeof obj !== 'object') {
|
||
// 非对象类型直接返回字符串形式
|
||
if (typeof obj === 'string') return obj
|
||
if (typeof obj === 'number') return obj.toString()
|
||
return ''
|
||
}
|
||
|
||
try {
|
||
const objStr = JSON.stringify(obj)
|
||
const objParsed = JSON.parse(objStr)
|
||
if (objParsed == null) return ''
|
||
|
||
const specObj = objParsed as UTSJSONObject
|
||
|
||
// 使用 JSON.stringify 获取所有键
|
||
const specObjStr = JSON.stringify(specObj)
|
||
const specObjForKeys = JSON.parse(specObjStr) as UTSJSONObject
|
||
|
||
// 手动提取键值对
|
||
const parts: string[] = []
|
||
|
||
// 尝试获取已知字段
|
||
const colorVal = specObjForKeys.getString('Color')
|
||
if (colorVal != null && colorVal != '') {
|
||
parts.push('Color: ' + colorVal)
|
||
}
|
||
|
||
const sizeVal = specObjForKeys.getString('Size')
|
||
if (sizeVal != null && sizeVal != '') {
|
||
parts.push('Size: ' + sizeVal)
|
||
}
|
||
|
||
const defaultVal = specObjForKeys.getString('默认')
|
||
if (defaultVal != null && defaultVal != '') {
|
||
parts.push('默认: ' + defaultVal)
|
||
}
|
||
|
||
// 如果没有匹配到已知字段,尝试直接显示 JSON
|
||
if (parts.length === 0) {
|
||
// 尝试遍历对象
|
||
const objAny = specObjForKeys as any
|
||
if (objAny != null) {
|
||
return specObjStr.replace(/[{}"]/g, '').replace(/:/g, ': ').replace(/,/g, ' | ')
|
||
}
|
||
}
|
||
|
||
return parts.join(' | ')
|
||
} catch (e) {
|
||
return ''
|
||
}
|
||
}
|
||
|
||
// 辅助函数:解析规格文本
|
||
function parseSpecText(specs: any): string {
|
||
if (specs == null) return ''
|
||
if (typeof specs === 'string') {
|
||
// 如果是 JSON 字符串,尝试解析
|
||
if (specs.startsWith('{') || specs.startsWith('[')) {
|
||
try {
|
||
const parsed = JSON.parse(specs)
|
||
if (parsed == null) return specs
|
||
return formatSpecObj(parsed)
|
||
} catch (e) {
|
||
return specs
|
||
}
|
||
}
|
||
return specs
|
||
}
|
||
// 对于对象类型,格式化显示
|
||
return formatSpecObj(specs)
|
||
}
|
||
|
||
// 辅助函数:更新标签计数
|
||
const updateTabsCounts = (allOrders: OrderItem[]) => {
|
||
const countAll = allOrders.length
|
||
const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length
|
||
const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length
|
||
const countDelivering = allOrders.filter((o: OrderItem) => o.status === 3).length
|
||
const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length
|
||
const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length
|
||
const countAftersale = allOrders.filter((o: OrderItem) => o.status === 6 || o.status === 7).length
|
||
|
||
orderTabs.value[0].count = countAll
|
||
orderTabs.value[1].count = countPending
|
||
orderTabs.value[2].count = countShipping
|
||
orderTabs.value[3].count = countDelivering
|
||
orderTabs.value[4].count = countCompleted
|
||
orderTabs.value[5].count = countAftersale
|
||
orderTabs.value[6].count = countCancelled
|
||
}
|
||
|
||
// 辅助函数:按标签筛选订单
|
||
const filterOrdersByTab = () => {
|
||
if (activeTab.value === 'all') {
|
||
orders.value = allOrdersList.value
|
||
} else if (activeTab.value === 'aftersale') {
|
||
orders.value = allOrdersList.value.filter((o: OrderItem) => {
|
||
return o.status === 6 || o.status === 7
|
||
})
|
||
} else {
|
||
const targetStatus = getStatusByTab(activeTab.value)
|
||
orders.value = allOrdersList.value.filter((o: OrderItem) => {
|
||
return o.status === targetStatus
|
||
})
|
||
}
|
||
}
|
||
|
||
// 加载订单数据
|
||
const loadOrders = async () => {
|
||
loading.value = true
|
||
|
||
try {
|
||
// Fetch all orders from Supabase (status=0)
|
||
const fetchedOrders = await supabaseService.getOrders(0)
|
||
console.log('[loadOrders] 获取到订单数量:', fetchedOrders.length)
|
||
|
||
// Map to View Model
|
||
const mappedOrders: OrderItem[] = []
|
||
for (let i = 0; i < fetchedOrders.length; i++) {
|
||
const order = fetchedOrders[i]
|
||
// 使用 JSON 序列化转换
|
||
const orderStr = JSON.stringify(order)
|
||
const orderParsed = JSON.parse(orderStr)
|
||
if (orderParsed == null) continue
|
||
const orderObj = orderParsed as UTSJSONObject
|
||
|
||
const itemsRaw = orderObj.get('ml_order_items')
|
||
const productsList: OrderProduct[] = []
|
||
|
||
console.log('[loadOrders] 订单商品数据:', itemsRaw)
|
||
|
||
if (itemsRaw != null) {
|
||
// 先检查是否为数组
|
||
if (Array.isArray(itemsRaw)) {
|
||
const items = itemsRaw as any[]
|
||
console.log('[loadOrders] 商品数量:', items.length)
|
||
for (let j = 0; j < items.length; j++) {
|
||
const item = items[j]
|
||
const itemStr = JSON.stringify(item)
|
||
const itemParsed = JSON.parse(itemStr)
|
||
if (itemParsed == null) continue
|
||
const itemObj = itemParsed as UTSJSONObject
|
||
|
||
const specRaw = itemObj.get('specifications')
|
||
const specText = specRaw != null ? parseSpecText(specRaw) : ''
|
||
|
||
const productId = itemObj.getString('product_id')
|
||
const productName = itemObj.getString('product_name')
|
||
const price = itemObj.getNumber('price')
|
||
const imageUrl = itemObj.getString('image_url')
|
||
const quantity = itemObj.getNumber('quantity')
|
||
|
||
console.log('[loadOrders] 商品:', productName, '图片:', imageUrl, '规格:', specText)
|
||
|
||
const productItem: OrderProduct = {
|
||
id: productId ?? '',
|
||
name: productName ?? '未知商品',
|
||
price: price ?? 0,
|
||
image: imageUrl ?? '/static/default-product.png',
|
||
spec: specText,
|
||
quantity: quantity ?? 1
|
||
}
|
||
productsList.push(productItem)
|
||
}
|
||
}
|
||
}
|
||
|
||
const orderId = orderObj.getString('id')
|
||
const orderNo = orderObj.getString('order_no')
|
||
const orderStatus = orderObj.getNumber('order_status')
|
||
const createdAt = orderObj.getString('created_at')
|
||
const productAmount = orderObj.getNumber('product_amount')
|
||
const shippingFee = orderObj.getNumber('shipping_fee')
|
||
const totalAmount = orderObj.getNumber('total_amount')
|
||
const paidAmount = orderObj.getNumber('paid_amount')
|
||
const merchantId = orderObj.getString('merchant_id')
|
||
|
||
// 从关联查询的 ml_shops 表获取店铺名称
|
||
let shopName = '自营店铺'
|
||
const shopsRaw = orderObj.get('ml_shops')
|
||
if (shopsRaw != null) {
|
||
const shopStr = JSON.stringify(shopsRaw)
|
||
const shopParsed = JSON.parse(shopStr)
|
||
if (shopParsed != null) {
|
||
const shopObj = shopParsed as UTSJSONObject
|
||
const shopNameFromDb = shopObj.getString('shop_name')
|
||
if (shopNameFromDb != null && shopNameFromDb != '') {
|
||
shopName = shopNameFromDb
|
||
}
|
||
}
|
||
} else if (merchantId != null && merchantId != '') {
|
||
shopName = '商家店铺'
|
||
}
|
||
|
||
console.log('[loadOrders] 订单号:', orderNo, '店铺:', shopName, '商品数:', productsList.length)
|
||
|
||
// 如果没有商品数据,添加一个占位商品
|
||
if (productsList.length === 0) {
|
||
const placeholderProduct: OrderProduct = {
|
||
id: 'placeholder',
|
||
name: '订单商品',
|
||
price: totalAmount ?? paidAmount ?? 0,
|
||
image: '/static/default-product.png',
|
||
spec: '',
|
||
quantity: 1
|
||
}
|
||
productsList.push(placeholderProduct)
|
||
}
|
||
|
||
const mappedOrder: OrderItem = {
|
||
id: orderId ?? '',
|
||
order_no: orderNo ?? '',
|
||
status: orderStatus ?? 1,
|
||
create_time: createdAt ?? '',
|
||
product_amount: productAmount ?? 0,
|
||
shipping_fee: shippingFee ?? 0,
|
||
total_amount: totalAmount ?? paidAmount ?? 0,
|
||
merchant_id: merchantId ?? '',
|
||
shop_name: shopName,
|
||
products: productsList
|
||
}
|
||
mappedOrders.push(mappedOrder)
|
||
}
|
||
|
||
// Sort by created_at desc - 直接使用 OrderItem 类型访问属性
|
||
mappedOrders.sort((a: OrderItem, b: OrderItem) => {
|
||
const timeA = new Date(a.create_time).getTime()
|
||
const timeB = new Date(b.create_time).getTime()
|
||
return timeB - timeA
|
||
})
|
||
|
||
allOrdersList.value = mappedOrders
|
||
|
||
// Update tab counts
|
||
updateTabsCounts(mappedOrders)
|
||
|
||
// Apply current tab filter
|
||
filterOrdersByTab()
|
||
|
||
} catch (err) {
|
||
console.error('加载订单异常:', err)
|
||
uni.showToast({ title: '加载订单失败', icon: 'none' })
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 生命周期
|
||
onLoad((options) => {
|
||
if (options == null) return
|
||
const statusVal = options['status']
|
||
if (statusVal != null) {
|
||
const status = statusVal as string
|
||
if (['all', 'pending', 'shipping', 'delivering', 'completed', 'aftersale', 'cancelled'].includes(status)) {
|
||
activeTab.value = status
|
||
}
|
||
}
|
||
const typeVal = options['type']
|
||
if (typeVal != null) {
|
||
const type = typeVal as string
|
||
if (type === 'pending') activeTab.value = 'pending'
|
||
else if (type === 'shipped') activeTab.value = 'delivering' // 映射到待收货
|
||
else if (type === 'review') activeTab.value = 'completed' // 映射到已完成
|
||
else if (type === 'refund') activeTab.value = 'all' // 申请售后默认显示全部
|
||
}
|
||
})
|
||
|
||
onShow(() => {
|
||
loadOrders()
|
||
})
|
||
|
||
const formatDate = (isoString: string): string => {
|
||
if (isoString == '') return ''
|
||
const date = new Date(isoString)
|
||
return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||
}
|
||
|
||
// 辅助函数:获取当前订单数据(必须在 performSearch 之前定义)
|
||
function getCurrentOrderData(): OrderItem[] {
|
||
return allOrdersList.value
|
||
}
|
||
|
||
// 搜索执行函数(必须在 onSearchInput 等之前定义)
|
||
const performSearch = () => {
|
||
const keyword = searchKeyword.value.trim().toLowerCase()
|
||
if (keyword == '') {
|
||
loadOrders()
|
||
return
|
||
}
|
||
|
||
// 在当前订单数据中搜索
|
||
const allOrders = getCurrentOrderData()
|
||
const filtered = allOrders.filter((order: any) => {
|
||
const orderObj = order as Record<string, any>
|
||
// 搜索订单号
|
||
const orderNo = orderObj['order_no'] as string
|
||
if (orderNo != null && orderNo.toLowerCase().includes(keyword)) {
|
||
return true
|
||
}
|
||
|
||
// 搜索商品名称
|
||
const products = orderObj['products']
|
||
if (products != null && Array.isArray(products)) {
|
||
return products.some((product: any) => {
|
||
const productObj = product as Record<string, any>
|
||
const name = productObj['name'] as string
|
||
return name != null && name.toLowerCase().includes(keyword)
|
||
})
|
||
}
|
||
|
||
return false
|
||
})
|
||
|
||
orders.value = filtered
|
||
}
|
||
|
||
// 搜索相关函数
|
||
const onSearchInput = (e: any) => {
|
||
const eObj = e as Record<string, any>
|
||
const detail = eObj['detail'] as Record<string, any>
|
||
searchKeyword.value = detail['value'] as string
|
||
performSearch()
|
||
}
|
||
|
||
const onSearchConfirm = () => {
|
||
performSearch()
|
||
}
|
||
|
||
const clearSearch = () => {
|
||
searchKeyword.value = ''
|
||
performSearch()
|
||
}
|
||
|
||
const formatSpec = (specs: any): string => {
|
||
if (specs == null) return ''
|
||
if (typeof specs === 'string') return specs
|
||
if (typeof specs === 'object') {
|
||
return JSON.stringify(specs)
|
||
}
|
||
return ''
|
||
}
|
||
|
||
// 切换标签
|
||
const switchTab = (tabId: string) => {
|
||
activeTab.value = tabId
|
||
filterOrdersByTab()
|
||
}
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (status: number): string => {
|
||
if (status == 1) return '待付款'
|
||
if (status == 2) return '待发货'
|
||
if (status == 3) return '待收货'
|
||
if (status == 4) return '已完成'
|
||
if (status == 5) return '已取消'
|
||
if (status == 6) return '退款中'
|
||
if (status == 7) return '已退款'
|
||
return '未知状态'
|
||
}
|
||
|
||
// 获取状态类名
|
||
const getStatusClass = (status: number): string => {
|
||
if (status == 1) return 'status-pending'
|
||
if (status == 2) return 'status-shipping'
|
||
if (status == 3) return 'status-delivering'
|
||
if (status == 4) return 'status-completed'
|
||
if (status == 5) return 'status-cancelled'
|
||
if (status == 6) return 'status-refunding'
|
||
if (status == 7) return 'status-refunded'
|
||
return 'status-unknown'
|
||
}
|
||
|
||
// 联系卖家
|
||
const contactSeller = (order: OrderItem) => {
|
||
if (order.merchant_id != '') {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/chat?merchantId=${order.merchant_id}`
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: '暂无卖家联系方式',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 删除订单
|
||
const deleteOrder = (orderId: string) => {
|
||
uni.showModal({
|
||
title: '删除订单',
|
||
content: '确定要删除此订单吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.showLoading({ title: '删除中...' })
|
||
supabaseService.deleteOrder(orderId).then(() => {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '订单已删除',
|
||
icon: 'success'
|
||
})
|
||
loadOrders()
|
||
}).catch(() => {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '删除失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 下拉刷新
|
||
const onRefresh = () => {
|
||
refreshing.value = true
|
||
setTimeout(() => {
|
||
loadOrders()
|
||
refreshing.value = false
|
||
uni.showToast({
|
||
title: '刷新成功',
|
||
icon: 'success'
|
||
})
|
||
}, 1000)
|
||
}
|
||
|
||
// 上拉加载更多
|
||
const loadMore = () => {
|
||
if (loadingMore.value || !hasMore.value) return
|
||
|
||
// 暂未实现分页,直接返回
|
||
hasMore.value = false
|
||
}
|
||
|
||
// 订单操作函数
|
||
const cancelOrder = (orderId: string) => {
|
||
uni.showModal({
|
||
title: '确认取消',
|
||
content: '确定要取消此订单吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.showLoading({ title: '取消中...' })
|
||
supabaseService.cancelOrder(orderId).then((success) => {
|
||
uni.hideLoading()
|
||
if (success) {
|
||
uni.showToast({
|
||
title: '订单已取消',
|
||
icon: 'success'
|
||
})
|
||
loadOrders()
|
||
} else {
|
||
uni.showToast({
|
||
title: '取消失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}).catch(() => {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '取消失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
const payOrder = (orderId: string) => {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/payment?orderId=${orderId}`
|
||
})
|
||
}
|
||
|
||
const remindShipping = async (orderId: string) => {
|
||
// 基础提醒
|
||
uni.showLoading({ title: '正在提醒...' })
|
||
|
||
try {
|
||
// 查找订单中的商家ID
|
||
const order = orders.value.find(o => o.id === orderId)
|
||
if (order != null) {
|
||
const merchantId = order.merchant_id
|
||
const orderNo = order.order_no
|
||
|
||
if (merchantId != '') {
|
||
// 向商家发送自动催单消息
|
||
const message = `你好,我的订单[${orderNo}]还没有发货,请尽快安排,谢谢。`
|
||
const success = await supabaseService.sendChatMessage(message, merchantId)
|
||
|
||
if (success) {
|
||
console.log('催单消息发送成功')
|
||
} else {
|
||
console.warn('催单消息发送失败,可能是由于网络原因')
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('提醒发货异常:', e)
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
|
||
uni.showToast({
|
||
title: '已提醒卖家发货',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
|
||
const viewLogistics = (orderId: string) => {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/logistics?orderId=${orderId}`
|
||
})
|
||
}
|
||
|
||
// goReview 必须在 doConfirmReceipt 之前定义,因为 doConfirmReceipt 会调用它
|
||
const goReview = (order: any) => {
|
||
const orderObj = order as Record<string, any>
|
||
const products = orderObj['products'] as any[]
|
||
const productIds = products.map((p: any) => {
|
||
const pObj = p as Record<string, any>
|
||
const pid = pObj['id']
|
||
return pid != null ? pid as string : ''
|
||
}).join(',')
|
||
const orderId = orderObj['id'] as string
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/review?orderId=${orderId}&productIds=${productIds}`
|
||
})
|
||
}
|
||
|
||
const doConfirmReceipt = async (orderId: string) => {
|
||
uni.showLoading({ title: '处理中...' })
|
||
try {
|
||
const result = await supabaseService.confirmReceipt(orderId)
|
||
uni.hideLoading()
|
||
|
||
if (result.success) {
|
||
uni.showToast({
|
||
title: '收货成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 更新本地状态
|
||
const index = orders.value.findIndex((o: any) => {
|
||
const obj = o as Record<string, any>
|
||
return obj['id'] === orderId
|
||
})
|
||
if (index !== -1) {
|
||
const orderObj = orders.value[index] as Record<string, any>
|
||
orderObj['status'] = 4
|
||
orders.value = [...orders.value]
|
||
}
|
||
|
||
// 跳转到评价页面
|
||
setTimeout(() => {
|
||
const order = orders.value.find((o: any) => {
|
||
const obj = o as Record<string, any>
|
||
return obj['id'] === orderId
|
||
})
|
||
if (order != null) {
|
||
goReview(order)
|
||
}
|
||
}, 1000)
|
||
} else {
|
||
uni.showToast({
|
||
title: result.error ?? '确认收货失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '系统异常',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
const confirmReceipt = (orderId: string) => {
|
||
uni.showModal({
|
||
title: '确认收货',
|
||
content: '请确认您已收到商品,且商品无误',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
doConfirmReceipt(orderId)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
const repurchase = (order: any) => {
|
||
const orderObj = order as Record<string, any>
|
||
const products = orderObj['products'] as any[]
|
||
|
||
if (products == null || products.length === 0) {
|
||
uni.showToast({
|
||
title: '订单无商品',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
uni.showLoading({ title: '处理中...' })
|
||
|
||
let completed = 0
|
||
const total = products.length
|
||
let successCount = 0
|
||
|
||
for (let i = 0; i < products.length; i++) {
|
||
const pObj = products[i] as Record<string, any>
|
||
const productId = pObj['id'] as string
|
||
const merchantId = orderObj['merchant_id'] as string
|
||
|
||
if (productId != null && productId !== '') {
|
||
supabaseService.addToCart(productId, 1, '', merchantId ?? '').then((success) => {
|
||
completed++
|
||
if (success) successCount++
|
||
if (completed === total) {
|
||
uni.hideLoading()
|
||
if (successCount > 0) {
|
||
uni.showToast({
|
||
title: `已添加${successCount}件商品`,
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: '添加失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}).catch(() => {
|
||
completed++
|
||
if (completed === total) {
|
||
uni.hideLoading()
|
||
if (successCount > 0) {
|
||
uni.showToast({
|
||
title: `已添加${successCount}件商品`,
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: '添加失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
} else {
|
||
completed++
|
||
if (completed === total) {
|
||
uni.hideLoading()
|
||
if (successCount > 0) {
|
||
uni.showToast({
|
||
title: `已添加${successCount}件商品`,
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: '添加失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const viewOrderDetail = (orderId: string) => {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/order-detail?id=${orderId}`
|
||
})
|
||
}
|
||
|
||
const onApplyRefund = (order: any) => {
|
||
const orderObj = order as Record<string, any>
|
||
const orderId = orderObj['id']
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/apply-refund?orderId=${orderId}`
|
||
})
|
||
}
|
||
|
||
const viewRefundProgress = (orderId: string) => {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/refund?orderId=${orderId}`
|
||
})
|
||
}
|
||
|
||
// 处理订单操作
|
||
const handleOrderAction = (order: OrderItem, action: string) => {
|
||
if (action === '取消订单') {
|
||
cancelOrder(order.id)
|
||
} else if (action === '联系卖家') {
|
||
contactSeller(order)
|
||
} else if (action === '提醒发货') {
|
||
remindShipping(order.id)
|
||
} else if (action === '申请退款' || action === '申请售后') {
|
||
onApplyRefund(order)
|
||
} else if (action === '查看物流') {
|
||
viewLogistics(order.id)
|
||
} else if (action === '确认收货') {
|
||
confirmReceipt(order.id)
|
||
} else if (action === '再次购买') {
|
||
repurchase(order)
|
||
} else if (action === '删除订单') {
|
||
deleteOrder(order.id)
|
||
} else if (action === '退款进度') {
|
||
viewRefundProgress(order.id)
|
||
}
|
||
}
|
||
|
||
// 显示订单操作菜单
|
||
const showOrderMenu = (order: OrderItem) => {
|
||
const status = order.status
|
||
let actions: string[] = []
|
||
|
||
if (status === 1) {
|
||
actions = ['取消订单', '联系卖家']
|
||
} else if (status === 2) {
|
||
actions = ['提醒发货', '申请退款', '联系卖家']
|
||
} else if (status === 3) {
|
||
actions = ['查看物流', '确认收货', '申请退款', '联系卖家']
|
||
} else if (status === 4) {
|
||
actions = ['申请售后', '再次购买', '联系卖家']
|
||
} else if (status === 5) {
|
||
actions = ['删除订单', '再次购买', '联系卖家']
|
||
} else if (status === 6) {
|
||
actions = ['退款进度', '联系卖家']
|
||
} else if (status === 7) {
|
||
actions = ['再次购买', '联系卖家']
|
||
}
|
||
|
||
uni.showActionSheet({
|
||
itemList: actions,
|
||
success: (res) => {
|
||
const action = actions[res.tapIndex]
|
||
handleOrderAction(order, action)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 导航函数
|
||
const navigateToSearch = () => {
|
||
uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
||
}
|
||
|
||
const navigateToProduct = (product: any) => {
|
||
const productObj = product as Record<string, any>
|
||
const productId = productObj['id']
|
||
uni.navigateTo({ url: `/pages/mall/consumer/product-detail?id=${productId}` })
|
||
}
|
||
|
||
const goShopping = () => {
|
||
uni.switchTab({ url: '/pages/mall/consumer/index' })
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.orders-page {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background-color: #f5f5f5;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 头部 */
|
||
.orders-header {
|
||
background-color: white;
|
||
padding: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-bottom: 1px solid #eee;
|
||
z-index: 10;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.header-search.full-width {
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
width: 100%;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
height: 36px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 18px;
|
||
padding: 0 40px 0 16px;
|
||
font-size: 14px;
|
||
background-color: #f5f5f5;
|
||
color: #333;
|
||
width: 100%;
|
||
}
|
||
|
||
.search-input::placeholder {
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
.search-input:focus {
|
||
border-color: #ff5000;
|
||
background-color: white;
|
||
}
|
||
|
||
.search-icon {
|
||
position: absolute;
|
||
right: 12px;
|
||
font-size: 18px;
|
||
color: #999;
|
||
}
|
||
|
||
.search-clear {
|
||
position: absolute;
|
||
right: 12px;
|
||
font-size: 20px;
|
||
color: #999;
|
||
width: 20px;
|
||
height: 20px;
|
||
line-height: 18px;
|
||
text-align: center;
|
||
border-radius: 10px; /* fixed 50% */
|
||
background-color: #ddd;
|
||
/* cursor: pointer; removed */
|
||
}
|
||
|
||
/* 标签页 */
|
||
.order-tabs-fixed-container {
|
||
background-color: #ffffff;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
width: 100%;
|
||
z-index: 10;
|
||
height: 50px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 安卓端滚动修复 */
|
||
.orders-content {
|
||
flex: 1;
|
||
width: 100%;
|
||
height: 0; /* 关键:强制 flex: 1 生效 */
|
||
}
|
||
|
||
.orders-header {
|
||
background-color: white;
|
||
padding: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-bottom: 1px solid #eee;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.tab-item-fixed {
|
||
padding: 0 15px;
|
||
text-align: center;
|
||
position: relative;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
min-width: 60px;
|
||
height: 100%;
|
||
}
|
||
|
||
.tab-item-fixed.active {
|
||
color: #ff5000;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.tab-scroll-mobile {
|
||
flex: 1;
|
||
white-space: nowrap;
|
||
/* 移除 width: 0,改用更稳健的安卓适配方式 */
|
||
display: flex;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.tab-container-mobile {
|
||
display: flex;
|
||
flex-direction: row;
|
||
/* 加上这个确保容器能够横向撑开 */
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
.tab-item-mobile {
|
||
padding: 15px 15px;
|
||
text-align: center;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: row; /* 显式声明 */
|
||
justify-content: center;
|
||
align-items: center;
|
||
white-space: nowrap;
|
||
flex-shrink: 0; /* 绝对不能被压缩,否则无法滚动 */
|
||
}
|
||
|
||
.tab-item-mobile.active {
|
||
color: #ff5000;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.active-indicator {
|
||
position: absolute;
|
||
bottom: 0px;
|
||
left: 10px;
|
||
right: 10px;
|
||
height: 3px;
|
||
background-color: #ff5000;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.tab-name {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.tab-count {
|
||
margin-left: 4px;
|
||
background-color: #ff5000;
|
||
color: white;
|
||
font-size: 10px;
|
||
padding: 1px 4px;
|
||
border-radius: 8px;
|
||
min-width: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 内容区 */
|
||
.orders-content {
|
||
flex: 1;
|
||
width: 100%;
|
||
height: 0; /* 关键:强制让 flex:1 在安卓端生效,防止内容撑开父容器 */
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-orders {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 80px 20px;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 80px;
|
||
color: #ddd;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 18px;
|
||
color: #666;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.empty-desc {
|
||
font-size: 14px;
|
||
color: #999;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.go-shopping-btn {
|
||
background-color: #ff5000;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 25px;
|
||
padding: 10px 40px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* 订单列表 */
|
||
.order-list {
|
||
padding: 10px;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.order-card {
|
||
background-color: white;
|
||
border-radius: 10px;
|
||
margin-bottom: 10px;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
flex-shrink: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 订单头部 */
|
||
.order-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
}
|
||
|
||
.order-no {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
.order-status {
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.status-pending {
|
||
color: #ff5000;
|
||
}
|
||
|
||
.status-shipping {
|
||
color: #ff9500;
|
||
}
|
||
|
||
.status-delivering {
|
||
color: #007aff;
|
||
}
|
||
|
||
.status-completed {
|
||
color: #34c759;
|
||
}
|
||
|
||
.status-cancelled {
|
||
color: #999;
|
||
}
|
||
|
||
.status-refunding {
|
||
color: #ff5000;
|
||
}
|
||
|
||
.status-refunded {
|
||
color: #999;
|
||
}
|
||
|
||
/* 订单商品 */
|
||
.order-products {
|
||
padding: 15px;
|
||
}
|
||
|
||
.order-product {
|
||
display: flex;
|
||
flex-direction: row; /* 显式声明横向排列 */
|
||
margin-bottom: 15px;
|
||
width: 100%;
|
||
}
|
||
|
||
.order-product:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.product-image {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 8px;
|
||
margin-right: 12px;
|
||
flex-shrink: 0; /* 防止图片被压缩 */
|
||
}
|
||
|
||
.product-info {
|
||
flex: 1; /* 占据右侧剩余所有空间 */
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
height: 80px;
|
||
overflow: hidden; /* 防止文字溢出 */
|
||
}
|
||
|
||
.product-top-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 14px;
|
||
color: #333;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
lines: 2;
|
||
}
|
||
|
||
.product-spec {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.product-footer {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.product-price {
|
||
font-size: 16px;
|
||
color: #ff5000;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.product-quantity {
|
||
font-size: 13px;
|
||
color: #999;
|
||
}
|
||
|
||
/* 订单信息 */
|
||
.order-info {
|
||
padding: 15px;
|
||
border-top: 1px solid #f5f5f5;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
}
|
||
|
||
.info-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.info-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.info-row.total {
|
||
margin-top: 8px;
|
||
padding-top: 8px;
|
||
border-top: 1px solid #f5f5f5;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.total-price {
|
||
font-size: 18px;
|
||
color: #ff5000;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 订单操作 */
|
||
.order-actions {
|
||
padding: 15px;
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
/* gap: 10px; removed */
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 6px 15px;
|
||
border-radius: 15px;
|
||
font-size: 13px;
|
||
border: 1px solid;
|
||
background-color: transparent; /* fixed background: none */
|
||
margin-left: 10px; /* alternative to gap */
|
||
}
|
||
|
||
.action-btn.cancel {
|
||
color: #666;
|
||
border-color: #ccc;
|
||
}
|
||
|
||
.action-btn.pay {
|
||
color: #ff5000;
|
||
border-color: #ff5000;
|
||
}
|
||
|
||
.action-btn.remind {
|
||
color: #666;
|
||
border-color: #ccc;
|
||
}
|
||
|
||
.action-btn.view {
|
||
color: #666;
|
||
border-color: #ccc;
|
||
}
|
||
|
||
.action-btn.confirm {
|
||
color: #34c759;
|
||
border-color: #34c759;
|
||
}
|
||
|
||
.action-btn.review {
|
||
color: #ff9500;
|
||
border-color: #ff9500;
|
||
}
|
||
|
||
.action-btn.repurchase {
|
||
color: #ff5000;
|
||
border-color: #ff5000;
|
||
}
|
||
|
||
/* 加载更多 */
|
||
.loading-more {
|
||
padding: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 24px;
|
||
height: 24px;
|
||
border: 2px solid #f0f5ff;
|
||
border-top-color: #ff5000;
|
||
border-radius: 12px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.no-more {
|
||
text-align: center;
|
||
color: #999;
|
||
font-size: 13px;
|
||
padding: 20px 0;
|
||
}
|
||
|
||
/* 安全区域 */
|
||
.safe-area {
|
||
height: 20px;
|
||
}
|
||
|
||
/* 底部导航占位 */
|
||
.tabbar-placeholder {
|
||
height: 50px;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
/* 响应式适配 */
|
||
@media screen and (min-width: 768px) {
|
||
.order-list {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
width: 100%;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
justify-content: flex-start;
|
||
align-content: flex-start;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.order-card {
|
||
width: 48%;
|
||
margin: 0 1% 20px 1%;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||
flex: none;
|
||
box-sizing: border-box;
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 1200px) {
|
||
.order-card {
|
||
width: 31%;
|
||
margin: 0 1% 20px 1%;
|
||
flex: none;
|
||
box-sizing: border-box;
|
||
}
|
||
}
|
||
|
||
/* 订单卡片新样式 */
|
||
.order-card-header {
|
||
padding: 12px 15px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 1px solid #f9f9f9;
|
||
}
|
||
|
||
.status-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.more-btn {
|
||
font-size: 18px;
|
||
color: #999;
|
||
margin-left: 8px;
|
||
padding: 0 5px;
|
||
}
|
||
|
||
.shop-info {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.shop-icon {
|
||
font-size: 16px;
|
||
margin-right: 6px;
|
||
}
|
||
|
||
.shop-name {
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
max-width: 150px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
lines: 1;
|
||
}
|
||
|
||
.arrow-right {
|
||
font-size: 14px;
|
||
color: #ccc;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
.product-title-row, .product-spec-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.product-title-row {
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.product-spec-row {
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.order-summary {
|
||
padding: 10px 15px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-top: 1px solid #f9f9f9;
|
||
}
|
||
|
||
.order-time {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
|
||
.summary-right {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.summary-label {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.summary-price {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
@media screen and (max-width: 320px) {
|
||
.tab-item {
|
||
padding: 0 10px;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 6px 10px;
|
||
font-size: 12px;
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 415px) {
|
||
.order-card {
|
||
margin-bottom: 15px;
|
||
}
|
||
}
|
||
</style>
|
||
|