import { useState, useEffect } from 'react'; import { Input, Button, InputNumber, Checkbox, Select, Card, Empty, Spin, message, Divider, Tooltip, } from 'antd'; import { QuestionCircleOutlined, ReloadOutlined, EyeOutlined, } from '@ant-design/icons'; import type { Document } from '~/api/dify-dataset/type/documentTypes'; import type { Segment } from '~/api/dify-dataset/type'; import { fetchSegments } from '~/api/dify-dataset/api/segmentApi'; import { updateDocumentWithSettings } from '~/api/dify-dataset/api/documentApi'; interface DocumentDetailProps { datasetId: string; document: Document | null; } /** * 分段设置配置 * 注意:Dify API 支持的参数有限 * - separator: ✅ 支持 * - maxTokens: ✅ 支持 * - removeExtraSpaces: ✅ 支持 * - removeUrlsEmails: ✅ 支持 * - useQASegment: ⚠️ 需要 doc_form: "qa_model" */ interface SegmentationSettings { separator: string; maxTokens: number; removeExtraSpaces: boolean; removeUrlsEmails: boolean; useQASegment: boolean; qaLanguage: string; } /** * 默认分段设置 */ const DEFAULT_SETTINGS: SegmentationSettings = { separator: '\\n\\n', maxTokens: 500, removeExtraSpaces: true, removeUrlsEmails: false, useQASegment: false, qaLanguage: 'Chinese', }; /** * 文档详情组件 * 显示文档的分段设置,支持修改并重新处理 */ export default function DocumentDetail({ datasetId, document, }: DocumentDetailProps) { // 分段设置状态 const [settings, setSettings] = useState(DEFAULT_SETTINGS); // 预览状态 const [previewSegments, setPreviewSegments] = useState([]); const [previewLoading, setPreviewLoading] = useState(false); const [showPreview, setShowPreview] = useState(false); // 保存状态 const [saving, setSaving] = useState(false); // 当文档变化时重置设置 useEffect(() => { if (document) { // 可以从文档中读取已有的设置,这里使用默认值 setSettings(DEFAULT_SETTINGS); setPreviewSegments([]); setShowPreview(false); } }, [document?.id]); /** * 更新设置 */ const updateSettings = (key: keyof SegmentationSettings, value: any) => { setSettings(prev => ({ ...prev, [key]: value })); }; /** * 重置设置 */ const handleReset = () => { setSettings(DEFAULT_SETTINGS); setPreviewSegments([]); setShowPreview(false); }; /** * 预览分段 */ const handlePreview = async () => { if (!document) return; setPreviewLoading(true); setShowPreview(true); try { // 获取当前文档的分段作为预览 const response = await fetchSegments(datasetId, document.id, 1, 50); setPreviewSegments(response.data || []); if (response.data?.length === 0) { message.info('该文档暂无分段数据'); } } catch (err: any) { console.error('预览分段失败:', err); message.error(err.message || '预览失败'); } finally { setPreviewLoading(false); } }; /** * 保存并处理 */ const handleSaveAndProcess = async () => { if (!document) return; setSaving(true); try { await updateDocumentWithSettings(datasetId, document.id, { indexing_technique: 'high_quality', process_rule: { mode: 'custom', rules: { pre_processing_rules: [ { id: 'remove_extra_spaces', enabled: settings.removeExtraSpaces }, { id: 'remove_urls_emails', enabled: settings.removeUrlsEmails }, ], segmentation: { separator: settings.separator.replace(/\\n/g, '\n'), max_tokens: settings.maxTokens, }, }, }, }); message.success('设置已保存,文档正在重新处理...'); } catch (err: any) { console.error('保存设置失败:', err); message.error(err.message || '保存失败'); } finally { setSaving(false); } }; if (!document) { return (
); } return (
{/* 左侧设置区域 */}
{/* 分段设置 */}

分段设置

{/* 分块模式 */}
通用 通用文本分块模式,检索和召回的块是相同的
{/* 分段标识符 */}
updateSettings('separator', e.target.value)} placeholder="\n\n" className="setting-input" />
{/* 分段最大长度 */}
updateSettings('maxTokens', value || 500)} min={100} max={4000} className="setting-input-number" /> characters
{/* 文本预处理规则 */}

文本预处理规则

updateSettings('removeExtraSpaces', e.target.checked)} > 替换掉连续的空格、换行符和制表符 updateSettings('removeUrlsEmails', e.target.checked)} > 删除所有 URL 和电子邮件地址
{/* Q&A 分段 */}
updateSettings('useQASegment', e.target.checked)} > 使用 Q&A 分段,语言 {showPreview ? `${previewSegments.length} 段块` : '0 段块'}
} className="preview-card" > {previewLoading ? (
加载中...
) : !showPreview ? (

点击左侧的"预览块"按钮来预览

) : previewSegments.length === 0 ? ( ) : (
{previewSegments.map((segment, index) => (
#{index + 1} {segment.word_count} 字符
{segment.content}
))}
)}
); }