feat: 电商工作台进度与生成记录健壮性优化

This commit is contained in:
2026-06-15 10:24:31 +08:00
parent e1fdbe5f9b
commit 0b2d6b901f
10 changed files with 533 additions and 102 deletions
+30 -46
View File
@@ -1,4 +1,4 @@
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react";
import {
BugOutlined,
CheckCircleFilled,
@@ -20,9 +20,7 @@ import {
import ErrorBoundary from "./components/ErrorBoundary";
import ToastContainer from "./components/toast/ToastContainer";
import { toast } from "./components/toast/toastStore";
import EcommercePage from "./features/ecommerce/EcommercePage";
import { flushPendingGenerationRecords } from "./api/generationRecordClient";
import { ossAssets } from "./data/ossAssets";
import { keyServerClient } from "./api/keyServerClient";
import { setUserMaxConcurrency } from "./api/generationConcurrency";
import {
@@ -38,6 +36,8 @@ import { useAppStore, useSessionStore } from "./stores";
import type { WebUserSession } from "./types";
import "./styles/ecommerce-standalone.css";
const EcommercePage = lazy(() => import("./features/ecommerce/EcommercePage"));
type AuthMode = "login" | "register";
type AuthMethod = "account" | "email" | "phone";
@@ -51,17 +51,6 @@ interface LocalProfilePageProps {
onLogout: () => void;
}
const profileWorks = [
{ title: "主图套图生成", desc: "电商主图与场景图自动生成", image: ossAssets.ecommerce.templateCases[0], type: "图像", time: "6/9 18:13" },
{ title: "A+详情页设计", desc: "产品卖点与长图详情版式", image: ossAssets.ecommerce.templateCases[1], type: "图像", time: "6/9 10:11" },
{ title: "短视频广告", desc: "产品展示短视频脚本与画面", image: ossAssets.ecommerce.productSet.hosting, type: "视频", time: "6/9 10:05" },
{ title: "模特图生成", desc: "服饰商品真人上身展示", image: ossAssets.ecommerce.tryOn.tryA, type: "图像", time: "6/9 10:03" },
{ title: "商品场景图", desc: "按平台比例输出营销素材", image: ossAssets.ecommerce.detail.gridA, type: "图像", time: "6/9 10:01" },
{ title: "高度复刻", desc: "参考图结构复刻与商品替换", image: ossAssets.ecommerce.detail.gridB, type: "图像", time: "6/9 09:39" },
{ title: "详情模块", desc: "功能卖点、参数和包装模块", image: ossAssets.ecommerce.detail.gridC, type: "图像", time: "6/8 21:20" },
{ title: "平台素材", desc: "淘宝/天猫投放图批量生成", image: ossAssets.ecommerce.detail.gridD, type: "图像", time: "6/8 18:26" },
];
function LocalAvatar({ session, size = "md" }: { session: WebUserSession; size?: "sm" | "md" | "lg" }) {
const displayName = session.user.displayName || session.user.username || "用户";
const label = displayName.trim().slice(0, 1).toUpperCase() || "用";
@@ -75,9 +64,9 @@ function LocalAvatar({ session, size = "md" }: { session: WebUserSession; size?:
function LocalProfilePage({ session, balance, imageCount, videoCount, onBack, onBugFeedback, onLogout }: LocalProfilePageProps) {
const displayName = session.user.displayName || session.user.username || "用户";
const workCount = Math.max(imageCount + videoCount, profileWorks.length);
const projectCount = Math.max(1, Math.round(workCount / 18));
const assetCount = Math.max(1, Math.round(workCount / 20));
const workCount = Math.max(imageCount + videoCount, 0);
const projectCount = 0;
const assetCount = 0;
return (
<section className="local-profile-page">
@@ -142,22 +131,15 @@ function LocalProfilePage({ session, balance, imageCount, videoCount, onBack, on
<header>
<div>
<strong></strong>
<span></span>
<span></span>
</div>
<em>{workCount} </em>
</header>
<div className="local-profile-work-grid">
{profileWorks.map((work) => (
<article key={`${work.title}-${work.time}`} className="local-profile-work-card">
<img src={work.image} alt="" />
<div>
<span>{work.type}</span>
<strong>{work.title}</strong>
<p>{work.desc}</p>
<em> · {work.time}</em>
</div>
</article>
))}
<div className="local-profile-work-grid local-profile-work-grid--empty">
<div className="local-profile-empty">
<strong></strong>
<span></span>
</div>
</div>
</section>
</main>
@@ -184,7 +166,6 @@ function App() {
const [sessionNotice, setSessionNotice] = useState<string | null>(null);
const [profileMenuOpen, setProfileMenuOpen] = useState(false);
const [currentPage, setCurrentPage] = useState<"workspace" | "profile">("workspace");
const [workspaceKey, setWorkspaceKey] = useState(0);
useEffect(() => {
void loadDarkGreenTheme();
@@ -339,7 +320,7 @@ function App() {
const balance = Math.max(usage.balanceCents, 0) / 100;
const displayName = session?.user.displayName || session?.user.username || "用户";
const actualWorkCount = Math.max(usage.imageUsed + usage.videoUsed, 0);
const shownWorkCount = Math.max(actualWorkCount, profileWorks.length);
const shownWorkCount = actualWorkCount;
const avatarMenuStats = useMemo(
() => [
@@ -360,7 +341,6 @@ function App() {
const handleOpenWorkspace = () => {
setProfileMenuOpen(false);
setCurrentPage("workspace");
setWorkspaceKey((k) => k + 1);
};
const handleBugFeedback = () => {
@@ -447,17 +427,22 @@ function App() {
</header>
<main className="ecommerce-standalone__content">
{currentPage === "profile" && session ? (
<LocalProfilePage
session={session}
balance={balance}
imageCount={usage.imageUsed}
videoCount={usage.videoUsed}
onBack={handleOpenWorkspace}
onBugFeedback={handleBugFeedback}
onLogout={handleLogout}
/>
) : (
{session ? (
<div className="ecommerce-standalone__page" hidden={currentPage !== "profile"}>
<LocalProfilePage
session={session}
balance={balance}
imageCount={usage.imageUsed}
videoCount={usage.videoUsed}
onBack={handleOpenWorkspace}
onBugFeedback={handleBugFeedback}
onLogout={handleLogout}
/>
</div>
) : null}
{/* 工作台常驻挂载,仅用 hidden 切换。切到个人中心时不卸载,
生成任务、进度动画、已上传图片等本地状态全部保留,切回即继续。 */}
<div className="ecommerce-standalone__page" hidden={Boolean(session) && currentPage === "profile"}>
<ErrorBoundary>
<Suspense
fallback={
@@ -468,7 +453,6 @@ function App() {
}
>
<EcommercePage
key={workspaceKey}
projects={[]}
isAuthenticated={Boolean(session)}
onStartCreate={() => undefined}
@@ -482,7 +466,7 @@ function App() {
/>
</Suspense>
</ErrorBoundary>
)}
</div>
</main>
{authOpen ? (