feat: 错误监控面板、生成通知、社区搜索、任务队列优化
- AdminMonitor: admin用户可见的客户端错误实时监控面板,右下角浮窗 - generationNotifier: 生成完成浏览器通知 + 站内Toast - CommunityPage: 新增搜索框,标题/描述/标签模糊匹配,防抖300ms - App.tsx: 全局unhandled error/rejection监听上报 - WorkbenchPage: 任务并发提示改为显示当前任务数 - serverConnection: 后端client-errors路由注册 - WelcomeSplash: 欢迎按钮全程显示 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,10 @@ import {
|
||||
PictureOutlined,
|
||||
PlusOutlined,
|
||||
RightOutlined,
|
||||
SearchOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useDebounce } from "../../hooks/useDebounce";
|
||||
import { communityClient, type ServerCommunityCase } from "../../api/communityClient";
|
||||
import WorkspacePageShell from "../../components/WorkspacePageShell";
|
||||
import OptimizedImage from "../../components/OptimizedImage";
|
||||
@@ -70,6 +72,8 @@ function buildWorkflowFromServerCase(item: ServerCommunityCase, fallback: WebCan
|
||||
function CommunityPage({ projects, isAuthenticated, onStartCreate, onOpenProject, onDeleteProject, onImportWorkflow, onRequireLogin }: CommunityPageProps) {
|
||||
const [serverCases, setServerCases] = useState<ServerCommunityCase[]>([]);
|
||||
const [serverNotice, setServerNotice] = useState<string | null>(null);
|
||||
const [query, setQuery] = useState("");
|
||||
const debouncedQuery = useDebounce(query, 300);
|
||||
const [favoriteIds, setFavoriteIds] = useState<string[]>([]);
|
||||
const canUseProtectedAction = (action: string) => onRequireLogin?.(action) !== false;
|
||||
|
||||
@@ -260,7 +264,17 @@ function CommunityPage({ projects, isAuthenticated, onStartCreate, onOpenProject
|
||||
}
|
||||
};
|
||||
|
||||
const liveCases: ServerCommunityCase[] = serverCases.slice(0, 12);
|
||||
const filteredCases = useMemo(() => {
|
||||
const q = debouncedQuery.trim().toLowerCase();
|
||||
if (!q) return serverCases;
|
||||
return serverCases.filter((c) =>
|
||||
(c.title || "").toLowerCase().includes(q) ||
|
||||
(c.description || "").toLowerCase().includes(q) ||
|
||||
(c.tags || []).some((t: string) => t.toLowerCase().includes(q))
|
||||
);
|
||||
}, [serverCases, debouncedQuery]);
|
||||
|
||||
const liveCases: ServerCommunityCase[] = filteredCases.slice(0, 12);
|
||||
|
||||
return (
|
||||
<WorkspacePageShell title="社区" fullWidth className="community-page page-motion">
|
||||
@@ -387,6 +401,15 @@ function CommunityPage({ projects, isAuthenticated, onStartCreate, onOpenProject
|
||||
<div>
|
||||
<h2>社区精选</h2>
|
||||
</div>
|
||||
<label className="asset-search">
|
||||
<SearchOutlined />
|
||||
<input
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="搜索案例..."
|
||||
/>
|
||||
{query ? <button type="button" className="asset-search__clear" onClick={() => setQuery("")} aria-label="清除搜索">×</button> : null}
|
||||
</label>
|
||||
{serverNotice ? <span className="studio-pill">{serverNotice}</span> : null}
|
||||
</div>
|
||||
{liveCases.length ? (
|
||||
@@ -473,8 +496,8 @@ function CommunityPage({ projects, isAuthenticated, onStartCreate, onOpenProject
|
||||
) : (
|
||||
<EmptyState
|
||||
icon={<ImportOutlined style={{ fontSize: 48 }} />}
|
||||
title="社区暂无模板"
|
||||
description="管理员审核通过后,画布社区案例会显示在这里。"
|
||||
title={debouncedQuery ? "无匹配结果" : "社区暂无模板"}
|
||||
description={debouncedQuery ? "尝试其他关键词,或清除搜索查看全部案例" : "管理员审核通过后,画布社区案例会显示在这里。"}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -15,7 +15,7 @@ const prefersReducedMotion = typeof window !== "undefined"
|
||||
export default function WelcomeSplash({ onEnter }: WelcomeSplashProps) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const rafRef = useRef(0);
|
||||
const [showWelcome, setShowWelcome] = useState(false);
|
||||
const [showWelcome, setShowWelcome] = useState(true);
|
||||
const [exiting, setExiting] = useState(false);
|
||||
|
||||
const handleEnter = useCallback(() => {
|
||||
|
||||
@@ -949,6 +949,11 @@ function WorkbenchPage({
|
||||
await patchConversationMessage(task.conversationId, task.assistantMessageId, completedPatch);
|
||||
removeKeepaliveTask(task.taskId);
|
||||
onRefreshUsage?.();
|
||||
if (status.status === "completed") {
|
||||
import("../../utils/generationNotifier").then((m) =>
|
||||
m.notifyTaskCompleted(task.mode === "video" ? "视频" : "图片", task.mode as "image" | "video"),
|
||||
);
|
||||
}
|
||||
try {
|
||||
if (status.resultUrl) {
|
||||
const persistedResult = await persistWorkbenchResultAsset({
|
||||
@@ -992,6 +997,11 @@ function WorkbenchPage({
|
||||
});
|
||||
removeKeepaliveTask(task.taskId);
|
||||
onRefreshUsage?.();
|
||||
if (status.status === "completed") {
|
||||
import("../../utils/generationNotifier").then((m) =>
|
||||
m.notifyTaskCompleted(task.mode === "video" ? "视频" : "图片", task.mode as "image" | "video"),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2105,7 +2115,7 @@ function WorkbenchPage({
|
||||
return;
|
||||
}
|
||||
if (getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id)) >= 3) {
|
||||
setProjectError("当前任务数已达上限(3个),请等待任务完成后再试");
|
||||
setProjectError(`当前已有 ${getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id))} 个任务进行中(上限3个),请等待任一任务完成后再提交新任务`);
|
||||
return;
|
||||
}
|
||||
if (!isAuthenticated) {
|
||||
@@ -2227,7 +2237,7 @@ function WorkbenchPage({
|
||||
return;
|
||||
}
|
||||
if (getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id)) >= 3) {
|
||||
setProjectError("当前任务数已达上限(3个),请等待任务完成后再试");
|
||||
setProjectError(`当前已有 ${getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id))} 个任务进行中(上限3个),请等待任一任务完成后再提交新任务`);
|
||||
return;
|
||||
}
|
||||
if (!isAuthenticated) {
|
||||
|
||||
Reference in New Issue
Block a user