= ({
}
}, [totalMessagesCount, handle]);
+ useEffect(() => {
+ prevMsgCountRef.current = 0;
+ isInitialMountRef.current = true;
+ }, [sessionId]);
+
return (
= ({ fileRaw, fileUrl }) =>
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [sheetNames, setSheetNames] = useState([]);
- const [activeSheetIndex, setActiveSheetIndex] = useState(0);
const [sheetData, setSheetData] = useState>({});
+ const [activeSheetIndex, setActiveSheetIndex] = useState(0);
+ const [truncatedSheets, setTruncatedSheets] = useState>({});
useEffect(() => {
let isMounted = true;
@@ -40,6 +41,8 @@ export const ExcelViewer: React.FC = ({ fileRaw, fileUrl }) =>
}
const parsedSheets: Record = {};
+ const truncated: Record = {};
+ const previewRowLimit = 1000;
for (const name of workbook.SheetNames) {
const ws = workbook.Sheets[name];
if (ws) {
@@ -48,7 +51,8 @@ export const ExcelViewer: React.FC = ({ fileRaw, fileUrl }) =>
defval: '',
blankrows: false,
});
- parsedSheets[name] = rawRows;
+ truncated[name] = rawRows.length > previewRowLimit;
+ parsedSheets[name] = rawRows.slice(0, previewRowLimit);
} else {
parsedSheets[name] = [];
}
@@ -57,6 +61,7 @@ export const ExcelViewer: React.FC = ({ fileRaw, fileUrl }) =>
if (isMounted) {
setSheetNames(workbook.SheetNames);
setSheetData(parsedSheets);
+ setTruncatedSheets(truncated);
setActiveSheetIndex(0);
setLoading(false);
}
@@ -173,6 +178,9 @@ export const ExcelViewer: React.FC = ({ fileRaw, fileUrl }) =>
工作表:{currentSheetName}
·
{rows.length} 行 × {maxCols} 列
+ {truncatedSheets[currentSheetName] && (
+ · 仅预览前 1000 行
+ )}
共 {sheetNames.length} 个工作表
diff --git a/web/src/engine/renderMarkdown.test.ts b/web/src/engine/renderMarkdown.test.ts
index 267029d2..565a2ac7 100644
--- a/web/src/engine/renderMarkdown.test.ts
+++ b/web/src/engine/renderMarkdown.test.ts
@@ -17,6 +17,10 @@ test('renderInlineMarkdown: escapes raw HTML tags to prevent XSS', () => {
assert.ok(!scriptHtml.includes('');
+ assert.ok(!forged.includes('