diff --git a/= b/= new file mode 100644 index 00000000..e69de29b diff --git a/frontend-web/.env.development b/frontend-web/.env.development index ac6db962..3837c075 100644 --- a/frontend-web/.env.development +++ b/frontend-web/.env.development @@ -5,7 +5,7 @@ VITE_USE_REAL_DATA=true # 任务详情(登录深链 cop-task-detail)数据源:默认真实接口;改成 true 走本地假数据(consumer 外网不可达时调试用)。 -VITE_MOCK_COP_TASK_DETAIL=flase +VITE_MOCK_COP_TASK_DETAIL=true # 研判情报报告(大屏绘制智能体深链 selectSqReport)数据源:true 走本地样例假数据(真实接口未就绪时调试用);联调通过后置 false。 VITE_MOCK_SQ_REPORT=true diff --git a/frontend-web/public/runtime-config.js b/frontend-web/public/runtime-config.js index f44dd78f..e6f21405 100644 --- a/frontend-web/public/runtime-config.js +++ b/frontend-web/public/runtime-config.js @@ -85,8 +85,8 @@ window.__CM_RUNTIME_CONFIG__ = { // 登录深链任务详情是否使用本地 mock:false=真实接口;true=本地 mock。 VITE_MOCK_COP_TASK_DETAIL: "false", - // 大屏绘制 selectSqReport 是否使用本地 mock:false=真实接口;true=本地 mock。 - VITE_MOCK_SQ_REPORT: "false", + // 大屏绘制 selectSqReport 是否使用本地 mock:不在运行时强制覆盖, + // 由各环境的 .env 文件决定(开发环境为 true,生产环境为 false)。 // 静态站点模式开关;一般部署业务系统时保持 false。 VITE_STATIC_WEBSITE_ONLY: "false", diff --git a/frontend-web/src/components/workspace/skill-display-sources.tsx b/frontend-web/src/components/workspace/skill-display-sources.tsx index 8fc2f344..3993f022 100644 --- a/frontend-web/src/components/workspace/skill-display-sources.tsx +++ b/frontend-web/src/components/workspace/skill-display-sources.tsx @@ -1,4 +1,4 @@ -import { ExternalLinkIcon } from "lucide-react"; +import { ExternalLinkIcon, FileTextIcon, LoaderCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { @@ -9,10 +9,12 @@ import { DialogTitle, } from "@/components/ui/dialog"; import type { RuntimeDisplaySource } from "@/core/skill-display/runtime"; +import { apiFetch } from "@/core/api/fetch-client"; +import { getBackendBaseURL } from "@/core/config"; import { useLocalSettings } from "@/core/settings"; import { useCitationDisplaySettings } from "@/core/system-settings/hooks"; import { cn } from "@/lib/utils"; -import { useState } from "react"; +import { useEffect, useState } from "react"; export function SkillDisplaySources({ highlightedReferenceId, @@ -51,6 +53,9 @@ export function SourceCard({ referenceTarget?: boolean; }) { const [open, setOpen] = useState(false); + const [context, setContext] = useState(null); + const [contextLoading, setContextLoading] = useState(false); + const [contextError, setContextError] = useState(null); const [settings] = useLocalSettings(); const { data: citationDisplay } = useCitationDisplaySettings(); const cleanupWords = @@ -73,6 +78,44 @@ export function SourceCard({ ].filter(Boolean); const isCitation = source.mode === "citation"; const showReferenceNumber = isCitation; + const previewTarget = parseWeKnoraPreviewUrl(source.url); + + useEffect(() => { + if (!open || !previewTarget) { + return; + } + let cancelled = false; + setContext(null); + setContextError(null); + setContextLoading(true); + const params = new URLSearchParams({ + knowledge_base_id: previewTarget.knowledgeBaseId, + chunk_id: previewTarget.chunkId, + }); + void apiFetch(`${getBackendBaseURL()}/api/weknora/source-context?${params}`) + .then(async (response) => { + const payload = (await response.json().catch(() => null)) as unknown; + if (!response.ok || !isWeKnoraSourceContextResponse(payload)) { + throw new Error(readPreviewError(payload, response.status)); + } + if (!cancelled) { + setContext(payload.data); + } + }) + .catch((error: unknown) => { + if (!cancelled) { + setContextError(error instanceof Error ? error.message : "无法加载原文上下文"); + } + }) + .finally(() => { + if (!cancelled) { + setContextLoading(false); + } + }); + return () => { + cancelled = true; + }; + }, [open, previewTarget?.chunkId, previewTarget?.knowledgeBaseId]); return ( <> @@ -114,7 +157,14 @@ export function SourceCard({ )} {source.url && ( - + previewTarget ? ( + + + 查看原文 + + ) : ( + + ) )} @@ -141,7 +191,7 @@ export function SourceCard({ )} - {source.url && ( + {source.url && !previewTarget && (