Merge branch 'PingChuan' into shiy-login
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -407,7 +407,7 @@ const ChatSidebar = forwardRef<ChatSidebarRef, ChatSidebarProps>(({
|
|||||||
confirmLoading={renameLoading}
|
confirmLoading={renameLoading}
|
||||||
okText="确定"
|
okText="确定"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
destroyOnClose
|
destroyOnHidden
|
||||||
>
|
>
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<Input
|
<Input
|
||||||
@@ -437,7 +437,7 @@ const ChatSidebar = forwardRef<ChatSidebarRef, ChatSidebarProps>(({
|
|||||||
okText="删除"
|
okText="删除"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
okType="danger"
|
okType="danger"
|
||||||
destroyOnClose
|
destroyOnHidden
|
||||||
>
|
>
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<p>确定要删除会话 <strong>"{deletingConversation?.name}"</strong> 吗?</p>
|
<p>确定要删除会话 <strong>"{deletingConversation?.name}"</strong> 吗?</p>
|
||||||
|
|||||||
@@ -577,7 +577,7 @@ export default function AreaDatasetConfig() {
|
|||||||
),
|
),
|
||||||
value: ds.id,
|
value: ds.id,
|
||||||
}))}
|
}))}
|
||||||
dropdownStyle={{ maxHeight: '300px' }}
|
styles={{ popup: { root: { maxHeight: '300px' } } }}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
@@ -594,7 +594,7 @@ export default function AreaDatasetConfig() {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{/* 知识库描述 */}
|
{/* 知识库描述 */}
|
||||||
<Form.Item
|
{/* <Form.Item
|
||||||
name="dataset_description"
|
name="dataset_description"
|
||||||
label="知识库描述"
|
label="知识库描述"
|
||||||
>
|
>
|
||||||
@@ -603,7 +603,7 @@ export default function AreaDatasetConfig() {
|
|||||||
rows={3}
|
rows={3}
|
||||||
maxLength={500}
|
maxLength={500}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item> */}
|
||||||
|
|
||||||
{/* 高级设置折叠面板 */}
|
{/* 高级设置折叠面板 */}
|
||||||
<div style={{ marginTop: '24px' }}>
|
<div style={{ marginTop: '24px' }}>
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { Form, Input, Button, Card, Spin, Divider, Select, Slider, InputNumber, Tooltip, Checkbox } from 'antd';
|
import { CheckCircleFilled, QuestionCircleOutlined, SaveOutlined } from '@ant-design/icons';
|
||||||
import { SaveOutlined, QuestionCircleOutlined, CheckCircleFilled } from '@ant-design/icons';
|
import { Button, Card, Checkbox, Descriptions, Divider, InputNumber, Select, Slider, Spin, Tag, Tooltip } from 'antd';
|
||||||
import { useDatasetSettings, type SearchMethod } from '~/hooks/dify-dataset-manager/dataset-settings';
|
import { useDatasetSettings, type SearchMethod } from '~/hooks/dify-dataset-manager/dataset-settings';
|
||||||
import type { DatasetSettingsProps } from '~/types/dify-dataset-manager/dataset-settings';
|
import type { DatasetSettingsProps } from '~/types/dify-dataset-manager/dataset-settings';
|
||||||
|
|
||||||
const { TextArea } = Input;
|
|
||||||
|
|
||||||
// 检索方式选项
|
// 检索方式选项
|
||||||
const SEARCH_METHOD_OPTIONS: { label: string; value: SearchMethod; description: string }[] = [
|
const SEARCH_METHOD_OPTIONS: { label: string; value: SearchMethod; description: string }[] = [
|
||||||
{ label: '向量检索', value: 'semantic_search', description: '基于语义理解的智能检索,适合需要理解上下文的场景' },
|
{ label: '向量检索', value: 'semantic_search', description: '基于语义理解的智能检索,适合需要理解上下文的场景' },
|
||||||
@@ -15,23 +13,21 @@ const SEARCH_METHOD_OPTIONS: { label: string; value: SearchMethod; description:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库设置组件
|
* 知识库设置组件
|
||||||
* 用于修改知识库名称和描述
|
* 使用 Descriptions 展示只读的知识库基本信息,提供可编辑的检索设置
|
||||||
|
* 注:Dify API 不支持修改知识库名称和描述,故这些字段仅作只读展示
|
||||||
*/
|
*/
|
||||||
export default function DatasetSettings({
|
export default function DatasetSettings({
|
||||||
dataset,
|
dataset,
|
||||||
onDatasetUpdated,
|
onDatasetUpdated,
|
||||||
}: DatasetSettingsProps) {
|
}: DatasetSettingsProps) {
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
saving,
|
saving,
|
||||||
hasChanges,
|
hasChanges,
|
||||||
retrievalSettings,
|
retrievalSettings,
|
||||||
handleValuesChange,
|
|
||||||
handleSave,
|
handleSave,
|
||||||
handleReset,
|
handleReset,
|
||||||
updateRetrievalSettings,
|
updateRetrievalSettings,
|
||||||
} = useDatasetSettings(dataset, form, onDatasetUpdated);
|
} = useDatasetSettings(dataset, onDatasetUpdated);
|
||||||
|
|
||||||
// 是否需要显示 Reranking 提示(语义检索和混合检索需要,且强制开启)
|
// 是否需要显示 Reranking 提示(语义检索和混合检索需要,且强制开启)
|
||||||
const showRerankingInfo = retrievalSettings.searchMethod === 'semantic_search' || retrievalSettings.searchMethod === 'hybrid_search';
|
const showRerankingInfo = retrievalSettings.searchMethod === 'semantic_search' || retrievalSettings.searchMethod === 'hybrid_search';
|
||||||
@@ -53,66 +49,56 @@ export default function DatasetSettings({
|
|||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
<h1>设置</h1>
|
<h1>设置</h1>
|
||||||
<p className="page-description">
|
<p className="page-description">
|
||||||
管理知识库的基本信息
|
管理知识库的基本信息和检索配置
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 设置表单 */}
|
{/* 知识库基本信息 */}
|
||||||
<Card className="settings-card">
|
<Card
|
||||||
<Form
|
className="settings-card"
|
||||||
form={form}
|
title={
|
||||||
layout="vertical"
|
<span style={{ fontSize: 16, fontWeight: 500 }}>
|
||||||
onValuesChange={handleValuesChange}
|
知识库信息
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Descriptions
|
||||||
|
column={{ xs: 1, sm: 2, md: 2, lg: 3 }}
|
||||||
|
labelStyle={{
|
||||||
|
color: '#8c8c8c',
|
||||||
|
fontWeight: 400,
|
||||||
|
}}
|
||||||
|
contentStyle={{
|
||||||
|
color: '#262626',
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Descriptions.Item label="知识库名称" span={3}>
|
||||||
name="name"
|
<span style={{ fontSize: 15 }}>{dataset.name}</span>
|
||||||
label="知识库名称"
|
</Descriptions.Item>
|
||||||
rules={[
|
{dataset.description && (
|
||||||
{ required: true, message: '请输入知识库名称' },
|
<Descriptions.Item label="描述" span={3}>
|
||||||
{ max: 100, message: '名称不能超过100个字符' },
|
<span style={{ color: '#595959' }}>{dataset.description}</span>
|
||||||
]}
|
</Descriptions.Item>
|
||||||
>
|
)}
|
||||||
<Input placeholder="请输入知识库名称" maxLength={100} />
|
<Descriptions.Item label="索引方式">
|
||||||
</Form.Item>
|
<Tag color={dataset.indexing_technique === 'high_quality' ? 'green' : 'blue'}>
|
||||||
|
{dataset.indexing_technique === 'high_quality' ? '高质量' : '经济'}
|
||||||
<Form.Item
|
</Tag>
|
||||||
name="description"
|
</Descriptions.Item>
|
||||||
label="知识库描述"
|
<Descriptions.Item label="文档数量">
|
||||||
extra="描述知识库的用途和内容(仅展示,暂不支持修改)"
|
<span style={{ fontFamily: 'monospace' }}>{dataset.document_count}</span>
|
||||||
>
|
</Descriptions.Item>
|
||||||
<TextArea
|
<Descriptions.Item label="总字符数">
|
||||||
placeholder="请输入知识库描述"
|
<span style={{ fontFamily: 'monospace' }}>{dataset.word_count?.toLocaleString() || 0}</span>
|
||||||
rows={4}
|
</Descriptions.Item>
|
||||||
maxLength={500}
|
<Descriptions.Item label="创建时间" span={2}>
|
||||||
showCount
|
{new Date(dataset.created_at * 1000).toLocaleString('zh-CN')}
|
||||||
disabled
|
</Descriptions.Item>
|
||||||
/>
|
<Descriptions.Item label="最后更新">
|
||||||
</Form.Item>
|
{new Date(dataset.updated_at * 1000).toLocaleString('zh-CN')}
|
||||||
|
</Descriptions.Item>
|
||||||
{/* 只读信息 */}
|
</Descriptions>
|
||||||
<div className="readonly-info">
|
|
||||||
<div className="info-item">
|
|
||||||
<span className="info-label">索引方式:</span>
|
|
||||||
<span className="info-value">
|
|
||||||
{dataset.indexing_technique === 'high_quality' ? '高质量' : '经济'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="info-item">
|
|
||||||
<span className="info-label">文档数量:</span>
|
|
||||||
<span className="info-value">{dataset.document_count}</span>
|
|
||||||
</div>
|
|
||||||
<div className="info-item">
|
|
||||||
<span className="info-label">总字符数:</span>
|
|
||||||
<span className="info-value">{dataset.word_count?.toLocaleString()}</span>
|
|
||||||
</div>
|
|
||||||
<div className="info-item">
|
|
||||||
<span className="info-label">创建时间:</span>
|
|
||||||
<span className="info-value">
|
|
||||||
{new Date(dataset.created_at * 1000).toLocaleString('zh-CN')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 检索设置卡片 */}
|
{/* 检索设置卡片 */}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default function DatasetLayout({
|
|||||||
placeholder="选择知识库"
|
placeholder="选择知识库"
|
||||||
suffixIcon={<SwapOutlined />}
|
suffixIcon={<SwapOutlined />}
|
||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={false}
|
||||||
dropdownStyle={{ minWidth: 200 }}
|
styles={{ popup: { root: { minWidth: 200 } } }}
|
||||||
>
|
>
|
||||||
{availableDatasets.map(ds => (
|
{availableDatasets.map(ds => (
|
||||||
<Select.Option key={ds.dataset_id} value={ds.dataset_id}>
|
<Select.Option key={ds.dataset_id} value={ds.dataset_id}>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SearchOutlined, FileSearchOutlined } from '@ant-design/icons';
|
import { FileSearchOutlined, SearchOutlined } from '@ant-design/icons';
|
||||||
import { Button, Tag, Input, Slider, Spin, Select, Flex, Switch, InputNumber, Tooltip } from 'antd';
|
import { Button, Card, Flex, Input, InputNumber, Select, Slider, Spin, Switch, Tag, Tooltip } from 'antd';
|
||||||
import type { RetrieveRecord } from '~/api/dify-dataset/type';
|
import type { RetrieveRecord } from '~/api/dify-dataset/type';
|
||||||
import { useRetrieveTest } from '~/hooks/dify-dataset-manager/retrieve-test';
|
import { useRetrieveTest } from '~/hooks/dify-dataset-manager/retrieve-test';
|
||||||
import type { RetrieveTestProps } from '~/types/dify-dataset-manager/retrieve-test';
|
import type { RetrieveTestProps } from '~/types/dify-dataset-manager/retrieve-test';
|
||||||
@@ -25,62 +25,46 @@ function ResultItem({ record, index }: { record: RetrieveRecord; index: number }
|
|||||||
const scoreColor = record.score > 0.8 ? '#52c41a' : record.score > 0.5 ? '#faad14' : '#666';
|
const scoreColor = record.score > 0.8 ? '#52c41a' : record.score > 0.5 ? '#faad14' : '#666';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<div className="segment-item">
|
||||||
vertical
|
<div className="segment-header">
|
||||||
gap={12}
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
style={{
|
<Tag style={{ background: scoreColor, color: '#fff', border: 'none', margin: 0 }}>
|
||||||
padding: 16,
|
|
||||||
background: colors.bgContainer,
|
|
||||||
borderRadius: 8,
|
|
||||||
border: `1px solid ${colors.border}`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex justify="space-between" align="center">
|
|
||||||
<Flex gap={8} align="center">
|
|
||||||
<Tag style={{ background: scoreColor, color: '#fff', border: 'none' }}>
|
|
||||||
{scorePercent}%
|
{scorePercent}%
|
||||||
</Tag>
|
</Tag>
|
||||||
<span style={{ color: colors.textSecondary, fontSize: 12 }}>
|
<span className="segment-index">#{index + 1}</span>
|
||||||
#{index + 1} · {record.segment.word_count} 字 · 命中 {record.segment.hit_count} 次
|
<span className="segment-chars">
|
||||||
|
{record.segment.word_count} 字 · 命中 {record.segment.hit_count} 次
|
||||||
</span>
|
</span>
|
||||||
</Flex>
|
</div>
|
||||||
{record.segment.document && (
|
{record.segment.document && (
|
||||||
<span style={{ color: colors.textTertiary, fontSize: 12 }}>
|
<span className="segment-chars">
|
||||||
来源: {record.segment.document.name}
|
来源: {record.segment.document.name}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</div>
|
||||||
<div style={{
|
<div className="segment-content">
|
||||||
color: colors.text,
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: 1.6,
|
|
||||||
whiteSpace: 'pre-wrap',
|
|
||||||
}}>
|
|
||||||
{record.segment.content.length > 500
|
{record.segment.content.length > 500
|
||||||
? record.segment.content.substring(0, 500) + '...'
|
? record.segment.content.substring(0, 500) + '...'
|
||||||
: record.segment.content}
|
: record.segment.content}
|
||||||
</div>
|
</div>
|
||||||
{record.segment.answer && (
|
{record.segment.answer && (
|
||||||
<Flex
|
<div style={{
|
||||||
vertical
|
padding: 12,
|
||||||
gap={4}
|
background: '#f0f0f0',
|
||||||
style={{
|
borderRadius: 6,
|
||||||
padding: 12,
|
marginTop: 8,
|
||||||
background: colors.fillTertiary,
|
}}>
|
||||||
borderRadius: 6,
|
<span style={{ color: '#8c8c8c', fontSize: 12, display: 'block', marginBottom: 4 }}>
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ color: colors.textSecondary, fontSize: 12 }}>
|
|
||||||
答案:
|
答案:
|
||||||
</span>
|
</span>
|
||||||
<span style={{ color: colors.text, fontSize: 14 }}>
|
<span style={{ color: '#262626', fontSize: 14 }}>
|
||||||
{record.segment.answer.length > 200
|
{record.segment.answer.length > 200
|
||||||
? record.segment.answer.substring(0, 200) + '...'
|
? record.segment.answer.substring(0, 200) + '...'
|
||||||
: record.segment.answer}
|
: record.segment.answer}
|
||||||
</span>
|
</span>
|
||||||
</Flex>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,63 +261,32 @@ export default function RetrieveTest({ datasetId }: RetrieveTestProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* 右侧面板 - 结果展示 */}
|
{/* 右侧面板 - 结果展示 */}
|
||||||
<Flex
|
<div className="preview-panel">
|
||||||
vertical
|
<Card
|
||||||
flex={1}
|
title={
|
||||||
gap={16}
|
<div className="preview-header">
|
||||||
style={{
|
<span>检索结果</span>
|
||||||
padding: 20,
|
<span className="segment-count">
|
||||||
background: colors.bgElevated,
|
{retrieveResults.length > 0 ? `${retrieveResults.length} 条结果` : '0 条结果'}
|
||||||
overflow: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{retrieving ? (
|
|
||||||
<Flex
|
|
||||||
flex={1}
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
vertical
|
|
||||||
gap={12}
|
|
||||||
>
|
|
||||||
<Spin size="large" />
|
|
||||||
<span style={{ color: colors.textSecondary }}>
|
|
||||||
检索中...
|
|
||||||
</span>
|
|
||||||
</Flex>
|
|
||||||
) : retrieveResults.length === 0 ? (
|
|
||||||
<Flex
|
|
||||||
flex={1}
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
vertical
|
|
||||||
gap={12}
|
|
||||||
>
|
|
||||||
<FileSearchOutlined style={{
|
|
||||||
fontSize: 48,
|
|
||||||
color: colors.textQuaternary,
|
|
||||||
}} />
|
|
||||||
<span style={{ color: colors.textTertiary }}>
|
|
||||||
召回测试结果将展示在这里
|
|
||||||
</span>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Flex justify="space-between" align="center">
|
|
||||||
<span style={{
|
|
||||||
fontSize: 14,
|
|
||||||
color: colors.text,
|
|
||||||
fontWeight: 500,
|
|
||||||
}}>
|
|
||||||
检索结果
|
|
||||||
</span>
|
</span>
|
||||||
<span style={{
|
</div>
|
||||||
fontSize: 13,
|
}
|
||||||
color: colors.textSecondary,
|
className="preview-card"
|
||||||
}}>
|
>
|
||||||
共找到 {retrieveResults.length} 条结果
|
{retrieving ? (
|
||||||
</span>
|
<div className="preview-loading">
|
||||||
</Flex>
|
<Spin size="large" />
|
||||||
<Flex vertical gap={12}>
|
<div className="loading-text">检索中...</div>
|
||||||
|
</div>
|
||||||
|
) : retrieveResults.length === 0 ? (
|
||||||
|
<div className="preview-empty">
|
||||||
|
<div className="empty-icon">
|
||||||
|
<FileSearchOutlined />
|
||||||
|
</div>
|
||||||
|
<p>召回测试结果将展示在这里</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="preview-segments">
|
||||||
{retrieveResults.map((record, index) => (
|
{retrieveResults.map((record, index) => (
|
||||||
<ResultItem
|
<ResultItem
|
||||||
key={record.segment.id}
|
key={record.segment.id}
|
||||||
@@ -341,10 +294,10 @@ export default function RetrieveTest({ datasetId }: RetrieveTestProps) {
|
|||||||
index={index}
|
index={index}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</div>
|
||||||
</>
|
)}
|
||||||
)}
|
</Card>
|
||||||
</Flex>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import type { FormInstance } from 'antd';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { fetchDataset, updateDatasetSettings } from '~/api/dify-dataset/api/datasetApi';
|
||||||
import type { Dataset, RetrievalModel } from '~/api/dify-dataset/type/datasetTypes';
|
import type { Dataset, RetrievalModel } from '~/api/dify-dataset/type/datasetTypes';
|
||||||
import { updateDatasetSettings, fetchDataset } from '~/api/dify-dataset/api/datasetApi';
|
|
||||||
import { DIFY_CONFIG } from '~/config/api-config';
|
import { DIFY_CONFIG } from '~/config/api-config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,10 +74,10 @@ function formValuesToRetrievalModel(values: RetrievalSettingsFormValues): Retrie
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库设置状态管理 Hook
|
* 知识库设置状态管理 Hook
|
||||||
|
* 注:Dify API 不支持修改知识库名称和描述,只支持修改检索设置
|
||||||
*/
|
*/
|
||||||
export function useDatasetSettings(
|
export function useDatasetSettings(
|
||||||
dataset: Dataset | null,
|
dataset: Dataset | null,
|
||||||
form: FormInstance,
|
|
||||||
onDatasetUpdated: (dataset: Dataset) => void
|
onDatasetUpdated: (dataset: Dataset) => void
|
||||||
) {
|
) {
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
@@ -89,18 +88,36 @@ export function useDatasetSettings(
|
|||||||
() => retrievalModelToFormValues(dataset?.retrieval_model_dict)
|
() => retrievalModelToFormValues(dataset?.retrieval_model_dict)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 初始化表单数据
|
// 原始检索设置,用于对比变化
|
||||||
|
const [originalSettings, setOriginalSettings] = useState<RetrievalSettingsFormValues>(
|
||||||
|
() => retrievalModelToFormValues(dataset?.retrieval_model_dict)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 初始化检索设置
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataset) {
|
if (dataset) {
|
||||||
form.setFieldsValue({
|
|
||||||
name: dataset.name,
|
|
||||||
description: dataset.description || '',
|
|
||||||
});
|
|
||||||
console.log('[DatasetSettings] 初始化检索设置, retrieval_model_dict:', dataset.retrieval_model_dict);
|
console.log('[DatasetSettings] 初始化检索设置, retrieval_model_dict:', dataset.retrieval_model_dict);
|
||||||
setRetrievalSettings(retrievalModelToFormValues(dataset.retrieval_model_dict));
|
const settings = retrievalModelToFormValues(dataset.retrieval_model_dict);
|
||||||
|
setRetrievalSettings(settings);
|
||||||
|
setOriginalSettings(settings);
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
}
|
}
|
||||||
}, [dataset, form]);
|
}, [dataset]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查检索设置是否有变化
|
||||||
|
*/
|
||||||
|
const checkRetrievalChanges = useCallback((newSettings: RetrievalSettingsFormValues) => {
|
||||||
|
const hasChanged =
|
||||||
|
newSettings.searchMethod !== originalSettings.searchMethod ||
|
||||||
|
newSettings.topK !== originalSettings.topK ||
|
||||||
|
newSettings.scoreThresholdEnabled !== originalSettings.scoreThresholdEnabled ||
|
||||||
|
newSettings.scoreThreshold !== originalSettings.scoreThreshold ||
|
||||||
|
newSettings.rerankingEnable !== originalSettings.rerankingEnable ||
|
||||||
|
newSettings.weights !== originalSettings.weights;
|
||||||
|
|
||||||
|
setHasChanges(hasChanged);
|
||||||
|
}, [originalSettings]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新检索设置
|
* 更新检索设置
|
||||||
@@ -112,40 +129,14 @@ export function useDatasetSettings(
|
|||||||
setRetrievalSettings(prev => {
|
setRetrievalSettings(prev => {
|
||||||
const newSettings = { ...prev, [key]: value };
|
const newSettings = { ...prev, [key]: value };
|
||||||
// 检查是否有变化
|
// 检查是否有变化
|
||||||
checkForChanges(newSettings);
|
checkRetrievalChanges(newSettings);
|
||||||
return newSettings;
|
return newSettings;
|
||||||
});
|
});
|
||||||
}, [dataset]);
|
}, [checkRetrievalChanges]);
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否有变化
|
|
||||||
*/
|
|
||||||
const checkForChanges = useCallback((newRetrievalSettings?: RetrievalSettingsFormValues) => {
|
|
||||||
const values = form.getFieldsValue();
|
|
||||||
const currentRetrieval = newRetrievalSettings || retrievalSettings;
|
|
||||||
const originalRetrieval = retrievalModelToFormValues(dataset?.retrieval_model_dict);
|
|
||||||
|
|
||||||
const nameChanged = values.name !== dataset?.name;
|
|
||||||
const retrievalChanged =
|
|
||||||
currentRetrieval.searchMethod !== originalRetrieval.searchMethod ||
|
|
||||||
currentRetrieval.topK !== originalRetrieval.topK ||
|
|
||||||
currentRetrieval.scoreThresholdEnabled !== originalRetrieval.scoreThresholdEnabled ||
|
|
||||||
currentRetrieval.scoreThreshold !== originalRetrieval.scoreThreshold ||
|
|
||||||
currentRetrieval.rerankingEnable !== originalRetrieval.rerankingEnable ||
|
|
||||||
currentRetrieval.weights !== originalRetrieval.weights;
|
|
||||||
|
|
||||||
setHasChanges(nameChanged || retrievalChanged);
|
|
||||||
}, [form, dataset, retrievalSettings]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理表单值变化
|
|
||||||
*/
|
|
||||||
const handleValuesChange = useCallback(() => {
|
|
||||||
checkForChanges();
|
|
||||||
}, [checkForChanges]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存设置
|
* 保存设置
|
||||||
|
* 注:仅保存检索设置,Dify API 不支持修改名称和描述
|
||||||
*/
|
*/
|
||||||
const handleSave = useCallback(async () => {
|
const handleSave = useCallback(async () => {
|
||||||
if (!dataset) {
|
if (!dataset) {
|
||||||
@@ -154,12 +145,10 @@ export function useDatasetSettings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const values = await form.validateFields();
|
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
|
||||||
// 构建完整的更新请求
|
// 仅更新检索设置
|
||||||
await updateDatasetSettings(dataset.id, {
|
await updateDatasetSettings(dataset.id, {
|
||||||
name: values.name,
|
|
||||||
retrieval_model: formValuesToRetrievalModel(retrievalSettings),
|
retrieval_model: formValuesToRetrievalModel(retrievalSettings),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -167,8 +156,9 @@ export function useDatasetSettings(
|
|||||||
const fullDataset = await fetchDataset(dataset.id);
|
const fullDataset = await fetchDataset(dataset.id);
|
||||||
console.log('[DatasetSettings] 保存后获取完整数据:', fullDataset);
|
console.log('[DatasetSettings] 保存后获取完整数据:', fullDataset);
|
||||||
|
|
||||||
message.success('保存成功');
|
message.success('检索设置保存成功');
|
||||||
onDatasetUpdated(fullDataset);
|
onDatasetUpdated(fullDataset);
|
||||||
|
setOriginalSettings(retrievalSettings);
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('保存设置失败:', err);
|
console.error('保存设置失败:', err);
|
||||||
@@ -176,21 +166,17 @@ export function useDatasetSettings(
|
|||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
}, [dataset, form, retrievalSettings, onDatasetUpdated]);
|
}, [dataset, retrievalSettings, onDatasetUpdated]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置表单
|
* 重置检索设置
|
||||||
*/
|
*/
|
||||||
const handleReset = useCallback(() => {
|
const handleReset = useCallback(() => {
|
||||||
if (dataset) {
|
if (dataset) {
|
||||||
form.setFieldsValue({
|
setRetrievalSettings(originalSettings);
|
||||||
name: dataset.name,
|
|
||||||
description: dataset.description || '',
|
|
||||||
});
|
|
||||||
setRetrievalSettings(retrievalModelToFormValues(dataset.retrieval_model_dict));
|
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
}
|
}
|
||||||
}, [dataset, form]);
|
}, [dataset, originalSettings]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 状态
|
// 状态
|
||||||
@@ -199,7 +185,6 @@ export function useDatasetSettings(
|
|||||||
retrievalSettings,
|
retrievalSettings,
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
handleValuesChange,
|
|
||||||
handleSave,
|
handleSave,
|
||||||
handleReset,
|
handleReset,
|
||||||
updateRetrievalSettings,
|
updateRetrievalSettings,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
.dataset-manager-wrapper {
|
.dataset-manager-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height:90%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -26,13 +26,14 @@
|
|||||||
|
|
||||||
/* 左侧侧边栏 */
|
/* 左侧侧边栏 */
|
||||||
.dataset-sidebar {
|
.dataset-sidebar {
|
||||||
width: 200px;
|
width: 26vh;
|
||||||
min-width: 200px;
|
min-width: 11vh;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
border-right: 1px solid #f0f0f0;
|
border-right: 1px solid #f0f0f0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
max-height: 90vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 返回按钮 */
|
/* 返回按钮 */
|
||||||
@@ -229,6 +230,7 @@
|
|||||||
/* 右侧主内容区 */
|
/* 右侧主内容区 */
|
||||||
.dataset-main {
|
.dataset-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
height: 85vh;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -735,6 +737,7 @@
|
|||||||
|
|
||||||
/* 右侧预览面板 */
|
/* 右侧预览面板 */
|
||||||
.preview-panel {
|
.preview-panel {
|
||||||
|
height: 85vh;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
@@ -852,6 +855,11 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
/* 关键修复:添加高度约束和内部滚动 */
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.segment-item {
|
.segment-item {
|
||||||
|
|||||||
Reference in New Issue
Block a user