Fix: Optimize the metadata code structure to implement metadata list structure functionality. (#12741)

### What problem does this PR solve?

Fix: Optimize the metadata code structure to implement metadata list
structure functionality.

#11564

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2026-01-21 16:15:43 +08:00
committed by GitHub
parent e1143d40bc
commit 83e17d8c4a
13 changed files with 315 additions and 251 deletions

View File

@ -18,7 +18,7 @@ import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-reques
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IParserConfig } from '@/interfaces/database/document'; import { IParserConfig } from '@/interfaces/database/document';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import { MetadataType } from '@/pages/dataset/components/metedata/hooks/use-manage-modal'; import { MetadataType } from '@/pages/dataset/components/metedata/constant';
import { import {
AutoMetadata, AutoMetadata,
ChunkMethodItem, ChunkMethodItem,

View File

@ -192,7 +192,7 @@ Example: A 1 KB message with 1024-dim embedding uses ~9 KB. The 5 MB default lim
toMetadataSettingTip: 'Set auto-metadata in Configuration.', toMetadataSettingTip: 'Set auto-metadata in Configuration.',
descriptionTip: descriptionTip:
'Provide descriptions or examples to guide LLM extract values for this field. If left empty, it will rely on the field name.', 'Provide descriptions or examples to guide LLM extract values for this field. If left empty, it will rely on the field name.',
restrictTDefinedValuesTip: restrictDefinedValuesTip:
'Enum Mode: Restricts LLM extraction to match preset values only. Define values below.', 'Enum Mode: Restricts LLM extraction to match preset values only. Define values below.',
valueExists: valueExists:
'Value already exists. Confirm to merge duplicates and combine all associated files.', 'Value already exists. Confirm to merge duplicates and combine all associated files.',

View File

@ -183,7 +183,7 @@ export default {
toMetadataSetting: '生成设置', toMetadataSetting: '生成设置',
descriptionTip: descriptionTip:
'提供描述或示例来指导大语言模型为此字段提取值。如果留空,将依赖字段名称。', '提供描述或示例来指导大语言模型为此字段提取值。如果留空,将依赖字段名称。',
restrictTDefinedValuesTip: restrictDefinedValuesTip:
'枚举模式:限制大语言模型提取的值只能匹配预设值。在下方定义值。', '枚举模式:限制大语言模型提取的值只能匹配预设值。在下方定义值。',
valueExists: '值已存在。确认合并重复项并组合所有关联文件。', valueExists: '值已存在。确认合并重复项并组合所有关联文件。',
fieldNameExists: '字段名已存在。确认合并重复项并组合所有关联文件。', fieldNameExists: '字段名已存在。确认合并重复项并组合所有关联文件。',

View File

@ -0,0 +1,80 @@
import { TFunction } from 'i18next';
import { MetadataValueType } from './interface';
export enum MetadataType {
Manage = 1,
UpdateSingle = 2,
Setting = 3,
SingleFileSetting = 4,
}
export const MetadataDeleteMap = (
t: TFunction<'translation', undefined>,
): Record<
MetadataType,
{
title: string;
warnFieldText: string;
warnValueText: string;
warnFieldName: string;
warnValueName: string;
}
> => {
return {
[MetadataType.Manage]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldAllWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteManageValueAllWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldNameExists'),
warnValueName: t('knowledgeDetails.metadata.valueExists'),
},
[MetadataType.Setting]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteSettingFieldWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteSettingValueWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldExists'),
warnValueName: t('knowledgeDetails.metadata.valueExists'),
},
[MetadataType.UpdateSingle]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldSingleWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteManageValueSingleWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldSingleNameExists'),
warnValueName: t('knowledgeDetails.metadata.valueSingleExists'),
},
[MetadataType.SingleFileSetting]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteSettingFieldWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteSettingValueWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldExists'),
warnValueName: t('knowledgeDetails.metadata.valueSingleExists'),
},
};
};
export const DEFAULT_VALUE_TYPE: MetadataValueType = 'string';
// const VALUE_TYPES_WITH_ENUM = new Set<MetadataValueType>(['enum']);
export const VALUE_TYPE_LABELS: Record<MetadataValueType, string> = {
string: 'String',
time: 'Time',
number: 'Number',
// bool: 'Bool',
// enum: 'Enum',
list: 'List',
// int: 'Int',
// float: 'Float',
};
export const metadataValueTypeEnum = Object.keys(VALUE_TYPE_LABELS).reduce(
(acc, item) => {
return { ...acc, [item]: item };
},
{} as Record<MetadataValueType, MetadataValueType>,
);
export const metadataValueTypeOptions = Object.entries(VALUE_TYPE_LABELS).map(
([value, label]) => ({ label, value }),
);
export const getMetadataValueTypeLabel = (value?: MetadataValueType) =>
VALUE_TYPE_LABELS[value || DEFAULT_VALUE_TYPE] || VALUE_TYPE_LABELS.string;

