Files
AIGC/demo/frontend/src/views/Orders.vue

526 lines
13 KiB
Vue
Raw Normal View History

2025-10-21 16:50:33 +08:00
<template>
<div class="orders">
<!-- 页面标题和操作 -->
<div class="page-header">
<div class="page-title">
<h2>
<el-icon><List /></el-icon>
订单管理
</h2>
</div>
<div class="page-actions">
<el-button type="primary" @click="$router.push('/orders/create')">
<el-icon><Plus /></el-icon>
创建订单
</el-button>
</div>
</div>
<!-- 筛选和搜索 -->
<el-card class="filter-card">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="8">
<el-select
v-model="filters.status"
placeholder="选择订单状态"
clearable
@change="handleFilterChange"
>
<el-option
v-for="status in orderStatuses"
:key="status.value"
:label="status.label"
:value="status.value"
/>
</el-select>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-input
v-model="filters.search"
placeholder="搜索订单号"
clearable
@input="handleSearch"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-button @click="resetFilters">重置筛选</el-button>
</el-col>
</el-row>
</el-card>
<!-- 订单列表 -->
<el-card class="orders-card">
<el-table
:data="orderStore.orders"
v-loading="orderStore.loading"
empty-text="暂无订单"
@sort-change="handleSortChange"
>
<el-table-column prop="orderNumber" label="订单号" width="150" sortable="custom">
<template #default="{ row }">
<router-link :to="`/orders/${row.id}`" class="order-link">
{{ row.orderNumber }}
</router-link>
</template>
</el-table-column>
<el-table-column prop="totalAmount" label="金额" width="120" sortable="custom">
<template #default="{ row }">
<span class="amount">{{ row.currency }} {{ row.totalAmount }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderType" label="类型" width="120">
<template #default="{ row }">
{{ getOrderTypeText(row.orderType) }}
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom">
<template #default="{ row }">
{{ formatDate(row.createdAt) }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button-group>
<el-button size="small" @click="$router.push(`/orders/${row.id}`)">
查看
</el-button>
<el-dropdown v-if="canPay(row)" trigger="click" :teleported="true" popper-class="table-dropdown" @command="(command) => handlePayment(row, command)">
<el-button size="small" type="success">
支付<el-icon class="el-icon--right"><ArrowDown /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="ALIPAY">
<el-icon><CreditCard /></el-icon>
支付宝支付
</el-dropdown-item>
<el-dropdown-item command="PAYPAL">
<el-icon><CreditCard /></el-icon>
PayPal支付
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button
v-if="canCancel(row)"
size="small"
type="danger"
@click="handleCancel(row)"
>
取消
</el-button>
</el-button-group>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.size"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 取消订单对话框 -->
<el-dialog
v-model="cancelDialogVisible"
title="取消订单"
width="400px"
>
<el-form :model="cancelForm" label-width="80px">
<el-form-item label="取消原因">
<el-input
v-model="cancelForm.reason"
type="textarea"
:rows="3"
placeholder="请输入取消原因(可选)"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="cancelDialogVisible = false">取消</el-button>
<el-button type="danger" @click="confirmCancel">确认取消</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue'
import { useOrderStore } from '@/stores/orders'
import { createOrderPayment } from '@/api/orders'
import { ElMessage, ElMessageBox } from 'element-plus'
const orderStore = useOrderStore()
// 筛选条件
const filters = reactive({
status: '',
search: ''
})
// 分页信息
const pagination = reactive({
page: 1,
size: 10,
total: 0
})
// 排序
const sortBy = ref('createdAt')
const sortDir = ref('desc')
// 取消订单对话框
const cancelDialogVisible = ref(false)
const cancelForm = reactive({
reason: ''
})
const currentCancelOrder = ref(null)
// 订单状态选项
const orderStatuses = [
{ value: '', label: '全部状态' },
{ value: 'PENDING', label: '待支付' },
{ value: 'CONFIRMED', label: '已确认' },
{ value: 'PAID', label: '已支付' },
{ value: 'PROCESSING', label: '处理中' },
{ value: 'SHIPPED', label: '已发货' },
{ value: 'DELIVERED', label: '已送达' },
{ value: 'COMPLETED', label: '已完成' },
{ value: 'CANCELLED', label: '已取消' },
{ value: 'REFUNDED', label: '已退款' }
]
// 获取状态类型
const getStatusType = (status) => {
const statusMap = {
'PENDING': 'warning',
'CONFIRMED': 'info',
'PAID': 'primary',
'PROCESSING': '',
'SHIPPED': 'success',
'DELIVERED': 'success',
'COMPLETED': 'success',
'CANCELLED': 'danger',
'REFUNDED': 'info'
}
return statusMap[status] || ''
}
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
'PENDING': '待支付',
'CONFIRMED': '已确认',
'PAID': '已支付',
'PROCESSING': '处理中',
'SHIPPED': '已发货',
'DELIVERED': '已送达',
'COMPLETED': '已完成',
'CANCELLED': '已取消',
'REFUNDED': '已退款'
}
return statusMap[status] || status
}
// 获取订单类型文本
const getOrderTypeText = (orderType) => {
const typeMap = {
'PRODUCT': '商品订单',
'SERVICE': '服务订单',
'SUBSCRIPTION': '订阅订单',
'DIGITAL': '数字商品',
'PHYSICAL': '实体商品'
}
return typeMap[orderType] || orderType
}
// 格式化日期
const formatDate = (dateString) => {
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
// 检查是否可以支付
const canPay = (order) => {
return order.status === 'PENDING' || order.status === 'CONFIRMED'
}
// 检查是否可以取消
const canCancel = (order) => {
return order.status === 'PENDING' || order.status === 'CONFIRMED'
}
// 获取订单列表
const fetchOrders = async () => {
console.log('=== 开始获取订单列表 ===')
console.log('当前用户:', userStore.user)
console.log('认证状态:', userStore.isAuthenticated)
console.log('Token:', sessionStorage.getItem('token'))
const params = {
page: pagination.page - 1,
size: pagination.size,
sortBy: sortBy.value,
sortDir: sortDir.value
}
if (filters.status) {
params.status = filters.status
}
if (filters.search) {
params.search = filters.search
}
console.log('请求参数:', params)
try {
const response = await orderStore.fetchOrders(params)
console.log('API响应:', response)
if (response.success) {
pagination.total = orderStore.pagination.total
console.log('订单数据:', orderStore.orders)
console.log('分页信息:', orderStore.pagination)
} else {
console.error('获取订单失败:', response.message)
}
} catch (error) {
console.error('获取订单异常:', error)
}
}
// 筛选变化
const handleFilterChange = () => {
pagination.page = 1
fetchOrders()
}
// 搜索
const handleSearch = () => {
pagination.page = 1
fetchOrders()
}
// 重置筛选
const resetFilters = () => {
filters.status = ''
filters.search = ''
pagination.page = 1
fetchOrders()
}
// 排序变化
const handleSortChange = ({ prop, order }) => {
if (prop) {
sortBy.value = prop
sortDir.value = order === 'ascending' ? 'asc' : 'desc'
fetchOrders()
}
}
// 分页大小变化
const handleSizeChange = (size) => {
pagination.size = size
pagination.page = 1
fetchOrders()
}
// 当前页变化
const handleCurrentChange = (page) => {
pagination.page = page
fetchOrders()
}
// 处理支付
const handlePayment = async (order, paymentMethod) => {
try {
const response = await createOrderPayment(order.id, paymentMethod)
if (response.success) {
ElMessage.success('正在跳转到支付页面...')
// 这里应该跳转到支付页面
window.open(response.data.paymentUrl, '_blank')
} else {
ElMessage.error(response.message || '创建支付失败')
}
} catch (error) {
console.error('Payment error:', error)
ElMessage.error('创建支付失败')
}
}
// 处理取消
const handleCancel = (order) => {
currentCancelOrder.value = order
cancelForm.reason = ''
cancelDialogVisible.value = true
}
// 确认取消
const confirmCancel = async () => {
if (!currentCancelOrder.value) return
try {
const response = await orderStore.cancelOrderById(
currentCancelOrder.value.id,
cancelForm.reason
)
if (response.success) {
ElMessage.success('订单取消成功')
cancelDialogVisible.value = false
fetchOrders()
} else {
ElMessage.error(response.message || '取消订单失败')
}
} catch (error) {
console.error('Cancel order error:', error)
ElMessage.error('取消订单失败')
}
}
onMounted(() => {
fetchOrders()
})
</script>
<style scoped>
.orders {
max-width: 1200px;
margin: 0 auto;
min-height: 100vh;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
position: relative;
overflow-x: hidden;
padding: 20px;
}
/* 页面特殊效果 */
.orders::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 30% 20%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
radial-gradient(circle at 70% 80%, rgba(255, 255, 255, 0.05) 0%, transparent 50%);
animation: ordersPulse 5s ease-in-out infinite alternate;
pointer-events: none;
z-index: 1;
}
@keyframes ordersPulse {
0% { opacity: 0.3; transform: scale(1); }
100% { opacity: 0.6; transform: scale(1.02); }
}
/* 内容层级 */
.orders > * {
position: relative;
z-index: 2;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-title h2 {
margin: 0;
color: #303133;
display: flex;
align-items: center;
gap: 8px;
}
.page-actions {
display: flex;
gap: 12px;
}
.filter-card {
margin-bottom: 20px;
}
.orders-card {
margin-bottom: 20px;
}
.order-link {
color: #409EFF;
text-decoration: none;
font-weight: 500;
}
.order-link:hover {
text-decoration: underline;
}
.amount {
font-weight: 600;
color: #E6A23C;
}
.pagination-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
/* 确保表格内下拉菜单不被裁剪/遮挡 */
:deep(.table-dropdown) {
z-index: 3000 !important;
}
@media (max-width: 768px) {
.page-header {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.page-actions {
justify-content: center;
}
}
</style>