952 lines
23 KiB
Plaintext
952 lines
23 KiB
Plaintext
<!-- 商家端 - 订单详情页面 -->
|
||
<template>
|
||
<view class="order-detail-page">
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<view style="padding-top: var(--status-bar-height); background-color: #ffffff; display: flex; flex-direction: row; align-items: flex-end; border-bottom: 1rpx solid #eeeeee; box-sizing: border-box; height: calc(88rpx + var(--status-bar-height));">
|
||
<view style="display: flex; flex-direction: row; align-items: center; padding: 0 30rpx; height: 88rpx;" @click="uni.navigateBack()">
|
||
<text style="font-size: 44rpx; color: #333333; line-height: 1; margin-right: 6rpx;">‹</text>
|
||
<text style="font-size: 28rpx; color: #333333;">返回</text>
|
||
</view>
|
||
</view>
|
||
<!-- #endif -->
|
||
<!-- 订单状态头部 -->
|
||
<view class="status-header" :class="getStatusBgClass(order.order_status)">
|
||
<text class="status-icon">{{ getStatusIcon(order.order_status) }}</text>
|
||
<text class="status-text">{{ getStatusText(order.order_status) }}</text>
|
||
<text class="status-desc">{{ getStatusDesc(order.order_status) }}</text>
|
||
</view>
|
||
|
||
<!-- 物流信息 -->
|
||
<view v-if="order.order_status >= 3" class="section logistics-section">
|
||
<view class="section-title">物流信息</view>
|
||
<view class="logistics-info">
|
||
<view class="logistics-company">
|
||
<text class="label">物流公司:</text>
|
||
<text class="value">{{ order.shipping_company || '待填写' }}</text>
|
||
</view>
|
||
<view class="logistics-number">
|
||
<text class="label">物流单号:</text>
|
||
<text class="value">{{ order.tracking_number || '待填写' }}</text>
|
||
<text v-if="order.tracking_number" class="copy-btn" @click="copyTrackingNumber">复制</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 收货地址 -->
|
||
<view class="section address-section">
|
||
<view class="section-title">收货信息</view>
|
||
<view class="address-info">
|
||
<view class="address-user">
|
||
<text class="name">{{ addressData.recipient_name || '未知' }}</text>
|
||
<text class="phone">{{ addressData.phone || '未知' }}</text>
|
||
</view>
|
||
<view class="address-detail">
|
||
{{ addressData.province || '' }}{{ addressData.city || '' }}{{ addressData.district || '' }}{{ addressData.detail_address || '' }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单信息 -->
|
||
<view class="section order-info-section">
|
||
<view class="section-title">订单信息</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="label">订单编号:</text>
|
||
<text class="value">{{ order.order_no }}</text>
|
||
<text class="copy-btn" @click="copyOrderNo">复制</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">下单时间:</text>
|
||
<text class="value">{{ formatTime(order.created_at) }}</text>
|
||
</view>
|
||
<view v-if="order.paid_at" class="info-item">
|
||
<text class="label">付款时间:</text>
|
||
<text class="value">{{ formatTime(order.paid_at) }}</text>
|
||
</view>
|
||
<view v-if="order.shipped_at" class="info-item">
|
||
<text class="label">发货时间:</text>
|
||
<text class="value">{{ formatTime(order.shipped_at) }}</text>
|
||
</view>
|
||
<view v-if="order.remark" class="info-item">
|
||
<text class="label">订单备注:</text>
|
||
<text class="value">{{ order.remark }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品列表 -->
|
||
<view class="section products-section">
|
||
<view class="section-title">商品信息</view>
|
||
<view class="products-list">
|
||
<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>
|
||
|
||
<!-- 费用明细 -->
|
||
<view class="section fees-section">
|
||
<view class="section-title">费用明细</view>
|
||
<view class="fees-list">
|
||
<view class="fee-item">
|
||
<text class="label">商品金额:</text>
|
||
<text class="value">¥{{ order.product_amount }}</text>
|
||
</view>
|
||
<view class="fee-item">
|
||
<text class="label">运费:</text>
|
||
<text class="value">¥{{ order.shipping_fee }}</text>
|
||
</view>
|
||
<view v-if="order.discount_amount > 0" class="fee-item">
|
||
<text class="label">优惠:</text>
|
||
<text class="value discount">-¥{{ order.discount_amount }}</text>
|
||
</view>
|
||
<view class="fee-item total">
|
||
<text class="label">实付金额:</text>
|
||
<text class="value">¥{{ order.total_amount }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="action-buttons">
|
||
<view
|
||
v-if="order.order_status === 2"
|
||
class="action-btn primary"
|
||
@click="shipOrder"
|
||
>
|
||
去发货
|
||
</view>
|
||
<view
|
||
v-if="order.order_status === 3"
|
||
class="action-btn primary"
|
||
@click="viewLogistics"
|
||
>
|
||
查看物流
|
||
</view>
|
||
<view
|
||
v-if="order.order_status === 3"
|
||
class="action-btn primary"
|
||
@click="confirmDelivery"
|
||
>
|
||
确认收货
|
||
</view>
|
||
<view
|
||
v-if="order.order_status === -1 || order.order_status === 5"
|
||
class="action-btn danger"
|
||
@click="deleteOrder"
|
||
>
|
||
删除订单
|
||
</view>
|
||
<view class="action-btn default" @click="contactBuyer">
|
||
联系买家
|
||
</view>
|
||
</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 AddressType = {
|
||
recipient_name: string
|
||
phone: string
|
||
province: string
|
||
city: string
|
||
district: string
|
||
detail_address: string
|
||
}
|
||
|
||
type LogisticsType = {
|
||
name: string
|
||
code: string
|
||
}
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
orderId: '',
|
||
order: {
|
||
id: '',
|
||
order_no: '',
|
||
user_id: '',
|
||
merchant_id: '',
|
||
order_status: 1,
|
||
total_amount: 0,
|
||
product_amount: 0,
|
||
shipping_fee: 0,
|
||
discount_amount: 0,
|
||
paid_amount: 0,
|
||
shipping_address: '',
|
||
remark: '',
|
||
shipping_company: '',
|
||
tracking_number: '',
|
||
paid_at: '',
|
||
shipped_at: '',
|
||
created_at: '',
|
||
updated_at: '',
|
||
items: [] as OrderItemType[]
|
||
},
|
||
addressData: {
|
||
recipient_name: '',
|
||
phone: '',
|
||
province: '',
|
||
city: '',
|
||
district: '',
|
||
detail_address: ''
|
||
} as AddressType,
|
||
|
||
showShipModal: false,
|
||
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: { name: '', code: '' } as LogisticsType,
|
||
trackingNumber: ''
|
||
}
|
||
},
|
||
|
||
onLoad(options: any) { console.log('--- DEBUG ON LOAD ---', options)
|
||
let id = ''
|
||
if (options['id'] != null) {
|
||
id = options['id'] as string
|
||
} else if (options.id != null) {
|
||
id = options.id as string
|
||
}
|
||
|
||
if (id !== '') {
|
||
this.orderId = id
|
||
this.loadOrderDetail()
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
async loadOrderDetail() { console.log('--- DEBUG LOAD ORDER DETAIL ---', this.orderId); try {
|
||
const response = await supa
|
||
.from('ml_orders')
|
||
.select(`
|
||
*,
|
||
ml_order_items (
|
||
id,
|
||
order_id,
|
||
product_id,
|
||
sku_id,
|
||
product_name,
|
||
sku_name,
|
||
price,
|
||
quantity,
|
||
image_url,
|
||
sku_snapshot
|
||
)
|
||
`)
|
||
.eq('id', this.orderId)
|
||
.single()
|
||
.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
console.error('获取订单详情失败:', response.error)
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
console.log('--- DEBUG RAW ORDER DATA ---', response.data); let realData = response.data;
|
||
let isArrLike = false;
|
||
if (response.data != null && (response.data as any)['0'] != null) {
|
||
realData = (response.data as any)['0'];
|
||
isArrLike = true;
|
||
}
|
||
console.log('--- EXTRACTED realData ---', isArrLike);
|
||
const rawData = realData as UTSJSONObject
|
||
if (rawData == null) return
|
||
|
||
this.order = {
|
||
id: String(rawData['id'] ?? '') || '',
|
||
order_no: String(rawData['order_no'] ?? '') || '',
|
||
user_id: String(rawData['user_id'] ?? '') || '',
|
||
merchant_id: String(rawData['merchant_id'] ?? '') || '',
|
||
order_status: Number(rawData['order_status'] ?? 0) || 1,
|
||
total_amount: Number(rawData['total_amount'] ?? 0) || 0,
|
||
product_amount: Number(rawData['product_amount'] ?? 0) || 0,
|
||
shipping_fee: Number(rawData['shipping_fee'] ?? 0) || 0,
|
||
discount_amount: Number(rawData['discount_amount'] ?? 0) || 0,
|
||
paid_amount: Number(rawData['paid_amount'] ?? 0) || 0,
|
||
shipping_address: String(rawData['shipping_address'] ?? '') || '{}',
|
||
remark: String(rawData['remark'] ?? '') || '',
|
||
shipping_company: String(rawData['carrier_name'] ?? rawData['shipping_company'] ?? '') || '',
|
||
tracking_number: String(rawData['tracking_no'] ?? rawData['tracking_number'] ?? '') || '',
|
||
paid_at: String(rawData['paid_at'] ?? '') || '',
|
||
shipped_at: String(rawData['shipped_at'] ?? '') || '',
|
||
created_at: String(rawData['created_at'] ?? '') || '',
|
||
updated_at: String(rawData['updated_at'] ?? '') || '',
|
||
items: []
|
||
}
|
||
|
||
const itemsObj = rawData['ml_order_items']
|
||
if (itemsObj != null && Array.isArray(itemsObj)) {
|
||
const itemsArray = itemsObj as any[]
|
||
for (let i = 0; i < itemsArray.length; i++) {
|
||
const orderItem = itemsArray[i] as UTSJSONObject
|
||
this.order.items.push({
|
||
id: String(orderItem['id'] ?? '') || '',
|
||
order_id: String(orderItem['order_id'] ?? '') || '',
|
||
product_id: String(orderItem['product_id'] ?? '') || '',
|
||
sku_id: String(orderItem['sku_id'] ?? '') || '',
|
||
product_name: String(orderItem['product_name'] ?? '') || '',
|
||
sku_name: String(orderItem['sku_name'] ?? '') || '',
|
||
price: Number(orderItem['price'] ?? 0) || 0,
|
||
quantity: Number(orderItem['quantity'] ?? 0) || 0,
|
||
image_url: String(orderItem['image_url'] ?? '') || '',
|
||
sku_snapshot: ''
|
||
} as OrderItemType)
|
||
}
|
||
}
|
||
|
||
this.parseAddress()
|
||
} catch (e) {
|
||
console.error('获取订单详情异常:', e)
|
||
}
|
||
},
|
||
|
||
parseAddress() {
|
||
try {
|
||
const addrStr = this.order.shipping_address
|
||
if (addrStr && addrStr !== '{}') {
|
||
const addrObj = JSON.parse(addrStr) as AddressType
|
||
this.addressData = addrObj
|
||
}
|
||
} catch (e) {
|
||
console.error('解析地址失败:', e)
|
||
}
|
||
},
|
||
|
||
getStatusIcon(status: number): string {
|
||
if (status === 1) return '💰'
|
||
if (status === 2) return '📦'
|
||
if (status === 3) return '🚚'
|
||
if (status === 4) return '✅'
|
||
if (status === 0) return '↩️'
|
||
if (status === 5 || status === -1) return '❌'
|
||
return '📋'
|
||
},
|
||
|
||
getStatusText(status: number): string {
|
||
if (status === 1) return '待付款'
|
||
if (status === 2) return '待发货'
|
||
if (status === 3) return '待收货'
|
||
if (status === 4) return '已完成'
|
||
if (status === 0) return '退款中'
|
||
if (status === 5 || status === -1) return '已取消'
|
||
return '未知'
|
||
},
|
||
|
||
getStatusDesc(status: number): string {
|
||
if (status === 1) return '买家已下单,请尽快发货'
|
||
if (status === 2) return '等待商家发货'
|
||
if (status === 3) return '商品运输中,请关注物流'
|
||
if (status === 4) return '订单已完成'
|
||
if (status === 0) return '买家申请退款,请处理'
|
||
if (status === 5 || status === -1) return '订单已取消'
|
||
return ''
|
||
},
|
||
|
||
getStatusBgClass(status: number): string {
|
||
if (status === 1) return 'status-bg-1'
|
||
if (status === 2) return 'status-bg-2'
|
||
if (status === 3) return 'status-bg-3'
|
||
if (status === 4) return 'status-bg-4'
|
||
if (status === 0 || status === 5 || status === -1) return 'status-bg-0'
|
||
return 'status-bg-1'
|
||
},
|
||
|
||
formatTime(timeStr: string): string {
|
||
if (!timeStr) return '-'
|
||
const date = new Date(timeStr)
|
||
const year = date.getFullYear()
|
||
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 `${year}-${month}-${day} ${hour}:${minute}`
|
||
},
|
||
|
||
copyOrderNo() {
|
||
uni.setClipboardData({
|
||
data: this.order.order_no,
|
||
success: () => {
|
||
uni.showToast({ title: '复制成功', icon: 'success' })
|
||
}
|
||
})
|
||
},
|
||
|
||
copyTrackingNumber() {
|
||
uni.setClipboardData({
|
||
data: this.order.tracking_number,
|
||
success: () => {
|
||
uni.showToast({ title: '复制成功', icon: 'success' })
|
||
}
|
||
})
|
||
},
|
||
|
||
shipOrder() {
|
||
this.showShipModal = true
|
||
},
|
||
|
||
closeShipModal() {
|
||
this.showShipModal = false
|
||
this.selectedLogistics = {} as LogisticsType
|
||
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 response = await supa
|
||
.from('ml_orders')
|
||
.update({
|
||
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()
|
||
})
|
||
.eq('id', this.order.id)
|
||
.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
let msg = response.error?.message ?? (response.data != null ? JSON.stringify(response.data) : '请检查网络或登录状态'); uni.showToast({ title: '发货被拦截: ' + msg, icon: 'none', duration: 4500 }); console.error('SUPABASE API ERR:', response)
|
||
return
|
||
}
|
||
|
||
uni.showToast({ title: '发货成功', icon: 'success' })
|
||
this.closeShipModal()
|
||
this.loadOrderDetail()
|
||
} catch (e) { uni.showToast({ title: '发货发生异常', icon: 'none' }); console.error(e) }
|
||
},
|
||
|
||
viewLogistics() {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/merchant/logistics?orderId=${this.order.id}`
|
||
})
|
||
},
|
||
|
||
async confirmDelivery() {
|
||
uni.showModal({
|
||
title: '确认收货',
|
||
content: '确认买家已收到货物吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
const response = await supa
|
||
.from('ml_orders')
|
||
.update({
|
||
order_status: 4,
|
||
delivered_at: new Date().toISOString(),
|
||
completed_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.eq('id', this.order.id)
|
||
.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
uni.showToast({ title: '操作成功', icon: 'success' })
|
||
this.loadOrderDetail()
|
||
} catch (e) {
|
||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
async deleteOrder() {
|
||
uni.showModal({
|
||
title: '确认删除',
|
||
content: '确定要删除该订单吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
const response = await supa
|
||
.from('ml_orders')
|
||
.delete()
|
||
.eq('id', this.order.id)
|
||
.execute()
|
||
|
||
if (response.error != null || (response.status ?? 200) >= 400) {
|
||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
uni.showToast({ title: '删除成功', icon: 'success' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
} catch (e) {
|
||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
contactBuyer() {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/merchant/chat?userId=${this.order.user_id}`
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.order-detail-page {
|
||
background-color: #f5f5f5;
|
||
min-height: 100vh;
|
||
padding-bottom: 180rpx;
|
||
}
|
||
|
||
.status-header {
|
||
padding: 50rpx 30rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.status-bg-1 {
|
||
background: linear-gradient(135deg, #FF9800 0%, #FF5722 100%);
|
||
}
|
||
|
||
.status-bg-2 {
|
||
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
|
||
}
|
||
|
||
.status-bg-3 {
|
||
background: linear-gradient(135deg, #4CAF50 0%, #388E3C 100%);
|
||
}
|
||
|
||
.status-bg-4 {
|
||
background: linear-gradient(135deg, #9C27B0 0%, #7B1FA2 100%);
|
||
}
|
||
|
||
.status-bg-0, .status-bg-5 {
|
||
background: linear-gradient(135deg, #607D8B 0%, #455A64 100%);
|
||
}
|
||
|
||
.status-icon {
|
||
font-size: 60rpx;
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
display: block;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.status-desc {
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
.section {
|
||
background-color: #fff;
|
||
margin-bottom: 20rpx;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.logistics-info {
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.logistics-company, .logistics-number {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.logistics-number .label {
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.logistics-number .value {
|
||
flex: 1;
|
||
}
|
||
|
||
.label {
|
||
color: #999;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.value {
|
||
color: #333;
|
||
}
|
||
|
||
.address-info {
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.address-user {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.address-user .name {
|
||
font-weight: bold;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.address-user .phone {
|
||
color: #666;
|
||
}
|
||
|
||
.address-detail {
|
||
color: #666;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.info-list {
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.info-item .label {
|
||
min-width: 140rpx;
|
||
}
|
||
|
||
.info-item .value {
|
||
flex: 1;
|
||
}
|
||
|
||
.copy-btn {
|
||
color: #007AFF;
|
||
font-size: 24rpx;
|
||
margin-left: 16rpx;
|
||
}
|
||
|
||
.products-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.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: 140rpx;
|
||
height: 140rpx;
|
||
border-radius: 8rpx;
|
||
margin-right: 20rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.product-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.product-spec {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.product-right {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.product-price {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.product-quantity {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.fees-list {
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.fee-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.fee-item .value {
|
||
color: #333;
|
||
}
|
||
|
||
.fee-item .value.discount {
|
||
color: #FF5722;
|
||
}
|
||
|
||
.fee-item.total {
|
||
margin-top: 20rpx;
|
||
padding-top: 20rpx;
|
||
border-top: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.fee-item.total .label {
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.fee-item.total .value {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #FF3B30;
|
||
}
|
||
|
||
.action-buttons {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
gap: 20rpx;
|
||
padding: 20rpx 30rpx;
|
||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
background-color: #fff;
|
||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
border-radius: 40rpx;
|
||
}
|
||
|
||
.action-btn.primary {
|
||
background-color: #007AFF;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.default {
|
||
background-color: #f5f5f5;
|
||
color: #333;
|
||
}
|
||
|
||
.action-btn.danger {
|
||
background-color: #FF3B30;
|
||
color: #fff;
|
||
}
|
||
|
||
.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> |