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:
2026-06-03 02:01:21 +08:00
parent 468d1d27dd
commit 4ed02aaad5
9 changed files with 411 additions and 6 deletions
+26 -3
View File
@@ -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>