mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-16 10:25:59 +08:00
### What problem does this PR solve? feat: Use Tree to display the knowledge base list on the search page #2247 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
f8e9a0590f
commit
68d0210e92
@ -21,6 +21,7 @@ export interface IKnowledge {
|
|||||||
update_date: string;
|
update_date: string;
|
||||||
update_time: number;
|
update_time: number;
|
||||||
vector_similarity_weight: number;
|
vector_similarity_weight: number;
|
||||||
|
embd_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Parserconfig {
|
export interface Parserconfig {
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
.searchSide {
|
.searchSide {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
:global(.ant-layout-sider-children) {
|
:global(.ant-layout-sider-children) {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
@ -42,9 +43,12 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.list {
|
.list {
|
||||||
|
padding-top: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 152px);
|
// height: 100%;
|
||||||
|
height: calc(100vh - 76px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
background-color: transparent;
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@ -53,7 +57,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.knowledgeName {
|
.knowledgeName {
|
||||||
width: 130px;
|
width: 116px;
|
||||||
|
}
|
||||||
|
.embeddingId {
|
||||||
|
width: 170px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,10 @@ import IndentedTree from '@/components/indented-tree/indented-tree';
|
|||||||
import PdfDrawer from '@/components/pdf-drawer';
|
import PdfDrawer from '@/components/pdf-drawer';
|
||||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||||
import RetrievalDocuments from '@/components/retrieval-documents';
|
import RetrievalDocuments from '@/components/retrieval-documents';
|
||||||
import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
|
import {
|
||||||
|
useNextFetchKnowledgeList,
|
||||||
|
useSelectTestingResult,
|
||||||
|
} from '@/hooks/knowledge-hooks';
|
||||||
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
|
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
|
||||||
import { IReference } from '@/interfaces/database/chat';
|
import { IReference } from '@/interfaces/database/chat';
|
||||||
import {
|
import {
|
||||||
@ -35,6 +38,11 @@ const SearchPage = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [checkedList, setCheckedList] = useState<string[]>([]);
|
const [checkedList, setCheckedList] = useState<string[]>([]);
|
||||||
const { chunks, total } = useSelectTestingResult();
|
const { chunks, total } = useSelectTestingResult();
|
||||||
|
const { list: knowledgeList } = useNextFetchKnowledgeList();
|
||||||
|
const checkedWithoutEmbeddingIdList = useMemo(() => {
|
||||||
|
return checkedList.filter((x) => knowledgeList.some((y) => y.id === x));
|
||||||
|
}, [checkedList, knowledgeList]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
sendQuestion,
|
sendQuestion,
|
||||||
handleClickRelatedQuestion,
|
handleClickRelatedQuestion,
|
||||||
@ -50,7 +58,7 @@ const SearchPage = () => {
|
|||||||
loading,
|
loading,
|
||||||
isFirstRender,
|
isFirstRender,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
} = useSendQuestion(checkedList);
|
} = useSendQuestion(checkedWithoutEmbeddingIdList);
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
const imgUrl = useFetchBackgroundImage();
|
const imgUrl = useFetchBackgroundImage();
|
||||||
@ -79,7 +87,7 @@ const SearchPage = () => {
|
|||||||
onSearch={sendQuestion}
|
onSearch={sendQuestion}
|
||||||
size="large"
|
size="large"
|
||||||
loading={sendingLoading}
|
loading={sendingLoading}
|
||||||
disabled={checkedList.length === 0}
|
disabled={checkedWithoutEmbeddingIdList.length === 0}
|
||||||
className={isFirstRender ? styles.globalInput : styles.partialInput}
|
className={isFirstRender ? styles.globalInput : styles.partialInput}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -92,7 +100,7 @@ const SearchPage = () => {
|
|||||||
>
|
>
|
||||||
<SearchSidebar
|
<SearchSidebar
|
||||||
isFirstRender={isFirstRender}
|
isFirstRender={isFirstRender}
|
||||||
checkedList={checkedList}
|
checkedList={checkedWithoutEmbeddingIdList}
|
||||||
setCheckedList={setCheckedList}
|
setCheckedList={setCheckedList}
|
||||||
></SearchSidebar>
|
></SearchSidebar>
|
||||||
<Layout className={isFirstRender ? styles.mainLayout : ''}>
|
<Layout className={isFirstRender ? styles.mainLayout : ''}>
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
||||||
import { UserOutlined } from '@ant-design/icons';
|
import { UserOutlined } from '@ant-design/icons';
|
||||||
import type { CheckboxProps } from 'antd';
|
import type { TreeDataNode, TreeProps } from 'antd';
|
||||||
import { Avatar, Checkbox, Layout, List, Space, Typography } from 'antd';
|
import { Avatar, Layout, Space, Spin, Tree, Typography } from 'antd';
|
||||||
import { CheckboxChangeEvent } from 'antd/es/checkbox';
|
|
||||||
import { CheckboxValueType } from 'antd/es/checkbox/Group';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -11,6 +9,7 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
@ -29,30 +28,109 @@ const SearchSidebar = ({
|
|||||||
setCheckedList,
|
setCheckedList,
|
||||||
}: IProps) => {
|
}: IProps) => {
|
||||||
const { list, loading } = useNextFetchKnowledgeList();
|
const { list, loading } = useNextFetchKnowledgeList();
|
||||||
const ids = useMemo(() => list.map((x) => x.id), [list]);
|
|
||||||
|
|
||||||
const checkAll = list.length === checkedList.length;
|
const groupedList = useMemo(() => {
|
||||||
|
return list.reduce((pre: TreeDataNode[], cur) => {
|
||||||
|
const parentItem = pre.find((x) => x.key === cur.embd_id);
|
||||||
|
const childItem: TreeDataNode = {
|
||||||
|
title: cur.name,
|
||||||
|
key: cur.id,
|
||||||
|
isLeaf: true,
|
||||||
|
};
|
||||||
|
if (parentItem) {
|
||||||
|
parentItem.children?.push(childItem);
|
||||||
|
} else {
|
||||||
|
pre.push({
|
||||||
|
title: cur.embd_id,
|
||||||
|
key: cur.embd_id,
|
||||||
|
isLeaf: false,
|
||||||
|
children: [childItem],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const indeterminate =
|
return pre;
|
||||||
checkedList.length > 0 && checkedList.length < list.length;
|
}, []);
|
||||||
|
}, [list]);
|
||||||
|
|
||||||
const onChange = useCallback(
|
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
|
||||||
(list: CheckboxValueType[]) => {
|
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
|
||||||
setCheckedList(list as string[]);
|
const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true);
|
||||||
},
|
|
||||||
[setCheckedList],
|
const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => {
|
||||||
|
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
|
||||||
|
// or, you can remove all expanded children keys.
|
||||||
|
setExpandedKeys(expandedKeysValue);
|
||||||
|
setAutoExpandParent(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCheck: TreeProps['onCheck'] = (checkedKeysValue, info) => {
|
||||||
|
console.log('onCheck', checkedKeysValue, info);
|
||||||
|
const currentCheckedKeysValue = checkedKeysValue as string[];
|
||||||
|
|
||||||
|
let nextSelectedKeysValue: string[] = [];
|
||||||
|
const { isLeaf, checked, key, children } = info.node;
|
||||||
|
if (isLeaf) {
|
||||||
|
const item = list.find((x) => x.id === key);
|
||||||
|
if (!checked) {
|
||||||
|
const embeddingIds = currentCheckedKeysValue
|
||||||
|
.filter((x) => list.some((y) => y.id === x))
|
||||||
|
.map((x) => list.find((y) => y.id === x)?.embd_id);
|
||||||
|
|
||||||
|
if (embeddingIds.some((x) => x !== item?.embd_id)) {
|
||||||
|
nextSelectedKeysValue = [key as string];
|
||||||
|
} else {
|
||||||
|
nextSelectedKeysValue = currentCheckedKeysValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextSelectedKeysValue = currentCheckedKeysValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!checked) {
|
||||||
|
nextSelectedKeysValue = [
|
||||||
|
key as string,
|
||||||
|
...(children?.map((x) => x.key as string) ?? []),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
nextSelectedKeysValue = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCheckedList(nextSelectedKeysValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect: TreeProps['onSelect'] = (selectedKeysValue, info) => {
|
||||||
|
console.log('onSelect', info);
|
||||||
|
|
||||||
|
setSelectedKeys(selectedKeysValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTitle = useCallback(
|
||||||
|
(node: TreeDataNode) => {
|
||||||
|
const item = list.find((x) => x.id === node.key);
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{node.isLeaf && (
|
||||||
|
<Avatar size={24} icon={<UserOutlined />} src={item?.avatar} />
|
||||||
|
)}
|
||||||
|
<Typography.Text
|
||||||
|
ellipsis={{ tooltip: node.title as string }}
|
||||||
|
className={node.isLeaf ? styles.knowledgeName : styles.embeddingId}
|
||||||
|
>
|
||||||
|
{node.title as string}
|
||||||
|
</Typography.Text>
|
||||||
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
const onCheckAllChange: CheckboxProps['onChange'] = useCallback(
|
|
||||||
(e: CheckboxChangeEvent) => {
|
|
||||||
setCheckedList(e.target.checked ? ids : []);
|
|
||||||
},
|
},
|
||||||
[ids, setCheckedList],
|
[list],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCheckedList(ids);
|
const firstGroup = groupedList[0]?.children?.map((x) => x.key as string);
|
||||||
}, [ids, setCheckedList]);
|
if (firstGroup) {
|
||||||
|
setCheckedList(firstGroup);
|
||||||
|
}
|
||||||
|
setExpandedKeys(groupedList.map((x) => x.key));
|
||||||
|
}, [groupedList, setExpandedKeys, setCheckedList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sider
|
<Sider
|
||||||
@ -62,41 +140,21 @@ const SearchSidebar = ({
|
|||||||
theme={'light'}
|
theme={'light'}
|
||||||
width={240}
|
width={240}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Spin spinning={loading}>
|
||||||
className={styles.modelForm}
|
<Tree
|
||||||
indeterminate={indeterminate}
|
|
||||||
onChange={onCheckAllChange}
|
|
||||||
checked={checkAll}
|
|
||||||
>
|
|
||||||
All
|
|
||||||
</Checkbox>
|
|
||||||
<Checkbox.Group
|
|
||||||
className={styles.checkGroup}
|
|
||||||
onChange={onChange}
|
|
||||||
value={checkedList}
|
|
||||||
>
|
|
||||||
<List
|
|
||||||
bordered
|
|
||||||
dataSource={list}
|
|
||||||
className={styles.list}
|
className={styles.list}
|
||||||
loading={loading}
|
checkable
|
||||||
renderItem={(item) => (
|
onExpand={onExpand}
|
||||||
<List.Item>
|
expandedKeys={expandedKeys}
|
||||||
<Checkbox value={item.id} className={styles.checkbox}>
|
autoExpandParent={autoExpandParent}
|
||||||
<Space>
|
onCheck={onCheck}
|
||||||
<Avatar size={30} icon={<UserOutlined />} src={item.avatar} />
|
checkedKeys={checkedList}
|
||||||
<Typography.Text
|
onSelect={onSelect}
|
||||||
ellipsis={{ tooltip: item.name }}
|
selectedKeys={selectedKeys}
|
||||||
className={styles.knowledgeName}
|
treeData={groupedList}
|
||||||
>
|
titleRender={renderTitle}
|
||||||
{item.name}
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
</Checkbox>
|
|
||||||
</List.Item>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</Checkbox.Group>
|
</Spin>
|
||||||
</Sider>
|
</Sider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user