feat(chat): 修复 Strategy/Workspace 统一聊天布局与历史路由归属

This commit is contained in:
董宏凯 committed 2026-09-01 14:51:16 +08:00
1 parent dbf62e5889
commit 519e4c0c01
7 files changed
+107 -70

No files matched your search

@@ -64,7 +64,7 @@ import {
import {
useDeleteThread,
useRenameThread,
useThreadsPage,
useThreads,
useToggleThreadFavorite,
useToggleThreadPin,
} from "@/core/threads/hooks";
@@ -351,8 +351,8 @@ export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string })
}
/**
* 当前智能体的「最近对话」面板(参考首页 GeneralQaSidebar 的「最近的对话」区块)。
* 按 agent_id 在服务端过滤;支持重命名/置顶/收藏/分享/导出/删除。
* 「最近的对话」面板(参考首页 GeneralQaSidebar 的「最近的对话」区块)。
* 跨智能体的近期会话,点击打开;支持重命名/置顶/收藏/分享/导出/删除。
*/
function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
const { t } = useI18n();
@@ -361,12 +361,7 @@ function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
const pathOf = usePathOfThread();
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
const { data: threadPage } = useThreadsPage({
page: 1,
pageSize: 50,
metadata: { agent_id: activeAgentId ?? "" },
});
const threads = threadPage?.threads ?? [];
const { data: threads = [] } = useThreads();
const { mutate: deleteThread } = useDeleteThread();
const { mutate: renameThread } = useRenameThread();
const { toggle: togglePin } = useToggleThreadPin();
@@ -378,8 +373,8 @@ function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
const [renameValue, setRenameValue] = useState("");
const [shareThreadId, setShareThreadId] = useState<string | null>(null);
// 服务端先按当前智能体过滤;客户端再排除调度/会商/报告结构&写作配置向导等
// 系统会话。智能体创建(bootstrap)会话保留展示(带机器人图标、点击回创建页);置顶优先。
// 排除调度/会商/报告结构&写作配置向导等系统会话,与首页一致;
// 智能体创建(bootstrap)会话保留展示(带机器人图标、点击回创建页);置顶优先。
const visibleThreads = useMemo(
() => sortThreadsPinnedFirst(threads.filter((thread) => !isHiddenFromRecentChats(thread))),
[threads],
@@ -484,11 +479,7 @@ function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
) : (
filteredThreads.map((thread) => {
const isBoot = isBootstrapThread(thread);
// 正常记录依据自身保存的 agent_id 跳转;早期记录可能尚未写入
// agent_id,此时保留当前智能体上下文,避免从智能体页误落到通用问答。
const href = isBoot
? bootstrapResumePath(thread.thread_id)
: pathOf(thread, { agent_id: activeAgentId });
const href = isBoot ? bootstrapResumePath(thread.thread_id) : pathOf(thread);
const isActive = href === pathname;
const pinned = isThreadPinned(thread);
const favorited = isThreadFavorite(thread);
@@ -7,7 +7,8 @@
* 共享的 WorkspaceNavChatList / FrequentAgentsList / RecentChatList 里塞会越来越难
* 维护,故单独成一个组件,只在 `/page/workspace/chats*` 的全量布局下挂载,互不影响。
*
* 三块结构(自上而下):
* 可挂在 workspace / strategy 两个普通问答路由下;导航目标由 RouteBaseProvider
* 决定。三块结构(自上而下):
* 1. 「新对话」胶囊按钮 + 收起整列的圆形按钮;
* 2. 「常用智能体」—— 彩色图标卡(名称 + 两行简介,命中为选中态:主色描边 + 右侧主色条);
* 3. 「最近的对话」—— 计数徽标 + 折叠、历史搜索框、会话列表(悬浮「更多」菜单:
@@ -85,6 +86,7 @@ import {
titleOfThread,
} from "@/core/threads/utils";
import { buildFullChatNavigationPath } from "@/core/page-layout/chats-iframe-route";
import { useRouteBase } from "@/core/route-base";
import { env } from "@/env";
import { desensitize } from "@/lib/desensitize";
import { isIMEComposing } from "@/lib/ime";
@@ -93,11 +95,6 @@ import { cn } from "@/lib/utils";
import { agentColor } from "./agent-icon-color";
import { SidebarCollapseButton } from "./workspace-header";
const NEW_CHAT_PATH = "/page/workspace/chats/new";
/** 「更多」常用智能体跳转目标:智能体管理页。 */
const MORE_AGENTS_HREF = "/page/workspace/agents";
/** 「更多最近对话」跳转目标。 */
const MORE_CHATS_HREF = "/page/workspace/chats";
/** 常用智能体最多展示个数。 */
const MAX_FREQUENT = 4;
@@ -142,6 +139,7 @@ function SectionLabel({
export function GeneralQaSidebar() {
const { t } = useI18n();
const routeBase = useRouteBase();
// 智能体图标配色分主题:深蓝主题用规范蓝-青系,其它(浅色)保持原高彩度色,互不影响。
const { theme } = useTheme();
const isDark = theme === "dark";
@@ -188,9 +186,14 @@ export function GeneralQaSidebar() {
const searchParams = useSearchParams();
const activeAgentId = searchParams.get("agent");
const currentSearch = searchParams.toString();
const newChatHref = buildFullChatNavigationPath(NEW_CHAT_PATH, currentSearch);
const moreAgentsHref = buildFullChatNavigationPath(MORE_AGENTS_HREF, currentSearch);
const moreChatsHref = buildFullChatNavigationPath(MORE_CHATS_HREF, currentSearch);
const chatsBasePath = `${routeBase}/chats`;
const newChatPath = `${chatsBasePath}/new`;
const newChatHref = buildFullChatNavigationPath(newChatPath, currentSearch);
const moreAgentsHref = buildFullChatNavigationPath(
`${routeBase}/agents`,
currentSearch,
);
const moreChatsHref = buildFullChatNavigationPath(chatsBasePath, currentSearch);
// 点击常用智能体 = 在通用问答右侧**原地嵌入**该智能体的对话页:停留在 /chats/new,
// 仅给 URL 加 ?agent=<id>,由 NewChatContent 把右侧问答区换成 AgentChatPage。
@@ -199,19 +202,18 @@ export function GeneralQaSidebar() {
(agentId: string) => {
navigate(
buildFullChatNavigationPath(
`${NEW_CHAT_PATH}?agent=${encodeURIComponent(agentId)}`,
routeBase === "/page/workspace"
? `${newChatPath}?agent=${encodeURIComponent(agentId)}`
: `${routeBase}/agents/${encodeURIComponent(agentId)}/chats/new`,
currentSearch,
),
);
},
[currentSearch, navigate],
[currentSearch, navigate, newChatPath, routeBase],
);
// ── 最近的对话取数 + 搜索 ──
const { thread_id: threadIdFromPath, agent_id: agentIdFromPath } = useParams<{
thread_id: string;
agent_id?: string;
}>();
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
const { data: threads = [] } = useThreads();
const { mutate: deleteThread } = useDeleteThread();
const { mutate: renameThread } = useRenameThread();
@@ -224,6 +226,14 @@ export function GeneralQaSidebar() {
const [renameValue, setRenameValue] = useState("");
const [shareThreadId, setShareThreadId] = useState<string | null>(null);
const pathForThread = useCallback(
(thread: AgentThread) =>
isBootstrapThread(thread)
? listPathOfThread(thread)
: pathOfThread(thread, undefined, routeBase),
[routeBase],
);
const visibleThreads = useMemo(
() =>
// 智能体创建(bootstrap)会话也展示(带机器人图标、点击回创建页);
@@ -254,21 +264,22 @@ export function GeneralQaSidebar() {
deleteThread({ threadId });
if (threadId === threadIdFromPath) {
const threadIndex = threads.findIndex((th) => th.thread_id === threadId);
let nextThreadPath = pathOfThread("new", { agent_id: agentIdFromPath });
let nextThreadPath = newChatPath;
if (threadIndex > -1) {
if (threads[threadIndex + 1]) {
nextThreadPath = listPathOfThread(threads[threadIndex + 1]!);
nextThreadPath = pathForThread(threads[threadIndex + 1]!);
} else if (threads[threadIndex - 1]) {
nextThreadPath = listPathOfThread(threads[threadIndex - 1]!);
nextThreadPath = pathForThread(threads[threadIndex - 1]!);
}
}
void router.push(buildFullChatNavigationPath(nextThreadPath, currentSearch));
}
},
[
agentIdFromPath,
currentSearch,
deleteThread,
newChatPath,
pathForThread,
router,
threadIdFromPath,
threads,
@@ -467,7 +478,7 @@ export function GeneralQaSidebar() {
) : (
filteredThreads.map((thread) => {
const href = buildFullChatNavigationPath(
listPathOfThread(thread),
pathForThread(thread),
currentSearch,
);
const isActive = href === pathname;
@@ -34,7 +34,9 @@ export function WorkspaceSidebar({
// 通用问答(全量模式)左栏:新建回答 → 常用智能体 → 聊天记录(需求 0616 #1)。
// 仅在通用问答(/page/workspace/chats*)下启用三块式布局;点击常用智能体后是
// 「原地嵌入」(停留在 /chats/new?agent=...,仅右侧问答区换成智能体对话),左侧栏不变。
const isGeneralQa = pathname.startsWith("/page/workspace/chats");
const isGeneralQa =
pathname.startsWith("/page/workspace/chats") ||
pathname.startsWith("/page/strategy/chats");
if (forceCollapsed) {
return (