|
|
@@ -10,11 +10,13 @@
|
|
10
|
10
|
* @module components/ChatPanel/MessageItem
|
|
11
|
11
|
*/
|
|
12
|
12
|
|
|
13
|
|
-import React, { memo, useCallback } from 'react';
|
|
14
|
|
-import { Typography, Card } from 'antd';
|
|
15
|
|
-import { FileTextOutlined, DownloadOutlined } from '@ant-design/icons';
|
|
|
13
|
+import React, { memo, useCallback, useState } from 'react';
|
|
|
14
|
+import { Typography, Card, message as antdMessage, Spin } from 'antd';
|
|
|
15
|
+import { FileTextOutlined, DownloadOutlined, LoadingOutlined } from '@ant-design/icons';
|
|
16
|
16
|
import { formatDate } from '../../utils/formatDate';
|
|
17
|
17
|
import type { ChatMessage } from '../../types/chat';
|
|
|
18
|
+import { useDocumentStore } from '../../stores/documentStore';
|
|
|
19
|
+import { useChatStore } from '../../stores/chatStore';
|
|
18
|
20
|
|
|
19
|
21
|
const { Text } = Typography;
|
|
20
|
22
|
|
|
|
@@ -56,6 +58,7 @@ const exportCardStyle: React.CSSProperties = {
|
|
56
|
58
|
marginTop: '8px',
|
|
57
|
59
|
cursor: 'pointer',
|
|
58
|
60
|
transition: 'all 0.2s',
|
|
|
61
|
+ position: 'relative',
|
|
59
|
62
|
};
|
|
60
|
63
|
|
|
61
|
64
|
const exportCardBodyStyle: React.CSSProperties = {
|
|
|
@@ -132,15 +135,45 @@ export interface MessageItemProps {
|
|
132
|
135
|
const MessageItem: React.FC<MessageItemProps> = memo(
|
|
133
|
136
|
({ message, onPreviewDocument, className }) => {
|
|
134
|
137
|
const { role, content, timestamp, exportRecord } = message;
|
|
|
138
|
+ const [isCreatingDocument, setIsCreatingDocument] = useState(false);
|
|
|
139
|
+ const createDocument = useDocumentStore((state) => state.createDocument);
|
|
|
140
|
+ const currentSessionId = useChatStore((state) => state.currentSessionId);
|
|
135
|
141
|
|
|
136
|
142
|
/**
|
|
137
|
143
|
* Handle preview document click
|
|
|
144
|
+ *
|
|
|
145
|
+ * This function needs to ensure the document exists in the document management
|
|
|
146
|
+ * system before opening the editor. The workflow returns an export record with
|
|
|
147
|
+ * a recordId and downloadUrl, but the document hasn't been imported yet.
|
|
|
148
|
+ *
|
|
|
149
|
+ * Steps:
|
|
|
150
|
+ * 1. Call POST /api/v1/documents to create/import the document
|
|
|
151
|
+ * 2. Use the returned documentId to open the editor
|
|
138
|
152
|
*/
|
|
139
|
|
- const handlePreviewClick = useCallback(() => {
|
|
140
|
|
- if (exportRecord && onPreviewDocument) {
|
|
141
|
|
- onPreviewDocument(exportRecord.documentId, exportRecord.fileName);
|
|
|
153
|
+ const handlePreviewClick = useCallback(async () => {
|
|
|
154
|
+ if (!exportRecord || !onPreviewDocument || isCreatingDocument) return;
|
|
|
155
|
+
|
|
|
156
|
+ try {
|
|
|
157
|
+ setIsCreatingDocument(true);
|
|
|
158
|
+
|
|
|
159
|
+ // Create/import document using the export record's download URL
|
|
|
160
|
+ // The backend will download the file and parse it into blocks
|
|
|
161
|
+ const response = await createDocument({
|
|
|
162
|
+ userId: 'default-user', // TODO: Get from auth context
|
|
|
163
|
+ fileUrl: exportRecord.downloadUrl,
|
|
|
164
|
+ sessionId: currentSessionId || 'default-session',
|
|
|
165
|
+ });
|
|
|
166
|
+
|
|
|
167
|
+ // Now we can open the preview with the document ID
|
|
|
168
|
+ onPreviewDocument(response.documentId, exportRecord.fileName);
|
|
|
169
|
+
|
|
|
170
|
+ } catch (error) {
|
|
|
171
|
+ console.error('Failed to create document:', error);
|
|
|
172
|
+ antdMessage.error('打开文档失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
|
|
173
|
+ } finally {
|
|
|
174
|
+ setIsCreatingDocument(false);
|
|
142
|
175
|
}
|
|
143
|
|
- }, [exportRecord, onPreviewDocument]);
|
|
|
176
|
+ }, [exportRecord, onPreviewDocument, isCreatingDocument, createDocument, currentSessionId]);
|
|
144
|
177
|
|
|
145
|
178
|
/**
|
|
146
|
179
|
* Handle download document click
|
|
|
@@ -226,6 +259,24 @@ const MessageItem: React.FC<MessageItemProps> = memo(
|
|
226
|
259
|
onClick={handlePreviewClick}
|
|
227
|
260
|
data-testid="export-record-card"
|
|
228
|
261
|
>
|
|
|
262
|
+ {isCreatingDocument && (
|
|
|
263
|
+ <div
|
|
|
264
|
+ style={{
|
|
|
265
|
+ position: 'absolute',
|
|
|
266
|
+ top: 0,
|
|
|
267
|
+ left: 0,
|
|
|
268
|
+ right: 0,
|
|
|
269
|
+ bottom: 0,
|
|
|
270
|
+ display: 'flex',
|
|
|
271
|
+ alignItems: 'center',
|
|
|
272
|
+ justifyContent: 'center',
|
|
|
273
|
+ backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
|
|
274
|
+ zIndex: 1,
|
|
|
275
|
+ }}
|
|
|
276
|
+ >
|
|
|
277
|
+ <Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} />
|
|
|
278
|
+ </div>
|
|
|
279
|
+ )}
|
|
229
|
280
|
<div style={exportCardHeaderStyle}>
|
|
230
|
281
|
<FileTextOutlined style={exportCardIconStyle} />
|
|
231
|
282
|
<div style={exportCardTitleStyle}>{exportRecord.fileName}</div>
|