fix
This commit is contained in:
parent
f960a7ef8d
commit
f843b2d894
|
|
@ -7,6 +7,6 @@ export type Body_local_async_change_bg_api_v2_local_batch_change_bg_post = {
|
||||||
/**
|
/**
|
||||||
* 场景标签集合JSON字符串
|
* 场景标签集合JSON字符串
|
||||||
*/
|
*/
|
||||||
scenes_tags: string;
|
scenes_list: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
export type VideoTaskRequest = {
|
export type VideoTaskRequest = {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
img_url: string;
|
img_url: string;
|
||||||
|
task_id: string;
|
||||||
duration?: string;
|
duration?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export const $Body_local_async_change_bg_api_v2_local_batch_change_bg_post = {
|
||||||
},
|
},
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
},
|
},
|
||||||
scenes_tags: {
|
scenes_list: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: `场景标签集合JSON字符串`,
|
description: `场景标签集合JSON字符串`,
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@ export const $VideoTaskRequest = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
},
|
},
|
||||||
|
task_id: {
|
||||||
|
type: 'string',
|
||||||
|
isRequired: true,
|
||||||
|
},
|
||||||
duration: {
|
duration: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -333,4 +333,15 @@ export class Service {
|
||||||
url: '/api/tag/model/tag_list',
|
url: '/api/tag/model/tag_list',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取JM模型的提示词
|
||||||
|
* @returns any Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static fetchJmPromptApiConfigJmPromptGet(): CancelablePromise<any> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/config/jm/prompt',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ function openDB(): Promise<IDBDatabase> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addVideoTasks(tasks: { id: string; img_url: string; prompt: string; create_time: string; task_id: string }[]) {
|
export async function addVideoTasks(tasks: { id: string; img_url: string; prompt: string; create_time: string; job_id: string; task_id: string }[]) {
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const tx = db.transaction(STORE_NAME, 'readwrite');
|
const tx = db.transaction(STORE_NAME, 'readwrite');
|
||||||
|
|
@ -39,7 +39,10 @@ export async function getAllVideoTasks(): Promise<any[]> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateVideoTask(id: string, partial: Partial<{ img_url: string; prompt: string; create_time: string; task_id: string; video_url?: string }>) {
|
export async function updateVideoTask(
|
||||||
|
id: string,
|
||||||
|
partial: Partial<{ img_url: string; prompt: string; create_time: string; job_id: string; task_id: string; video_url?: string }>
|
||||||
|
) {
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const tx = db.transaction(STORE_NAME, 'readwrite');
|
const tx = db.transaction(STORE_NAME, 'readwrite');
|
||||||
|
|
|
||||||
|
|
@ -9,17 +9,25 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { ACTION_PROMPTS } from '@/lib/prompt';
|
import { ACTION_PROMPTS } from '@/lib/prompt';
|
||||||
import { Service } from '@/api/services/Service';
|
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { addVideoTasks } from '@/lib/indexeddb';
|
import { addVideoTasks } from '@/lib/indexeddb';
|
||||||
|
import type { BatchVideoTaskRequest } from '@/api';
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
const promptGetter = (list: string[]) => {
|
||||||
|
let index = 0;
|
||||||
|
return () => {
|
||||||
|
index++;
|
||||||
|
return list[index % list.length];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const TryOnTasksPage: React.FC = () => {
|
const TryOnTasksPage: React.FC = () => {
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [previewImg, setPreviewImg] = useState<string | null>(null);
|
const [previewImg, setPreviewImg] = useState<string | null>(null);
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const [selectedRows, setSelectedRows] = useState<{ id: number; img_url: string }[]>([]);
|
const [selectedRows, setSelectedRows] = useState<{ id: number; img_url: string; task_id: string }[]>([]);
|
||||||
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
||||||
const [selectedPrompts, setSelectedPrompts] = useState<string[]>([]);
|
const [selectedPrompts, setSelectedPrompts] = useState<string[]>([]);
|
||||||
const [creating, setCreating] = useState(false);
|
const [creating, setCreating] = useState(false);
|
||||||
|
|
@ -47,12 +55,12 @@ const TryOnTasksPage: React.FC = () => {
|
||||||
const isAllCurrentPageSelected = currentPageIds.length > 0 && currentPageIds.every(id => isRowSelected(id));
|
const isAllCurrentPageSelected = currentPageIds.length > 0 && currentPageIds.every(id => isRowSelected(id));
|
||||||
|
|
||||||
// 单行选择
|
// 单行选择
|
||||||
const handleRowSelect = (item: { id: number; img_url?: string }) => {
|
const handleRowSelect = (item: { id: number; img_url?: string; task_id?: string }) => {
|
||||||
setSelectedRows(prev => {
|
setSelectedRows(prev => {
|
||||||
if (isRowSelected(item.id)) {
|
if (isRowSelected(item.id)) {
|
||||||
return prev.filter(row => row.id !== item.id);
|
return prev.filter(row => row.id !== item.id);
|
||||||
} else {
|
} else {
|
||||||
return [...prev, { id: item.id, img_url: item.img_url || '' }];
|
return [...prev, { id: item.id, img_url: item.img_url || '', task_id: item.task_id || '' }];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -61,7 +69,9 @@ const TryOnTasksPage: React.FC = () => {
|
||||||
if (isAllCurrentPageSelected) {
|
if (isAllCurrentPageSelected) {
|
||||||
setSelectedRows(prev => prev.filter(row => !currentPageIds.includes(row.id)));
|
setSelectedRows(prev => prev.filter(row => !currentPageIds.includes(row.id)));
|
||||||
} else {
|
} else {
|
||||||
const toAdd = (data?.data || []).filter(item => !isRowSelected(item.id)).map(item => ({ id: item.id, img_url: item.img_url || '' }));
|
const toAdd = (data?.data || [])
|
||||||
|
.filter(item => !isRowSelected(item.id))
|
||||||
|
.map(item => ({ id: item.id, img_url: item.img_url || '', task_id: item.task_id || '' }));
|
||||||
setSelectedRows(prev => [...prev, ...toAdd]);
|
setSelectedRows(prev => [...prev, ...toAdd]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -70,9 +80,14 @@ const TryOnTasksPage: React.FC = () => {
|
||||||
const handleCreateTask = async () => {
|
const handleCreateTask = async () => {
|
||||||
if (!selectedRows.length || !selectedPrompts.length) return;
|
if (!selectedRows.length || !selectedPrompts.length) return;
|
||||||
setCreating(true);
|
setCreating(true);
|
||||||
|
const getPrompt = promptGetter(selectedPrompts);
|
||||||
try {
|
try {
|
||||||
const tasks = selectedRows.flatMap(row => selectedPrompts.map(prompt => ({ prompt, img_url: row.img_url })));
|
const tasks: BatchVideoTaskRequest['tasks'] = selectedRows.map(row => ({
|
||||||
const res = (await Service.submitVideoTaskApiJmSubmitTaskPost({ requestBody: { tasks } })) as {
|
prompt: getPrompt(),
|
||||||
|
img_url: row.img_url,
|
||||||
|
task_id: row.task_id,
|
||||||
|
}));
|
||||||
|
const res = (await api.Service.submitVideoTaskApiJmSubmitTaskPost({ requestBody: { tasks } })) as {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
data: { success: { task_id: string; img_url: string }[]; failed: string[] };
|
data: { success: { task_id: string; img_url: string }[]; failed: string[] };
|
||||||
msg: string;
|
msg: string;
|
||||||
|
|
@ -98,6 +113,7 @@ const TryOnTasksPage: React.FC = () => {
|
||||||
img_url: item.img_url,
|
img_url: item.img_url,
|
||||||
prompt,
|
prompt,
|
||||||
create_time: new Date().toISOString(),
|
create_time: new Date().toISOString(),
|
||||||
|
job_id: item.job_id,
|
||||||
task_id: item.task_id,
|
task_id: item.task_id,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ interface LocalVideoTask {
|
||||||
img_url: string;
|
img_url: string;
|
||||||
prompt: string;
|
prompt: string;
|
||||||
create_time: string;
|
create_time: string;
|
||||||
|
job_id: string;
|
||||||
task_id: string;
|
task_id: string;
|
||||||
video_url?: string;
|
video_url?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +52,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
const newMap = { ...prev };
|
const newMap = { ...prev };
|
||||||
tasks.forEach(t => {
|
tasks.forEach(t => {
|
||||||
if (t.video_url) {
|
if (t.video_url) {
|
||||||
newMap[t.task_id] = { status: 'finished', video_url: t.video_url };
|
newMap[t.job_id] = { status: 'finished', video_url: t.video_url };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return newMap;
|
return newMap;
|
||||||
|
|
@ -59,7 +60,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
// 只查本地没有 video_url 的任务
|
// 只查本地没有 video_url 的任务
|
||||||
const job_ids = tasks
|
const job_ids = tasks
|
||||||
.filter(t => !t.video_url)
|
.filter(t => !t.video_url)
|
||||||
.map(t => t.task_id)
|
.map(t => t.job_id)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
if (!job_ids.length) return;
|
if (!job_ids.length) return;
|
||||||
setStatusLoading(true);
|
setStatusLoading(true);
|
||||||
|
|
@ -71,7 +72,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
(res.data.finished || []).forEach((item: any) => {
|
(res.data.finished || []).forEach((item: any) => {
|
||||||
map[item.job_id] = { status: 'finished', video_url: item.video_url };
|
map[item.job_id] = { status: 'finished', video_url: item.video_url };
|
||||||
if (item.video_url) {
|
if (item.video_url) {
|
||||||
const localTask = tasks.find(t => t.task_id === item.job_id);
|
const localTask = tasks.find(t => t.job_id === item.job_id);
|
||||||
if (localTask && !localTask.video_url) {
|
if (localTask && !localTask.video_url) {
|
||||||
updateVideoTask(localTask.id, { video_url: item.video_url });
|
updateVideoTask(localTask.id, { video_url: item.video_url });
|
||||||
setTasks(prev => prev.map(t => (t.id === localTask.id ? { ...t, video_url: item.video_url } : t)));
|
setTasks(prev => prev.map(t => (t.id === localTask.id ? { ...t, video_url: item.video_url } : t)));
|
||||||
|
|
@ -91,7 +92,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
}, [tasks]);
|
}, [tasks]);
|
||||||
|
|
||||||
const renderStatus = (task: LocalVideoTask) => {
|
const renderStatus = (task: LocalVideoTask) => {
|
||||||
const s = statusMap[task.task_id];
|
const s = statusMap[task.job_id];
|
||||||
if (!s || !['finished', 'running', 'failed'].includes(s.status)) {
|
if (!s || !['finished', 'running', 'failed'].includes(s.status)) {
|
||||||
return <span className='text-gray-400'>未知</span>;
|
return <span className='text-gray-400'>未知</span>;
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +111,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算可选的任务ID(已完成且有video_url)
|
// 计算可选的任务ID(已完成且有video_url)
|
||||||
const downloadableIds = tasks.filter(t => statusMap[t.task_id]?.status === 'finished' && statusMap[t.task_id]?.video_url).map(t => t.id);
|
const downloadableIds = tasks.filter(t => statusMap[t.job_id]?.status === 'finished' && statusMap[t.job_id]?.video_url).map(t => t.id);
|
||||||
|
|
||||||
const isAllSelected = downloadableIds.length > 0 && downloadableIds.every(id => selectedIds.includes(id));
|
const isAllSelected = downloadableIds.length > 0 && downloadableIds.every(id => selectedIds.includes(id));
|
||||||
|
|
||||||
|
|
@ -135,7 +136,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
const selectedTasks = tasks.filter(t => selectedIds.includes(t.id));
|
const selectedTasks = tasks.filter(t => selectedIds.includes(t.id));
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const task of selectedTasks) {
|
for (const task of selectedTasks) {
|
||||||
const s = statusMap[task.task_id];
|
const s = statusMap[task.job_id];
|
||||||
if (s?.status === 'finished' && s.video_url) {
|
if (s?.status === 'finished' && s.video_url) {
|
||||||
try {
|
try {
|
||||||
// 直接用 a 标签下载,避免 CORS
|
// 直接用 a 标签下载,避免 CORS
|
||||||
|
|
@ -156,7 +157,7 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-6'>
|
<div className='p-6 overflow-x-auto w-full'>
|
||||||
<h2 className='text-xl font-bold mb-4'>视频任务进度</h2>
|
<h2 className='text-xl font-bold mb-4'>视频任务进度</h2>
|
||||||
<div className='mb-6 flex justify-between items-center'>
|
<div className='mb-6 flex justify-between items-center'>
|
||||||
<Button variant='outline' onClick={() => window.location.reload()}>
|
<Button variant='outline' onClick={() => window.location.reload()}>
|
||||||
|
|
@ -177,23 +178,23 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
<div className='text-gray-400 text-center mt-20'>暂无本地视频任务</div>
|
<div className='text-gray-400 text-center mt-20'>暂无本地视频任务</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
<Table className='min-w-full text-sm'>
|
<Table className='text-sm'>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow className='bg-gray-100'>
|
<TableRow className='bg-gray-100'>
|
||||||
<TableHead className='p-2 text-center'>
|
<TableHead className='p-2 text-center'>
|
||||||
<Checkbox checked={isAllSelected} onCheckedChange={handleSelectAll} aria-label='全选' disabled={downloadableIds.length === 0} />
|
<Checkbox checked={isAllSelected} onCheckedChange={handleSelectAll} aria-label='全选' disabled={downloadableIds.length === 0} />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className='p-2 text-center'>图片</TableHead>
|
<TableHead className='p-2 text-center'>图片</TableHead>
|
||||||
|
<TableHead className='p-2'>状态</TableHead>
|
||||||
<TableHead className='p-2'>ID</TableHead>
|
<TableHead className='p-2'>ID</TableHead>
|
||||||
<TableHead className='p-2'>任务ID</TableHead>
|
<TableHead className='p-2'>任务ID</TableHead>
|
||||||
<TableHead className='p-2'>Prompt</TableHead>
|
<TableHead className='p-2'>Prompt</TableHead>
|
||||||
<TableHead className='p-2'>创建时间</TableHead>
|
<TableHead className='p-2'>创建时间</TableHead>
|
||||||
<TableHead className='p-2'>状态</TableHead>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{tasks.map(task => {
|
{tasks.map(task => {
|
||||||
const s = statusMap[task.task_id];
|
const s = statusMap[task.job_id];
|
||||||
const canDownload = s?.status === 'finished' && s.video_url;
|
const canDownload = s?.status === 'finished' && s.video_url;
|
||||||
return (
|
return (
|
||||||
<TableRow key={task.id}>
|
<TableRow key={task.id}>
|
||||||
|
|
@ -220,11 +221,11 @@ const TryOnVideoTaskPage: React.FC = () => {
|
||||||
<span className='text-gray-400'>无</span>
|
<span className='text-gray-400'>无</span>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>{statusLoading ? '查询中...' : renderStatus(task)}</TableCell>
|
||||||
<TableCell>{task.id}</TableCell>
|
<TableCell>{task.id}</TableCell>
|
||||||
<TableCell>{task.task_id}</TableCell>
|
<TableCell>{task.job_id}</TableCell>
|
||||||
<TableCell>{task.prompt}</TableCell>
|
<TableCell>{task.prompt}</TableCell>
|
||||||
<TableCell>{task.create_time ? new Date(task.create_time).toLocaleString() : '-'}</TableCell>
|
<TableCell>{task.create_time ? new Date(task.create_time).toLocaleString() : '-'}</TableCell>
|
||||||
<TableCell>{statusLoading ? '查询中...' : renderStatus(task)}</TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue