1238 lines
27 KiB
Vue
1238 lines
27 KiB
Vue
<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> |