mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Fix: Restore the sidebar description of DP slicing method #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -17,6 +17,7 @@ import {
|
|||||||
export const enum ParseDocumentType {
|
export const enum ParseDocumentType {
|
||||||
DeepDOC = 'DeepDOC',
|
DeepDOC = 'DeepDOC',
|
||||||
PlainText = 'Plain Text',
|
PlainText = 'Plain Text',
|
||||||
|
MinerU = 'MinerU',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LayoutRecognizeFormField({
|
export function LayoutRecognizeFormField({
|
||||||
@ -38,9 +39,12 @@ export function LayoutRecognizeFormField({
|
|||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
const list = optionsWithoutLLM
|
const list = optionsWithoutLLM
|
||||||
? optionsWithoutLLM
|
? optionsWithoutLLM
|
||||||
: [ParseDocumentType.DeepDOC, ParseDocumentType.PlainText].map((x) => ({
|
: [
|
||||||
label:
|
ParseDocumentType.DeepDOC,
|
||||||
x === ParseDocumentType.PlainText ? t(camelCase(x)) : 'DeepDoc',
|
ParseDocumentType.PlainText,
|
||||||
|
ParseDocumentType.MinerU,
|
||||||
|
].map((x) => ({
|
||||||
|
label: x === ParseDocumentType.PlainText ? t(camelCase(x)) : x,
|
||||||
value: x,
|
value: x,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -116,7 +116,10 @@ const Modal: ModalType = ({
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={confirmLoading || disabled}
|
disabled={confirmLoading || disabled}
|
||||||
onClick={() => handleOk()}
|
onClick={() => handleOk()}
|
||||||
className="px-2 py-1 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
className={cn(
|
||||||
|
'px-2 py-1 bg-primary text-primary-foreground rounded-md hover:bg-primary/90',
|
||||||
|
{ 'cursor-not-allowed': disabled },
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{confirmLoading && (
|
{confirmLoading && (
|
||||||
<Loader className="inline-block mr-2 h-4 w-4 animate-spin" />
|
<Loader className="inline-block mr-2 h-4 w-4 animate-spin" />
|
||||||
|
|||||||
72
web/src/pages/dataset/dataset-setting/category-panel.tsx
Normal file
72
web/src/pages/dataset/dataset-setting/category-panel.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import SvgIcon from '@/components/svg-icon';
|
||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import { useSelectParserList } from '@/hooks/user-setting-hooks';
|
||||||
|
import { Col, Divider, Empty, Row, Typography } from 'antd';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import camelCase from 'lodash/camelCase';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { TagTabs } from './tag-tabs';
|
||||||
|
import { ImageMap } from './utils';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||||
|
const parserList = useSelectParserList();
|
||||||
|
const { t } = useTranslate('knowledgeConfiguration');
|
||||||
|
|
||||||
|
const item = useMemo(() => {
|
||||||
|
const item = parserList.find((x) => x.value === chunkMethod);
|
||||||
|
if (item) {
|
||||||
|
return {
|
||||||
|
title: item.label,
|
||||||
|
description: t(camelCase(item.value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { title: '', description: '' };
|
||||||
|
}, [parserList, chunkMethod, t]);
|
||||||
|
|
||||||
|
const imageList = useMemo(() => {
|
||||||
|
if (chunkMethod in ImageMap) {
|
||||||
|
return ImageMap[chunkMethod as keyof typeof ImageMap];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [chunkMethod]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
{imageList.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<h5 className="font-semibold text-base mt-0 mb-1">
|
||||||
|
{`"${item.title}" ${t('methodTitle')}`}
|
||||||
|
</h5>
|
||||||
|
<p
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(item.description),
|
||||||
|
}}
|
||||||
|
></p>
|
||||||
|
<h5 className="font-semibold text-base mt-4 mb-1">{`"${item.title}" ${t('methodExamples')}`}</h5>
|
||||||
|
<Text>{t('methodExamplesDescription')}</Text>
|
||||||
|
<Row gutter={[10, 10]} className="mt-4">
|
||||||
|
{imageList.map((x) => (
|
||||||
|
<Col span={12} key={x}>
|
||||||
|
<SvgIcon name={x} width={'100%'} className="w-full"></SvgIcon>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
<h5 className="font-semibold text-base mt-4 mb-1">
|
||||||
|
{item.title} {t('dialogueExamplesTitle')}
|
||||||
|
</h5>
|
||||||
|
<Divider></Divider>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Empty description={''} image={null}>
|
||||||
|
<p>{t('methodEmpty')}</p>
|
||||||
|
<SvgIcon name={'chunk-method/chunk-empty'} width={'100%'}></SvgIcon>
|
||||||
|
</Empty>
|
||||||
|
)}
|
||||||
|
{chunkMethod === 'tag' && <TagTabs></TagTabs>}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CategoryPanel;
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import CategoryPanel from './category-panel';
|
||||||
|
|
||||||
|
export default ({ parserId }: { parserId: string }) => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('hidden flex-1', 'flex flex-col')}>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(!visible);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('knowledgeDetails.learnMore')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="bg-[#FFF]/10 p-[20px] rounded-[12px] mt-[10px] relative flex-1 overflow-auto"
|
||||||
|
style={{ display: visible ? 'block' : 'none' }}
|
||||||
|
>
|
||||||
|
<CategoryPanel chunkMethod={parserId}></CategoryPanel>
|
||||||
|
<div
|
||||||
|
className="absolute right-1 top-1 cursor-pointer hover:text-[#FFF]/30"
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -21,6 +21,7 @@ import {
|
|||||||
IGenerateLogButtonProps,
|
IGenerateLogButtonProps,
|
||||||
} from '../dataset/generate-button/generate';
|
} from '../dataset/generate-button/generate';
|
||||||
import { ChunkMethodForm } from './chunk-method-form';
|
import { ChunkMethodForm } from './chunk-method-form';
|
||||||
|
import ChunkMethodLearnMore from './chunk-method-learn-more';
|
||||||
import { IDataPipelineNodeProps } from './components/link-data-pipeline';
|
import { IDataPipelineNodeProps } from './components/link-data-pipeline';
|
||||||
import { MainContainer } from './configuration-form-container';
|
import { MainContainer } from './configuration-form-container';
|
||||||
import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item';
|
import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item';
|
||||||
@ -169,10 +170,7 @@ export default function DatasetSettings() {
|
|||||||
></TopTitle>
|
></TopTitle>
|
||||||
<div className="flex gap-14 flex-1 min-h-0">
|
<div className="flex gap-14 flex-1 min-h-0">
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 ">
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-6 flex-1"
|
|
||||||
>
|
|
||||||
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
|
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
|
||||||
<MainContainer className="text-text-secondary">
|
<MainContainer className="text-text-secondary">
|
||||||
<GeneralForm></GeneralForm>
|
<GeneralForm></GeneralForm>
|
||||||
@ -231,6 +229,9 @@ export default function DatasetSettings() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
<div className="flex-1">
|
||||||
|
{parseType === 1 && <ChunkMethodLearnMore parserId={selectedTag} />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
305
web/src/pages/dataset/dataset-setting/tag-table/index.tsx
Normal file
305
web/src/pages/dataset/dataset-setting/tag-table/index.tsx
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table';
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from '@/components/ui/tooltip';
|
||||||
|
import { useDeleteTag, useFetchTagList } from '@/hooks/knowledge-hooks';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useRenameKnowledgeTag } from '../hooks';
|
||||||
|
import { RenameDialog } from './rename-dialog';
|
||||||
|
|
||||||
|
export type ITag = {
|
||||||
|
tag: string;
|
||||||
|
frequency: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function TagTable() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { list } = useFetchTagList();
|
||||||
|
const [tagList, setTagList] = useState<ITag[]>([]);
|
||||||
|
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = useState({});
|
||||||
|
|
||||||
|
const { deleteTag } = useDeleteTag();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTagList(list.map((x) => ({ tag: x[0], frequency: x[1] })));
|
||||||
|
}, [list]);
|
||||||
|
|
||||||
|
const handleDeleteTag = useCallback(
|
||||||
|
(tags: string[]) => () => {
|
||||||
|
deleteTag(tags);
|
||||||
|
},
|
||||||
|
[deleteTag],
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
showTagRenameModal,
|
||||||
|
hideTagRenameModal,
|
||||||
|
tagRenameVisible,
|
||||||
|
initialName,
|
||||||
|
} = useRenameKnowledgeTag();
|
||||||
|
|
||||||
|
const columns: ColumnDef<ITag>[] = [
|
||||||
|
{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
table.getIsAllPageRowsSelected() ||
|
||||||
|
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
aria-label="Select all"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
aria-label="Select row"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'tag',
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
>
|
||||||
|
{t('knowledgeConfiguration.tagName')}
|
||||||
|
<ArrowUpDown />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const value: string = row.getValue('tag');
|
||||||
|
return <div>{value}</div>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'frequency',
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
>
|
||||||
|
{t('knowledgeConfiguration.frequency')}
|
||||||
|
<ArrowUpDown />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="capitalize ">{row.getValue('frequency')}</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
enableHiding: false,
|
||||||
|
header: t('common.action'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<Tooltip>
|
||||||
|
<ConfirmDeleteDialog onOk={handleDeleteTag([row.original.tag])}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
</ConfirmDeleteDialog>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t('common.delete')}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => showTagRenameModal(row.original.tag)}
|
||||||
|
>
|
||||||
|
<Pencil />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t('common.rename')}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: tagList,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedRowLength = table.getFilteredSelectedRowModel().rows.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipProvider>
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="flex items-center justify-between py-4 ">
|
||||||
|
<Input
|
||||||
|
placeholder={t('knowledgeConfiguration.searchTags')}
|
||||||
|
value={(table.getColumn('tag')?.getFilterValue() as string) ?? ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
table.getColumn('tag')?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
{selectedRowLength > 0 && (
|
||||||
|
<ConfirmDeleteDialog
|
||||||
|
onOk={handleDeleteTag(
|
||||||
|
table
|
||||||
|
.getFilteredSelectedRowModel()
|
||||||
|
.rows.map((x) => x.original.tag),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
</ConfirmDeleteDialog>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Table rootClassName="rounded-none border max-h-80 overflow-y-auto">
|
||||||
|
<TableHeader className="bg-[#39393b]">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext(),
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext(),
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-end space-x-2 py-4">
|
||||||
|
<div className="flex-1 text-sm text-muted-foreground">
|
||||||
|
{selectedRowLength} of {table.getFilteredRowModel().rows.length}{' '}
|
||||||
|
row(s) selected.
|
||||||
|
</div>
|
||||||
|
<div className="space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
{t('common.previousPage')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
{t('common.nextPage')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{tagRenameVisible && (
|
||||||
|
<RenameDialog
|
||||||
|
hideModal={hideTagRenameModal}
|
||||||
|
initialName={initialName}
|
||||||
|
></RenameDialog>
|
||||||
|
)}
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { LoadingButton } from '@/components/ui/loading-button';
|
||||||
|
import { useTagIsRenaming } from '@/hooks/knowledge-hooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { RenameForm } from './rename-form';
|
||||||
|
|
||||||
|
export function RenameDialog({
|
||||||
|
hideModal,
|
||||||
|
initialName,
|
||||||
|
}: IModalProps<any> & { initialName: string }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const loading = useTagIsRenaming();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onOpenChange={hideModal}>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{t('common.rename')}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<RenameForm
|
||||||
|
initialName={initialName}
|
||||||
|
hideModal={hideModal}
|
||||||
|
></RenameForm>
|
||||||
|
<DialogFooter>
|
||||||
|
<LoadingButton type="submit" form={TagRenameId} loading={loading}>
|
||||||
|
{t('common.save')}
|
||||||
|
</LoadingButton>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/components/ui/form';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { useRenameTag } from '@/hooks/knowledge-hooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export function RenameForm({
|
||||||
|
initialName,
|
||||||
|
hideModal,
|
||||||
|
}: IModalProps<any> & { initialName: string }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const FormSchema = z.object({
|
||||||
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, {
|
||||||
|
message: t('common.namePlaceholder'),
|
||||||
|
})
|
||||||
|
.trim(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { renameTag } = useRenameTag();
|
||||||
|
|
||||||
|
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
const ret = await renameTag({ fromTag: initialName, toTag: data.name });
|
||||||
|
if (ret) {
|
||||||
|
hideModal?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setValue('name', initialName);
|
||||||
|
}, [form, initialName]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-6"
|
||||||
|
id={TagRenameId}
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('common.name')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t('common.namePlaceholder')}
|
||||||
|
{...field}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
40
web/src/pages/dataset/dataset-setting/tag-tabs.tsx
Normal file
40
web/src/pages/dataset/dataset-setting/tag-tabs.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Segmented } from 'antd';
|
||||||
|
import { SegmentedLabeledOption } from 'antd/es/segmented';
|
||||||
|
import { upperFirst } from 'lodash';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { TagTable } from './tag-table';
|
||||||
|
import { TagWordCloud } from './tag-word-cloud';
|
||||||
|
|
||||||
|
enum TagType {
|
||||||
|
Cloud = 'cloud',
|
||||||
|
Table = 'table',
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagContentMap = {
|
||||||
|
[TagType.Cloud]: <TagWordCloud></TagWordCloud>,
|
||||||
|
[TagType.Table]: <TagTable></TagTable>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function TagTabs() {
|
||||||
|
const [value, setValue] = useState<TagType>(TagType.Cloud);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const options: SegmentedLabeledOption[] = [TagType.Cloud, TagType.Table].map(
|
||||||
|
(x) => ({
|
||||||
|
label: t(`knowledgeConfiguration.tag${upperFirst(x)}`),
|
||||||
|
value: x,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="mt-4">
|
||||||
|
<Segmented
|
||||||
|
value={value}
|
||||||
|
options={options}
|
||||||
|
onChange={(val) => setValue(val as TagType)}
|
||||||
|
/>
|
||||||
|
{TagContentMap[value]}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
62
web/src/pages/dataset/dataset-setting/tag-word-cloud.tsx
Normal file
62
web/src/pages/dataset/dataset-setting/tag-word-cloud.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { useFetchTagList } from '@/hooks/knowledge-hooks';
|
||||||
|
import { Chart } from '@antv/g2';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
|
export function TagWordCloud() {
|
||||||
|
const domRef = useRef<HTMLDivElement>(null);
|
||||||
|
let chartRef = useRef<Chart>();
|
||||||
|
const { list } = useFetchTagList();
|
||||||
|
|
||||||
|
const { list: tagList } = useMemo(() => {
|
||||||
|
const nextList = list.sort((a, b) => b[1] - a[1]).slice(0, 256);
|
||||||
|
|
||||||
|
return {
|
||||||
|
list: nextList.map((x) => ({ text: x[0], value: x[1], name: x[0] })),
|
||||||
|
sumValue: sumBy(nextList, (x: [string, number]) => x[1]),
|
||||||
|
length: nextList.length,
|
||||||
|
};
|
||||||
|
}, [list]);
|
||||||
|
|
||||||
|
const renderWordCloud = useCallback(() => {
|
||||||
|
if (domRef.current) {
|
||||||
|
chartRef.current = new Chart({ container: domRef.current });
|
||||||
|
|
||||||
|
chartRef.current.options({
|
||||||
|
type: 'wordCloud',
|
||||||
|
autoFit: true,
|
||||||
|
layout: {
|
||||||
|
fontSize: [10, 50],
|
||||||
|
// fontSize: (d: any) => {
|
||||||
|
// if (d.value) {
|
||||||
|
// return (d.value / sumValue) * 100 * (length / 10);
|
||||||
|
// }
|
||||||
|
// return 0;
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: 'inline',
|
||||||
|
value: tagList,
|
||||||
|
},
|
||||||
|
encode: { color: 'text' },
|
||||||
|
legend: false,
|
||||||
|
tooltip: {
|
||||||
|
title: 'name', // title
|
||||||
|
items: ['value'], // data item
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
chartRef.current.render();
|
||||||
|
}
|
||||||
|
}, [tagList]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
renderWordCloud();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
chartRef.current?.destroy();
|
||||||
|
};
|
||||||
|
}, [renderWordCloud]);
|
||||||
|
|
||||||
|
return <div ref={domRef} className="w-full h-[38vh]"></div>;
|
||||||
|
}
|
||||||
20
web/src/pages/dataset/dataset-setting/utils.ts
Normal file
20
web/src/pages/dataset/dataset-setting/utils.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const getImageName = (prefix: string, length: number) =>
|
||||||
|
new Array(length)
|
||||||
|
.fill(0)
|
||||||
|
.map((x, idx) => `chunk-method/${prefix}-0${idx + 1}`);
|
||||||
|
|
||||||
|
export const ImageMap = {
|
||||||
|
book: getImageName('book', 4),
|
||||||
|
laws: getImageName('law', 2),
|
||||||
|
manual: getImageName('manual', 4),
|
||||||
|
picture: getImageName('media', 2),
|
||||||
|
naive: getImageName('naive', 2),
|
||||||
|
paper: getImageName('paper', 2),
|
||||||
|
presentation: getImageName('presentation', 2),
|
||||||
|
qa: getImageName('qa', 2),
|
||||||
|
resume: getImageName('resume', 2),
|
||||||
|
table: getImageName('table', 2),
|
||||||
|
one: getImageName('one', 2),
|
||||||
|
knowledge_graph: getImageName('knowledge-graph', 2),
|
||||||
|
tag: getImageName('tag', 2),
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user