feat: show fission records in history
This commit is contained in:
@@ -18,6 +18,7 @@ const STATUS_LABEL: Record<string, { text: string; color: string }> = {
|
||||
archived: { text: '已归档', color: 'text-gray-500 bg-gray-100' },
|
||||
rejected: { text: '被打回', color: 'text-red-500 bg-red-50' },
|
||||
failed: { text: '生成失败', color: 'text-red-600 bg-red-50' },
|
||||
fission: { text: '裂变', color: 'text-orange-600 bg-orange-50' },
|
||||
};
|
||||
|
||||
// --- DownloadButton --- 触发打包→轮询→下载 zip
|
||||
@@ -73,6 +74,40 @@ function DownloadButton({ taskId }: { taskId: number }) {
|
||||
);
|
||||
}
|
||||
|
||||
function FissionDownloadButton({ fissionId }: { fissionId: number }) {
|
||||
const [state, setState] = useState<'idle' | 'downloading' | 'done' | 'error'>('idle');
|
||||
|
||||
async function handleDownload() {
|
||||
setState('downloading');
|
||||
try {
|
||||
const blob = await api.getBlob(`/api/v1/fission/${fissionId}/download`);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `fission-${fissionId}.zip`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
setState('done');
|
||||
} catch {
|
||||
setState('error');
|
||||
}
|
||||
}
|
||||
|
||||
if (state === 'done') return <span className="text-xs text-green-600">✓ 已下载</span>;
|
||||
if (state === 'error') return <span className="text-xs text-red-500">失败,刷新重试</span>;
|
||||
return (
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
disabled={state === 'downloading'}
|
||||
className="text-xs text-brand-orange hover:underline disabled:text-text-tertiary"
|
||||
>
|
||||
{state === 'downloading' ? '下载中…' : '下载裂变包'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// --- HistoryTable ---
|
||||
export function HistoryTable({ tasks }: { tasks: TaskListItem[] }) {
|
||||
return (
|
||||
@@ -120,12 +155,12 @@ export function HistoryTable({ tasks }: { tasks: TaskListItem[] }) {
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Link
|
||||
href={`/tasks/${task.id}/confirm`}
|
||||
href={task.item_type === 'fission' ? `/fission/${task.id}/notes` : `/tasks/${task.id}/confirm`}
|
||||
className="text-brand-orange hover:underline text-xs"
|
||||
>
|
||||
查看
|
||||
</Link>
|
||||
{task.status !== 'failed' && (
|
||||
{task.item_type !== 'fission' && task.status !== 'failed' && (
|
||||
<Link
|
||||
href={`/tasks/${task.id}/images`}
|
||||
className="text-brand-orange hover:underline text-xs"
|
||||
@@ -134,6 +169,7 @@ export function HistoryTable({ tasks }: { tasks: TaskListItem[] }) {
|
||||
</Link>
|
||||
)}
|
||||
{task.status === 'approved' && <DownloadButton taskId={task.id} />}
|
||||
{task.item_type === 'fission' && <FissionDownloadButton fissionId={task.id} />}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -13,9 +13,9 @@ import { clsx } from 'clsx';
|
||||
import { HistoryTable, HistorySkeleton, Pagination } from './components';
|
||||
import { HistoryFilters } from './HistoryFilters';
|
||||
|
||||
const HISTORY_STATUSES = ['approved', 'archived', 'rejected', 'failed'];
|
||||
const HISTORY_STATUSES = ['approved', 'archived', 'rejected', 'failed', 'fission'];
|
||||
const STATUS_LABELS: Record<string, string> = {
|
||||
approved: '已通过', archived: '已归档', rejected: '被打回', failed: '生成失败',
|
||||
approved: '已通过', archived: '已归档', rejected: '被打回', failed: '生成失败', fission: '裂变',
|
||||
};
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const STATUS_LABELS: Record<TaskStatus, { label: string; color: string }> = {
|
||||
rejected: { label: '已打回', color: 'text-status-error' },
|
||||
archived: { label: '已归档', color: 'text-text-tertiary' },
|
||||
failed: { label: '生成失败', color: 'text-status-error' },
|
||||
fission: { label: '裂变', color: 'text-brand-orange' },
|
||||
};
|
||||
|
||||
export function RecentTasksBar() {
|
||||
@@ -47,7 +48,9 @@ export function RecentTasksBar() {
|
||||
{recentTasks.slice(0, 4).map((task) => {
|
||||
const statusInfo = STATUS_LABELS[task.status];
|
||||
return (
|
||||
<Link key={task.id} href={`/tasks/${task.id}/text`}
|
||||
<Link
|
||||
key={task.id}
|
||||
href={task.item_type === 'fission' ? `/fission/${task.id}/notes` : `/tasks/${task.id}/text`}
|
||||
aria-label={`任务:${task.theme},状态:${statusInfo.label}`}
|
||||
className="flex-1 border border-border-default rounded-xl p-3 bg-surface-primary hover:border-brand-orange transition-colors">
|
||||
<p className="text-sm font-medium text-text-primary line-clamp-1">{task.theme}</p>
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
// 任务
|
||||
export type TaskStatus =
|
||||
| 'pending' | 'generating' | 'pending_selection'
|
||||
| 'pending_review' | 'approved' | 'rejected' | 'archived' | 'failed';
|
||||
| 'pending_review' | 'approved' | 'rejected' | 'archived' | 'failed' | 'fission';
|
||||
|
||||
export interface TaskListItem {
|
||||
id: number;
|
||||
item_type?: 'task' | 'fission';
|
||||
product_id: number;
|
||||
product_name?: string | null; // M4: 历史页"产品"列,list_tasks 批量 JOIN 填充
|
||||
theme: string;
|
||||
|
||||
Reference in New Issue
Block a user