View File

@ -1,20 +1,17 @@
import message from '@/components/ui/message'; import message from '@/components/ui/message';
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { useSelectedIds } from '@/hooks/logic-hooks/use-row-selection'; import { useSelectedIds } from '@/hooks/logic-hooks/use-row-selection';
import { import { DocumentApiAction } from '@/hooks/use-document-request';
DocumentApiAction,
useSetDocumentMeta,
} from '@/hooks/use-document-request';
import kbService, { import kbService, {
getMetaDataService, getMetaDataService,
updateMetaData, updateMetaData,
} from '@/services/knowledge-service'; } from '@/services/knowledge-service';
import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query';
import { RowSelectionState } from '@tanstack/react-table'; import { RowSelectionState } from '@tanstack/react-table';
import { TFunction } from 'i18next';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
import { MetadataType, metadataValueTypeEnum } from '../constant';
import { import {
IBuiltInMetadataItem, IBuiltInMetadataItem,
IMetaDataReturnJSONSettings, IMetaDataReturnJSONSettings,
@ -25,141 +22,6 @@ import {
MetadataValueType, MetadataValueType,
ShowManageMetadataModalProps, ShowManageMetadataModalProps,
} from '../interface'; } from '../interface';
export enum MetadataType {
Manage = 1,
UpdateSingle = 2,
Setting = 3,
SingleFileSetting = 4,
}
export const MetadataDeleteMap = (
t: TFunction<'translation', undefined>,
): Record<
MetadataType,
{
title: string;
warnFieldText: string;
warnValueText: string;
warnFieldName: string;
warnValueName: string;
}
> => {
return {
[MetadataType.Manage]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldAllWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteManageValueAllWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldNameExists'),
warnValueName: t('knowledgeDetails.metadata.valueExists'),
},
[MetadataType.Setting]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteSettingFieldWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteSettingValueWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldExists'),
warnValueName: t('knowledgeDetails.metadata.valueExists'),
},
[MetadataType.UpdateSingle]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldSingleWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteManageValueSingleWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldSingleNameExists'),
warnValueName: t('knowledgeDetails.metadata.valueSingleExists'),
},
[MetadataType.SingleFileSetting]: {
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
warnFieldText: t('knowledgeDetails.metadata.deleteSettingFieldWarn'),
warnValueText: t('knowledgeDetails.metadata.deleteSettingValueWarn'),
warnFieldName: t('knowledgeDetails.metadata.fieldExists'),
warnValueName: t('knowledgeDetails.metadata.valueSingleExists'),
},
};
};
const DEFAULT_VALUE_TYPE: MetadataValueType = 'string';
// const VALUE_TYPES_WITH_ENUM = new Set<MetadataValueType>(['enum']);
const VALUE_TYPE_LABELS: Record<MetadataValueType, string> = {
string: 'String',
time: 'Time',
number: 'Number',
// bool: 'Bool',
// enum: 'Enum',
// 'list<string>': 'List<String>',
// int: 'Int',
// float: 'Float',
};
export const metadataValueTypeOptions = Object.entries(VALUE_TYPE_LABELS).map(
([value, label]) => ({ label, value }),
);
export const getMetadataValueTypeLabel = (value?: MetadataValueType) =>
VALUE_TYPE_LABELS[value || DEFAULT_VALUE_TYPE] || VALUE_TYPE_LABELS.string;
// export const isMetadataValueTypeWithEnum = (value?: MetadataValueType) =>
// VALUE_TYPES_WITH_ENUM.has(value || DEFAULT_VALUE_TYPE);
// const schemaToValueType = (
// property?: IMetaDataJsonSchemaProperty,
// ): MetadataValueType => {
// if (!property) return DEFAULT_VALUE_TYPE;
// if (
// property.type === 'array' &&
// property.items?.type === 'string' &&
// (property.items.enum?.length || 0) > 0
// ) {
// return 'enum';
// }
// if (property.type === 'boolean') return 'bool';
// if (property.type === 'integer') return 'int';
// if (property.type === 'number') return 'float';
// if (property.type === 'string' && property.format) {
// return 'time';
// }
// if (property.type === 'string' && property.enum?.length) {
// return 'enum';
// }
// return DEFAULT_VALUE_TYPE;
// };
// const valueTypeToSchema = (
// valueType: MetadataValueType,
// description: string,
// values: string[],
// ): IMetaDataJsonSchemaProperty => {
// const schema: IMetaDataJsonSchemaProperty = {
// description: description || '',
// };
// switch (valueType) {
// case 'bool':
// schema.type = 'boolean';
// return schema;
// case 'int':
// schema.type = 'integer';
// return schema;
// case 'float':
// schema.type = 'number';
// return schema;
// case 'time':
// schema.type = 'string';
// schema.format = 'date-time';
// return schema;
// case 'enum':
// schema.type = 'string';
// if (values?.length) {
// schema.enum = values;
// }
// return schema;
// case 'string':
// default:
// schema.type = 'string';
// if (values?.length) {
// schema.enum = values;
// }
// return schema;
// }
// };
export const util = { export const util = {
changeToMetaDataTableData(data: IMetaDataReturnType): IMetaDataTableData[] { changeToMetaDataTableData(data: IMetaDataReturnType): IMetaDataTableData[] {
@ -293,7 +155,22 @@ export const useMetadataOperations = () => {
}, []); }, []);
const addUpdateValue = useCallback( const addUpdateValue = useCallback(
(key: string, originalValue: string, newValue: string) => { (
key: string,
originalValue: string,
newValue: string | string[],
type?: MetadataValueType,
) => {
let newValuesRes: string | string[];
if (type !== metadataValueTypeEnum['list']) {
if (Array.isArray(newValue) && newValue.length > 0) {
newValuesRes = newValue[0];
} else {
newValuesRes = newValue;
}
} else {
newValuesRes = newValue;
}
setOperations((prev) => { setOperations((prev) => {
const existsIndex = prev.updates.findIndex( const existsIndex = prev.updates.findIndex(
(update) => update.key === key && update.match === originalValue, (update) => update.key === key && update.match === originalValue,
@ -304,7 +181,8 @@ export const useMetadataOperations = () => {
updatedUpdates[existsIndex] = { updatedUpdates[existsIndex] = {
key, key,
match: originalValue, match: originalValue,
value: newValue, value: newValuesRes,
type,
}; };
return { return {
...prev, ...prev,
@ -315,7 +193,7 @@ export const useMetadataOperations = () => {
...prev, ...prev,
updates: [ updates: [
...prev.updates, ...prev.updates,
{ key, match: originalValue, value: newValue }, { key, match: originalValue, value: newValuesRes, type },
], ],
}; };
}); });
@ -398,7 +276,7 @@ export const useManageMetaDataModal = (
resetOperations, resetOperations,
} = useMetadataOperations(); } = useMetadataOperations();
const { setDocumentMeta } = useSetDocumentMeta(); // const { setDocumentMeta } = useSetDocumentMeta();
useEffect(() => { useEffect(() => {
if (fetchTypeList.includes(type)) { if (fetchTypeList.includes(type)) {
@ -468,6 +346,7 @@ export const useManageMetaDataModal = (
const handleSaveManage = useCallback( const handleSaveManage = useCallback(
async (callback: () => void) => { async (callback: () => void) => {
console.log('handleSaveManage', tableData);
const { data: res } = await updateMetaData({ const { data: res } = await updateMetaData({
kb_id: id as string, kb_id: id as string,
data: operations, data: operations,
@ -482,25 +361,25 @@ export const useManageMetaDataModal = (
callback(); callback();
} }
}, },
[operations, id, t, queryClient, resetOperations, documentIds], [operations, id, t, queryClient, resetOperations, documentIds, tableData],
); );
const handleSaveUpdateSingle = useCallback( // const handleSaveUpdateSingle = useCallback(
async (callback: () => void) => { // async (callback: () => void) => {
const reqData = util.tableDataToMetaDataJSON(tableData); // const reqData = util.tableDataToMetaDataJSON(tableData);
if (otherData?.id) { // if (otherData?.id) {
const ret = await setDocumentMeta({ // const ret = await setDocumentMeta({
documentId: otherData?.id, // documentId: otherData?.id,
meta: JSON.stringify(reqData), // meta: JSON.stringify(reqData),
}); // });
if (ret === 0) { // if (ret === 0) {
// message.success(t('message.success')); // // message.success(t('message.success'));
callback(); // callback();
} // }
} // }
}, // },
[tableData, otherData, setDocumentMeta], // [tableData, otherData, setDocumentMeta],
); // );
const handleSaveSettings = useCallback( const handleSaveSettings = useCallback(
async (callback: () => void, builtInMetadata?: IBuiltInMetadataItem[]) => { async (callback: () => void, builtInMetadata?: IBuiltInMetadataItem[]) => {

View File

@ -1,12 +1,16 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MetadataDeleteMap, MetadataType } from '../hooks/use-manage-modal'; import {
MetadataDeleteMap,
MetadataType,
metadataValueTypeEnum,
} from '../constant';
import { IManageValuesProps, IMetaDataTableData } from '../interface'; import { IManageValuesProps, IMetaDataTableData } from '../interface';
export const useManageValues = (props: IManageValuesProps) => { export const useManageValues = (props: IManageValuesProps) => {
const { const {
data, data,
isAddValueMode,
hideModal, hideModal,
onSave, onSave,
addUpdateValue, addUpdateValue,
@ -17,7 +21,8 @@ export const useManageValues = (props: IManageValuesProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [metaData, setMetaData] = useState<IMetaDataTableData>({ const [metaData, setMetaData] = useState<IMetaDataTableData>({
...data, ...data,
valueType: data.valueType || 'string', valueType: data.valueType || metadataValueTypeEnum.string,
values: data.values || [''],
}); });
const [valueError, setValueError] = useState<Record<string, string>>({ const [valueError, setValueError] = useState<Record<string, string>>({
field: '', field: '',
@ -31,6 +36,8 @@ export const useManageValues = (props: IManageValuesProps) => {
onOk: () => {}, onOk: () => {},
onCancel: () => {}, onCancel: () => {},
}); });
const [shouldSave, setShouldSave] = useState(false);
const hideDeleteModal = () => { const hideDeleteModal = () => {
setDeleteDialogContent({ setDeleteDialogContent({
visible: false, visible: false,
@ -79,10 +86,11 @@ export const useManageValues = (props: IManageValuesProps) => {
restrictDefinedValues: prev.restrictDefinedValues, restrictDefinedValues: prev.restrictDefinedValues,
}; };
} }
return { const newMetadata = {
...prev, ...prev,
[field]: value, [field]: value,
}; };
return newMetadata;
}); });
return true; return true;
}, },
@ -90,13 +98,13 @@ export const useManageValues = (props: IManageValuesProps) => {
); );
// Maintain separate state for each input box // Maintain separate state for each input box
const [tempValues, setTempValues] = useState<string[]>([...data.values]); const [tempValues, setTempValues] = useState<string[]>(['']);
useEffect(() => { useEffect(() => {
setTempValues([...data.values]); setTempValues([...data.values]);
setMetaData({ setMetaData({
...data, ...data,
valueType: data.valueType || 'string', valueType: data.valueType || metadataValueTypeEnum.string,
}); });
}, [data]); }, [data]);
@ -119,31 +127,63 @@ export const useManageValues = (props: IManageValuesProps) => {
// handleHideModal(); // handleHideModal();
// return; // return;
// } // }
onSave(metaData); if (isAddValueMode) {
handleHideModal(); addUpdateValue(
}, [metaData, onSave, handleHideModal, type, valueError]); metaData.field,
undefined,
metaData.values,
metaData.valueType,
);
}
// onSave(metaData);
setShouldSave(true);
}, [
metaData,
// onSave,
// handleHideModal,
type,
valueError,
isAddValueMode,
addUpdateValue,
]);
useEffect(() => {
if (shouldSave) {
const timer = setTimeout(() => {
onSave(metaData);
setShouldSave(false);
clearTimeout(timer);
handleHideModal();
}, 100);
}
}, [shouldSave, onSave, handleHideModal, metaData]);
// Handle blur event, synchronize to main state // Handle blur event, synchronize to main state
const handleValueBlur = useCallback( const handleValueBlur = useCallback(
(values?: string[]) => { (values?: string[]) => {
const newValues = values || tempValues; const newValues = values || tempValues;
if (data.values.length > 0) { if (data.values.length > 0 && !isAddValueMode) {
newValues.forEach((newValue, index) => { newValues.forEach((newValue, index) => {
if (index < data.values.length) { if (index < data.values.length) {
const originalValue = data.values[index]; const originalValue = data.values[index];
if (originalValue !== newValue) { if (originalValue !== newValue) {
addUpdateValue(metaData.field, originalValue, newValue); addUpdateValue(
metaData.field,
originalValue,
newValue,
metaData.valueType,
);
} }
} else { } else {
if (newValue) { if (newValue) {
addUpdateValue(metaData.field, '', newValue); addUpdateValue(metaData.field, '', newValue, metaData.valueType);
} }
} }
}); });
} }
handleChange('values', [...new Set([...newValues])]); handleChange('values', [...new Set([...newValues])]);
}, },
[handleChange, tempValues, metaData, data, addUpdateValue], [handleChange, tempValues, metaData, data, addUpdateValue, isAddValueMode],
); );
// Handle value changes, only update temporary state // Handle value changes, only update temporary state
@ -200,13 +240,16 @@ export const useManageValues = (props: IManageValuesProps) => {
[addDeleteValue, metaData], [addDeleteValue, metaData],
); );
const handleClearValues = useCallback(() => { const handleClearValues = useCallback(
setTempValues([]); (isClearInitialValues = false) => {
setMetaData((prev) => ({ setTempValues(isClearInitialValues ? [] : ['']);
...prev, setMetaData((prev) => ({
values: [], ...prev,
})); values: isClearInitialValues ? [] : [''],
}, [setTempValues, setMetaData]); }));
},
[setTempValues, setMetaData],
);
const showDeleteModal = (item: string, callback: () => void) => { const showDeleteModal = (item: string, callback: () => void) => {
setDeleteDialogContent({ setDeleteDialogContent({

View File

@ -1,5 +1,5 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { MetadataType } from './hooks/use-manage-modal'; import { MetadataType } from './constant';
export type IMetaDataReturnType = Record< export type IMetaDataReturnType = Record<
string, string,
| { type: string; values: Array<Array<string | number>> } | { type: string; values: Array<Array<string | number>> }
@ -36,7 +36,7 @@ export type IMetaDataReturnJSONSettings =
export type MetadataValueType = export type MetadataValueType =
| 'string' | 'string'
// | 'list<string>' | 'list'
// | 'bool' // | 'bool'
// | 'enum' // | 'enum'
| 'time' | 'time'
@ -84,14 +84,16 @@ export interface IManageValuesProps {
isShowValueSwitch?: boolean; isShowValueSwitch?: boolean;
isShowType?: boolean; isShowType?: boolean;
isVerticalShowValue?: boolean; isVerticalShowValue?: boolean;
isAddValueMode?: boolean;
data: IMetaDataTableData; data: IMetaDataTableData;
type: MetadataType; type: MetadataType;
hideModal: () => void; hideModal: () => void;
onSave: (data: IMetaDataTableData) => void; onSave: (data: IMetaDataTableData) => void;
addUpdateValue: ( addUpdateValue: (
key: string, key: string,
originalValue: string, originalValue: string | undefined,
newValue: string, newValue: string | string[],
type?: MetadataValueType,
) => void; ) => void;
addDeleteValue: (key: string, value: string) => void; addDeleteValue: (key: string, value: string) => void;
} }
@ -104,7 +106,8 @@ interface DeleteOperation {
interface UpdateOperation { interface UpdateOperation {
key: string; key: string;
match: string; match: string;
value: string; value: string | string[];
type?: MetadataValueType;
} }
export interface MetadataOperations { export interface MetadataOperations {

View File

@ -1,7 +1,7 @@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { ColumnDef } from '@tanstack/react-table'; import { ColumnDef, Row, Table } from '@tanstack/react-table';
import { import {
ListChevronsDownUp, ListChevronsDownUp,
ListChevronsUpDown, ListChevronsUpDown,
@ -14,7 +14,7 @@ import {
getMetadataValueTypeLabel, getMetadataValueTypeLabel,
MetadataDeleteMap, MetadataDeleteMap,
MetadataType, MetadataType,
} from './hooks/use-manage-modal'; } from './constant';
import { IMetaDataTableData } from './interface'; import { IMetaDataTableData } from './interface';
interface IUseMetadataColumns { interface IUseMetadataColumns {
@ -102,7 +102,7 @@ export const useMetadataColumns = ({
? [ ? [
{ {
id: 'select', id: 'select',
header: ({ table }) => ( header: ({ table }: { table: Table<IMetaDataTableData> }) => (
<Checkbox <Checkbox
checked={ checked={
table.getIsAllPageRowsSelected() || table.getIsAllPageRowsSelected() ||
@ -114,7 +114,7 @@ export const useMetadataColumns = ({
aria-label="Select all" aria-label="Select all"
/> />
), ),
cell: ({ row }) => ( cell: ({ row }: { row: Row<IMetaDataTableData> }) => (
<Checkbox <Checkbox
checked={row.getIsSelected()} checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)} onCheckedChange={(value) => row.toggleSelected(!!value)}

View File

@ -35,6 +35,9 @@ import { useHandleMenuClick } from '../../sidebar/hooks';
import { import {
getMetadataValueTypeLabel, getMetadataValueTypeLabel,
MetadataType, MetadataType,
metadataValueTypeEnum,
} from './constant';
import {
useManageMetaDataModal, useManageMetaDataModal,
useOperateData, useOperateData,
} from './hooks/use-manage-modal'; } from './hooks/use-manage-modal';
@ -72,7 +75,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
field: '', field: '',
description: '', description: '',
values: [], values: [],
valueType: 'string', valueType: metadataValueTypeEnum.string,
}); });
const [activeTab, setActiveTab] = useState<MetadataSettingsTab>('generation'); const [activeTab, setActiveTab] = useState<MetadataSettingsTab>('generation');
@ -98,12 +101,18 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
); );
const { handleMenuClick } = useHandleMenuClick(); const { handleMenuClick } = useHandleMenuClick();
const [shouldSave, setShouldSave] = useState(false); const [shouldSave, setShouldSave] = useState(false);
const [isAddValueMode, setIsAddValueMode] = useState(false);
const { const {
visible: manageValuesVisible, visible: manageValuesVisible,
showModal: showManageValuesModal, showModal: showManageValuesModal,
hideModal: hideManageValuesModal, hideModal: hideManageValuesModal,
} = useSetModalState(); } = useSetModalState();
const hideManageValuesModalFunc = () => {
setIsAddValueMode(false);
hideManageValuesModal();
};
const isSettingsMode = const isSettingsMode =
metadataType === MetadataType.Setting || metadataType === MetadataType.Setting ||
metadataType === MetadataType.SingleFileSetting || metadataType === MetadataType.SingleFileSetting ||
@ -166,10 +175,15 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
setValueData({ setValueData({
field: '', field: '',
description: '', description: '',
values: [], values:
valueType: 'string', metadataType === MetadataType.Setting ||
metadataType === MetadataType.SingleFileSetting
? []
: [''],
valueType: metadataValueTypeEnum.string,
}); });
setCurrentValueIndex(tableData.length || 0); setCurrentValueIndex(tableData.length || 0);
setIsAddValueMode(true);
showManageValuesModal(); showManageValuesModal();
}; };
const handleEditValueRow = useCallback( const handleEditValueRow = useCallback(
@ -246,6 +260,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
return Array.from(fieldMap.values()); return Array.from(fieldMap.values());
}); });
setShouldSave(true); setShouldSave(true);
setIsAddValueMode(false);
}; };
useEffect(() => { useEffect(() => {
@ -254,7 +269,6 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
handleSave({ callback: () => {}, builtInMetadata: builtInSelection }); handleSave({ callback: () => {}, builtInMetadata: builtInSelection });
setShouldSave(false); setShouldSave(false);
}, 0); }, 0);
console.log('shouldSave');
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
}, [tableData, shouldSave, handleSave, builtInSelection]); }, [tableData, shouldSave, handleSave, builtInSelection]);
@ -302,28 +316,30 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div>{t('knowledgeDetails.metadata.metadata')}</div> <div>{t('knowledgeDetails.metadata.metadata')}</div>
{metadataType === MetadataType.Manage && ( <div>
<Button {metadataType === MetadataType.Manage && (
variant={'ghost'} <Button
className="border border-border-button" variant={'ghost'}
type="button" className="border border-border-button"
onClick={handleMenuClick(Routes.DataSetSetting, { type="button"
openMetadata: true, onClick={handleMenuClick(Routes.DataSetSetting, {
})} openMetadata: true,
> })}
{t('knowledgeDetails.metadata.toMetadataSetting')} >
</Button> {t('knowledgeDetails.metadata.toMetadataSetting')}
)} </Button>
{isCanAdd && activeTab !== 'built-in' && ( )}
<Button {isCanAdd && activeTab !== 'built-in' && (
variant={'ghost'} <Button
className="border border-border-button" variant={'ghost'}
type="button" className="border border-border-button"
onClick={handAddValueRow} type="button"
> onClick={handAddValueRow}
<Plus /> >
</Button> <Plus />
)} </Button>
)}
</div>
</div> </div>
{rowSelectionIsEmpty || ( {rowSelectionIsEmpty || (
@ -538,17 +554,18 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
type={metadataType} type={metadataType}
existsKeys={existsKeys} existsKeys={existsKeys}
visible={manageValuesVisible} visible={manageValuesVisible}
hideModal={hideManageValuesModal} hideModal={hideManageValuesModalFunc}
data={valueData} data={valueData}
onSave={handleSaveValues} onSave={handleSaveValues}
addUpdateValue={addUpdateValue} addUpdateValue={addUpdateValue}
addDeleteValue={addDeleteValue} addDeleteValue={addDeleteValue}
isEditField={isEditField || isCanAdd} isEditField={isEditField || isAddValueMode}
isAddValue={isAddValue || isCanAdd} isAddValue={isAddValue || isAddValueMode}
isShowDescription={isShowDescription} isShowDescription={isShowDescription}
isShowValueSwitch={isShowValueSwitch} isShowValueSwitch={isShowValueSwitch}
isShowType={isSettingsMode} isShowType={isSettingsMode}
isVerticalShowValue={isVerticalShowValue} isVerticalShowValue={isVerticalShowValue}
isAddValueMode={isAddValueMode}
// handleDeleteSingleValue={handleDeleteSingleValue} // handleDeleteSingleValue={handleDeleteSingleValue}
// handleDeleteSingleRow={handleDeleteSingleRow} // handleDeleteSingleRow={handleDeleteSingleRow}
/> />

View File

@ -13,7 +13,11 @@ import dayjs from 'dayjs';
import { Plus, Trash2 } from 'lucide-react'; import { Plus, Trash2 } from 'lucide-react';
import { memo, useMemo, useRef, useState } from 'react'; import { memo, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { metadataValueTypeOptions } from './hooks/use-manage-modal'; import {
MetadataType,
metadataValueTypeEnum,
metadataValueTypeOptions,
} from './constant';
import { useManageValues } from './hooks/use-manage-values-modal'; import { useManageValues } from './hooks/use-manage-values-modal';
import { IManageValuesProps, MetadataValueType } from './interface'; import { IManageValuesProps, MetadataValueType } from './interface';
@ -26,6 +30,7 @@ const ValueInputItem = memo(
onValueChange, onValueChange,
onDelete, onDelete,
onBlur, onBlur,
isCanDelete = true,
}: { }: {
item: string; item: string;
index: number; index: number;
@ -33,6 +38,7 @@ const ValueInputItem = memo(
onValueChange: (index: number, value: string, isUpdate?: boolean) => void; onValueChange: (index: number, value: string, isUpdate?: boolean) => void;
onDelete: (index: number) => void; onDelete: (index: number) => void;
onBlur: (index: number) => void; onBlur: (index: number) => void;
isCanDelete?: boolean;
}) => { }) => {
const value = useMemo(() => { const value = useMemo(() => {
if (type === 'time') { if (type === 'time') {
@ -84,14 +90,16 @@ const ValueInputItem = memo(
/> />
)} )}
</div> </div>
<Button {isCanDelete && (
type="button" <Button
variant="delete" type="button"
className="border border-border-button px-1 h-6 w-6 rounded-sm" variant="delete"
onClick={() => onDelete(index)} className="border border-border-button px-1 h-6 w-6 rounded-sm"
> onClick={() => onDelete(index)}
<Trash2 size={14} className="w-4 h-4" /> >
</Button> <Trash2 size={14} className="w-4 h-4" />
</Button>
)}
</div> </div>
); );
}, },
@ -109,6 +117,7 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
isShowDescription, isShowDescription,
isVerticalShowValue, isVerticalShowValue,
isShowType, isShowType,
type: metadataType,
} = props; } = props;
const { const {
metaData, metaData,
@ -158,11 +167,33 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
label: 'Type', label: 'Type',
type: FormFieldType.Select, type: FormFieldType.Select,
options: metadataValueTypeOptions, options: metadataValueTypeOptions,
defaultValue: metaData.valueType || 'string', defaultValue: metaData.valueType || metadataValueTypeEnum.string,
onChange: (value: string) => { onChange: (value: string) => {
setValueType(value as MetadataValueType); setValueType(value as MetadataValueType);
handleChange('valueType', value); handleChange('valueType', value);
handleClearValues(); if (
metadataType === MetadataType.Manage ||
metadataType === MetadataType.UpdateSingle
) {
handleClearValues();
}
if (
metadataType === MetadataType.Setting ||
metadataType === MetadataType.SingleFileSetting
) {
if (
value !== metadataValueTypeEnum.list &&
value !== metadataValueTypeEnum.string
) {
handleChange('restrictDefinedValues', false);
handleClearValues(true);
formRef.current?.form.setValue(
'restrictDefinedValues',
false,
);
}
}
}, },
}, },
] ]
@ -188,6 +219,11 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
tooltip: t('knowledgeDetails.metadata.restrictDefinedValuesTip'), tooltip: t('knowledgeDetails.metadata.restrictDefinedValuesTip'),
type: FormFieldType.Switch, type: FormFieldType.Switch,
defaultValue: metaData.restrictDefinedValues || false, defaultValue: metaData.restrictDefinedValues || false,
shouldRender: (formData: any) => {
return (
formData.valueType === 'list' || formData.valueType === 'string'
);
},
onChange: (value: boolean) => onChange: (value: boolean) =>
handleChange('restrictDefinedValues', value), handleChange('restrictDefinedValues', value),
}, },
@ -232,17 +268,19 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div>{t('knowledgeDetails.metadata.values')}</div> <div>{t('knowledgeDetails.metadata.values')}</div>
{isAddValue && isVerticalShowValue && ( {isAddValue &&
<div> isVerticalShowValue &&
<Button metaData.valueType === metadataValueTypeEnum['list'] && (
variant={'ghost'} <div>
className="border border-border-button" <Button
onClick={handleAddValue} variant={'ghost'}
> className="border border-border-button"
<Plus /> onClick={handleAddValue}
</Button> >
</div> <Plus />
)} </Button>
</div>
)}
</div> </div>
{isVerticalShowValue && ( {isVerticalShowValue && (
<div className="flex flex-col gap-2 w-full"> <div className="flex flex-col gap-2 w-full">
@ -259,7 +297,8 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
handleDelete(idx); handleDelete(idx);
}); });
}} }}
onBlur={handleValueBlur} isCanDelete={tempValues.length > 1}
onBlur={() => handleValueBlur()}
/> />
); );
})} })}

View File

@ -34,11 +34,12 @@ import {
} from 'react-hook-form'; } from 'react-hook-form';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import { DataSetContext } from '..'; import { DataSetContext } from '..';
import { MetadataType } from '../../components/metedata/constant';
import { import {
MetadataType,
useManageMetadata, useManageMetadata,
util, util,
} from '../../components/metedata/hooks/use-manage-modal'; } from '../../components/metedata/hooks/use-manage-modal';
import { import {
IBuiltInMetadataItem, IBuiltInMetadataItem,
IMetaDataReturnJSONSettings, IMetaDataReturnJSONSettings,

View File

@ -19,10 +19,8 @@ import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-reques
import { Upload } from 'lucide-react'; import { Upload } from 'lucide-react';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import { MetadataType } from '../components/metedata/constant';
MetadataType, import { useManageMetadata } from '../components/metedata/hooks/use-manage-modal';
useManageMetadata,
} from '../components/metedata/hooks/use-manage-modal';
import { ManageMetadataModal } from '../components/metedata/manage-modal'; import { ManageMetadataModal } from '../components/metedata/manage-modal';
import { useKnowledgeBaseContext } from '../contexts/knowledge-base-context'; import { useKnowledgeBaseContext } from '../contexts/knowledge-base-context';
import { DatasetTable } from './dataset-table'; import { DatasetTable } from './dataset-table';
@ -253,6 +251,8 @@ export default function Dataset() {
// selectedRowKeys={selectedRowKeys} // selectedRowKeys={selectedRowKeys}
tableData={tableData} tableData={tableData}
isCanAdd={metadataConfig.isCanAdd} isCanAdd={metadataConfig.isCanAdd}
isAddValue={metadataConfig.isAddValue}
isVerticalShowValue={metadataConfig.isVerticalShowValue}
isEditField={metadataConfig.isEditField} isEditField={metadataConfig.isEditField}
isDeleteSingleValue={metadataConfig.isDeleteSingleValue} isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
type={metadataConfig.type} type={metadataConfig.type}

View File

@ -16,7 +16,7 @@ import { formatDate } from '@/utils/date';
import { ColumnDef } from '@tanstack/table-core'; import { ColumnDef } from '@tanstack/table-core';
import { ArrowUpDown, MonitorUp } from 'lucide-react'; import { ArrowUpDown, MonitorUp } from 'lucide-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MetadataType } from '../components/metedata/hooks/use-manage-modal'; import { MetadataType } from '../components/metedata/constant';
import { ShowManageMetadataModalProps } from '../components/metedata/interface'; import { ShowManageMetadataModalProps } from '../components/metedata/interface';
import { DatasetActionCell } from './dataset-action-cell'; import { DatasetActionCell } from './dataset-action-cell';
import { ParsingStatusCell } from './parsing-status-cell'; import { ParsingStatusCell } from './parsing-status-cell';
@ -181,7 +181,9 @@ export function useDatasetTableColumns({
// metadata: util.JSONToMetaDataTableData( // metadata: util.JSONToMetaDataTableData(
// row.original.meta_fields || {}, // row.original.meta_fields || {},
// ), // ),
isEditField: false,
isCanAdd: true, isCanAdd: true,
isAddValue: true,
type: MetadataType.UpdateSingle, type: MetadataType.UpdateSingle,
record: row.original, record: row.original,
title: ( title: (