Files
jurong_circle_frontdesk/src/views/Orders.vue
2025-09-05 11:40:34 +08:00

1238 lines
27 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="orders-page">
<!-- 导航栏 -->
<nav class="navbar">
<div class="nav-center">
<h1 class="nav-title">我的订单</h1>
</div>
<div class="nav-right">
<el-button
type="text"
@click="$router.push('/shop')"
class="shop-btn"
>
<el-icon><ShoppingBag /></el-icon>
商城
</el-button>
</div>
</nav>
<!-- 订单状态筛选 -->
<div class="filter-tabs">
<div class="tabs-container">
<div
v-for="tab in statusTabs"
:key="tab.value"
:class="['tab-item', { active: selectedStatus === tab.value }]"
@click="selectStatus(tab.value)"
>
<span>{{ tab.label }}</span>
<el-badge
v-if="tab.count > 0"
:value="tab.count"
class="tab-badge"
/>
</div>
</div>
</div>
<!-- 订单列表 -->
<div class="orders-content">
<div v-loading="loading" class="orders-list">
<div v-if="filteredOrders.length === 0" class="empty-state">
<el-icon size="60"><Box /></el-icon>
<p>{{ getEmptyText() }}</p>
<el-button type="primary" @click="$router.push('/shop')">
去购物
</el-button>
</div>
<div v-else class="orders-container">
<div v-for="order in filteredOrders" :key="order.id" class="order-card">
<!-- 订单头部 -->
<div class="order-header">
<div class="order-info">
<span class="order-number">订单号{{ order.orderNumber }}</span>
<span class="order-date">{{ formatDate(order.createdAt) }}</span>
</div>
<div class="order-status">
<el-tag :type="getStatusType(order.status)">{{ getStatusText(order.status) }}</el-tag>
</div>
</div>
<!-- 订单商品 -->
<div class="order-items">
<div
v-for="item in order.items"
:key="item.id"
class="order-item"
@click="goToProduct(item.productId)"
>
<img :src="item.product.image" :alt="item.product.name" class="item-image" />
<div class="item-info">
<h4 class="item-name">{{ item.product.name }}</h4>
<p class="item-desc">{{ truncateText(item.product.description, 40) }}</p>
<div class="item-price">
<div class="item-price-container">
<div class="item-main-price">
<img src='/imgs/profile/rongdou.png' alt="融豆" class="item-rongdou-icon" />
<span class="item-rongdou-price">{{ item.rongdouPrice }}</span>
</div>
<div class="item-sub-price">
<el-icon class="item-points-icon"><Coin /></el-icon>
<span class="item-points-price">{{ item.points }}</span>
</div>
</div>
<span class="quantity">x{{ item.quantity }}</span>
</div>
</div>
</div>
</div>
<!-- 订单总计 -->
<div class="order-total">
<div class="total-info">
<span>{{ order.totalQuantity }}件商品</span>
<div class="total-price">
<span>总计</span>
<div class="total-price-group">
<span class="total-rongdou">
<img src='/imgs/profile/rongdou.png' alt="融豆" class="total-rongdou-icon" />
{{ order.totalRongdou }}
</span>
</div>
</div>
</div>
</div>
<!-- 订单操作 -->
<div class="order-actions">
<el-button
v-if="order.status === 'pre_order'"
type="primary"
size="small"
@click="goToPay(order.id)"
>
立即支付
</el-button>
<el-button
v-if="order.status === 'pre_order'"
size="small"
@click="cancelOrder(order.id)"
>
取消订单
</el-button>
<el-button
v-if="order.status === 'pending'"
size="small"
@click="cancelOrder(order.id)"
>
取消订单
</el-button>
<el-button
v-if="order.status === 'shipped'"
type="primary"
size="small"
@click="confirmReceive(order.id)"
>
确认收货
</el-button>
<el-button
v-if="order.status === 'completed'"
size="small"
@click="showReviewDialog(order)"
>
评价
</el-button>
<el-button
size="small"
@click="viewOrderDetail(order.id)"
>
查看详情
</el-button>
</div>
</div>
</div>
</div>
<!-- 加载更多 -->
<div v-if="hasMore" class="load-more">
<el-button @click="loadMore" :loading="loadingMore">
加载更多
</el-button>
</div>
</div>
<!-- 评价对话框 -->
<el-dialog
v-model="showReview"
title="商品评价"
width="90%"
:before-close="handleReviewClose"
>
<div v-if="reviewOrder" class="review-form">
<div v-for="item in reviewOrder.items" :key="item.id" class="review-item">
<div class="review-product">
<img :src="item.product.image" :alt="item.product.name" class="product-image" />
<div class="product-info">
<h4>{{ item.product.name }}</h4>
<p>{{ item.product.description }}</p>
</div>
</div>
<div class="review-rating">
<span class="rating-label">评分</span>
<el-rate v-model="item.rating" size="large" />
</div>
<div class="review-content">
<el-input
v-model="item.reviewContent"
type="textarea"
:rows="3"
placeholder="请分享您的使用体验..."
maxlength="200"
show-word-limit
/>
</div>
<div class="review-images">
<el-upload
v-model:file-list="item.reviewImages"
action="#"
list-type="picture-card"
:auto-upload="false"
:limit="3"
>
<el-icon><Plus /></el-icon>
</el-upload>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showReview = false">取消</el-button>
<el-button type="primary" @click="submitReview" :loading="submittingReview">
提交评价
</el-button>
</span>
</template>
</el-dialog>
<!-- 订单详情对话框 -->
<el-dialog
v-model="showOrderDetail"
title="订单详情"
width="90%"
>
<div v-if="orderDetail" class="order-detail">
<div class="detail-section">
<h4>订单信息</h4>
<div class="detail-item">
<span class="label">订单号</span>
<span class="value">{{ orderDetail.orderNumber }}</span>
</div>
<div class="detail-item">
<span class="label">用户名</span>
<span class="value">{{ orderDetail.username || '未知用户' }}</span>
</div>
<div class="detail-item">
<span class="label">下单时间</span>
<span class="value">{{ formatDateTime(orderDetail.createdAt) }}</span>
</div>
<div class="detail-item">
<span class="label">更新时间</span>
<span class="value">{{ formatDateTime(orderDetail.updatedAt) }}</span>
</div>
<div class="detail-item">
<span class="label">订单状态</span>
<span class="value">
<el-tag :type="getStatusType(orderDetail.status)">
{{ getStatusText(orderDetail.status) }}
</el-tag>
</span>
</div>
</div>
<div class="detail-section">
<h4>商品信息</h4>
<div v-for="item in orderDetail.items" :key="item.id" class="detail-product">
<img :src="item.product.image" :alt="item.product.name" />
<div class="product-info">
<h5>{{ item.product.name }}</h5>
<p>{{ item.product.description }}</p>
<p v-if="item.specInfo" class="spec-info">规格{{ item.specInfo }}</p>
<div class="product-price">
<div class="detail-item-price-container">
<div class="detail-item-main-price">
<img src='/imgs/profile/rongdou.png' alt="融豆" class="detail-rongdou-icon" />
<span class="detail-rongdou-price">{{ item.rongdouPrice }}</span>
</div>
<div class="detail-item-sub-price">
<el-icon class="detail-points-icon"><Coin /></el-icon>
<span class="detail-points-price">{{ item.points }}</span>
</div>
</div>
<span class="quantity-text">x {{ item.quantity }}</span>
</div>
</div>
</div>
</div>
<div class="detail-section">
<h4>配送信息</h4>
<div class="detail-item">
<span class="label">物流信息</span>
<span class="value">{{ orderDetail.trackingNumber || '暂无' }}</span>
</div>
</div>
<div class="detail-section">
<h4>费用明细</h4>
<div class="detail-item">
<span class="label">商品总计</span>
<div class="value">
<span class="detail-price-group">
<img src='/imgs/profile/rongdou.png' alt="融豆" class="cost-rongdou-icon" />
<span>{{ orderDetail.totalRongdou }}</span>
</span>
</div>
</div>
<div class="detail-item total">
<span class="label">实付</span>
<div class="value">
<span class="detail-price-group">
<img src='/imgs/profile/rongdou.png' alt="融豆" class="cost-rongdou-icon" />
<span>{{ orderDetail.totalRongdou }}</span>
</span>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
ArrowLeft,
ShoppingBag,
Box,
Coin,
Plus
} from '@element-plus/icons-vue'
import api from '@/utils/api'
import { getImageUrl } from '@/config'
const router = useRouter()
const userStore = useUserStore()
// 响应式数据
const loading = ref(false)
const loadingMore = ref(false)
const selectedStatus = ref('all')
const orders = ref([])
const page = ref(1)
const hasMore = ref(true)
const showReview = ref(false)
const showOrderDetail = ref(false)
const reviewOrder = ref(null)
const orderDetail = ref(null)
const submittingReview = ref(false)
// 状态标签
const statusTabs = ref([
{ label: '全部', value: 'all', count: 0 },
{ label: '待发货', value: 'pending', count: 0 },
{ label: '待支付', value: 'pre_order', count: 0 },
{ label: '已发货', value: 'shipped', count: 0 },
{ label: '已完成', value: 'completed', count: 0 },
{ label: '已取消', value: 'cancelled', count: 0 }
])
// 计算属性
const filteredOrders = computed(() => {
if (selectedStatus.value === 'all') {
return orders.value
}
return orders.value.filter(order => order.status === selectedStatus.value)
})
// 方法
const selectStatus = (status) => {
selectedStatus.value = status
}
const getEmptyText = () => {
const textMap = {
all: '暂无订单',
pending: '暂无待发货订单',
pre_order: '暂无待支付订单',
shipped: '暂无已发货订单',
completed: '暂无已完成订单',
cancelled: '暂无已取消订单'
}
return textMap[selectedStatus.value]
}
const getStatusType = (status) => {
const typeMap = {
pending: 'warning',
pre_order: 'warning',
shipped: 'primary',
completed: 'success',
cancelled: 'danger'
}
return typeMap[status] || 'info'
}
const mapOrderStatus = (backendStatus) => {
const statusMap = {
'pre_order': 'pre_order',
'pending': 'pending',
'shipped': 'shipped',
'completed': 'completed',
'cancelled': 'cancelled'
}
return statusMap[backendStatus] || 'pending'
}
const getStatusText = (status) => {
const textMap = {
pending: '待发货',
pre_order: '待支付',
shipped: '已发货',
completed: '已完成',
cancelled: '已取消'
}
return textMap[status] || '未知状态'
}
const formatDate = (date) => {
return new Date(date).toLocaleDateString('zh-CN')
}
const formatDateTime = (date) => {
return new Date(date).toLocaleString('zh-CN')
}
const truncateText = (text, maxLength) => {
if (text.length <= maxLength) return text
return text.substring(0, maxLength) + '...'
}
const goToProduct = (productId) => {
router.push(`/product/${productId}`)
}
const goToPay = (orderId) => {
router.push(`/pay/${orderId}`)
}
const cancelOrder = async (orderId) => {
try {
await ElMessageBox.confirm('确定要取消这个订单吗?', '确认取消', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await api.put(`/orders/${orderId}/cancel`)
// 更新订单状态
const order = orders.value.find(o => o.id === orderId)
if (order) {
order.status = 'cancelled'
}
updateStatusCounts()
ElMessage.success('订单已取消')
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('取消订单失败')
}
}
}
const confirmReceive = async (orderId) => {
try {
await ElMessageBox.confirm('确认已收到商品吗?', '确认收货', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
})
await api.put(`/orders/${orderId}/confirm`)
// 更新订单状态
const order = orders.value.find(o => o.id === orderId)
if (order) {
order.status = 'completed'
}
updateStatusCounts()
ElMessage.success('确认收货成功')
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('确认收货失败')
}
}
}
const showReviewDialog = (order) => {
reviewOrder.value = {
...order,
items: order.items.map(item => ({
...item,
rating: 5,
reviewContent: '',
reviewImages: []
}))
}
showReview.value = true
}
const handleReviewClose = () => {
reviewOrder.value = null
showReview.value = false
}
const submitReview = async () => {
try {
submittingReview.value = true
const reviewData = {
orderId: reviewOrder.value.id,
reviews: reviewOrder.value.items.map(item => ({
productId: item.productId,
rating: item.rating,
content: item.reviewContent,
images: item.reviewImages.map(img => img.url)
}))
}
await api.post('/reviews', reviewData)
showReview.value = false
ElMessage.success('评价提交成功')
} catch (error) {
ElMessage.error('评价提交失败')
} finally {
submittingReview.value = false
}
}
const viewOrderDetail = (orderId) => {
try {
console.log('正在查找订单详情订单ID:', orderId)
// 从已加载的订单列表中查找对应订单
const order = orders.value.find(o => o.id === orderId)
console.log('找到的订单数据:', order)
if (!order) {
ElMessage.error('未找到订单信息')
return
}
// 直接使用已映射的订单数据
orderDetail.value = {
id: order.id,
orderNumber: order.orderNumber,
createdAt: order.createdAt,
updatedAt: order.updatedAt || order.createdAt,
username: order.username || '未知用户',
status: order.status,
totalPoints: order.totalPoints,
totalRongdou: order.totalRongdou,
totalQuantity: order.totalQuantity,
items: order.items || [],
shippingAddress: order.shippingAddress || null,
trackingNumber: order.trackingNumber || null
}
console.log('设置的订单详情数据:', orderDetail.value)
showOrderDetail.value = true
} catch (error) {
console.error('查看订单详情失败:', error)
ElMessage.error(`查看订单详情失败: ${error.message || '未知错误'}`)
}
}
const getOrders = async (isLoadMore = false) => {
try {
if (!isLoadMore) {
loading.value = true
page.value = 1
} else {
loadingMore.value = true
}
const {data} = await api.get('/orders', {
params: {
page: page.value,
limit: 10
}
})
console.log(data,'response');
// 映射后端数据结构到前端需要的格式
const mappedOrders = data.data.orders.map(order => ({
id: order.id,
orderNumber: order.order_no,
createdAt: order.created_at,
updatedAt: order.updated_at,
username: order.username,
status: mapOrderStatus(order.status),
totalPoints: order.total_points,
totalRongdou: order.total_rongdou,
totalQuantity: order.items?.reduce((sum, item) => sum + item.quantity, 0) || 0,
items: order.items?.map(item => ({
id: item.id,
productId: item.product_id,
quantity: item.quantity,
points: item.points_price,
rongdouPrice: item.rongdou_price,
specInfo: item.spec_info,
product: {
name: item.product_name || '商品名称',
description: item.description || '商品描述',
image: item.image_url || '/imgs/loading.png'
}
})) || [],
shippingAddress: order.address || null,
trackingNumber: null
}))
if (isLoadMore) {
orders.value.push(...mappedOrders)
} else {
orders.value = mappedOrders
}
hasMore.value = data.data.pagination.page < data.data.pagination.pages
page.value++
updateStatusCounts()
} catch (error) {
ElMessage.error('获取订单列表失败')
} finally {
loading.value = false
loadingMore.value = false
}
}
const loadMore = () => {
getOrders(true)
}
const updateStatusCounts = () => {
const counts = {
all: orders.value.length,
pending: 0,
shipped: 0,
completed: 0,
cancelled: 0
}
orders.value.forEach(order => {
counts[order.status]++
})
statusTabs.value.forEach(tab => {
tab.count = counts[tab.value]
})
}
// 生命周期
onMounted(() => {
getOrders()
})
</script>
<style scoped>
.orders-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
height: 56px;
background: white;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
z-index: 100;
}
.nav-left,
.nav-right {
flex: 1;
}
.nav-right {
display: flex;
justify-content: flex-end;
}
.back-btn,
.shop-btn {
color: #409eff;
font-size: 14px;
}
.nav-title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: #333;
}
.filter-tabs {
background: white;
border-bottom: 1px solid #eee;
padding: 0 16px;
}
.tabs-container {
display: flex;
overflow-x: auto;
}
.tab-item {
position: relative;
padding: 16px 20px;
color: #666;
font-size: 14px;
cursor: pointer;
white-space: nowrap;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #409eff;
border-bottom-color: #409eff;
}
.tab-badge {
position: absolute;
top: 8px;
right: 8px;
}
.orders-content {
padding: 16px;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
}
.orders-list {
display: flex;
flex-direction: column;
}
.orders-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.order-card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #f5f5f5;
}
.order-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.order-number {
font-size: 14px;
color: #333;
font-weight: 500;
}
.order-date {
font-size: 12px;
color: #999;
}
.order-items {
padding: 16px;
}
.order-item {
display: flex;
gap: 12px;
padding: 8px 0;
cursor: pointer;
transition: all 0.3s;
}
.order-item:hover {
background: #f8f9fa;
border-radius: 8px;
padding: 8px;
margin: 0 -8px;
}
.item-image {
width: 60px;
height: 60px;
border-radius: 8px;
object-fit: cover;
flex-shrink: 0;
}
.item-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.item-name {
margin: 0;
font-size: 14px;
color: #333;
font-weight: 500;
line-height: 1.4;
}
.item-desc {
margin: 0;
font-size: 12px;
color: #666;
line-height: 1.4;
}
.item-price {
display: flex;
justify-content: space-between;
align-items: center;
}
.price-group {
display: flex;
align-items: center;
gap: 4px;
}
.price {
display: flex;
align-items: center;
gap: 2px;
color: #ff6b35;
font-weight: 600;
font-size: 14px;
}
.plus-sign {
color: #999;
font-size: 12px;
margin: 0 2px;
}
.rongdou-icon {
width: 14px;
height: 14px;
object-fit: contain;
}
.detail-price-group {
display: flex;
align-items: center;
gap: 4px;
}
.detail-item-price-group {
display: flex;
align-items: center;
gap: 4px;
}
.quantity {
color: #999;
font-size: 12px;
}
.order-total {
padding: 16px;
border-top: 1px solid #f5f5f5;
border-bottom: 1px solid #f5f5f5;
}
.total-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.total-price {
display: flex;
align-items: center;
gap: 8px;
}
.total-price-group {
display: flex;
align-items: center;
gap: 4px;
}
.total-points,
.total-rongdou {
display: flex;
align-items: center;
gap: 4px;
color: #ff6b35;
font-weight: 600;
font-size: 16px;
}
.order-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 16px;
}
.load-more {
text-align: center;
padding: 20px;
}
.review-form {
display: flex;
flex-direction: column;
gap: 24px;
}
.review-item {
border: 1px solid #eee;
border-radius: 8px;
padding: 16px;
}
.review-product {
display: flex;
gap: 12px;
margin-bottom: 16px;
}
.review-product .product-image {
width: 60px;
height: 60px;
border-radius: 8px;
object-fit: cover;
}
.review-product .product-info h4 {
margin: 0 0 4px 0;
font-size: 14px;
color: #333;
}
.review-product .product-info p {
margin: 0;
font-size: 12px;
color: #666;
}
.review-rating {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.rating-label {
font-size: 14px;
color: #333;
}
.review-content {
margin-bottom: 16px;
}
.review-images {
margin-bottom: 16px;
}
.order-detail {
display: flex;
flex-direction: column;
gap: 24px;
}
.detail-section h4 {
margin: 0 0 16px 0;
font-size: 16px;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
.detail-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #f5f5f5;
}
.detail-item.total {
font-weight: 600;
color: #ff6b35;
border-bottom: none;
padding-top: 16px;
border-top: 1px solid #eee;
}
.detail-item .label {
color: #666;
font-size: 14px;
}
.detail-item .value {
color: #333;
font-size: 14px;
display: flex;
align-items: center;
gap: 4px;
}
.detail-product {
display: flex;
gap: 12px;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 8px;
}
.detail-product img {
width: 60px;
height: 60px;
border-radius: 8px;
object-fit: cover;
}
.detail-product .product-info h5 {
margin: 0 0 4px 0;
font-size: 14px;
color: #333;
}
.detail-product .product-info p {
margin: 0 0 8px 0;
font-size: 12px;
color: #666;
}
.detail-product .product-price {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.detail-item-price-group {
display: flex;
align-items: center;
gap: 4px;
color: #ff6b35;
font-weight: 600;
font-size: 14px;
}
.detail-item-price-group img {
width: 12px !important;
height: 12px !important;
}
.quantity-text {
color: #999;
font-size: 12px;
}
.detail-price-group {
display: flex;
align-items: center;
gap: 4px;
color: #ff6b35;
font-weight: 600;
}
.detail-price-group img {
width: 14px !important;
height: 14px !important;
}
.spec-info {
font-size: 12px;
color: #666;
margin: 4px 0;
background: #f5f5f5;
padding: 2px 8px;
border-radius: 4px;
display: inline-block;
}
/* 商品价格显示样式 */
.item-price-container {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2px;
margin-bottom: 4px;
}
.item-main-price {
display: flex;
align-items: center;
gap: 2px;
}
.item-rongdou-icon {
width: 12px;
height: 12px;
}
.item-rongdou-price {
font-size: 12px;
font-weight: 500;
color: #333;
}
.item-sub-price {
display: flex;
align-items: center;
gap: 2px;
}
.item-points-icon {
font-size: 10px;
color: #ffae00;
}
.item-points-price {
font-size: 10px;
color: #666;
}
/* 订单总计融豆图标样式 */
.total-rongdou-icon {
width: 14px;
height: 14px;
margin-right: 2px;
}
/* 订单详情商品价格样式 */
.detail-item-price-container {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.detail-item-main-price {
display: flex;
align-items: center;
gap: 2px;
}
.detail-rongdou-icon {
width: 12px !important;
height: 12px !important;
}
.detail-item-price-container .detail-rongdou-icon {
width: 12px !important;
height: 12px !important;
}
.detail-rongdou-price {
font-size: 12px;
font-weight: 500;
color: #333;
}
.detail-item-sub-price {
display: flex;
align-items: center;
gap: 2px;
}
.detail-points-icon {
font-size: 10px;
color: #ffae00;
}
.detail-points-price {
font-size: 10px;
color: #666;
}
/* 费用明细融豆图标样式 */
.cost-rongdou-icon {
width: 14px !important;
height: 14px !important;
margin-right: 2px;
}
.detail-price-group .cost-rongdou-icon {
width: 14px !important;
height: 14px !important;
}
/* 响应式设计 */
@media (max-width: 480px) {
.order-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.order-actions {
display: flex;
justify-content: flex-end;
gap: 6px;
padding: 12px 16px;
flex-wrap: nowrap;
}
.order-actions .el-button {
flex: 0 0 auto;
min-width: 70px;
font-size: 12px;
padding: 6px 12px;
}
.detail-item {
flex-direction: column;
gap: 4px;
}
}
</style>