import { useEffect, useMemo, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import PageHeader from '@dukang/shared-ui/PageHeader'; import PullToRefresh from '@dukang/shared-ui/PullToRefresh'; import type { PartnerBillDto } from '@dukang/shared-types'; import { isLoggedIn, request } from '../lib/api'; import { toastError, toastSuccess } from '../lib/toast'; import { usePartnerPageView } from '../lib/usePageView'; function fmtMoney(n: number) { return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function billStatusLabel(status: string) { switch (status) { case 'AWAITING_CONFIRM': return '待确认'; case 'UNPAID': return '未打款'; case 'PAID': return '已打款'; case 'REJECTED': return '已驳回'; default: return status; } } function canConfirm(status: string) { return status === 'AWAITING_CONFIRM'; } export default function BillsPage() { usePartnerPageView('partner_bills_view'); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [bills, setBills] = useState([]); const [confirmed, setConfirmed] = useState(false); const [submitting, setSubmitting] = useState(false); const [loading, setLoading] = useState(true); const [selectedIds, setSelectedIds] = useState([]); async function loadBills() { setLoading(true); try { const list = await request('PARTNER_H5', '/partner/settlement/bills'); setBills(Array.isArray(list) ? list : []); } catch { setBills([]); } finally { setLoading(false); } } useEffect(() => { if (!isLoggedIn()) { navigate('/login'); return; } void loadBills(); }, [navigate]); const billId = searchParams.get('id'); const actionable = useMemo( () => bills.filter((b) => canConfirm(b.status)), [bills], ); const bill = billId ? bills.find((b) => String(b.id) === billId) ?? actionable[0] ?? bills[0] : actionable[0] ?? bills[0]; const status = String(bill?.status || ''); const showApplyForm = !!bill && canConfirm(status); const isUnpaid = status === 'UNPAID'; const isRejected = status === 'REJECTED'; const isPaid = status === 'PAID'; function askConfirm(ids: string[]) { if (!window.confirm(`确认 ${ids.length} 笔账单无误并提交?确认后状态将变为「未打款」,等待总部打款。`)) { return; } void doConfirm(ids); } async function doConfirm(ids: string[]) { setSubmitting(true); try { if (ids.length === 1) { await request('PARTNER_H5', `/partner/settlement/bills/${ids[0]}/confirm`, { method: 'POST' }); } else { await request('PARTNER_H5', '/partner/settlement/bills/batch-confirm', { method: 'POST', body: JSON.stringify({ ids }), }); } toastSuccess('已确认,等待总部打款'); setConfirmed(false); setSelectedIds([]); await loadBills(); } catch (e) { toastError(e instanceof Error ? e.message : '提交失败'); } finally { setSubmitting(false); } } function toggleSelect(id: string) { setSelectedIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id])); } return ( navigate('/center/settlement')} />
check
数据核算
{showApplyForm && !confirmed ? '2' : ( check )}
账单确认
{isPaid ? ( check ) : '3'}
{isRejected ? '已驳回' : isPaid ? '已打款' : '总部打款'}
{loading &&
加载中…
} {!loading && !bill &&
暂无待确认账单
} {bill && (

SETTLEMENT PERIOD

{String(bill.billNo || '月度结算账单')}

{billStatusLabel(status)}
{isRejected && bill.rejectReason && (
error

账单已驳回

{bill.rejectReason}

)} {isUnpaid && (
hourglass_top

您已确认账单,总部打款中,请耐心等待。

)}

应结总金额

¥ {fmtMoney(Number(bill.totalAmount || 0))}
订单分佣收入 ¥ {fmtMoney(Number(bill.orderCommission || 0))}
核销权益分佣 ¥ {fmtMoney(Number(bill.redeemCommission || 0))}
)} {bills.length > 0 && (

全部账单

{actionable.length > 1 && ( )}
{bills.map((b) => { const id = String(b.id); const selectable = canConfirm(b.status); return (
{selectable ? ( toggleSelect(id)} style={{ marginTop: 4 }} /> ) : ( )}
); })}
)} {showApplyForm && ( <>
info

确认后账单变为「未打款」,由总部完成打款。如有异议请先联系城市运营经理。

)} ); }