Commit b4a5a76d by siigure Committed by GitHub

feat: tooltip (#7087)

* feat: tooltip

* fix: 优化 tooltip 展示逻辑并修复 header UI 布局

* fix: 优化名称 tooltip 溢出展示

* submodule

---------

Co-authored-by: archer <545436317@qq.com>
parent d38bf227
...@@ -10,3 +10,4 @@ description: 'FastGPT V4.15.0-beta5 Release Notes' ...@@ -10,3 +10,4 @@ description: 'FastGPT V4.15.0-beta5 Release Notes'
## ⚙️ Improvements ## ⚙️ Improvements
1. HTML output now automatically switches to preview mode after generation, reducing the need to open the preview manually. 1. HTML output now automatically switches to preview mode after generation, reducing the need to open the preview manually.
2. Improved long-name display for apps, datasets, files, and folders: names are truncated when they exceed the available width, and the full name is shown when hovering over the name.
...@@ -10,3 +10,4 @@ description: 'FastGPT V4.15.0-beta5 更新说明' ...@@ -10,3 +10,4 @@ description: 'FastGPT V4.15.0-beta5 更新说明'
## ⚙️ 优化 ## ⚙️ 优化
1. HTML 输出后自动切换为预览,减少手动打开预览的操作。 1. HTML 输出后自动切换为预览,减少手动打开预览的操作。
2. 优化应用、知识库、文件和文件夹等长名称展示:超出宽度时自动省略,并在 hover 名称时展示完整内容。
...@@ -130,39 +130,11 @@ ...@@ -130,39 +130,11 @@
"content/guide/workspace/team/team_roles_permissions.en.mdx": "2026-05-07T15:06:40+08:00", "content/guide/workspace/team/team_roles_permissions.en.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/workspace/team/team_roles_permissions.mdx": "2026-05-07T15:06:40+08:00", "content/guide/workspace/team/team_roles_permissions.mdx": "2026-05-07T15:06:40+08:00",
"content/openapi/app.en.mdx": "2026-05-29T19:31:16+08:00", "content/openapi/app.en.mdx": "2026-05-29T19:31:16+08:00",
"content/openapi/app.mdx": "2026-05-29T19:31:16+08:00",
"content/openapi/chat.en.mdx": "2026-05-29T19:31:16+08:00",
"content/openapi/chat.mdx": "2026-05-29T19:31:16+08:00",
"content/openapi/dataset.en.mdx": "2026-05-29T19:31:16+08:00", "content/openapi/dataset.en.mdx": "2026-05-29T19:31:16+08:00",
"content/openapi/dataset.mdx": "2026-05-29T19:31:16+08:00", "content/openapi/dataset.mdx": "2026-05-29T19:31:16+08:00",
"content/openapi/index.en.mdx": "2026-04-26T21:08:47+08:00",
"content/openapi/index.mdx": "2026-04-26T21:08:47+08:00",
"content/openapi/intro.en.mdx": "2026-05-29T21:03:14+08:00",
"content/openapi/intro.mdx": "2026-05-29T21:03:14+08:00",
"content/openapi/share.en.mdx": "2026-04-26T21:08:47+08:00",
"content/openapi/share.mdx": "2026-04-26T21:08:47+08:00",
"content/plugin/index.en.mdx": "2026-06-04T16:10:15+08:00",
"content/plugin/index.mdx": "2026-06-04T16:10:15+08:00", "content/plugin/index.mdx": "2026-06-04T16:10:15+08:00",
"content/plugin/intro.en.mdx": "2026-06-09T16:03:58+08:00",
"content/plugin/intro.mdx": "2026-06-09T16:03:58+08:00",
"content/plugin/model-presets.en.mdx": "2026-06-04T16:10:15+08:00", "content/plugin/model-presets.en.mdx": "2026-06-04T16:10:15+08:00",
"content/plugin/model-presets.mdx": "2026-06-04T16:10:15+08:00",
"content/plugin/system-tool-development.en.mdx": "2026-06-09T16:03:58+08:00",
"content/plugin/system-tool-development.mdx": "2026-06-09T16:03:58+08:00",
"content/self-host/config/env.en.mdx": "2026-06-10T19:02:59+08:00",
"content/self-host/config/env.mdx": "2026-06-10T19:02:59+08:00",
"content/self-host/config/json.en.mdx": "2026-05-25T11:21:30+08:00",
"content/self-host/config/json.mdx": "2026-05-25T11:21:30+08:00",
"content/self-host/config/model/intro.en.mdx": "2026-06-04T16:10:15+08:00",
"content/self-host/config/model/intro.mdx": "2026-06-04T16:10:15+08:00",
"content/self-host/config/model/minimax.en.mdx": "2026-06-03T10:40:17+08:00",
"content/self-host/config/model/minimax.mdx": "2026-06-03T10:40:17+08:00",
"content/self-host/config/model/siliconCloud.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/config/model/siliconCloud.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/config/model/siliconCloud.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/config/object-storage.en.mdx": "2026-05-21T11:24:48+08:00",
"content/self-host/config/object-storage.mdx": "2026-05-21T11:24:48+08:00",
"content/self-host/config/signoz.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/config/signoz.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/custom-models/bge-rerank.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/custom-models/bge-rerank.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/custom-models/bge-rerank.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/custom-models/bge-rerank.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/custom-models/chatglm2-m3e.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/custom-models/chatglm2-m3e.en.mdx": "2026-04-26T21:08:47+08:00",
...@@ -193,8 +165,8 @@ ...@@ -193,8 +165,8 @@
"content/self-host/migration/docker_db.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/migration/docker_db.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/migration/docker_mongo.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/migration/docker_mongo.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/migration/docker_mongo.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/migration/docker_mongo.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/troubleshooting/attention.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/troubleshooting/attention.en.mdx": "2026-06-12T12:19:25+08:00",
"content/self-host/troubleshooting/attention.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/troubleshooting/attention.mdx": "2026-06-12T12:19:25+08:00",
"content/self-host/troubleshooting/faq.en.mdx": "2026-05-12T18:16:10+08:00", "content/self-host/troubleshooting/faq.en.mdx": "2026-05-12T18:16:10+08:00",
"content/self-host/troubleshooting/faq.mdx": "2026-05-12T18:16:10+08:00", "content/self-host/troubleshooting/faq.mdx": "2026-05-12T18:16:10+08:00",
"content/self-host/troubleshooting/methods.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/troubleshooting/methods.en.mdx": "2026-04-26T21:08:47+08:00",
......
import React from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Tooltip, type TooltipProps } from '@chakra-ui/react'; import { Tooltip, type TooltipProps, useMergeRefs } from '@chakra-ui/react';
interface Props extends TooltipProps {} const defaultTooltipProps = {
className: 'chakra-tooltip',
bg: 'white',
arrowShadowColor: 'rgba(0,0,0,0.05)',
hasArrow: true,
arrowSize: 12,
offset: [-15, 15] as [number, number],
color: 'myGray.800',
px: 4,
py: 2,
borderRadius: '8px',
whiteSpace: 'pre-wrap',
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)'
} satisfies Omit<TooltipProps, 'children'>;
interface Props extends TooltipProps {
/**
* 仅当触发元素横向内容溢出时展示 Tooltip,用于替代外部按字数估算的判断。
*/
showOnlyWhenOverflow?: boolean;
}
/**
* 为“仅溢出时展示”模式注入测量 ref,并保留原子元素上的 ref 与 hover/focus 事件。
* 普通 Tooltip 不走这段逻辑,避免全局 tooltip 都承担 DOM 测量成本。
*/
const useOverflowTooltipTrigger = ({
children,
enabled
}: {
children: React.ReactNode;
enabled: boolean;
}) => {
const triggerRef = useRef<HTMLElement | null>(null);
const [isOverflow, setIsOverflow] = useState(false);
const checkOverflow = useCallback(() => {
if (!enabled) {
setIsOverflow(false);
return;
}
const target = triggerRef.current;
if (!target) {
setIsOverflow(false);
return;
}
setIsOverflow(
target.scrollWidth > target.clientWidth || target.scrollHeight > target.clientHeight
);
}, [enabled]);
useEffect(() => {
if (!enabled) return;
const frame = window.requestAnimationFrame(checkOverflow);
const target = triggerRef.current;
if (!target) {
return () => window.cancelAnimationFrame(frame);
}
const observer =
typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(checkOverflow);
observer?.observe(target);
window.addEventListener('resize', checkOverflow);
return () => {
window.cancelAnimationFrame(frame);
observer?.disconnect();
window.removeEventListener('resize', checkOverflow);
};
}, [checkOverflow, children, enabled]);
const setTriggerRef = useCallback(
(node: HTMLElement | null) => {
triggerRef.current = node;
checkOverflow();
},
[checkOverflow]
);
const triggerElement = React.isValidElement(children) ? children : null;
const childOriginRef = triggerElement
? ((triggerElement as any).ref as React.Ref<HTMLElement> | undefined)
: undefined;
const triggerElementProps = triggerElement?.props as
| {
onMouseEnter?: React.MouseEventHandler;
onFocus?: React.FocusEventHandler;
}
| undefined;
const mergedRef = useMergeRefs(childOriginRef, setTriggerRef);
if (!enabled) {
return {
isOverflow: false,
triggerChildren: children
};
}
return {
isOverflow,
triggerChildren: triggerElement ? (
React.cloneElement(triggerElement as React.ReactElement<any>, {
ref: mergedRef,
onMouseEnter: (event: React.MouseEvent) => {
checkOverflow();
triggerElementProps?.onMouseEnter?.(event);
},
onFocus: (event: React.FocusEvent) => {
checkOverflow();
triggerElementProps?.onFocus?.(event);
}
})
) : (
<span
ref={setTriggerRef}
style={{ display: 'inline-block', maxWidth: '100%' }}
onMouseEnter={checkOverflow}
onFocus={checkOverflow}
>
{children}
</span>
)
};
};
const MyTooltip = ({
children,
shouldWrapChildren = true,
showOnlyWhenOverflow = false,
isDisabled,
...props
}: Props) => {
const { isOverflow, triggerChildren } = useOverflowTooltipTrigger({
children,
enabled: showOnlyWhenOverflow
});
const finalChildren = showOnlyWhenOverflow ? triggerChildren : children;
const finalShouldWrapChildren = showOnlyWhenOverflow ? false : shouldWrapChildren;
const finalIsDisabled = isDisabled || (showOnlyWhenOverflow && !isOverflow);
const MyTooltip = ({ children, shouldWrapChildren = true, ...props }: Props) => {
return ( return (
<Tooltip <Tooltip
className="chakra-tooltip" {...defaultTooltipProps}
bg={'white'} shouldWrapChildren={finalShouldWrapChildren}
arrowShadowColor={'rgba(0,0,0,0.05)'} isDisabled={finalIsDisabled}
hasArrow
arrowSize={12}
offset={[-15, 15]}
color={'myGray.800'}
px={4}
py={2}
borderRadius={'8px'}
whiteSpace={'pre-wrap'}
boxShadow={'1px 1px 10px rgba(0,0,0,0.2)'}
shouldWrapChildren={shouldWrapChildren}
{...props} {...props}
> >
{children} {finalChildren}
</Tooltip> </Tooltip>
); );
}; };
......
Subproject commit 0f49c306a09ba34ea427643dd81d7939cd80985f Subproject commit cd209c713a0263acb08492738c2c4265d8dc37b9
...@@ -110,9 +110,6 @@ const FolderPath = (props: { ...@@ -110,9 +110,6 @@ const FolderPath = (props: {
} }
}; };
const shouldTruncate = !isLast && item.parentName.length > 10;
const displayName = shouldTruncate ? `${item.parentName.slice(0, 10)}...` : item.parentName;
const pathBox = ( const pathBox = (
<Box <Box
fontSize={['xs', fontSize || 'sm']} fontSize={['xs', fontSize || 'sm']}
...@@ -133,13 +130,15 @@ const FolderPath = (props: { ...@@ -133,13 +130,15 @@ const FolderPath = (props: {
})} })}
{...(isLast && !forbidLastClick && clickStyles)} {...(isLast && !forbidLastClick && clickStyles)}
> >
{displayName} {item.parentName}
</Box> </Box>
); );
return ( return (
<Flex key={item.parentId || index} alignItems={'center'}> <Flex key={item.parentId || index} alignItems={'center'}>
{shouldTruncate ? <MyTooltip label={item.parentName}>{pathBox}</MyTooltip> : pathBox} <MyTooltip label={item.parentName} showOnlyWhenOverflow>
{pathBox}
</MyTooltip>
{!isLast && <MyIcon name={'common/line'} color={'myGray.500'} mx={1} width={'5px'} />} {!isLast && <MyIcon name={'common/line'} color={'myGray.500'} mx={1} width={'5px'} />}
</Flex> </Flex>
); );
......
...@@ -29,6 +29,7 @@ import FolderPath from '@/components/common/folder/Path'; ...@@ -29,6 +29,7 @@ import FolderPath from '@/components/common/folder/Path';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import QuickCreateDatasetModal from '@/pageComponents/app/detail/components/QuickCreateDatasetModal'; import QuickCreateDatasetModal from '@/pageComponents/app/detail/components/QuickCreateDatasetModal';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
// Dataset selection modal component // Dataset selection modal component
export const DatasetSelectModal = ({ export const DatasetSelectModal = ({
...@@ -306,14 +307,29 @@ export const DatasetSelectModal = ({ ...@@ -306,14 +307,29 @@ export const DatasetSelectModal = ({
</Box> </Box>
{/* Avatar */} {/* Avatar */}
<Avatar src={item.avatar} w={7} h={7} borderRadius="sm" ml={3} mr={2.5} /> <Avatar
src={item.avatar}
w={7}
h={7}
borderRadius="sm"
ml={3}
mr={2.5}
flexShrink={0}
/>
{/* Name and type */} {/* Name and type */}
<Box flex={1} minW={0}> <Box flex={1} minW={0}>
<Box fontSize="sm" color={'myGray.900'} lineHeight={1}> <MyTooltip label={item.name} showOnlyWhenOverflow>
<Box
fontSize="sm"
color={'myGray.900'}
lineHeight={1.2}
className="textEllipsis"
>
{item.name} {item.name}
</Box> </Box>
<Box fontSize="xs" color="myGray.500"> </MyTooltip>
<Box fontSize="xs" color="myGray.500" className="textEllipsis">
{item.type === DatasetTypeEnum.folder ? ( {item.type === DatasetTypeEnum.folder ? (
<>{t('common:Folder')}</> <>{t('common:Folder')}</>
) : ( ) : (
...@@ -408,9 +424,20 @@ export const DatasetSelectModal = ({ ...@@ -408,9 +424,20 @@ export const DatasetSelectModal = ({
cursor="pointer" cursor="pointer"
alignItems="center" alignItems="center"
> >
<Avatar src={item.avatar} w={6} h={6} borderRadius="sm" mr={3} /> <Avatar
<Box flex={1} minW={0}> src={item.avatar}
<Box fontSize="sm">{item.name}</Box> w={6}
h={6}
borderRadius="sm"
mr={3}
flexShrink={0}
/>
<Box flex={1} minW={0} mr={2}>
<MyTooltip label={item.name} showOnlyWhenOverflow>
<Box fontSize="sm" className="textEllipsis" lineHeight={1.2}>
{item.name}
</Box>
</MyTooltip>
</Box> </Box>
<IconButton <IconButton
aria-label="Remove" aria-label="Remove"
...@@ -418,6 +445,7 @@ export const DatasetSelectModal = ({ ...@@ -418,6 +445,7 @@ export const DatasetSelectModal = ({
size="xs" size="xs"
variant="ghost" variant="ghost"
color="black" color="black"
flexShrink={0}
_hover={{ bg: 'myGray.200' }} _hover={{ bg: 'myGray.200' }}
onClick={() => onClick={() =>
setSelectedDatasets((prev) => setSelectedDatasets((prev) =>
......
...@@ -51,12 +51,9 @@ const RawSourceBox = ({ ...@@ -51,12 +51,9 @@ const RawSourceBox = ({
teamId, teamId,
teamToken teamToken
}); });
const displaySourceName = sourceName || t('common:unknow_source');
return ( return (
<MyTooltip
label={canPreview ? t('file:click_to_view_raw_source') : ''}
shouldWrapChildren={false}
>
<Box <Box
color={'myGray.900'} color={'myGray.900'}
fontWeight={'medium'} fontWeight={'medium'}
...@@ -71,16 +68,18 @@ const RawSourceBox = ({ ...@@ -71,16 +68,18 @@ const RawSourceBox = ({
: {})} : {})}
{...props} {...props}
> >
<MyIcon name={icon as any} w={['1rem', '1.25rem']} mr={2} /> <MyIcon name={icon as any} w={['1rem', '1.25rem']} mr={2} flexShrink={0} />
<MyTooltip label={displaySourceName} showOnlyWhenOverflow>
<Box <Box
maxW={['200px', '300px']} maxW={['200px', '300px']}
className={props.className ?? 'textEllipsis'} className={props.className ?? 'textEllipsis'}
wordBreak={'break-all'} wordBreak={'break-all'}
minW={0}
> >
{sourceName || t('common:unknow_source')} {displaySourceName}
</Box>
</Box> </Box>
</MyTooltip> </MyTooltip>
</Box>
); );
}; };
......
...@@ -27,6 +27,7 @@ import type { SimpleAppSnapshotType } from './useSnapshots'; ...@@ -27,6 +27,7 @@ import type { SimpleAppSnapshotType } from './useSnapshots';
import ExportConfigPopover from '@/pageComponents/app/detail/ExportConfigPopover'; import ExportConfigPopover from '@/pageComponents/app/detail/ExportConfigPopover';
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants'; import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
import type { Form2WorkflowFnType } from './type'; import type { Form2WorkflowFnType } from './type';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const AppCard = ({ const AppCard = ({
appForm, appForm,
...@@ -91,6 +92,7 @@ const AppCard = ({ ...@@ -91,6 +92,7 @@ const AppCard = ({
<Flex alignItems={'center'} justifyContent={'space-between'} mb={5}> <Flex alignItems={'center'} justifyContent={'space-between'} mb={5}>
<Flex alignItems={'center'} flex={1} minW={0}> <Flex alignItems={'center'} flex={1} minW={0}>
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} h={'28px'} /> <Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} h={'28px'} />
<MyTooltip label={appDetail.name} showOnlyWhenOverflow>
<Box <Box
ml={3} ml={3}
fontWeight={'bold'} fontWeight={'bold'}
...@@ -101,6 +103,7 @@ const AppCard = ({ ...@@ -101,6 +103,7 @@ const AppCard = ({
> >
{appDetail.name} {appDetail.name}
</Box> </Box>
</MyTooltip>
</Flex> </Flex>
{/* Right Action Icons */} {/* Right Action Icons */}
......
...@@ -132,7 +132,7 @@ const ExportConfigPopover = ({ ...@@ -132,7 +132,7 @@ const ExportConfigPopover = ({
<MyPopover <MyPopover
placement={'right-start'} placement={'right-start'}
offset={[0, 0]} offset={[0, 0]}
hasArrow={false} hasArrow={true}
trigger={'hover'} trigger={'hover'}
flip={false} flip={false}
zIndex={2000} zIndex={2000}
......
...@@ -12,6 +12,7 @@ import MyPopover from '@fastgpt/web/components/common/MyPopover'; ...@@ -12,6 +12,7 @@ import MyPopover from '@fastgpt/web/components/common/MyPopover';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { WorkflowUtilsContext } from './context/workflowUtilsContext'; import { WorkflowUtilsContext } from './context/workflowUtilsContext';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const ImportSettings = dynamic(() => import('./Flow/ImportSettings')); const ImportSettings = dynamic(() => import('./Flow/ImportSettings'));
const ExportConfigPopover = dynamic( const ExportConfigPopover = dynamic(
...@@ -165,11 +166,15 @@ const AppCard = ({ showSaveStatus, isSaved }: { showSaveStatus: boolean; isSaved ...@@ -165,11 +166,15 @@ const AppCard = ({ showSaveStatus, isSaved }: { showSaveStatus: boolean; isSaved
const Render = useMemo(() => { const Render = useMemo(() => {
return ( return (
<HStack flex={1} justifyContent={'space-between'}> <HStack flex={1} justifyContent={'space-between'}>
<HStack> <HStack minW={0}>
<Avatar src={appDetail.avatar} w={'1.75rem'} borderRadius={'md'} /> <Avatar src={appDetail.avatar} w={'1.75rem'} borderRadius={'md'} flexShrink={0} />
<Box> <Box minW={0}>
<HStack spacing={1}> <HStack spacing={1} minW={0}>
<Box color={'myGray.900'}>{appDetail.name}</Box> <MyTooltip label={appDetail.name} showOnlyWhenOverflow>
<Box color={'myGray.900'} maxW={['45vw', '280px']} className="textEllipsis">
{appDetail.name}
</Box>
</MyTooltip>
</HStack> </HStack>
{showSaveStatus && ( {showSaveStatus && (
<Flex alignItems={'center'} fontSize={'mini'} lineHeight={1}> <Flex alignItems={'center'} fontSize={'mini'} lineHeight={1}>
...@@ -199,6 +204,7 @@ const AppCard = ({ showSaveStatus, isSaved }: { showSaveStatus: boolean; isSaved ...@@ -199,6 +204,7 @@ const AppCard = ({ showSaveStatus, isSaved }: { showSaveStatus: boolean; isSaved
icon={<MyIcon name={'common/select'} w={'18px'} color={'myGray.500'} />} icon={<MyIcon name={'common/select'} w={'18px'} color={'myGray.500'} />}
w={'34px'} w={'34px'}
h={'34px'} h={'34px'}
flexShrink={0}
bg={'white'} bg={'white'}
border={'1px solid'} border={'1px solid'}
borderColor={'myGray.250'} borderColor={'myGray.250'}
......
...@@ -48,8 +48,9 @@ const QuoteItem = ({ ...@@ -48,8 +48,9 @@ const QuoteItem = ({
> >
<Flex gap={2} alignItems={'center'} pb={'8px'}> <Flex gap={2} alignItems={'center'} pb={'8px'}>
<Box alignItems={'center'} fontSize={'10px'} fontWeight={500} display={'inline-flex'}> <Box alignItems={'center'} fontSize={'10px'} fontWeight={500} display={'inline-flex'}>
<Flex gap={1}> <Flex gap={1} minW={0}>
<MyIcon name={icon as any} flexShrink={0} w={'12px'} /> <MyIcon name={icon as any} flexShrink={0} w={'12px'} />
<MyTooltip label={sourceName} showOnlyWhenOverflow>
<Box <Box
className={'textEllipsis'} className={'textEllipsis'}
wordBreak={'break-all'} wordBreak={'break-all'}
...@@ -58,6 +59,7 @@ const QuoteItem = ({ ...@@ -58,6 +59,7 @@ const QuoteItem = ({
> >
{sourceName} {sourceName}
</Box> </Box>
</MyTooltip>
</Flex> </Flex>
</Box> </Box>
</Flex> </Flex>
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
import { getQuoteDataList } from '@/web/core/chat/record/api'; import { getQuoteDataList } from '@/web/core/chat/record/api';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs'; import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
type MobileQuoteTab = 'detail' | 'source'; type MobileQuoteTab = 'detail' | 'source';
...@@ -177,9 +178,11 @@ const QuoteReader = ({ ...@@ -177,9 +178,11 @@ const QuoteReader = ({
onClick={() => openCollectionQuote(item)} onClick={() => openCollectionQuote(item)}
> >
<MyIcon name={item.icon as any} flexShrink={0} w={'16px'} /> <MyIcon name={item.icon as any} flexShrink={0} w={'16px'} />
<MyTooltip label={item.sourceName} showOnlyWhenOverflow>
<Box className={'textEllipsis'} minW={0}> <Box className={'textEllipsis'} minW={0}>
{item.sourceName} {item.sourceName}
</Box> </Box>
</MyTooltip>
</Flex> </Flex>
))} ))}
</Flex> </Flex>
......
...@@ -165,17 +165,8 @@ const List = () => { ...@@ -165,17 +165,8 @@ const List = () => {
const isTool = ToolTypeList.includes(app.type); const isTool = ToolTypeList.includes(app.type);
const isFolder = AppFolderTypeList.includes(app.type); const isFolder = AppFolderTypeList.includes(app.type);
return ( return (
<MyTooltip
key={app._id}
label={
app.type === AppTypeEnum.folder
? t('common:open_folder')
: app.permission.hasWritePer || app.permission.hasReadChatLogPer
? t('app:edit_app')
: t('app:go_to_chat')
}
>
<MyBox <MyBox
key={app._id}
data-virtual-item="" data-virtual-item=""
py={4} py={4}
px={5} px={5}
...@@ -222,7 +213,9 @@ const List = () => { ...@@ -222,7 +213,9 @@ const List = () => {
<Grid templateColumns="auto 1fr auto" alignItems="center" width="100%" gap={2}> <Grid templateColumns="auto 1fr auto" alignItems="center" width="100%" gap={2}>
<Avatar src={app.avatar} borderRadius={'sm'} w={'1.5rem'} /> <Avatar src={app.avatar} borderRadius={'sm'} w={'1.5rem'} />
<Box color={'myGray.900'} fontWeight={'medium'} minWidth={0} overflow="hidden"> <Box color={'myGray.900'} fontWeight={'medium'} minWidth={0} overflow="hidden">
<MyTooltip label={app.name} showOnlyWhenOverflow>
<Box className={'textEllipsis'}>{app.name}</Box> <Box className={'textEllipsis'}>{app.name}</Box>
</MyTooltip>
</Box> </Box>
<Box justifySelf="end" mr={-5}> <Box justifySelf="end" mr={-5}>
<AppTypeTag type={app.type} /> <AppTypeTag type={app.type} />
...@@ -278,11 +271,9 @@ const List = () => { ...@@ -278,11 +271,9 @@ const List = () => {
/> />
} }
menuList={[ menuList={[
...([ ...([AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.chatAgent].includes(
AppTypeEnum.simple, app.type
AppTypeEnum.workflow, )
AppTypeEnum.chatAgent
].includes(app.type)
? [ ? [
{ {
children: [ children: [
...@@ -421,7 +412,6 @@ const List = () => { ...@@ -421,7 +412,6 @@ const List = () => {
</HStack> </HStack>
</HStack> </HStack>
</MyBox> </MyBox>
</MyTooltip>
); );
}; };
......
...@@ -198,7 +198,7 @@ const CollectionCard = () => { ...@@ -198,7 +198,7 @@ const CollectionCard = () => {
parentId: targetId parentId: targetId
}); });
getData(pageNum); getData(pageNum);
} catch (error) {} } catch {}
} }
}); });
...@@ -261,18 +261,29 @@ const CollectionCard = () => { ...@@ -261,18 +261,29 @@ const CollectionCard = () => {
}} }}
> >
<Td minW={'150px'} maxW={['200px', '300px']} draggable py={2}> <Td minW={'150px'} maxW={['200px', '300px']} draggable py={2}>
<HStack> <HStack minW={0}>
<HStack onClick={(e) => e.stopPropagation()}> <HStack onClick={(e) => e.stopPropagation()}>
<Checkbox <Checkbox
isChecked={isSelected(collection)} isChecked={isSelected(collection)}
onChange={(e) => toggleSelect(collection)} onChange={() => toggleSelect(collection)}
/> />
</HStack> </HStack>
<Box> <Box minW={0} flex={1}>
<Flex alignItems={'center'}> <Flex alignItems={'center'} minW={0}>
<MyIcon name={collection.icon as any} w={'1.25rem'} mr={2} /> <MyIcon
<MyTooltip label={t('common:click_drag_tip')} shouldWrapChildren={false}> name={collection.icon as any}
<Box color={'myGray.900'} fontWeight={'500'} className="textEllipsis"> w={'1.25rem'}
mr={2}
flexShrink={0}
/>
<MyTooltip label={collection.name} showOnlyWhenOverflow>
<Box
color={'myGray.900'}
fontWeight={'500'}
className="textEllipsis"
minW={0}
flex={'0 1 auto'}
>
{collection.name} {collection.name}
</Box> </Box>
</MyTooltip> </MyTooltip>
......
import React, { useCallback, useMemo } from 'react'; import React, { useCallback } from 'react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { Box, Flex, IconButton, useTheme, Progress } from '@chakra-ui/react'; import { Box, Flex, IconButton, useTheme } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
...@@ -8,6 +8,7 @@ import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContex ...@@ -8,6 +8,7 @@ import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContex
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import FolderPath from '@/components/common/folder/Path'; import FolderPath from '@/components/common/folder/Path';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
export enum TabEnum { export enum TabEnum {
dataCard = 'dataCard', dataCard = 'dataCard',
...@@ -23,10 +24,7 @@ const NavBar = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -23,10 +24,7 @@ const NavBar = ({ currentTab }: { currentTab: TabEnum }) => {
const router = useRouter(); const router = useRouter();
const query = router.query; const query = router.query;
const { isPc } = useSystem(); const { isPc } = useSystem();
const { datasetDetail, rebuildingCount, paths } = useContextSelector( const { datasetDetail, paths } = useContextSelector(DatasetPageContext, (v) => v);
DatasetPageContext,
(v) => v
);
const tabList = [ const tabList = [
{ {
...@@ -92,9 +90,17 @@ const NavBar = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -92,9 +90,17 @@ const NavBar = ({ currentTab }: { currentTab: TabEnum }) => {
aria-label={''} aria-label={''}
_hover={'none'} _hover={'none'}
/> />
<Box fontWeight={500} color={'myGray.600'} fontSize={'sm'}> <MyTooltip label={datasetDetail.name} showOnlyWhenOverflow>
<Box
fontWeight={500}
color={'myGray.600'}
fontSize={'sm'}
maxW={['45vw', '280px']}
className="textEllipsis"
>
{datasetDetail.name} {datasetDetail.name}
</Box> </Box>
</MyTooltip>
</Flex> </Flex>
</> </>
) : ( ) : (
......
...@@ -149,19 +149,8 @@ function List() { ...@@ -149,19 +149,8 @@ function List() {
const vectorModelAvatar = getModelProvider(dataset.vectorModel.provider)?.avatar; const vectorModelAvatar = getModelProvider(dataset.vectorModel.provider)?.avatar;
return ( return (
<MyTooltip
key={dataset._id}
label={
<Flex flexDirection={'column'} alignItems={'center'}>
<Box fontSize={'xs'} color={'myGray.500'}>
{dataset.type === DatasetTypeEnum.folder
? t('common:open_folder')
: t('common:folder.open_dataset')}
</Box>
</Flex>
}
>
<MyBox <MyBox
key={dataset._id}
data-virtual-item="" data-virtual-item=""
display={'flex'} display={'flex'}
flexDirection={'column'} flexDirection={'column'}
...@@ -217,7 +206,9 @@ function List() { ...@@ -217,7 +206,9 @@ function List() {
<Flex w="100%"> <Flex w="100%">
<Avatar src={dataset.avatar} borderRadius={6} w={'28px'} flexShrink={0} /> <Avatar src={dataset.avatar} borderRadius={6} w={'28px'} flexShrink={0} />
<Box width="0" flex="1" className="textEllipsis" color={'myGray.900'} ml={2}> <Box width="0" flex="1" className="textEllipsis" color={'myGray.900'} ml={2}>
{dataset.name} <MyTooltip label={dataset.name} showOnlyWhenOverflow>
<Box className="textEllipsis">{dataset.name}</Box>
</MyTooltip>
</Box> </Box>
{dataset.type !== DatasetTypeEnum.folder && ( {dataset.type !== DatasetTypeEnum.folder && (
...@@ -391,7 +382,6 @@ function List() { ...@@ -391,7 +382,6 @@ function List() {
</HStack> </HStack>
</Flex> </Flex>
</MyBox> </MyBox>
</MyTooltip>
); );
}; };
......
...@@ -17,6 +17,7 @@ import { useContextSelector } from 'use-context-selector'; ...@@ -17,6 +17,7 @@ import { useContextSelector } from 'use-context-selector';
import { DatasetPageContext } from '../context/datasetPageContext'; import { DatasetPageContext } from '../context/datasetPageContext';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type'; import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const SelectCollections = ({ const SelectCollections = ({
datasetId, datasetId,
...@@ -187,14 +188,16 @@ const SelectCollections = ({ ...@@ -187,14 +188,16 @@ const SelectCollections = ({
result = [...selectedDatasetCollectionIds, item._id]; result = [...selectedDatasetCollectionIds, item._id];
} }
setSelectedDatasetCollectionIds(result); setSelectedDatasetCollectionIds(result);
onChange && onChange({ parentId, collectionIds: result }); onChange?.({ parentId, collectionIds: result });
} }
}} }}
> >
<Flex alignItems={'center'} h={'38px'}> <Flex alignItems={'center'} h={'38px'} minW={0}>
<MyIcon name={item.icon as any} w={'18px'} /> <MyIcon name={item.icon as any} w={'18px'} flexShrink={0} />
<Box ml={3} fontSize={'sm'} className="textEllipsis"> <Box ml={3} fontSize={'sm'} minW={0} flex={1}>
{item.name} <MyTooltip label={item.name} showOnlyWhenOverflow>
<Box className="textEllipsis">{item.name}</Box>
</MyTooltip>
</Box> </Box>
</Flex> </Flex>
</Card> </Card>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment