import { useEffect, useRef, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { Button, Checkbox, Drawer, Form, Input, Select, Table, Tag, Typography } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { REDEEM_CHANNEL_LABELS, type RedeemChannel } from '@dukang/shared-types'; import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions'; import { request } 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'; type Row = { id: string; redeemNo: string; amount: number; settleAmount: number; channel?: RedeemChannel; createdAt: string; isTest?: boolean; user?: { id?: string; userNo: string; phone: string | null; nickname?: string | null; hqRemark?: string | null; }; store?: { name: string; cityName: string }; coupon?: { id?: string; couponNo: string }; }; function formatNicknameWithRemark(user?: { nickname?: string | null; hqRemark?: string | null; } | null) { const name = user?.nickname?.trim() || '—'; const remark = user?.hqRemark?.trim(); return remark ? `${name}(${remark})` : name; } export default function RedeemRecordsPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const initialRedeemNo = searchParams.get('redeemNo')?.trim() || ''; const [form] = Form.useForm(); const [filters, setFilters] = useState>(() => { const init: Record = {}; if (initialRedeemNo) init.redeemNo = initialRedeemNo; return init; }); const { data, loading, page, pageSize, setPage, setPageSize } = useAdminList( '/admin/redeem-records', () => { const qs = new URLSearchParams(); if (filters.redeemNo) qs.set('redeemNo', String(filters.redeemNo)); if (filters.storeId) qs.set('storeId', String(filters.storeId)); if (filters.channel) qs.set('channel', String(filters.channel)); if (filters.excludeTest) qs.set('excludeTest', 'true'); return qs; }, [filters], ); const [detail, setDetail] = useState | null>(null); const [drawerOpen, setDrawerOpen] = useState(false); const [detailLoading, setDetailLoading] = useState(false); const deepLinkOpenedRef = useRef(false); useEffect(() => { if (initialRedeemNo) form.setFieldsValue({ redeemNo: initialRedeemNo }); }, [form, initialRedeemNo]); async function openDetail(id: string) { setDetailLoading(true); setDrawerOpen(true); try { setDetail(await request(`/admin/redeem-records/${id}`)); } finally { setDetailLoading(false); } } useEffect(() => { if (!initialRedeemNo || deepLinkOpenedRef.current || loading) return; const first = data?.items?.[0]; if (first && String(first.redeemNo) === initialRedeemNo) { deepLinkOpenedRef.current = true; void openDetail(first.id); } }, [data, initialRedeemNo, loading]); const baseColumns: ColumnsType = [ { title: '核销号', dataIndex: 'redeemNo', width: 200, render: (v, row) => ( void openDetail(row.id)}>{v} {row.isTest ? ( 测试 ) : null} ), }, { title: '方式', dataIndex: 'channel', width: 110, render: (v: RedeemChannel | undefined) => { const channel = v === 'PHONE' ? 'PHONE' : 'SCAN'; return ( {REDEEM_CHANNEL_LABELS[channel]} ); }, }, { title: '用户编号', dataIndex: ['user', 'userNo'], width: 110, render: (v: string | undefined, row) => row.user?.id ? ( navigate('/users', { state: { openUserId: String(row.user!.id) } })} > {v || '—'} ) : ( v || '—' ), }, { title: '用户昵称', dataIndex: ['user', 'nickname'], width: 160, render: (_: string | null | undefined, row) => row.user?.id ? ( navigate('/users', { state: { openUserId: String(row.user!.id) } })} > {formatNicknameWithRemark(row.user)} ) : ( formatNicknameWithRemark(row.user) ), }, { title: '用户手机', dataIndex: ['user', 'phone'], width: 120, render: (v: string | null | undefined) => v || '—', }, { title: '门店', dataIndex: ['store', 'name'] }, { title: '核销额', dataIndex: 'amount', width: 90, render: (v) => `¥${v}` }, { title: '结算额', dataIndex: 'settleAmount', width: 90, render: (v) => `¥${v}` }, { title: '券号', dataIndex: ['coupon', 'couponNo'], width: 160, render: (v: string | undefined, row) => v ? ( navigate(`/benefit/coupons?couponNo=${encodeURIComponent(v)}`, { state: row.coupon?.id ? { openCouponId: String(row.coupon.id) } : undefined, }) } > {v} ) : ( '—' ), }, { title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime }, { title: '操作', width: 80, render: (_, row) => ( ), }, ]; const { columns, settingsButton, settingsModal } = useAdminListColumns('redeem-records', baseColumns, { page, pageSize }); return (
{settingsModal}
{ setFilters(v); setPage(1); }} >