1047 lines
24 KiB
Plaintext
1047 lines
24 KiB
Plaintext
<!-- 商家端 - 订单管理页面 -->
|
||
<template>
|
||
<view class="orders-page">
|
||
<!-- 标签页切换 -->
|
||
<view class="tabs-container">
|
||
<view class="tabs-scroll">
|
||
<view
|
||
v-for="(tab, index) in tabs"
|
||
:key="index"
|
||
class="tab-item"
|
||
:class="{ active: currentTab === tab.status }"
|
||
@click="switchTab(tab.status)"
|
||
>
|
||
<text class="tab-text">{{ tab.name }}</text>
|
||
<view v-if="tab.count > 0" class="tab-badge">{{ tab.count }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 搜索栏 -->
|
||
<view class="search-bar">
|
||
<input
|
||
class="search-input"
|
||
type="text"
|
||
v-model="searchKeyword"
|
||
placeholder="搜索订单号/商品名称"
|
||
@confirm="handleSearch"
|
||
/>
|
||
<view class="search-btn" @click="handleSearch">搜索</view>
|
||
</view>
|
||
|
||
<!-- 订单列表 -->
|
||
<scroll-view
|
||
class="orders-list"
|
||
scroll-y
|
||
:refresher-enabled="true"
|
||
:refresher-triggered="refreshing"
|
||
@refresherrefresh="onRefresh"
|
||
@scrolltolower="loadMore"
|
||
>
|
||
<view v-if="loading && orders.length === 0" class="loading-container">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<view v-else-if="orders.length === 0" class="empty-container">
|
||
<text class="empty-icon">📋</text>
|
||
<text class="empty-text">暂无订单</text>
|
||
</view>
|
||
|
||
<view v-else>
|
||
<view
|
||
v-for="order in orders"
|
||
:key="order.id"
|
||
class="order-card"
|
||
@click="viewOrderDetail(order.id)"
|
||
>
|
||
<!-- 订单头部 -->
|
||
<view class="order-header">
|
||
<view class="order-info-left">
|
||
<text class="order-no">订单号: {{ order.order_no }}</text>
|
||
<text class="order-time">{{ formatTime(order.created_at) }}</text>
|
||
</view>
|
||
<text class="order-status" :class="'status-' + order.order_status">
|
||
{{ getStatusText(order.order_status) }}
|
||
</text>
|
||
</view>
|
||
|
||
<!-- 订单商品 -->
|
||
<view class="order-products">
|
||
<view
|
||
v-for="item in order.items"
|
||
:key="item.id"
|
||
class="product-item"
|
||
>
|
||
<image
|
||
:src="item.image_url || '/static/images/default-product.png'"
|
||
class="product-image"
|
||
mode="aspectFill"
|
||
/>
|
||
<view class="product-info">
|
||
<text class="product-name">{{ item.product_name }}</text>
|
||
<text class="product-spec">{{ item.sku_name || '标准规格' }}</text>
|
||
</view>
|
||
<view class="product-right">
|
||
<text class="product-price">¥{{ item.price }}</text>
|
||
<text class="product-quantity">x{{ item.quantity }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单底部 -->
|
||
<view class="order-footer">
|
||
<view class="order-amount">
|
||
<text class="amount-label">共{{ getTotalQuantity(order.items) }}件商品</text>
|
||
<text class="amount-value">合计: ¥{{ order.total_amount }}</text>
|
||
</view>
|
||
<view class="order-actions">
|
||
<view
|
||
v-if="order.order_status === 2"
|
||
class="action-btn primary"
|
||
@click.stop="shipOrder(order)"
|
||
>
|
||
发货
|
||
</view>
|
||
<view
|
||
v-if="order.order_status === 3"
|
||
class="action-btn info"
|
||
@click.stop="viewLogistics(order)"
|
||
>
|
||
查看物流
|
||
</view>
|
||
<view
|
||
v-if="order.order_status === -1 || order.order_status === 5"
|
||
class="action-btn default"
|
||
@click.stop="deleteOrder(order)"
|
||
>
|
||
删除
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="loadingMore" class="load-more">
|
||
<text class="load-more-text">加载中...</text>
|
||
</view>
|
||
|
||
<view v-if="!hasMore && orders.length > 0" class="no-more">
|
||
<text class="no-more-text">没有更多了</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 发货弹窗 -->
|
||
<view v-if="showShipModal" class="modal-mask" @click="closeShipModal">
|
||
<view class="modal-content" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">发货</text>
|
||
<text class="modal-close" @click="closeShipModal">×</text>
|
||
</view>
|
||
<view class="modal-body">
|
||
<view class="form-item">
|
||
<text class="form-label">物流公司</text>
|
||
<picker
|
||
class="form-picker"
|
||
:range="logisticsCompanies"
|
||
range-key="name"
|
||
@change="onLogisticsChange"
|
||
>
|
||
<view class="picker-value">
|
||
{{ selectedLogistics?.name || '请选择物流公司' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="form-label">物流单号</text>
|
||
<input
|
||
class="form-input"
|
||
v-model="trackingNumber"
|
||
placeholder="请输入物流单号"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view class="modal-footer">
|
||
<view class="modal-btn cancel" @click="closeShipModal">取消</view>
|
||
<view class="modal-btn confirm" @click="confirmShip">确认发货</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="uts">
|
||
import supa from '@/components/supadb/aksupainstance.uts'
|
||
|
||
type OrderItemType = {
|
||
id: string
|
||
order_id: string
|
||
product_id: string
|
||
sku_id: string
|
||
product_name: string
|
||
sku_name: string
|
||
price: number
|
||
quantity: number
|
||
image_url: string
|
||
sku_snapshot: string
|
||
}
|
||
|
||
type OrderType = {
|
||
id: string
|
||
order_no: string
|
||
user_id: string
|
||
merchant_id: string
|
||
order_status: number
|
||
total_amount: number
|
||
product_amount: number
|
||
shipping_fee: number
|
||
paid_amount: number
|
||
shipping_address: string
|
||
remark: string
|
||
created_at: string
|
||
updated_at: string
|
||
items: OrderItemType[]
|
||
}
|
||
|
||
type TabType = {
|
||
name: string
|
||
status: number
|
||
count: number
|
||
}
|
||
|
||
type LogisticsType = {
|
||
name: string | null
|
||
code: string | null
|
||
}
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
tabs: [
|
||
{ name: '全部', status: -2, count: 0 },
|
||
{ name: '待付款', status: 1, count: 0 },
|
||
{ name: '待发货', status: 2, count: 0 },
|
||
{ name: '待收货', status: 3, count: 0 },
|
||
{ name: '已完成', status: 4, count: 0 },
|
||
{ name: '退款', status: 6, count: 0 }
|
||
] as TabType[],
|
||
currentTab: -2,
|
||
searchKeyword: '',
|
||
orders: [] as OrderType[],
|
||
loading: false,
|
||
loadingMore: false,
|
||
refreshing: false,
|
||
page: 1,
|
||
limit: 20,
|
||
hasMore: true,
|
||
merchantId: '',
|
||
|
||
showShipModal: false,
|
||
currentOrder: null as OrderType | null,
|
||
logisticsCompanies: [
|
||
{ name: '顺丰速运', code: 'SF' },
|
||
{ name: '圆通速递', code: 'YTO' },
|
||
{ name: '中通快递', code: 'ZTO' },
|
||
{ name: '韵达快递', code: 'YD' },
|
||
{ name: '申通快递', code: 'STO' },
|
||
{ name: 'EMS', code: 'EMS' },
|
||
{ name: '京东物流', code: 'JD' }
|
||
] as LogisticsType[],
|
||
selectedLogistics: null as LogisticsType | null,
|
||
trackingNumber: ''
|
||
}
|
||
},
|
||
|
||
onLoad(options: any) {
|
||
const type = options.type as string
|
||
if (type && type !== 'all') {
|
||
const statusMap: Record<string, number> = {
|
||
'pending': 1,
|
||
'shipped': 3,
|
||
'refund': 6,
|
||
'completed': 4
|
||
}
|
||
this.currentTab = statusMap[type] ?? -2
|
||
}
|
||
this.initMerchantId()
|
||
},
|
||
|
||
onShow() {
|
||
if (this.merchantId) {
|
||
this.loadOrders()
|
||
this.loadOrderCounts()
|
||
} else {
|
||
setTimeout(() => {
|
||
this.loadOrders()
|
||
this.loadOrderCounts()
|
||
}, 500)
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
async initMerchantId() {
|
||
try {
|
||
const session = supa.getSession()
|
||
if (session != null && session.user != null) {
|
||
this.merchantId = session.user.getString('id') || ''
|
||
}
|
||
if (!this.merchantId) {
|
||
this.merchantId = uni.getStorageSync('user_id') || ''
|
||
}
|
||
} catch (e) {
|
||
console.error('获取商户ID失败:', e)
|
||
}
|
||
},
|
||
|
||
async loadOrders() {
|
||
if (this.loading) return
|
||
this.loading = true
|
||
|
||
try {
|
||
let query = supa
|
||
.from('ml_orders')
|
||
.select(`
|
||
*,
|
||
order_items (
|
||
id,
|
||
order_id,
|
||
product_id,
|
||
sku_id,
|
||
product_name,
|
||
sku_name,
|
||
price,
|
||
quantity,
|
||
image_url,
|
||
sku_snapshot
|
||
)
|
||
`)
|
||
.eq('merchant_id', this.merchantId)
|
||
.order('created_at', { ascending: false })
|
||
.page(this.page)
|
||
.limit(this.limit)
|
||
|
||
if (this.currentTab !== -2) {
|
||
if (this.currentTab === 6) {
|
||
// 退款状态同时查询 0 和 6
|
||
query = query.in('order_status', [0, 6])
|
||
} else {
|
||
query = query.eq('order_status', this.currentTab)
|
||
}
|
||
}
|
||
|
||
if (this.searchKeyword) {
|
||
query = query.like('order_no', `%${this.searchKeyword}%`)
|
||
}
|
||
|
||
const response = await query.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
console.error('获取订单失败:', response.error)
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
const rawData = response.data as any[]
|
||
if (rawData == null || rawData.length === 0) {
|
||
this.orders = []
|
||
this.hasMore = false
|
||
return
|
||
}
|
||
|
||
const ordersData: OrderType[] = []
|
||
for (let i = 0; i < rawData.length; i++) {
|
||
const item = rawData[i]
|
||
const str = JSON.stringify(item)
|
||
const orderObj = JSON.parse(str) as UTSJSONObject
|
||
|
||
const order: OrderType = {
|
||
id: orderObj.getString('id') || '',
|
||
order_no: orderObj.getString('order_no') || '',
|
||
user_id: orderObj.getString('user_id') || '',
|
||
merchant_id: orderObj.getString('merchant_id') || '',
|
||
order_status: orderObj.getNumber('order_status') ?? (orderObj.get('order_status') == null ? 1 : (orderObj.get('order_status') as number)),
|
||
total_amount: orderObj.getNumber('total_amount') || 0,
|
||
product_amount: orderObj.getNumber('product_amount') || 0,
|
||
shipping_fee: orderObj.getNumber('shipping_fee') || 0,
|
||
paid_amount: orderObj.getNumber('paid_amount') || 0,
|
||
shipping_address: orderObj.get('shipping_address') != null ? (typeof orderObj.get('shipping_address') === 'string' ? orderObj.getString('shipping_address')! : JSON.stringify(orderObj.get('shipping_address'))) : '',
|
||
remark: orderObj.getString('remark') || '',
|
||
created_at: orderObj.getString('created_at') || '',
|
||
updated_at: orderObj.getString('updated_at') || '',
|
||
items: []
|
||
}
|
||
|
||
const itemsObj = orderObj.get('order_items')
|
||
if (itemsObj != null && Array.isArray(itemsObj)) {
|
||
const itemsArray = itemsObj as any[]
|
||
for (let j = 0; j < itemsArray.length; j++) {
|
||
const rawItem = itemsArray[j]
|
||
const itemStr = JSON.stringify(rawItem)
|
||
const orderItem = JSON.parse(itemStr) as UTSJSONObject
|
||
|
||
order.items.push({
|
||
id: orderItem.getString('id') || '',
|
||
order_id: orderItem.getString('order_id') || '',
|
||
product_id: orderItem.getString('product_id') || '',
|
||
sku_id: orderItem.getString('sku_id') || '',
|
||
product_name: orderItem.getString('product_name') || '',
|
||
sku_name: orderItem.getString('sku_name') || '',
|
||
price: orderItem.getNumber('price') || 0,
|
||
quantity: orderItem.getNumber('quantity') || 0,
|
||
image_url: orderItem.getString('image_url') || '',
|
||
sku_snapshot: ''
|
||
} as OrderItemType)
|
||
}
|
||
}
|
||
|
||
ordersData.push(order)
|
||
}
|
||
|
||
if (this.page === 1) {
|
||
this.orders = ordersData
|
||
} else {
|
||
this.orders = [...this.orders, ...ordersData]
|
||
}
|
||
|
||
this.hasMore = rawData.length >= this.limit
|
||
} catch (e) {
|
||
console.error('获取订单异常:', e)
|
||
} finally {
|
||
this.loading = false
|
||
this.refreshing = false
|
||
}
|
||
},
|
||
|
||
async loadOrderCounts() {
|
||
try {
|
||
const response = await supa
|
||
.from('ml_orders')
|
||
.select('order_status', { count: 'exact' })
|
||
.eq('merchant_id', this.merchantId)
|
||
.execute()
|
||
|
||
if (response.error != null || response.total == null) return
|
||
|
||
const counts = {
|
||
1: 0, 2: 0, 3: 0, 4: 0, 0: 0
|
||
}
|
||
let total = 0
|
||
|
||
const rawData = response.data as any[]
|
||
if (rawData != null) {
|
||
for (let i = 0; i < rawData.length; i++) {
|
||
const row = rawData[i]
|
||
const istr = JSON.stringify(row)
|
||
const item = JSON.parse(istr) as UTSJSONObject
|
||
const status_val = item.get('order_status')
|
||
let status = 1
|
||
if (status_val != null) {
|
||
status = (typeof status_val === 'number') ? (status_val as number) : parseInt(status_val.toString())
|
||
}
|
||
|
||
if (status === 1) counts[1]++
|
||
else if (status === 2) counts[2]++
|
||
else if (status === 3) counts[3]++
|
||
else if (status === 4) counts[4]++
|
||
else if (status === 0 || status === 6) counts[0]++
|
||
total++
|
||
}
|
||
}
|
||
|
||
this.tabs[0].count = total
|
||
this.tabs[1].count = counts[1] || 0
|
||
this.tabs[2].count = counts[2] || 0
|
||
this.tabs[3].count = counts[3] || 0
|
||
this.tabs[4].count = counts[4] || 0
|
||
this.tabs[5].count = counts[0] || 0
|
||
} catch (e) {
|
||
console.error('获取订单数量异常:', e)
|
||
}
|
||
},
|
||
|
||
switchTab(status: number) {
|
||
this.currentTab = status
|
||
this.page = 1
|
||
this.hasMore = true
|
||
this.loadOrders()
|
||
},
|
||
|
||
handleSearch() {
|
||
this.page = 1
|
||
this.hasMore = true
|
||
this.loadOrders()
|
||
},
|
||
|
||
onRefresh() {
|
||
this.refreshing = true
|
||
this.page = 1
|
||
this.loadOrders()
|
||
this.loadOrderCounts()
|
||
},
|
||
|
||
loadMore() {
|
||
if (!this.loadingMore && this.hasMore) {
|
||
this.loadingMore = true
|
||
this.page++
|
||
this.loadOrders().then(() => {
|
||
this.loadingMore = false
|
||
})
|
||
}
|
||
},
|
||
|
||
viewOrderDetail(orderId: string) {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/merchant/order-detail?id=${orderId}`
|
||
})
|
||
},
|
||
|
||
shipOrder(order: OrderType) {
|
||
this.currentOrder = order
|
||
this.showShipModal = true
|
||
},
|
||
|
||
closeShipModal() {
|
||
this.showShipModal = false
|
||
this.currentOrder = null
|
||
this.selectedLogistics = null
|
||
this.trackingNumber = ''
|
||
},
|
||
|
||
onLogisticsChange(e: any) {
|
||
const index = e.detail.value as number
|
||
this.selectedLogistics = this.logisticsCompanies[index]
|
||
},
|
||
|
||
async confirmShip() {
|
||
if (this.selectedLogistics == null || !this.selectedLogistics?.name) {
|
||
uni.showToast({ title: '请选择物流公司', icon: 'none' })
|
||
return
|
||
}
|
||
if (!this.trackingNumber) {
|
||
uni.showToast({ title: '请输入物流单号', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
try {
|
||
const payloadStr = JSON.stringify({
|
||
order_status: 3,
|
||
shipping_status: 2,
|
||
carrier_name: this.selectedLogistics?.name ?? '未知',
|
||
tracking_no: this.trackingNumber,
|
||
shipped_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
});
|
||
const payload = JSON.parse(payloadStr) as UTSJSONObject;
|
||
console.log('--- PAYLOAD TO SEND ---', JSON.stringify(payload));
|
||
const response = await supa.from('ml_orders').update(payload
|
||
)
|
||
.eq('id', this.currentOrder!.id)
|
||
.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
let msg = '';
|
||
if (response.error != null) msg = response.error!.message;
|
||
else if (response.data != null) {
|
||
const rData = response.data as UTSJSONObject;
|
||
msg = rData.getString('message') ?? rData.getString('code') ?? JSON.stringify(rData);
|
||
}
|
||
if (!msg) msg = '请检查网络或登录状态'; uni.showToast({ title: '发货被拦截: ' + msg, icon: 'none', duration: 4500 }); console.error('SUPABASE API ERR:', response)
|
||
return
|
||
}
|
||
|
||
uni.showToast({ title: '发货成功', icon: 'success' })
|
||
this.closeShipModal()
|
||
this.loadOrders()
|
||
this.loadOrderCounts()
|
||
} catch (e) { uni.showToast({ title: '发货发生异常', icon: 'none' }); console.error(e) }
|
||
},
|
||
|
||
viewLogistics(order: OrderType) {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/logistics?orderId=${order.id}`
|
||
})
|
||
},
|
||
|
||
async deleteOrder(order: OrderType) {
|
||
uni.showModal({
|
||
title: '确认删除',
|
||
content: '确定要删除该订单吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
const response = await supa
|
||
.from('ml_orders')
|
||
.delete()
|
||
.eq('id', order.id)
|
||
.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
uni.showToast({ title: '删除成功', icon: 'success' })
|
||
this.loadOrders()
|
||
this.loadOrderCounts()
|
||
} catch (e) {
|
||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
getStatusText(status: number): string {
|
||
if (status === 1) return '待付款'
|
||
if (status === 2) return '待发货'
|
||
if (status === 3) return '待收货'
|
||
if (status === 4) return '已完成'
|
||
if (status === 0 || status === 6) return '退款/售后'
|
||
if (status === 7) return '退货完成'
|
||
if (status === 5 || status === -1) return '已取消'
|
||
return '未知'
|
||
},
|
||
|
||
getTotalQuantity(items: OrderItemType[]): number {
|
||
let total = 0
|
||
for (let i = 0; i < items.length; i++) {
|
||
total += items[i].quantity
|
||
}
|
||
return total
|
||
},
|
||
|
||
formatTime(timeStr: string): string {
|
||
if (!timeStr) return ''
|
||
const date = new Date(timeStr)
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||
const day = date.getDate().toString().padStart(2, '0')
|
||
const hour = date.getHours().toString().padStart(2, '0')
|
||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||
return `${month}-${day} ${hour}:${minute}`
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.orders-page {
|
||
background-color: #f5f5f5;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.tabs-container {
|
||
background-color: #fff;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
}
|
||
|
||
.tabs-scroll {
|
||
display: flex;
|
||
overflow-x: auto;
|
||
white-space: nowrap;
|
||
padding: 0 20rpx;
|
||
}
|
||
|
||
.tab-item {
|
||
position: relative;
|
||
padding: 24rpx 30rpx;
|
||
text-align: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.tab-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.tab-item.active .tab-text {
|
||
color: #007AFF;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.tab-item.active::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 40rpx;
|
||
height: 4rpx;
|
||
background-color: #007AFF;
|
||
border-radius: 2rpx;
|
||
}
|
||
|
||
.tab-badge {
|
||
position: absolute;
|
||
top: 10rpx;
|
||
right: 10rpx;
|
||
min-width: 32rpx;
|
||
height: 32rpx;
|
||
padding: 0 8rpx;
|
||
background-color: #FF3B30;
|
||
color: #fff;
|
||
font-size: 20rpx;
|
||
border-radius: 16rpx;
|
||
text-align: center;
|
||
line-height: 32rpx;
|
||
}
|
||
|
||
.search-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 30rpx;
|
||
background-color: #fff;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
height: 64rpx;
|
||
background-color: #f5f5f5;
|
||
border-radius: 32rpx;
|
||
padding: 0 30rpx;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.search-btn {
|
||
margin-left: 20rpx;
|
||
padding: 16rpx 30rpx;
|
||
background-color: #007AFF;
|
||
color: #fff;
|
||
font-size: 26rpx;
|
||
border-radius: 32rpx;
|
||
}
|
||
|
||
.orders-list {
|
||
padding: 0 20rpx;
|
||
height: calc(100vh - 300rpx);
|
||
}
|
||
|
||
.loading-container, .empty-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 100rpx 0;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 100rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.empty-text, .loading-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.order-card {
|
||
background-color: #fff;
|
||
border-radius: 20rpx;
|
||
margin-bottom: 24rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.order-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 24rpx;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.order-info-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.order-no {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.order-time {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.order-status {
|
||
font-size: 24rpx;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
|
||
.status-1 {
|
||
background-color: #FFF3E0;
|
||
color: #F57C00;
|
||
}
|
||
|
||
.status-2 {
|
||
background-color: #E3F2FD;
|
||
color: #1976D2;
|
||
}
|
||
|
||
.status-3 {
|
||
background-color: #E8F5E9;
|
||
color: #388E3C;
|
||
}
|
||
|
||
.status-4, .status-5 {
|
||
background-color: #F5F5F5;
|
||
color: #999;
|
||
}
|
||
|
||
.status-0, .status--1 {
|
||
background-color: #FFEBEE;
|
||
color: #F44336;
|
||
}
|
||
|
||
.order-products {
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.product-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.product-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.product-image {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 8rpx;
|
||
margin-right: 20rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.product-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
line-height: 1.4;
|
||
margin-bottom: 8rpx;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.product-spec {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
background-color: #f8f8f8;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 4rpx;
|
||
align-self: flex-start;
|
||
}
|
||
|
||
.product-right {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
margin-left: 20rpx;
|
||
min-width: 120rpx;
|
||
}
|
||
|
||
.product-price {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.product-quantity {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.order-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24rpx;
|
||
border-top: 1rpx solid #f8f8f8;
|
||
background-color: #fafafa;
|
||
}
|
||
|
||
.order-amount {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
|
||
.amount-label {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.amount-value {
|
||
font-size: 32rpx;
|
||
color: #FF3B30;
|
||
font-weight: bold;
|
||
margin-left: 10rpx;
|
||
}
|
||
|
||
.order-actions {
|
||
display: flex;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.action-btn {
|
||
min-width: 120rpx;
|
||
height: 56rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24rpx;
|
||
border-radius: 28rpx;
|
||
border: 1rpx solid #eee;
|
||
padding: 0 20rpx;
|
||
}
|
||
|
||
.action-btn.primary {
|
||
background-color: #007AFF;
|
||
color: #fff;
|
||
border: none;
|
||
}
|
||
|
||
.action-btn.info {
|
||
background-color: #fff;
|
||
color: #007AFF;
|
||
border-color: #007AFF;
|
||
}
|
||
|
||
.action-btn.default {
|
||
background-color: #fff;
|
||
color: #666;
|
||
border-color: #ddd;
|
||
}
|
||
|
||
.load-more, .no-more {
|
||
padding: 30rpx 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.load-more-text, .no-more-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.modal-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
z-index: 99;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 100%;
|
||
background-color: #fff;
|
||
border-radius: 24rpx 24rpx 0 0;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
position: relative;
|
||
z-index: 99;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.modal-close {
|
||
font-size: 44rpx;
|
||
color: #999;
|
||
line-height: 1;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
display: block;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.form-picker, .form-input {
|
||
height: 72rpx;
|
||
border: 1rpx solid #e5e5e5;
|
||
border-radius: 8rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.picker-value {
|
||
height: 72rpx;
|
||
line-height: 72rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
border-top: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.modal-btn {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.modal-btn.cancel {
|
||
color: #666;
|
||
border-right: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.modal-btn.confirm {
|
||
color: #007AFF;
|
||
font-weight: bold;
|
||
}
|
||
</style> |