diff --git a/src/App.tsx b/src/App.tsx index 69259d4..53db1b3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,7 @@ import ProfilePage from './pages/ProfilePage'; import PricingPage from './pages/PricingPage'; import SharedSessionPage from './pages/SharedSessionPage'; import WorkflowsPage from './pages/WorkflowsPage'; +import KnowledgeBasePage from './pages/KnowledgeBase'; import { useAuth } from './store/auth'; import { AgentAPI } from './api'; import { useIsMobile } from './hooks/useIsMobile'; @@ -64,6 +65,7 @@ export default function App() { } /> } /> } /> + } /> } /> ); diff --git a/src/api/index.ts b/src/api/index.ts index be93997..2535ef4 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -16,4 +16,5 @@ export * from './llmProviders'; export * from './streamChat'; export * from './workflows'; export * from './membership'; +export * from './knowledgeBase'; diff --git a/src/api/knowledgeBase.ts b/src/api/knowledgeBase.ts new file mode 100644 index 0000000..d9a5877 --- /dev/null +++ b/src/api/knowledgeBase.ts @@ -0,0 +1,55 @@ +import { api } from './http'; + +export interface KBDTO { + id: string; + name: string; + description: string; + ownerId: string; + isPublic: boolean; + createdAt: number; + updatedAt: number; + access: 'owner' | 'view' | 'none'; +} + +export interface KBFileDTO { + id: string; + originalName: string; + filename: string; + size: number; + status: 'indexing' | 'ready' | 'error'; + chunkCount?: number; + createdAt?: number; +} + +export const KnowledgeBaseAPI = { + // 核心管理 + listKBs: () => api.get<{ data: KBDTO[] }>('/kbs').then((r) => r.data.data), + getKB: (id: string) => api.get<{ data: KBDTO }>(`/kbs/${id}`).then((r) => r.data.data), + createKB: (data: { name: string; description: string; isPublic: boolean }) => + api.post<{ data: KBDTO }>('/kbs', data).then((r) => r.data.data), + updateKB: (id: string, data: Partial<{ name: string; description: string; isPublic: boolean }>) => + api.patch<{ data: KBDTO }>(`/kbs/${id}`, data).then((r) => r.data.data), + deleteKB: (id: string) => api.delete(`/kbs/${id}`).then((r) => r.data), + + // 文件管理 + listFiles: (id: string) => api.get<{ data: KBFileDTO[] }>(`/kbs/${id}/files`).then((r) => r.data.data), + uploadFiles: (id: string, files: File[]) => { + const formData = new FormData(); + files.forEach((file) => formData.append('files', file)); + return api.post<{ data: KBFileDTO[] }>(`/kbs/${id}/files`, formData).then((r) => r.data.data); + }, + removeFile: (kbId: string, fileId: string) => + api.delete(`/kbs/${kbId}/files/${fileId}`).then((r) => r.data), + + // 授权与共享 + shareKB: (id: string, data: { subjectType: 'user' | 'team'; subjectId: string }) => + api.post(`/kbs/${id}/shares`, data).then((r) => r.data), + unshareKB: (id: string, data: { subjectType: 'user' | 'team'; subjectId: string }) => + api.delete(`/kbs/${id}/shares`, { data }).then((r) => r.data), + + // 智能体关联 + mountToAgent: (agentId: string, kbId: string) => + api.post(`/agents/${agentId}/kbs/${kbId}`).then((r) => r.data), + unmountFromAgent: (agentId: string, kbId: string) => + api.delete(`/agents/${agentId}/kbs/${kbId}`).then((r) => r.data), +}; diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index fc19aab..492f0dc 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -14,7 +14,8 @@ import { RightOutlined, UserOutlined, CreditCardOutlined, - ShoppingCartOutlined + ShoppingCartOutlined, + DatabaseOutlined } from '@ant-design/icons'; import { useAuth } from '../store/auth'; import kaiwuIcon from '../assets/brand/kaiwu-icon-gradient-transparent.png'; @@ -42,6 +43,7 @@ const NAV_GROUPS: Array<{ { label: '资源', items: [ + { to: '/knowledge', icon: , label: '知识库' }, { to: '/prompts', icon: , label: 'Prompt 模板库' }, { to: '/workflows', icon: , label: '工作流' } ] diff --git a/src/pages/KnowledgeBase/KnowledgeBaseLogic.ts b/src/pages/KnowledgeBase/KnowledgeBaseLogic.ts new file mode 100644 index 0000000..a69a33c --- /dev/null +++ b/src/pages/KnowledgeBase/KnowledgeBaseLogic.ts @@ -0,0 +1,150 @@ +import { useState, useEffect, useCallback } from 'react'; +import { message } from 'antd'; +import { KnowledgeBaseAPI, KBDTO, KBFileDTO } from '../../api/knowledgeBase'; +import { useAuth } from '../../store/auth'; + +export function useKnowledgeBaseLogic() { + const { user } = useAuth(); + const [loading, setLoading] = useState(false); + const [kbs, setKbs] = useState([]); + const [activeKb, setActiveKb] = useState(null); + const [files, setFiles] = useState([]); + const [filesLoading, setFilesLoading] = useState(false); + + // 弹窗状态 + const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [isEditModalOpen, setIsEditModalOpen] = useState(false); + const [isShareModalOpen, setIsShareModalOpen] = useState(false); + const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); + + // 获取知识库列表 + const loadKBs = useCallback(async () => { + setLoading(true); + try { + const data = await KnowledgeBaseAPI.listKBs(); + setKbs(data); + // 如果没有选中的,默认选第一个 + if (data.length > 0 && !activeKb) { + setActiveKb(data[0]); + } else if (activeKb) { + // 刷新当前选中的 KB 信息 + const updated = data.find(kb => kb.id === activeKb.id); + if (updated) setActiveKb(updated); + } + } catch (err) { + message.error('加载知识库失败'); + } finally { + setLoading(false); + } + }, [activeKb]); + + // 获取当前 KB 的文件列表 + const loadFiles = useCallback(async (kbId: string) => { + setFilesLoading(true); + try { + const data = await KnowledgeBaseAPI.listFiles(kbId); + setFiles(data); + } catch (err) { + message.error('加载文件列表失败'); + } finally { + setFilesLoading(false); + } + }, []); + + useEffect(() => { + loadKBs(); + }, []); + + useEffect(() => { + if (activeKb) { + loadFiles(activeKb.id); + } else { + setFiles([]); + } + }, [activeKb?.id, loadFiles]); + + // CRUD 操作 + const handleCreate = async (values: { name: string; description: string; isPublic: boolean }) => { + try { + const newKb = await KnowledgeBaseAPI.createKB(values); + message.success('创建成功'); + setIsCreateModalOpen(false); + await loadKBs(); + setActiveKb(newKb); + } catch (err) { + message.error('创建失败'); + } + }; + + const handleUpdate = async (values: Partial) => { + if (!activeKb) return; + try { + await KnowledgeBaseAPI.updateKB(activeKb.id, values); + message.success('更新成功'); + setIsEditModalOpen(false); + await loadKBs(); + } catch (err) { + message.error('更新失败'); + } + }; + + const handleDelete = async (id: string) => { + try { + await KnowledgeBaseAPI.deleteKB(id); + message.success('删除成功'); + if (activeKb?.id === id) { + setActiveKb(null); + } + await loadKBs(); + } catch (err) { + message.error('删除失败'); + } + }; + + const handleUpload = async (fileList: File[]) => { + if (!activeKb) return; + try { + await KnowledgeBaseAPI.uploadFiles(activeKb.id, fileList); + message.success('上传成功'); + setIsUploadModalOpen(false); + await loadFiles(activeKb.id); + } catch (err) { + message.error('上传失败'); + } + }; + + const handleRemoveFile = async (fileId: string) => { + if (!activeKb) return; + try { + await KnowledgeBaseAPI.removeFile(activeKb.id, fileId); + message.success('文件已移除'); + await loadFiles(activeKb.id); + } catch (err) { + message.error('移除失败'); + } + }; + + return { + loading, + kbs, + activeKb, + setActiveKb, + files, + filesLoading, + isCreateModalOpen, + setIsCreateModalOpen, + isEditModalOpen, + setIsEditModalOpen, + isShareModalOpen, + setIsShareModalOpen, + isUploadModalOpen, + setIsUploadModalOpen, + handleCreate, + handleUpdate, + handleDelete, + handleUpload, + handleRemoveFile, + refreshKBs: loadKBs, + refreshFiles: () => activeKb && loadFiles(activeKb.id), + }; +} diff --git a/src/pages/KnowledgeBase/components/KBDetail.tsx b/src/pages/KnowledgeBase/components/KBDetail.tsx new file mode 100644 index 0000000..d6a7625 --- /dev/null +++ b/src/pages/KnowledgeBase/components/KBDetail.tsx @@ -0,0 +1,196 @@ +import { useState, useEffect } from 'react'; +import { Button, Table, Space, Tag, Popconfirm, Tooltip, Dropdown, MenuProps } from 'antd'; +import { + UploadOutlined, + ShareAltOutlined, + DeleteOutlined, + EditOutlined, + FileTextOutlined, + EllipsisOutlined, + RobotOutlined +} from '@ant-design/icons'; +import { KBFileDTO } from '../../../api/knowledgeBase'; +import { AgentAPI, Agent } from '../../../api/agents'; +import { useAuth } from '../../../store/auth'; +import dayjs from 'dayjs'; + +interface Props { + logic: any; // 简化类型,实际开发中建议定义完整接口 +} + +export default function KBDetail({ logic }: Props) { + const { user } = useAuth(); + const { + activeKb, + files, + filesLoading, + setIsEditModalOpen, + setIsShareModalOpen, + setIsUploadModalOpen, + handleDelete, + handleRemoveFile + } = logic; + + const [agents, setAgents] = useState([]); + + useEffect(() => { + // 获取智能体列表,用于挂载 + const fetchAgents = async () => { + if (!user?.phone) return; + try { + const data = await AgentAPI.mine(user.phone); + setAgents(data); + } catch (err) { + console.error('获取智能体列表失败', err); + } + }; + fetchAgents(); + }, [user?.phone]); + + const columns = [ + { + title: '文件名', + dataIndex: 'originalName', + key: 'originalName', + render: (text: string) => ( + + + {text} + + ), + }, + { + title: '大小', + dataIndex: 'size', + key: 'size', + width: 100, + render: (size: number) => { + if (size < 1024) return `${size} B`; + if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`; + return `${(size / (1024 * 1024)).toFixed(1)} MB`; + }, + }, + { + title: '分块数', + dataIndex: 'chunkCount', + key: 'chunkCount', + width: 80, + render: (count: number) => count ?? '-', + }, + { + title: '上传时间', + dataIndex: 'createdAt', + key: 'createdAt', + width: 160, + render: (time: number) => time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '-', + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 100, + render: (status: KBFileDTO['status']) => { + const config = { + indexing: { color: 'processing', text: '索引中' }, + ready: { color: 'success', text: '就绪' }, + error: { color: 'error', text: '失败' }, + }; + const item = config[status] || config.indexing; + return {item.text}; + }, + }, + { + title: '操作', + key: 'action', + width: 80, + render: (_: any, record: KBFileDTO) => ( + handleRemoveFile(record.id)} + okText="确定" + cancelText="取消" + > + } size="small" /> + + ), + }, + ]; + + const menuItems: MenuProps['items'] = [ + { + key: 'edit', + label: '编辑信息', + icon: , + onClick: () => setIsEditModalOpen(true), + }, + { + key: 'share', + label: '共享设置', + icon: , + onClick: () => setIsShareModalOpen(true), + }, + { + type: 'divider', + }, + { + key: 'delete', + label: '删除知识库', + icon: , + danger: true, + onClick: () => handleDelete(activeKb.id), + }, + ]; + + return ( + + + + {activeKb.name} + {activeKb.description || '暂无描述'} + + + + } + onClick={() => setIsShareModalOpen(true)} // 暂时复用共享弹窗或后续独立 + > + 授权智能体 + + } + onClick={() => setIsShareModalOpen(true)} + > + 共享 + + } + onClick={() => setIsUploadModalOpen(true)} + > + 上传文件 + + + } /> + + + + + + + + 文件列表 + 共 {files.length} 个文件 + + + + + ); +} diff --git a/src/pages/KnowledgeBase/components/KBList.tsx b/src/pages/KnowledgeBase/components/KBList.tsx new file mode 100644 index 0000000..81310ef --- /dev/null +++ b/src/pages/KnowledgeBase/components/KBList.tsx @@ -0,0 +1,61 @@ +import { Spin, Tag, Empty } from 'antd'; +import { GlobalOutlined, LockOutlined, TeamOutlined } from '@ant-design/icons'; +import { KBDTO } from '../../../api/knowledgeBase'; + +interface Props { + kbs: KBDTO[]; + activeId?: string; + onSelect: (kb: KBDTO) => void; + loading: boolean; +} + +export default function KBList({ kbs, activeId, onSelect, loading }: Props) { + if (loading && kbs.length === 0) { + return ( + + + + ); + } + + if (kbs.length === 0) { + return ( + + + + ); + } + + return ( + + {kbs.map((kb) => ( + onSelect(kb)} + > + + + {kb.name} + {kb.isPublic ? ( + + ) : kb.access === 'owner' ? ( + + ) : ( + + )} + + + {kb.description || '暂无描述'} + + + + {kb.access === 'owner' ? '所有者' : '查看者'} + + + + + ))} + + ); +} diff --git a/src/pages/KnowledgeBase/components/KBModals.tsx b/src/pages/KnowledgeBase/components/KBModals.tsx new file mode 100644 index 0000000..53008ad --- /dev/null +++ b/src/pages/KnowledgeBase/components/KBModals.tsx @@ -0,0 +1,274 @@ +import { useState, useEffect } from 'react'; +import { + Modal, + Form, + Input, + Switch, + Button, + Upload, + Tabs, + Select, + Space, + message as antdMessage +} from 'antd'; +import { InboxOutlined, UserOutlined, TeamOutlined, RobotOutlined } from '@ant-design/icons'; +import { KnowledgeBaseAPI } from '../../../api/knowledgeBase'; +import { TeamAPI, Team } from '../../../api/teams'; +import { AgentAPI, Agent } from '../../../api/agents'; +import { useAuth } from '../../../store/auth'; + +const { Dragger } = Upload; + +interface Props { + logic: any; +} + +export default function KBModals({ logic }: Props) { + const { user } = useAuth(); + const { + activeKb, + isCreateModalOpen, setIsCreateModalOpen, + isEditModalOpen, setIsEditModalOpen, + isShareModalOpen, setIsShareModalOpen, + isUploadModalOpen, setIsUploadModalOpen, + handleCreate, + handleUpdate, + handleUpload, + refreshFiles + } = logic; + + const [form] = Form.useForm(); + const [editForm] = Form.useForm(); + const [fileList, setFileList] = useState([]); + const [teams, setTeams] = useState([]); + const [agents, setAgents] = useState([]); + const [sharingLoading, setSharingLoading] = useState(false); + + // 选中的目标状态 + const [selectedAgentId, setSelectedAgentId] = useState(); + const [selectedTeamId, setSelectedTeamId] = useState(); + const [selectedUserId, setSelectedUserId] = useState(''); + + useEffect(() => { + if (isEditModalOpen && activeKb) { + editForm.setFieldsValue(activeKb); + } + }, [isEditModalOpen, activeKb, editForm]); + + useEffect(() => { + if (isShareModalOpen) { + TeamAPI.list().then(setTeams).catch(() => {}); + if (user?.phone) { + AgentAPI.mine(user.phone).then(setAgents).catch(() => {}); + } + // 重置选择 + setSelectedAgentId(undefined); + setSelectedTeamId(undefined); + setSelectedUserId(''); + } + }, [isShareModalOpen, user?.phone]); + + const onShare = async (type: 'user' | 'team', id: string) => { + if (!activeKb) return; + setSharingLoading(true); + try { + await KnowledgeBaseAPI.shareKB(activeKb.id, { subjectType: type, subjectId: id }); + antdMessage.success('共享成功'); + } catch (err) { + antdMessage.error('共享失败'); + } finally { + setSharingLoading(false); + } + }; + + const onMount = async (agentId: string) => { + if (!activeKb) return; + setSharingLoading(true); + try { + await KnowledgeBaseAPI.mountToAgent(agentId, activeKb.id); + antdMessage.success('已挂载到智能体'); + } catch (err) { + antdMessage.error('挂载失败'); + } finally { + setSharingLoading(false); + } + }; + + return ( + <> + {/* Create Modal */} + setIsCreateModalOpen(false)} + footer={null} + destroyOnHidden + > + + + + + + + + + + + + + setIsCreateModalOpen(false)}>取消 + 创建 + + + + + + {/* Edit Modal */} + setIsEditModalOpen(false)} + footer={null} + destroyOnHidden + > + + + + + + + + + + + + + setIsEditModalOpen(false)}>取消 + 保存 + + + + + + {/* Upload Modal */} + setIsUploadModalOpen(false)} + onOk={() => handleUpload(fileList.map(f => f.originFileObj))} + okText="开始上传" + cancelText="取消" + destroyOnHidden + > + setFileList(fileList)} + beforeUpload={() => false} + > + + 点击或拖拽文件到此区域上传 + 支持多文件上传,单个文件最大支持 50MB + + + + {/* Share & Mount Modal */} + setIsShareModalOpen(false)} + footer={null} + width={600} + > + 授权智能体, + children: ( + + + 挂载到智能体后,该智能体的对话将自动检索此知识库。 + + + ({ label: a.name, value: a.id }))} + filterOption={(input, option) => + (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) + } + /> + selectedAgentId && onMount(selectedAgentId)} + > + 确认授权 + + + + ) + }, + { + key: 'team', + label: 共享至团队, + children: ( + + + 共享后,团队内的成员将拥有此知识库的查看权限。 + + + ({ label: t.name, value: t.id }))} + /> + selectedTeamId && onShare('team', selectedTeamId)} + > + 确认共享 + + + + ) + }, + { + key: 'user', + label: 共享至个人, + children: ( + + + 输入用户的手机号或 ID 进行共享。 + + + setSelectedUserId(e.target.value)} + /> + selectedUserId.trim() && onShare('user', selectedUserId.trim())} + > + 确认共享 + + + + ) + } + ]} /> + + > + ); +} diff --git a/src/pages/KnowledgeBase/components/KnowledgeBaseH5.tsx b/src/pages/KnowledgeBase/components/KnowledgeBaseH5.tsx new file mode 100644 index 0000000..77f92a0 --- /dev/null +++ b/src/pages/KnowledgeBase/components/KnowledgeBaseH5.tsx @@ -0,0 +1,131 @@ +import { useState } from 'react'; +import { Button, List, Tag, Space, Empty, Spin } from 'antd'; +import { + PlusOutlined, + DatabaseOutlined, + FileTextOutlined, + ShareAltOutlined, + DeleteOutlined, + ArrowLeftOutlined +} from '@ant-design/icons'; +import { useKnowledgeBaseLogic } from '../KnowledgeBaseLogic'; +import KBModals from './KBModals'; +import dayjs from 'dayjs'; +import '../styles/knowledge-base.css'; + +export default function KnowledgeBaseH5() { + const logic = useKnowledgeBaseLogic(); + const { + loading, + kbs, + activeKb, + setActiveKb, + files, + filesLoading, + setIsCreateModalOpen, + setIsUploadModalOpen, + setIsShareModalOpen, + handleRemoveFile + } = logic; + + const [view, setView] = useState<'list' | 'detail'>('list'); + + const handleSelect = (kb: any) => { + setActiveKb(kb); + setView('detail'); + }; + + if (view === 'detail' && activeKb) { + return ( + + + } type="text" onClick={() => setView('list')} /> + {activeKb.name} + } type="text" onClick={() => setIsUploadModalOpen(true)} /> + + + + + + } onClick={() => setIsShareModalOpen(true)}>共享 + } onClick={() => logic.handleDelete(activeKb.id)}>删除 + + + + 文件列表 ({files.length}) + {filesLoading ? ( + + ) : ( + ( + } + onClick={() => handleRemoveFile(file.id)} + /> + ]} + > + } + title={file.originalName} + description={ + + {(file.size / 1024).toFixed(1)} KB + {file.chunkCount && · {file.chunkCount} 分块} + {file.createdAt && · {dayjs(file.createdAt).format('MM-DD HH:mm')}} + · {file.status} + + } + /> + + )} + /> + )} + + + + ); + } + + return ( + + + 知识库 + } + onClick={() => setIsCreateModalOpen(true)} + /> + + + + {loading ? ( + + ) : kbs.length === 0 ? ( + + ) : ( + ( + handleSelect(kb)} className="kb-h5-list-item"> + } + title={kb.name} + description={kb.description || '暂无描述'} + /> + {kb.access === 'owner' ? '所有者' : '查看者'} + + )} + /> + )} + + + + ); +} diff --git a/src/pages/KnowledgeBase/components/KnowledgeBaseWeb.tsx b/src/pages/KnowledgeBase/components/KnowledgeBaseWeb.tsx new file mode 100644 index 0000000..a5a06b9 --- /dev/null +++ b/src/pages/KnowledgeBase/components/KnowledgeBaseWeb.tsx @@ -0,0 +1,85 @@ +import { Button, Spin, Empty } from 'antd'; +import { PlusOutlined, DatabaseOutlined } from '@ant-design/icons'; +import { useKnowledgeBaseLogic } from '../KnowledgeBaseLogic'; +import KBList from './KBList'; +import KBDetail from './KBDetail'; +import KBModals from './KBModals'; +import '../styles/knowledge-base.css'; + +export default function KnowledgeBaseWeb() { + const logic = useKnowledgeBaseLogic(); + const { + loading, + kbs, + activeKb, + setActiveKb, + setIsCreateModalOpen + } = logic; + + return ( + + {/* Hero Section */} + + + + + + KNOWLEDGE BASE + + 知识库管理 + + 管理您的团队和个人知识库,支持多文件上传、共享和智能体挂载,让 AI 更懂您的业务。 + + + } + size="large" + className="kb-page-web-create-btn" + onClick={() => setIsCreateModalOpen(true)} + > + 创建知识库 + + + + + {/* Main Content */} + + {/* Left Sidebar: KB List */} + + + {/* Right Content: KB Detail */} + + {loading ? ( + + + + ) : activeKb ? ( + + ) : ( + + + + )} + + + + {/* Modals */} + + + ); +} diff --git a/src/pages/KnowledgeBase/index.tsx b/src/pages/KnowledgeBase/index.tsx new file mode 100644 index 0000000..acab6ad --- /dev/null +++ b/src/pages/KnowledgeBase/index.tsx @@ -0,0 +1,8 @@ +import { useIsMobile } from '../../hooks/useIsMobile'; +import KnowledgeBaseWeb from './components/KnowledgeBaseWeb'; +import KnowledgeBaseH5 from './components/KnowledgeBaseH5'; + +export default function KnowledgeBasePage() { + const isMobile = useIsMobile(); + return isMobile ? : ; +} diff --git a/src/pages/KnowledgeBase/styles/knowledge-base.css b/src/pages/KnowledgeBase/styles/knowledge-base.css new file mode 100644 index 0000000..8fc90f8 --- /dev/null +++ b/src/pages/KnowledgeBase/styles/knowledge-base.css @@ -0,0 +1,307 @@ +.kb-page-web { + max-width: 1200px; + margin: 0 auto; + padding: 24px; +} + +.kb-page-web-hero { + border-radius: 24px; + padding: 30px 30px 26px; + margin-bottom: 24px; + background: linear-gradient(135deg, rgba(255,255,255,0.98), rgba(236,253,245,0.92) 42%, rgba(239,246,255,0.96)); + border: 1px solid rgba(8, 145, 178, 0.12); + box-shadow: 0 20px 48px rgba(15, 23, 42, 0.06); +} + +.kb-page-web-hero-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 20px; +} + +.kb-page-web-badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + border-radius: 999px; + margin-bottom: 16px; + background: rgba(255,255,255,0.78); + border: 1px solid rgba(8, 145, 178, 0.1); + color: var(--color-text-secondary); + font-size: 12px; + font-weight: 700; +} + +.kb-page-web-badge .anticon { + color: var(--color-brand); +} + +.kb-page-web-title { + margin-bottom: 8px; + font-size: 28px; + font-weight: 850; + color: var(--color-text); + letter-spacing: -0.02em; +} + +.kb-page-web-subtitle { + margin: 0; + font-size: 14.5px; + line-height: 1.6; + color: var(--color-text-secondary); + max-width: 580px; +} + +.kb-page-web-create-btn { + border-radius: 14px !important; + height: 46px !important; + padding: 0 18px !important; + font-weight: 600 !important; +} + +.kb-page-web-main-grid { + display: grid; + grid-template-columns: 280px minmax(0, 1fr); + gap: 24px; + align-items: stretch; +} + +.kb-page-web-list-panel { + background: white; + border: 1px solid var(--color-border); + border-radius: 22px; + padding: 16px; + height: fit-content; + box-shadow: 0 12px 28px rgba(15, 23, 42, 0.045); +} + +.kb-page-web-list-head { + padding: 0 8px 16px; + border-bottom: 1px solid var(--color-border-faint); + margin-bottom: 12px; +} + +.kb-page-web-section-title { + margin: 0 0 4px; + font-size: 15px; + font-weight: 800; + color: var(--color-text); +} + +.kb-page-web-section-desc { + margin: 0; + font-size: 12px; + color: var(--color-text-tertiary); +} + +.kb-page-web-list-items { + display: flex; + flex-direction: column; + gap: 8px; +} + +.kb-page-web-nav-item { + width: 100%; + border: 1px solid transparent; + border-radius: 14px; + padding: 12px; + background: transparent; + cursor: pointer; + text-align: left; + transition: all 0.2s; +} + +.kb-page-web-nav-item:hover { + background: var(--color-surface-2); +} + +.kb-page-web-nav-item.active { + background: var(--color-brand-soft); + border-color: rgba(8, 145, 178, 0.12); +} + +.kb-page-web-nav-item-top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 4px; +} + +.kb-page-web-nav-item-name { + font-size: 14px; + font-weight: 600; + color: var(--color-text); +} + +.kb-page-web-nav-item-type-icon { + font-size: 12px; + color: var(--color-text-tertiary); +} + +.kb-page-web-nav-item-type-icon.public { color: var(--color-success); } +.kb-page-web-nav-item-type-icon.private { color: var(--color-brand); } +.kb-page-web-nav-item-type-icon.team { color: var(--color-info); } + +.kb-page-web-nav-item-desc { + font-size: 12px; + color: var(--color-text-tertiary); + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.kb-page-web-nav-item-tags { + margin-top: 8px; +} + +.kb-page-web-access-tag { + font-size: 10px; + padding: 0 6px; + border-radius: 4px; +} + +.kb-page-web-detail-container { + background: white; + border: 1px solid var(--color-border); + border-radius: 22px; + min-height: 500px; + box-shadow: 0 12px 28px rgba(15, 23, 42, 0.045); +} + +.kb-detail { + padding: 24px; +} + +.kb-detail-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 24px; + padding-bottom: 20px; + border-bottom: 1px solid var(--color-border-faint); +} + +.kb-detail-title { + margin: 0 0 8px; + font-size: 22px; + font-weight: 800; + color: var(--color-text); +} + +.kb-detail-desc { + margin: 0; + font-size: 14px; + color: var(--color-text-secondary); +} + +.kb-detail-section-head { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; +} + +.kb-detail-section-title { + margin: 0; + font-size: 16px; + font-weight: 700; +} + +.kb-detail-section-count { + font-size: 12px; + color: var(--color-text-tertiary); +} + +.kb-file-name { + font-weight: 500; + color: var(--color-text); +} + +.kb-files-table .ant-table-thead > tr > th { + background: transparent; + font-size: 13px; + font-weight: 600; + color: var(--color-text-tertiary); +} + +.kb-page-web-loading, +.kb-page-web-empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + +/* Dark mode adjustments if needed */ +[data-theme='dark'] .kb-page-web-hero { + background: linear-gradient(135deg, #1e293b, #0f172a); + border-color: rgba(255, 255, 255, 0.08); +} + +[data-theme='dark'] .kb-page-web-list-panel, +[data-theme='dark'] .kb-page-web-detail-container { + background: #1e293b; + border-color: rgba(255, 255, 255, 0.08); +} + +/* H5 Styles */ +.kb-page-h5 { + padding: 16px; +} + +.kb-h5-hero { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.kb-h5-title { + font-size: 24px; + font-weight: 800; + margin: 0; +} + +.kb-h5-list-item { + padding: 16px !important; + background: white; + border-radius: 12px; + margin-bottom: 12px; + border: 1px solid var(--color-border) !important; +} + +.kb-h5-header { + display: flex; + align-items: center; + padding: 8px 12px; + background: white; + border-bottom: 1px solid var(--color-border); + position: sticky; + top: 0; + z-index: 10; +} + +.kb-h5-header-title { + flex: 1; + text-align: center; + font-weight: 600; + font-size: 16px; +} + +.kb-h5-content { + padding: 16px; +} + +.kb-h5-actions { + margin-bottom: 20px; +} + +.kb-h5-section-title { + font-weight: 700; + margin-bottom: 12px; + font-size: 14px; + color: var(--color-text-secondary); +}
{activeKb.description || '暂无描述'}
点击或拖拽文件到此区域上传
支持多文件上传,单个文件最大支持 50MB
+ 挂载到智能体后,该智能体的对话将自动检索此知识库。 +
+ 共享后,团队内的成员将拥有此知识库的查看权限。 +
+ 输入用户的手机号或 ID 进行共享。 +
+ 管理您的团队和个人知识库,支持多文件上传、共享和智能体挂载,让 AI 更懂您的业务。 +