@@ -4,6 +4,62 @@ import {useState, useCallback} from 'react';
44import { toast } from 'sonner' ;
55import { handleBulkImportContentWithFilter } from '@/components/common/project' ;
66
7+ /**
8+ * 解析 JSONL 文件内容
9+ * 支持两种格式:
10+ * 1. JSON 数组格式:[{}, {}, {}]
11+ * 2. 每行一个 JSON 对象
12+ */
13+ const parseJsonlContent = ( content : string ) : string => {
14+ const trimmedContent = content . trim ( ) ;
15+
16+ // 尝试解析为 JSON 数组格式
17+ if ( trimmedContent . startsWith ( '[' ) && trimmedContent . endsWith ( ']' ) ) {
18+ try {
19+ const jsonArray = JSON . parse ( trimmedContent ) ;
20+ if ( Array . isArray ( jsonArray ) ) {
21+ return jsonArray
22+ . map ( ( item ) => {
23+ if ( typeof item === 'object' && item !== null ) {
24+ return JSON . stringify ( item ) ;
25+ }
26+ return String ( item ) ;
27+ } )
28+ . filter ( ( item ) => item . trim ( ) )
29+ . join ( '\n' ) ;
30+ }
31+ } catch {
32+ // JSON 数组解析失败,继续尝试逐行解析
33+ }
34+ }
35+
36+ // 逐行解析 JSON 对象
37+ const lines = trimmedContent
38+ . split ( / \r ? \n / )
39+ . map ( ( line ) => line . trim ( ) )
40+ . filter ( ( line ) => line . length > 0 ) ;
41+
42+ const validJsonLines : string [ ] = [ ] ;
43+
44+ for ( const line of lines ) {
45+ try {
46+ // 尝试解析为 JSON 对象
47+ const jsonObj = JSON . parse ( line ) ;
48+ if ( typeof jsonObj === 'object' && jsonObj !== null ) {
49+ validJsonLines . push ( JSON . stringify ( jsonObj ) ) ;
50+ } else {
51+ // 非对象类型,直接转为字符串
52+ validJsonLines . push ( String ( jsonObj ) ) ;
53+ }
54+ } catch {
55+ // JSON 解析失败,作为普通文本处理
56+ validJsonLines . push ( line ) ;
57+ }
58+ }
59+
60+ return validJsonLines . join ( '\n' ) ;
61+ } ;
62+
763export function useFileUpload ( ) {
864 const [ fileUploadOpen , setFileUploadOpen ] = useState ( false ) ;
965
@@ -16,10 +72,11 @@ export function useFileUpload() {
1672 if ( files . length === 0 ) return ;
1773
1874 const file = files [ 0 ] ;
75+ const fileName = file . name . toLowerCase ( ) ;
1976
2077 // 检查文件类型
21- if ( ! file . name . toLowerCase ( ) . endsWith ( '.txt ' ) ) {
22- toast . error ( '仅支持上传 .txt 格式的文件' ) ;
78+ if ( ! fileName . endsWith ( '.txt' ) && ! fileName . endsWith ( '.jsonl ' ) ) {
79+ toast . error ( '仅支持上传 .txt 或 .jsonl 格式的文件' ) ;
2380 return ;
2481 }
2582
@@ -33,20 +90,35 @@ export function useFileUpload() {
3390 reader . onload = ( e ) => {
3491 const content = e . target ?. result as string ;
3592 if ( content ) {
36- // 按行分割并过滤空行
37- const lines = content
38- . split ( / \r ? \n / )
39- . map ( ( line ) => line . trim ( ) )
40- . filter ( ( line ) => line . length > 0 ) ;
41-
42- if ( lines . length === 0 ) {
43- toast . error ( '文件内容为空' ) ;
93+ let processedContent = content ;
94+
95+ // 根据文件扩展名选择解析方式
96+ if ( fileName . endsWith ( '.jsonl' ) ) {
97+ // JSONL 文件处理
98+ processedContent = parseJsonlContent ( content ) ;
99+ } else {
100+ // TXT 文件处理(保持原有逻辑)
101+ const lines = content
102+ . split ( / \r ? \n / )
103+ . map ( ( line ) => line . trim ( ) )
104+ . filter ( ( line ) => line . length > 0 ) ;
105+
106+ if ( lines . length === 0 ) {
107+ toast . error ( '文件内容为空' ) ;
108+ return ;
109+ }
110+
111+ processedContent = lines . join ( '\n' ) ;
112+ }
113+
114+ if ( ! processedContent ) {
115+ toast . error ( '文件内容为空或格式无效' ) ;
44116 return ;
45117 }
46118
47119 // 执行导入
48120 handleBulkImportContentWithFilter (
49- lines . join ( '\n' ) ,
121+ processedContent ,
50122 currentItems ,
51123 allowDuplicates ,
52124 ( updatedItems : string [ ] , importedCount : number , skippedInfo ?: string ) => {
0 commit comments