mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-11-15 17:44:13 +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