feat: 邮箱注册验证 + 9项功能修复与优化
【认证系统】 - 新增邮箱验证码注册/登录流程 (sendEmailCode / verifyEmail / forgotPassword / resetPassword) - register-email 现在需要验证码 - 服务端新增 email_verification_codes 表 + patch-email-verification.js - App.tsx 登录后 emailVerified 检查提醒 - keyServerClient token 显式传递修复 401 错误 【电商模块】 - 自动推进: 策划完成后自动生成分镜图/视频 - 模特图选项 (性别/年龄/种族/体型/场景) 注入 AI 提示词 - 任务持久化指纹修复 (图片数量替代 blob URL) - 新增「视频换装」入口 (happyhorse-1.0-video-edit) 【剧本评分】 - 新增 .docx/.doc Word 文档支持 (ZIP解压+XML提取) - 历史记录支持点击查看/恢复评测结果 【画布】 - ReactFlow 节点禁止内置拖拽避免冲突 - 连接线拖拽弹窗优化 (预览线不消失, 弹窗跟踪鼠标) 【页面修复】 - 首页轮播图改为 aspect-ratio: 16/9 解决尺寸问题 - 资产库新增悬停删除按钮 - scriptEvalClient 改用服务端 /api/ai/chat 端点 - TokenUsagePage 未登录跳过 API 调用
This commit is contained in:
@@ -100,14 +100,14 @@ function AssetsPage({ isAuthenticated, onOpenLogin }: AssetsPageProps) {
|
||||
setContextMenu({ x: e.clientX, y: e.clientY, asset });
|
||||
}, []);
|
||||
|
||||
const handleDeleteAsset = useCallback(async () => {
|
||||
if (!contextMenu) return;
|
||||
const { asset } = contextMenu;
|
||||
const handleDeleteAsset = useCallback(async (asset?: LibraryAssetItem) => {
|
||||
const target = asset || contextMenu?.asset;
|
||||
if (!target) return;
|
||||
setContextMenu(null);
|
||||
try {
|
||||
await assetClient.delete(asset.id);
|
||||
setServerAssets((prev) => prev.filter((a) => a.id !== asset.id));
|
||||
setServerNotice(`已删除 ${asset.name}`);
|
||||
await assetClient.delete(target.id);
|
||||
setServerAssets((prev) => prev.filter((a) => a.id !== target.id));
|
||||
setServerNotice(`已删除 ${target.name}`);
|
||||
} catch (err) {
|
||||
setServerNotice(err instanceof Error ? err.message : "删除失败");
|
||||
}
|
||||
@@ -287,32 +287,42 @@ function AssetsPage({ isAuthenticated, onOpenLogin }: AssetsPageProps) {
|
||||
{visibleAssets.length ? (
|
||||
<div className="asset-grid asset-grid--desktop motion-stagger">
|
||||
{visibleAssets.map((asset) => (
|
||||
<button
|
||||
key={asset.id}
|
||||
type="button"
|
||||
className="asset-card asset-card--desktop"
|
||||
onClick={() => setPreviewAsset(asset)}
|
||||
onContextMenu={(e) => handleContextMenu(e, asset)}
|
||||
aria-label={`预览素材 ${asset.name}`}
|
||||
>
|
||||
<div className={`asset-card__thumb ${asset.thumbClass}`}>
|
||||
{asset.imageUrl ? <OptimizedImage src={asset.imageUrl} alt={asset.name} /> : null}
|
||||
</div>
|
||||
<div className="asset-card__body">
|
||||
<div className="asset-card__head">
|
||||
<strong>{asset.name}</strong>
|
||||
<span className={`studio-status-bar__badge ${statusBadgeClass[asset.status]}`}>
|
||||
{statusLabel[asset.status]}
|
||||
</span>
|
||||
<div key={asset.id} className="asset-card-wrapper">
|
||||
<button
|
||||
type="button"
|
||||
className="asset-card asset-card--desktop"
|
||||
onClick={() => setPreviewAsset(asset)}
|
||||
onContextMenu={(e) => handleContextMenu(e, asset)}
|
||||
aria-label={`预览素材 ${asset.name}`}
|
||||
>
|
||||
<div className={`asset-card__thumb ${asset.thumbClass}`}>
|
||||
{asset.imageUrl ? <OptimizedImage src={asset.imageUrl} alt={asset.name} /> : null}
|
||||
</div>
|
||||
<p className="asset-card__desc">{asset.description}</p>
|
||||
<div className="asset-card__tags">
|
||||
{asset.tags.slice(0, 2).map((tag) => (
|
||||
<span key={tag}>{tag}</span>
|
||||
))}
|
||||
<div className="asset-card__body">
|
||||
<div className="asset-card__head">
|
||||
<strong>{asset.name}</strong>
|
||||
<span className={`studio-status-bar__badge ${statusBadgeClass[asset.status]}`}>
|
||||
{statusLabel[asset.status]}
|
||||
</span>
|
||||
</div>
|
||||
<p className="asset-card__desc">{asset.description}</p>
|
||||
<div className="asset-card__tags">
|
||||
{asset.tags.slice(0, 2).map((tag) => (
|
||||
<span key={tag}>{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="asset-card__delete"
|
||||
title="删除素材"
|
||||
onClick={(e) => { e.stopPropagation(); void handleDeleteAsset(asset); }}
|
||||
aria-label={`删除 ${asset.name}`}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : isLoading ? (
|
||||
|
||||
Reference in New Issue
Block a user