@gientech/modual 1.2.8 → 1.2.9-fix

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +593 -79
  2. package/USAGE.md +56 -0
  3. package/dist/README.md +593 -79
  4. package/dist/assets/GientechStreamReader-C21-q_Qv.js +449 -0
  5. package/dist/assets/chevron-down-DjLtKwcs.js +280 -0
  6. package/dist/assets/databse.svg +6 -0
  7. package/dist/assets/graph.svg +4 -0
  8. package/dist/assets/homeBg.png +0 -0
  9. package/dist/assets/index-BMz4lcjQ.js +1 -0
  10. package/dist/assets/index-C3Viu8Oj.js +1 -0
  11. package/dist/assets/index-C9GlPyHu.js +13 -0
  12. package/dist/assets/index-CRbX3ZA1.js +1 -0
  13. package/dist/assets/index-CTwzi_v2.js +21 -0
  14. package/dist/assets/index-DQlLDleQ.js +11 -0
  15. package/dist/assets/index-DRU1P9R0.js +1150 -0
  16. package/dist/assets/index-Dqej68NT.js +585 -0
  17. package/dist/assets/index-ECprhahs.js +157 -0
  18. package/dist/assets/index-i7qcZOwY.js +1088 -0
  19. package/dist/assets/knowledge.svg +4 -0
  20. package/dist/assets/left.jpg +0 -0
  21. package/dist/assets/logoImg.png +0 -0
  22. package/dist/assets/{plus-omCUN0e3.js → plus-CvJRSbOe.js} +1 -1
  23. package/dist/assets/sensitive.svg +5 -0
  24. package/dist/assets/style.css +1 -1
  25. package/dist/assets/style3.css +1 -1
  26. package/dist/assets/worker-BbpylX7l.js +13 -0
  27. package/dist/assets/{x-vPcWt3fC.js → x-DKPeLdlu.js} +1 -1
  28. package/dist/assistantConfig.d.ts +31 -0
  29. package/dist/assistantConfig.js +1 -0
  30. package/dist/chat.d.ts +25 -1
  31. package/dist/chat.js +563 -369
  32. package/dist/database.js +2 -2
  33. package/dist/databaseId.js +1 -11
  34. package/dist/databaseTable.js +2 -2
  35. package/dist/index.d.ts +85 -0
  36. package/dist/index.js +1 -0
  37. package/dist/modelManage.js +1 -1
  38. package/dist/package.json +13 -1
  39. package/dist/sensitive.js +1 -1
  40. package/dist/streamFilesReader.d.ts +3 -0
  41. package/dist/streamFilesReader.js +1 -442
  42. package/doc_assets//346/226/271/346/241/210//344/274/230/345/214/226/346/226/271/346/241/210-/345/244/232/344/274/232/350/257/235SSE/350/277/236/346/216/245/347/256/241/347/220/206.md +504 -0
  43. package/package.json +125 -99
  44. package/package.json.demo-backup +109 -0
  45. package/scripts/README.md +133 -133
  46. package/scripts/build-demo.js +88 -88
  47. package/scripts/demo-selector.js +216 -216
  48. package/scripts/preview-demo.js +130 -130
  49. package/scripts/run-demo.bat +34 -34
  50. package/src/assets/img/close.png +0 -0
  51. package/src/assets/img/database.png +0 -0
  52. package/src/assets/img/downLoad.png +0 -0
  53. package/src/assets/img/graphIcon.png +0 -0
  54. package/src/assets/img/pdf.png +0 -0
  55. package/src/assets/img/singleQa.png +0 -0
  56. package/src/assets/img/webSearch.png +0 -0
  57. package/src/examples/ConversationAssistantPage/index.tsx +37 -0
  58. package/src/examples/Demo/index.tsx +12 -0
  59. package/src/examples/chat/components/DrawerGraphPreview.tsx +78 -0
  60. package/src/examples/chat/index.tsx +112 -99
  61. package/src/examples/chat/logo03.png +0 -0
  62. package/src/examples/gientechStreamFilesReader/index.tsx +4 -69
  63. package/src/lib_enter.ts +11 -6
  64. package/src/modules/assistantConfig/assets/databse.svg +6 -0
  65. package/src/modules/assistantConfig/assets/empty.png +0 -0
  66. package/src/modules/assistantConfig/assets/graph.svg +4 -0
  67. package/src/modules/assistantConfig/assets/knowledge.svg +4 -0
  68. package/src/modules/assistantConfig/assets/sensitive.svg +5 -0
  69. package/src/modules/assistantConfig/components/Database.tsx +144 -0
  70. package/src/modules/assistantConfig/components/Graph.tsx +156 -0
  71. package/src/modules/assistantConfig/components/Knowledge.tsx +266 -0
  72. package/src/modules/assistantConfig/components/NotFoundContent.tsx +21 -0
  73. package/src/modules/assistantConfig/components/Paragraph.tsx +51 -0
  74. package/src/modules/assistantConfig/components/ParamsItem.tsx +39 -0
  75. package/src/modules/assistantConfig/components/ResourceBinderItem.tsx +132 -0
  76. package/src/modules/assistantConfig/components/SearchableSelector.tsx +500 -0
  77. package/src/modules/assistantConfig/components/Sensitive.tsx +179 -0
  78. package/src/modules/assistantConfig/components/SliderInput.tsx +65 -0
  79. package/src/modules/assistantConfig/constants.tsx +74 -0
  80. package/src/modules/assistantConfig/index.tsx +700 -0
  81. package/src/modules/assistantConfig/server.ts +262 -0
  82. package/src/modules/chat/Conversations/List.tsx +76 -9
  83. package/src/modules/chat/Conversations/index.tsx +37 -19
  84. package/src/modules/chat/ReferenceBar.tsx +592 -0
  85. package/src/modules/chat/constants.tsx +29 -6
  86. package/src/modules/chat/data.txt +82 -0
  87. package/src/modules/chat/index.tsx +357 -113
  88. package/src/modules/chat/referenceCom/DeleteModal.tsx +75 -0
  89. package/src/modules/chat/referenceCom/DrawerContent.tsx +136 -0
  90. package/src/modules/chat/referenceCom/DrawerDatabase.tsx +110 -0
  91. package/src/modules/chat/referenceCom/DrawerGraphPreview.tsx +86 -0
  92. package/src/modules/chat/referenceCom/DrawerPreview.tsx +73 -0
  93. package/src/modules/chat/referenceCom/DrawerTitle.tsx +26 -0
  94. package/src/modules/chat/referenceCom/RenameModal.tsx +86 -0
  95. package/src/modules/chat/referenceCom/TagCom.tsx +30 -0
  96. package/src/modules/chat/style.less +3 -0
  97. package/src/modules/chat/utils/index.ts +326 -0
  98. package/src/modules/database/CreateModal.tsx +1 -1
  99. package/src/modules/headlessChat/index.tsx +1 -3
  100. package/src/modules/nodegraph/index.tsx +1 -0
  101. package/src/modules/search/components/ResultContent.tsx +2 -2
  102. package/src/modules/streamFilesReader/GientechStreamReader.tsx +436 -367
  103. package/src/modules/streamFilesReader/index.tsx +1 -1
  104. package/src/utils/gientechCommon/components/AppLoading.tsx +10 -10
  105. package/src/utils/gientechCommon/components/Messages/GientechNewChatWelcome.tsx +312 -27
  106. package/src/utils/gientechCommon/hooks/AichatUseController.tsx +84 -6
  107. package/src/utils/testconfigs/index.ts +7 -1
  108. package/stats.html +1 -1
  109. package/vite.config.ts +69 -20
  110. package/dist/assets/_commonjsHelpers-gnU0ypJ3.js +0 -1
  111. package/dist/assets/circle-alert-g2Y6zAjt.js +0 -6
  112. package/dist/assets/index-97TKgPKE.js +0 -1284
  113. package/dist/assets/index-CEK88UzR.js +0 -26
  114. package/dist/assets/index-DIm7RgkM.js +0 -1709
  115. package/dist/assets/styled-components.browser.esm-DPkS13KC.js +0 -2
