mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
fix: disable sending messages if both application and conversation are empty and add loading to all pages (#134)
* feat: add loading to all pages * fix: disable sending messages if both application and conversation are empty * feat: add chatSpin class to Spin of chat
This commit is contained in:
@ -41,8 +41,10 @@ const RenameModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
form.setFieldValue('name', initialName);
|
form.setFieldValue('name', initialName);
|
||||||
}, [initialName, form]);
|
}
|
||||||
|
}, [initialName, form, visible]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
24
web/src/hooks/chunkHooks.ts
Normal file
24
web/src/hooks/chunkHooks.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'umi';
|
||||||
|
import { useGetKnowledgeSearchParams } from './routeHook';
|
||||||
|
|
||||||
|
interface PayloadType {
|
||||||
|
doc_id: string;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFetchChunkList = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { documentId } = useGetKnowledgeSearchParams();
|
||||||
|
|
||||||
|
const fetchChunkList = useCallback(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'chunkModel/chunk_list',
|
||||||
|
payload: {
|
||||||
|
doc_id: documentId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [dispatch, documentId]);
|
||||||
|
|
||||||
|
return fetchChunkList;
|
||||||
|
};
|
||||||
@ -1,8 +1,9 @@
|
|||||||
import showDeleteConfirm from '@/components/deleting-confirm';
|
import showDeleteConfirm from '@/components/deleting-confirm';
|
||||||
import { KnowledgeSearchParams } from '@/constants/knowledge';
|
|
||||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
||||||
|
import { useGetKnowledgeSearchParams } from './routeHook';
|
||||||
|
import { useOneNamespaceEffectsLoading } from './storeHooks';
|
||||||
|
|
||||||
export const useKnowledgeBaseId = (): string => {
|
export const useKnowledgeBaseId = (): string => {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
@ -11,17 +12,6 @@ export const useKnowledgeBaseId = (): string => {
|
|||||||
return knowledgeBaseId || '';
|
return knowledgeBaseId || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetKnowledgeSearchParams = () => {
|
|
||||||
const [currentQueryParameters] = useSearchParams();
|
|
||||||
|
|
||||||
return {
|
|
||||||
documentId:
|
|
||||||
currentQueryParameters.get(KnowledgeSearchParams.DocumentId) || '',
|
|
||||||
knowledgeId:
|
|
||||||
currentQueryParameters.get(KnowledgeSearchParams.KnowledgeId) || '',
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteDocumentById = (): {
|
export const useDeleteDocumentById = (): {
|
||||||
removeDocument: (documentId: string) => Promise<number>;
|
removeDocument: (documentId: string) => Promise<number>;
|
||||||
} => {
|
} => {
|
||||||
@ -135,8 +125,9 @@ export const useFetchKnowledgeBaseConfiguration = () => {
|
|||||||
|
|
||||||
export const useFetchKnowledgeList = (
|
export const useFetchKnowledgeList = (
|
||||||
shouldFilterListWithoutDocument: boolean = false,
|
shouldFilterListWithoutDocument: boolean = false,
|
||||||
): IKnowledge[] => {
|
): { list: IKnowledge[]; loading: boolean } => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']);
|
||||||
|
|
||||||
const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
|
const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
|
||||||
const { data = [] } = knowledgeModel;
|
const { data = [] } = knowledgeModel;
|
||||||
@ -156,7 +147,7 @@ export const useFetchKnowledgeList = (
|
|||||||
fetchList();
|
fetchList();
|
||||||
}, [fetchList]);
|
}, [fetchList]);
|
||||||
|
|
||||||
return list;
|
return { list, loading };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectFileThumbnails = () => {
|
export const useSelectFileThumbnails = () => {
|
||||||
@ -189,3 +180,29 @@ export const useFetchFileThumbnails = (docIds?: Array<string>) => {
|
|||||||
|
|
||||||
return { fileThumbnails, fetchFileThumbnails };
|
return { fileThumbnails, fetchFileThumbnails };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//#region knowledge configuration
|
||||||
|
|
||||||
|
export const useUpdateKnowledge = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const saveKnowledgeConfiguration = useCallback(
|
||||||
|
(payload: any) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'kSModel/updateKb',
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return saveKnowledgeConfiguration;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectKnowledgeDetails = () => {
|
||||||
|
const knowledgeDetails: IKnowledge = useSelector(
|
||||||
|
(state: any) => state.kSModel.knowledgeDetails,
|
||||||
|
);
|
||||||
|
return knowledgeDetails;
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
export const useFetchLlmList = (
|
export const useFetchLlmList = (
|
||||||
isOnMountFetching: boolean = true,
|
|
||||||
modelType?: LlmModelType,
|
modelType?: LlmModelType,
|
||||||
|
isOnMountFetching: boolean = true,
|
||||||
) => {
|
) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useLocation } from 'umi';
|
import { KnowledgeSearchParams } from '@/constants/knowledge';
|
||||||
|
import { useLocation, useSearchParams } from 'umi';
|
||||||
|
|
||||||
export enum SegmentIndex {
|
export enum SegmentIndex {
|
||||||
Second = '2',
|
Second = '2',
|
||||||
@ -19,3 +20,14 @@ export const useSecondPathName = () => {
|
|||||||
export const useThirdPathName = () => {
|
export const useThirdPathName = () => {
|
||||||
return useSegmentedPathName(SegmentIndex.Third);
|
return useSegmentedPathName(SegmentIndex.Third);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetKnowledgeSearchParams = () => {
|
||||||
|
const [currentQueryParameters] = useSearchParams();
|
||||||
|
|
||||||
|
return {
|
||||||
|
documentId:
|
||||||
|
currentQueryParameters.get(KnowledgeSearchParams.DocumentId) || '',
|
||||||
|
knowledgeId:
|
||||||
|
currentQueryParameters.get(KnowledgeSearchParams.KnowledgeId) || '',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useGetKnowledgeSearchParams } from '@/hooks/knowledgeHook';
|
import { useGetKnowledgeSearchParams } from '@/hooks/routeHook';
|
||||||
import { api_host } from '@/utils/api';
|
import { api_host } from '@/utils/api';
|
||||||
import { useSize } from 'ahooks';
|
import { useSize } from 'ahooks';
|
||||||
import { CustomTextRenderer } from 'node_modules/react-pdf/dist/esm/shared/types';
|
import { CustomTextRenderer } from 'node_modules/react-pdf/dist/esm/shared/types';
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
import { buildChunkHighlights } from '@/utils/documentUtils';
|
import { buildChunkHighlights } from '@/utils/documentUtils';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
@ -46,3 +47,11 @@ export const useGetChunkHighlights = (
|
|||||||
|
|
||||||
return highlights;
|
return highlights;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSelectChunkListLoading = () => {
|
||||||
|
return useOneNamespaceEffectsLoading('chunkModel', [
|
||||||
|
'create_hunk',
|
||||||
|
'chunk_list',
|
||||||
|
'switch_chunk',
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|||||||
@ -1,23 +1,22 @@
|
|||||||
|
import { useFetchChunkList } from '@/hooks/chunkHooks';
|
||||||
import { useDeleteChunkByIds } from '@/hooks/knowledgeHook';
|
import { useDeleteChunkByIds } from '@/hooks/knowledgeHook';
|
||||||
import { getOneNamespaceEffectsLoading } from '@/utils/storeUtil';
|
|
||||||
import type { PaginationProps } from 'antd';
|
import type { PaginationProps } from 'antd';
|
||||||
import { Divider, Flex, Pagination, Space, Spin, message } from 'antd';
|
import { Divider, Flex, Pagination, Space, Spin, message } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
||||||
import ChunkCard from './components/chunk-card';
|
import ChunkCard from './components/chunk-card';
|
||||||
import CreatingModal from './components/chunk-creating-modal';
|
import CreatingModal from './components/chunk-creating-modal';
|
||||||
import ChunkToolBar from './components/chunk-toolbar';
|
import ChunkToolBar from './components/chunk-toolbar';
|
||||||
// import DocumentPreview from './components/document-preview';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import DocumentPreview from './components/document-preview/preview';
|
import DocumentPreview from './components/document-preview/preview';
|
||||||
import { useHandleChunkCardClick, useSelectDocumentInfo } from './hooks';
|
import {
|
||||||
|
useHandleChunkCardClick,
|
||||||
|
useSelectChunkListLoading,
|
||||||
|
useSelectDocumentInfo,
|
||||||
|
} from './hooks';
|
||||||
import { ChunkModelState } from './model';
|
import { ChunkModelState } from './model';
|
||||||
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
interface PayloadType {
|
|
||||||
doc_id: string;
|
|
||||||
keywords?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Chunk = () => {
|
const Chunk = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -27,12 +26,7 @@ const Chunk = () => {
|
|||||||
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const { data = [], total, pagination } = chunkModel;
|
const { data = [], total, pagination } = chunkModel;
|
||||||
const effects = useSelector((state: any) => state.loading.effects);
|
const loading = useSelectChunkListLoading();
|
||||||
const loading = getOneNamespaceEffectsLoading('chunkModel', effects, [
|
|
||||||
'create_hunk',
|
|
||||||
'chunk_list',
|
|
||||||
'switch_chunk',
|
|
||||||
]);
|
|
||||||
const documentId: string = searchParams.get('doc_id') || '';
|
const documentId: string = searchParams.get('doc_id') || '';
|
||||||
const [chunkId, setChunkId] = useState<string | undefined>();
|
const [chunkId, setChunkId] = useState<string | undefined>();
|
||||||
const { removeChunk } = useDeleteChunkByIds();
|
const { removeChunk } = useDeleteChunkByIds();
|
||||||
@ -40,18 +34,7 @@ const Chunk = () => {
|
|||||||
const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick();
|
const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick();
|
||||||
const isPdf = documentInfo.type === 'pdf';
|
const isPdf = documentInfo.type === 'pdf';
|
||||||
|
|
||||||
const getChunkList = useCallback(() => {
|
const getChunkList = useFetchChunkList();
|
||||||
const payload: PayloadType = {
|
|
||||||
doc_id: documentId,
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'chunkModel/chunk_list',
|
|
||||||
payload: {
|
|
||||||
...payload,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [dispatch, documentId]);
|
|
||||||
|
|
||||||
const handleEditChunk = useCallback(
|
const handleEditChunk = useCallback(
|
||||||
(chunk_id?: string) => {
|
(chunk_id?: string) => {
|
||||||
@ -169,8 +152,8 @@ const Chunk = () => {
|
|||||||
vertical
|
vertical
|
||||||
className={isPdf ? styles.pagePdfWrapper : styles.pageWrapper}
|
className={isPdf ? styles.pagePdfWrapper : styles.pageWrapper}
|
||||||
>
|
>
|
||||||
<div className={styles.pageContent}>
|
|
||||||
<Spin spinning={loading} className={styles.spin} size="large">
|
<Spin spinning={loading} className={styles.spin} size="large">
|
||||||
|
<div className={styles.pageContent}>
|
||||||
<Space
|
<Space
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
size={'middle'}
|
size={'middle'}
|
||||||
@ -193,8 +176,8 @@ const Chunk = () => {
|
|||||||
></ChunkCard>
|
></ChunkCard>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
</Spin>
|
|
||||||
</div>
|
</div>
|
||||||
|
</Spin>
|
||||||
<div className={styles.pageFooter}>
|
<div className={styles.pageFooter}>
|
||||||
<Pagination
|
<Pagination
|
||||||
responsive
|
responsive
|
||||||
|
|||||||
@ -52,8 +52,10 @@ const RenameModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isModalOpen) {
|
||||||
form.setFieldValue('name', initialName);
|
form.setFieldValue('name', initialName);
|
||||||
}, [initialName, documentId, form]);
|
}
|
||||||
|
}, [initialName, documentId, form, isModalOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -1,19 +1,4 @@
|
|||||||
import {
|
import { normFile } from '@/utils/fileUtil';
|
||||||
useFetchKnowledgeBaseConfiguration,
|
|
||||||
useKnowledgeBaseId,
|
|
||||||
} from '@/hooks/knowledgeHook';
|
|
||||||
import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
|
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
|
||||||
import {
|
|
||||||
useFetchTenantInfo,
|
|
||||||
useSelectParserList,
|
|
||||||
} from '@/hooks/userSettingHook';
|
|
||||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
|
||||||
import {
|
|
||||||
getBase64FromUploadFileList,
|
|
||||||
getUploadFileListFromBase64,
|
|
||||||
normFile,
|
|
||||||
} from '@/utils/fileUtil';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -26,14 +11,14 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
Slider,
|
Slider,
|
||||||
Space,
|
Space,
|
||||||
|
Spin,
|
||||||
Typography,
|
Typography,
|
||||||
Upload,
|
Upload,
|
||||||
UploadFile,
|
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import pick from 'lodash/pick';
|
import {
|
||||||
import { useEffect } from 'react';
|
useFetchKnowledgeConfigurationOnMount,
|
||||||
import { useDispatch, useSelector } from 'umi';
|
useSubmitKnowledgeConfiguration,
|
||||||
import { LlmModelType } from '../../constant';
|
} from './hooks';
|
||||||
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
@ -41,70 +26,27 @@ const { Title } = Typography;
|
|||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
const Configuration = () => {
|
const Configuration = () => {
|
||||||
const [form] = Form.useForm();
|
const { submitKnowledgeConfiguration, submitLoading } =
|
||||||
const dispatch = useDispatch();
|
useSubmitKnowledgeConfiguration();
|
||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
const { form, parserList, embeddingModelOptions, loading } =
|
||||||
const loading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
|
useFetchKnowledgeConfigurationOnMount();
|
||||||
|
|
||||||
const knowledgeDetails: IKnowledge = useSelector(
|
|
||||||
(state: any) => state.kSModel.knowledgeDetails,
|
|
||||||
);
|
|
||||||
|
|
||||||
const parserList = useSelectParserList();
|
|
||||||
|
|
||||||
const embeddingModelOptions = useSelectLlmOptions();
|
|
||||||
|
|
||||||
const onFinish = async (values: any) => {
|
|
||||||
const avatar = await getBase64FromUploadFileList(values.avatar);
|
|
||||||
dispatch({
|
|
||||||
type: 'kSModel/updateKb',
|
|
||||||
payload: {
|
|
||||||
...values,
|
|
||||||
avatar,
|
|
||||||
kb_id: knowledgeBaseId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFinishFailed = (errorInfo: any) => {
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
console.log('Failed:', errorInfo);
|
console.log('Failed:', errorInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fileList: UploadFile[] = getUploadFileListFromBase64(
|
|
||||||
knowledgeDetails.avatar,
|
|
||||||
);
|
|
||||||
|
|
||||||
form.setFieldsValue({
|
|
||||||
...pick(knowledgeDetails, [
|
|
||||||
'description',
|
|
||||||
'name',
|
|
||||||
'permission',
|
|
||||||
'embd_id',
|
|
||||||
'parser_id',
|
|
||||||
'language',
|
|
||||||
'parser_config.chunk_token_num',
|
|
||||||
]),
|
|
||||||
avatar: fileList,
|
|
||||||
});
|
|
||||||
}, [form, knowledgeDetails]);
|
|
||||||
|
|
||||||
useFetchTenantInfo();
|
|
||||||
useFetchKnowledgeBaseConfiguration();
|
|
||||||
|
|
||||||
useFetchLlmList(LlmModelType.Embedding);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.configurationWrapper}>
|
<div className={styles.configurationWrapper}>
|
||||||
<Title level={5}>Configuration</Title>
|
<Title level={5}>Configuration</Title>
|
||||||
<p>Update your knowledge base details especially parsing method here.</p>
|
<p>Update your knowledge base details especially parsing method here.</p>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
|
<Spin spinning={loading}>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
name="validateOnly"
|
name="validateOnly"
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onFinish={onFinish}
|
onFinish={submitKnowledgeConfiguration}
|
||||||
onFinishFailed={onFinishFailed}
|
onFinishFailed={onFinishFailed}
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -198,13 +140,15 @@ const Configuration = () => {
|
|||||||
{ required: true, message: 'Province is required' },
|
{ required: true, message: 'Province is required' },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Slider className={styles.variableSlider} max={2048} />
|
<Slider
|
||||||
|
className={styles.variableSlider}
|
||||||
|
max={2048}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['parser_config', 'chunk_token_num']}
|
name={['parser_config', 'chunk_token_num']}
|
||||||
noStyle
|
noStyle
|
||||||
initialValue={128}
|
|
||||||
rules={[
|
rules={[
|
||||||
{ required: true, message: 'Street is required' },
|
{ required: true, message: 'Street is required' },
|
||||||
]}
|
]}
|
||||||
@ -232,7 +176,7 @@ const Configuration = () => {
|
|||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
size={'middle'}
|
size={'middle'}
|
||||||
loading={loading}
|
loading={submitLoading}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
@ -240,6 +184,7 @@ const Configuration = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
useFetchKnowledgeBaseConfiguration,
|
||||||
|
useKnowledgeBaseId,
|
||||||
|
useSelectKnowledgeDetails,
|
||||||
|
useUpdateKnowledge,
|
||||||
|
} from '@/hooks/knowledgeHook';
|
||||||
|
import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
|
||||||
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
|
import {
|
||||||
|
useFetchTenantInfo,
|
||||||
|
useSelectParserList,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
|
import {
|
||||||
|
getBase64FromUploadFileList,
|
||||||
|
getUploadFileListFromBase64,
|
||||||
|
} from '@/utils/fileUtil';
|
||||||
|
import { Form, UploadFile } from 'antd';
|
||||||
|
import pick from 'lodash/pick';
|
||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import { LlmModelType } from '../../constant';
|
||||||
|
|
||||||
|
export const useSubmitKnowledgeConfiguration = () => {
|
||||||
|
const save = useUpdateKnowledge();
|
||||||
|
const knowledgeBaseId = useKnowledgeBaseId();
|
||||||
|
const submitLoading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
|
||||||
|
|
||||||
|
const submitKnowledgeConfiguration = useCallback(
|
||||||
|
async (values: any) => {
|
||||||
|
const avatar = await getBase64FromUploadFileList(values.avatar);
|
||||||
|
save({
|
||||||
|
...values,
|
||||||
|
avatar,
|
||||||
|
kb_id: knowledgeBaseId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[save, knowledgeBaseId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { submitKnowledgeConfiguration, submitLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchKnowledgeConfigurationOnMount = () => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const loading = useOneNamespaceEffectsLoading('kSModel', ['getKbDetail']);
|
||||||
|
|
||||||
|
const knowledgeDetails = useSelectKnowledgeDetails();
|
||||||
|
const parserList = useSelectParserList();
|
||||||
|
const embeddingModelOptions = useSelectLlmOptions();
|
||||||
|
|
||||||
|
useFetchTenantInfo();
|
||||||
|
useFetchKnowledgeBaseConfiguration();
|
||||||
|
useFetchLlmList(LlmModelType.Embedding);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fileList: UploadFile[] = getUploadFileListFromBase64(
|
||||||
|
knowledgeDetails.avatar,
|
||||||
|
);
|
||||||
|
form.setFieldsValue({
|
||||||
|
...pick(knowledgeDetails, [
|
||||||
|
'description',
|
||||||
|
'name',
|
||||||
|
'permission',
|
||||||
|
'embd_id',
|
||||||
|
'parser_id',
|
||||||
|
'language',
|
||||||
|
'parser_config.chunk_token_num',
|
||||||
|
]),
|
||||||
|
avatar: fileList,
|
||||||
|
});
|
||||||
|
}, [form, knowledgeDetails]);
|
||||||
|
|
||||||
|
return { form, parserList, embeddingModelOptions, loading };
|
||||||
|
};
|
||||||
@ -34,7 +34,7 @@ const model: DvaModel<KSModelState> = {
|
|||||||
const { data } = yield call(kbService.createKb, payload);
|
const { data } = yield call(kbService.createKb, payload);
|
||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
message.success('Created successfully!');
|
message.success('Created!');
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
@ -43,7 +43,7 @@ const model: DvaModel<KSModelState> = {
|
|||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
yield put({ type: 'getKbDetail', payload: { kb_id: payload.kb_id } });
|
yield put({ type: 'getKbDetail', payload: { kb_id: payload.kb_id } });
|
||||||
message.success('Updated successfully!');
|
message.success('Updated!');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*getKbDetail({ payload = {} }, { call, put }) {
|
*getKbDetail({ payload = {} }, { call, put }) {
|
||||||
|
|||||||
@ -37,9 +37,9 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.testingControlWrapper}>
|
<section className={styles.testingControlWrapper}>
|
||||||
<p>
|
<div>
|
||||||
<b>Retrieval testing</b>
|
<b>Retrieval testing</b>
|
||||||
</p>
|
</div>
|
||||||
<p>Final step! After success, leave the rest to Infiniflow AI.</p>
|
<p>Final step! After success, leave the rest to Infiniflow AI.</p>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<section>
|
<section>
|
||||||
@ -48,8 +48,6 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
form={form}
|
form={form}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
similarity_threshold: 0.2,
|
|
||||||
vector_similarity_weight: 0.3,
|
|
||||||
top_k: 1024,
|
top_k: 1024,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -81,12 +79,12 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
|
|||||||
</Form>
|
</Form>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<p className={styles.historyTitle}>
|
<div className={styles.historyTitle}>
|
||||||
<Space size={'middle'}>
|
<Space size={'middle'}>
|
||||||
<HistoryOutlined className={styles.historyIcon} />
|
<HistoryOutlined className={styles.historyIcon} />
|
||||||
<b>Test history</b>
|
<b>Test history</b>
|
||||||
</Space>
|
</Space>
|
||||||
</p>
|
</div>
|
||||||
<Space
|
<Space
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
size={'middle'}
|
size={'middle'}
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
|
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Form, Input, Select, Upload } from 'antd';
|
import { Form, Input, Select, Upload } from 'antd';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ISegmentedContentProps } from '../interface';
|
import { ISegmentedContentProps } from '../interface';
|
||||||
|
|
||||||
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||||
const knowledgeList = useFetchKnowledgeList(true);
|
const { list: knowledgeList } = useFetchKnowledgeList(true);
|
||||||
const knowledgeOptions = knowledgeList.map((x) => ({
|
const knowledgeOptions = knowledgeList.map((x) => ({
|
||||||
label: x.name,
|
label: x.name,
|
||||||
value: x.id,
|
value: x.id,
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import {
|
|||||||
useClickDrawer,
|
useClickDrawer,
|
||||||
useFetchConversationOnMount,
|
useFetchConversationOnMount,
|
||||||
useGetFileIcon,
|
useGetFileIcon,
|
||||||
|
useGetSendButtonDisabled,
|
||||||
useSendMessage,
|
useSendMessage,
|
||||||
} from '../hooks';
|
} from '../hooks';
|
||||||
|
|
||||||
@ -248,11 +249,15 @@ const ChatContainer = () => {
|
|||||||
addNewestConversation,
|
addNewestConversation,
|
||||||
removeLatestMessage,
|
removeLatestMessage,
|
||||||
} = useFetchConversationOnMount();
|
} = useFetchConversationOnMount();
|
||||||
const { handleInputChange, handlePressEnter, value, loading } =
|
const {
|
||||||
useSendMessage(conversation, addNewestConversation, removeLatestMessage);
|
handleInputChange,
|
||||||
|
handlePressEnter,
|
||||||
|
value,
|
||||||
|
loading: sendLoading,
|
||||||
|
} = useSendMessage(conversation, addNewestConversation, removeLatestMessage);
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
|
const disabled = useGetSendButtonDisabled();
|
||||||
useGetFileIcon();
|
useGetFileIcon();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -284,8 +289,14 @@ const ChatContainer = () => {
|
|||||||
size="large"
|
size="large"
|
||||||
placeholder="Message Resume Assistant..."
|
placeholder="Message Resume Assistant..."
|
||||||
value={value}
|
value={value}
|
||||||
|
disabled={disabled}
|
||||||
suffix={
|
suffix={
|
||||||
<Button type="primary" onClick={handlePressEnter} loading={loading}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={handlePressEnter}
|
||||||
|
loading={sendLoading}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
Send
|
Send
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -767,4 +767,16 @@ export const useClickDrawer = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSelectDialogListLoading = () => {
|
||||||
|
return useOneNamespaceEffectsLoading('chatModel', ['listDialog']);
|
||||||
|
};
|
||||||
|
export const useSelectConversationListLoading = () => {
|
||||||
|
return useOneNamespaceEffectsLoading('chatModel', ['listConversation']);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetSendButtonDisabled = () => {
|
||||||
|
const { dialogId, conversationId } = useGetChatSearchParams();
|
||||||
|
|
||||||
|
return dialogId === '' && conversationId === '';
|
||||||
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
@ -41,6 +41,14 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chatSpin {
|
||||||
|
:global(.ant-spin-container) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chatTitleCard {
|
.chatTitleCard {
|
||||||
:global(.ant-card-body) {
|
:global(.ant-card-body) {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
||||||
import { useSetModalState } from '@/hooks/commonHooks';
|
|
||||||
import { DeleteOutlined, EditOutlined, FormOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, EditOutlined, FormOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -10,6 +9,7 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
MenuProps,
|
MenuProps,
|
||||||
Space,
|
Space,
|
||||||
|
Spin,
|
||||||
Tag,
|
Tag,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { MenuItemProps } from 'antd/lib/menu/MenuItem';
|
import { MenuItemProps } from 'antd/lib/menu/MenuItem';
|
||||||
@ -29,8 +29,9 @@ import {
|
|||||||
useRemoveDialog,
|
useRemoveDialog,
|
||||||
useRenameConversation,
|
useRenameConversation,
|
||||||
useSelectConversationList,
|
useSelectConversationList,
|
||||||
|
useSelectConversationListLoading,
|
||||||
|
useSelectDialogListLoading,
|
||||||
useSelectFirstDialogOnMount,
|
useSelectFirstDialogOnMount,
|
||||||
useSetCurrentDialog,
|
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
import RenameModal from '@/components/rename-modal';
|
import RenameModal from '@/components/rename-modal';
|
||||||
@ -38,8 +39,6 @@ import styles from './index.less';
|
|||||||
|
|
||||||
const Chat = () => {
|
const Chat = () => {
|
||||||
const dialogList = useSelectFirstDialogOnMount();
|
const dialogList = useSelectFirstDialogOnMount();
|
||||||
const { visible, hideModal, showModal } = useSetModalState();
|
|
||||||
const { setCurrentDialog, currentDialog } = useSetCurrentDialog();
|
|
||||||
const { onRemoveDialog } = useRemoveDialog();
|
const { onRemoveDialog } = useRemoveDialog();
|
||||||
const { onRemoveConversation } = useRemoveConversation();
|
const { onRemoveConversation } = useRemoveConversation();
|
||||||
const { handleClickDialog } = useClickDialogCard();
|
const { handleClickDialog } = useClickDialogCard();
|
||||||
@ -70,6 +69,8 @@ const Chat = () => {
|
|||||||
hideDialogEditModal,
|
hideDialogEditModal,
|
||||||
showDialogEditModal,
|
showDialogEditModal,
|
||||||
} = useEditDialog();
|
} = useEditDialog();
|
||||||
|
const dialogLoading = useSelectDialogListLoading();
|
||||||
|
const conversationLoading = useSelectConversationListLoading();
|
||||||
|
|
||||||
useFetchDialogOnMount(dialogId, true);
|
useFetchDialogOnMount(dialogId, true);
|
||||||
|
|
||||||
@ -204,6 +205,7 @@ const Chat = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<Flex className={styles.chatAppContent} vertical gap={10}>
|
<Flex className={styles.chatAppContent} vertical gap={10}>
|
||||||
|
<Spin spinning={dialogLoading} wrapperClassName={styles.chatSpin}>
|
||||||
{dialogList.map((x) => (
|
{dialogList.map((x) => (
|
||||||
<Card
|
<Card
|
||||||
key={x.id}
|
key={x.id}
|
||||||
@ -226,13 +228,16 @@ const Chat = () => {
|
|||||||
{activated === x.id && (
|
{activated === x.id && (
|
||||||
<section>
|
<section>
|
||||||
<Dropdown menu={{ items: buildAppItems(x.id) }}>
|
<Dropdown menu={{ items: buildAppItems(x.id) }}>
|
||||||
<ChatAppCube className={styles.cubeIcon}></ChatAppCube>
|
<ChatAppCube
|
||||||
|
className={styles.cubeIcon}
|
||||||
|
></ChatAppCube>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
</Spin>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -254,6 +259,10 @@ const Chat = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<Flex vertical gap={10} className={styles.chatTitleContent}>
|
<Flex vertical gap={10} className={styles.chatTitleContent}>
|
||||||
|
<Spin
|
||||||
|
spinning={conversationLoading}
|
||||||
|
wrapperClassName={styles.chatSpin}
|
||||||
|
>
|
||||||
{conversationList.map((x) => (
|
{conversationList.map((x) => (
|
||||||
<Card
|
<Card
|
||||||
key={x.id}
|
key={x.id}
|
||||||
@ -269,14 +278,19 @@ const Chat = () => {
|
|||||||
<div>{x.name}</div>
|
<div>{x.name}</div>
|
||||||
{conversationActivated === x.id && x.id !== '' && (
|
{conversationActivated === x.id && x.id !== '' && (
|
||||||
<section>
|
<section>
|
||||||
<Dropdown menu={{ items: buildConversationItems(x.id) }}>
|
<Dropdown
|
||||||
<ChatAppCube className={styles.cubeIcon}></ChatAppCube>
|
menu={{ items: buildConversationItems(x.id) }}
|
||||||
|
>
|
||||||
|
<ChatAppCube
|
||||||
|
className={styles.cubeIcon}
|
||||||
|
></ChatAppCube>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
</Spin>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
|
import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
|
||||||
import ModalManager from '@/components/modal-manager';
|
import ModalManager from '@/components/modal-manager';
|
||||||
|
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
||||||
|
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Empty, Flex, Space } from 'antd';
|
import { Button, Empty, Flex, Space, Spin } from 'antd';
|
||||||
import KnowledgeCard from './knowledge-card';
|
import KnowledgeCard from './knowledge-card';
|
||||||
import KnowledgeCreatingModal from './knowledge-creating-modal';
|
import KnowledgeCreatingModal from './knowledge-creating-modal';
|
||||||
|
|
||||||
import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
|
|
||||||
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const Knowledge = () => {
|
const Knowledge = () => {
|
||||||
const list = useFetchKnowledgeList();
|
const { list, loading } = useFetchKnowledgeList();
|
||||||
const userInfo = useSelectUserInfo();
|
const userInfo = useSelectUserInfo();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -50,15 +50,23 @@ const Knowledge = () => {
|
|||||||
</ModalManager>
|
</ModalManager>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<Flex gap={'large'} wrap="wrap" className={styles.knowledgeCardContainer}>
|
<Spin spinning={loading}>
|
||||||
|
<Flex
|
||||||
|
gap={'large'}
|
||||||
|
wrap="wrap"
|
||||||
|
className={styles.knowledgeCardContainer}
|
||||||
|
>
|
||||||
{list.length > 0 ? (
|
{list.length > 0 ? (
|
||||||
list.map((item: any) => {
|
list.map((item: any) => {
|
||||||
return <KnowledgeCard item={item} key={item.name}></KnowledgeCard>;
|
return (
|
||||||
|
<KnowledgeCard item={item} key={item.name}></KnowledgeCard>
|
||||||
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<Empty></Empty>
|
<Empty></Empty>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
</Spin>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,5 +19,8 @@ export const useValidateSubmittable = () => {
|
|||||||
return { submittable, form };
|
return { submittable, form };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetUserInfoLoading = () =>
|
export const useSelectSubmitUserInfoLoading = () =>
|
||||||
useOneNamespaceEffectsLoading('settingModel', ['setting']);
|
useOneNamespaceEffectsLoading('settingModel', ['setting']);
|
||||||
|
|
||||||
|
export const useSelectUserInfoLoading = () =>
|
||||||
|
useOneNamespaceEffectsLoading('settingModel', ['getUserInfo']);
|
||||||
|
|||||||
@ -113,3 +113,12 @@ export const useFetchSystemModelSettingOnMount = (visible: boolean) => {
|
|||||||
|
|
||||||
return { systemSetting, allOptions };
|
return { systemSetting, allOptions };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSelectModelProvidersLoading = () => {
|
||||||
|
const loading = useOneNamespaceEffectsLoading('settingModel', [
|
||||||
|
'my_llm',
|
||||||
|
'factories_list',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return loading;
|
||||||
|
};
|
||||||
|
|||||||
@ -23,12 +23,17 @@ import {
|
|||||||
List,
|
List,
|
||||||
Row,
|
Row,
|
||||||
Space,
|
Space,
|
||||||
|
Spin,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import SettingTitle from '../components/setting-title';
|
import SettingTitle from '../components/setting-title';
|
||||||
import ApiKeyModal from './api-key-modal';
|
import ApiKeyModal from './api-key-modal';
|
||||||
import { useSubmitApiKey, useSubmitSystemModelSetting } from './hooks';
|
import {
|
||||||
|
useSelectModelProvidersLoading,
|
||||||
|
useSubmitApiKey,
|
||||||
|
useSubmitSystemModelSetting,
|
||||||
|
} from './hooks';
|
||||||
import SystemModelSettingModal from './system-model-setting-modal';
|
import SystemModelSettingModal from './system-model-setting-modal';
|
||||||
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
@ -111,6 +116,7 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
|
|||||||
const UserSettingModel = () => {
|
const UserSettingModel = () => {
|
||||||
const factoryList = useFetchLlmFactoryListOnMount();
|
const factoryList = useFetchLlmFactoryListOnMount();
|
||||||
const llmList = useFetchMyLlmListOnMount();
|
const llmList = useFetchMyLlmListOnMount();
|
||||||
|
const loading = useSelectModelProvidersLoading();
|
||||||
const {
|
const {
|
||||||
saveApiKeyLoading,
|
saveApiKeyLoading,
|
||||||
initialApiKey,
|
initialApiKey,
|
||||||
@ -191,6 +197,7 @@ const UserSettingModel = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Spin spinning={loading}>
|
||||||
<section className={styles.modelWrapper}>
|
<section className={styles.modelWrapper}>
|
||||||
<SettingTitle
|
<SettingTitle
|
||||||
title="Model Setting"
|
title="Model Setting"
|
||||||
@ -201,6 +208,7 @@ const UserSettingModel = () => {
|
|||||||
<Divider></Divider>
|
<Divider></Divider>
|
||||||
<Collapse defaultActiveKey={['1']} ghost items={items} />
|
<Collapse defaultActiveKey={['1']} ghost items={items} />
|
||||||
</section>
|
</section>
|
||||||
|
</Spin>
|
||||||
<ApiKeyModal
|
<ApiKeyModal
|
||||||
visible={apiKeyVisible}
|
visible={apiKeyVisible}
|
||||||
hideModal={hideApiKeyModal}
|
hideModal={hideApiKeyModal}
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
import { useSaveSetting, useSelectUserInfo } from '@/hooks/userSettingHook';
|
import {
|
||||||
|
useFetchUserInfo,
|
||||||
|
useSaveSetting,
|
||||||
|
useSelectUserInfo,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
import {
|
import {
|
||||||
getBase64FromUploadFileList,
|
getBase64FromUploadFileList,
|
||||||
getUploadFileListFromBase64,
|
getUploadFileListFromBase64,
|
||||||
@ -12,6 +16,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
|
Spin,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Upload,
|
Upload,
|
||||||
UploadFile,
|
UploadFile,
|
||||||
@ -19,7 +24,11 @@ import {
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import SettingTitle from '../components/setting-title';
|
import SettingTitle from '../components/setting-title';
|
||||||
import { TimezoneList } from '../constants';
|
import { TimezoneList } from '../constants';
|
||||||
import { useGetUserInfoLoading, useValidateSubmittable } from '../hooks';
|
import {
|
||||||
|
useSelectSubmitUserInfoLoading,
|
||||||
|
useSelectUserInfoLoading,
|
||||||
|
useValidateSubmittable,
|
||||||
|
} from '../hooks';
|
||||||
|
|
||||||
import parentStyles from '../index.less';
|
import parentStyles from '../index.less';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
@ -42,8 +51,10 @@ const tailLayout = {
|
|||||||
const UserSettingProfile = () => {
|
const UserSettingProfile = () => {
|
||||||
const userInfo = useSelectUserInfo();
|
const userInfo = useSelectUserInfo();
|
||||||
const saveSetting = useSaveSetting();
|
const saveSetting = useSaveSetting();
|
||||||
const loading = useGetUserInfoLoading();
|
const submitLoading = useSelectSubmitUserInfoLoading();
|
||||||
const { form, submittable } = useValidateSubmittable();
|
const { form, submittable } = useValidateSubmittable();
|
||||||
|
const loading = useSelectUserInfoLoading();
|
||||||
|
useFetchUserInfo();
|
||||||
|
|
||||||
const onFinish = async (values: any) => {
|
const onFinish = async (values: any) => {
|
||||||
const avatar = await getBase64FromUploadFileList(values.avatar);
|
const avatar = await getBase64FromUploadFileList(values.avatar);
|
||||||
@ -66,6 +77,7 @@ const UserSettingProfile = () => {
|
|||||||
description="Update your photo and personal details here."
|
description="Update your photo and personal details here."
|
||||||
></SettingTitle>
|
></SettingTitle>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
<Spin spinning={loading}>
|
||||||
<Form
|
<Form
|
||||||
colon={false}
|
colon={false}
|
||||||
name="basic"
|
name="basic"
|
||||||
@ -184,13 +196,14 @@ const UserSettingProfile = () => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
disabled={!submittable}
|
disabled={!submittable}
|
||||||
loading={loading}
|
loading={submitLoading}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
</Spin>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -48,8 +48,14 @@ export const getUploadFileListFromBase64 = (avatar: string) => {
|
|||||||
|
|
||||||
export const getBase64FromUploadFileList = async (fileList?: UploadFile[]) => {
|
export const getBase64FromUploadFileList = async (fileList?: UploadFile[]) => {
|
||||||
if (Array.isArray(fileList) && fileList.length > 0) {
|
if (Array.isArray(fileList) && fileList.length > 0) {
|
||||||
const base64 = await transformFile2Base64(fileList[0].originFileObj);
|
const file = fileList[0];
|
||||||
|
const originFileObj = file.originFileObj;
|
||||||
|
if (originFileObj) {
|
||||||
|
const base64 = await transformFile2Base64(originFileObj);
|
||||||
return base64;
|
return base64;
|
||||||
|
} else {
|
||||||
|
return file.thumbUrl;
|
||||||
|
}
|
||||||
// return fileList[0].thumbUrl; TODO: Even JPG files will be converted to base64 parameters in png format
|
// return fileList[0].thumbUrl; TODO: Even JPG files will be converted to base64 parameters in png format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user