import { Sources } from '@ant-design/x'; import XMarkdown, { type ComponentProps } from '@ant-design/x-markdown'; import { Tooltip } from 'antd'; import React, { useMemo } from 'react'; import type { RetrieverResource } from '~/api/dify-chat'; import '../../styles/components/chat-with-llm/markdown.css'; interface MarkdownProps { content: string; className?: string; retrieverResources?: RetrieverResource[]; } /** * 引用索引组件 - 在文本中显示可点击的引用标记 */ const SourceRefComponent = React.memo(({ children, resources }: ComponentProps & { resources?: RetrieverResource[] }) => { const refNumber = parseInt(`${children}` || '0', 10); // 如果没有资源数据或引用号无效,只显示上标数字 if (!resources || resources.length === 0 || refNumber <= 0) { return [{children}]; } // 查找对应的资源 const resource = resources.find(r => r.position === refNumber); if (!resource) { return [{children}]; } // 构建引用项列表 - 显示完整内容 const items = resources.map((r) => ({ title: `${r.position}. ${r.document_name}`, key: r.position, description: r.content || '', })); return ( ); }); SourceRefComponent.displayName = 'SourceRefComponent'; /** * 引用来源面板组件 - 在消息下方显示所有引用(独立导出) */ export const SourcesPanel = React.memo(({ resources }: { resources: RetrieverResource[] }) => { if (!resources || resources.length === 0) { return null; } return (
引用来源
{resources.map((resource) => (
{resource.document_name}
{resource.content}
相关度: {(resource.score * 100).toFixed(0)}% 来源: {resource.dataset_name}
} placement="top" autoAdjustOverflow={true} color="rgba(0, 104, 74, 0.92)" classNames={{ root: 'source-tooltip-overlay' }} overlayStyle={{ maxWidth: 'calc(100vw - 32px)' }} >
{resource.position} {resource.document_name} {(resource.score * 100).toFixed(0)}%
))}
); }); SourcesPanel.displayName = 'SourcesPanel'; /** * Markdown 渲染组件 * 使用 @ant-design/x-markdown 进行 Markdown 解析,支持流式渲染 */ export default function Markdown({ content, className = '', retrieverResources }: MarkdownProps) { // 创建自定义的 sup 组件,传入引用资源 const customComponents = useMemo(() => { return { sup: (props: ComponentProps) => ( ), }; }, [retrieverResources]); if (!content) { return null; } return (
{content}
); }