feat: 添加新的模型,删除后端数据库
This commit is contained in:
@@ -15,7 +15,7 @@ import { SettingOutlined, DownloadOutlined, ThunderboltOutlined, UploadOutlined,
|
||||
import type { ScanResult, ImageMaterial } from '../../src/collector/scan';
|
||||
import {
|
||||
buildGeneratePayload, suiteZipUrl, uploadImage,
|
||||
DEFAULT_PLAN, IMAGE_MODEL_OPTIONS, PLATFORM_OPTIONS, PLATFORM_SPECS, STYLE_SET_OPTIONS,
|
||||
DEFAULT_PLAN, IMAGE_MODEL_OPTIONS, PLATFORM_OPTIONS, PLATFORM_SPECS, STYLE_SET_OPTIONS, WEAK_FIDELITY_MODELS,
|
||||
type PlanItem, type SuiteInfo,
|
||||
} from '../../src/api/client';
|
||||
import { loadSettings, saveSettings, type BackendSettings } from '../../src/storage/settings';
|
||||
@@ -280,10 +280,24 @@ const App: React.FC = () => {
|
||||
|
||||
const pollSuite = useCallback((suiteId: string) => {
|
||||
stopPolling();
|
||||
const startedAt = Date.now();
|
||||
let failCount = 0; // 连续轮询失败计数(容忍瞬时抖动,连续 3 次才判定后端不可达)
|
||||
let timeoutWarned = false;
|
||||
pollRef.current = setInterval(async () => {
|
||||
try {
|
||||
const s = await send<SuiteInfo>('getSuite', { baseUrl: settings.baseUrl, token: settings.token, suiteId });
|
||||
setSuite(s);
|
||||
failCount = 0;
|
||||
// 超时兜底:单张最长约 5 分钟,超预算仍 running 多半是任务已中断(如后端重启)
|
||||
const budgetMs = ((s.total ?? 1) * 5 + 10) * 60_000;
|
||||
if (!timeoutWarned && ['running', 'pending'].includes(s.status) && Date.now() - startedAt > budgetMs) {
|
||||
timeoutWarned = true;
|
||||
modal.warning({
|
||||
title: '任务耗时异常',
|
||||
content: '生成耗时远超预期,任务可能已中断。可稍等片刻观察,或刷新页面后重新生成。',
|
||||
okText: '知道了',
|
||||
});
|
||||
}
|
||||
if (['done', 'partial', 'failed'].includes(s.status)) {
|
||||
stopPolling();
|
||||
setGenerating(false);
|
||||
@@ -291,8 +305,16 @@ const App: React.FC = () => {
|
||||
if (s.status === 'failed') modal.error({ title: '生成失败', content: s.error || '未知错误', okText: '知道了' });
|
||||
}
|
||||
} catch (e) {
|
||||
stopPolling();
|
||||
setGenerating(false);
|
||||
failCount += 1;
|
||||
if (failCount >= 3) {
|
||||
stopPolling();
|
||||
setGenerating(false);
|
||||
modal.error({
|
||||
title: '无法连接后端',
|
||||
content: '连续查询生成进度失败,已停止跟踪。若刚重启过后端(./start.command),任务已被中断,请重新生成。',
|
||||
okText: '知道了',
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 3000);
|
||||
}, [settings.baseUrl, settings.token, modal]);
|
||||
@@ -396,7 +418,8 @@ const App: React.FC = () => {
|
||||
|
||||
const totalPlanned = plan.reduce((s, i) => s + i.count, 0);
|
||||
const doneCount = suite?.images.filter(i => i.status === 'ok').length ?? 0;
|
||||
const suiteTotal = suite?.images.length ?? totalPlanned;
|
||||
// 分母用后端返回的计划总张数:images 是逐张落库的,生成中 length 永远等于已完成数
|
||||
const suiteTotal = suite?.total ?? totalPlanned;
|
||||
/** 当前风格生效的提示词:用户改写值 > 该风格默认值 */
|
||||
const currentStylePrompt = stylePrompts[styleSet]
|
||||
?? STYLE_SET_OPTIONS.find(s => s.value === styleSet)?.prompt ?? '';
|
||||
@@ -408,7 +431,21 @@ const App: React.FC = () => {
|
||||
const spec = PLATFORM_SPECS[platform];
|
||||
modal.confirm({
|
||||
title: '生成电商套图',
|
||||
content: `目标平台「${spec.label}」(${spec.lang}文案 · ${spec.ratio}),模型「${model}」,风格「${STYLE_SET_OPTIONS.find(s => s.value === styleSet)?.label}」,共 ${totalPlanned} 张、参考图 ${selectedKeys.size} 张。生成需要几分钟,可在下方查看进度。`,
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
目标平台「{spec.label}」({spec.lang}文案 · {spec.ratio}),模型「{model}」,风格「
|
||||
{STYLE_SET_OPTIONS.find(s => s.value === styleSet)?.label}」,共 {totalPlanned} 张、参考图{' '}
|
||||
{selectedKeys.size} 张。生成需要几分钟,可在下方查看进度。
|
||||
</div>
|
||||
{WEAK_FIDELITY_MODELS.has(model) && (
|
||||
<div style={{ marginTop: 8, color: '#d4380d', fontWeight: 600 }}>
|
||||
⚠️ 「{model}」为官逆通道,商品还原度不稳定,可能生成与原商品不符的图片。建议先只出 1
|
||||
张确认效果,正式套图请改用 gpt-image-2。
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
okText: '开始生成', cancelText: '取消',
|
||||
onOk: () => startGenerate(),
|
||||
});
|
||||
@@ -796,10 +833,11 @@ const App: React.FC = () => {
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 4 }}>
|
||||
{generating && (
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ flex: 6 }}>
|
||||
{/* 进度条与右侧弹性占位 6:1,长度 = 剩余空间的 6/7(两次加长后的累积比例) */}
|
||||
<Progress
|
||||
percent={suiteTotal ? Math.round(doneCount / suiteTotal * 100) : 0}
|
||||
size="small" status="active" format={() => `${doneCount}/${suiteTotal}`}
|
||||
size={['100%', 10]} status="active" format={() => `${doneCount}/${suiteTotal}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -822,6 +860,7 @@ const App: React.FC = () => {
|
||||
</Select>
|
||||
<button
|
||||
className="btn btn-primary btn-main"
|
||||
style={{ alignSelf: 'stretch' }}
|
||||
disabled={!result || generating || selectedKeys.size === 0 || totalPlanned === 0}
|
||||
onClick={handleGenerate}
|
||||
>
|
||||
@@ -852,7 +891,7 @@ const App: React.FC = () => {
|
||||
</b>
|
||||
{' '}· {PLATFORM_SPECS[suite.platform]?.label ?? suite.platform}
|
||||
{' '}· {PLATFORM_SPECS[suite.platform]?.lang ?? suite.lang}文案 · {suite.ratio}
|
||||
{' '}· 风格「{STYLE_SET_OPTIONS.find(s => s.value === suite.style_set)?.label}」
|
||||
{' '}· 风格「{STYLE_SET_OPTIONS.find(s => s.value === suite.style_set)?.label ?? '自定义'}」
|
||||
</div>
|
||||
<div className="result-grid">
|
||||
{suite.images.map(img => (
|
||||
|
||||
Reference in New Issue
Block a user