Commit 5c1d1bd2 by Archer Committed by GitHub

Update admin modal migration (#7466)

* update admin modal dependencies

* fix: workflow tool ui

* fix: response data
parent 5a048432
...@@ -57,8 +57,17 @@ export const dispatchPluginInput = async ( ...@@ -57,8 +57,17 @@ export const dispatchPluginInput = async (
const val = params[key]; const val = params[key];
const maxFiles = fileInputMaxFiles.get(key); const maxFiles = fileInputMaxFiles.get(key);
if (maxFiles !== undefined && Array.isArray(val)) { if (maxFiles !== undefined && Array.isArray(val)) {
// 文件选择器未选文件时可能提交无 key/url 的占位项,应按空输入处理。
const fileItems = val
.filter((fileItem) => {
if (typeof fileItem === 'string') return fileItem.trim().length > 0;
return (
!!fileItem && typeof fileItem === 'object' && Boolean(fileItem.key || fileItem.url)
);
})
.slice(0, maxFiles);
const fileUrls = await Promise.all( const fileUrls = await Promise.all(
val.slice(0, maxFiles).map(async (fileItem) => { fileItems.map(async (fileItem) => {
const storeValue = const storeValue =
typeof fileItem === 'string' typeof fileItem === 'string'
? normalizeChatFileStoreValue({ url: fileItem }) ? normalizeChatFileStoreValue({ url: fileItem })
......
...@@ -204,6 +204,21 @@ describe('dispatchPluginInput', () => { ...@@ -204,6 +204,21 @@ describe('dispatchPluginInput', () => {
expect(result.data?.upload).toEqual(['https://external.example.com/1.pdf']); expect(result.data?.upload).toEqual(['https://external.example.com/1.pdf']);
}); });
it('treats empty file selector placeholders as no file input', async () => {
const result = await runWithMockFileContext(() =>
dispatchPluginInput({
params: {
upload: [{ type: ChatFileTypeEnum.file }]
},
query: [],
node: pluginInputNode
} as any)
);
expect(result.data?.upload).toEqual([]);
expect(mockRegisterInputFile).not.toHaveBeenCalled();
});
it('reuses a file already selected in the current child context', async () => { it('reuses a file already selected in the current child context', async () => {
mockResolveInputFile.mockReturnValueOnce({ mockResolveInputFile.mockReturnValueOnce({
modelUrl: 'https://parent.example.com/signed.pdf' modelUrl: 'https://parent.example.com/signed.pdf'
......
Subproject commit c4367e0522a30b03d2d38271e954cb1e6161470d Subproject commit 18869dd92cd3f0559918e9234f39e415114ae402
...@@ -131,7 +131,7 @@ const RenderInput = () => { ...@@ -131,7 +131,7 @@ const RenderInput = () => {
const isDisabledInput = !!hasHistory; const isDisabledInput = !!hasHistory;
return ( return (
<Box> <>
{/* instruction */} {/* instruction */}
{instruction && ( {instruction && (
<Box <Box
...@@ -273,7 +273,7 @@ const RenderInput = () => { ...@@ -273,7 +273,7 @@ const RenderInput = () => {
</Button> </Button>
</Flex> </Flex>
)} )}
</Box> </>
); );
}; };
......
import { ResponseBox } from '../../../components/WholeResponseModal'; import { WorkflowToolResponseBox } from '../../../components/WholeResponseModal/WorkflowToolResponseBox';
import React, { useMemo } from 'react'; import React from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { PluginRunContext } from '../context'; import { PluginRunContext } from '../context';
import { Box } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext'; import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
...@@ -14,22 +13,20 @@ const RenderResponseDetail = () => { ...@@ -14,22 +13,20 @@ const RenderResponseDetail = () => {
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const isChatting = useContextSelector(PluginRunContext, (v) => v.isChatting); const isChatting = useContextSelector(PluginRunContext, (v) => v.isChatting);
const aiRecord = useMemo( // 流式响应可能更新记录对象本身,直接读取最新的 AI 记录,避免按数组引用缓存旧结果。
() => [...chatRecords].reverse().find((item) => item.obj === ChatRoleEnum.AI), const aiRecord = [...chatRecords].reverse().find((item) => item.obj === ChatRoleEnum.AI);
[chatRecords] const responseData = aiRecord?.responseData ?? [];
);
const responseData = aiRecord?.responseData || [];
return isChatting ? ( return isChatting ? (
<>{t('chat:in_progress')}</> <>{t('chat:in_progress')}</>
) : ( ) : (
<Box flex={'1 0 0'} h={'100%'} overflow={'auto'}> <>
{responseData.length > 0 ? ( {responseData.length > 0 ? (
<ResponseBox useMobile={true} response={responseData} dataId={aiRecord?.dataId} /> <WorkflowToolResponseBox response={responseData} dataId={aiRecord?.dataId} />
) : ( ) : (
<EmptyTip text={t('chat:response.no_workflow_response')} /> <EmptyTip text={t('chat:response.no_workflow_response')} />
)} )}
</Box> </>
); );
}; };
......
import React, { type ReactNode, useCallback, useMemo, useRef } from 'react'; import React, { type ReactNode, useCallback, useRef } from 'react';
import { createContext, useContextSelector } from 'use-context-selector'; import { createContext, useContextSelector } from 'use-context-selector';
import { type PluginRunBoxProps } from './type'; import { type PluginRunBoxProps } from './type';
import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
...@@ -150,12 +150,8 @@ const PluginRunContextProvider = ({ ...@@ -150,12 +150,8 @@ const PluginRunContextProvider = ({
[setChatRecords, resetVariables] [setChatRecords, resetVariables]
); );
const isChatting = useMemo( const isChatting =
() => chatRecords[chatRecords.length - 1] && chatRecords[chatRecords.length - 1]?.status !== 'finish';
chatRecords[chatRecords.length - 1] &&
chatRecords[chatRecords.length - 1]?.status !== 'finish',
[chatRecords]
);
const onSubmit = useCallback( const onSubmit = useCallback(
async ({ variables }: ChatBoxInputFormType) => { async ({ variables }: ChatBoxInputFormType) => {
...@@ -243,7 +239,8 @@ const PluginRunContextProvider = ({ ...@@ -243,7 +239,8 @@ const PluginRunContextProvider = ({
}) })
); );
} catch (err: any) { } catch (err: any) {
toast({ title: err.message, status: 'error' }); const errorMsg = t(getErrText(err, t('common:core.chat.error.Chat error') as any));
toast({ title: errorMsg, status: 'error' });
setChatRecords((state) => setChatRecords((state) =>
state.map((item, index) => { state.map((item, index) => {
if (index !== state.length - 1) return item; if (index !== state.length - 1) return item;
......
...@@ -7,7 +7,6 @@ import { useContextSelector } from 'use-context-selector'; ...@@ -7,7 +7,6 @@ import { useContextSelector } from 'use-context-selector';
import RenderOutput from './components/RenderOutput'; import RenderOutput from './components/RenderOutput';
import RenderResponseDetail from './components/RenderResponseDetail'; import RenderResponseDetail from './components/RenderResponseDetail';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { Box } from '@chakra-ui/react';
const PluginRunBox = (props: PluginRunBoxProps) => { const PluginRunBox = (props: PluginRunBoxProps) => {
const tab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab); const tab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
...@@ -15,11 +14,11 @@ const PluginRunBox = (props: PluginRunBoxProps) => { ...@@ -15,11 +14,11 @@ const PluginRunBox = (props: PluginRunBoxProps) => {
return ( return (
<PluginRunContextProvider {...props}> <PluginRunContextProvider {...props}>
<Box h={'100%'} minH={0} display={'flex'} flexDirection={'column'}> <>
{formatTab === PluginRunBoxTabEnum.input && <RenderInput />} {formatTab === PluginRunBoxTabEnum.input && <RenderInput />}
{formatTab === PluginRunBoxTabEnum.output && <RenderOutput />} {formatTab === PluginRunBoxTabEnum.output && <RenderOutput />}
{formatTab === PluginRunBoxTabEnum.detail && <RenderResponseDetail />} {formatTab === PluginRunBoxTabEnum.detail && <RenderResponseDetail />}
</Box> </>
</PluginRunContextProvider> </PluginRunContextProvider>
); );
}; };
......
...@@ -20,13 +20,11 @@ const sideTabDeepTreeMinDepth = 4; ...@@ -20,13 +20,11 @@ const sideTabDeepTreeMinDepth = 4;
export const ResponseBox = React.memo(function ResponseBox({ export const ResponseBox = React.memo(function ResponseBox({
response, response,
dataId, dataId,
hideTabs = false, hideTabs = false
useMobile = false
}: { }: {
response: ChatHistoryItemResType[]; response: ChatHistoryItemResType[];
dataId?: string; dataId?: string;
hideTabs?: boolean; hideTabs?: boolean;
useMobile?: boolean;
}) { }) {
const { t } = useSafeTranslation(); const { t } = useSafeTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
...@@ -79,7 +77,7 @@ export const ResponseBox = React.memo(function ResponseBox({ ...@@ -79,7 +77,7 @@ export const ResponseBox = React.memo(function ResponseBox({
return ( return (
<> <>
{isPc && !useMobile ? ( {isPc ? (
<Flex <Flex
overflow={'hidden'} overflow={'hidden'}
height={'100%'} height={'100%'}
......
...@@ -32,7 +32,9 @@ export const WholeResponseContent = ({ ...@@ -32,7 +32,9 @@ export const WholeResponseContent = ({
minH={0} minH={0}
ref={contentRef} ref={contentRef}
py={3} py={3}
px={hideTabs ? 4 : 3} // 详情页移动端需要让内容贴合滚动容器,水平留白由外层面板负责。
// 桌面端保留原有留白,避免改变完整结果的布局。
px={hideTabs ? [0, 4] : 3}
display={'flex'} display={'flex'}
flexDirection={'column'} flexDirection={'column'}
gap={3} gap={3}
......
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Box, Flex, useDisclosure } from '@chakra-ui/react';
import dynamic from 'next/dynamic';
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation';
import { WholeResponseSideTab } from './SideTab';
import { WholeResponseContent } from './WholeResponseContent';
import { flattenResponse, getSideTabItems } from './responseData';
const RequestIdDetailModal = dynamic(() => import('@/components/core/ai/requestId'));
/**
* 工作流工具专用完整结果:列表沿用工具面板的父级滚动,详情覆盖整个结果区域。
*/
export const WorkflowToolResponseBox = React.memo(function WorkflowToolResponseBox({
response,
dataId
}: {
response: ChatHistoryItemResType[];
dataId?: string;
}) {
const { t } = useSafeTranslation();
const rootRef = useRef<HTMLDivElement>(null);
const [selectedRequestId, setSelectedRequestId] = useState<string>();
// 流式运行期间 response 可能原地追加内容,不能按数组引用缓存计算结果。
const flattedResponse = flattenResponse(response);
const [currentNodeId, setCurrentNodeId] = useState(
flattedResponse[0]?.id ?? flattedResponse[0]?.nodeId ?? ''
);
const firstNodeId = flattedResponse[0]?.id ?? flattedResponse[0]?.nodeId ?? '';
const hasCurrentNode = flattedResponse.some((item) => item.id === currentNodeId);
useEffect(() => {
if (!firstNodeId) {
setCurrentNodeId('');
return;
}
if (hasCurrentNode) return;
setCurrentNodeId(firstNodeId);
}, [currentNodeId, firstNodeId, hasCurrentNode]);
const activeModule = (flattedResponse.find((item) => item.id === currentNodeId) ||
flattedResponse[0]) as ChatHistoryItemResType;
// 流式响应会暂时按 parentId 挂成树,工具面板需要和历史结果保持一致,逐节点展示完整执行链路。
const sliderResponseList = getSideTabItems(flattedResponse);
const { isOpen: isOpenDetail, onOpen: onOpenDetail, onClose: onCloseDetail } = useDisclosure();
const handleOpenRequestIdDetail = useCallback((requestId: string) => {
setSelectedRequestId(requestId);
}, []);
return (
<Box ref={rootRef} minH={'100%'} h={'100%'}>
{isOpenDetail ? (
<Flex bg={'white'} flexDirection={'column'} h={'100%'} minH={0}>
<Flex
align={'center'}
justifyContent={'center'}
px={2}
py={2}
borderBottom={'sm'}
position={'relative'}
height={'40px'}
flexShrink={0}
>
<MyIcon
width={4}
height={4}
name="common/backLight"
onClick={(e) => {
e.stopPropagation();
onCloseDetail();
}}
position={'absolute'}
left={2}
top={'50%'}
transform={'translateY(-50%)'}
cursor={'pointer'}
_hover={{ color: 'primary.500' }}
/>
<Avatar
src={
activeModule.moduleLogo ||
moduleTemplatesFlat.find(
(template) => activeModule.moduleType === template.flowNodeType
)?.avatar
}
w={'1.25rem'}
h={'1.25rem'}
borderRadius={'sm'}
/>
<Box ml={1.5} lineHeight={'1.25rem'} alignItems={'center'}>
{t(activeModule.moduleName as any, activeModule.moduleNameArgs)}
</Box>
</Flex>
<Box flex={'1 1 0'} minH={0} overflowY={'auto'}>
<WholeResponseContent
dataId={dataId}
activeModule={activeModule}
hideTabs={true}
onOpenRequestIdDetail={handleOpenRequestIdDetail}
/>
</Box>
</Flex>
) : (
<WholeResponseSideTab
response={sliderResponseList}
value={currentNodeId}
onChange={(item: string) => {
setCurrentNodeId(item);
rootRef.current?.parentElement?.scrollTo({ top: 0 });
onOpenDetail();
}}
isMobile={true}
/>
)}
{selectedRequestId && (
<RequestIdDetailModal
onClose={() => setSelectedRequestId(undefined)}
requestId={selectedRequestId}
/>
)}
</Box>
);
});
...@@ -193,7 +193,16 @@ const DetailLogsModal = ({ ...@@ -193,7 +193,16 @@ const DetailLogsModal = ({
<Flex flex={'1 0 0'} h={0}> <Flex flex={'1 0 0'} h={0}>
<Box flex={'1 0 0'} h={'100%'} minH={0} overflow={isPlugin ? 'hidden' : 'auto'}> <Box flex={'1 0 0'} h={'100%'} minH={0} overflow={isPlugin ? 'hidden' : 'auto'}>
{isPlugin ? ( {isPlugin ? (
<Box px={5} py={2} h={'100%'} minH={0} display={'flex'} flexDirection={'column'}> <Box
px={5}
py={2}
h={'100%'}
minH={0}
minW={0}
overflowY={'auto'}
display={'flex'}
flexDirection={'column'}
>
<PluginRunBox appId={appId} chatId={chatId} /> <PluginRunBox appId={appId} chatId={chatId} />
</Box> </Box>
) : ( ) : (
......
...@@ -10,7 +10,6 @@ import { type StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge' ...@@ -10,7 +10,6 @@ import { type StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { Box } from '@chakra-ui/react';
import { type AppChatConfigType } from '@fastgpt/global/core/app/type'; import { type AppChatConfigType } from '@fastgpt/global/core/app/type';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox'; import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore';
...@@ -21,6 +20,7 @@ import { useTranslation } from 'next-i18next'; ...@@ -21,6 +20,7 @@ import { useTranslation } from 'next-i18next';
import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/constants'; import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/constants';
import { ChatSourceTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatSourceTypeEnum } from '@fastgpt/global/core/chat/constants';
import { getAppChatSourceKey } from '@/web/core/chat/utils'; import { getAppChatSourceKey } from '@/web/core/chat/utils';
import { Box } from '@chakra-ui/react';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox')); const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
...@@ -172,7 +172,15 @@ export const useChatTest = ({ ...@@ -172,7 +172,15 @@ export const useChatTest = ({
const CustomChatContainer = useMemoizedFn(() => const CustomChatContainer = useMemoizedFn(() =>
appDetail.type === AppTypeEnum.workflowTool ? ( appDetail.type === AppTypeEnum.workflowTool ? (
<Box p={5} h={'100%'} minH={0} display={'flex'} flexDirection={'column'}> <Box
p={5}
h={'100%'}
minH={0}
minW={0}
overflowY={'auto'}
display={'flex'}
flexDirection={'column'}
>
<PluginRunBox <PluginRunBox
appId={appId} appId={appId}
chatId={chatId} chatId={chatId}
......
...@@ -174,7 +174,7 @@ const AppChatWindow = () => { ...@@ -174,7 +174,7 @@ const AppChatWindow = () => {
); );
return ( return (
<Flex h={'100%'} flexDirection={['column', 'row']}> <Flex h={'100%'} minH={0} minW={0} flexDirection={['column', 'row']}>
{/* set window title and icon */} {/* set window title and icon */}
<NextHead <NextHead
title={isCurrentChatReady ? chatBoxData.app.name : undefined} title={isCurrentChatReady ? chatBoxData.app.name : undefined}
...@@ -202,64 +202,69 @@ const AppChatWindow = () => { ...@@ -202,64 +202,69 @@ const AppChatWindow = () => {
<Flex <Flex
position={'relative'} position={'relative'}
h={[0, '100%']} h={[0, '100%']}
minH={0}
minW={0}
w={['100%', 0]} w={['100%', 0]}
flex={'1 0 0'} flex={'1 0 0'}
flexDirection={'column'} flexDirection={'column'}
> >
{isPc ? ( {!isPlugin &&
<ChatWindowHeader (isPc ? (
title={chatWindowTitle} <ChatWindowHeader
history={chatRecords} title={chatWindowTitle}
chatType={ChatTypeEnum.chat} history={chatRecords}
rightActions={<SandboxEntryIcon onOpen={onOpenSandboxModal} />} chatType={ChatTypeEnum.chat}
/> rightActions={<SandboxEntryIcon onOpen={onOpenSandboxModal} />}
) : (
<Flex
h="48px"
px={4}
bg="white"
alignItems="center"
justifyContent="space-between"
color="myGray.600"
>
<IconButton
aria-label="Open history"
icon={<MyIcon name="core/chat/sidebar/menu" w="20px" h="20px" color="currentColor" />}
variant="unstyled"
{...mobileChatHeaderIconButtonStyle}
onClick={onOpenSlider}
/> />
) : (
<Flex
h="48px"
px={4}
bg="white"
alignItems="center"
justifyContent="space-between"
color="myGray.600"
>
<IconButton
aria-label="Open history"
icon={
<MyIcon name="core/chat/sidebar/menu" w="20px" h="20px" color="currentColor" />
}
variant="unstyled"
{...mobileChatHeaderIconButtonStyle}
onClick={onOpenSlider}
/>
<Flex alignItems="center" minW={0} flex="1" justifyContent="center" px={3} gap={2}> <Flex alignItems="center" minW={0} flex="1" justifyContent="center" px={3} gap={2}>
{isCurrentChatReady && ( {isCurrentChatReady && (
<Avatar <Avatar
src={chatBoxData.app.avatar} src={chatBoxData.app.avatar}
w="24px" w="24px"
h="24px" h="24px"
borderRadius="6px" borderRadius="6px"
flexShrink={0} flexShrink={0}
/> />
)} )}
<Box <Box
minW={0} minW={0}
fontSize="16px" fontSize="16px"
fontWeight={500} fontWeight={500}
color="myGray.900" color="myGray.900"
overflow="hidden" overflow="hidden"
whiteSpace="nowrap" whiteSpace="nowrap"
textOverflow="clip" textOverflow="clip"
> >
{mobileHeaderTitle} {mobileHeaderTitle}
</Box>
</Flex>
<Box minW="36px">
<ToolMenu history={chatRecords} chatType={ChatTypeEnum.chat} />
</Box> </Box>
</Flex> </Flex>
))}
<Box minW="36px"> <Box flex={'1 0 0'} minH={0} minW={0} overflow={'hidden'} bg={'white'}>
<ToolMenu history={chatRecords} chatType={ChatTypeEnum.chat} />
</Box>
</Flex>
)}
<Box flex={'1 0 0'} bg={'white'}>
{isPlugin ? ( {isPlugin ? (
<CustomPluginRunBox <CustomPluginRunBox
appId={appId} appId={appId}
......
...@@ -23,14 +23,32 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => { ...@@ -23,14 +23,32 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
}, [isPc, setTab, tab]); }, [isPc, setTab, tab]);
return isPc ? ( return isPc ? (
<Grid gridTemplateColumns={'450px 1fr'} h={'100%'}> <Grid gridTemplateColumns={'450px 1fr'} h={'100%'} minH={0} minW={0}>
<Box px={3} py={4} borderRight={'base'} h={'100%'} overflowY={'auto'} w={'100%'}> <Box
px={3}
py={4}
borderRight={'base'}
h={'100%'}
minH={0}
minW={0}
overflowY={'auto'}
w={'100%'}
>
<Box color={'myGray.900'} pb={5}> <Box color={'myGray.900'} pb={5}>
{t('common:Input')} {t('common:Input')}
</Box> </Box>
<PluginRunBox {...props} showTab={PluginRunBoxTabEnum.input} /> <PluginRunBox {...props} showTab={PluginRunBoxTabEnum.input} />
</Box> </Box>
<Stack px={3} py={4} h={'100%'} alignItems={'flex-start'} w={'100%'} overflow={'auto'}> <Stack
px={3}
py={4}
h={'100%'}
minH={0}
minW={0}
alignItems={'flex-start'}
w={'100%'}
overflow={'hidden'}
>
<Box display={'inline-block'}> <Box display={'inline-block'}>
<LightRowTabs<PluginRunBoxTabEnum> <LightRowTabs<PluginRunBoxTabEnum>
list={[ list={[
...@@ -48,31 +66,35 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => { ...@@ -48,31 +66,35 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
fontSize={'sm'} fontSize={'sm'}
/> />
</Box> </Box>
<Box flex={'1 0 0'} overflow={'auto'} w={'100%'}> <Box flex={'1 0 0'} minH={0} minW={0} overflowY={'auto'} w={'100%'}>
<PluginRunBox {...props} /> <PluginRunBox {...props} />
</Box> </Box>
</Stack> </Stack>
</Grid> </Grid>
) : ( ) : (
<Stack py={2} px={4} h={'100%'}> <Stack pt={2} h={'100%'} minH={0} minW={0} overflow={'hidden'}>
<LightRowTabs<PluginRunBoxTabEnum> <Box px={4}>
list={[ <LightRowTabs<PluginRunBoxTabEnum>
{ label: t('common:Input'), value: PluginRunBoxTabEnum.input }, list={[
{ label: t('common:Output'), value: PluginRunBoxTabEnum.output }, { label: t('common:Input'), value: PluginRunBoxTabEnum.input },
{ label: t('common:all_result'), value: PluginRunBoxTabEnum.detail } { label: t('common:Output'), value: PluginRunBoxTabEnum.output },
]} { label: t('common:all_result'), value: PluginRunBoxTabEnum.detail }
value={tab} ]}
onChange={setTab} value={tab}
inlineStyles={{ px: 0.5, pt: 0 }} onChange={setTab}
outerPadding="4px" inlineStyles={{ px: 0.5, pt: 0 }}
outerHeight="40px" outerPadding="4px"
itemHeight="32px" outerHeight="40px"
gap={5} itemHeight="32px"
py={0} gap={5}
fontSize={'sm'} py={0}
/> fontSize={'sm'}
<Box flex={'1 0 0'} w={'100%'}> />
<PluginRunBox {...props} /> </Box>
<Box flex={'1 0 0'} minH={0} minW={0} overflowY={'auto'} w={'100%'}>
<Box px={4} pb={2}>
<PluginRunBox {...props} />
</Box>
</Box> </Box>
</Stack> </Stack>
); );
......
...@@ -53,15 +53,17 @@ export async function handler(req: ApiRequestProps): Promise<GetPaginationRecord ...@@ -53,15 +53,17 @@ export async function handler(req: ApiRequestProps): Promise<GetPaginationRecord
chatId, chatId,
outLinkAuthData outLinkAuthData
}); });
// 后续查询统一使用鉴权解析后的来源,避免分享链接请求体缺少 sourceType 时降级为普通对话。
const resolvedSourceType = authRes.sourceType;
const resolvedSourceId = authRes.sourceId; const resolvedSourceId = authRes.sourceId;
const [app] = await Promise.all([ const [app] = await Promise.all([
sourceType === ChatSourceTypeEnum.app resolvedSourceType === ChatSourceTypeEnum.app
? MongoApp.findById(resolvedSourceId, 'type').lean() ? MongoApp.findById(resolvedSourceId, 'type').lean()
: null : null
]); ]);
if (sourceType === ChatSourceTypeEnum.app && !app) { if (resolvedSourceType === ChatSourceTypeEnum.app && !app) {
return Promise.reject(AppErrEnum.unExist); return Promise.reject(AppErrEnum.unExist);
} }
const isPlugin = app?.type === AppTypeEnum.workflowTool; const isPlugin = app?.type === AppTypeEnum.workflowTool;
...@@ -76,7 +78,7 @@ export async function handler(req: ApiRequestProps): Promise<GetPaginationRecord ...@@ -76,7 +78,7 @@ export async function handler(req: ApiRequestProps): Promise<GetPaginationRecord
}; };
const { total, histories: sourceHistories } = await getChatItems({ const { total, histories: sourceHistories } = await getChatItems({
sourceType, sourceType: resolvedSourceType,
sourceId: resolvedSourceId, sourceId: resolvedSourceId,
chatId, chatId,
field: fieldMap[type], field: fieldMap[type],
......
...@@ -55,14 +55,16 @@ async function handler(req: ApiRequestProps): Promise<GetRecordsV2ResponseType> ...@@ -55,14 +55,16 @@ async function handler(req: ApiRequestProps): Promise<GetRecordsV2ResponseType>
sourceId, sourceId,
chatId chatId
}); });
// 后续查询统一使用鉴权解析后的来源,避免分享链接请求体缺少 sourceType 时降级为普通对话。
const resolvedSourceType = authRes.sourceType;
const resolvedSourceId = authRes.sourceId; const resolvedSourceId = authRes.sourceId;
const app = const app =
sourceType === ChatSourceTypeEnum.app resolvedSourceType === ChatSourceTypeEnum.app
? await MongoApp.findById(resolvedSourceId, 'type').lean() ? await MongoApp.findById(resolvedSourceId, 'type').lean()
: null; : null;
if (sourceType === ChatSourceTypeEnum.app && !app) { if (resolvedSourceType === ChatSourceTypeEnum.app && !app) {
return Promise.reject(AppErrEnum.unExist); return Promise.reject(AppErrEnum.unExist);
} }
const isPlugin = app?.type === AppTypeEnum.workflowTool; const isPlugin = app?.type === AppTypeEnum.workflowTool;
...@@ -78,7 +80,7 @@ async function handler(req: ApiRequestProps): Promise<GetRecordsV2ResponseType> ...@@ -78,7 +80,7 @@ async function handler(req: ApiRequestProps): Promise<GetRecordsV2ResponseType>
const result = await getChatItems({ const result = await getChatItems({
includeDeleted, includeDeleted,
sourceType, sourceType: resolvedSourceType,
sourceId: resolvedSourceId, sourceId: resolvedSourceId,
chatId, chatId,
field: fieldMap[type], field: fieldMap[type],
......
...@@ -632,7 +632,8 @@ const authShareChat = async ({ ...@@ -632,7 +632,8 @@ const authShareChat = async ({
app, app,
apikey: '', apikey: '',
authType, authType,
responseAllData: false, // 工作流工具的分享运行需要把完整节点链路返回给运行面板;普通对话仍按公开字段过滤。
responseAllData: app.type === AppTypeEnum.workflowTool,
showCite, showCite,
outLinkUserId: uid, outLinkUserId: uid,
showRunningStatus, showRunningStatus,
......
...@@ -632,7 +632,8 @@ const authShareChat = async ({ ...@@ -632,7 +632,8 @@ const authShareChat = async ({
app, app,
apikey: '', apikey: '',
authType, authType,
responseAllData: false, // 工作流工具的分享运行需要把完整节点链路返回给运行面板;普通对话仍按公开字段过滤。
responseAllData: app.type === AppTypeEnum.workflowTool,
showCite, showCite,
outLinkUserId: uid, outLinkUserId: uid,
showRunningStatus, showRunningStatus,
......
...@@ -304,6 +304,8 @@ const OutLink = (props: Props) => { ...@@ -304,6 +304,8 @@ const OutLink = (props: Props) => {
/> />
<Flex <Flex
h={'full'} h={'full'}
minH={0}
minW={0}
gap={datasetCiteData ? 0 : 4} gap={datasetCiteData ? 0 : 4}
{...(isEmbed ? { p: '0 !important', borderRadius: '0', boxShadow: 'none' } : { p: [0, 5] })} {...(isEmbed ? { p: '0 !important', borderRadius: '0', boxShadow: 'none' } : { p: [0, 5] })}
> >
...@@ -311,6 +313,8 @@ const OutLink = (props: Props) => { ...@@ -311,6 +313,8 @@ const OutLink = (props: Props) => {
<PageContainer <PageContainer
flex={'1 0 0'} flex={'1 0 0'}
w={0} w={0}
minH={0}
minW={0}
p={'0 !important'} p={'0 !important'}
insertProps={ insertProps={
datasetCiteData datasetCiteData
...@@ -320,19 +324,22 @@ const OutLink = (props: Props) => { ...@@ -320,19 +324,22 @@ const OutLink = (props: Props) => {
: undefined : undefined
} }
> >
<Flex h={'100%'} flexDirection={['column', 'row']}> <Flex h={'100%'} minH={0} minW={0} flexDirection={['column', 'row']}>
{RenderHistoryList} {RenderHistoryList}
{/* chat container */} {/* chat container */}
<Flex <Flex
position={'relative'} position={'relative'}
h={[0, '100%']} h={[0, '100%']}
minH={0}
minW={0}
w={['100%', 0]} w={['100%', 0]}
flex={'1 0 0'} flex={'1 0 0'}
flexDirection={'column'} flexDirection={'column'}
> >
{/* header */} {/* header */}
{showHead === '1' && {showHead === '1' &&
!isPlugin &&
(isPc ? ( (isPc ? (
<ChatWindowHeader <ChatWindowHeader
title={chatWindowTitle} title={chatWindowTitle}
...@@ -414,7 +421,7 @@ const OutLink = (props: Props) => { ...@@ -414,7 +421,7 @@ const OutLink = (props: Props) => {
</Flex> </Flex>
))} ))}
{/* chat box */} {/* chat box */}
<Box flex={1} bg={'white'}> <Box flex={1} minH={0} minW={0} overflow={'hidden'} bg={'white'}>
{isPlugin ? ( {isPlugin ? (
<CustomPluginRunBox <CustomPluginRunBox
appId={appId} appId={appId}
......
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