diff --git a/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx b/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx new file mode 100644 index 00000000..428b5e82 --- /dev/null +++ b/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx @@ -0,0 +1,35 @@ +import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; +import { Card, Skeleton } from 'antd'; +import { useRouter } from 'next/navigation'; +import React, { useState } from 'react'; + +const { Meta } = Card; + +interface KnowLedgeCardProps { + intro: string; + name: string; +} +const KnowledgeCard: React.FC = (props: KnowLedgeCardProps) => { + const [loading, setLoading] = useState(false); + const { name, intro } = props; + const router = useRouter(); + const handleCardEditClick = () => { + router.push('/knowledge/1/base'); + }; + return ( + , + , + ]} + bordered={false} + style={{ marginTop: 16, width: 300 }} + > + + + + + ); +}; + +export default KnowledgeCard; diff --git a/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx b/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx new file mode 100644 index 00000000..927a8308 --- /dev/null +++ b/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx @@ -0,0 +1,25 @@ +import React, { memo } from 'react'; + +import KnowledgeCard from './KnowledgeCard'; + +const list = [ + { intro: '知识库简介', name: '知识库名称' }, + { intro: '知识库简介', name: '知识库名称' }, + { intro: '知识库简介', name: '知识库名称' }, + { intro: '知识库简介', name: '知识库名称' }, + { intro: '知识库简介', name: '知识库名称' }, + { intro: '知识库简介', name: '知识库名称' }, + { intro: '知识库简介', name: '知识库名称' }, +]; + +const RenderList = memo(() => + list.map((item, index) => { + return ; + }), +); + +const KnowledgeCardList = memo(() => { + return ; +}); + +export default KnowledgeCardList; diff --git a/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx b/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx new file mode 100644 index 00000000..34efcb7f --- /dev/null +++ b/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx @@ -0,0 +1,61 @@ +import { Modal, type ModalProps } from '@lobehub/ui'; +import { Form, Input, Select } from 'antd'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Flexbox } from 'react-layout-kit'; + +const DEFAULT_FIELD_VALUE = { + // imageType: ImageType.JPG, + withBackground: true, + withFooter: false, + withPluginInfo: false, + withSystemRole: false, +}; +interface ModalCreateKnowledgeProps extends ModalProps { + toggleModal: (open: boolean) => void; +} +const CreateKnowledgeBase = memo(({ toggleModal, open }) => { + const { t } = useTranslation('chat'); + + return ( + toggleModal(false)} + onOk={() => toggleModal(false)} + open={open} + title="创建知识库" + > + + + + + + + + + + + + 文本 + 图片 + + + + + + + Bert + Word2Vec + FastText + + + + + + + ); +}); + +export default CreateKnowledgeBase; diff --git a/frontend/src/app/knowledge/(desktop)/index.tsx b/frontend/src/app/knowledge/(desktop)/index.tsx new file mode 100644 index 00000000..eca8c602 --- /dev/null +++ b/frontend/src/app/knowledge/(desktop)/index.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { FloatButton } from 'antd'; +import { Plus } from 'lucide-react'; +import dynamic from 'next/dynamic'; +import { memo, useState } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import KnowledgeCardList from './features/KnowledgeList'; +// import CreateKnowledgeBase from './features/createKnowledgeBase'; +import Layout from './layout.desktop'; + +const ModalCreateKnowledge = dynamic(() => import('./features/ModalCreateKnowledge')); + +const DesktopPage = memo(() => { + const [showModal, setShowModal] = useState(false); + return ( + <> + + + + + } onClick={() => setShowModal(true)}> + 新建知识库 + + + + > + ); +}); +export default DesktopPage; diff --git a/frontend/src/app/knowledge/(desktop)/layout.desktop.tsx b/frontend/src/app/knowledge/(desktop)/layout.desktop.tsx new file mode 100644 index 00000000..509fec90 --- /dev/null +++ b/frontend/src/app/knowledge/(desktop)/layout.desktop.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { PropsWithChildren, memo } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import AppLayoutDesktop from '@/layout/AppLayout.desktop'; +import { SidebarTabKey } from '@/store/global/initialState'; + +export default memo(({ children }: PropsWithChildren) => { + return ( + + + {children} + + + ); +}); diff --git a/frontend/src/app/knowledge/[id]/base/[fileId]/features/ModalSegment.tsx b/frontend/src/app/knowledge/[id]/base/[fileId]/features/ModalSegment.tsx new file mode 100644 index 00000000..600b69bf --- /dev/null +++ b/frontend/src/app/knowledge/[id]/base/[fileId]/features/ModalSegment.tsx @@ -0,0 +1,22 @@ +import { Input, Modal } from 'antd'; +import { memo } from 'react'; +import { Center, Flexbox } from 'react-layout-kit'; + +type ModalSegmentProps = { + open: boolean; + toggleOpen: (open: boolean) => void; +}; + +const ModalSegment = memo(({ open, toggleOpen }) => { + return ( + toggleOpen(false)} open={open} title="知识片段"> + + + + + + + ); +}); + +export default ModalSegment; diff --git a/frontend/src/app/knowledge/[id]/base/[fileId]/page.tsx b/frontend/src/app/knowledge/[id]/base/[fileId]/page.tsx new file mode 100644 index 00000000..0749f2ff --- /dev/null +++ b/frontend/src/app/knowledge/[id]/base/[fileId]/page.tsx @@ -0,0 +1,75 @@ +'use client'; + +import { Card, List } from 'antd'; +import { createStyles } from 'antd-style'; +import dynamic from 'next/dynamic'; +import React, { memo, useState } from 'react'; + +const ModalSegment = dynamic(() => import('./features/ModalSegment')); + +const data = [ + { + title: 'Title 1', + }, + { + title: 'Title 2', + }, + { + title: 'Title 3', + }, + { + title: 'Title 4', + }, + { + title: 'Title 5', + }, + { + title: 'Title 6', + }, +]; +const useStyle = createStyles(({ css, token }) => ({ + card: css` + cursor: pointer; + overflow: hidden; + &:hover { + box-shadow: 0 0 0 1px ${token.colorText}; + } + + &:active { + scale: 0.95; + } + `, +})); + +const App = memo(() => { + const { styles } = useStyle(); + const [isModalOpen, toggleOpen] = useState(false); + console.log(toggleOpen); + const handleSegmentCardClick = () => { + toggleOpen(true); + }; + return ( + <> + ( + + + Card content + + + )} + size="large" + /> + + > + ); +}); + +export default App; diff --git a/frontend/src/app/knowledge/[id]/base/features/ModalAddFile.tsx b/frontend/src/app/knowledge/[id]/base/features/ModalAddFile.tsx new file mode 100644 index 00000000..734159bc --- /dev/null +++ b/frontend/src/app/knowledge/[id]/base/features/ModalAddFile.tsx @@ -0,0 +1,50 @@ +import { InboxOutlined } from '@ant-design/icons'; +import type { UploadProps } from 'antd'; +import { Modal, Upload, message } from 'antd'; +import React, { memo } from 'react'; + +const { Dragger } = Upload; + +type ModalAddFileProps = { + open: boolean; + setModalOpen: (open: boolean) => void; +}; + +const props: UploadProps = { + action: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload', + multiple: true, + name: 'file', + onChange(info) { + const { status } = info.file; + if (status !== 'uploading') { + console.log(info.file, info.fileList); + } + if (status === 'done') { + message.success(`${info.file.name} file uploaded successfully.`); + } else if (status === 'error') { + message.error(`${info.file.name} file upload failed.`); + } + }, + onDrop(e) { + console.log('Dropped files', e.dataTransfer.files); + }, +}; + +const ModalAddFile = memo(({ open, setModalOpen }) => { + return ( + setModalOpen(false)} open={open} title="添加文件"> + + + + + Click or drag file to this area to upload + + Support for a single or bulk upload. Strictly prohibited from uploading company data or + other banned files. + + + + ); +}); + +export default ModalAddFile; diff --git a/frontend/src/app/knowledge/[id]/base/page.tsx b/frontend/src/app/knowledge/[id]/base/page.tsx new file mode 100644 index 00000000..94e1d9ea --- /dev/null +++ b/frontend/src/app/knowledge/[id]/base/page.tsx @@ -0,0 +1,112 @@ +'use client'; + +import { Button, Table } from 'antd'; +import type { TableColumnsType } from 'antd'; +import dynamic from 'next/dynamic'; +import Link from 'next/link'; +import React, { useState } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +// import ModalAddFile from './features/ModalAddFile'; +const ModalAddFile = dynamic(() => import('./features/ModalAddFile')); +interface DataType { + address: string; + age: number; + key: React.Key; + name: string; +} + +const data: DataType[] = []; +for (let i = 0; i < 46; i++) { + data.push({ + address: `London, Park Lane no. ${i}`, + age: 32, + index: i, + key: i, + name: `Edward King ${i}`, + }); +} + +const App: React.FC<{ params }> = ({ params }) => { + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [loading, setLoading] = useState(false); + const [isShowModal, setModal] = useState(false); + const onSelectChange = (newSelectedRowKeys: React.Key[]) => { + console.log('selectedRowKeys changed:', newSelectedRowKeys); + setSelectedRowKeys(newSelectedRowKeys); + }; + const columns: TableColumnsType = [ + { + dataIndex: 'index', + title: '序号', + }, + { + dataIndex: 'name', + render: (text) => {text}, + title: '文档名称', + }, + { + dataIndex: 'loader', + title: '文档加载器', + }, + { + dataIndex: 'loader', + title: '文档加载器', + }, + { + dataIndex: 'splitter', + title: '分词器', + }, + { + dataIndex: 'source', + title: '源文件', + }, + { + dataIndex: 'vector', + title: '向量库', + }, + ]; + const rowSelection = { + onChange: onSelectChange, + selectedRowKeys, + }; + const hasSelected = selectedRowKeys.length > 0; + console.log(params); + return ( + <> + + + + + 下载选中文档 + + + 重新添加至向量库 + + + 向量库删除 + + + 从知识库中删除 + + + + setModal(true)} type="primary"> + 添加文件 + + + + + + + > + ); +}; + +export default App; diff --git a/frontend/src/app/knowledge/[id]/config/page.tsx b/frontend/src/app/knowledge/[id]/config/page.tsx new file mode 100644 index 00000000..ac67ac9e --- /dev/null +++ b/frontend/src/app/knowledge/[id]/config/page.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { Form, type ItemGroup } from '@lobehub/ui'; +import { Form as AntForm, Button, Input, InputNumber, Switch } from 'antd'; +import { Settings } from 'lucide-react'; +import { memo, useCallback } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import { FORM_STYLE } from '@/const/layoutTokens'; + +const KnowledgeBaseConfig = memo(() => { + const [form] = AntForm.useForm(); + + const handleConfigChange = useCallback(async () => { + try { + const values = await form.validateFields(); + console.log('Success:', values); + } catch (errorInfo) { + console.log('Failed:', errorInfo); + } + }, [form]); + const system: ItemGroup = { + children: [ + { + children: , + label: '知识库名称', + name: 'name', + rules: [{ message: '请输入知识库名称', required: true }], + }, + { + children: , + label: '知识库简介', + name: 'intro', + rules: [{ message: '请输入知识库简介', required: true }], + }, + { + children: , + label: '单段文本最大长度', + name: 'paragraphMaxLength', + rules: [{ message: '请输入知识库名称', required: true }], + }, + { + children: , + label: '相邻文本重合长度', + name: 'paragraphOverlapLength', + rules: [{ message: '请输入知识库名称', required: true }], + }, + { + children: , + label: '文本匹配条数', + name: 'paragraphMatchCount', + rules: [{ message: '请输入知识库名称', required: true }], + }, + { + children: , + label: '开启中文标题加强', + name: 'chineseTitleEnhance', + }, + ], + icon: Settings, + title: '知识库设置', + }; + + return ( + <> + + + + 保存 + + + > + ); +}); + +export default KnowledgeBaseConfig; diff --git a/frontend/src/app/knowledge/[id]/layout.tsx b/frontend/src/app/knowledge/[id]/layout.tsx new file mode 100644 index 00000000..a2806266 --- /dev/null +++ b/frontend/src/app/knowledge/[id]/layout.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { PropsWithChildren, memo } from 'react'; +import { Center, Flexbox } from 'react-layout-kit'; + +import AppLayoutDesktop from '@/layout/AppLayout.desktop'; +import { SidebarTabKey } from '@/store/global/initialState'; + +import KnowledgeTabs from './tabs'; + +interface LayoutProps extends PropsWithChildren { + params: Record; +} +export default memo(({ children, params }) => { + console.log(params); + + return ( + + + + + 图标占位 + 知识库名称 + + + + + + {children} + + + + ); +}); diff --git a/frontend/src/app/knowledge/[id]/tabs/TabItem.tsx b/frontend/src/app/knowledge/[id]/tabs/TabItem.tsx new file mode 100644 index 00000000..22fcd99a --- /dev/null +++ b/frontend/src/app/knowledge/[id]/tabs/TabItem.tsx @@ -0,0 +1,50 @@ +import { Icon, List } from '@lobehub/ui'; +import { createStyles, useResponsive } from 'antd-style'; +import { ChevronRight, type LucideIcon } from 'lucide-react'; +import { CSSProperties, ReactNode, memo } from 'react'; + +const { Item } = List; + +const useStyles = createStyles(({ css, token, responsive }) => ({ + container: css` + position: relative; + padding-top: 16px; + padding-bottom: 16px; + border-radius: ${token.borderRadius}px; + ${responsive.mobile} { + border-radius: 0; + } + `, + noHover: css` + pointer-events: none; + `, +})); + +export interface ItemProps { + active?: boolean; + className?: string; + hoverable?: boolean; + icon: LucideIcon; + label: ReactNode; + style?: CSSProperties; +} + +const KnowledgeTabItem = memo( + ({ label, icon, hoverable = true, active = false, style, className }) => { + const { cx, styles } = useStyles(); + const { mobile } = useResponsive(); + return ( + } + className={cx(styles.container, !hoverable && styles.noHover, className)} + style={style} + title={label as string} + > + {mobile && } + + ); + }, +); + +export default KnowledgeTabItem; diff --git a/frontend/src/app/knowledge/[id]/tabs/index.tsx b/frontend/src/app/knowledge/[id]/tabs/index.tsx new file mode 100644 index 00000000..eac91458 --- /dev/null +++ b/frontend/src/app/knowledge/[id]/tabs/index.tsx @@ -0,0 +1,36 @@ +import { Settings2, Webhook } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { memo, useState } from 'react'; + +import Item from './TabItem'; + +export enum KnowledgeTabs { + Base = 'base', + Config = 'config', +} + +export interface KnowledgeTabsProps { + activeTab?: KnowledgeTabs; + params: Record; +} + +const KnowledgeTabsBox = memo(({ params }) => { + console.log(params); + const [activeTab, setActiveTab] = useState(KnowledgeTabs.Base); + const items = [ + { icon: Webhook, label: '知识库', value: KnowledgeTabs.Base }, + { icon: Settings2, label: '配置', value: KnowledgeTabs.Config }, + ]; + const router = useRouter(); + const handleTabClick = (value: KnowledgeTabs) => { + setActiveTab(value); + router.push(`/knowledge/${params.id}/${value}`); + }; + return items.map(({ value, icon, label }) => ( + handleTabClick(value)}> + + + )); +}); + +export default KnowledgeTabsBox; diff --git a/frontend/src/app/knowledge/page.tsx b/frontend/src/app/knowledge/page.tsx new file mode 100644 index 00000000..fc90ec13 --- /dev/null +++ b/frontend/src/app/knowledge/page.tsx @@ -0,0 +1,16 @@ +import DesktopPage from './(desktop)'; + +// import MobilePage from './(mobile)'; +// import SessionHydration from './components/SessionHydration'; +// import Migration from './features/Migration'; + +const Page = () => { + // const mobile = isMobileDevice(); + + // const Page = mobile ? MobilePage : DesktopPage; + const Page = DesktopPage; + + return ; +}; + +export default Page; diff --git a/frontend/src/features/SideBar/TopActions.tsx b/frontend/src/features/SideBar/TopActions.tsx index 66f5a0ab..df1a4c07 100644 --- a/frontend/src/features/SideBar/TopActions.tsx +++ b/frontend/src/features/SideBar/TopActions.tsx @@ -1,5 +1,5 @@ import { ActionIcon } from '@lobehub/ui'; -import { Compass, MessageSquare } from 'lucide-react'; +import { Compass, Library, MessageSquare } from 'lucide-react'; import Link from 'next/link'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -43,6 +43,15 @@ const TopActions = memo(({ tab }) => { title={t('tab.market')} /> + + + > ); }); diff --git a/frontend/src/store/global/slices/common/initialState.ts b/frontend/src/store/global/slices/common/initialState.ts index dcdb2b82..77a9f0da 100644 --- a/frontend/src/store/global/slices/common/initialState.ts +++ b/frontend/src/store/global/slices/common/initialState.ts @@ -2,6 +2,7 @@ import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.share export enum SidebarTabKey { Chat = 'chat', + Knowledge = 'knowledge', Market = 'market', Setting = 'settings', }
+ +
Click or drag file to this area to upload
+ Support for a single or bulk upload. Strictly prohibited from uploading company data or + other banned files. +