Files
quyun-v2/frontend/portal/src/views/user/OrdersView.vue
2025-12-26 09:34:41 +08:00

165 lines
6.7 KiB
Vue

<template>
<div class="bg-white rounded-xl shadow-sm border border-slate-100 min-h-[600px]">
<!-- Header & Tabs -->
<div class="px-6 pt-6 border-b border-slate-100">
<h1 class="text-2xl font-bold text-slate-900 mb-6">我的订单</h1>
<div class="flex items-center gap-8">
<button
v-for="tab in tabs"
:key="tab.value"
@click="currentTab = tab.value"
class="pb-4 text-sm font-medium transition-colors border-b-2"
:class="currentTab === tab.value ? 'text-primary-600 border-primary-600' : 'text-slate-500 border-transparent hover:text-slate-700'"
>
{{ tab.label }}
</button>
</div>
</div>
<!-- Order List -->
<div class="p-6 space-y-6">
<!-- Search/Filter (Optional) -->
<div class="flex justify-end mb-4">
<div class="relative w-64">
<i class="pi pi-search absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
<input type="text" placeholder="搜索订单号或商品..." class="w-full pl-10 pr-4 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:border-primary-500">
</div>
</div>
<!-- List Items -->
<div v-if="filteredOrders.length > 0" class="space-y-6">
<div v-for="order in filteredOrders" :key="order.id" class="border border-slate-200 rounded-lg overflow-hidden hover:border-slate-300 transition-colors">
<!-- 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>订单号: {{ order.id }}</span>
<span>{{ order.tenantName }}</span>
</div>
<div class="font-bold" :class="statusColor(order.status)">{{ statusText(order.status) }}</div>
</div>
<!-- Order Body -->
<div class="p-4 flex flex-col sm:flex-row gap-6">
<!-- Product Info -->
<div class="flex-1 flex gap-4">
<div class="w-24 h-16 bg-slate-100 rounded object-cover flex-shrink-0 relative overflow-hidden">
<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>
<div>
<h3 class="font-bold text-slate-900 line-clamp-1 mb-1">{{ 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>
</div>
</div>
<!-- Price & Actions -->
<div class="flex items-center justify-between sm:w-1/3 sm:border-l sm:border-slate-100 sm:pl-6">
<div class="text-right sm:text-left">
<div class="font-bold text-slate-900 text-lg">¥ {{ order.amount }}</div>
<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">去支付</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 inline-block text-center">查看详情</router-link>
<button v-if="order.status === 'completed'" class="px-4 py-1.5 text-primary-600 text-sm hover:underline">申请售后</button>
<button v-if="order.status === 'unpaid'" class="text-xs text-slate-400 hover:text-slate-600">取消订单</button>
</div>
</div>
</div>
</div>
</div>
<!-- Empty State -->
<div v-else class="text-center py-20">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-slate-50 mb-4">
<i class="pi pi-shopping-bag text-2xl text-slate-300"></i>
</div>
<p class="text-slate-500 text-lg">暂无相关订单</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const currentTab = ref('all');
const tabs = [
{ label: '全部订单', value: 'all' },
{ label: '待支付', value: 'unpaid' },
{ label: '已完成', value: 'completed' },
{ label: '退款/售后', value: 'refund' }
];
// Mock Data
const orders = ref([
{
id: '82934712',
date: '2025-12-24 14:30',
tenantName: '梅派传人小林',
cover: 'https://images.unsplash.com/photo-1514306191717-452ec28c7f31?ixlib=rb-1.2.1&auto=format&fit=crop&w=100&q=60',
title: '《霸王别姬》全本实录珍藏版',
type: 'video',
typeLabel: '戏曲视频',
isVirtual: true,
amount: '9.90',
status: 'completed'
},
{
id: '82934713',
date: '2025-12-23 09:15',
tenantName: '戏曲周边商城',
cover: 'https://images.unsplash.com/photo-1557683316-973673baf926?ixlib=rb-1.2.1&auto=format&fit=crop&w=100&q=60',
title: '京剧脸谱纪念书签 (一套4张)',
type: 'product',
typeLabel: '实体商品',
isVirtual: false,
amount: '45.00',
status: 'unpaid'
},
{
id: '82934711',
date: '2025-12-20 18:20',
tenantName: '豫剧李大师',
cover: 'https://images.unsplash.com/photo-1469571486292-0ba58a3f068b?ixlib=rb-1.2.1&auto=format&fit=crop&w=100&q=60',
title: '豫剧唱腔发音技巧专栏',
type: 'article',
typeLabel: '付费专栏',
isVirtual: true,
amount: '99.00',
status: 'refunded' // or refunding
}
]);
const filteredOrders = computed(() => {
if (currentTab.value === 'all') return orders.value;
if (currentTab.value === 'refund') return orders.value.filter(o => ['refunded', 'refunding'].includes(o.status));
return orders.value.filter(o => o.status === currentTab.value);
});
const statusText = (status) => {
const map = {
unpaid: '待支付',
paid: '已支付',
completed: '交易成功',
refunding: '退款中',
refunded: '已退款',
cancelled: '已取消'
};
return map[status] || status;
};
const statusColor = (status) => {
const map = {
unpaid: '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';
};
</script>