@@ -1,11 +1,11 @@
1
1
  import React, { useState, useCallback, useEffect, useRef, useMemo } from 'react';
2
2
  import styled from 'styled-components';
3
3
  import axios from 'axios';
4
- import {Fviewer,parseFile} from '@mxmweb/fviewer';
4
+ import { Fviewer, parseFile, registerPDFWorker } from '@mxmweb/fviewer';
5
5
  import type { ParseResult } from '@mxmweb/fviewer';
6
6
  import Header from './components/Header';
7
-
8
- import type { Annotation, ToolsConfig } from '@mxmweb/fviewer';
7
+ export { registerPDFWorker };
8
+ import type { Annotation, ToolsConfig } from '@mxmweb/fviewer';
9
9
 
10
10
  import { Styles } from '@mxmweb/zui';
11
11
  // Stream接口参数类型
@@ -104,8 +104,12 @@ const ReaderContainer = styled.div<{ $theme: Styles }>`
104
104
 
105
105
  /* 添加动画样式 */
106
106
  @keyframes spin {
107
- 0% { transform: rotate(0deg); }
108
- 100% { transform: rotate(360deg); }
107
+ 0% {
108
+ transform: rotate(0deg);
109
+ }
110
+ 100% {
111
+ transform: rotate(360deg);
112
+ }
109
113
  }
110
114
  `;
111
115
 
