mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-01 03:48:24 +00:00

* Feat: Images dataset collection (#4941) * New pic (#4858) * 更新数据集相关类型,添加图像文件ID和预览URL支持;优化数据集导入功能,新增图像数据集处理组件;修复部分国际化文本;更新文件上传逻辑以支持新功能。 * 与原先代码的差别 * 新增 V4.9.10 更新说明,支持 PG 设置`systemEnv.hnswMaxScanTuples`参数,优化 LLM stream 调用超时,修复全文检索多知识库排序问题。同时更新数据集索引,移除 datasetId 字段以简化查询。 * 更换成fileId_image逻辑,并增加训练队列匹配的逻辑 * 新增图片集合判断逻辑,优化预览URL生成流程,确保仅在数据集为图片集合时生成预览URL,并添加相关日志输出以便调试。 * Refactor Docker Compose configuration to comment out exposed ports for production environments, update image versions for pgvector, fastgpt, and mcp_server, and enhance Redis service with a health check. Additionally, standardize dataset collection labels in constants and improve internationalization strings across multiple languages. * Enhance TrainingStates component by adding internationalization support for the imageParse training mode and update defaultCounts to include imageParse mode in trainingDetail API. * Enhance dataset import context by adding additional steps for image dataset import process and improve internationalization strings for modal buttons in the useEditTitle hook. * Update DatasetImportContext to conditionally render MyStep component based on data source type, improving the import process for non-image datasets. * Refactor image dataset handling by improving internationalization strings, enhancing error messages, and streamlining the preview URL generation process. * 图片上传到新建的 dataset_collection_images 表,逻辑跟随更改 * 修改了除了controller的其他部分问题 * 把图片数据集的逻辑整合到controller里面 * 补充i18n * 补充i18n * resolve评论:主要是上传逻辑的更改和组件复用 * 图片名称的图标显示 * 修改编译报错的命名问题 * 删除不需要的collectionid部分 * 多余文件的处理和改动一个删除按钮 * 除了loading和统一的imageId,其他都resolve掉的 * 处理图标报错 * 复用了MyPhotoView并采用全部替换的方式将imageFileId变成imageId * 去除不必要文件修改 * 报错和字段修改 * 增加上传成功后删除临时文件的逻辑以及回退一些修改 * 删除path字段,将图片保存到gridfs内,并修改增删等操作的代码 * 修正编译错误 --------- Co-authored-by: archer <545436317@qq.com> * perf: image dataset * feat: insert image * perf: image icon * fix: training state --------- Co-authored-by: Zhuangzai fa <143257420+ctrlz526@users.noreply.github.com> * fix: ts (#4948) * Thirddatasetmd (#4942) * add thirddataset.md * fix thirddataset.md * fix * delete wrong png --------- Co-authored-by: dreamer6680 <146868355@qq.com> * perf: api dataset code * perf: log * add secondary.tsx (#4946) * add secondary.tsx * fix --------- Co-authored-by: dreamer6680 <146868355@qq.com> * perf: multiple menu * perf: i18n * feat: parse queue (#4960) * feat: parse queue * feat: sync parse queue * fix thirddataset.md (#4962) * fix thirddataset-4.png (#4963) * feat: Dataset template import (#4934) * 模版导入部分除了文档还没写 * 修复模版导入的 build 错误 * Document production * compress pictures * Change some constants to variables --------- Co-authored-by: Archer <545436317@qq.com> * perf: template import * doc * llm pargraph * bocha tool * fix: del collection --------- Co-authored-by: Zhuangzai fa <143257420+ctrlz526@users.noreply.github.com> Co-authored-by: dreamer6680 <1468683855@qq.com> Co-authored-by: dreamer6680 <146868355@qq.com>
332 lines
6.9 KiB
TypeScript
332 lines
6.9 KiB
TypeScript
import React, { useMemo, useRef, useState } from 'react';
|
|
import {
|
|
Box,
|
|
Flex,
|
|
type MenuItemProps,
|
|
type PlacementWithLogical,
|
|
type AvatarProps,
|
|
type BoxProps,
|
|
type DividerProps
|
|
} from '@chakra-ui/react';
|
|
import MyDivider from '../MyDivider';
|
|
import type { IconNameType } from '../Icon/type';
|
|
import { useSystem } from '../../../hooks/useSystem';
|
|
import Avatar from '../Avatar';
|
|
import MyPopover from '../MyPopover';
|
|
|
|
export type MenuItemType = 'primary' | 'danger' | 'gray' | 'grayBg';
|
|
|
|
export type MenuSizeType = 'sm' | 'md' | 'xs' | 'mini';
|
|
|
|
export type MenuItemData = {
|
|
label?: string;
|
|
children: Array<{
|
|
isActive?: boolean;
|
|
type?: MenuItemType;
|
|
icon?: IconNameType | string;
|
|
label: string | React.ReactNode;
|
|
description?: string;
|
|
onClick?: () => any;
|
|
menuItemStyles?: MenuItemProps;
|
|
menuList?: MenuItemData[];
|
|
}>;
|
|
};
|
|
|
|
export type Props = {
|
|
label?: string;
|
|
width?: number | string;
|
|
offset?: [number, number];
|
|
Trigger: React.ReactNode;
|
|
trigger?: 'hover' | 'click';
|
|
size?: MenuSizeType;
|
|
placement?: PlacementWithLogical;
|
|
hasArrow?: boolean;
|
|
onClose?: () => void;
|
|
menuList: MenuItemData[];
|
|
};
|
|
|
|
const typeMapStyle: Record<MenuItemType, { styles: MenuItemProps; iconColor?: string }> = {
|
|
primary: {
|
|
styles: {
|
|
_hover: {
|
|
backgroundColor: 'primary.50',
|
|
color: 'primary.600'
|
|
},
|
|
_focus: {
|
|
backgroundColor: 'primary.50',
|
|
color: 'primary.600'
|
|
},
|
|
_active: {
|
|
backgroundColor: 'primary.50',
|
|
color: 'primary.600'
|
|
}
|
|
},
|
|
iconColor: 'myGray.600'
|
|
},
|
|
gray: {
|
|
styles: {
|
|
_hover: {
|
|
backgroundColor: 'myGray.05',
|
|
color: 'primary.600'
|
|
},
|
|
_focus: {
|
|
backgroundColor: 'myGray.05',
|
|
color: 'primary.600'
|
|
},
|
|
_active: {
|
|
backgroundColor: 'myGray.05',
|
|
color: 'primary.600'
|
|
}
|
|
},
|
|
iconColor: 'myGray.400'
|
|
},
|
|
grayBg: {
|
|
styles: {
|
|
_hover: {
|
|
backgroundColor: 'myGray.05',
|
|
color: 'primary.600'
|
|
},
|
|
_focus: {
|
|
backgroundColor: 'myGray.05',
|
|
color: 'primary.600'
|
|
},
|
|
_active: {
|
|
backgroundColor: 'myGray.05',
|
|
color: 'primary.600'
|
|
}
|
|
},
|
|
iconColor: 'myGray.600'
|
|
},
|
|
danger: {
|
|
styles: {
|
|
color: 'red.600',
|
|
_hover: {
|
|
background: 'red.1'
|
|
},
|
|
_focus: {
|
|
background: 'red.1'
|
|
},
|
|
_active: {
|
|
background: 'red.1'
|
|
}
|
|
},
|
|
iconColor: 'red.600'
|
|
}
|
|
};
|
|
const sizeMapStyle: Record<
|
|
MenuSizeType,
|
|
{
|
|
iconStyle: AvatarProps;
|
|
labelStyle: BoxProps;
|
|
dividerStyle: DividerProps;
|
|
menuItemStyle: MenuItemProps;
|
|
}
|
|
> = {
|
|
mini: {
|
|
iconStyle: {
|
|
w: '14px'
|
|
},
|
|
labelStyle: {
|
|
fontSize: 'mini'
|
|
},
|
|
dividerStyle: {
|
|
my: 0.5
|
|
},
|
|
menuItemStyle: {
|
|
py: 1.5,
|
|
px: 2
|
|
}
|
|
},
|
|
xs: {
|
|
iconStyle: {
|
|
w: '14px'
|
|
},
|
|
labelStyle: {
|
|
fontSize: 'sm'
|
|
},
|
|
dividerStyle: {
|
|
my: 0.5
|
|
},
|
|
menuItemStyle: {
|
|
py: 1.5,
|
|
px: 2
|
|
}
|
|
},
|
|
sm: {
|
|
iconStyle: {
|
|
w: '1rem'
|
|
},
|
|
labelStyle: {
|
|
fontSize: 'sm'
|
|
},
|
|
dividerStyle: {
|
|
my: 1
|
|
},
|
|
menuItemStyle: {
|
|
py: 2,
|
|
px: 3,
|
|
_notLast: {
|
|
mb: 0.5
|
|
}
|
|
}
|
|
},
|
|
md: {
|
|
iconStyle: {
|
|
w: '2rem',
|
|
borderRadius: '6px'
|
|
},
|
|
labelStyle: {
|
|
fontSize: 'sm'
|
|
},
|
|
dividerStyle: {
|
|
my: 1
|
|
},
|
|
menuItemStyle: {
|
|
py: 2,
|
|
px: 3,
|
|
_notLast: {
|
|
mb: 0.5
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const MenuItem = ({
|
|
item,
|
|
size,
|
|
onClose
|
|
}: {
|
|
item: MenuItemData['children'][number];
|
|
size: MenuSizeType;
|
|
onClose: () => void;
|
|
}) => {
|
|
return (
|
|
<Box
|
|
px={3}
|
|
py={2}
|
|
cursor="pointer"
|
|
borderRadius="md"
|
|
_hover={{
|
|
bg: 'primary.50',
|
|
color: 'primary.600'
|
|
}}
|
|
onClick={(e) => {
|
|
if (item.onClick) {
|
|
item.onClick();
|
|
}
|
|
if (!item.menuList) {
|
|
onClose();
|
|
}
|
|
}}
|
|
>
|
|
<Flex alignItems="center" w="100%">
|
|
{!!item.icon && (
|
|
<Avatar
|
|
src={item.icon as any}
|
|
mr={2}
|
|
{...sizeMapStyle[size].iconStyle}
|
|
color={item.isActive ? 'inherit' : typeMapStyle[item.type || 'primary'].iconColor}
|
|
/>
|
|
)}
|
|
<Box flex="1">
|
|
<Box
|
|
color={item.description ? 'myGray.900' : 'inherit'}
|
|
{...sizeMapStyle[size].labelStyle}
|
|
>
|
|
{item.label}
|
|
</Box>
|
|
{item.description && (
|
|
<Box color={'myGray.500'} fontSize={'mini'}>
|
|
{item.description}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
</Flex>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const MultipleMenu = (props: Props) => {
|
|
const {
|
|
width = 'auto',
|
|
trigger = 'hover',
|
|
size = 'sm',
|
|
offset,
|
|
Trigger,
|
|
menuList,
|
|
hasArrow = false,
|
|
placement = 'bottom-start'
|
|
} = props;
|
|
|
|
const { isPc } = useSystem();
|
|
const formatTrigger = !isPc ? 'click' : trigger;
|
|
|
|
return (
|
|
<MyPopover
|
|
placement={placement}
|
|
offset={offset}
|
|
hasArrow={hasArrow}
|
|
trigger={formatTrigger}
|
|
w={width}
|
|
zIndex={999}
|
|
closeOnBlur={false}
|
|
autoFocus={false}
|
|
Trigger={Trigger}
|
|
>
|
|
{({ onClose }) => {
|
|
const onCloseFn = () => {
|
|
onClose();
|
|
props?.onClose?.();
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
bg="white"
|
|
maxW="300px"
|
|
p="6px"
|
|
border={'1px solid #fff'}
|
|
boxShadow={'3'}
|
|
borderRadius={'md'}
|
|
>
|
|
{menuList.map((group, i) => (
|
|
<Box key={i}>
|
|
{i !== 0 && <MyDivider h={'1.5px'} {...sizeMapStyle[size].dividerStyle} />}
|
|
{group.label && (
|
|
<Box fontSize="sm" px={3} py={1} color="myGray.500">
|
|
{group.label}
|
|
</Box>
|
|
)}
|
|
{group.children.map((item, index) => {
|
|
return (
|
|
<Box key={index}>
|
|
{item.menuList ? (
|
|
<MultipleMenu
|
|
{...props}
|
|
placement={'left'}
|
|
trigger={'hover'}
|
|
menuList={item.menuList}
|
|
onClose={onCloseFn}
|
|
Trigger={
|
|
<Box>
|
|
<MenuItem item={item} size={size} onClose={onCloseFn} />
|
|
</Box>
|
|
}
|
|
hasArrow
|
|
/>
|
|
) : (
|
|
<MenuItem item={item} size={size} onClose={onCloseFn} />
|
|
)}
|
|
</Box>
|
|
);
|
|
})}
|
|
</Box>
|
|
))}
|
|
</Box>
|
|
);
|
|
}}
|
|
</MyPopover>
|
|
);
|
|
};
|
|
|
|
export default React.memo(MultipleMenu);
|