feat: 图片压缩后上传COS + 修复订单LazyInitializationException + 添加调试日志

This commit is contained in:
AIGC Developer
2025-12-05 21:06:16 +08:00
parent b4b0230ee1
commit 624d560fb4
35 changed files with 1916 additions and 218 deletions

View File

@@ -102,13 +102,10 @@
<el-option :label="$t('orders.cancelled')" value="CANCELLED" />
<el-option :label="$t('orders.refunded')" value="REFUNDED" />
</el-select>
<el-select v-model="filters.type" :placeholder="$t('orders.allTypes')" size="small" @change="handleFilterChange">
<el-option :label="$t('orders.allTypes')" value="" />
<el-option label="商品订单" value="PRODUCT" />
<el-option label="服务订单" value="SERVICE" />
<el-option label="订阅订单" value="SUBSCRIPTION" />
<el-option label="数字商品" value="DIGITAL" />
<el-option label="实体商品" value="PHYSICAL" />
<el-select v-model="filters.paymentMethod" :placeholder="$t('orders.allPaymentMethods') || '全部支付方式'" size="small" @change="handleFilterChange">
<el-option :label="$t('orders.allPaymentMethods') || '全部支付方式'" value="" />
<el-option :label="$t('orders.alipay') || '支付宝'" value="ALIPAY" />
<el-option :label="$t('orders.paypal') || 'PayPal'" value="PAYPAL" />
</el-select>
</div>
<div class="toolbar-right">
@@ -200,6 +197,92 @@
</section>
</main>
<!-- 订单详情弹窗 -->
<el-dialog
v-model="orderDetailVisible"
:title="$t('orders.orderDetail') || '订单详情'"
width="600px"
class="order-detail-dialog"
>
<div v-if="currentOrderDetail" class="order-detail-content">
<div class="detail-section">
<h4>{{ $t('orders.basicInfo') || '基本信息' }}</h4>
<div class="detail-grid">
<div class="detail-item">
<span class="label">{{ $t('orders.orderNumber') }}:</span>
<span class="value">{{ currentOrderDetail.orderNumber || currentOrderDetail.id }}</span>
</div>
<div class="detail-item">
<span class="label">{{ $t('orders.username') }}:</span>
<span class="value">{{ currentOrderDetail.user?.username || '-' }}</span>
</div>
<div class="detail-item">
<span class="label">{{ $t('orders.orderType') || '订单类型' }}:</span>
<span class="value">{{ getOrderTypeText(currentOrderDetail.orderType) }}</span>
</div>
<div class="detail-item">
<span class="label">{{ $t('orders.status') }}:</span>
<span class="status-tag" :class="getStatusClass(currentOrderDetail.status)">
{{ getStatusText(currentOrderDetail.status) }}
</span>
</div>
</div>
</div>
<div class="detail-section">
<h4>{{ $t('orders.paymentInfo') || '支付信息' }}</h4>
<div class="detail-grid">
<div class="detail-item">
<span class="label">{{ $t('orders.amount') }}:</span>
<span class="value amount">{{ currentOrderDetail.currency || '¥' }}{{ currentOrderDetail.totalAmount || 0 }}</span>
</div>
<div class="detail-item">
<span class="label">{{ $t('orders.paymentMethod') }}:</span>
<span class="value">
<template v-if="currentOrderDetail.paymentMethod === 'ALIPAY'">{{ $t('orders.alipay') }}</template>
<template v-else-if="currentOrderDetail.paymentMethod === 'WECHAT'">{{ $t('orders.wechat') }}</template>
<template v-else-if="currentOrderDetail.paymentMethod === 'PAYPAL'">{{ $t('orders.paypal') }}</template>
<template v-else>{{ $t('orders.unpaid') }}</template>
</span>
</div>
<div class="detail-item">
<span class="label">{{ $t('orders.createTime') }}:</span>
<span class="value">{{ formatDate(currentOrderDetail.createdAt) }}</span>
</div>
<div class="detail-item" v-if="currentOrderDetail.paidAt">
<span class="label">{{ $t('orders.paidTime') || '支付时间' }}:</span>
<span class="value">{{ formatDate(currentOrderDetail.paidAt) }}</span>
</div>
</div>
</div>
<div class="detail-section" v-if="currentOrderDetail.contactEmail || currentOrderDetail.contactPhone">
<h4>{{ $t('orders.contactInfo') || '联系信息' }}</h4>
<div class="detail-grid">
<div class="detail-item" v-if="currentOrderDetail.contactEmail">
<span class="label">{{ $t('orders.email') || '邮箱' }}:</span>
<span class="value">{{ currentOrderDetail.contactEmail }}</span>
</div>
<div class="detail-item" v-if="currentOrderDetail.contactPhone">
<span class="label">{{ $t('orders.phone') || '电话' }}:</span>
<span class="value">{{ currentOrderDetail.contactPhone }}</span>
</div>
</div>
</div>
<div class="detail-section" v-if="currentOrderDetail.description">
<h4>{{ $t('orders.description') || '订单描述' }}</h4>
<p class="description-text">{{ currentOrderDetail.description }}</p>
</div>
</div>
<div v-else class="loading-container">
<el-icon class="is-loading"><Loading /></el-icon>
<span>{{ $t('common.loading') || '加载中...' }}</span>
</div>
<template #footer>
<el-button @click="orderDetailVisible = false">{{ $t('common.close') || '关闭' }}</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -219,7 +302,8 @@ import {
ArrowRight,
Delete,
CreditCard,
Wallet
Wallet,
Loading
} from '@element-plus/icons-vue'
import { getOrders, getAdminOrders, getOrderStats, deleteOrder as deleteOrderAPI, deleteOrders } from '@/api/orders'
import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
@@ -245,7 +329,7 @@ const systemUptime = ref('加载中...')
// 筛选条件
const filters = reactive({
status: '',
type: '',
paymentMethod: '',
search: ''
})
@@ -417,8 +501,13 @@ const goToPage = (page) => {
fetchOrders()
}
const viewOrder = (order) => {
router.push(`/orders/${order.id}`)
// 订单详情弹窗相关
const orderDetailVisible = ref(false)
const currentOrderDetail = ref(null)
const viewOrder = async (order) => {
currentOrderDetail.value = order
orderDetailVisible.value = true
}
const deleteOrder = async (order) => {
@@ -433,19 +522,24 @@ const deleteOrder = async (order) => {
}
)
await deleteOrderAPI(order.id)
const response = await deleteOrderAPI(order.id)
console.log('删除订单响应:', response)
const index = orders.value.findIndex(o => o.id === order.id)
if (index > -1) {
orders.value.splice(index, 1)
totalOrders.value--
// 检查响应状态
if (response.data?.success) {
const index = orders.value.findIndex(o => o.id === order.id)
if (index > -1) {
orders.value.splice(index, 1)
totalOrders.value--
}
ElMessage.success('删除成功')
} else {
ElMessage.error(response.data?.message || '删除失败')
}
ElMessage.success('删除成功')
} catch (error) {
if (error !== 'cancel') {
console.error('删除失败:', error)
ElMessage.error('删除失败')
ElMessage.error('删除失败: ' + (error.message || '未知错误'))
}
}
}
@@ -468,17 +562,21 @@ const deleteSelected = async () => {
)
const ids = selectedOrders.value.map(o => o.id)
await deleteOrders(ids)
const response = await deleteOrders(ids)
console.log('批量删除订单响应:', response)
orders.value = orders.value.filter(o => !ids.includes(o.id))
totalOrders.value -= ids.length
selectedOrders.value = []
ElMessage.success('批量删除成功')
if (response.data?.success) {
orders.value = orders.value.filter(o => !ids.includes(o.id))
totalOrders.value -= response.data?.deletedCount || ids.length
selectedOrders.value = []
ElMessage.success(response.data?.message || '批量删除成功')
} else {
ElMessage.error(response.data?.message || '批量删除失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('批量删除失败:', error)
ElMessage.error('批量删除失败')
ElMessage.error('批量删除失败: ' + (error.message || '未知错误'))
}
}
}
@@ -493,8 +591,9 @@ const fetchOrders = async () => {
const response = await apiFunction({
page: currentPage.value - 1,
size: pageSize.value,
status: filters.status,
search: filters.search || searchText.value
status: filters.status || undefined,
paymentMethod: filters.paymentMethod || undefined,
search: filters.search || searchText.value || undefined
})
console.log('获取订单列表响应:', response)
@@ -1002,6 +1101,98 @@ const fetchSystemStats = async () => {
padding: 16px;
}
}
/* 订单详情弹窗样式 */
.order-detail-content {
padding: 0 10px;
}
.detail-section {
margin-bottom: 24px;
}
.detail-section:last-child {
margin-bottom: 0;
}
.detail-section h4 {
font-size: 15px;
font-weight: 600;
color: #1e293b;
margin: 0 0 16px 0;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px 24px;
}
.detail-item {
display: flex;
align-items: center;
gap: 8px;
}
.detail-item .label {
color: #64748b;
font-size: 14px;
min-width: 80px;
}
.detail-item .value {
color: #1e293b;
font-size: 14px;
font-weight: 500;
}
.detail-item .value.amount {
color: #f59e0b;
font-weight: 600;
font-size: 16px;
}
.description-text {
color: #475569;
font-size: 14px;
line-height: 1.6;
margin: 0;
padding: 12px;
background: #f8fafc;
border-radius: 6px;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
color: #64748b;
gap: 12px;
}
.loading-container .el-icon {
font-size: 32px;
color: #3b82f6;
}
:deep(.order-detail-dialog .el-dialog__header) {
padding: 16px 20px;
border-bottom: 1px solid #e5e7eb;
margin-right: 0;
}
:deep(.order-detail-dialog .el-dialog__body) {
padding: 20px;
}
:deep(.order-detail-dialog .el-dialog__footer) {
padding: 12px 20px;
border-top: 1px solid #e5e7eb;
}
</style>