mirror of
				https://github.com/infiniflow/ragflow.git
				synced 2025-11-04 11:49:37 +00:00 
			
		
		
		
	feat: delete the added model #503 and display an error message when the requested file fails to parse #684 (#708)
### What problem does this PR solve? feat: delete the added model #503 feat: display an error message when the requested file fails to parse #684 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
		
							parent
							
								
									bef1bbdf3e
								
							
						
					
					
						commit
						d65ba3e4d7
					
				@ -27,7 +27,7 @@ export default defineConfig({
 | 
				
			|||||||
  devtool: 'source-map',
 | 
					  devtool: 'source-map',
 | 
				
			||||||
  proxy: {
 | 
					  proxy: {
 | 
				
			||||||
    '/v1': {
 | 
					    '/v1': {
 | 
				
			||||||
      target: 'http://123.60.95.134:9380/',
 | 
					      target: '',
 | 
				
			||||||
      changeOrigin: true,
 | 
					      changeOrigin: true,
 | 
				
			||||||
      // pathRewrite: { '^/v1': '/v1' },
 | 
					      // pathRewrite: { '^/v1': '/v1' },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1883
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1883
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -23,10 +23,10 @@
 | 
				
			|||||||
    "js-base64": "^3.7.5",
 | 
					    "js-base64": "^3.7.5",
 | 
				
			||||||
    "jsencrypt": "^3.3.2",
 | 
					    "jsencrypt": "^3.3.2",
 | 
				
			||||||
    "lodash": "^4.17.21",
 | 
					    "lodash": "^4.17.21",
 | 
				
			||||||
 | 
					    "mammoth": "^1.7.2",
 | 
				
			||||||
    "rc-tween-one": "^3.0.6",
 | 
					    "rc-tween-one": "^3.0.6",
 | 
				
			||||||
    "react-chat-elements": "^12.0.13",
 | 
					    "react-chat-elements": "^12.0.13",
 | 
				
			||||||
    "react-copy-to-clipboard": "^5.1.0",
 | 
					    "react-copy-to-clipboard": "^5.1.0",
 | 
				
			||||||
    "react-file-viewer": "^1.2.1",
 | 
					 | 
				
			||||||
    "react-i18next": "^14.0.0",
 | 
					    "react-i18next": "^14.0.0",
 | 
				
			||||||
    "react-infinite-scroll-component": "^6.1.0",
 | 
					    "react-infinite-scroll-component": "^6.1.0",
 | 
				
			||||||
    "react-markdown": "^9.0.1",
 | 
					    "react-markdown": "^9.0.1",
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ const HighlightPopup = ({
 | 
				
			|||||||
  ) : null;
 | 
					  ) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
 | 
					const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
 | 
				
			||||||
  const url = useGetDocumentUrl(documentId);
 | 
					  const getDocumentUrl = useGetDocumentUrl(documentId);
 | 
				
			||||||
  const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk);
 | 
					  const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk);
 | 
				
			||||||
  const ref = useRef<(highlight: IHighlight) => void>(() => {});
 | 
					  const ref = useRef<(highlight: IHighlight) => void>(() => {});
 | 
				
			||||||
  const [loaded, setLoaded] = useState(false);
 | 
					  const [loaded, setLoaded] = useState(false);
 | 
				
			||||||
@ -55,7 +55,7 @@ const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
 | 
				
			|||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={styles.documentContainer}>
 | 
					    <div className={styles.documentContainer}>
 | 
				
			||||||
      <PdfLoader
 | 
					      <PdfLoader
 | 
				
			||||||
        url={url}
 | 
					        url={getDocumentUrl()}
 | 
				
			||||||
        beforeLoad={<Skeleton active />}
 | 
					        beforeLoad={<Skeleton active />}
 | 
				
			||||||
        workerSrc="/pdfjs-dist/pdf.worker.min.js"
 | 
					        workerSrc="/pdfjs-dist/pdf.worker.min.js"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
 | 
				
			|||||||
@ -69,6 +69,8 @@ export const FileMimeTypeMap = {
 | 
				
			|||||||
  mp4: 'video/mp4',
 | 
					  mp4: 'video/mp4',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Domain = 'demo.ragflow.io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//#region file preview
 | 
					//#region file preview
 | 
				
			||||||
export const Images = [
 | 
					export const Images = [
 | 
				
			||||||
  'jpg',
 | 
					  'jpg',
 | 
				
			||||||
@ -84,7 +86,7 @@ export const Images = [
 | 
				
			|||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Without FileViewer
 | 
					// Without FileViewer
 | 
				
			||||||
export const ExceptiveType = ['xlsx', 'xls', 'pdf', ...Images];
 | 
					export const ExceptiveType = ['xlsx', 'xls', 'pdf', 'docx', ...Images];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SupportedPreviewDocumentTypes = ['docx', 'csv', ...ExceptiveType];
 | 
					export const SupportedPreviewDocumentTypes = [...ExceptiveType];
 | 
				
			||||||
//#endregion
 | 
					//#endregion
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,10 @@ import {
 | 
				
			|||||||
  IMyLlmValue,
 | 
					  IMyLlmValue,
 | 
				
			||||||
  IThirdOAIModelCollection,
 | 
					  IThirdOAIModelCollection,
 | 
				
			||||||
} from '@/interfaces/database/llm';
 | 
					} from '@/interfaces/database/llm';
 | 
				
			||||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
 | 
					import {
 | 
				
			||||||
 | 
					  IAddLlmRequestBody,
 | 
				
			||||||
 | 
					  IDeleteLlmRequestBody,
 | 
				
			||||||
 | 
					} from '@/interfaces/request/llm';
 | 
				
			||||||
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
 | 
					import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
 | 
				
			||||||
import { useCallback, useEffect, useMemo } from 'react';
 | 
					import { useCallback, useEffect, useMemo } from 'react';
 | 
				
			||||||
import { useDispatch, useSelector } from 'umi';
 | 
					import { useDispatch, useSelector } from 'umi';
 | 
				
			||||||
@ -211,7 +214,7 @@ export const useSaveTenantInfo = () => {
 | 
				
			|||||||
export const useAddLlm = () => {
 | 
					export const useAddLlm = () => {
 | 
				
			||||||
  const dispatch = useDispatch();
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const saveTenantInfo = useCallback(
 | 
					  const addLlm = useCallback(
 | 
				
			||||||
    (requestBody: IAddLlmRequestBody) => {
 | 
					    (requestBody: IAddLlmRequestBody) => {
 | 
				
			||||||
      return dispatch<any>({
 | 
					      return dispatch<any>({
 | 
				
			||||||
        type: 'settingModel/add_llm',
 | 
					        type: 'settingModel/add_llm',
 | 
				
			||||||
@ -221,5 +224,21 @@ export const useAddLlm = () => {
 | 
				
			|||||||
    [dispatch],
 | 
					    [dispatch],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return saveTenantInfo;
 | 
					  return addLlm;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useDeleteLlm = () => {
 | 
				
			||||||
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const deleteLlm = useCallback(
 | 
				
			||||||
 | 
					    (requestBody: IDeleteLlmRequestBody) => {
 | 
				
			||||||
 | 
					      return dispatch<any>({
 | 
				
			||||||
 | 
					        type: 'settingModel/delete_llm',
 | 
				
			||||||
 | 
					        payload: requestBody,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [dispatch],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return deleteLlm;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -4,3 +4,8 @@ export interface IAddLlmRequestBody {
 | 
				
			|||||||
  model_type: string;
 | 
					  model_type: string;
 | 
				
			||||||
  api_base?: string; // chat|embedding|speech2text|image2text
 | 
					  api_base?: string; // chat|embedding|speech2text|image2text
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDeleteLlmRequestBody {
 | 
				
			||||||
 | 
					  llm_factory: string; // Ollama
 | 
				
			||||||
 | 
					  llm_name: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -506,6 +506,7 @@ export default {
 | 
				
			|||||||
      local: 'Local uploads',
 | 
					      local: 'Local uploads',
 | 
				
			||||||
      s3: 'S3 uploads',
 | 
					      s3: 'S3 uploads',
 | 
				
			||||||
      preview: 'Preview',
 | 
					      preview: 'Preview',
 | 
				
			||||||
 | 
					      fileError: 'File error',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    footer: {
 | 
					    footer: {
 | 
				
			||||||
      profile: 'All rights reserved @ React',
 | 
					      profile: 'All rights reserved @ React',
 | 
				
			||||||
 | 
				
			|||||||
@ -469,6 +469,7 @@ export default {
 | 
				
			|||||||
      local: '本地上傳',
 | 
					      local: '本地上傳',
 | 
				
			||||||
      s3: 'S3 上傳',
 | 
					      s3: 'S3 上傳',
 | 
				
			||||||
      preview: '預覽',
 | 
					      preview: '預覽',
 | 
				
			||||||
 | 
					      fileError: '文件錯誤',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    footer: {
 | 
					    footer: {
 | 
				
			||||||
      profile: '“保留所有權利 @ react”',
 | 
					      profile: '“保留所有權利 @ react”',
 | 
				
			||||||
 | 
				
			|||||||
@ -487,6 +487,7 @@ export default {
 | 
				
			|||||||
      local: '本地上传',
 | 
					      local: '本地上传',
 | 
				
			||||||
      s3: 'S3 上传',
 | 
					      s3: 'S3 上传',
 | 
				
			||||||
      preview: '预览',
 | 
					      preview: '预览',
 | 
				
			||||||
 | 
					      fileError: '文件错误',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    footer: {
 | 
					    footer: {
 | 
				
			||||||
      profile: 'All rights reserved @ React',
 | 
					      profile: 'All rights reserved @ React',
 | 
				
			||||||
 | 
				
			|||||||
@ -55,7 +55,7 @@ const PopoverContent = ({ record }: IProps) => {
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
      key: 'process_duation',
 | 
					      key: 'process_duation',
 | 
				
			||||||
      label: t('processDuration'),
 | 
					      label: t('processDuration'),
 | 
				
			||||||
      children: record.process_duation,
 | 
					      children: `${record.process_duation} s`,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      key: 'progress_msg',
 | 
					      key: 'progress_msg',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
import LineChart from '@/components/line-chart';
 | 
					import LineChart from '@/components/line-chart';
 | 
				
			||||||
 | 
					import { Domain } from '@/constants/common';
 | 
				
			||||||
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
 | 
					import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
 | 
				
			||||||
import { IModalProps } from '@/interfaces/common';
 | 
					import { IModalProps } from '@/interfaces/common';
 | 
				
			||||||
import { IDialog, IStats } from '@/interfaces/database/chat';
 | 
					import { IDialog, IStats } from '@/interfaces/database/chat';
 | 
				
			||||||
@ -80,7 +81,9 @@ const ChatOverviewModal = ({
 | 
				
			|||||||
            <Flex gap={8} vertical>
 | 
					            <Flex gap={8} vertical>
 | 
				
			||||||
              {t('serviceApiEndpoint')}
 | 
					              {t('serviceApiEndpoint')}
 | 
				
			||||||
              <Paragraph copyable className={styles.linkText}>
 | 
					              <Paragraph copyable className={styles.linkText}>
 | 
				
			||||||
                https://demo.ragflow.io/v1/api/
 | 
					                https://
 | 
				
			||||||
 | 
					                {location.hostname === Domain ? Domain : '<YOUR_MACHINE_IP>'}
 | 
				
			||||||
 | 
					                /v1/api/
 | 
				
			||||||
              </Paragraph>
 | 
					              </Paragraph>
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
            <Space size={'middle'}>
 | 
					            <Space size={'middle'}>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import CopyToClipboard from '@/components/copy-to-clipboard';
 | 
					import CopyToClipboard from '@/components/copy-to-clipboard';
 | 
				
			||||||
import HightLightMarkdown from '@/components/highlight-markdown';
 | 
					import HightLightMarkdown from '@/components/highlight-markdown';
 | 
				
			||||||
 | 
					import { Domain } from '@/constants/common';
 | 
				
			||||||
import { useTranslate } from '@/hooks/commonHooks';
 | 
					import { useTranslate } from '@/hooks/commonHooks';
 | 
				
			||||||
import { IModalProps } from '@/interfaces/common';
 | 
					import { IModalProps } from '@/interfaces/common';
 | 
				
			||||||
import { Card, Modal, Tabs, TabsProps } from 'antd';
 | 
					import { Card, Modal, Tabs, TabsProps } from 'antd';
 | 
				
			||||||
@ -15,7 +16,7 @@ const EmbedModal = ({
 | 
				
			|||||||
  const text = `
 | 
					  const text = `
 | 
				
			||||||
  ~~~ html
 | 
					  ~~~ html
 | 
				
			||||||
  <iframe
 | 
					  <iframe
 | 
				
			||||||
  src="https://demo.ragflow.io/chat/share?shared_id=${token}"
 | 
					  src="https://${Domain}/chat/share?shared_id=${token}"
 | 
				
			||||||
  style="width: 100%; height: 100%; min-height: 600px"
 | 
					  style="width: 100%; height: 100%; min-height: 600px"
 | 
				
			||||||
  frameborder="0"
 | 
					  frameborder="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										281
									
								
								web/src/pages/document-viewer/docx/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								web/src/pages/document-viewer/docx/index.less
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,281 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2017 PlanGrid, Inc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.docxViewerWrapper {
 | 
				
			||||||
 | 
					  overflow-y: scroll;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .box {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  :global(.document-container) {
 | 
				
			||||||
 | 
					    padding: 30px;
 | 
				
			||||||
 | 
					    width: 700px;
 | 
				
			||||||
 | 
					    background: white;
 | 
				
			||||||
 | 
					    margin: auto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  html,
 | 
				
			||||||
 | 
					  bodyaddress,
 | 
				
			||||||
 | 
					  blockquote,
 | 
				
			||||||
 | 
					  body,
 | 
				
			||||||
 | 
					  dd,
 | 
				
			||||||
 | 
					  div,
 | 
				
			||||||
 | 
					  dl,
 | 
				
			||||||
 | 
					  dt,
 | 
				
			||||||
 | 
					  fieldset,
 | 
				
			||||||
 | 
					  form,
 | 
				
			||||||
 | 
					  frame,
 | 
				
			||||||
 | 
					  frameset,
 | 
				
			||||||
 | 
					  h1,
 | 
				
			||||||
 | 
					  h2,
 | 
				
			||||||
 | 
					  h3,
 | 
				
			||||||
 | 
					  h4,
 | 
				
			||||||
 | 
					  h5,
 | 
				
			||||||
 | 
					  h6,
 | 
				
			||||||
 | 
					  noframes,
 | 
				
			||||||
 | 
					  ol,
 | 
				
			||||||
 | 
					  p,
 | 
				
			||||||
 | 
					  ul,
 | 
				
			||||||
 | 
					  center,
 | 
				
			||||||
 | 
					  dir,
 | 
				
			||||||
 | 
					  hr,
 | 
				
			||||||
 | 
					  menu,
 | 
				
			||||||
 | 
					  pre {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    unicode-bidi: embed;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  li {
 | 
				
			||||||
 | 
					    display: list-item;
 | 
				
			||||||
 | 
					    list-style-type: disc;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  head {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  table {
 | 
				
			||||||
 | 
					    display: table;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  img {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tr {
 | 
				
			||||||
 | 
					    display: table-row;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  thead {
 | 
				
			||||||
 | 
					    display: table-header-group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tbody {
 | 
				
			||||||
 | 
					    display: table-row-group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tfoot {
 | 
				
			||||||
 | 
					    display: table-footer-group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  col {
 | 
				
			||||||
 | 
					    display: table-column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  colgroup {
 | 
				
			||||||
 | 
					    display: table-column-group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  th {
 | 
				
			||||||
 | 
					    display: table-cell;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  td {
 | 
				
			||||||
 | 
					    display: table-cell;
 | 
				
			||||||
 | 
					    border-bottom: 1px solid #ccc;
 | 
				
			||||||
 | 
					    border-right: 1px solid #ccc;
 | 
				
			||||||
 | 
					    padding: 0.2em 0.5em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  caption {
 | 
				
			||||||
 | 
					    display: table-caption;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  th {
 | 
				
			||||||
 | 
					    font-weight: bolder;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  caption {
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  body {
 | 
				
			||||||
 | 
					    margin: 8px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h1 {
 | 
				
			||||||
 | 
					    font-size: 2em;
 | 
				
			||||||
 | 
					    margin: 0.67em 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h2 {
 | 
				
			||||||
 | 
					    font-size: 1.5em;
 | 
				
			||||||
 | 
					    margin: 0.75em 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h3 {
 | 
				
			||||||
 | 
					    font-size: 1.17em;
 | 
				
			||||||
 | 
					    margin: 0.83em 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h4,
 | 
				
			||||||
 | 
					  p,
 | 
				
			||||||
 | 
					  blockquote,
 | 
				
			||||||
 | 
					  ul,
 | 
				
			||||||
 | 
					  fieldset,
 | 
				
			||||||
 | 
					  form,
 | 
				
			||||||
 | 
					  ol,
 | 
				
			||||||
 | 
					  dl,
 | 
				
			||||||
 | 
					  dir,
 | 
				
			||||||
 | 
					  menu {
 | 
				
			||||||
 | 
					    margin: 1.12em 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h5 {
 | 
				
			||||||
 | 
					    font-size: 0.83em;
 | 
				
			||||||
 | 
					    margin: 1.5em 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h6 {
 | 
				
			||||||
 | 
					    font-size: 0.75em;
 | 
				
			||||||
 | 
					    margin: 1.67em 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  h1,
 | 
				
			||||||
 | 
					  h2,
 | 
				
			||||||
 | 
					  h3,
 | 
				
			||||||
 | 
					  h4,
 | 
				
			||||||
 | 
					  h5,
 | 
				
			||||||
 | 
					  h6,
 | 
				
			||||||
 | 
					  b,
 | 
				
			||||||
 | 
					  strong {
 | 
				
			||||||
 | 
					    font-weight: bolder;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  blockquote {
 | 
				
			||||||
 | 
					    margin-left: 40px;
 | 
				
			||||||
 | 
					    margin-right: 40px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  i,
 | 
				
			||||||
 | 
					  cite,
 | 
				
			||||||
 | 
					  em,
 | 
				
			||||||
 | 
					  var,
 | 
				
			||||||
 | 
					  address {
 | 
				
			||||||
 | 
					    font-style: italic;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  pre,
 | 
				
			||||||
 | 
					  tt,
 | 
				
			||||||
 | 
					  code,
 | 
				
			||||||
 | 
					  kbd,
 | 
				
			||||||
 | 
					  samp {
 | 
				
			||||||
 | 
					    font-family: monospace;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  pre {
 | 
				
			||||||
 | 
					    white-space: pre;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  button,
 | 
				
			||||||
 | 
					  textarea,
 | 
				
			||||||
 | 
					  input,
 | 
				
			||||||
 | 
					  select {
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  big {
 | 
				
			||||||
 | 
					    font-size: 1.17em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  small,
 | 
				
			||||||
 | 
					  sub,
 | 
				
			||||||
 | 
					  sup {
 | 
				
			||||||
 | 
					    font-size: 0.83em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sub {
 | 
				
			||||||
 | 
					    vertical-align: sub;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sup {
 | 
				
			||||||
 | 
					    vertical-align: super;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  table {
 | 
				
			||||||
 | 
					    border-spacing: 2px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  thead,
 | 
				
			||||||
 | 
					  tbody,
 | 
				
			||||||
 | 
					  tfoot {
 | 
				
			||||||
 | 
					    vertical-align: middle;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  td,
 | 
				
			||||||
 | 
					  th,
 | 
				
			||||||
 | 
					  tr {
 | 
				
			||||||
 | 
					    vertical-align: inherit;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  s,
 | 
				
			||||||
 | 
					  strike,
 | 
				
			||||||
 | 
					  del {
 | 
				
			||||||
 | 
					    text-decoration: line-through;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  hr {
 | 
				
			||||||
 | 
					    border: 1px inset;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ol,
 | 
				
			||||||
 | 
					  ul,
 | 
				
			||||||
 | 
					  dir,
 | 
				
			||||||
 | 
					  menu,
 | 
				
			||||||
 | 
					  dd {
 | 
				
			||||||
 | 
					    margin-left: 40px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ol {
 | 
				
			||||||
 | 
					    list-style-type: decimal;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ol ul,
 | 
				
			||||||
 | 
					  ol ul,
 | 
				
			||||||
 | 
					  ul ol,
 | 
				
			||||||
 | 
					  ul ol,
 | 
				
			||||||
 | 
					  ul ul,
 | 
				
			||||||
 | 
					  ul ul,
 | 
				
			||||||
 | 
					  ol ol,
 | 
				
			||||||
 | 
					  ol ol {
 | 
				
			||||||
 | 
					    margin-top: 0;
 | 
				
			||||||
 | 
					    margin-bottom: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  u,
 | 
				
			||||||
 | 
					  ins {
 | 
				
			||||||
 | 
					    text-decoration: underline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  br:before {
 | 
				
			||||||
 | 
					    content: '\A';
 | 
				
			||||||
 | 
					    white-space: pre-line;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  center {
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  :link,
 | 
				
			||||||
 | 
					  :visited {
 | 
				
			||||||
 | 
					    text-decoration: underline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  :focus {
 | 
				
			||||||
 | 
					    outline: thin dotted invert;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /* Begin bidirectionality settings (do not change) */
 | 
				
			||||||
 | 
					  BDO[DIR='ltr'] {
 | 
				
			||||||
 | 
					    direction: ltr;
 | 
				
			||||||
 | 
					    unicode-bidi: bidi-override;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  BDO[DIR='rtl'] {
 | 
				
			||||||
 | 
					    direction: rtl;
 | 
				
			||||||
 | 
					    unicode-bidi: bidi-override;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  *[DIR='ltr'] {
 | 
				
			||||||
 | 
					    direction: ltr;
 | 
				
			||||||
 | 
					    unicode-bidi: embed;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  *[DIR='rtl'] {
 | 
				
			||||||
 | 
					    direction: rtl;
 | 
				
			||||||
 | 
					    unicode-bidi: embed;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  @media print {
 | 
				
			||||||
 | 
					    h1 {
 | 
				
			||||||
 | 
					      page-break-before: always;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    h1,
 | 
				
			||||||
 | 
					    h2,
 | 
				
			||||||
 | 
					    h3,
 | 
				
			||||||
 | 
					    h4,
 | 
				
			||||||
 | 
					    h5,
 | 
				
			||||||
 | 
					    h6 {
 | 
				
			||||||
 | 
					      page-break-after: avoid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ul,
 | 
				
			||||||
 | 
					    ol,
 | 
				
			||||||
 | 
					    dl {
 | 
				
			||||||
 | 
					      page-break-before: avoid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								web/src/pages/document-viewer/docx/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/pages/document-viewer/docx/index.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { Spin } from 'antd';
 | 
				
			||||||
 | 
					import FileError from '../file-error';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useFetchDocx } from '../hooks';
 | 
				
			||||||
 | 
					import styles from './index.less';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Docx = ({ filePath }: { filePath: string }) => {
 | 
				
			||||||
 | 
					  const { succeed, containerRef } = useFetchDocx(filePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      {succeed ? (
 | 
				
			||||||
 | 
					        <section className={styles.docxViewerWrapper}>
 | 
				
			||||||
 | 
					          <div id="docx" ref={containerRef} className={styles.box}>
 | 
				
			||||||
 | 
					            <Spin />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </section>
 | 
				
			||||||
 | 
					      ) : (
 | 
				
			||||||
 | 
					        <FileError></FileError>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Docx;
 | 
				
			||||||
@ -1,35 +1,19 @@
 | 
				
			|||||||
import jsPreviewExcel from '@js-preview/excel';
 | 
					 | 
				
			||||||
import '@js-preview/excel/lib/index.css';
 | 
					import '@js-preview/excel/lib/index.css';
 | 
				
			||||||
import { useEffect } from 'react';
 | 
					import FileError from '../file-error';
 | 
				
			||||||
 | 
					import { useFetchExcel } from '../hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Excel = ({ filePath }: { filePath: string }) => {
 | 
					const Excel = ({ filePath }: { filePath: string }) => {
 | 
				
			||||||
  const fetchDocument = async () => {
 | 
					  const { status, containerRef } = useFetchExcel(filePath);
 | 
				
			||||||
    const myExcelPreviewer = jsPreviewExcel.init(
 | 
					 | 
				
			||||||
      document.getElementById('excel'),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    const jsonFile = new XMLHttpRequest();
 | 
					 | 
				
			||||||
    jsonFile.open('GET', filePath, true);
 | 
					 | 
				
			||||||
    jsonFile.send();
 | 
					 | 
				
			||||||
    jsonFile.responseType = 'arraybuffer';
 | 
					 | 
				
			||||||
    jsonFile.onreadystatechange = () => {
 | 
					 | 
				
			||||||
      if (jsonFile.readyState === 4 && jsonFile.status === 200) {
 | 
					 | 
				
			||||||
        myExcelPreviewer
 | 
					 | 
				
			||||||
          .preview(jsonFile.response)
 | 
					 | 
				
			||||||
          .then((res: any) => {
 | 
					 | 
				
			||||||
            console.log('succeed');
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
          .catch((e) => {
 | 
					 | 
				
			||||||
            console.log('failed', e);
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  return (
 | 
				
			||||||
    fetchDocument();
 | 
					    <div
 | 
				
			||||||
  }, []);
 | 
					      id="excel"
 | 
				
			||||||
 | 
					      ref={containerRef}
 | 
				
			||||||
  return <div id="excel" style={{ height: '100%' }}></div>;
 | 
					      style={{ height: '100%', width: '100%' }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {status || <FileError></FileError>}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Excel;
 | 
					export default Excel;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								web/src/pages/document-viewer/file-error/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/pages/document-viewer/file-error/index.less
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					.errorWrapper {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								web/src/pages/document-viewer/file-error/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								web/src/pages/document-viewer/file-error/index.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					import { Alert, Flex } from 'antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useTranslate } from '@/hooks/commonHooks';
 | 
				
			||||||
 | 
					import styles from './index.less';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const FileError = () => {
 | 
				
			||||||
 | 
					  const { t } = useTranslate('fileManager');
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Flex align="center" justify="center" className={styles.errorWrapper}>
 | 
				
			||||||
 | 
					      <Alert type="error" message={<h1>{t('fileError')}</h1>}></Alert>
 | 
				
			||||||
 | 
					    </Flex>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default FileError;
 | 
				
			||||||
							
								
								
									
										78
									
								
								web/src/pages/document-viewer/hooks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								web/src/pages/document-viewer/hooks.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					import jsPreviewExcel from '@js-preview/excel';
 | 
				
			||||||
 | 
					import axios from 'axios';
 | 
				
			||||||
 | 
					import mammoth from 'mammoth';
 | 
				
			||||||
 | 
					import { useCallback, useEffect, useRef, useState } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useFetchDocument = () => {
 | 
				
			||||||
 | 
					  const fetchDocument = useCallback((api: string) => {
 | 
				
			||||||
 | 
					    return axios.get(api, { responseType: 'arraybuffer' });
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetchDocument;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useFetchExcel = (filePath: string) => {
 | 
				
			||||||
 | 
					  const [status, setStatus] = useState(true);
 | 
				
			||||||
 | 
					  const fetchDocument = useFetchDocument();
 | 
				
			||||||
 | 
					  const containerRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const fetchDocumentAsync = useCallback(async () => {
 | 
				
			||||||
 | 
					    let myExcelPreviewer;
 | 
				
			||||||
 | 
					    if (containerRef.current) {
 | 
				
			||||||
 | 
					      myExcelPreviewer = jsPreviewExcel.init(containerRef.current);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const jsonFile = await fetchDocument(filePath);
 | 
				
			||||||
 | 
					    myExcelPreviewer
 | 
				
			||||||
 | 
					      ?.preview(jsonFile.data)
 | 
				
			||||||
 | 
					      .then(() => {
 | 
				
			||||||
 | 
					        console.log('succeed');
 | 
				
			||||||
 | 
					        setStatus(true);
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch((e) => {
 | 
				
			||||||
 | 
					        console.warn('failed', e);
 | 
				
			||||||
 | 
					        myExcelPreviewer.destroy();
 | 
				
			||||||
 | 
					        setStatus(false);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }, [filePath, fetchDocument]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    fetchDocumentAsync();
 | 
				
			||||||
 | 
					  }, [fetchDocumentAsync]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { status, containerRef };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useFetchDocx = (filePath: string) => {
 | 
				
			||||||
 | 
					  const [succeed, setSucceed] = useState(true);
 | 
				
			||||||
 | 
					  const fetchDocument = useFetchDocument();
 | 
				
			||||||
 | 
					  const containerRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const fetchDocumentAsync = useCallback(async () => {
 | 
				
			||||||
 | 
					    const jsonFile = await fetchDocument(filePath);
 | 
				
			||||||
 | 
					    mammoth
 | 
				
			||||||
 | 
					      .convertToHtml(
 | 
				
			||||||
 | 
					        { arrayBuffer: jsonFile.data },
 | 
				
			||||||
 | 
					        { includeDefaultStyleMap: true },
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      .then((result) => {
 | 
				
			||||||
 | 
					        setSucceed(true);
 | 
				
			||||||
 | 
					        const docEl = document.createElement('div');
 | 
				
			||||||
 | 
					        docEl.className = 'document-container';
 | 
				
			||||||
 | 
					        docEl.innerHTML = result.value;
 | 
				
			||||||
 | 
					        const container = containerRef.current;
 | 
				
			||||||
 | 
					        if (container) {
 | 
				
			||||||
 | 
					          container.innerHTML = docEl.outerHTML;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch((a) => {
 | 
				
			||||||
 | 
					        setSucceed(false);
 | 
				
			||||||
 | 
					        console.warn('alexei: something went wrong', a);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }, [filePath, fetchDocument]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    fetchDocumentAsync();
 | 
				
			||||||
 | 
					  }, [fetchDocumentAsync]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { succeed, containerRef };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
import { ExceptiveType, Images } from '@/constants/common';
 | 
					import { Images } from '@/constants/common';
 | 
				
			||||||
import { api_host } from '@/utils/api';
 | 
					import { api_host } from '@/utils/api';
 | 
				
			||||||
import { Flex, Image } from 'antd';
 | 
					import { Flex, Image } from 'antd';
 | 
				
			||||||
import FileViewer from 'react-file-viewer';
 | 
					 | 
				
			||||||
import { useParams, useSearchParams } from 'umi';
 | 
					import { useParams, useSearchParams } from 'umi';
 | 
				
			||||||
 | 
					import Docx from './docx';
 | 
				
			||||||
import Excel from './excel';
 | 
					import Excel from './excel';
 | 
				
			||||||
import Pdf from './pdf';
 | 
					import Pdf from './pdf';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -10,18 +10,12 @@ import styles from './index.less';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TODO: The interface returns an incorrect content-type for the SVG.
 | 
					// TODO: The interface returns an incorrect content-type for the SVG.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isNotExceptiveType = (ext: string) => ExceptiveType.indexOf(ext) === -1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const DocumentViewer = () => {
 | 
					const DocumentViewer = () => {
 | 
				
			||||||
  const { id: documentId } = useParams();
 | 
					  const { id: documentId } = useParams();
 | 
				
			||||||
  const api = `${api_host}/file/get/${documentId}`;
 | 
					  const api = `${api_host}/file/get/${documentId}`;
 | 
				
			||||||
  const [currentQueryParameters] = useSearchParams();
 | 
					  const [currentQueryParameters] = useSearchParams();
 | 
				
			||||||
  const ext = currentQueryParameters.get('ext');
 | 
					  const ext = currentQueryParameters.get('ext');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onError = (e: any) => {
 | 
					 | 
				
			||||||
    console.error(e, 'error in file-viewer');
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <section className={styles.viewerWrapper}>
 | 
					    <section className={styles.viewerWrapper}>
 | 
				
			||||||
      {Images.includes(ext!) && (
 | 
					      {Images.includes(ext!) && (
 | 
				
			||||||
@ -31,9 +25,8 @@ const DocumentViewer = () => {
 | 
				
			|||||||
      )}
 | 
					      )}
 | 
				
			||||||
      {ext === 'pdf' && <Pdf url={api}></Pdf>}
 | 
					      {ext === 'pdf' && <Pdf url={api}></Pdf>}
 | 
				
			||||||
      {(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
 | 
					      {(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
 | 
				
			||||||
      {isNotExceptiveType(ext!) && (
 | 
					
 | 
				
			||||||
        <FileViewer fileType={ext} filePath={api} onError={onError} />
 | 
					      {ext === 'docx' && <Docx filePath={api}></Docx>}
 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { Skeleton } from 'antd';
 | 
					import { Skeleton } from 'antd';
 | 
				
			||||||
import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
 | 
					import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
 | 
				
			||||||
 | 
					import FileError from '../file-error';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IProps {
 | 
					interface IProps {
 | 
				
			||||||
  url: string;
 | 
					  url: string;
 | 
				
			||||||
@ -9,11 +10,15 @@ const DocumentPreviewer = ({ url }: IProps) => {
 | 
				
			|||||||
  const resetHash = () => {};
 | 
					  const resetHash = () => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div style={{ width: '100%' }}>
 | 
					    <div style={{ width: '100%', height: '100%' }}>
 | 
				
			||||||
      <PdfLoader
 | 
					      <PdfLoader
 | 
				
			||||||
        url={url}
 | 
					        url={url}
 | 
				
			||||||
        beforeLoad={<Skeleton active />}
 | 
					        beforeLoad={<Skeleton active />}
 | 
				
			||||||
        workerSrc="/pdfjs-dist/pdf.worker.min.js"
 | 
					        workerSrc="/pdfjs-dist/pdf.worker.min.js"
 | 
				
			||||||
 | 
					        errorMessage={<FileError></FileError>}
 | 
				
			||||||
 | 
					        onError={(e) => {
 | 
				
			||||||
 | 
					          console.warn(e);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        {(pdfDocument) => {
 | 
					        {(pdfDocument) => {
 | 
				
			||||||
          return (
 | 
					          return (
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
 | 
				
			|||||||
import { Icon, useNavigate } from 'umi';
 | 
					import { Icon, useNavigate } from 'umi';
 | 
				
			||||||
import RightPanel from './right-panel';
 | 
					import RightPanel from './right-panel';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Domain } from '@/constants/common';
 | 
				
			||||||
import styles from './index.less';
 | 
					import styles from './index.less';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Login = () => {
 | 
					const Login = () => {
 | 
				
			||||||
@ -167,7 +168,7 @@ const Login = () => {
 | 
				
			|||||||
                    Sign in with Google
 | 
					                    Sign in with Google
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                </Button> */}
 | 
					                </Button> */}
 | 
				
			||||||
                {location.host === 'demo.ragflow.io' && (
 | 
					                {location.host === Domain && (
 | 
				
			||||||
                  <Button
 | 
					                  <Button
 | 
				
			||||||
                    block
 | 
					                    block
 | 
				
			||||||
                    size="large"
 | 
					                    size="large"
 | 
				
			||||||
 | 
				
			|||||||
@ -167,6 +167,17 @@ const model: DvaModel<SettingModelState> = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return retcode;
 | 
					      return retcode;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    *delete_llm({ payload = {} }, { call, put }) {
 | 
				
			||||||
 | 
					      const { data } = yield call(userService.delete_llm, payload);
 | 
				
			||||||
 | 
					      const { retcode } = data;
 | 
				
			||||||
 | 
					      if (retcode === 0) {
 | 
				
			||||||
 | 
					        message.success(i18n.t('message.deleted'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        yield put({ type: 'my_llm' });
 | 
				
			||||||
 | 
					        yield put({ type: 'factories_list' });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return retcode;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export default model;
 | 
					export default model;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
import { useSetModalState } from '@/hooks/commonHooks';
 | 
					import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  IApiKeySavingParams,
 | 
					  IApiKeySavingParams,
 | 
				
			||||||
  ISystemModelSettingSavingParams,
 | 
					  ISystemModelSettingSavingParams,
 | 
				
			||||||
  useAddLlm,
 | 
					  useAddLlm,
 | 
				
			||||||
 | 
					  useDeleteLlm,
 | 
				
			||||||
  useFetchLlmList,
 | 
					  useFetchLlmList,
 | 
				
			||||||
  useSaveApiKey,
 | 
					  useSaveApiKey,
 | 
				
			||||||
  useSaveTenantInfo,
 | 
					  useSaveTenantInfo,
 | 
				
			||||||
@ -164,3 +165,18 @@ export const useSubmitOllama = () => {
 | 
				
			|||||||
    selectedLlmFactory,
 | 
					    selectedLlmFactory,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useHandleDeleteLlm = (llmFactory: string) => {
 | 
				
			||||||
 | 
					  const deleteLlm = useDeleteLlm();
 | 
				
			||||||
 | 
					  const showDeleteConfirm = useShowDeleteConfirm();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleDeleteLlm = (name: string) => () => {
 | 
				
			||||||
 | 
					    showDeleteConfirm({
 | 
				
			||||||
 | 
					      onOk: async () => {
 | 
				
			||||||
 | 
					        deleteLlm({ llm_factory: llmFactory, llm_name: name });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { handleDeleteLlm };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,11 @@ import {
 | 
				
			|||||||
  useFetchLlmFactoryListOnMount,
 | 
					  useFetchLlmFactoryListOnMount,
 | 
				
			||||||
  useFetchMyLlmListOnMount,
 | 
					  useFetchMyLlmListOnMount,
 | 
				
			||||||
} from '@/hooks/llmHooks';
 | 
					} from '@/hooks/llmHooks';
 | 
				
			||||||
import { SettingOutlined, UserOutlined } from '@ant-design/icons';
 | 
					import {
 | 
				
			||||||
 | 
					  CloseCircleOutlined,
 | 
				
			||||||
 | 
					  SettingOutlined,
 | 
				
			||||||
 | 
					  UserOutlined,
 | 
				
			||||||
 | 
					} from '@ant-design/icons';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Avatar,
 | 
					  Avatar,
 | 
				
			||||||
  Button,
 | 
					  Button,
 | 
				
			||||||
@ -21,6 +25,7 @@ import {
 | 
				
			|||||||
  Space,
 | 
					  Space,
 | 
				
			||||||
  Spin,
 | 
					  Spin,
 | 
				
			||||||
  Tag,
 | 
					  Tag,
 | 
				
			||||||
 | 
					  Tooltip,
 | 
				
			||||||
  Typography,
 | 
					  Typography,
 | 
				
			||||||
} from 'antd';
 | 
					} from 'antd';
 | 
				
			||||||
import { useCallback } from 'react';
 | 
					import { useCallback } from 'react';
 | 
				
			||||||
@ -28,6 +33,7 @@ import SettingTitle from '../components/setting-title';
 | 
				
			|||||||
import { isLocalLlmFactory } from '../utils';
 | 
					import { isLocalLlmFactory } from '../utils';
 | 
				
			||||||
import ApiKeyModal from './api-key-modal';
 | 
					import ApiKeyModal from './api-key-modal';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
 | 
					  useHandleDeleteLlm,
 | 
				
			||||||
  useSelectModelProvidersLoading,
 | 
					  useSelectModelProvidersLoading,
 | 
				
			||||||
  useSubmitApiKey,
 | 
					  useSubmitApiKey,
 | 
				
			||||||
  useSubmitOllama,
 | 
					  useSubmitOllama,
 | 
				
			||||||
@ -67,6 +73,7 @@ interface IModelCardProps {
 | 
				
			|||||||
const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
 | 
					const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
 | 
				
			||||||
  const { visible, switchVisible } = useSetModalState();
 | 
					  const { visible, switchVisible } = useSetModalState();
 | 
				
			||||||
  const { t } = useTranslate('setting');
 | 
					  const { t } = useTranslate('setting');
 | 
				
			||||||
 | 
					  const { handleDeleteLlm } = useHandleDeleteLlm(item.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleApiKeyClick = () => {
 | 
					  const handleApiKeyClick = () => {
 | 
				
			||||||
    clickApiKey(item.name);
 | 
					    clickApiKey(item.name);
 | 
				
			||||||
@ -113,6 +120,11 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
 | 
				
			|||||||
              <List.Item>
 | 
					              <List.Item>
 | 
				
			||||||
                <Space>
 | 
					                <Space>
 | 
				
			||||||
                  {item.name} <Tag color="#b8b8b8">{item.type}</Tag>
 | 
					                  {item.name} <Tag color="#b8b8b8">{item.type}</Tag>
 | 
				
			||||||
 | 
					                  <Tooltip title={t('delete', { keyPrefix: 'common' })}>
 | 
				
			||||||
 | 
					                    <Button type={'text'} onClick={handleDeleteLlm(item.name)}>
 | 
				
			||||||
 | 
					                      <CloseCircleOutlined style={{ color: '#D92D20' }} />
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </Tooltip>
 | 
				
			||||||
                </Space>
 | 
					                </Space>
 | 
				
			||||||
              </List.Item>
 | 
					              </List.Item>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ const {
 | 
				
			|||||||
  set_api_key,
 | 
					  set_api_key,
 | 
				
			||||||
  set_tenant_info,
 | 
					  set_tenant_info,
 | 
				
			||||||
  add_llm,
 | 
					  add_llm,
 | 
				
			||||||
 | 
					  delete_llm,
 | 
				
			||||||
} = api;
 | 
					} = api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const methods = {
 | 
					const methods = {
 | 
				
			||||||
@ -66,6 +67,10 @@ const methods = {
 | 
				
			|||||||
    url: add_llm,
 | 
					    url: add_llm,
 | 
				
			||||||
    method: 'post',
 | 
					    method: 'post',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  delete_llm: {
 | 
				
			||||||
 | 
					    url: delete_llm,
 | 
				
			||||||
 | 
					    method: 'post',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
} as const;
 | 
					} as const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const userService = registerServer<keyof typeof methods>(methods, request);
 | 
					const userService = registerServer<keyof typeof methods>(methods, request);
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@ export default {
 | 
				
			|||||||
  my_llm: `${api_host}/llm/my_llms`,
 | 
					  my_llm: `${api_host}/llm/my_llms`,
 | 
				
			||||||
  set_api_key: `${api_host}/llm/set_api_key`,
 | 
					  set_api_key: `${api_host}/llm/set_api_key`,
 | 
				
			||||||
  add_llm: `${api_host}/llm/add_llm`,
 | 
					  add_llm: `${api_host}/llm/add_llm`,
 | 
				
			||||||
 | 
					  delete_llm: `${api_host}/llm/delete_llm`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // knowledge base
 | 
					  // knowledge base
 | 
				
			||||||
  kb_list: `${api_host}/kb/list`,
 | 
					  kb_list: `${api_host}/kb/list`,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								web/typings.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								web/typings.d.ts
									
									
									
									
										vendored
									
									
								
							@ -10,7 +10,6 @@ import { LoginModelState } from '@/pages/login/model';
 | 
				
			|||||||
import { SettingModelState } from '@/pages/user-setting/model';
 | 
					import { SettingModelState } from '@/pages/user-setting/model';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module 'lodash';
 | 
					declare module 'lodash';
 | 
				
			||||||
declare module 'react-file-viewer';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function useSelector<TState = RootState, TSelected = unknown>(
 | 
					function useSelector<TState = RootState, TSelected = unknown>(
 | 
				
			||||||
  selector: (state: TState) => TSelected,
 | 
					  selector: (state: TState) => TSelected,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user