"use client"; import { PlusOutlined } from "@ant-design/icons"; import { List } from "@refinedev/antd"; import { Alert, App, Button, Card, Form, Input, InputNumber, Modal, Row, Col, Space, Switch, Table, Typography, type TableColumnsType } from "antd"; import { useCallback, useEffect, useState } from "react"; const { Text } = Typography; type PaymentPackage = { id: string; name: string; description: string; priceCents: number; credits: number; sortOrder: number; enabled: boolean; }; type PackageFormValues = Omit & { priceYuan: number }; function formatMoney(cents: number) { return `¥${(cents / 100).toFixed(2)}`; } async function responsePayload(response: Response) { const payload = await response.json().catch(() => ({})); if (!response.ok) throw new Error(payload.error || "请求失败"); return payload; } export function PackageManagement() { const { message } = App.useApp(); const [packageForm] = Form.useForm(); const [packages, setPackages] = useState([]); const [packagesLoading, setPackagesLoading] = useState(true); const [packagesError, setPackagesError] = useState(""); const [modalOpen, setModalOpen] = useState(false); const [editingPackage, setEditingPackage] = useState(null); const [saving, setSaving] = useState(false); const [disablingId, setDisablingId] = useState(null); const loadPackages = useCallback(async () => { setPackagesLoading(true); setPackagesError(""); try { const payload = await responsePayload(await fetch("/api/admin/packages", { cache: "no-store" })); setPackages(payload.packages); } catch (error) { setPackagesError(error instanceof Error ? error.message : "读取套餐失败"); } finally { setPackagesLoading(false); } }, []); useEffect(() => { const timer = window.setTimeout(() => void loadPackages(), 0); return () => window.clearTimeout(timer); }, [loadPackages]); function openCreateModal() { setEditingPackage(null); packageForm.setFieldsValue({ name: "", description: "", priceYuan: 1, credits: 10, sortOrder: 0, enabled: true }); setModalOpen(true); } function openEditModal(item: PaymentPackage) { setEditingPackage(item); packageForm.setFieldsValue({ name: item.name, description: item.description, priceYuan: item.priceCents / 100, credits: item.credits, sortOrder: item.sortOrder, enabled: item.enabled, }); setModalOpen(true); } function closeModal() { if (saving) return; setModalOpen(false); setEditingPackage(null); packageForm.resetFields(); } async function savePackage(values: PackageFormValues) { setSaving(true); try { await responsePayload(await fetch("/api/admin/packages", { method: editingPackage ? "PATCH" : "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...(editingPackage ? { id: editingPackage.id } : {}), name: values.name.trim(), description: values.description?.trim() ?? "", priceCents: Math.round(values.priceYuan * 100), credits: values.credits, sortOrder: values.sortOrder, enabled: values.enabled, }), })); message.success(editingPackage ? "套餐已更新" : "套餐已添加"); setModalOpen(false); setEditingPackage(null); packageForm.resetFields(); await loadPackages(); } catch (error) { message.error(error instanceof Error ? error.message : "保存套餐失败"); } finally { setSaving(false); } } async function disablePackage(id: string) { setDisablingId(id); try { await responsePayload(await fetch("/api/admin/packages", { method: "DELETE", headers: { "content-type": "application/json" }, body: JSON.stringify({ id }), })); message.success("套餐已停用"); await loadPackages(); } catch (error) { message.error(error instanceof Error ? error.message : "停用套餐失败"); } finally { setDisablingId(null); } } const columns: TableColumnsType = [ { title: "名称", dataIndex: "name", render: (_, item) => {item.name}{item.description || "暂无描述"} }, { title: "价格", dataIndex: "priceCents", align: "right", render: formatMoney }, { title: "点数", dataIndex: "credits", align: "right" }, { title: "排序", dataIndex: "sortOrder", align: "right" }, { title: "状态", dataIndex: "enabled", render: (enabled) => {enabled ? "启用" : "停用"} }, { title: "操作", key: "actions", fixed: "right", render: (_, item) => {item.enabled && } }, ]; return ( } onClick={openCreateModal}>添加套餐}> {packagesError && void loadPackages()}>重试} />} rowKey="id" columns={columns} dataSource={packages} loading={packagesLoading} pagination={false} scroll={{ x: "max-content" }} /> packageForm.submit()} onCancel={closeModal} destroyOnHidden afterClose={() => packageForm.resetFields()} maskClosable={!saving} keyboard={!saving}> form={packageForm} layout="vertical" onFinish={savePackage} requiredMark="optional" initialValues={{ priceYuan: 1, credits: 10, sortOrder: 0, enabled: true }}> ); }