feat: 电商页面 KeepAlive 保活机制,切换页面不再丢失生成状态

通过 display:none 模式实现轻量 KeepAlive,电商页面首次访问后保持挂载,
切换到其他页面再切回时所有右侧面板状态(上传图片、生成进度、结果)完整保留。
同时清理项目中的临时文件和本地冗余图片。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 23:20:57 +08:00
parent fdf9c43731
commit 0fc180637c
21 changed files with 2909 additions and 458 deletions
@@ -0,0 +1,168 @@
import { CloudUploadOutlined, LoadingOutlined, QuestionCircleOutlined } from "@ant-design/icons";
import type { ChangeEvent, RefObject } from "react";
import { EcommerceProgressBar } from "../EcommerceProgressBar";
interface EcommerceDetailPanelProps {
detailInputRef: RefObject<HTMLInputElement>;
detailProductImages: Array<{ id: string; src: string; name: string }>;
detailPlatform: string;
detailMarket: string;
detailLanguage: string;
detailType: string;
detailRequirement: string;
selectedDetailModules: string[];
detailStatus: string;
canGenerateDetail: boolean;
detailPrimaryLabel: string;
platformOptions: string[];
marketOptions: string[];
detailLanguageOptions: string[];
detailTypeOptions: string[];
detailModules: Array<{ id: string; title: string; desc: string }>;
handleDetailUpload: (event: ChangeEvent<HTMLInputElement>) => void;
handleDetailPlatformChange: (value: string) => void;
handleDetailMarketChange: (value: string) => void;
setDetailLanguage: (value: string) => void;
setDetailType: (value: string) => void;
setDetailRequirement: (value: string) => void;
handleDetailAiWrite: () => void;
toggleDetailModule: (id: string) => void;
handleDetailGenerate: () => void;
}
export default function EcommerceDetailPanel({
detailInputRef,
detailProductImages,
detailPlatform,
detailMarket,
detailLanguage,
detailType,
detailRequirement,
selectedDetailModules,
detailStatus,
canGenerateDetail,
detailPrimaryLabel,
platformOptions,
marketOptions,
detailLanguageOptions,
detailTypeOptions,
detailModules,
handleDetailUpload,
handleDetailPlatformChange,
handleDetailMarketChange,
setDetailLanguage,
setDetailType,
setDetailRequirement,
handleDetailAiWrite,
toggleDetailModule,
handleDetailGenerate,
}: EcommerceDetailPanelProps) {
return (
<>
<div className="product-clone-panel__scroll">
<section className="product-clone-field">
<h2>
<QuestionCircleOutlined />
</h2>
<button type="button" className="product-clone-upload-zone product-detail-upload" onClick={() => detailInputRef.current?.click()}>
<strong>
<CloudUploadOutlined />
</strong>
<span>3</span>
</button>
<input ref={detailInputRef} type="file" accept="image/*" multiple onChange={handleDetailUpload} />
{detailProductImages.length ? (
<div className="product-clone-thumb-row" aria-label="已上传商品原图">
{detailProductImages.map((item) => (
<figure key={item.id} className="product-clone-uploaded-thumb">
<img src={item.src} alt={item.name} />
<span className="uploaded-image-zoom" aria-hidden="true">
<img src={item.src} alt="" />
</span>
</figure>
))}
</div>
) : null}
</section>
<section className="product-clone-field">
<h2></h2>
<div className="product-detail-settings-grid">
<select value={detailPlatform} onChange={(event) => handleDetailPlatformChange(event.target.value)}>
{platformOptions.map((item) => (
<option key={item}>{item}</option>
))}
</select>
<select value={detailMarket} onChange={(event) => handleDetailMarketChange(event.target.value)}>
{marketOptions.map((item) => (
<option key={item}>{item}</option>
))}
</select>
<select value={detailLanguage} onChange={(event) => setDetailLanguage(event.target.value)}>
{detailLanguageOptions.map((item) => (
<option key={item}>{item}</option>
))}
</select>
<select value={detailType} onChange={(event) => setDetailType(event.target.value)}>
{detailTypeOptions.map((item) => (
<option key={item}>{item}</option>
))}
</select>
</div>
</section>
<section className="product-clone-field product-detail-requirement">
<h2>
&
<QuestionCircleOutlined />
<button
type="button"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
handleDetailAiWrite();
}}
>
AI
</button>
</h2>
<textarea
value={detailRequirement}
onChange={(event) => setDetailRequirement(event.target.value)}
placeholder={"建议包含以下信息生成更精准:\n1.产品名称\n2.核心卖点\n3.适用人群\n4.期望场景\n5.具体参数"}
/>
</section>
<section className="product-clone-field">
<h2>
<QuestionCircleOutlined />
</h2>
<div className="product-detail-module-grid">
{detailModules.map((module) => (
<button
key={module.id}
type="button"
className={selectedDetailModules.includes(module.id) ? "is-active" : ""}
onClick={() => toggleDetailModule(module.id)}
>
<strong>{module.title}</strong>
<span>{module.desc}</span>
</button>
))}
</div>
</section>
</div>
<footer className="product-clone-panel__footer">
{detailStatus === "generating" ? <EcommerceProgressBar status="generating" label="A+详情页" /> : null}
<button type="button" className="product-clone-primary" disabled={!canGenerateDetail} onClick={handleDetailGenerate}>
{detailStatus === "generating" ? <LoadingOutlined /> : null}
{detailPrimaryLabel}
</button>
</footer>
</>
);
}