66 lines
2.5 KiB
Vue
66 lines
2.5 KiB
Vue
<script setup lang="ts">
|
|
import type { Invoice } from "@vmall/shared";
|
|
|
|
const { $api } = useNuxtApp();
|
|
const { locale } = useI18n();
|
|
const { formatAmount } = usePrice();
|
|
const invoices = ref<Invoice[]>([]);
|
|
const amounts = ref<Record<string, string>>({});
|
|
const loading = ref(true);
|
|
const error = ref("");
|
|
|
|
function errorMessage(value: unknown): string {
|
|
return value instanceof Error ? value.message : $t("mall.loadFailed");
|
|
}
|
|
|
|
async function loadInvoices(): Promise<void> {
|
|
loading.value = true;
|
|
error.value = "";
|
|
try {
|
|
invoices.value = await $api.listMyInvoices();
|
|
const entries = await Promise.all(invoices.value.map(async (invoice) => [invoice.id, await formatAmount(invoice.amount_minor, invoice.currency)] as const));
|
|
amounts.value = Object.fromEntries(entries);
|
|
} catch (value) {
|
|
error.value = errorMessage(value);
|
|
invoices.value = [];
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
watch(locale, () => {
|
|
if (invoices.value.length > 0) void loadInvoices();
|
|
});
|
|
onMounted(() => void loadInvoices());
|
|
|
|
definePageMeta({ middleware: "auth" });
|
|
</script>
|
|
|
|
<template>
|
|
<section>
|
|
<h1 class="page-title">{{ $t("invoice.title") }}</h1>
|
|
<p v-if="loading" class="muted">{{ $t("common.loading") }}</p>
|
|
<p v-if="error" class="error-text" role="alert">{{ error }}</p>
|
|
<div v-if="!loading && invoices.length === 0" class="card empty-state">{{ $t("common.empty") }}</div>
|
|
<table v-if="invoices.length > 0" class="table">
|
|
<thead><tr><th>{{ $t("invoice.invoiceNo") }}</th><th>{{ $t("order.orderNo") }}</th><th>{{ $t("invoice.invoiceTitle") }}</th><th>{{ $t("invoice.kind") }}</th><th>{{ $t("invoice.amount") }}</th><th>{{ $t("common.status") }}</th><th>{{ $t("invoice.status.issued") }}</th></tr></thead>
|
|
<tbody>
|
|
<tr v-for="item in invoices" :key="item.id">
|
|
<td>{{ item.invoice_no || $t("mall.notAvailable") }}</td>
|
|
<td>{{ item.order_no || $t("mall.notAvailable") }}</td>
|
|
<td>{{ item.title }}</td>
|
|
<td>{{ $t(`invoice.${item.kind}`) }}</td>
|
|
<td>{{ amounts[item.id] || $t("common.loading") }}</td>
|
|
<td><StatusBadge :status="item.status" kind="invoice" /></td>
|
|
<td>{{ item.issued_at ? new Date(item.issued_at).toLocaleString(locale === "zh" ? "zh-CN" : "en-US") : $t("mall.notAvailable") }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.empty-state { text-align: center; }
|
|
@media (max-width: 760px) { .table { display: block; overflow-x: auto; white-space: nowrap; } }
|
|
</style>
|