2025-07-26 15:35:53 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
|
2025-09-01 13:39:34 +08:00
|
|
|
|
<div v-else class="orders-container">
|
2025-07-26 15:35:53 +08:00
|
|
|
|
<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">
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<div class="item-price-container">
|
|
|
|
|
|
<div class="item-main-price">
|
2025-09-03 11:00:08 +08:00
|
|
|
|
<img src='/imgs/profile/rongdou.png' alt="融豆" class="item-rongdou-icon" />
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<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>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
<span class="quantity">x{{ item.quantity }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 订单总计 -->
|
|
|
|
|
|
<div class="order-total">
|
|
|
|
|
|
<div class="total-info">
|
2025-08-29 16:58:00 +08:00
|
|
|
|
<span>共{{ order.totalQuantity }}件商品</span>
|
|
|
|
|
|
<div class="total-price">
|
|
|
|
|
|
<span>总计:</span>
|
|
|
|
|
|
<div class="total-price-group">
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<span class="total-rongdou">
|
2025-09-03 11:00:08 +08:00
|
|
|
|
<img src='/imgs/profile/rongdou.png' alt="融豆" class="total-rongdou-icon" />
|
2025-09-02 11:41:20 +08:00
|
|
|
|
{{ order.totalRongdou }}
|
2025-08-29 16:58:00 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 订单操作 -->
|
|
|
|
|
|
<div class="order-actions">
|
2025-09-01 13:39:34 +08:00
|
|
|
|
<el-button
|
2025-09-01 17:42:19 +08:00
|
|
|
|
v-if="order.status === 'pre_order'"
|
2025-09-01 13:39:34 +08:00
|
|
|
|
type="primary"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@click="goToPay(order.id)"
|
|
|
|
|
|
>
|
|
|
|
|
|
立即支付
|
|
|
|
|
|
</el-button>
|
2025-09-05 11:40:34 +08:00
|
|
|
|
<el-button
|
|
|
|
|
|
v-if="order.status === 'pre_order'"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@click="cancelOrder(order.id)"
|
|
|
|
|
|
>
|
|
|
|
|
|
取消订单
|
|
|
|
|
|
</el-button>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
<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>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
<div class="detail-item">
|
|
|
|
|
|
<span class="label">用户名:</span>
|
|
|
|
|
|
<span class="value">{{ orderDetail.username || '未知用户' }}</span>
|
|
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
<div class="detail-item">
|
|
|
|
|
|
<span class="label">下单时间:</span>
|
|
|
|
|
|
<span class="value">{{ formatDateTime(orderDetail.createdAt) }}</span>
|
|
|
|
|
|
</div>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
<div class="detail-item">
|
|
|
|
|
|
<span class="label">更新时间:</span>
|
|
|
|
|
|
<span class="value">{{ formatDateTime(orderDetail.updatedAt) }}</span>
|
|
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
<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>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
<p v-if="item.specInfo" class="spec-info">规格:{{ item.specInfo }}</p>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
<div class="product-price">
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<div class="detail-item-price-container">
|
|
|
|
|
|
<div class="detail-item-main-price">
|
2025-09-03 11:00:08 +08:00
|
|
|
|
<img src='/imgs/profile/rongdou.png' alt="融豆" class="detail-rongdou-icon" />
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<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>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<span class="quantity-text">x {{ item.quantity }}</span>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
</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>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
<div class="value">
|
|
|
|
|
|
<span class="detail-price-group">
|
2025-09-03 11:00:08 +08:00
|
|
|
|
<img src='/imgs/profile/rongdou.png' alt="融豆" class="cost-rongdou-icon" />
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<span>{{ orderDetail.totalRongdou }}</span>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="detail-item total">
|
2025-08-29 16:58:00 +08:00
|
|
|
|
<span class="label">实付:</span>
|
|
|
|
|
|
<div class="value">
|
|
|
|
|
|
<span class="detail-price-group">
|
2025-09-03 11:00:08 +08:00
|
|
|
|
<img src='/imgs/profile/rongdou.png' alt="融豆" class="cost-rongdou-icon" />
|
2025-09-02 11:41:20 +08:00
|
|
|
|
<span>{{ orderDetail.totalRongdou }}</span>
|
2025-08-29 16:58:00 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2025-07-26 15:35:53 +08:00
|
|
|
|
</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'
|
2025-09-02 17:05:49 +08:00
|
|
|
|
import { getImageUrl } from '@/config'
|
2025-07-26 15:35:53 +08:00
|
|
|
|
|
|
|
|
|
|
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 },
|
2025-09-02 13:56:09 +08:00
|
|
|
|
{ label: '待发货', value: 'pending', count: 0 },
|
|
|
|
|
|
{ label: '待支付', value: 'pre_order', count: 0 },
|
2025-07-26 15:35:53 +08:00
|
|
|
|
{ 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: '暂无订单',
|
2025-09-01 17:42:19 +08:00
|
|
|
|
pending: '暂无待发货订单',
|
|
|
|
|
|
pre_order: '暂无待支付订单',
|
2025-07-26 15:35:53 +08:00
|
|
|
|
shipped: '暂无已发货订单',
|
|
|
|
|
|
completed: '暂无已完成订单',
|
|
|
|
|
|
cancelled: '暂无已取消订单'
|
|
|
|
|
|
}
|
|
|
|
|
|
return textMap[selectedStatus.value]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getStatusType = (status) => {
|
|
|
|
|
|
const typeMap = {
|
|
|
|
|
|
pending: 'warning',
|
2025-09-01 17:42:19 +08:00
|
|
|
|
pre_order: 'warning',
|
2025-07-26 15:35:53 +08:00
|
|
|
|
shipped: 'primary',
|
|
|
|
|
|
completed: 'success',
|
|
|
|
|
|
cancelled: 'danger'
|
|
|
|
|
|
}
|
|
|
|
|
|
return typeMap[status] || 'info'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
const mapOrderStatus = (backendStatus) => {
|
|
|
|
|
|
const statusMap = {
|
2025-09-01 17:42:19 +08:00
|
|
|
|
'pre_order': 'pre_order',
|
2025-08-29 16:58:00 +08:00
|
|
|
|
'pending': 'pending',
|
|
|
|
|
|
'shipped': 'shipped',
|
|
|
|
|
|
'completed': 'completed',
|
|
|
|
|
|
'cancelled': 'cancelled'
|
|
|
|
|
|
}
|
|
|
|
|
|
return statusMap[backendStatus] || 'pending'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-26 15:35:53 +08:00
|
|
|
|
const getStatusText = (status) => {
|
|
|
|
|
|
const textMap = {
|
2025-09-01 17:42:19 +08:00
|
|
|
|
pending: '待发货',
|
|
|
|
|
|
pre_order: '待支付',
|
2025-07-26 15:35:53 +08:00
|
|
|
|
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}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-01 13:39:34 +08:00
|
|
|
|
const goToPay = (orderId) => {
|
|
|
|
|
|
router.push(`/pay/${orderId}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-26 15:35:53 +08:00
|
|
|
|
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'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-09-01 17:42:19 +08:00
|
|
|
|
await api.put(`/orders/${orderId}/confirm`)
|
2025-07-26 15:35:53 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新订单状态
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
const viewOrderDetail = (orderId) => {
|
2025-07-26 15:35:53 +08:00
|
|
|
|
try {
|
2025-08-29 16:58:00 +08:00
|
|
|
|
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)
|
2025-07-26 15:35:53 +08:00
|
|
|
|
showOrderDetail.value = true
|
|
|
|
|
|
} catch (error) {
|
2025-08-29 16:58:00 +08:00
|
|
|
|
console.error('查看订单详情失败:', error)
|
|
|
|
|
|
ElMessage.error(`查看订单详情失败: ${error.message || '未知错误'}`)
|
2025-07-26 15:35:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getOrders = async (isLoadMore = false) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (!isLoadMore) {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
page.value = 1
|
|
|
|
|
|
} else {
|
|
|
|
|
|
loadingMore.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-01 09:33:46 +08:00
|
|
|
|
const {data} = await api.get('/orders', {
|
2025-07-26 15:35:53 +08:00
|
|
|
|
params: {
|
|
|
|
|
|
page: page.value,
|
|
|
|
|
|
limit: 10
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-08-01 09:33:46 +08:00
|
|
|
|
console.log(data,'response');
|
2025-07-26 15:35:53 +08:00
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
// 映射后端数据结构到前端需要的格式
|
|
|
|
|
|
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
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
2025-07-26 15:35:53 +08:00
|
|
|
|
if (isLoadMore) {
|
2025-08-29 16:58:00 +08:00
|
|
|
|
orders.value.push(...mappedOrders)
|
2025-07-26 15:35:53 +08:00
|
|
|
|
} else {
|
2025-08-29 16:58:00 +08:00
|
|
|
|
orders.value = mappedOrders
|
2025-07-26 15:35:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
hasMore.value = data.data.pagination.page < data.data.pagination.pages
|
2025-07-26 15:35:53 +08:00
|
|
|
|
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;
|
2025-09-01 13:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.orders-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 10px;
|
2025-07-26 15:35:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
.price-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-26 15:35:53 +08:00
|
|
|
|
.price {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
color: #ff6b35;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-26 15:35:53 +08:00
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
.total-price {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.total-price-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.total-points,
|
|
|
|
|
|
.total-rongdou {
|
2025-07-26 15:35:53 +08:00
|
|
|
|
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 {
|
2025-08-29 16:58:00 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-item-price-group {
|
2025-07-26 15:35:53 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
color: #ff6b35;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-02 11:41:20 +08:00
|
|
|
|
.detail-item-price-group img {
|
|
|
|
|
|
width: 12px !important;
|
|
|
|
|
|
height: 12px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
.quantity-text {
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-price-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
color: #ff6b35;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-02 11:41:20 +08:00
|
|
|
|
.detail-price-group img {
|
|
|
|
|
|
width: 14px !important;
|
|
|
|
|
|
height: 14px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:58:00 +08:00
|
|
|
|
.spec-info {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
margin: 4px 0;
|
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-02 11:41:20 +08:00
|
|
|
|
/* 商品价格显示样式 */
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-26 15:35:53 +08:00
|
|
|
|
/* 响应式设计 */
|
|
|
|
|
|
@media (max-width: 480px) {
|
|
|
|
|
|
.order-header {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-actions {
|
2025-09-05 11:40:34 +08:00
|
|
|
|
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;
|
2025-07-26 15:35:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-item {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|