perf: confirm ux (#4843)

* perf: delete tip ux

* perf: confirm ux
This commit is contained in:
Archer
2025-05-20 13:41:56 +08:00
committed by GitHub
parent 1dac2b70ec
commit d44c338059
18 changed files with 309 additions and 391 deletions

View File

@@ -15,6 +15,7 @@ weight: 790
## ⚙️ 优化
1. LLM stream调用默认超时调大。
2. 部分确认交互优化。
## 🐛 修复

View File

@@ -11,15 +11,16 @@ import {
HStack,
Box,
Button,
PopoverArrow
PopoverArrow,
Portal
} from '@chakra-ui/react';
const PopoverConfirm = ({
content,
showCancel,
showCancel = true,
type,
Trigger,
placement = 'bottom-start',
placement = 'auto',
offset,
onConfirm,
confirmText,
@@ -50,7 +51,7 @@ const PopoverConfirm = ({
};
if (type && map[type]) return map[type];
return map.info;
}, [type, t]);
}, [type]);
const firstFieldRef = React.useRef(null);
const { onOpen, onClose, isOpen } = useDisclosure();
@@ -67,7 +68,7 @@ const PopoverConfirm = ({
onClose={onClose}
placement={placement}
offset={offset}
closeOnBlur={false}
closeOnBlur={true}
trigger={'click'}
openDelay={100}
closeDelay={100}
@@ -75,6 +76,7 @@ const PopoverConfirm = ({
lazyBehavior="keepMounted"
arrowSize={10}
strategy={'fixed'}
computePositionOnMount={true}
>
<PopoverTrigger>{Trigger}</PopoverTrigger>
<PopoverContent p={4}>
@@ -82,15 +84,25 @@ const PopoverConfirm = ({
<HStack alignItems={'flex-start'} color={'myGray.700'}>
<MyIcon name={map.icon as any} w={'1.5rem'} />
<Box fontSize={'sm'}>{content}</Box>
<Box fontSize={'sm'} whiteSpace={'pre-wrap'}>
{content}
</Box>
</HStack>
<HStack mt={1} justifyContent={'flex-end'}>
<HStack mt={2} justifyContent={'flex-end'}>
{showCancel && (
<Button variant={'whiteBase'} size="sm" onClick={onClose}>
{cancelText || t('common:Cancel')}
</Button>
)}
<Button isLoading={loading} variant={map.variant} size="sm" onClick={onclickConfirm}>
<Button
isLoading={loading}
variant={map.variant}
size="sm"
onClick={async (e) => {
e.stopPropagation();
await onclickConfirm();
}}
>
{confirmText || t('common:Confirm')}
</Button>
</HStack>

View File

@@ -221,7 +221,8 @@ const Button = defineStyleConfig({
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
color: 'red.600',
borderColor: 'red.300'
borderColor: 'red.300',
bg: 'red.50'
},
_active: {
color: 'red.600'
@@ -281,11 +282,11 @@ const Button = defineStyleConfig({
bg: 'transparent',
transition: 'background 0.1s',
_hover: {
bg: 'myGray.150',
bg: 'red.50',
color: 'red.600'
},
_active: {
bg: 'myGray.150'
bg: 'red.50'
},
_disabled: {
color: 'myGray.800 !important'

View File

@@ -5,14 +5,13 @@ import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
import { useTranslation } from 'next-i18next';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
import CollaboratorContextProvider, {
type MemberManagerInputPropsType
} from '../../support/permission/MemberManager/context';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
const FolderSlideCard = ({
refreshDeps,
@@ -47,11 +46,6 @@ const FolderSlideCard = ({
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: deleteTip
});
return (
<Box w={'13rem'}>
<Box>
@@ -93,22 +87,26 @@ const FolderSlideCard = ({
{t('common:Move')}
</Button>
{managePer.permission.isOwner && (
<Button
variant={'transparentDanger'}
pl={1}
leftIcon={<MyIcon name={'delete'} w={'1rem'} />}
transform={'none !important'}
w={'100%'}
justifyContent={'flex-start'}
size={'sm'}
fontSize={'mini'}
mt={3}
onClick={() => {
openConfirm(onDelete)();
}}
>
{t('common:delete_folder')}
</Button>
<PopoverConfirm
Trigger={
<Button
variant={'transparentDanger'}
pl={1}
leftIcon={<MyIcon name={'delete'} w={'1rem'} />}
transform={'none !important'}
w={'100%'}
justifyContent={'flex-start'}
size={'sm'}
fontSize={'mini'}
mt={3}
>
{t('common:delete_folder')}
</Button>
}
type="delete"
content={deleteTip}
onConfirm={onDelete}
/>
)}
</Box>
</>
@@ -177,8 +175,6 @@ const FolderSlideCard = ({
</Box>
</>
)}
<ConfirmModal />
</Box>
);
};

View File

@@ -37,7 +37,7 @@ import HighlightText from '@fastgpt/web/components/common/String/HighlightText';
import { defaultChatInputGuideConfig } from '@fastgpt/global/core/app/constants';
import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
const csvTemplate = `"第一列内容"
"只会将第一列内容导入,其余列会被忽略"
@@ -191,10 +191,6 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
const [searchKey, setSearchKey] = useState('');
const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({
type: 'delete'
});
const {
scrollDataList,
setData,
@@ -355,24 +351,26 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
>
{t('common:Delete')}
</Button>
<Button
variant={'whiteBase'}
display={selectedRows.length !== 0 ? 'none' : 'flex'}
size={'sm'}
leftIcon={<MyIcon name={'delete'} boxSize={4} />}
onClick={() =>
openConfirmDel(
() => {
onDeleteAllData();
setSelectedRows([]);
},
undefined,
t('chat:delete_all_input_guide_confirm')
)()
<PopoverConfirm
Trigger={
<Button
variant={'whiteBase'}
display={selectedRows.length !== 0 ? 'none' : 'flex'}
size={'sm'}
leftIcon={<MyIcon name={'delete'} boxSize={4} />}
>
{t('chat:Delete_all')}
</Button>
}
>
{t('chat:Delete_all')}
</Button>
type="delete"
content={t('chat:delete_all_input_guide_confirm')}
onConfirm={() => {
onDeleteAllData();
setSelectedRows([]);
}}
/>
<Button
display={selectedRows.length !== 0 ? 'none' : 'flex'}
onClick={() => {
@@ -504,7 +502,6 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
})}
</ScrollList>
<DelConfirmModal />
<File onSelect={onSelectFile} />
</MyModal>
);

View File

@@ -41,7 +41,6 @@ import {
} from '@/web/core/ai/config';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { type SystemModelItemType } from '@fastgpt/service/core/ai/type';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
import { clientInitData } from '@/web/common/system/staticData';
@@ -54,6 +53,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import AIModelSelector from '@/components/Select/AIModelSelector';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
import { AddModelButton } from './AddModelBox';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
const ModelEditModal = dynamic(() => import('./AddModelBox').then((mod) => mod.ModelEditModal));
@@ -260,10 +260,6 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
onSuccess: refreshModels
});
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: t('account:model.delete_model_confirm')
});
const { runAsync: deleteModel } = useRequest2(deleteSystemModel, {
onSuccess: refreshModels
});
@@ -459,10 +455,15 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
onClick={() => onEditModel(item.model)}
/>
{item.isCustom && (
<MyIconButton
icon={'delete'}
hoverColor={'red.500'}
onClick={() => openConfirm(() => deleteModel({ model: item.model }))()}
<PopoverConfirm
Trigger={
<Box>
<MyIconButton icon={'delete'} hoverColor={'red.500'} />
</Box>
}
type="delete"
content={t('account:model.delete_model_confirm')}
onConfirm={() => deleteModel({ model: item.model })}
/>
)}
</HStack>
@@ -475,7 +476,6 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
</Flex>
</MyBox>
<ConfirmModal />
{!!editModelData && (
<ModelEditModal
modelData={editModelData}
@@ -510,9 +510,6 @@ const JsonConfigModal = ({
}
});
const { openConfirm, ConfirmModal } = useConfirm({
content: t('account:model.json_config_confirm')
});
const { runAsync } = useRequest2(putUpdateWithJson, {
onSuccess: () => {
onSuccess();
@@ -542,18 +539,14 @@ const JsonConfigModal = ({
<Button variant={'whiteBase'} mr={4} onClick={onClose}>
{t('common:Cancel')}
</Button>
<Button
onClick={() =>
openConfirm(() => {
return runAsync({ config: data });
})()
}
>
{t('common:Confirm')}
</Button>
</ModalFooter>
<ConfirmModal />
<PopoverConfirm
Trigger={<Button>{t('common:Confirm')}</Button>}
type="info"
content={t('account:model.json_config_confirm')}
onConfirm={() => runAsync({ config: data })}
/>
</ModalFooter>
</MyModal>
);
};

View File

@@ -16,7 +16,6 @@ import {
} from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import {
delRemoveMember,
getTeamMembers,
@@ -50,6 +49,8 @@ import { type PaginationResponse } from '@fastgpt/web/common/fetch/type';
import _ from 'lodash';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
const InviteModal = dynamic(() => import('./Invite/InviteModal'));
const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal'));
@@ -121,9 +122,6 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
errorToast: t('account_team:sync_member_failed')
});
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
content: t('account_team:confirm_leave_team')
});
const { runAsync: onLeaveTeam } = useRequest2(delLeaveTeam, {
onSuccess() {
const defaultTeam = myTeams[0];
@@ -132,19 +130,10 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
errorToast: t('account_team:user_team_leave_team_failed')
});
const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm({
type: 'delete'
});
const { runAsync: onRemoveMember } = useRequest2(delRemoveMember, {
onSuccess: onRefreshMembers
});
const { ConfirmModal: ConfirmRestoreMemberModal, openConfirm: openRestoreMember } = useConfirm({
type: 'common',
title: t('account_team:restore_tip_title'),
iconSrc: 'common/confirm/restoreTip',
iconColor: 'primary.500'
});
const { runAsync: onRestore } = useRequest2(postRestoreMember, {
onSuccess: onRefreshMembers,
successToast: t('common:Success'),
@@ -247,16 +236,22 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
</Button>
)}
{!userInfo?.team.permission.isOwner && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'support/account/loginoutLight'} w={'14px'} />}
onClick={() => openLeaveConfirm(onLeaveTeam)()}
>
{t('account_team:user_team_leave_team')}
</Button>
<PopoverConfirm
Trigger={
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'support/account/loginoutLight'} w={'14px'} />}
>
{t('account_team:user_team_leave_team')}
</Button>
}
type="delete"
content={t('account_team:confirm_leave_team')}
onConfirm={() => onLeaveTeam()}
/>
)}
</HStack>
</Flex>
@@ -317,62 +312,48 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
member.role !== TeamMemberRoleEnum.owner &&
member.tmbId !== userInfo?.team.tmbId &&
(member.status === TeamMemberStatusEnum.active ? (
<>
<Icon
mr={2}
name={'edit'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'blue.600',
bgColor: 'myGray.100'
}}
<HStack>
<MyIconButton
icon={'edit'}
size="1rem"
hoverColor={'blue.500'}
onClick={() => handleEditMemberName(member.tmbId, member.memberName)}
/>
<Icon
name={'common/trash'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'red.600',
bgColor: 'myGray.100'
}}
onClick={() => {
openRemoveMember(
() => onRemoveMember(member.tmbId),
undefined,
t('account_team:remove_tip', {
username: member.memberName
})
)();
}}
<PopoverConfirm
Trigger={
<Box>
<MyIconButton
icon={'common/trash'}
hoverColor={'red.500'}
hoverBg="red.50"
size={'1rem'}
/>
</Box>
}
type="delete"
content={t('account_team:remove_tip', {
username: member.memberName
})}
onConfirm={() => onRemoveMember(member.tmbId)}
/>
</>
</HStack>
) : (
member.status === TeamMemberStatusEnum.forbidden && (
<Icon
name={'common/confirm/restoreTip'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'primary.500',
bgColor: 'myGray.100'
}}
onClick={() => {
openRestoreMember(
() => onRestore(member.tmbId),
undefined,
t('account_team:restore_tip', {
username: member.memberName
})
)();
}}
<PopoverConfirm
Trigger={
<Box display={'inline-block'}>
<MyIconButton
icon={'common/confirm/restoreTip'}
size={'1rem'}
hoverColor={'primary.500'}
/>
</Box>
}
type="info"
content={t('account_team:restore_tip', {
username: member.memberName
})}
onConfirm={() => onRestore(member.tmbId)}
/>
)
))}
@@ -381,14 +362,11 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
))}
</Tbody>
</Table>
<ConfirmRemoveMemberModal />
<ConfirmRestoreMemberModal />
<EditMemberNameModal />
</TableContainer>
</MemberScrollData>
</MyBox>
<ConfirmLeaveTeamModal />
{isOpenInvite && userInfo?.team?.teamId && <InviteModal onClose={onCloseInvite} />}
{isOpenTeamTagsAsync && <TeamTagModal onClose={onCloseTeamTagsAsync} />}
</>

View File

@@ -5,7 +5,6 @@ import type { IconNameType } from '@fastgpt/web/components/common/Icon/type';
function IconButton({
name,
w = '1rem',
h = '1rem',
...props
}: {
name: IconNameType;
@@ -14,7 +13,7 @@ function IconButton({
<MyIcon
name={name}
w={w}
h={h}
h={w}
transition={'background 0.1s'}
cursor={'pointer'}
p="1"

View File

@@ -23,6 +23,7 @@ import {
SNADBOX_CODE_TEMPLATE
} from '@fastgpt/global/core/workflow/template/system/sandbox/constants';
import MySelect from '@fastgpt/web/components/common/MySelect';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation();
@@ -35,11 +36,6 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
const onChangeNode = useContextSelector(WorkflowContext, (ctx) => ctx.onChangeNode);
// 重置模板确认
const { ConfirmModal: ResetTemplateConfirm, openConfirm: openResetTemplateConfirm } = useConfirm({
content: t('workflow:code.Reset template confirm')
});
// 切换语言确认
const { ConfirmModal: SwitchLangConfirm, openConfirm: openSwitchLangConfirm } = useConfirm({
content: t('workflow:code.Switch language confirm')
@@ -84,13 +80,16 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
{codeType.value === 'py' && (
<QuestionTip ml={2} label={t('workflow:support_code_language')} />
)}
<Box
cursor={'pointer'}
color={'primary.500'}
fontSize={'xs'}
ml="auto"
mr={2}
onClick={openResetTemplateConfirm(() => {
<PopoverConfirm
Trigger={
<Box cursor={'pointer'} color={'primary.500'} fontSize={'xs'} ml="auto" mr={2}>
{t('workflow:code.Reset template')}
</Box>
}
showCancel
content={t('workflow:code.Reset template confirm')}
placement={'top-end'}
onConfirm={() =>
onChangeNode({
nodeId,
type: 'updateInput',
@@ -99,11 +98,9 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
...item,
value: codeType.value === 'js' ? JS_TEMPLATE : PY_TEMPLATE
}
});
})}
>
{t('workflow:code.Reset template')}
</Box>
})
}
/>
</Flex>
<CodeEditor
bg={'white'}
@@ -123,7 +120,7 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
);
}
};
}, [codeType, nodeId, onChangeNode, openResetTemplateConfirm, openSwitchLangConfirm, t]);
}, [codeType, nodeId, onChangeNode, openSwitchLangConfirm, t]);
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
@@ -146,7 +143,6 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
<IOTitle text={t('common:Output')} />
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
</Container>
<ResetTemplateConfirm />
<SwitchLangConfirm />
</NodeCard>
);

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { type NodeProps } from 'reactflow';
import NodeCard from '../render/NodeCard';
import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
@@ -19,9 +19,10 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import ValueTypeLabel from '../render/ValueTypeLabel';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import PluginOutputEditModal, { defaultOutput } from './PluginOutputEditModal';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
const customOutputConfig = {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
@@ -102,10 +103,7 @@ function Reference({
input: FlowNodeInputItemType;
}) {
const { t } = useTranslation();
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: t('workflow:confirm_delete_field_tip')
});
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const [editField, setEditField] = useState<FlowNodeInputItemType>();
@@ -161,25 +159,32 @@ function Reference({
{/* value */}
<ValueTypeLabel valueType={input.valueType} valueDesc={input.valueDesc} />
<MyIcon
name={'common/settingLight'}
w={'14px'}
cursor={'pointer'}
ml={3}
<MyIconButton
icon={'common/settingLight'}
size={'14px'}
ml={2}
color={'myGray.600'}
_hover={{ color: 'primary.500' }}
hoverBg="primary.50"
hoverColor="primary.500"
onClick={() => setEditField(input)}
/>
<MyIcon
className="delete"
name={'delete'}
w={'14px'}
color={'myGray.500'}
cursor={'pointer'}
ml={2}
_hover={{ color: 'red.600' }}
onClick={openConfirm(onDel)}
<PopoverConfirm
Trigger={
<Box ml={1}>
<MyIconButton
icon="delete"
color={'myGray.600'}
hoverBg="red.50"
size={'14px'}
hoverColor="red.600"
/>
</Box>
}
placement={'bottom'}
type={'delete'}
content={t('workflow:confirm_delete_field_tip')}
onConfirm={onDel}
/>
</Flex>
<MyTooltip label={t('workflow:plugin_output_tool')}>
@@ -215,7 +220,6 @@ function Reference({
onSubmit={onUpdateField}
/>
)}
<ConfirmModal />
</>
);
}

View File

@@ -2,7 +2,6 @@ import React, { useCallback, useMemo, useRef } from 'react';
import { Box, Button, Card, Flex } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../../context';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
@@ -19,6 +18,8 @@ import {
} from '@fastgpt/global/core/workflow/runtime/utils';
import { type ChatItemType, type UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
type NodeDebugResponseProps = {
nodeId: string;
@@ -97,13 +98,6 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
const response = debugResult?.response;
const { openConfirm, ConfirmModal } = useConfirm({
content: t('common:core.workflow.Confirm stop debug')
});
const onStop = () => {
openConfirm(onStopNodeDebug)();
};
const interactive = debugResult?.workflowInteractiveResponse;
const onNextInteractive = useCallback(
(userContent: string) => {
@@ -196,14 +190,20 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
{t('common:core.workflow.debug.Run result')}
</Box>
{workflowDebugData?.nextRunNodes.length !== 0 && (
<Button
size={'sm'}
leftIcon={<MyIcon name={'core/chat/stopSpeech'} w={'16px'} />}
variant={'whiteDanger'}
onClick={onStop}
>
{t('common:core.workflow.Stop debug')}
</Button>
<PopoverConfirm
Trigger={
<Button
size={'sm'}
leftIcon={<MyIcon name={'core/chat/stopSpeech'} w={'16px'} />}
variant={'whiteDanger'}
>
{t('common:core.workflow.Stop debug')}
</Button>
}
placement={'top'}
content={t('common:core.workflow.Confirm stop debug')}
onConfirm={onStopNodeDebug}
/>
)}
{!interactive && (
<>
@@ -266,7 +266,6 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
)}
</Card>
)}
<ConfirmModal />
</>
) : null;
};

View File

@@ -16,8 +16,8 @@ import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/ut
import { ReferSelector, useReference } from '../Reference';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import ValueTypeLabel from '../../../ValueTypeLabel';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
const FieldEditModal = dynamic(() => import('../../FieldEditModal'));
@@ -95,7 +95,7 @@ const DynamicInputs = (props: RenderInputProps) => {
)}
</Box>
);
}, [editField, dynamicInputs, item, keys, onAddField, props, t, t]);
}, [editField, dynamicInputs, item, keys, onAddField, props, t]);
return Render;
};
@@ -110,10 +110,7 @@ function Reference({
}) {
const { nodeId, inputs = [], item } = props;
const { t } = useTranslation();
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: t('workflow:confirm_delete_field_tip')
});
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const keys = useMemo(() => {
@@ -187,25 +184,31 @@ function Reference({
{/* value */}
<ValueTypeLabel valueType={inputChildren.valueType} valueDesc={inputChildren.valueDesc} />
<MyIcon
name={'common/settingLight'}
w={'14px'}
cursor={'pointer'}
ml={3}
<MyIconButton
icon="common/settingLight"
ml={2}
color={'myGray.600'}
_hover={{ color: 'primary.500' }}
hoverBg="primary.50"
hoverColor="primary.500"
size={'14px'}
onClick={() => setEditField(inputChildren)}
/>
<MyIcon
className={'delete'}
name={'delete'}
w={'14px'}
color={'myGray.500'}
cursor={'pointer'}
ml={2}
_hover={{ color: 'red.600' }}
onClick={openConfirm(onDel)}
<PopoverConfirm
Trigger={
<Box ml={1}>
<MyIconButton
icon="delete"
color={'myGray.600'}
hoverBg="red.50"
hoverColor="red.600"
size={'14px'}
/>
</Box>
}
type={'delete'}
content={t('workflow:confirm_delete_field_tip')}
onConfirm={onDel}
/>
</Flex>
<ReferSelector
@@ -225,7 +228,6 @@ function Reference({
onSubmit={onUpdateField}
/>
)}
<ConfirmModal />
</>
);
}

View File

@@ -7,7 +7,6 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useUserStore } from '@/web/support/user/useUserStore';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useContextSelector } from 'use-context-selector';
@@ -16,6 +15,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
type HistoryItemType = {
id: string;
@@ -73,9 +73,6 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
title: t('common:core.chat.Custom History Title'),
placeholder: t('common:core.chat.Custom History Title Description')
});
const { openConfirm, ConfirmModal } = useConfirm({
content: confirmClearText
});
const canRouteToDetail = useMemo(
() => appId && userInfo?.team.permission.hasWritePer && showRouteToAppDetail,
@@ -153,19 +150,21 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
</Button>
{/* Clear */}
{isPc && histories.length > 0 && (
<IconButton
ml={3}
h={'100%'}
variant={'whiteDanger'}
size={'mdSquare'}
aria-label={''}
borderRadius={'50%'}
icon={<MyIcon name={'common/clearLight'} w={'16px'} />}
onClick={() =>
openConfirm(() => {
onClearHistory();
})()
<PopoverConfirm
Trigger={
<Box ml={3} h={'100%'}>
<IconButton
variant={'whiteDanger'}
size={'mdSquare'}
aria-label={''}
borderRadius={'50%'}
icon={<MyIcon name={'common/clearLight'} w={'16px'} />}
/>
</Box>
}
type="delete"
content={confirmClearText}
onConfirm={() => onClearHistory()}
/>
)}
</Flex>
@@ -314,7 +313,6 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
</Flex>
)}
<EditTitleModal />
<ConfirmModal />
</MyBox>
);
};

View File

@@ -3,7 +3,7 @@ import { type Dispatch, type ReactNode, type SetStateAction, useState } from 're
import { useTranslation } from 'next-i18next';
import { createContext, useContextSelector } from 'use-context-selector';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useDisclosure } from '@chakra-ui/react';
import { checkTeamWebSyncLimit } from '@/web/support/user/team/api';
import { getDatasetCollections, postWebsiteSync } from '@/web/core/dataset/api';

View File

@@ -3,22 +3,12 @@ import { useTranslation } from 'next-i18next';
import { strIsLink } from '@fastgpt/global/common/string/tools';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { getDocPath } from '@/web/common/system/doc';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useMyStep } from '@fastgpt/web/hooks/useStep';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
import React, { useRef } from 'react';
import {
Box,
Link,
Input,
Button,
ModalBody,
ModalFooter,
Textarea,
Stack
} from '@chakra-ui/react';
import React from 'react';
import { Box, Link, Input, Button, ModalBody, ModalFooter, Stack } from '@chakra-ui/react';
import {
DataChunkSplitModeEnum,
DatasetCollectionDataProcessModeEnum
@@ -33,6 +23,7 @@ import CollectionChunkForm, {
} from '../Form/CollectionChunkForm';
import { getLLMDefaultChunkSize } from '@fastgpt/global/core/dataset/training/utils';
import { type ChunkSettingsType } from '@fastgpt/global/core/dataset/type';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
export type WebsiteConfigFormType = {
websiteConfig: {
@@ -78,10 +69,6 @@ const WebsiteConfigModal = ({
const isEdit = !!websiteConfig?.url;
const { ConfirmModal, openConfirm } = useConfirm({
type: 'common'
});
const { activeStep, goToPrevious, goToNext, MyStep } = useMyStep({
defaultStep: 0,
steps
@@ -186,73 +173,31 @@ const WebsiteConfigModal = ({
<Button variant={'whiteBase'} onClick={goToPrevious}>
{t('common:last_step')}
</Button>
<Button
ml={2}
onClick={form.handleSubmit((data) => {
openConfirm(
() =>
onSuccess({
websiteConfig: websiteInfoGetValues(),
chunkSettings: collectionChunkForm2StoreChunkData({
...data,
agentModel: datasetDetail.agentModel,
vectorModel: datasetDetail.vectorModel
})
}),
undefined,
isEdit
? t('common:core.dataset.website.Confirm Update Tips')
: t('common:core.dataset.website.Confirm Create Tips')
)();
})}
>
{t('common:core.dataset.website.Start Sync')}
</Button>
<PopoverConfirm
Trigger={<Button ml={2}>{t('common:core.dataset.website.Start Sync')}</Button>}
content={
isEdit
? t('common:core.dataset.website.Confirm Update Tips')
: t('common:core.dataset.website.Confirm Create Tips')
}
onConfirm={() =>
form.handleSubmit((data) =>
onSuccess({
websiteConfig: websiteInfoGetValues(),
chunkSettings: collectionChunkForm2StoreChunkData({
...data,
agentModel: datasetDetail.agentModel,
vectorModel: datasetDetail.vectorModel
})
})
)()
}
/>
</>
)}
</ModalFooter>
<ConfirmModal />
</MyModal>
);
};
export default WebsiteConfigModal;
const PromptTextarea = ({
defaultValue,
onChange,
onClose
}: {
defaultValue: string;
onChange: (e: string) => void;
onClose: () => void;
}) => {
const ref = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation();
return (
<MyModal
isOpen
title={t('common:core.dataset.import.Custom prompt')}
iconSrc="modal/edit"
w={'600px'}
onClose={onClose}
>
<ModalBody whiteSpace={'pre-wrap'} fontSize={'sm'} px={[3, 6]} pt={[3, 6]}>
<Textarea ref={ref} rows={8} fontSize={'sm'} defaultValue={defaultValue} />
<Box>{Prompt_AgentQA.fixedText}</Box>
</ModalBody>
<ModalFooter>
<Button
onClick={() => {
const val = ref.current?.value || Prompt_AgentQA.description;
onChange(val);
onClose();
}}
>
{t('common:Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};

View File

@@ -7,7 +7,6 @@ import {
} from '@/web/core/dataset/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -32,6 +31,7 @@ import { ImportDataSourceEnum } from '@fastgpt/global/core/dataset/constants';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import TrainingStates from './CollectionCard/TrainingStates';
import { getTextValidLength } from '@fastgpt/global/common/string/utils';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
const DataCard = () => {
const theme = useTheme();
@@ -90,28 +90,22 @@ const DataCard = () => {
const canWrite = useMemo(() => datasetDetail.permission.hasWritePer, [datasetDetail]);
const { openConfirm, ConfirmModal } = useConfirm({
content: t('common:dataset.Confirm to delete the data'),
type: 'delete'
});
const onDeleteOneData = useMemoizedFn((dataId: string) => {
openConfirm(async () => {
try {
await delOneDatasetDataById(dataId);
setDatasetDataList((prev) => {
return prev.filter((data) => data._id !== dataId);
});
toast({
title: t('common:delete_success'),
status: 'success'
});
} catch (error) {
toast({
title: getErrText(error),
status: 'error'
});
}
})();
const onDeleteOneData = useMemoizedFn(async (dataId: string) => {
try {
await delOneDatasetDataById(dataId);
setDatasetDataList((prev) => {
return prev.filter((data) => data._id !== dataId);
});
toast({
title: t('common:delete_success'),
status: 'success'
});
} catch (error) {
toast({
title: getErrText(error),
status: 'error'
});
}
});
return (
@@ -333,18 +327,24 @@ const DataCard = () => {
{getTextValidLength(item.q + item.a || '')}
</Flex>
{canWrite && (
<IconButton
display={'flex'}
p={1}
boxShadow={'1'}
icon={<MyIcon name={'common/trash'} w={'14px'} color={'myGray.600'} />}
variant={'whiteDanger'}
size={'xsSquare'}
onClick={(e) => {
e.stopPropagation();
onDeleteOneData(item._id);
}}
aria-label={''}
<PopoverConfirm
Trigger={
<IconButton
display={'flex'}
p={1}
boxShadow={'1'}
icon={<MyIcon name={'common/trash'} w={'14px'} />}
variant={'whiteDanger'}
size={'xsSquare'}
aria-label={''}
onClick={(e) => {
e.stopPropagation();
}}
/>
}
content={t('common:dataset.Confirm to delete the data')}
type="delete"
onConfirm={() => onDeleteOneData(item._id)}
/>
)}
</Flex>
@@ -386,7 +386,6 @@ const DataCard = () => {
onClose={() => setErrorModalId('')}
/>
)}
<ConfirmModal />
</MyBox>
);
};

View File

@@ -55,10 +55,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
const vllmModelList = useMemo(() => getVlmModelList(), [getVlmModelList]);
const vlmModel = watch('vlmModel');
const { ConfirmModal: ConfirmDelModal } = useConfirm({
content: t('common:core.dataset.Delete Confirm'),
type: 'delete'
});
const { openConfirm: onOpenConfirmRebuild, ConfirmModal: ConfirmRebuildModal } = useConfirm({
title: t('common:action_confirm'),
content: t('dataset:confirm_to_rebuild_embedding_tip'),
@@ -414,7 +410,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
</>
)}
<ConfirmDelModal />
<ConfirmRebuildModal countDown={10} />
<ConfirmSyncScheduleModal />
{editedDataset && (

View File

@@ -25,12 +25,11 @@ import EditMcpModal, {
} from '@/pageComponents/dashboard/mcp/EditModal';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import dynamic from 'next/dynamic';
import { type McpKeyType } from '@fastgpt/global/support/mcp/type';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useUserStore } from '@/web/support/user/useUserStore';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
const UsageWay = dynamic(() => import('@/pageComponents/dashboard/mcp/usageWay'), {
ssr: false
@@ -52,10 +51,6 @@ const McpServer = () => {
const [editMcp, setEditMcp] = useState<EditMcForm>();
const [usageWay, setUsageWay] = useState<McpKeyType>();
const { openConfirm: openDelConfirm, ConfirmModal: DelConfirmModal } = useConfirm({
type: 'delete',
content: t('dashboard_mcp:delete_mcp_server_confirm_tip')
});
const { runAsync: onDeleteMcpServer } = useRequest2(deleteMcpServer, {
manual: true,
onSuccess: () => {
@@ -146,10 +141,19 @@ const McpServer = () => {
}
/>
<MyIconButton
icon="delete"
hoverColor={'red.600'}
onClick={() => openDelConfirm(() => onDeleteMcpServer(mcp._id))()}
<PopoverConfirm
Trigger={
<Box>
<MyIconButton
icon="delete"
hoverBg="red.50"
hoverColor={'red.600'}
/>
</Box>
}
type="delete"
content={t('dashboard_mcp:delete_mcp_server_confirm_tip')}
onConfirm={() => onDeleteMcpServer(mcp._id)}
/>
</HStack>
</Td>
@@ -164,7 +168,6 @@ const McpServer = () => {
)}
</DashboardContainer>
<DelConfirmModal />
{!!usageWay && <UsageWay mcp={usageWay} onClose={() => setUsageWay(undefined)} />}
{!!editMcp && (
<EditMcpModal