205 lines
7.8 KiB
Vue
205 lines
7.8 KiB
Vue
<script setup>
|
|
import { orderService } from '@/api/orderService';
|
|
import { formatDate } from '@/utils/date';
|
|
import Badge from 'primevue/badge';
|
|
import Column from 'primevue/column';
|
|
import ConfirmDialog from 'primevue/confirmdialog';
|
|
import DataTable from 'primevue/datatable';
|
|
import InputText from 'primevue/inputtext';
|
|
import ProgressSpinner from 'primevue/progressspinner';
|
|
import Toast from 'primevue/toast';
|
|
import { useConfirm } from 'primevue/useconfirm';
|
|
import { useToast } from 'primevue/usetoast';
|
|
import { onMounted, ref } from 'vue';
|
|
|
|
const toast = useToast();
|
|
const confirm = useConfirm();
|
|
|
|
const globalFilterValue = ref('');
|
|
const loading = ref(false);
|
|
const searchTimeout = ref(null);
|
|
const filters = ref({
|
|
global: { value: null, matchMode: 'contains' },
|
|
status: { value: null, matchMode: 'equals' }
|
|
});
|
|
|
|
const orders = ref({
|
|
items: [],
|
|
total: 0,
|
|
page: 1,
|
|
limit: 10
|
|
});
|
|
|
|
const first = ref(0);
|
|
const rows = ref(10);
|
|
|
|
const orderStatusMap = {
|
|
'pending': { label: '待支付', severity: 'warning' },
|
|
'paid': { label: '已支付', severity: 'success' },
|
|
'refunding': { label: '退款中', severity: 'help' },
|
|
'refunded': { label: '已退款', severity: 'info' },
|
|
'cancelled': { label: '已取消', severity: 'danger' },
|
|
'completed': { label: '已完成', severity: 'success' }
|
|
};
|
|
|
|
const formatPrice = (price) => {
|
|
return (price / 100).toFixed(2);
|
|
};
|
|
|
|
const getDiscountAmount = (price, discount) => {
|
|
return (price * discount / 100);
|
|
};
|
|
|
|
const getFinalPrice = (price, discount) => {
|
|
return price - getDiscountAmount(price, discount);
|
|
};
|
|
|
|
const fetchOrders = async () => {
|
|
loading.value = true;
|
|
try {
|
|
const currentPage = (first.value / rows.value) + 1;
|
|
const response = await orderService.getOrders({
|
|
page: currentPage,
|
|
limit: rows.value,
|
|
keyword: globalFilterValue.value
|
|
});
|
|
orders.value = response.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch orders:', error);
|
|
toast.add({ severity: 'error', summary: '错误', detail: '加载订单数据失败', life: 3000 });
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
const onPage = (event) => {
|
|
first.value = event.first;
|
|
rows.value = event.rows;
|
|
fetchOrders();
|
|
};
|
|
|
|
const onSearch = (event) => {
|
|
if (searchTimeout.value) {
|
|
clearTimeout(searchTimeout.value);
|
|
}
|
|
|
|
searchTimeout.value = setTimeout(() => {
|
|
first.value = 0;
|
|
fetchOrders();
|
|
}, 300);
|
|
};
|
|
|
|
const handleDelete = (order) => {
|
|
confirm.require({
|
|
message: `确定要删除订单 "${order.id}" 吗?`,
|
|
header: '确认删除',
|
|
icon: 'pi pi-exclamation-triangle',
|
|
acceptClass: 'p-button-danger',
|
|
accept: async () => {
|
|
try {
|
|
await orderService.deleteOrder(order.id);
|
|
toast.add({ severity: 'success', summary: '成功', detail: '订单已删除', life: 3000 });
|
|
fetchOrders();
|
|
} catch (error) {
|
|
console.error('Failed to delete order:', error);
|
|
toast.add({ severity: 'error', summary: '错误', detail: '删除订单失败', life: 3000 });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
onMounted(() => {
|
|
fetchOrders();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<Toast />
|
|
<ConfirmDialog />
|
|
|
|
<div class="w-full">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h1 class="text-2xl font-semibold text-gray-800">订单列表</h1>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="pb-10 flex">
|
|
<InputText v-model="globalFilterValue" placeholder="搜索订单..." class="flex-1" @input="onSearch" />
|
|
</div>
|
|
|
|
<DataTable v-model:filters="filters" :value="orders.items" :paginator="true" :rows="rows"
|
|
:totalRecords="orders.total" :loading="loading" :lazy="true" :first="first" @page="onPage"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
:rowsPerPageOptions="[10, 25, 50]"
|
|
currentPageReportTemplate="显示第 {first} 到 {last} 条,共 {totalRecords} 条结果" dataKey="id"
|
|
:globalFilterFields="['order_no', 'user_id']" stripedRows removableSort class="p-datatable-sm"
|
|
responsiveLayout="scroll">
|
|
|
|
<template #empty>
|
|
<div class="text-center p-4">未找到订单。</div>
|
|
</template>
|
|
|
|
<template #loading>
|
|
<div class="flex flex-col items-center justify-center p-4">
|
|
<ProgressSpinner style="width:50px;height:50px" />
|
|
<span class="mt-2">加载订单数据...</span>
|
|
</div>
|
|
</template>
|
|
|
|
<Column field="id" header="ID" sortable></Column>
|
|
<Column field="order_no" header="订单号" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex flex-col">
|
|
<span class="text-gray-700">系统订单号: {{ data.order_no }}</span>
|
|
<span class="text-gray-500">商户订单号: {{ data.transaction_id || '-' }}</span>
|
|
<span class="text-orange-500">退款订单号: {{ data.refund_transaction_id || '-' }}</span>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
<Column field="status" header="状态" sortable>
|
|
<template #body="{ data }">
|
|
<Badge :value="orderStatusMap[data.status]?.label"
|
|
:severity="orderStatusMap[data.status]?.severity" />
|
|
</template>
|
|
</Column>
|
|
<Column field="user_id" header="用户ID" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex flex-col">
|
|
<span class="text-gray-700">用户名: {{ data.username }}</span>
|
|
<span class="text-gray-500">ID: {{ data.user_id || '-' }}</span>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
<Column field="post_id" header="文章ID" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex flex-col">
|
|
<span class="text-gray-700"> 标题: {{ data.post_title }}</span>
|
|
<span class="text-gray-500">ID: {{ data.post_id || '-' }}</span>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
<Column field="price" header="价格信息" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex flex-col">
|
|
<span class="text-gray-500">原价: ¥{{ formatPrice(data.price) }}</span>
|
|
<span class="text-orange-500">优惠: -¥{{ formatPrice(getDiscountAmount(data.price,
|
|
data.discount)) }}</span>
|
|
<span class="font-bold">实付: ¥{{ formatPrice(getFinalPrice(data.price, data.discount))
|
|
}}</span>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="updated_at" header="时间信息" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex flex-col">
|
|
<span class="text-gray-500">更新: {{ formatDate(data.updated_at) }}</span>
|
|
<span class="text-gray-400">创建: {{ formatDate(data.created_at) }}</span>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
</div>
|
|
</div>
|
|
</template>
|