import { useEffect, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; import { Alert, Button, Form, Image, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, message, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { ACTIVITY_POSTER_STATUS_LABELS, DEFAULT_ACTIVITY_POSTER_QR_SLOT, type ActivityPosterItem, type ActivityPosterQrSlot, type ActivityPosterStatus, } from '@dukang/shared-types'; import { request, requestDownload } from '../lib/api'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; import OssUpload from '../components/OssUpload'; import ActivityPosterQrSlotEditor from '../components/ActivityPosterQrSlotEditor'; type FormValues = { title: string; copyText?: string; imageUrl: string; qrXPct: number; qrYPct: number; qrSizePct: number; sortOrder?: number; status?: ActivityPosterStatus; }; type PartnerHint = { id: string; companyName?: string | null; name?: string; phone?: string; }; export default function ActivityPostersPage() { const [searchParams] = useSearchParams(); const partnerId = searchParams.get('partnerId')?.trim() || ''; const [form] = Form.useForm(); const [editForm] = Form.useForm(); const [filters, setFilters] = useState>({}); const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList( '/admin/activity-posters', () => { const qs = new URLSearchParams(); if (filters.status) qs.set('status', filters.status); return qs; }, [filters], ); const [modalOpen, setModalOpen] = useState(false); const [editing, setEditing] = useState(null); const [partnerHint, setPartnerHint] = useState(null); const [downloadingId, setDownloadingId] = useState(null); const imageUrl = Form.useWatch('imageUrl', editForm); const qrXPct = Form.useWatch('qrXPct', editForm); const qrYPct = Form.useWatch('qrYPct', editForm); const qrSizePct = Form.useWatch('qrSizePct', editForm); const slot: ActivityPosterQrSlot = { qrXPct: qrXPct ?? DEFAULT_ACTIVITY_POSTER_QR_SLOT.qrXPct, qrYPct: qrYPct ?? DEFAULT_ACTIVITY_POSTER_QR_SLOT.qrYPct, qrSizePct: qrSizePct ?? DEFAULT_ACTIVITY_POSTER_QR_SLOT.qrSizePct, }; useEffect(() => { if (!partnerId) { setPartnerHint(null); return; } void request(`/admin/partners/${partnerId}`) .then(setPartnerHint) .catch(() => setPartnerHint({ id: partnerId })); }, [partnerId]); const partnerLabel = partnerHint ? (partnerHint.companyName || partnerHint.name || partnerHint.phone || partnerHint.id) : partnerId; function openCreate() { setEditing(null); editForm.setFieldsValue({ title: '', copyText: '', imageUrl: '', ...DEFAULT_ACTIVITY_POSTER_QR_SLOT, sortOrder: 0, status: 'ACTIVE', }); setModalOpen(true); } function openEdit(row: ActivityPosterItem) { setEditing(row); editForm.setFieldsValue({ title: row.title, copyText: row.copyText, imageUrl: row.imageUrl, qrXPct: row.qrXPct, qrYPct: row.qrYPct, qrSizePct: row.qrSizePct, sortOrder: row.sortOrder, status: row.status, }); setModalOpen(true); } async function save() { const v = await editForm.validateFields(); const body = JSON.stringify(v); if (editing) { await request(`/admin/activity-posters/${editing.id}`, { method: 'PUT', body }); message.success('已保存'); } else { await request('/admin/activity-posters', { method: 'POST', body }); message.success('已创建'); } setModalOpen(false); void reload(); } async function downloadForPartner(posterId: string) { if (!partnerId) return; setDownloadingId(posterId); try { await requestDownload( `/admin/activity-posters/${posterId}/image?partnerId=${encodeURIComponent(partnerId)}`, {}, 'activity-poster.png', ); message.success('已开始下载'); } catch (e) { message.error(e instanceof Error ? e.message : '下载失败'); } finally { setDownloadingId(null); } } const baseColumns: ColumnsType = [ { title: '标题', dataIndex: 'title', width: 180, render: (v, row) => openEdit(row)}>{v}, }, { title: '封面', dataIndex: 'imageUrl', width: 90, render: (url: string) => , }, { title: '文案', dataIndex: 'copyText', width: 240, render: (v: string) => v || '—', }, { title: '排序', dataIndex: 'sortOrder', width: 70 }, { title: '状态', dataIndex: 'status', width: 80, render: (s: ActivityPosterStatus) => ( {ACTIVITY_POSTER_STATUS_LABELS[s] || s} ), }, { title: '更新', dataIndex: 'updatedAt', width: 160, render: fmtTime }, { title: '操作', width: partnerId ? 280 : 200, render: (_, row) => ( {partnerId && row.status === 'ACTIVE' ? ( ) : null} { await request(`/admin/activity-posters/${row.id}`, { method: 'DELETE' }); message.success('已删除'); void reload(); }} > ), }, ]; const { columns, settingsButton, settingsModal } = useAdminListColumns('activity-posters', baseColumns, { page, pageSize, }); return (
{settingsModal} 新建活动图} /> {partnerId ? ( 正在为合伙人「{partnerLabel}」下载活动图。点行内「为该合伙人下载」,或返回{' '} 城市合伙人。 } /> ) : null}
{ setFilters(v); setPage(1); }}> editForm.setFieldsValue(next)} />