feat: polish reporting templates and shared controls
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { Download, Search } from 'lucide-react';
|
||||
import { adminApi, type AdminChannel, type DailyProfitReport, type EnterpriseApplication, type TenantOption } from '@/api/adminApi';
|
||||
import { Breadcrumb, Button, DateRangeInput, Pagination, Select, Tag, type DateRangeValue } from '@/components/ui';
|
||||
import { formatCents } from '@/utils/currency';
|
||||
@@ -21,6 +21,7 @@ export function AdminProfitReportsPage() {
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const [exporting, setExporting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([adminApi.listTenants(), adminApi.listEnterpriseApplications(), adminApi.listChannels()])
|
||||
@@ -45,6 +46,13 @@ export function AdminProfitReportsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function exportData() {
|
||||
setExporting(true); setError('');
|
||||
try { downloadBlob(await adminApi.exportProfitReports({ dateFrom: dateRange.start, dateTo: dateRange.end, dimensionType, tenantId: tenantId || undefined, applicationId: applicationId || undefined, channelId: channelId || undefined }), '利润报表.csv'); }
|
||||
catch (failure) { setError(failure instanceof Error ? failure.message : '利润报表导出失败'); }
|
||||
finally { setExporting(false); }
|
||||
}
|
||||
|
||||
const availableApplications = useMemo(() => applications.filter((application) => !tenantId || application.tenantId === tenantId), [applications, tenantId]);
|
||||
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
||||
|
||||
@@ -52,7 +60,7 @@ export function AdminProfitReportsPage() {
|
||||
<section className="page-stack">
|
||||
<div className="page-heading">
|
||||
<div><Breadcrumb items={['报表对账', '利润报表']} /><h1>利润报表</h1></div>
|
||||
<Tag tone="info">T+1 生成 · 每日重算 T-4~T-1</Tag>
|
||||
<div className="page-actions"><Button disabled={exporting} icon={<Download size={16} />} onClick={() => void exportData()} size="sm" variant="secondary">{exporting ? '导出中...' : '导出报表'}</Button><Tag tone="info">T+1 生成 · 每日重算 T-4~T-1</Tag></div>
|
||||
</div>
|
||||
|
||||
<div className="surface" style={{ display: 'grid', gap: 16, gridTemplateColumns: 'minmax(270px, 1.3fr) minmax(180px, .8fr) minmax(210px, 1fr) minmax(220px, 1fr) auto', padding: 20, alignItems: 'end' }}>
|
||||
@@ -92,3 +100,5 @@ function defaultDateRange(): DateRangeValue {
|
||||
function localDate(value: Date) {
|
||||
return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function downloadBlob(blob: Blob, fileName: string) { const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = fileName; anchor.click(); URL.revokeObjectURL(url); }
|
||||
|
||||
Reference in New Issue
Block a user