From 7a2a481f44c5f7532da7735c7fc3a0760fa13be2 Mon Sep 17 00:00:00 2001 From: PingChuan <1259732256@qq.com> Date: Fri, 5 Dec 2025 23:36:12 +0800 Subject: [PATCH] =?UTF-8?q?refertor=EF=BC=9A=E4=BD=BF=E7=94=A8antdX?= =?UTF-8?q?=E9=87=8D=E6=9E=84dify=E8=81=8A=E5=A4=A9=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=8C=E5=88=B0=E5=A4=84=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + app/api/dify-chat/index.ts | 1 + app/api/dify-chat/types.ts | 39 ++++ app/components/dify-chat/chat-message.tsx | 17 +- app/components/dify-chat/index.tsx | 3 +- app/components/dify-chat/markdown.tsx | 187 +++++++++++------ app/config/api-config.ts | 20 +- app/hooks/use-chat-message.ts | 23 +- .../components/chat-with-llm/markdown.css | 197 ++++++++++++++++++ pnpm-lock.yaml | 51 +++++ vite.config.ts | 21 +- 11 files changed, 469 insertions(+), 91 deletions(-) diff --git a/.gitignore b/.gitignore index 538486e..ac18449 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ docreview-frontend-deploy.tar.gz .database/ .auth_doc/ typecheck_result.txt +*.DS_Store diff --git a/app/api/dify-chat/index.ts b/app/api/dify-chat/index.ts index f690c41..6edca65 100644 --- a/app/api/dify-chat/index.ts +++ b/app/api/dify-chat/index.ts @@ -30,6 +30,7 @@ export type { MessageMore, Feedbacktype, ThoughtItem, + RetrieverResource, // 文件类型 VisionFile, diff --git a/app/api/dify-chat/types.ts b/app/api/dify-chat/types.ts index 8fa79de..59c936f 100644 --- a/app/api/dify-chat/types.ts +++ b/app/api/dify-chat/types.ts @@ -28,6 +28,29 @@ export interface ConversationItem { introduction?: string; } +/** + * 检索资源类型 - 来自 RAG 的引用内容 + */ +export interface RetrieverResource { + position: number; + dataset_id: string; + dataset_name: string; + document_id: string; + document_name: string; + data_source_type: string; + segment_id: string; + retriever_from: string; + score: number; + hit_count: number | null; + word_count: number | null; + segment_position: number | null; + index_node_hash: string | null; + content: string; + page: number | null; + doc_metadata: Record | null; + title: string | null; +} + /** * 聊天消息类型 */ @@ -45,6 +68,7 @@ export interface ChatItem { useCurrentUserAvatar?: boolean; isOpeningStatement?: boolean; suggestedQuestions?: string[]; + retriever_resources?: RetrieverResource[]; } /** @@ -336,6 +360,21 @@ export interface MessageEnd { task_id: string; conversation_id: string; message_id: string; + id?: string; + created_at?: number; + metadata?: { + annotation_reply?: any; + retriever_resources?: RetrieverResource[]; + usage?: { + prompt_tokens: number; + completion_tokens: number; + total_tokens: number; + total_price: string; + currency: string; + latency: number; + }; + }; + files?: any[]; } /** diff --git a/app/components/dify-chat/chat-message.tsx b/app/components/dify-chat/chat-message.tsx index a481c50..8e5207d 100644 --- a/app/components/dify-chat/chat-message.tsx +++ b/app/components/dify-chat/chat-message.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import type { ChatItem, Feedbacktype } from '~/api/dify-chat'; import '../../styles/components/chat-with-llm/chat-message.css'; import { parseMessageContent } from '../../utils/message-parser'; -import Markdown from './markdown'; +import Markdown, { SourcesPanel } from './markdown'; import ThinkingBlock from './thinking-block'; import ThoughtProcess from './thought-process'; @@ -27,7 +27,7 @@ export default function ChatMessage({ message.feedback?.rating || null ); - const { id, content, isAnswer, agent_thoughts, message_files, isOpeningStatement, suggestedQuestions, more } = message; + const { id, content, isAnswer, agent_thoughts, message_files, isOpeningStatement, suggestedQuestions, more, retriever_resources } = message; const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0; /** @@ -70,7 +70,10 @@ export default function ChatMessage({
{thought.thought && (
- +
)} {thought.tool && ( @@ -98,7 +101,7 @@ export default function ChatMessage({ {/* 实际回复内容 */} {parsed.response && (
- +
)}
@@ -184,6 +187,12 @@ export default function ChatMessage({ + {/* 引用来源面板 - 放在气泡外面 */} + {isAnswer && retriever_resources && retriever_resources.length > 0 && ( +
+ +
+ )} ); } \ No newline at end of file diff --git a/app/components/dify-chat/index.tsx b/app/components/dify-chat/index.tsx index d692aee..1fa135b 100644 --- a/app/components/dify-chat/index.tsx +++ b/app/components/dify-chat/index.tsx @@ -227,7 +227,7 @@ export default function Chat() { message_files: item.message_files?.filter((file: any) => file.belongs_to === 'user') || [], }); - // 添加AI回答 + // 添加AI回答(包含检索资源) newChatList.push({ id: item.id, content: item.answer, @@ -235,6 +235,7 @@ export default function Chat() { feedback: item.feedback, isAnswer: true, message_files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [], + retriever_resources: item.retriever_resources || [], }); }); diff --git a/app/components/dify-chat/markdown.tsx b/app/components/dify-chat/markdown.tsx index 3eb51af..0b01df8 100644 --- a/app/components/dify-chat/markdown.tsx +++ b/app/components/dify-chat/markdown.tsx @@ -1,89 +1,138 @@ -import React from 'react'; -import ReactMarkdown from 'react-markdown'; -import 'katex/dist/katex.min.css'; -import RemarkMath from 'remark-math'; -import RemarkBreaks from 'remark-breaks'; -import RehypeKatex from 'rehype-katex'; -import RemarkGfm from 'remark-gfm'; +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[]; } /** - * Markdown 渲染组件 - * 使用 react-markdown 库进行标准 Markdown 解析,支持流式渲染 + * 引用索引组件 - 在文本中显示可点击的引用标记 */ -export default function Markdown({ content, className = '' }: MarkdownProps) { - // console.log('🎨 [Markdown] 渲染组件:', { - // contentLength: content?.length || 0, - // contentPreview: content?.substring(0, 100) + (content && content.length > 100 ? '...' : ''), - // className, - // hasContent: !!content - // }); +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="topLeft" + autoAdjustOverflow={false} + color="rgba(0, 104, 74, 0.92)" + classNames={{ root: 'source-tooltip-overlay' }} + > +
+ {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) { - console.log('⚠️ [Markdown] 内容为空,返回null'); return null; } return (
- - - {String(children).replace(/\n$/, '')} - - - ); - } else { - // 内联代码 - return ( - - {children} - - ); - } - }, - }} + {content} - +
); -} \ No newline at end of file +} diff --git a/app/config/api-config.ts b/app/config/api-config.ts index 28c04c3..2e2d088 100644 --- a/app/config/api-config.ts +++ b/app/config/api-config.ts @@ -44,9 +44,9 @@ const portConfigs: Record> = { // 主要 // 梅州 '51703': { - baseUrl: 'http://172.16.0.78:8073', - documentUrl: 'http://172.16.0.78:8073/docauditai/', - uploadUrl: 'http://172.16.0.78:8073/admin/documents', + baseUrl: 'http://172.16.0.56:8073', + documentUrl: 'http://172.16.0.56:8073/docauditai/', + uploadUrl: 'http://172.16.0.56:8073/admin/documents', collaboraUrl: 'http://172.16.0.81:9980', appUrl: 'http://172.16.0.34:51703', @@ -121,15 +121,15 @@ const portConfigs: Record> = { const configs: Record = { // 开发环境 development: { - baseUrl: 'http://172.16.0.58:8073', // FastAPI后端(包含/dify代理) - documentUrl: 'http://172.16.0.58:8073/docauditai/', - uploadUrl: 'http://172.16.0.58:8073/admin/documents', + baseUrl: 'http://172.16.0.56:8073', // FastAPI后端(包含/dify代理) + documentUrl: 'http://172.16.0.56:8073/docauditai/', + uploadUrl: 'http://172.16.0.56:8073/admin/documents', // baseUrl: 'http://172.16.0.55:8073', // FastAPI后端(包含/dify代理) // documentUrl: 'http://172.16.0.55:8073/docauditai/', // uploadUrl: 'http://172.16.0.55:8073/admin/documents', collaboraUrl: 'http://172.16.0.81:9980', - appUrl: 'http://172.16.0.78:51703', + appUrl: 'http://172.16.0.56:51703', oauth: { serverUrl: 'http://10.79.112.85', // IDaaS服务器地址 @@ -146,9 +146,9 @@ const configs: Record = { // 测试环境 testing: { - baseUrl: 'http://nas.7bm.co:8873', // FastAPI后端(包含/dify代理) - documentUrl: 'http://nas.7bm.co:8873/docauditai/', - uploadUrl: 'http://nas.7bm.co:8873/admin/documents', + baseUrl: 'http://172.16.0.56:8073', // FastAPI后端(包含/dify代理) + documentUrl: 'http://172.16.0.56:8073/docauditai/', + uploadUrl: 'http://172.16.0.56:8073/admin/documents', collaboraUrl: 'http://10.79.97.17:9980', appUrl: 'http://10.79.97.17:51703', oauth: { diff --git a/app/hooks/use-chat-message.ts b/app/hooks/use-chat-message.ts index 7519358..4bfab29 100644 --- a/app/hooks/use-chat-message.ts +++ b/app/hooks/use-chat-message.ts @@ -411,8 +411,27 @@ export default function useChatMessage({ }, onMessageEnd: (messageEnd: MessageEnd) => { - // 处理消息结束事件 - // console.log('Message ended:', messageEnd); + // 处理消息结束事件 - 获取检索资源 + console.log('📋 [useChatMessage] 消息结束事件:', { + messageId: messageEnd.message_id, + hasMetadata: !!messageEnd.metadata, + hasRetrieverResources: !!messageEnd.metadata?.retriever_resources, + resourceCount: messageEnd.metadata?.retriever_resources?.length || 0 + }); + + // 如果有检索资源,更新响应项 + if (messageEnd.metadata?.retriever_resources && messageEnd.metadata.retriever_resources.length > 0) { + responseItem.retriever_resources = messageEnd.metadata.retriever_resources; + + // 更新聊天列表 + updateCurrentQA({ + responseItem: { ...responseItem }, + questionId, + placeholderAnswerId, + questionItem, + originalResponseId, + }); + } }, onMessageReplace: (messageReplace: MessageReplace) => { diff --git a/app/styles/components/chat-with-llm/markdown.css b/app/styles/components/chat-with-llm/markdown.css index 9e7e350..8659252 100644 --- a/app/styles/components/chat-with-llm/markdown.css +++ b/app/styles/components/chat-with-llm/markdown.css @@ -298,4 +298,201 @@ .message-card.assistant .markdown-content { background-color: #a4e2ad; max-width: 65vh; +} + +/* ============================================================================ */ +/* 引用来源样式 - 绿白主题配色 */ +/* ============================================================================ */ + +/* 普通引用上标 */ +.source-ref-plain { + color: #00684a; + font-size: 0.75em; + vertical-align: super; + cursor: default; +} + +/* 引用来源面板外层容器 */ +.sources-panel-wrapper { + padding-left: 8px; + margin-top: 4px; + margin-bottom: 8px; +} + +/* 引用来源面板 - 更紧凑的设计 */ +.sources-panel { + padding: 6px 10px; + background: transparent; + border-radius: 4px; +} + +.sources-panel-header { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 11px; + font-weight: 500; + color: #6b7280; + margin-right: 8px; +} + +.sources-panel-header i { + font-size: 12px; + color: #00684a; +} + +.sources-panel-list { + display: inline-flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} + +/* 引用项 - 更小更紧凑 */ +.source-item { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + background: #fff; + border: 1px solid #d1e7dd; + border-radius: 12px; + font-size: 11px; + cursor: pointer; + transition: all 0.2s ease; + max-width: 200px; +} + +.source-item:hover { + border-color: #00684a; + background: #f0fdf4; +} + +/* 引用编号 - 使用绿色主题 */ +.source-item-number { + display: inline-flex; + align-items: center; + justify-content: center; + width: 14px; + height: 14px; + background: #00684a; + color: #fff; + border-radius: 50%; + font-size: 9px; + font-weight: 600; + flex-shrink: 0; +} + +.source-item-name { + color: #374151; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + min-width: 0; + font-size: 11px; +} + +/* 得分显示 - 使用绿色渐变 */ +.source-item-score { + flex-shrink: 0; + font-size: 10px; + color: #00684a; + font-weight: 500; +} + +/* 引用来源 Tooltip 样式 - 绿色半透明主题 */ +.source-tooltip-overlay { + max-width: 480px !important; +} + +.source-tooltip-overlay .ant-tooltip-inner { + padding: 12px 14px; + border-radius: 8px; + box-shadow: 0 4px 16px rgba(0, 104, 74, 0.25); +} + +.source-tooltip { + max-width: 100%; +} + +.source-tooltip-header { + margin-bottom: 10px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + font-size: 13px; + font-weight: 600; + color: #fff; +} + +.source-tooltip-content { + font-size: 12px; + line-height: 1.6; + color: rgba(255, 255, 255, 0.95); + margin-bottom: 10px; + white-space: pre-wrap; + word-break: break-word; + max-height: 300px; + overflow-y: auto; + padding-right: 4px; +} + +/* 自定义滚动条样式 */ +.source-tooltip-content::-webkit-scrollbar { + width: 5px; +} + +.source-tooltip-content::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); + border-radius: 3px; +} + +.source-tooltip-content::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.35); + border-radius: 3px; +} + +.source-tooltip-content::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.5); +} + +.source-tooltip-meta { + font-size: 11px; + color: rgba(255, 255, 255, 0.7); + padding-top: 8px; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Ant Design X Sources 组件样式覆盖 - 使用绿色主题 */ +.markdown-content .ant-x-sources { + display: inline; +} + +.markdown-content .ant-x-sources-tag { + font-size: 11px; + padding: 0 3px; + cursor: pointer; + color: #00684a; + background: transparent; + border: none; +} + +.markdown-content .ant-x-sources-tag:hover { + color: #005a3f; + text-decoration: underline; +} + +/* 响应式调整 */ +@media (max-width: 768px) { + .sources-panel-wrapper { + padding-left: 4px; + } + + .sources-panel-list { + flex-wrap: wrap; + } + + .source-item { + max-width: 180px; + } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7fcf91..0e4d2fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: docx-preview: specifier: ^0.3.5 version: 0.3.5 + docxtemplater: + specifier: ^3.67.5 + version: 3.67.5 dotenv: specifier: ^16.5.0 version: 16.6.1 @@ -95,6 +98,9 @@ importers: pg: specifier: ^8.14.1 version: 8.16.3 + pizzip: + specifier: ^3.2.0 + version: 3.2.0 pm2: specifier: ^6.0.8 version: 6.0.8 @@ -1431,56 +1437,67 @@ packages: resolution: {integrity: sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.44.1': resolution: {integrity: sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.44.1': resolution: {integrity: sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.44.1': resolution: {integrity: sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.44.1': resolution: {integrity: sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.44.1': resolution: {integrity: sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.44.1': resolution: {integrity: sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.44.1': resolution: {integrity: sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.44.1': resolution: {integrity: sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.44.1': resolution: {integrity: sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.44.1': resolution: {integrity: sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.44.1': resolution: {integrity: sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==} @@ -1802,41 +1819,49 @@ packages: resolution: {integrity: sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.9.2': resolution: {integrity: sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.9.2': resolution: {integrity: sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.9.2': resolution: {integrity: sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.9.2': resolution: {integrity: sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.9.2': resolution: {integrity: sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.9.2': resolution: {integrity: sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.9.2': resolution: {integrity: sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.9.2': resolution: {integrity: sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==} @@ -1877,6 +1902,10 @@ packages: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} + '@xmldom/xmldom@0.9.8': + resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} + engines: {node: '>=14.6'} + '@zxing/text-encoding@0.9.0': resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} @@ -2698,6 +2727,10 @@ packages: docx-preview@0.3.5: resolution: {integrity: sha512-nod1jG5PkvzDIiZAcgAY4gSFQzgmAAChcuZH4Hj9dj7oCzscY3Hn8NfbUv7X7Jk4xL1lfKO113JLDhWKOt6fYw==} + docxtemplater@3.67.5: + resolution: {integrity: sha512-Jnh9rdMf5sDmrfONs3nVDhZwVFxLNdP3RVHkqLQYA6eZLkWA+kx5aYy0cVmEqJcVIaFYf5JJEFdClD7fn6ZUng==} + engines: {node: '>=0.10'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -4549,6 +4582,9 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4692,6 +4728,9 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pizzip@3.2.0: + resolution: {integrity: sha512-X4NPNICxCfIK8VYhF6wbksn81vTiziyLbvKuORVAmolvnUzl1A1xmz9DAWKxPRq9lZg84pJOOAMq3OE61bD8IQ==} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -7859,6 +7898,8 @@ snapshots: '@xmldom/xmldom@0.8.10': {} + '@xmldom/xmldom@0.9.8': {} + '@zxing/text-encoding@0.9.0': optional: true @@ -8756,6 +8797,10 @@ snapshots: dependencies: jszip: 3.10.1 + docxtemplater@3.67.5: + dependencies: + '@xmldom/xmldom': 0.9.8 + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -11385,6 +11430,8 @@ snapshots: pako@1.0.11: {} + pako@2.1.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -11530,6 +11577,10 @@ snapshots: pirates@4.0.7: {} + pizzip@3.2.0: + dependencies: + pako: 2.1.0 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 diff --git a/vite.config.ts b/vite.config.ts index e4062cc..484a084 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -93,9 +93,20 @@ export default defineConfig({ '@ant-design/x', ], }, - // SSR 配置 - 排除只能在客户端运行的包 - // ssr: { - // noExternal: [], - // external: ['@monaco-editor/react', 'monaco-editor'] - // }, + // SSR 配置 - 解决 antd 6.0 和 @ant-design/x 的 ESM 模块解析问题 + ssr: { + // 使用正则匹配所有 antd 和 rc-* 相关包 + noExternal: [/^antd/, /^@ant-design/, /^rc-/, /^@rc-component/], + // 优化 SSR 依赖预构建 + optimizeDeps: { + include: ['antd', '@ant-design/x', '@ant-design/x-markdown', '@ant-design/icons'], + }, + }, + // 解决 antd ESM 导入问题 + resolve: { + alias: [ + // 将 antd/lib 重定向到 antd/es + { find: /^antd\/lib\/(.*)/, replacement: 'antd/es/$1' }, + ], + }, });