Add recovery Excel export and automatic account detection
This commit is contained in:
@@ -8,7 +8,10 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { formatShanghaiDate as formatDate } from "../lib/date-utils";
|
||||
import {
|
||||
formatShanghaiDate as formatDate,
|
||||
formatShanghaiToday,
|
||||
} from "../lib/date-utils";
|
||||
|
||||
type Partner = {
|
||||
id: string;
|
||||
@@ -317,6 +320,7 @@ export default function Home() {
|
||||
const [sourcePreview, setSourcePreview] = useState<FeishuPreview | null>(null);
|
||||
const [sourceWorking, setSourceWorking] = useState(false);
|
||||
const [metricForm, setMetricForm] = useState({ exposure: "", views: "" });
|
||||
const [exportingTaskId, setExportingTaskId] = useState<string | null>(null);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
@@ -502,6 +506,37 @@ export default function Home() {
|
||||
);
|
||||
};
|
||||
|
||||
const exportRecoveryData = async (task: Task) => {
|
||||
try {
|
||||
setExportingTaskId(task.id);
|
||||
const response = await fetch(
|
||||
`/api/recovery-export?task=${encodeURIComponent(task.id)}`,
|
||||
{ cache: "no-store" },
|
||||
);
|
||||
if (!response.ok) {
|
||||
const result = await readApiResponse<{ error?: string }>(
|
||||
response,
|
||||
"导出失败",
|
||||
);
|
||||
throw new Error(result.error || "导出失败");
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = objectUrl;
|
||||
anchor.download = `${task.name}-数据回收.xlsx`;
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
anchor.remove();
|
||||
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
|
||||
setToast("Excel 已导出,图片和截图已嵌入表格");
|
||||
} catch (reason) {
|
||||
setToast(reason instanceof Error ? reason.message : "导出失败");
|
||||
} finally {
|
||||
setExportingTaskId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const uploadScreenshot = async (
|
||||
distribution: Distribution,
|
||||
event: ChangeEvent<HTMLInputElement>,
|
||||
@@ -654,7 +689,7 @@ export default function Home() {
|
||||
{activeNav === "overview" && (
|
||||
<div className="date-chip">
|
||||
<span>今天</span>
|
||||
<strong>7月27日</strong>
|
||||
<strong>{formatShanghaiToday()}</strong>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -710,6 +745,8 @@ export default function Home() {
|
||||
onUpload={uploadScreenshot}
|
||||
onFallback={openMetricFallback}
|
||||
onRetryFailed={retryFailedMetrics}
|
||||
onExport={exportRecoveryData}
|
||||
exportingTaskId={exportingTaskId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -1373,6 +1410,8 @@ function RecoveryPage({
|
||||
onUpload,
|
||||
onFallback,
|
||||
onRetryFailed,
|
||||
onExport,
|
||||
exportingTaskId,
|
||||
}: {
|
||||
tasks: Task[];
|
||||
distributions: Distribution[];
|
||||
@@ -1386,6 +1425,8 @@ function RecoveryPage({
|
||||
onUpload: (distribution: Distribution, event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onFallback: (distribution: Distribution) => void;
|
||||
onRetryFailed: (taskId: string, failedCount: number) => void;
|
||||
onExport: (task: Task) => void;
|
||||
exportingTaskId: string | null;
|
||||
}) {
|
||||
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null);
|
||||
const selectedTask = tasks.find((task) => task.id === selectedTaskId);
|
||||
@@ -1431,16 +1472,27 @@ function RecoveryPage({
|
||||
<h2>数据回收队列</h2>
|
||||
<p>{taskDistributions.length} 篇已发布笔记 · 展示最近一次采集结果</p>
|
||||
</div>
|
||||
<button
|
||||
className="retry-failed-button"
|
||||
disabled={working || failedCollectionCount === 0}
|
||||
onClick={() =>
|
||||
onRetryFailed(selectedTask.id, failedCollectionCount)
|
||||
}
|
||||
>
|
||||
一键补采异常数据
|
||||
{failedCollectionCount > 0 && <b>{failedCollectionCount}</b>}
|
||||
</button>
|
||||
<div className="recovery-panel-actions">
|
||||
<button
|
||||
className="export-data-button"
|
||||
disabled={working || exportingTaskId === selectedTask.id}
|
||||
onClick={() => onExport(selectedTask)}
|
||||
>
|
||||
{exportingTaskId === selectedTask.id
|
||||
? "正在生成 Excel…"
|
||||
: "导出全部数据"}
|
||||
</button>
|
||||
<button
|
||||
className="retry-failed-button"
|
||||
disabled={working || failedCollectionCount === 0}
|
||||
onClick={() =>
|
||||
onRetryFailed(selectedTask.id, failedCollectionCount)
|
||||
}
|
||||
>
|
||||
一键补采异常数据
|
||||
{failedCollectionCount > 0 && <b>{failedCollectionCount}</b>}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-scroll">
|
||||
<table className="data-table recovery-table">
|
||||
|
||||
Reference in New Issue
Block a user