feat: 更新订单和用户视图,增强订单信息展示,支持充值类型订单

This commit is contained in:
2025-12-31 12:19:01 +08:00
parent 95bc5bdb5d
commit c133169c7b
5 changed files with 161 additions and 105 deletions

View File

@@ -50,19 +50,24 @@
<div class="space-y-4">
<div v-for="order in recentOrders" :key="order.id" @click="$router.push(`/me/orders/${order.id}`)"
class="flex items-center gap-4 p-4 border border-slate-100 rounded-lg hover:border-primary-100 hover:shadow-sm transition-all cursor-pointer active:scale-[0.99] group">
<div class="w-16 h-16 bg-slate-100 rounded object-cover flex-shrink-0">
<img v-if="order.items && order.items.length > 0"
<div class="w-16 h-16 bg-slate-100 rounded flex-shrink-0 flex items-center justify-center relative overflow-hidden">
<template v-if="order.type === 'recharge' || !order.items?.length">
<i class="pi pi-wallet text-2xl text-primary-500"></i>
</template>
<img v-else-if="order.items && order.items.length > 0"
:src="order.items[0].cover"
class="w-full h-full object-cover rounded transition-transform group-hover:scale-105">
<i v-else class="pi pi-box text-2xl text-slate-400"></i>
</div>
<div class="flex-1 min-w-0">
<h3 class="font-bold text-slate-900 truncate group-hover:text-primary-600 transition-colors">
{{ order.items && order.items.length > 0 ? order.items[0].title : '未知商品' }}</h3>
{{ (order.type === 'recharge' || !order.items?.length) ? '账户充值' : (order.items?.[0]?.title || '未知商品') }}
</h3>
<div class="text-sm text-slate-500 mt-1">{{ order.create_time }} · 订单号: {{ order.id }}</div>
</div>
<div class="text-right">
<div class="font-bold text-slate-900">¥ {{ order.amount }}</div>
<div class="text-sm text-green-600 mt-1">{{ order.status }}</div>
<div class="text-sm mt-1 font-medium" :class="statusColor(order.status)">{{ order.status_description }}</div>
</div>
</div>
<div v-if="recentOrders.length === 0" class="text-center text-slate-400 py-4">暂无订单</div>
@@ -90,22 +95,38 @@ const wallet = ref({ balance: 0, points: 0 });
const couponCount = ref(0);
const recentOrders = ref([]);
const statusColor = (status) => {
const map = {
created: 'text-orange-600',
paid: 'text-blue-600',
completed: 'text-green-600',
refunding: 'text-purple-600',
refunded: 'text-slate-500',
cancelled: 'text-slate-400'
};
return map[status] || 'text-slate-500';
};
const fetchData = async () => {
try {
const w = await userApi.getWallet();
wallet.value.balance = w.balance || 0;
const u = await userApi.getMe();
wallet.value.points = u.points || 0;
const c = await userApi.getCoupons('unused');
couponCount.value = c.length;
const o = await userApi.getOrders('all');
recentOrders.value = (o || []).slice(0, 3);
} catch (e) {
console.error(e);
}
// Fetch Wallet
userApi.getWallet()
.then(w => { wallet.value.balance = w.balance || 0; })
.catch(e => console.error("Wallet fetch failed:", e));
// Fetch User Info (Points)
userApi.getMe()
.then(u => { wallet.value.points = u.points || 0; })
.catch(e => console.error("Me fetch failed:", e));
// Fetch Coupons
userApi.getCoupons('unused')
.then(c => { couponCount.value = (c || []).length; })
.catch(e => console.error("Coupons fetch failed:", e));
// Fetch Recent Orders
userApi.getOrders('all')
.then(o => { recentOrders.value = (o || []).slice(0, 3); })
.catch(e => console.error("Orders fetch failed:", e));
};
onMounted(() => {

View File

@@ -32,25 +32,40 @@
<!-- Order Header -->
<div class="bg-slate-50 px-4 py-3 flex items-center justify-between text-sm text-slate-500">
<div class="flex items-center gap-4">
<span class="font-medium text-slate-900">{{ order.date }}</span>
<span class="font-medium text-slate-900">{{ order.create_time }}</span>
<span class="select-all">订单号: {{ order.id }}</span>
<span class="hover:text-primary-600 cursor-pointer transition-colors">{{ order.tenantName }} <i class="pi pi-angle-right text-xs"></i></span>
<span class="hover:text-primary-600 cursor-pointer transition-colors" v-if="order.tenant_id !== '0'">{{ order.tenant_name }} <i class="pi pi-angle-right text-xs"></i></span>
<span v-else class="text-slate-500">平台自营</span>
</div>
<div class="font-bold" :class="statusColor(order.status)">{{ statusText(order.status) }}</div>
<div class="font-bold" :class="statusColor(order.status)">{{ order.status_description }}</div>
</div>
<!-- Order Body -->
<div class="p-4 flex flex-col sm:flex-row gap-6">
<!-- Product Info (Clickable Area) -->
<div class="flex-1 flex gap-4 cursor-pointer" @click="$router.push(`/me/orders/${order.id}`)">
<div class="w-24 h-16 bg-slate-100 rounded object-cover flex-shrink-0 relative overflow-hidden group-hover:opacity-90 transition-opacity">
<img :src="order.cover" class="w-full h-full object-cover">
<div v-if="order.type === 'video'" class="absolute inset-0 flex items-center justify-center bg-black/20 text-white"><i class="pi pi-play-circle"></i></div>
<div class="w-24 h-16 bg-slate-100 rounded flex-shrink-0 relative overflow-hidden group-hover:opacity-90 transition-opacity flex items-center justify-center">
<template v-if="order.type === 'recharge' || !order.items?.length">
<i class="pi pi-wallet text-3xl text-primary-500"></i>
</template>
<template v-else-if="order.items && order.items.length > 0">
<img :src="order.items[0].cover" class="w-full h-full object-cover">
<!-- Assuming genre is on item, not order.type -->
</template>
<template v-else>
<i class="pi pi-box text-2xl text-slate-400"></i>
</template>
</div>
<div>
<h3 class="font-bold text-slate-900 line-clamp-1 mb-1 group-hover:text-primary-600 transition-colors">{{ order.title }}</h3>
<div class="text-xs text-slate-500 mb-2">{{ order.typeLabel }}</div>
<div v-if="order.isVirtual" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-blue-600">虚拟发货</div>
<h3 class="font-bold text-slate-900 line-clamp-1 mb-1 group-hover:text-primary-600 transition-colors">
{{ (order.type === 'recharge' || !order.items?.length) ? '账户充值' : (order.items?.[0]?.title || '未知商品') }}
</h3>
<div class="text-xs text-slate-500 mb-2">
{{ (order.type === 'recharge' || !order.items?.length) ? '在线充值' : (order.items?.[0]?.genre || '数字内容') }}
</div>
<div v-if="order.is_virtual || order.type === 'recharge' || !order.items?.length" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-blue-600">
{{ (order.type === 'recharge' || !order.items?.length) ? '即时到账' : '虚拟发货' }}
</div>
</div>
</div>
@@ -61,10 +76,10 @@
<div class="text-xs text-slate-400">在线支付</div>
</div>
<div class="flex flex-col gap-2">
<button v-if="order.status === 'unpaid'" class="px-4 py-1.5 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700 transition-colors shadow-sm active:scale-95 cursor-pointer">去支付</button>
<button v-if="order.status === 'created'" class="px-4 py-1.5 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700 transition-colors shadow-sm active:scale-95 cursor-pointer">去支付</button>
<router-link :to="`/me/orders/${order.id}`" v-if="order.status === 'paid' || order.status === 'completed'" class="px-4 py-1.5 border border-slate-300 text-slate-700 text-sm font-medium rounded-lg hover:bg-slate-50 hover:border-slate-400 transition-colors inline-block text-center cursor-pointer active:scale-95">查看详情</router-link>
<button v-if="order.status === 'completed'" class="px-4 py-1.5 text-primary-600 text-sm hover:underline cursor-pointer">申请售后</button>
<button v-if="order.status === 'unpaid'" class="text-xs text-slate-400 hover:text-slate-600 cursor-pointer">取消订单</button>
<button v-if="order.status === 'completed' && order.type !== 'recharge'" class="px-4 py-1.5 text-primary-600 text-sm hover:underline cursor-pointer">申请售后</button>
<button v-if="order.status === 'created'" class="text-xs text-slate-400 hover:text-slate-600 cursor-pointer">取消订单</button>
</div>
</div>
</div>
@@ -124,18 +139,6 @@ watch(currentTab, () => {
// Use orders directly (filtered by backend)
const filteredOrders = computed(() => orders.value);
const statusText = (status) => {
const map = {
created: '待支付',
paid: '已支付',
completed: '交易成功',
refunding: '退款中',
refunded: '已退款',
cancelled: '已取消'
};
return map[status] || status;
};
const statusColor = (status) => {
const map = {
created: 'text-orange-600',