feat: 电商页面 KeepAlive 保活机制,切换页面不再丢失生成状态
通过 display:none 模式实现轻量 KeepAlive,电商页面首次访问后保持挂载, 切换到其他页面再切回时所有右侧面板状态(上传图片、生成进度、结果)完整保留。 同时清理项目中的临时文件和本地冗余图片。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import { CloudUploadOutlined, CloseOutlined, FileImageOutlined, SettingOutlined } from "@ant-design/icons";
|
||||
import type { ChangeEvent, DragEvent, RefObject } from "react";
|
||||
|
||||
interface EcommerceSetPanelProps {
|
||||
setInputRef: RefObject<HTMLInputElement>;
|
||||
setImages: Array<{ id: string; src: string; name: string }>;
|
||||
isSetUploadDragging: boolean;
|
||||
productSetOutputOptions: Array<{ key: string; label: string }>;
|
||||
productSetOutput: string;
|
||||
platformOptions: string[];
|
||||
marketOptions: string[];
|
||||
productSetLanguageOptions: string[];
|
||||
productSetRatioOptions: string[];
|
||||
productSetPlatform: string;
|
||||
productSetMarket: string;
|
||||
productSetLanguage: string;
|
||||
productSetRatio: string;
|
||||
setIsSetUploadDragging: (value: boolean) => void;
|
||||
handleSetDrop: (event: DragEvent<HTMLElement>) => void;
|
||||
handleSetUpload: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
removeSetImage: (id: string) => void;
|
||||
handleProductSetOutputChange: (value: string) => void;
|
||||
handleProductSetPlatformChange: (value: string) => void;
|
||||
handleProductSetMarketChange: (value: string) => void;
|
||||
setProductSetLanguage: (value: string) => void;
|
||||
setProductSetRatio: (value: string) => void;
|
||||
formatRatioDisplayValue: (value: string) => string;
|
||||
}
|
||||
|
||||
export default function EcommerceSetPanel({
|
||||
setInputRef,
|
||||
setImages,
|
||||
isSetUploadDragging,
|
||||
productSetOutputOptions,
|
||||
productSetOutput,
|
||||
platformOptions,
|
||||
marketOptions,
|
||||
productSetLanguageOptions,
|
||||
productSetRatioOptions,
|
||||
productSetPlatform,
|
||||
productSetMarket,
|
||||
productSetLanguage,
|
||||
productSetRatio,
|
||||
setIsSetUploadDragging,
|
||||
handleSetDrop,
|
||||
handleSetUpload,
|
||||
removeSetImage,
|
||||
handleProductSetOutputChange,
|
||||
handleProductSetPlatformChange,
|
||||
handleProductSetMarketChange,
|
||||
setProductSetLanguage,
|
||||
setProductSetRatio,
|
||||
formatRatioDisplayValue,
|
||||
}: EcommerceSetPanelProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="product-clone-panel__scroll">
|
||||
<section className="product-clone-field product-set-upload-section">
|
||||
<h2>
|
||||
上传商品原图
|
||||
<CloudUploadOutlined />
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
className={`product-clone-upload-zone product-set-upload${isSetUploadDragging ? " is-dragging" : ""}`}
|
||||
onClick={() => setInputRef.current?.click()}
|
||||
onDragEnter={(event) => {
|
||||
event.preventDefault();
|
||||
setIsSetUploadDragging(true);
|
||||
}}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDragLeave={() => setIsSetUploadDragging(false)}
|
||||
onDrop={handleSetDrop}
|
||||
>
|
||||
<span className="product-set-upload-icon">
|
||||
<FileImageOutlined />
|
||||
</span>
|
||||
<span className="product-set-upload-title">拖拽或点击上传</span>
|
||||
<strong>
|
||||
<span aria-hidden="true">+</span>
|
||||
上传图片
|
||||
</strong>
|
||||
<span className="product-set-upload-note">同一产品,最多 3 张</span>
|
||||
</button>
|
||||
<input ref={setInputRef} type="file" accept="image/jpeg,image/png,image/webp" multiple onChange={handleSetUpload} />
|
||||
{setImages.length ? (
|
||||
<div className="product-clone-thumb-row product-set-thumb-row" aria-label="已上传商品原图">
|
||||
{setImages.map((item) => (
|
||||
<figure key={item.id} className="product-set-thumb">
|
||||
<img src={item.src} alt={item.name} />
|
||||
<span className="uploaded-image-zoom" aria-hidden="true">
|
||||
<img src={item.src} alt="" />
|
||||
</span>
|
||||
<button type="button" onClick={() => removeSetImage(item.id)} aria-label={`删除${item.name}`}>
|
||||
<CloseOutlined />
|
||||
</button>
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<section className="product-clone-field product-set-settings-section">
|
||||
<h2>
|
||||
生成设置
|
||||
<SettingOutlined />
|
||||
</h2>
|
||||
<div className="product-set-setting-block">
|
||||
<span className="product-set-setting-title">生成内容</span>
|
||||
<div className="product-set-output-grid" role="radiogroup" aria-label="生成内容">
|
||||
{productSetOutputOptions.map((option) => (
|
||||
<button
|
||||
key={option.key}
|
||||
type="button"
|
||||
className={productSetOutput === option.key ? "is-active" : ""}
|
||||
aria-pressed={productSetOutput === option.key}
|
||||
onClick={() => handleProductSetOutputChange(option.key)}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="product-set-setting-block">
|
||||
<span className="product-set-setting-title">基础设置</span>
|
||||
<div className="product-set-field-grid">
|
||||
<label>
|
||||
<span>平台</span>
|
||||
<select value={productSetPlatform} onChange={(event) => handleProductSetPlatformChange(event.target.value)}>
|
||||
{platformOptions.map((item) => (
|
||||
<option key={item}>{item}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>国家</span>
|
||||
<select value={productSetMarket} onChange={(event) => handleProductSetMarketChange(event.target.value)}>
|
||||
{marketOptions.map((item) => (
|
||||
<option key={item}>{item}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>语言</span>
|
||||
<select value={productSetLanguage} onChange={(event) => setProductSetLanguage(event.target.value)}>
|
||||
{productSetLanguageOptions.map((item) => (
|
||||
<option key={item}>{item}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>尺寸/比例</span>
|
||||
<select
|
||||
value={productSetRatio}
|
||||
onChange={(event) => setProductSetRatio(event.target.value)}
|
||||
disabled={productSetRatioOptions.length <= 1}
|
||||
>
|
||||
{productSetRatioOptions.map((item) => (
|
||||
<option key={item} value={item}>{formatRatioDisplayValue(item)}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user