Ver Fonte

优化文件上传提示信息,增强403和504错误处理逻辑,添加上传过程中的加载提示,改进问答列表的分页和搜索功能。

刘博博 há 3 dias atrás
pai
commit
94e43d8660

+ 2 - 2
src/apis/api.ts

@@ -52,7 +52,7 @@ axiosInstance.interceptors.response.use(
             message.error('请求超时,请稍后重试');
         } else if (statusCode === 403) {
             // 403禁止访问,通常是防火墙拦截或内容违规
-            message.error('您提交的信息内容非法,访问已被防火墙拒绝');
+            message.error('检测到您的此次提交请求未能通过安全验证,请检查提示词中是否包含特殊符号(如 ` \ $ ), 请调整后重新提交。');
         } else {
             if (statusCode < 500) {
                 message.error('请求失败');
@@ -64,4 +64,4 @@ axiosInstance.interceptors.response.use(
     }
 );
 
-export default axiosInstance;
+export default axiosInstance;

+ 27 - 7
src/pages/deepseek/knowledgeLib/detail/drawerIndex.tsx

@@ -68,6 +68,7 @@ const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
 
   const [ fileList, setFileList ] = React.useState<any[]>( [] );
   const [ uploading, setUploading ] = React.useState( false );
+  const uploadMessageRef = React.useRef<(() => void) | null>( null );
 
   const [ sListFlag, setSListFlag ] = React.useState<boolean>();
   const [ cUpdateFlag, setCUpdateFlag ] = React.useState<boolean>();
@@ -81,7 +82,6 @@ const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
     multiple: true,
     action: '/api/deepseek/api/uploadDocument/' + params.knowledgeId,
     headers: getHeaders(),
-    showUploadList: false, // 隐藏上传列表,避免显示HTML错误信息的tooltip
     beforeUpload( file, fileList ) {
       setUploadLoading( true );
       // const allowedExtensions = ['md', 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'docx', 'xlsx', 'pptx', 'eml', 'csv', 'tar', 'gz', 'bz2', 'zip', 'rar', 'jar'];
@@ -127,14 +127,26 @@ const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
 
     },
     onChange( info ) {
-      const { status } = info.file;
+      const { status, name } = info.file;
 
-      if ( status !== 'uploading' ) {
-        console.log( status, 'status--uploading' );
-      }
-      if ( status === 'done' ) {
+      if ( status === 'uploading' ) {
+        // 文件开始上传,显示持续提示
+        if ( !uploadMessageRef.current ) {
+          const closeMessage = message.loading(
+            `为了确保文件 "${name}" 的解析质量,系统需要一些时间进行准备(通常为2-20分钟)。辛苦您耐心等待,您可以继续使用其他功能,只要不关闭当前页面即可。`,
+            0 // 0表示不自动关闭
+          );
+          uploadMessageRef.current = closeMessage;
+        }
+        setUploadLoading( true );
+      } else if ( status === 'done' ) {
         console.log( status, 'status--done' );
         console.info( info.file.response, 'info.file.response.data' );
+        // 关闭上传提示
+        if ( uploadMessageRef.current ) {
+          uploadMessageRef.current();
+          uploadMessageRef.current = null;
+        }
         if ( info.file.response.code === 200 && info.file.response.data === 1 ) {
           message.success( `${ info.file.name } file uploaded successfully.` );
           init( params.knowledgeId );
@@ -142,6 +154,11 @@ const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
         setUploadLoading( false );
       } else if ( status === 'error' ) {
         console.log( status, 'status--error' );
+        // 关闭上传提示
+        if ( uploadMessageRef.current ) {
+          uploadMessageRef.current();
+          uploadMessageRef.current = null;
+        }
         // 检查是否是504超时错误
         const response = info.file.response;
         const error = info.file.error;
@@ -155,6 +172,9 @@ const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
             responseStr.includes('<h1>504 Gateway Time-out</h1>') ||
             errorStr.includes('timeout') || 
             errorStr.includes('504')) {
+          // 替换文件对象中的错误信息,避免显示HTML内容
+          info.file.response = '上传文件超时,请修改文件后再上传';
+          info.file.error = '上传文件超时,请修改文件后再上传';
           message.error( '上传文件超时,请修改文件后再上传' );
         } else {
           message.error( `${ info.file.name } 文件上传失败` );
@@ -192,7 +212,7 @@ const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
       if (err?.response?.status === 504 || err?.code === 'ECONNABORTED' || String(err).includes('timeout')) {
         message.error( '上传文件超时,请修改文件后再上传' );
       } else {
-        message.error( '上传失败' );
+      message.error( '上传失败' );
       }
     } finally {
       setUploading( false );

+ 30 - 9
src/pages/deepseek/knowledgeLib/detail/index.tsx

@@ -65,6 +65,7 @@ const KnowledgeLibInfo : React.FC = () => {
 
   const [ fileList, setFileList ] = React.useState<any[]>( [] );
   const [ uploading, setUploading ] = React.useState( false );
+  const uploadMessageRef = React.useRef<(() => void) | null>( null );
 
   const [ sListFlag, setSListFlag ] = React.useState<boolean>();
   const [ cUpdateFlag, setCUpdateFlag ] = React.useState<boolean>();
@@ -81,7 +82,6 @@ const KnowledgeLibInfo : React.FC = () => {
     multiple: true,
     action: '/api/deepseek/api/uploadDocument/' + params.knowledgeId,
     headers: getHeaders(),
-    showUploadList: false, // 隐藏上传列表,避免显示HTML错误信息的tooltip
     beforeUpload( file, fileList ) {
       setUploadLoading( true );
       // const allowedExtensions = ['md', 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'docx', 'xlsx', 'pptx', 'eml', 'csv', 'tar', 'gz', 'bz2', 'zip', 'rar', 'jar'];
@@ -127,14 +127,26 @@ const KnowledgeLibInfo : React.FC = () => {
 
     },
     onChange( info ) {
-      const { status } = info.file;
+      const { status, name } = info.file;
 
-      if ( status !== 'uploading' ) {
-        console.log( status, 'status--uploading' );
-      }
-      if ( status === 'done' ) {
+      if ( status === 'uploading' ) {
+        // 文件开始上传,显示持续提示
+        if ( !uploadMessageRef.current ) {
+          const closeMessage = message.loading(
+            `为了确保文件 "${name}" 的解析质量,系统需要一些时间进行准备(通常为2-20分钟)。辛苦您耐心等待,您可以继续使用其他功能,只要不关闭当前页面即可。`,
+            0 // 0表示不自动关闭
+          );
+          uploadMessageRef.current = closeMessage;
+        }
+        setUploadLoading( true );
+      } else if ( status === 'done' ) {
         console.log( status, 'status--done' );
         console.info( info.file.response, 'info.file.response.data' );
+        // 关闭上传提示
+        if ( uploadMessageRef.current ) {
+          uploadMessageRef.current();
+          uploadMessageRef.current = null;
+        }
         if ( info.file.response.code === 200 && info.file.response.data === 1 ) {
           message.success( `${ info.file.name } file uploaded successfully.` );
           init( params.knowledgeId );
@@ -142,6 +154,11 @@ const KnowledgeLibInfo : React.FC = () => {
         setUploadLoading( false );
       } else if ( status === 'error' ) {
         console.log( status, 'status--error' );
+        // 关闭上传提示
+        if ( uploadMessageRef.current ) {
+          uploadMessageRef.current();
+          uploadMessageRef.current = null;
+        }
         // 检查是否是504超时错误
         const response = info.file.response;
         const error = info.file.error;
@@ -155,6 +172,8 @@ const KnowledgeLibInfo : React.FC = () => {
             responseStr.includes('<h1>504 Gateway Time-out</h1>') ||
             errorStr.includes('timeout') || 
             errorStr.includes('504')) {
+          info.file.response = '上传文件超时,请修改文件后再上传';
+          info.file.error = '上传文件超时,请修改文件后再上传';
           message.error( '上传文件超时,请修改文件后再上传' );
         } else {
           message.error( `${ info.file.name } 文件上传失败` );
@@ -192,7 +211,7 @@ const KnowledgeLibInfo : React.FC = () => {
       if (err?.response?.status === 504 || err?.code === 'ECONNABORTED' || String(err).includes('timeout')) {
         message.error( '上传文件超时,请修改文件后再上传' );
       } else {
-        message.error( '上传失败' );
+      message.error( '上传失败' );
       }
     } finally {
       setUploading( false );
@@ -412,6 +431,7 @@ const KnowledgeLibInfo : React.FC = () => {
                     title={<span style={{ fontWeight: 600 }}>上传知识文档</span>}
                   >
                     <div style={{ color: '#666', fontSize: 12 }}>支持多种格式(Word、PDF、图片等),单个/总大小有上限,上传后自动处理(约5-10分钟,如遇到超时,可等待几分钟后刷新页面,将自动成功)。</div>
+                    {/* <div style={{ color: '#666', fontSize: 12 }}>支持pdf,doc,docx文件格式上传,建议单个文件不要超过10M,页数控制在100页以内。尽量单文件上传。</div> */}
                     <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 1</div>
                   </Card>
                 </Col>
@@ -477,9 +497,10 @@ const KnowledgeLibInfo : React.FC = () => {
                           点击上传,或拖放文件到此处
                         </p>
                         <p className="ant-upload-hint">
-                          支持文件格式md,txt,pdf,jpg,png,jpeg,docx,xlsx,
+                          {/* 支持文件格式md,txt,pdf,jpg,png,jpeg,docx,xlsx,
                           pptx,eml,csv,单个文档 ≤ 30M,单张图片 ≤ 2.5M,文件总
-                          大小不得超过125M.
+                          大小不得超过125M. */}
+                          支持pdf,doc,docx,jpg,png,jpeg文件格式上传,建议单个文件不要超过10M,页数控制在100页以内。尽量单文件上传。
                         </p>
                       </Dragger>
                     </div>

+ 19 - 15
src/pages/deepseek/questionAnswer/info/index.tsx

@@ -623,7 +623,7 @@ const QuestionAnswerInfo: React.FC = () => {
             } catch (error: any) {
                 // 检查是否是403错误
                 if (error?.response?.status === 403) {
-                    message.error('您提交的信息内容非法,访问已被防火墙拒绝');
+                    message.error('检测到您的此次提交请求未能通过安全验证, 请调整后重新提交。');
                 } else {
                     console.error('提交失败:', error);
                     // 其他错误会由axios拦截器处理,这里不再重复显示错误信息
@@ -1071,20 +1071,24 @@ const QuestionAnswerInfo: React.FC = () => {
 如果在知识片段中找不到答案,你可以运用自身知识回答,但必须明确告知用户“此信息未在知识库中明确记载,仅供参考”。
 如果用户问题模糊,你必须根据知识片段内容,主动询问用户可能想问的具体问题。例如,用户提问“标准”,你可以回复“您是否想了解‘高处作业安全’相关的法规与标准?”
 
-二、 信息来源引用规则(针对URL)
-回答中涉及的每一个事实或结论,只要来源于某个知识片段,就必须在其后明确标注该片段的下载链接。
-引用格式:在相关句子或段落的结尾,使用方括号数字上标进行标记,例如“这是某个规定或描述[1]”。然后在回答的最后,单独列出所有引用来源。
-来源列表格式:另起一行,写“信息来源:”,然后按引用顺序列出编号和对应的完整下载链接。例如:
-信息来源:
-[1] http://****.****.com/share?code=TDTATFG0
-[2] http://***.****.info/share?code=h5PBNUQA
-严禁编造、修改或混淆下载链接,必须直接使用知识片段中提供的原始链接。
-
-三、 示意图占位符处理规则
-必须按固定格式返回示意图占位符,例如【示意图序号a29375108162406318082】。
-返回占位符时,必须同时带上知识片段中描述该占位符的原文。例如,片段中有“功能介绍:【示意图序号a29375108162406318082】”,你应回复“功能介绍:【示意图序号a29375108162406318082】”。
-严禁输出知识片段中不存在的占位符。
-从回答内容中删除所有图注、序号等信息,如“(图1.1)”应删除。
+二、示意图占位符处理规则
+- 触发条件与精确复制:仅当所依据的知识片段中明确包含符合 【示意图序号_xxxxxxxxxxxxxxxxxxxxx】格式的占位符时,才可在回答中引用。引用时必须将占位符及其在源片段中的直接上下文描述文字一并原样复制,严禁任何修改、概括或截断。
+正确示例:若知识片段为“...施工流程如下【示意图序号_a29375108162406318082】...”,则回答中应为“...施工流程如下【示意图序号_a29375108162406318082】”。
+- 严禁虚构:严禁生成任何知识片段中不存在的示意图占位符或描述性文字。如果回答内容所依据的知识片段内没有示意图占位符,则整个回答中不得出现任何形式的 【示意图序号_xxxxxxxxxxxxxxxxxxxxx】格式的占位符。
+清理无关标记:从最终回答中删除所有来自知识片段的、与示意图占位符无关的图注、图表序号(如“(图1.1)”)等信息。
+
+三、 针对URL信息来源的引用规则(修订版)
+1. 引用前提:
+   - 仅当知识片段中已存在原始URL链接,且回答内容直接引用该片段时,方可标注来源。
+   - 若知识片段无URL,则回答中禁止出现任何形式的链接或引用标记(如[1])。
+2. 合规操作示例:
+   - 知识片段包含:"混凝土强度要求:... [原始链接:http://真实链接]"
+   - 合规输出:"混凝土强度需≥C30[1]。\\n信息来源:\\n[1] http://真实链接"
+3. 违规操作示例:
+   - 知识片段无链接时输出:"依据《规范》[1] http://虚构链接" ❌
+   - 修改原始链接格式 ❌
+4. 无URL时的替代方案:
+   - 若需引用无URL的知识片段,直接注明:"根据知识片段中《XX规范》第X条..."
 
 四、LaTeX公式处理规则
 公式代码保护:知识片段中如出现以美元符号包裹(例如 公式或 双美元符号包裹)或其他数学标记的LaTeX公式代码,你必须将这些代码视为纯文本并完整地、一字不差地输出在你的回答中。

+ 24 - 11
src/pages/deepseek/questionAnswer/list/index.tsx

@@ -146,12 +146,13 @@ const QuestionAnswerList: React.FC = () => {
   const isResettingRef = React.useRef(false);
 
   const appApi = {
-    fetchList: async (typeId: any, projectId: any, forceRefresh: boolean = false) => {
+    fetchList: async (typeId: any, projectId: any, forceRefresh: boolean = false, pageNumber?: number) => {
       const keyword = form.getFieldValue('keyword');
+      const currentPageNumber = pageNumber !== undefined ? pageNumber : page.pageNumber;
       
       // 如果有关键字且不是强制刷新,直接从现有数据筛选,不请求接口
       if (keyword && keyword.trim() !== '' && !forceRefresh) {
-        applyFrontendFilter(originalList, keyword, page.pageNumber);
+        applyFrontendFilter(originalList, keyword, currentPageNumber);
         return;
       }
 
@@ -161,7 +162,7 @@ const QuestionAnswerList: React.FC = () => {
         const userId = (userInfo?.id ?? '').toString();
         const res = await apis.fetchTakaiAppList({
           pageSize: page.pageSize,
-          pageNumber: page.pageNumber,
+          pageNumber: currentPageNumber,
           userId: userId,
           typeId: typeId,
           projectId: projectId?.toString(),
@@ -197,7 +198,7 @@ const QuestionAnswerList: React.FC = () => {
         // 没有关键字时,显示当前页的数据
         setList(filteredList);
         setPage({
-          pageNumber: page.pageNumber,
+          pageNumber: currentPageNumber,
           pageSize: page.pageSize,
           total: res.total,
         });
@@ -237,14 +238,15 @@ const QuestionAnswerList: React.FC = () => {
   }
 
   const indexApi = {
-    fetchIndex: async (typeId: any, projectId: any) => {
+    fetchIndex: async (typeId: any, projectId: any, pageNumber?: number) => {
       try {
         const userInfo = LocalStorage.getUserInfo();
         const userId = (userInfo?.id ?? '').toString();
         const keyword = form.getFieldValue('keyword');
+        const currentPageNumber = pageNumber !== undefined ? pageNumber : page.pageNumber;
         const res = await apis.fetchTakaiIndexCount({
           pageSize: page.pageSize,
-          pageNumber: page.pageNumber,
+          pageNumber: currentPageNumber,
           userId: userId,
           typeId: typeId,
           projectId: projectId?.toString(),
@@ -492,9 +494,9 @@ const QuestionAnswerList: React.FC = () => {
       pageSize: 10,
       total: 0,
     });
-    // 调用接口获取数据
-    await appApi.fetchList(null, null, true); // 强制刷新
-    await indexApi.fetchIndex(null, null);
+    // 调用接口获取数据,传递 pageNumber: 1 确保返回第一页
+    await appApi.fetchList(null, null, true, 1); // 强制刷新,第一页
+    await indexApi.fetchIndex(null, null, 1); // 第一页
   };
 
   /** 点击外部关闭面板 */
@@ -641,6 +643,17 @@ const QuestionAnswerList: React.FC = () => {
                 options={appProjectList}
                 placeholder="请选择项目"
                 showSearch
+                onChange={(value) => {
+                  // 项目选择器自动提交逻辑
+                  const currentProTypeId = form.getFieldValue('proTypeId');
+                  let projectId: any = value;
+                  // 如果是数组且长度为2,取第二个元素(项目ID)
+                  if (projectId instanceof Array && projectId.length === 2) {
+                    projectId = projectId[1];
+                  }
+                  appApi.fetchList(currentProTypeId, projectId, true); // 强制刷新
+                  indexApi.fetchIndex(currentProTypeId, projectId);
+                }}
               />
             </FormItem>
           )}
@@ -666,7 +679,7 @@ const QuestionAnswerList: React.FC = () => {
           {/* </FormItem> */}
           <FormItem>
             <Space size={12}>
-              <div 
+              {/* <div 
                 className="search-expand-wrapper"
                 ref={searchWrapperRef}
                 onMouseEnter={() => setIsSearchExpanded(true)}
@@ -705,7 +718,7 @@ const QuestionAnswerList: React.FC = () => {
                   icon={<SearchOutlined />}
                   className="search-button"
                 />
-              </div>
+              </div> */}
               <Tooltip title="重置">
                 <Button
                   shape="circle"

+ 13 - 0
src/pages/layout/style.less

@@ -396,6 +396,7 @@
                 background-color: #e9ecef;
                 color: #409eff;
                 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+                transform: translateY(-2px);
             }
             
             &:active {
@@ -406,6 +407,18 @@
                 margin-right: 4px;
             }
         }
+        
+        .ant-btn-primary {
+            border-radius: 6px;
+            font-weight: 500;
+            box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
+            transition: all 0.3s ease;
+            
+            &:hover {
+                transform: translateY(-2px);
+                box-shadow: 0 4px 8px rgba(24, 144, 255, 0.3);
+            }
+        }
     }
 
     /* 内容区域滚动 */