霖雨寺

This commit is contained in:
wenfp
2026-07-27 09:57:35 +08:00
commit 6e531cf7af
57 changed files with 7529 additions and 0 deletions
@@ -0,0 +1,169 @@
<script setup lang="ts">
import { DeleteOutlined, EditOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons-vue';
import { Modal, message } from 'ant-design-vue';
import type { ColumnsType } from 'ant-design-vue/es/table';
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import {
deleteProduct,
listProducts,
type Product,
type ProductStatus,
} from '@/api/products';
const router = useRouter();
const products = ref<Product[]>([]);
const loading = ref(false);
const columns: ColumnsType<Product> = [
{ title: '商品名称', dataIndex: 'name', width: 180 },
{ title: '简称', dataIndex: 'alias', width: 120 },
{ title: '销量', dataIndex: 'sales_count', width: 100 },
{ title: '首页展示', dataIndex: 'show_on_home', width: 110 },
{ title: '上架时间', dataIndex: 'listed_at', width: 180 },
{ title: '开售时间', dataIndex: 'sale_starts_at', width: 180 },
{ title: '商品状态', dataIndex: 'status', width: 120 },
{ title: '操作', key: 'actions', width: 140, fixed: 'right' },
];
const statusLabels: Record<ProductStatus, string> = {
delisted: '已下架',
ended: '已结束',
inactive: '未启用',
pending_list: '待上架',
pending_sale: '待开售',
selling: '销售中',
};
const statusColors: Record<ProductStatus, string> = {
delisted: 'default',
ended: 'default',
inactive: 'default',
pending_list: 'orange',
pending_sale: 'blue',
selling: 'green',
};
function getStatusLabel(status: ProductStatus) {
return statusLabels[status] || status;
}
function getStatusColor(status: ProductStatus) {
return statusColors[status] || 'default';
}
function formatDate(value: string | null) {
if (!value) {
return '-';
}
const date = new Date(value);
return Number.isNaN(date.getTime()) ? value : date.toLocaleString('zh-CN', { hour12: false });
}
async function fetchProducts() {
loading.value = true;
try {
products.value = await listProducts();
} catch (error) {
message.error(error instanceof Error ? error.message : '加载商品失败,请先选择寺院');
} finally {
loading.value = false;
}
}
function openCreatePage() {
router.push('/products/create');
}
function openEditPage(product: Product) {
router.push(`/products/${product.id}/edit`);
}
function confirmDelete(product: Product) {
Modal.confirm({
title: '删除商品',
content: `确认删除商品 ${product.name}`,
okText: '删除',
okType: 'danger',
cancelText: '取消',
async onOk() {
await deleteProduct(product.id);
message.success('商品已删除');
await fetchProducts();
},
});
}
onMounted(fetchProducts);
</script>
<template>
<section class="workspace">
<div class="page-heading">
<h1>商品管理</h1>
<a-space>
<a-button :loading="loading" @click="fetchProducts">
<template #icon><ReloadOutlined /></template>
刷新
</a-button>
<a-button type="primary" @click="openCreatePage">
<template #icon><PlusOutlined /></template>
新建商品
</a-button>
</a-space>
</div>
<a-table
:columns="columns"
:data-source="products"
:loading="loading"
:pagination="{ pageSize: 8, showSizeChanger: false }"
:scroll="{ x: 1120 }"
bordered
row-key="id"
size="middle"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'alias'">
{{ record.alias || '-' }}
</template>
<template v-if="column.dataIndex === 'show_on_home'">
<a-tag :color="record.show_on_home ? 'green' : 'default'">
{{ record.show_on_home ? '展示' : '不展示' }}
</a-tag>
</template>
<template v-if="column.dataIndex === 'listed_at'">
{{ formatDate(record.listed_at) }}
</template>
<template v-if="column.dataIndex === 'sale_starts_at'">
{{ formatDate(record.sale_starts_at) }}
</template>
<template v-if="column.dataIndex === 'status'">
<a-tag :color="getStatusColor(record.status)">
{{ getStatusLabel(record.status) }}
</a-tag>
</template>
<template v-if="column.key === 'actions'">
<a-space>
<a-tooltip title="编辑">
<a-button type="text" @click="openEditPage(record)">
<template #icon><EditOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip title="删除">
<a-button type="text" danger @click="confirmDelete(record)">
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
</a-table>
</section>
</template>