@@ -118,85 +122,101 @@ const DefaultLoadingComponent: React.FC<{
118
122
  theme: Styles;
119
123
  }> = ({ status, theme }) => {
120
124
  return (
121
- <div style={{
122
- display: 'flex',
123
- flexDirection: 'column',
124
- alignItems: 'center',
125
- justifyContent: 'center',
126
- height: '100%',
127
- padding: '40px',
128
- background: theme.colors.background
129
- }}>
125
+ <div
126
+ style={{
127
+ display: 'flex',
128
+ flexDirection: 'column',
129
+ alignItems: 'center',
130
+ justifyContent: 'center',
131
+ height: '100%',
132
+ padding: '40px',
133
+ background: theme.colors.background,
134
+ }}
135
+ >
130
136
  {/* 文件图标动画 */}
131
- <div style={{
132
- position: 'relative',
133
- width: '80px',
134
- height: '80px',
135
- marginBottom: '24px'
136
- }}>
137
- {/* 文件图标背景 */}
138
- <div style={{
139
- width: '60px',
140
- height: '80px',
141
- background: theme.colors.primary,
142
- borderRadius: '8px 8px 0 0',
137
+ <div
138
+ style={{
143
139
  position: 'relative',
144
- margin: '0 auto'
145
- }}>
140
+ width: '80px',
141
+ height: '80px',
142
+ marginBottom: '24px',
143
+ }}
144
+ >
145
+ {/* 文件图标背景 */}
146
+ <div
147
+ style={{
148
+ width: '60px',
149
+ height: '80px',
150
+ background: theme.colors.primary,
151
+ borderRadius: '8px 8px 0 0',
152
+ position: 'relative',
153
+ margin: '0 auto',
154
+ }}
155
+ >
146
156
  {/* 文件折角 */}
147
- <div style={{
148
- position: 'absolute',
149
- top: '0',
150
- right: '0',
151
- width: '0',
152
- height: '0',
153
- borderStyle: 'solid',
154
- borderWidth: '0 20px 20px 0',
155
- borderColor: `transparent ${theme.colors.background} transparent transparent`
156
- }} />
157
+ <div
158
+ style={{
159
+ position: 'absolute',
160
+ top: '0',
161
+ right: '0',
162
+ width: '0',
163
+ height: '0',
164
+ borderStyle: 'solid',
165
+ borderWidth: '0 20px 20px 0',
166
+ borderColor: `transparent ${theme.colors.background} transparent transparent`,
167
+ }}
168
+ />
157
169
  </div>
158
170
 
159
171
  {/* 加载动画圆点 */}
160
- <div style={{
161
- position: 'absolute',
162
- top: '50%',
163
- left: '50%',
164
- transform: 'translate(-50%, -50%)',
165
- width: '20px',
166
- height: '20px',
167
- borderRadius: '50%',
168
- background: theme.colors.background,
169
- animation: 'pulse 1.5s ease-in-out infinite'
170
- }} />
172
+ <div
173
+ style={{
174
+ position: 'absolute',
175
+ top: '50%',
176
+ left: '50%',
177
+ transform: 'translate(-50%, -50%)',
178
+ width: '20px',
179
+ height: '20px',
180
+ borderRadius: '50%',
181
+ background: theme.colors.background,
182
+ animation: 'pulse 1.5s ease-in-out infinite',
183
+ }}
184
+ />
171
185
  </div>
172
186
 
173
187
  {/* 状态文本 */}
174
- <div style={{
175
- fontSize: '16px',
176
- fontWeight: '500',
177
- color: theme.colors.text,
178
- marginBottom: '8px',
179
- textAlign: 'center'
180
- }}>
188
+ <div
189
+ style={{
190
+ fontSize: '16px',
191
+ fontWeight: '500',
192
+ color: theme.colors.text,
193
+ marginBottom: '8px',
194
+ textAlign: 'center',
195
+ }}
196
+ >
181
197
  {status}
182
198
  </div>
183
199
 
184
200
  {/* 进度指示器 */}
185
- <div style={{
186
- width: '200px',
187
- height: '4px',
188
- background: theme.colors.border,
189
- borderRadius: '2px',
190
- overflow: 'hidden',
191
- position: 'relative'
192
- }}>
193
- <div style={{
194
- width: '30%',
195
- height: '100%',
196
- background: theme.colors.primary,
201
+ <div
202
+ style={{
203
+ width: '200px',
204
+ height: '4px',
205
+ background: theme.colors.border,
197
206
  borderRadius: '2px',
198
- animation: 'loading-progress 2s ease-in-out infinite'
199
- }} />
207
+ overflow: 'hidden',
208
+ position: 'relative',
209
+ }}
210
+ >
211
+ <div
212
+ style={{
213
+ width: '30%',
214
+ height: '100%',
215
+ background: theme.colors.primary,
216
+ borderRadius: '2px',
217
+ animation: 'loading-progress 2s ease-in-out infinite',
218
+ }}
219
+ />
200
220
  </div>
201
221
 
202
222
  {/* 动画样式 */}
@@ -245,8 +265,6 @@ const ErrorContainer = styled.div<{ $theme: Styles }>`
245
265
  const DEFAULT_STREAM_API_URL = 'http://10.15.12.13:9005/proxy/index/knowledgeBase/file/stream';
246
266
  const CACHE_EXPIRY_TIME = 5 * 60 * 1000; // 5分钟缓存过期
247
267
 
248
-
249
-
250
268
  // 默认主题
251
269
  const defaultTheme: Styles = {
252
270
  colors: {
@@ -322,7 +340,7 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
322
340
 
323
341
  // 通用文件缓存状态
324
342
  const [genericContentCache, setGenericContentCache] = useState<{
325
- [key: string]: { content: any; timestamp: number }
343
+ [key: string]: { content: any; timestamp: number };
326
344
  }>({});
327
345
 
328
346
  // 防抖状态 - 防止重复请求
@@ -380,31 +398,31 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
380
398
  case 'pdf_slides':
381
399
  defaultTools = {
382
400
  ...baseTools,
383
- annotation: false, // PDF模式关闭标注功能
384
- download: false, // PDF模式关闭下载功能
385
- navigation: true, // PDF模式开启翻页导航
386
- rotate: false, // PDF模式关闭旋转功能
401
+ annotation: false, // PDF模式关闭标注功能
402
+ download: false, // PDF模式关闭下载功能
403
+ navigation: true, // PDF模式开启翻页导航
404
+ rotate: false, // PDF模式关闭旋转功能
387
405
  };
388
406
  break;
389
407
  case 'image':
390
408
  defaultTools = {
391
409
  ...baseTools,
392
- annotation: false, // 图片模式关闭标注功能
393
- download: false, // 图片模式关闭下载功能
394
- navigation: false, // 图片模式关闭翻页导航
395
- rotate: true, // 图片模式开启旋转功能
396
- zoom:true,
410
+ annotation: false, // 图片模式关闭标注功能
411
+ download: false, // 图片模式关闭下载功能
412
+ navigation: false, // 图片模式关闭翻页导航
413
+ rotate: true, // 图片模式开启旋转功能
414
+ zoom: true,
397
415
  };
398
416
  break;
399
417
  case 'markdown':
400
418
  case 'markdown_table':
401
419
  defaultTools = {
402
420
  ...baseTools,
403
- annotation: false, // Markdown模式开启标注功能
404
- download: false, // Markdown模式关闭下载功能
405
- navigation: false, // Markdown模式关闭翻页导航
406
- rotate: false, // Markdown模式关闭旋转功能
407
- zoom: false, // Markdown模式关闭缩放功能
421
+ annotation: false, // Markdown模式开启标注功能
422
+ download: false, // Markdown模式关闭下载功能
423
+ navigation: false, // Markdown模式关闭翻页导航
424
+ rotate: false, // Markdown模式关闭旋转功能
425
+ zoom: false, // Markdown模式关闭缩放功能
408
426
  };
409
427
  break;
410
428
  case 'text':
@@ -412,11 +430,11 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
412
430
  case 'unknown':
413
431
  defaultTools = {
414
432
  ...baseTools,
415
- annotation: false, // 其他模式关闭标注功能
416
- download: false, // 其他模式关闭下载功能
417
- navigation: false, // 其他模式关闭翻页导航
418
- rotate: false, // 其他模式关闭旋转功能
419
- zoom: false, // 其他模式关闭缩放功能
433
+ annotation: false, // 其他模式关闭标注功能
434
+ download: false, // 其他模式关闭下载功能
435
+ navigation: false, // 其他模式关闭翻页导航
436
+ rotate: false, // 其他模式关闭旋转功能
437
+ zoom: false, // 其他模式关闭缩放功能
420
438
  };
421
439
  break;
422
440
  default:
@@ -448,257 +466,296 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
448
466
  return `${chunkStart}-${chunkStart + pageSize - 1}`;
449
467
  }, []);
450
468
 
451
- /**
469
+ /**
452
470
  * 检查页面是否在缓存中,并返回解析后的PDF文档
453
471
  * @param pageNo 页码
454
472
  * @returns 缓存的PDF文档或null
455
473
  */
456
- const getCachedPage = useCallback(async (pageNo: number): Promise<any | null> => {
457
- // 根据文件类型决定处理方式
458
- const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
459
-
460
- if (!isPDF) {
461
- console.log(`文件类型 ${fileType} 不是PDF,跳过缓存检查`);
462
- return null;
463
- }
464
-
465
- console.log(`检查页面 ${pageNo} 是否在缓存中`);
474
+ const getCachedPage = useCallback(
475
+ async (pageNo: number): Promise<any | null> => {
476
+ // 根据文件类型决定处理方式
477
+ const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
466
478
 
467
- // 先检查单个页面缓存
468
- const pageCached = pageCache.current[pageNo];
469
- if (pageCached && Date.now() - pageCached.timestamp < CACHE_EXPIRY_TIME) {
470
- console.log(`使用单个页面缓存: ${pageNo}`);
471
- if (pageCached.pdfDocument) {
472
- console.log(`直接返回缓存的PDF文档: ${pageNo}`);
473
- return pageCached.pdfDocument;
479
+ if (!isPDF) {
480
+ console.log(`文件类型 ${fileType} 不是PDF,跳过缓存检查`);
481
+ return null;
474
482
  }
475
- // 如果没有缓存的PDF文档,需要重新解析
476
- return null;
477
- }
478
483
 
479
- // 检查chunk缓存
480
- const chunkKey = getChunkKey(pageNo, pageSize);
481
- const chunkCached = chunkCache.current[chunkKey];
482
- console.log(`检查chunk缓存: ${chunkKey}, 存在: ${!!chunkCached}, 包含页面: ${chunkCached?.pages?.join(', ') || '无'}`);
484
+ console.log(`检查页面 ${pageNo} 是否在缓存中`);
483
485
 
484
- if (chunkCached && Date.now() - chunkCached.timestamp < CACHE_EXPIRY_TIME) {
485
- // 验证页面是否真的在这个chunk中
486
- if (chunkCached.pages.includes(pageNo)) {
487
- console.log(`使用chunk缓存: ${pageNo} (chunk: ${chunkKey})`);
488
- if (chunkCached.pdfDocument) {
486
+ // 先检查单个页面缓存
487
+ const pageCached = pageCache.current[pageNo];
488
+ if (pageCached && Date.now() - pageCached.timestamp < CACHE_EXPIRY_TIME) {
489
+ console.log(`使用单个页面缓存: ${pageNo}`);
490
+ if (pageCached.pdfDocument) {
489
491
  console.log(`直接返回缓存的PDF文档: ${pageNo}`);
490
- return chunkCached.pdfDocument;
492
+ return pageCached.pdfDocument;
491
493
  }
492
494
  // 如果没有缓存的PDF文档,需要重新解析
493
495
  return null;
494
- } else {
495
- console.log(`页面 ${pageNo} 不在chunk ${chunkKey} 中,chunk包含: ${chunkCached.pages.join(', ')}`);
496
496
  }
497
- }
498
497
 
499
- console.log(`页面 ${pageNo} 不在任何缓存中,需要重新请求`);
500
- return null;
501
- }, [pageSize, getChunkKey, fileType]);
498
+ // 检查chunk缓存
499
+ const chunkKey = getChunkKey(pageNo, pageSize);
500
+ const chunkCached = chunkCache.current[chunkKey];
501
+ console.log(
502
+ `检查chunk缓存: ${chunkKey}, 存在: ${!!chunkCached}, 包含页面: ${chunkCached?.pages?.join(', ') || '无'}`
503
+ );
504
+
505
+ if (chunkCached && Date.now() - chunkCached.timestamp < CACHE_EXPIRY_TIME) {
506
+ // 验证页面是否真的在这个chunk中
507
+ if (chunkCached.pages.includes(pageNo)) {
508
+ console.log(`使用chunk缓存: ${pageNo} (chunk: ${chunkKey})`);
509
+ if (chunkCached.pdfDocument) {
510
+ console.log(`直接返回缓存的PDF文档: ${pageNo}`);
511
+ return chunkCached.pdfDocument;
512
+ }
513
+ // 如果没有缓存的PDF文档,需要重新解析
514
+ return null;
515
+ } else {
516
+ console.log(
517
+ `页面 ${pageNo} 不在chunk ${chunkKey} 中,chunk包含: ${chunkCached.pages.join(', ')}`
518
+ );
519
+ }
520
+ }
521
+
522
+ console.log(`页面 ${pageNo} 不在任何缓存中,需要重新请求`);
523
+ return null;
524
+ },
525
+ [pageSize, getChunkKey, fileType]
526
+ );
502
527
 
503
528
  /**
504
529
  * 获取stream数据(仅用于PDF文件)
505
530
  * @param pageNo 页码
506
531
  * @returns Promise<ArrayBuffer>
507
532
  */
508
- const fetchStreamData = useCallback(async (pageNo: number): Promise<ArrayBuffer> => {
509
- try {
510
- // 根据文件类型决定是否传递分页参数
511
- const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
533
+ const fetchStreamData = useCallback(
534
+ async (pageNo: number): Promise<ArrayBuffer> => {
535
+ try {
536
+ // 根据文件类型决定是否传递分页参数
537
+ const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
512
538
 
513
- if (!isPDF) {
514
- console.log(`文件类型 ${fileType} 不需要分页参数,获取完整内容`);
515
- throw new Error(`文件类型 ${fileType} 不支持分页加载`);
516
- }
539
+ if (!isPDF) {
540
+ console.log(`文件类型 ${fileType} 不需要分页参数,获取完整内容`);
541
+ throw new Error(`文件类型 ${fileType} 不支持分页加载`);
542
+ }
517
543
 
518
- // 计算需要请求的chunk
519
- const chunkStart = Math.floor((pageNo - 1) / pageSize) * pageSize + 1;
520
- const chunkKey = getChunkKey(pageNo, pageSize);
544
+ // 计算需要请求的chunk
545
+ const chunkStart = Math.floor((pageNo - 1) / pageSize) * pageSize + 1;
546
+ const chunkKey = getChunkKey(pageNo, pageSize);
521
547
 
522
- console.log(`请求PDF chunk数据: ${chunkStart} (包含页面 ${chunkStart}-${chunkStart + pageSize - 1})`);
548
+ console.log(
549
+ `请求PDF chunk数据: ${chunkStart} (包含页面 ${chunkStart}-${chunkStart + pageSize - 1})`
550
+ );
523
551
 
524
- const headers: Record<string, string> = {
525
- 'Content-Type': 'application/json',
526
- };
552
+ const headers: Record<string, string> = {
553
+ 'Content-Type': 'application/json',
554
+ };
527
555
 
528
- if (authorization) {
529
- headers['Authorization'] = authorization;
530
- }
556
+ if (authorization) {
557
+ headers['Authorization'] = authorization;
558
+ }
531
559
 
532
- if (csrfToken) {
533
- headers['X-CSRF-TOKEN-IN'] = csrfToken;
534
- }
560
+ if (csrfToken) {
561
+ headers['X-CSRF-TOKEN-IN'] = csrfToken;
562
+ }
535
563
 
536
- // PDF文件类型传递分页参数
537
- const requestData = {
538
- convertedFilePath,
539
- pageNo: chunkStart, // 使用chunk的起始页
540
- pageSize,
541
- };
564
+ // PDF文件类型传递分页参数
565
+ const requestData = {
566
+ convertedFilePath,
567
+ pageNo: chunkStart, // 使用chunk的起始页
568
+ pageSize,
569
+ };
542
570
 
543
- const response = await axios.post<ArrayBuffer>(
544
- streamApiUrl,
545
- requestData,
546
- {
571
+ const response = await axios.post<ArrayBuffer>(streamApiUrl, requestData, {
547
572
  headers,
548
573
  responseType: 'arraybuffer',
574
+ });
575
+
576
+ // 获取响应头中的CSRF Token并触发事件
577
+ const responseCsrfToken =
578
+ response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
579
+ if (responseCsrfToken) {
580
+ console.log('获取到响应头中的CSRF Token:', responseCsrfToken);
581
+ eventsEmit('request_finish', responseCsrfToken);
549
582
  }
550
- );
551
583
 
552
- // 获取响应头中的CSRF Token并触发事件
553
- const responseCsrfToken = response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
554
- if (responseCsrfToken) {
555
- console.log('获取到响应头中的CSRF Token:', responseCsrfToken);
556
- eventsEmit('request_finish', responseCsrfToken);
557
- }
558
-
559
- // 缓存chunk数据
560
- const chunkPages = Array.from({ length: pageSize }, (_, i) => chunkStart + i);
561
- chunkCache.current[chunkKey] = {
562
- data: response.data,
563
- pages: chunkPages,
564
- timestamp: Date.now(),
565
- totalPages: userTotalPages, // 始终使用用户传入的总页数,不覆盖
566
- };
584
+ // 缓存chunk数据
585
+ const chunkPages = Array.from({ length: pageSize }, (_, i) => chunkStart + i);
586
+ chunkCache.current[chunkKey] = {
587
+ data: response.data,
588
+ pages: chunkPages,
589
+ timestamp: Date.now(),
590
+ totalPages: userTotalPages, // 始终使用用户传入的总页数,不覆盖
591
+ };
567
592
 
568
- console.log(`缓存PDF chunk数据: ${chunkKey}, 包含页面: ${chunkPages.join(', ')}, 请求的页面: ${pageNo}`);
569
- console.log(`当前所有缓存:`, Object.keys(chunkCache.current));
593
+ console.log(
594
+ `缓存PDF chunk数据: ${chunkKey}, 包含页面: ${chunkPages.join(', ')}, 请求的页面: ${pageNo}`
595
+ );
596
+ console.log(`当前所有缓存:`, Object.keys(chunkCache.current));
570
597
 
571
- return response.data;
572
- } catch (error: any) {
573
- console.error('获取PDF stream数据失败:', error);
574
- throw new Error(`PDF请求失败: ${error.response?.status || '网络错误'}`);
575
- }
576
- }, [streamApiUrl, convertedFilePath, pageSize, authorization, getChunkKey, userTotalPages, totalPages, fileType, eventsEmit]);
598
+ return response.data;
599
+ } catch (error: any) {
600
+ console.error('获取PDF stream数据失败:', error);
601
+ throw new Error(`PDF请求失败: ${error.response?.status || '网络错误'}`);
602
+ }
603
+ },
604
+ [
605
+ streamApiUrl,
606
+ convertedFilePath,
607
+ pageSize,
608
+ authorization,
609
+ getChunkKey,
610
+ userTotalPages,
611
+ totalPages,
612
+ fileType,
613
+ eventsEmit,
614
+ ]
615
+ );
577
616
 
578
617
  /**
579
618
  * 加载PDF文档(仅用于PDF文件类型)
580
619
  * @param pageNo 页码
581
620
  * @param isInitialLoad 是否为初始加载
582
621
  */
583
- const loadPDFDocument = useCallback(async (pageNo: number, isInitialLoad: boolean = false) => {
584
- if (!pageNo) return;
585
-
586
- // 根据文件类型决定处理方式
587
- const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
622
+ const loadPDFDocument = useCallback(
623
+ async (pageNo: number, isInitialLoad: boolean = false) => {
624
+ if (!pageNo) return;
588
625
 
589
- if (!isPDF) {
590
- console.log(`文件类型 ${fileType} 不是PDF,跳过PDF加载逻辑`);
591
- return null; // 直接返回,不进行任何操作
592
- }
626
+ // 根据文件类型决定处理方式
627
+ const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
593
628
 
594
- try {
595
- if (isInitialLoad) {
596
- setIsLoading(true);
597
- setLoadingStatus('正在加载PDF文档...');
598
- } else {
599
- setIsLoading(true);
600
- setLoadingStatus(`正在加载第 ${pageNo} 页...`);
629
+ if (!isPDF) {
630
+ console.log(`文件类型 ${fileType} 不是PDF,跳过PDF加载逻辑`);
631
+ return null; // 直接返回,不进行任何操作
601
632
  }
602
- setError(null);
603
633
 
604
- console.log(`开始加载PDF页面: ${pageNo}, 初始加载: ${isInitialLoad}`);
634
+ try {
635
+ if (isInitialLoad) {
636
+ setIsLoading(true);
637
+ setLoadingStatus('正在加载PDF文档...');
638
+ } else {
639
+ setIsLoading(true);
640
+ setLoadingStatus(`正在加载第 ${pageNo} 页...`);
641
+ }
642
+ setError(null);
643
+
644
+ console.log(`开始加载PDF页面: ${pageNo}, 初始加载: ${isInitialLoad}`);
645
+
646
+ // 先检查缓存
647
+ const cachedPdf = await getCachedPage(pageNo);
648
+ if (cachedPdf) {
649
+ // 如果缓存中有PDF文档,直接使用,无需重新解析
650
+ console.log(`使用缓存的PDF文档: ${pageNo}`);
651
+ setPdfDocument(cachedPdf);
652
+ setIsLoading(false);
653
+ setLoadingStatus('');
654
+ console.log(`页面 ${pageNo} 加载完成(使用缓存)`);
655
+ return cachedPdf;
656
+ }
605
657
 
606
- // 先检查缓存
607
- const cachedPdf = await getCachedPage(pageNo);
608
- if (cachedPdf) {
609
- // 如果缓存中有PDF文档,直接使用,无需重新解析
610
- console.log(`使用缓存的PDF文档: ${pageNo}`);
611
- setPdfDocument(cachedPdf);
612
- setIsLoading(false);
613
- setLoadingStatus('');
614
- console.log(`页面 ${pageNo} 加载完成(使用缓存)`);
615
- return cachedPdf;
616
- }
658
+ // 获取stream数据
659
+ const streamData = await fetchStreamData(pageNo);
617
660
 
618
- // 获取stream数据
619
- const streamData = await fetchStreamData(pageNo);
661
+ // 使用fileParser解析数据
662
+ const parseResult: ParseResult = await parseFile(streamData, {
663
+ fileName: fileName || 'Stream文档',
664
+ fileType: 'pdf',
665
+ token: authorization,
666
+ });
620
667
 
621
- // 使用fileParser解析数据
622
- const parseResult: ParseResult = await parseFile(streamData, {
623
- fileName: fileName || 'Stream文档',
624
- fileType: 'pdf',
625
- token: authorization,
626
- });
668
+ if (parseResult.error) {
669
+ throw new Error(parseResult.error);
670
+ }
627
671
 
628
- if (parseResult.error) {
629
- throw new Error(parseResult.error);
630
- }
672
+ if (parseResult.type !== 'pdf' || !parseResult.content?.document) {
673
+ throw new Error('解析结果不是有效的PDF文档');
674
+ }
631
675
 
632
- if (parseResult.type !== 'pdf' || !parseResult.content?.document) {
633
- throw new Error('解析结果不是有效的PDF文档');
634
- }
676
+ const pdf = parseResult.content.document;
677
+ console.log('设置PDF文档:', pdf);
678
+ console.log('PDF文档类型:', typeof pdf);
679
+ console.log(
680
+ 'PDF文档方法:',
681
+ pdf ? Object.getOwnPropertyNames(Object.getPrototypeOf(pdf)) : '无文档'
682
+ );
683
+ console.log('PDF文档页数:', pdf ? pdf.numPages : '无页数');
684
+ console.log('当前请求的页面:', pageNo);
685
+ console.log('PDF文档包含的页面范围:', pdf ? `1-${pdf.numPages}` : '无页数');
686
+ setPdfDocument(pdf);
687
+
688
+ // 缓存解析后的PDF文档
689
+ const chunkKey = getChunkKey(pageNo, pageSize);
690
+ if (chunkCache.current[chunkKey]) {
691
+ chunkCache.current[chunkKey].pdfDocument = pdf;
692
+ console.log(`缓存PDF文档到chunk: ${chunkKey}`);
693
+ }
635
694
 
636
- const pdf = parseResult.content.document;
637
- console.log('设置PDF文档:', pdf);
638
- console.log('PDF文档类型:', typeof pdf);
639
- console.log('PDF文档方法:', pdf ? Object.getOwnPropertyNames(Object.getPrototypeOf(pdf)) : '无文档');
640
- console.log('PDF文档页数:', pdf ? pdf.numPages : '无页数');
641
- console.log('当前请求的页面:', pageNo);
642
- console.log('PDF文档包含的页面范围:', pdf ? `1-${pdf.numPages}` : '无页数');
643
- setPdfDocument(pdf);
695
+ // 设置总页数 - 优先使用用户传入的总页数,如果没有传入则使用PDF文档的实际页数
696
+ if (userTotalPages) {
697
+ console.log(`使用用户传入的总页数: ${userTotalPages}`);
698
+ setTotalPages(userTotalPages);
699
+ } else if (pdf && pdf.numPages) {
700
+ const actualTotalPages = pdf.numPages;
701
+ console.log(`用户未传入总页数,从PDF文档获取实际总页数: ${actualTotalPages}`);
702
+ setTotalPages(actualTotalPages);
703
+ }
644
704
 
645
- // 缓存解析后的PDF文档
646
- const chunkKey = getChunkKey(pageNo, pageSize);
647
- if (chunkCache.current[chunkKey]) {
648
- chunkCache.current[chunkKey].pdfDocument = pdf;
649
- console.log(`缓存PDF文档到chunk: ${chunkKey}`);
650
- }
705
+ setIsLoading(false);
706
+ setLoadingStatus('');
651
707
 
652
- // 设置总页数 - 优先使用用户传入的总页数,如果没有传入则使用PDF文档的实际页数
653
- if (userTotalPages) {
654
- console.log(`使用用户传入的总页数: ${userTotalPages}`);
655
- setTotalPages(userTotalPages);
656
- } else if (pdf && pdf.numPages) {
657
- const actualTotalPages = pdf.numPages;
658
- console.log(`用户未传入总页数,从PDF文档获取实际总页数: ${actualTotalPages}`);
659
- setTotalPages(actualTotalPages);
708
+ console.log(`页面 ${pageNo} 加载完成`);
709
+ return pdf;
710
+ } catch (error: any) {
711
+ console.error('PDF加载失败:', error);
712
+ setError(error.message || 'PDF加载失败,请稍后重试');
713
+ setIsLoading(false);
714
+ setLoadingStatus('');
715
+ throw error;
660
716
  }
661
-
662
- setIsLoading(false);
663
- setLoadingStatus('');
664
-
665
- console.log(`页面 ${pageNo} 加载完成`);
666
- return pdf;
667
- } catch (error: any) {
668
- console.error('PDF加载失败:', error);
669
- setError(error.message || 'PDF加载失败,请稍后重试');
670
- setIsLoading(false);
671
- setLoadingStatus('');
672
- throw error;
673
- }
674
- }, [fetchStreamData, fileName, authorization, totalPages, getCachedPage, getChunkKey, pageSize, fileType]);
717
+ },
718
+ [
719
+ fetchStreamData,
720
+ fileName,
721
+ authorization,
722
+ totalPages,
723
+ getCachedPage,
724
+ getChunkKey,
725
+ pageSize,
726
+ fileType,
727
+ ]
728
+ );
675
729
 
676
730
  /**
677
731
  * 检查页面是否在缓存中(用于调试)
678
732
  */
679
- const isPageInCache = useCallback((pageNo: number): boolean => {
680
- // 根据文件类型决定处理方式
681
- const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
733
+ const isPageInCache = useCallback(
734
+ (pageNo: number): boolean => {
735
+ // 根据文件类型决定处理方式
736
+ const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
682
737
 
683
- if (!isPDF) {
684
- return false;
685
- }
738
+ if (!isPDF) {
739
+ return false;
740
+ }
686
741
 
687
- // 先检查单个页面缓存
688
- const pageCached = pageCache.current[pageNo];
689
- if (pageCached && Date.now() - pageCached.timestamp < CACHE_EXPIRY_TIME) {
690
- return !!pageCached.pdfDocument;
691
- }
742
+ // 先检查单个页面缓存
743
+ const pageCached = pageCache.current[pageNo];
744
+ if (pageCached && Date.now() - pageCached.timestamp < CACHE_EXPIRY_TIME) {
745
+ return !!pageCached.pdfDocument;
746
+ }
692
747
 
693
- // 检查chunk缓存
694
- const chunkKey = getChunkKey(pageNo, pageSize);
695
- const chunkCached = chunkCache.current[chunkKey];
696
- if (chunkCached && Date.now() - chunkCached.timestamp < CACHE_EXPIRY_TIME) {
697
- return chunkCached.pages.includes(pageNo) && !!chunkCached.pdfDocument;
698
- }
748
+ // 检查chunk缓存
749
+ const chunkKey = getChunkKey(pageNo, pageSize);
750
+ const chunkCached = chunkCache.current[chunkKey];
751
+ if (chunkCached && Date.now() - chunkCached.timestamp < CACHE_EXPIRY_TIME) {
752
+ return chunkCached.pages.includes(pageNo) && !!chunkCached.pdfDocument;
753
+ }
699
754
 
700
- return false;
701
- }, [pageSize, getChunkKey, fileType]);
755
+ return false;
756
+ },
757
+ [pageSize, getChunkKey, fileType]
758
+ );
702
759
 
703
760
  // ========== Markdown相关工具函数 ==========
704
761
 
@@ -752,7 +809,8 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
752
809
  });
753
810
 
754
811
  // 获取响应头中的CSRF Token并触发事件
755
- const responseCsrfToken = response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
812
+ const responseCsrfToken =
813
+ response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
756
814
  if (responseCsrfToken) {
757
815
  console.log('获取到响应头中的CSRF Token:', responseCsrfToken);
758
816
  eventsEmit('request_finish', responseCsrfToken);
@@ -796,7 +854,7 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
796
854
  // 缓存markdown内容
797
855
  markdownContentCache.current = {
798
856
  data: streamData,
799
- timestamp: Date.now()
857
+ timestamp: Date.now(),
800
858
  };
801
859
 
802
860
  setMarkdownContent(streamData);
@@ -835,17 +893,14 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
835
893
  // 不传 pageNo/pageSize,获取完整图片二进制
836
894
  };
837
895
 
838
- const response = await axios.post<ArrayBuffer>(
839
- streamApiUrl,
840
- requestData,
841
- {
842
- headers,
843
- responseType: 'arraybuffer',
844
- }
845
- );
896
+ const response = await axios.post<ArrayBuffer>(streamApiUrl, requestData, {
897
+ headers,
898
+ responseType: 'arraybuffer',
899
+ });
846
900
 
847
901
  // 获取响应头中的CSRF Token并触发事件
848
- const responseCsrfToken = response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
902
+ const responseCsrfToken =
903
+ response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
849
904
  if (responseCsrfToken) {
850
905
  console.log('获取到响应头中的CSRF Token:', responseCsrfToken);
851
906
  eventsEmit('request_finish', responseCsrfToken);
@@ -924,17 +979,14 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
924
979
 
925
980
  console.log('发送通用文件请求:', requestData);
926
981
 
927
- const response = await axios.post<ArrayBuffer>(
928
- streamApiUrl,
929
- requestData,
930
- {
931
- headers,
932
- responseType: 'arraybuffer',
933
- }
934
- );
982
+ const response = await axios.post<ArrayBuffer>(streamApiUrl, requestData, {
983
+ headers,
984
+ responseType: 'arraybuffer',
985
+ });
935
986
 
936
987
  // 获取响应头中的CSRF Token并触发事件
937
- const responseCsrfToken = response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
988
+ const responseCsrfToken =
989
+ response.headers['x-csrf-token-out'] || response.headers['X-CSRF-TOKEN-OUT'];
938
990
  if (responseCsrfToken) {
939
991
  console.log('获取到响应头中的CSRF Token:', responseCsrfToken);
940
992
  eventsEmit('request_finish', responseCsrfToken);
@@ -974,7 +1026,9 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
974
1026
  // 检查缓存
975
1027
  const cacheKey = `${fileType}_${convertedFilePath}`;
976
1028
  const cached = genericContentCache[cacheKey];
977
- console.log(`检查通用文档缓存,key: ${cacheKey}, 缓存存在: ${!!cached}, 缓存时间: ${cached ? Date.now() - cached.timestamp : 'N/A'}ms`);
1029
+ console.log(
1030
+ `检查通用文档缓存,key: ${cacheKey}, 缓存存在: ${!!cached}, 缓存时间: ${cached ? Date.now() - cached.timestamp : 'N/A'}ms`
1031
+ );
978
1032
 
979
1033
  if (cached && Date.now() - cached.timestamp < CACHE_EXPIRY_TIME) {
980
1034
  console.log(`使用缓存的通用文档内容,文件类型: ${fileType}`);
@@ -1042,8 +1096,8 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1042
1096
  ...genericContentCache,
1043
1097
  [cacheKey]: {
1044
1098
  content: finalContent,
1045
- timestamp: Date.now()
1046
- }
1099
+ timestamp: Date.now(),
1100
+ },
1047
1101
  };
1048
1102
  setGenericContentCache(newCache);
1049
1103
  console.log(`通用文档内容已缓存,文件类型: ${fileType}, 缓存键: ${cacheKey}`);
@@ -1072,40 +1126,43 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1072
1126
  /**
1073
1127
  * 处理页面变化(仅用于PDF文件类型)
1074
1128
  */
1075
- const handlePageChange = useCallback(async (pageNumber: number) => {
1076
- if (pageNumber === currentPageRef.current) return;
1077
- if (isLoading) {
1078
- console.log('页面正在加载中,忽略翻页请求');
1079
- return;
1080
- }
1129
+ const handlePageChange = useCallback(
1130
+ async (pageNumber: number) => {
1131
+ if (pageNumber === currentPageRef.current) return;
1132
+ if (isLoading) {
1133
+ console.log('页面正在加载中,忽略翻页请求');
1134
+ return;
1135
+ }
1081
1136
 
1082
- // 根据文件类型决定处理方式
1083
- const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
1137
+ // 根据文件类型决定处理方式
1138
+ const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
1084
1139
 
1085
- if (!isPDF) {
1086
- console.log(`文件类型 ${fileType} 不支持翻页,忽略翻页请求`);
1087
- return; // 直接返回,不进行任何操作
1088
- }
1140
+ if (!isPDF) {
1141
+ console.log(`文件类型 ${fileType} 不支持翻页,忽略翻页请求`);
1142
+ return; // 直接返回,不进行任何操作
1143
+ }
1089
1144
 
1090
- console.log('PDF页面变化:', pageNumber, '当前页:', currentPageRef.current);
1091
- console.log(`页面 ${pageNumber} 是否在缓存中:`, isPageInCache(pageNumber));
1145
+ console.log('PDF页面变化:', pageNumber, '当前页:', currentPageRef.current);
1146
+ console.log(`页面 ${pageNumber} 是否在缓存中:`, isPageInCache(pageNumber));
1092
1147
 
1093
- // 更新ref和state
1094
- currentPageRef.current = pageNumber;
1095
- setCurrentPage(pageNumber);
1148
+ // 更新ref和state
1149
+ currentPageRef.current = pageNumber;
1150
+ setCurrentPage(pageNumber);
1096
1151
 
1097
- try {
1098
- await loadPDFDocument(pageNumber, false); // 非初始加载
1099
- // 优先使用用户传入的总页数
1100
- const finalTotalPages = userTotalPages || totalPages;
1101
- eventsEmit('pageChange', { pageNumber, totalPages: finalTotalPages });
1102
- } catch (error) {
1103
- console.error('PDF页面加载失败:', error);
1104
- // 加载失败时恢复当前页
1105
- const originalPage = currentPageRef.current;
1106
- setCurrentPage(originalPage);
1107
- }
1108
- }, [loadPDFDocument, totalPages, eventsEmit, isPageInCache, isLoading, fileType]);
1152
+ try {
1153
+ await loadPDFDocument(pageNumber, false); // 非初始加载
1154
+ // 优先使用用户传入的总页数
1155
+ const finalTotalPages = userTotalPages || totalPages;
1156
+ eventsEmit('pageChange', { pageNumber, totalPages: finalTotalPages });
1157
+ } catch (error) {
1158
+ console.error('PDF页面加载失败:', error);
1159
+ // 加载失败时恢复当前页
1160
+ const originalPage = currentPageRef.current;
1161
+ setCurrentPage(originalPage);
1162
+ }
1163
+ },
1164
+ [loadPDFDocument, totalPages, eventsEmit, isPageInCache, isLoading, fileType]
1165
+ );
1109
1166
 
1110
1167
  // 添加useEffect来监听currentPage变化,确保状态同步
1111
1168
  useEffect(() => {
@@ -1116,10 +1173,13 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1116
1173
  /**
1117
1174
  * 处理缩放变化
1118
1175
  */
1119
- const handleZoomChange = useCallback((newScale: number) => {
1120
- setScale(newScale);
1121
- eventsEmit('zoomChange', { scale: newScale });
1122
- }, [eventsEmit]);
1176
+ const handleZoomChange = useCallback(
1177
+ (newScale: number) => {
1178
+ setScale(newScale);
1179
+ eventsEmit('zoomChange', { scale: newScale });
1180
+ },
1181
+ [eventsEmit]
1182
+ );
1123
1183
 
1124
1184
  /**
1125
1185
  * 处理关闭
@@ -1225,7 +1285,10 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1225
1285
  });
1226
1286
 
1227
1287
  // 清理markdown内容缓存
1228
- if (markdownContentCache.current && now - markdownContentCache.current.timestamp > CACHE_EXPIRY_TIME) {
1288
+ if (
1289
+ markdownContentCache.current &&
1290
+ now - markdownContentCache.current.timestamp > CACHE_EXPIRY_TIME
1291
+ ) {
1229
1292
  markdownContentCache.current = null;
1230
1293
  console.log('清理过期markdown内容缓存');
1231
1294
  }
@@ -1285,8 +1348,15 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1285
1348
  try {
1286
1349
  // 根据文件类型决定是否使用翻页参数
1287
1350
  const isPDF = fileType === 'pdf' || fileType === 'pdf_slides';
1288
- const effectivePage = isPDF ? (initialPage || 1) : 1;
1289
- console.log('开始初始加载,文件类型:', fileType, '有效页码:', effectivePage, '是否PDF:', isPDF);
1351
+ const effectivePage = isPDF ? initialPage || 1 : 1;
1352
+ console.log(
1353
+ '开始初始加载,文件类型:',
1354
+ fileType,
1355
+ '有效页码:',
1356
+ effectivePage,
1357
+ '是否PDF:',
1358
+ isPDF
1359
+ );
1290
1360
 
1291
1361
  if (fileType === 'markdown' || fileType === 'markdown_table') {
1292
1362
  // Markdown文件加载完整内容,忽略翻页参数
@@ -1321,22 +1391,28 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1321
1391
  setTotalPages(userTotalPages || 1);
1322
1392
  eventsEmit('pageChange', { pageNumber: 1, totalPages: userTotalPages || 1 });
1323
1393
  }
1324
- } catch (error) {
1394
+ } catch (error: any) {
1325
1395
  console.error('初始加载失败:', error);
1396
+ // 确保错误状态被正确设置,避免组件状态不一致
1397
+ setError(error?.message || '文件加载失败,请稍后重试');
1398
+ setIsLoading(false);
1399
+ setLoadingStatus('');
1326
1400
  }
1327
1401
  };
1328
1402
 
1329
1403
  loadInitialPage();
1330
1404
  }, [initialPage, fileType, convertedFilePath]);
1331
1405
 
1406
+ // 所有 hooks 必须在条件返回之前调用
1407
+ const pdfStartPage = useMemo(() => {
1408
+ return Math.floor((currentPage - 1) / pageSize) * pageSize + 1;
1409
+ }, [currentPage, pageSize]);
1410
+
1332
1411
  if (error) {
1333
1412
  return (
1334
1413
  <ReaderContainer $theme={mergedTheme} className={className}>
1335
1414
  {customComponents?.ErrorComponent ? (
1336
- <customComponents.ErrorComponent
1337
- error={error}
1338
- theme={mergedTheme}
1339
- />
1415
+ <customComponents.ErrorComponent error={error} theme={mergedTheme} />
1340
1416
  ) : (
1341
1417
  <ErrorContainer $theme={mergedTheme}>
1342
1418
  <div>加载失败</div>
@@ -1347,10 +1423,6 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1347
1423
  );
1348
1424
  }
1349
1425
 
1350
- const pdfStartPage = useMemo(()=>{
1351
- return Math.floor((currentPage - 1) / pageSize) * pageSize + 1
1352
- },[currentPage,pageSize])
1353
-
1354
1426
  // 渲染加载状态
1355
1427
  if (isLoading) {
1356
1428
  return (
@@ -1376,15 +1448,9 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1376
1448
 
1377
1449
  {/* 内容区域 - 显示加载状态 */}
1378
1450
  {customComponents?.LoadingComponent ? (
1379
- <customComponents.LoadingComponent
1380
- status={loadingStatus}
1381
- theme={mergedTheme}
1382
- />
1451
+ <customComponents.LoadingComponent status={loadingStatus} theme={mergedTheme} />
1383
1452
  ) : (
1384
- <DefaultLoadingComponent
1385
- status={loadingStatus}
1386
- theme={mergedTheme}
1387
- />
1453
+ <DefaultLoadingComponent status={loadingStatus} theme={mergedTheme} />
1388
1454
  )}
1389
1455
  </ReaderContainer>
1390
1456
  );
@@ -1392,8 +1458,8 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1392
1458
 
1393
1459
  return (
1394
1460
  <ReaderContainer $theme={mergedTheme} className={className}>
1395
- {/* Header组件 */}
1396
- <Header
1461
+ {/* Header组件 */}
1462
+ <Header
1397
1463
  fileName={getFileName()}
1398
1464
  currentPage={currentPage}
1399
1465
  totalPage={totalPages}
@@ -1401,8 +1467,8 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1401
1467
  isAnnotating={false}
1402
1468
  tools={{
1403
1469
  ...tools,
1404
- // 仅PDF和PDF幻灯片显示导航
1405
- navigation: (fileType === 'pdf' || fileType === 'pdf_slides') ? tools.navigation : false,
1470
+ // 仅PDF和PDF幻灯片显示导航
1471
+ navigation: fileType === 'pdf' || fileType === 'pdf_slides' ? tools.navigation : false,
1406
1472
  // 仅图片显示旋转
1407
1473
  rotate: fileType === 'image',
1408
1474
  }}
@@ -1419,7 +1485,7 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1419
1485
  className={headerClass}
1420
1486
  />
1421
1487
 
1422
- {(fileType === 'markdown' || fileType === 'markdown_table') ? (
1488
+ {fileType === 'markdown' || fileType === 'markdown_table' ? (
1423
1489
  // Markdown内容渲染 - 使用Fviewer的markdown渲染逻辑
1424
1490
  <div
1425
1491
  ref={scrollContainerRef}
@@ -1432,7 +1498,7 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1432
1498
  >
1433
1499
  <Fviewer
1434
1500
  data={{
1435
- content: markdownContent, // 直接传递markdown内容字符串
1501
+ content: markdownContent, // 直接传递markdown内容字符串
1436
1502
  ...(getFileName() && { fileName: getFileName() }),
1437
1503
  ...(fileType && { fileType }),
1438
1504
  ...(totalPages && { totalPages }),
@@ -1463,7 +1529,10 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1463
1529
  console.log('markdownContent:', markdownContent);
1464
1530
  console.log('markdownContent长度:', markdownContent.length);
1465
1531
  console.log('markdownContent前100字符:', markdownContent.substring(0, 100));
1466
- console.log('markdownContent后100字符:', markdownContent.substring(markdownContent.length - 100));
1532
+ console.log(
1533
+ 'markdownContent后100字符:',
1534
+ markdownContent.substring(markdownContent.length - 100)
1535
+ );
1467
1536
  return null;
1468
1537
  })()}
1469
1538
 
@@ -1496,12 +1565,12 @@ const GientechStreamReader: React.FC<GientechStreamReaderProps> = ({
1496
1565
  // PDF内容渲染
1497
1566
  <Fviewer
1498
1567
  data={{
1499
- content: { document: pdfDocument }, // 修复:包装成期望的数据结构
1568
+ content: { document: pdfDocument }, // 修复:包装成期望的数据结构
1500
1569
  ...(getFileName() && { fileName: getFileName() }),
1501
1570
  ...(fileType && { fileType }),
1502
1571
  ...(totalPages && { totalPages }),
1503
1572
  // 添加PDF起始页信息,用于分页PDF的页码映射
1504
- pdfStartPage
1573
+ pdfStartPage,
1505
1574
  }}
1506
1575
  annotationData={annotations}
1507
1576
  totalPage={totalPages}