mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 09:44:47 +00:00
perf: btn color (#423)
This commit is contained in:
@@ -5,6 +5,7 @@ import { authUser } from '@fastgpt/service/support/user/auth';
|
||||
import { App } from '@/service/models/app';
|
||||
import type { AppUpdateParams } from '@/types/app';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
import { SystemOutputEnum } from '@/constants/app';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -40,7 +41,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
'share.isShare': share.isShare,
|
||||
'share.isShareDetail': share.isShareDetail
|
||||
}),
|
||||
...(modules && { modules })
|
||||
...(modules && {
|
||||
modules: modules.map((modules) => ({
|
||||
...modules,
|
||||
outputs: modules.outputs.sort((a, b) => {
|
||||
// finish output always at last
|
||||
if (a.key === SystemOutputEnum.finish) return 1;
|
||||
if (b.key === SystemOutputEnum.finish) return -1;
|
||||
return 0;
|
||||
})
|
||||
}))
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -29,7 +29,7 @@ import { pushChatBill } from '@/service/common/bill/push';
|
||||
import { BillSourceEnum } from '@/constants/user';
|
||||
import { ChatHistoryItemResType } from '@/types/chat';
|
||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { SystemInputEnum } from '@/constants/app';
|
||||
import { SystemInputEnum, SystemOutputEnum } from '@/constants/app';
|
||||
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
|
||||
import { authOutLinkChat } from '@fastgpt/service/support/outLink/auth';
|
||||
import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
||||
@@ -431,7 +431,7 @@ export async function dispatchModules({
|
||||
inputs: params
|
||||
};
|
||||
|
||||
const dispatchRes = await (async () => {
|
||||
const dispatchRes: Record<string, any> = await (async () => {
|
||||
const callbackMap: Record<string, Function> = {
|
||||
[FlowModuleTypeEnum.historyNode]: dispatchHistory,
|
||||
[FlowModuleTypeEnum.questionInput]: dispatchChatInput,
|
||||
@@ -449,7 +449,10 @@ export async function dispatchModules({
|
||||
return {};
|
||||
})();
|
||||
|
||||
return moduleOutput(module, dispatchRes);
|
||||
return moduleOutput(module, {
|
||||
[SystemOutputEnum.finish]: true,
|
||||
...dispatchRes
|
||||
});
|
||||
}
|
||||
|
||||
// start process width initInput
|
||||
|
@@ -66,7 +66,7 @@ const AIChatSettingsModal = ({
|
||||
isOpen
|
||||
title={
|
||||
<Flex alignItems={'flex-end'}>
|
||||
{t('app.AI Settings')}
|
||||
{t('app.AI Advanced Settings')}
|
||||
{feConfigs?.show_doc && (
|
||||
<Link
|
||||
href={`${feConfigs.docUrl}/docs/use-cases/ai_settings/`}
|
||||
|
@@ -51,12 +51,10 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
...item,
|
||||
connected: item.connected ?? item.type !== FlowInputItemTypeEnum.target
|
||||
})),
|
||||
outputs: item.data.outputs
|
||||
.map((item) => ({
|
||||
...item,
|
||||
targets: [] as FlowOutputTargetItemType[]
|
||||
}))
|
||||
.sort((a, b) => (a.key === SystemOutputEnum.finish ? 1 : -1)) // finish output always at last
|
||||
outputs: item.data.outputs.map((item) => ({
|
||||
...item,
|
||||
targets: [] as FlowOutputTargetItemType[]
|
||||
}))
|
||||
}));
|
||||
|
||||
// update inputs and outputs
|
||||
@@ -100,7 +98,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
}
|
||||
|
||||
return updateAppDetail(app._id, {
|
||||
modules: modules,
|
||||
modules,
|
||||
type: AppTypeEnum.advanced
|
||||
});
|
||||
},
|
||||
|
@@ -128,9 +128,15 @@ const RenderOutput = ({
|
||||
flowOutputList: FlowOutputItemType[];
|
||||
}) => {
|
||||
const sortOutput = useMemo(
|
||||
() => [...flowOutputList].sort((a, b) => (a.key === SystemOutputEnum.finish ? -1 : 1)),
|
||||
() =>
|
||||
[...flowOutputList].sort((a, b) => {
|
||||
if (a.key === SystemOutputEnum.finish) return -1;
|
||||
if (b.key === SystemOutputEnum.finish) return 1;
|
||||
return 0;
|
||||
}),
|
||||
[flowOutputList]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{sortOutput.map(
|
||||
|
@@ -82,7 +82,8 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
const { openConfirm: openConfirmSave, ConfirmModal: ConfirmSaveModal } = useConfirm({
|
||||
content: t('app.Confirm Save App Tip')
|
||||
content: t('app.Confirm Save App Tip'),
|
||||
bg: appDetail.type === AppTypeEnum.basic ? '' : 'red.600'
|
||||
});
|
||||
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
|
||||
content: t('app.Confirm Del App Tip')
|
||||
@@ -211,7 +212,7 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
>
|
||||
<Flex alignItems={'flex-end'}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||
基础信息
|
||||
{t('app.Basic Settings')}
|
||||
</Box>
|
||||
<Box ml={1} color={'myGray.500'} fontSize={'sm'}>
|
||||
(
|
||||
@@ -310,6 +311,7 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
isLoading={isSaving}
|
||||
fontSize={'sm'}
|
||||
size={['sm', 'md']}
|
||||
variant={appDetail.type === AppTypeEnum.basic ? 'primary' : 'base'}
|
||||
onClick={() => {
|
||||
if (appDetail.type !== AppTypeEnum.basic) {
|
||||
openConfirmSave(handleSubmit((data) => onSubmitSave(data)))();
|
||||
@@ -398,21 +400,21 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* model */}
|
||||
{/* ai */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
AI 配置
|
||||
{t('app.AI Settings')}
|
||||
</Box>
|
||||
<Flex {...BoxBtnStyles} onClick={onOpenAIChatSetting}>
|
||||
<MyIcon mr={1} name={'settingLight'} w={'14px'} />
|
||||
高级配置
|
||||
{t('app.Open AI Advanced Settings')}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box {...LabelStyles}>对话模型</Box>
|
||||
<Box {...LabelStyles}>{t('core.ai.Model')}</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
@@ -432,7 +434,7 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
</Flex>
|
||||
<Flex mt={10} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
提示词
|
||||
{t('core.ai.Prompt')}
|
||||
<MyTooltip label={ChatModelSystemTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
@@ -451,24 +453,29 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
||||
<Box ml={2}>知识库</Box>
|
||||
<Box ml={2}>{t('core.dataset.Choose Dataset')}</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
||||
<SmallAddIcon />
|
||||
选择
|
||||
{t('common.Choose')}
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
||||
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
||||
参数
|
||||
{t('common.Params')}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
||||
相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')},
|
||||
空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'}
|
||||
{t('core.dataset.Similarity')}: {getValues('kb.searchSimilarity')},{' '}
|
||||
{t('core.dataset.Search Top K')}: {getValues('kb.searchLimit')}
|
||||
{getValues('kb.searchEmptyText') === '' ? '' : t('core.dataset.Set Empty Result Tip')}
|
||||
</Flex>
|
||||
<Grid templateColumns={['repeat(2,1fr)', 'repeat(3,1fr)']} my={2} gridGap={[2, 4]}>
|
||||
<Grid
|
||||
gridTemplateColumns={['repeat(2, minmax(0, 1fr))', 'repeat(3, minmax(0, 1fr))']}
|
||||
my={2}
|
||||
gridGap={[2, 4]}
|
||||
>
|
||||
{selectDatasets.map((item) => (
|
||||
<MyTooltip key={item._id} label={'查看知识库详情'}>
|
||||
<MyTooltip key={item._id} label={t('core.dataset.Read Dataset')} overflow={'hidden'}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
p={2}
|
||||
@@ -499,7 +506,7 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'questionGuide'} mr={2} w={'16px'} />
|
||||
<Box>下一步指引</Box>
|
||||
<Box>{t('core.app.Next Step Guide')}</Box>
|
||||
<MyTooltip label={questionGuideTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
@@ -635,9 +642,9 @@ const ChatTest = ({ appId }: { appId: string }) => {
|
||||
<Flex position={'relative'} flexDirection={'column'} h={'100%'} py={4} overflowX={'auto'}>
|
||||
<Flex px={[2, 5]}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'} flex={1}>
|
||||
调试预览
|
||||
{t('app.Chat Debug')}
|
||||
</Box>
|
||||
<MyTooltip label={'重置'}>
|
||||
<MyTooltip label={t('core.chat.Restart')}>
|
||||
<IconButton
|
||||
className="chat"
|
||||
size={'sm'}
|
||||
|
@@ -74,140 +74,153 @@ export const DatasetSelectModal = ({
|
||||
tips={'仅能选择同一个索引模型的知识库'}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalBody
|
||||
flex={['1 0 0', '1 0 auto']}
|
||||
maxH={'80vh'}
|
||||
overflowY={['auto', 'unset']}
|
||||
userSelect={'none'}
|
||||
>
|
||||
<Grid gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']} gridGap={3}>
|
||||
{filterKbList.selected.map((item) =>
|
||||
(() => {
|
||||
return (
|
||||
<Card
|
||||
key={item._id}
|
||||
p={3}
|
||||
border={theme.borders.base}
|
||||
boxShadow={'sm'}
|
||||
bg={'myBlue.300'}
|
||||
>
|
||||
<Flex alignItems={'center'} h={'38px'}>
|
||||
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
||||
<Box flex={'1 0 0'} mx={3}>
|
||||
{item.name}
|
||||
</Box>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
setSelectedKbList((state) =>
|
||||
state.filter((kb) => kb.datasetId !== item._id)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Card>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{filterKbList.selected.length > 0 && <Divider my={3} />}
|
||||
|
||||
<Grid gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']} gridGap={3}>
|
||||
{filterKbList.unSelected.map((item) =>
|
||||
(() => {
|
||||
return (
|
||||
<MyTooltip
|
||||
key={item._id}
|
||||
label={
|
||||
item.type === DatasetTypeEnum.dataset
|
||||
? t('dataset.Select Dataset')
|
||||
: t('dataset.Select Folder')
|
||||
}
|
||||
>
|
||||
<Flex h={'100%'} flexDirection={'column'} flex={'1 0 0'}>
|
||||
<ModalBody flex={'1 0 0'} overflowY={'auto'} userSelect={'none'}>
|
||||
<Grid
|
||||
gridTemplateColumns={[
|
||||
'repeat(1, minmax(0, 1fr))',
|
||||
'repeat(2, minmax(0, 1fr))',
|
||||
'repeat(3, minmax(0, 1fr))'
|
||||
]}
|
||||
gridGap={3}
|
||||
>
|
||||
{filterKbList.selected.map((item) =>
|
||||
(() => {
|
||||
return (
|
||||
<Card
|
||||
key={item._id}
|
||||
p={3}
|
||||
border={theme.borders.base}
|
||||
boxShadow={'sm'}
|
||||
h={'80px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
boxShadow: 'md'
|
||||
}}
|
||||
onClick={() => {
|
||||
if (item.type === DatasetTypeEnum.folder) {
|
||||
setParentId(item._id);
|
||||
} else if (item.type === DatasetTypeEnum.dataset) {
|
||||
const vectorModel = selectedKbList[0]?.vectorModel?.model;
|
||||
|
||||
if (vectorModel && vectorModel !== item.vectorModel.model) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: '仅能选择同一个索引模型的知识库'
|
||||
});
|
||||
}
|
||||
setSelectedKbList((state) => [
|
||||
...state,
|
||||
{ datasetId: item._id, vectorModel: item.vectorModel }
|
||||
]);
|
||||
}
|
||||
}}
|
||||
bg={'myBlue.300'}
|
||||
>
|
||||
<Flex alignItems={'center'} h={'38px'}>
|
||||
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
||||
<Box
|
||||
className="textEllipsis"
|
||||
ml={3}
|
||||
fontWeight={'bold'}
|
||||
fontSize={['md', 'lg', 'xl']}
|
||||
>
|
||||
<Box flex={'1 0 0'} w={0} className="textEllipsis" mx={3}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
|
||||
{item.type === DatasetTypeEnum.folder ? (
|
||||
<Box color={'myGray.500'}>{t('Folder')}</Box>
|
||||
) : (
|
||||
<>
|
||||
<MyIcon mr={1} name="kbTest" w={'12px'} />
|
||||
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
|
||||
</>
|
||||
)}
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
setSelectedKbList((state) =>
|
||||
state.filter((kb) => kb.datasetId !== item._id)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Card>
|
||||
</MyTooltip>
|
||||
);
|
||||
})()
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{filterKbList.selected.length > 0 && <Divider my={3} />}
|
||||
|
||||
<Grid
|
||||
gridTemplateColumns={[
|
||||
'repeat(1, minmax(0, 1fr))',
|
||||
'repeat(2, minmax(0, 1fr))',
|
||||
'repeat(3, minmax(0, 1fr))'
|
||||
]}
|
||||
gridGap={3}
|
||||
>
|
||||
{filterKbList.unSelected.map((item) =>
|
||||
(() => {
|
||||
return (
|
||||
<MyTooltip
|
||||
key={item._id}
|
||||
label={
|
||||
item.type === DatasetTypeEnum.dataset
|
||||
? t('dataset.Select Dataset')
|
||||
: t('dataset.Select Folder')
|
||||
}
|
||||
>
|
||||
<Card
|
||||
p={3}
|
||||
border={theme.borders.base}
|
||||
boxShadow={'sm'}
|
||||
h={'80px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
boxShadow: 'md'
|
||||
}}
|
||||
onClick={() => {
|
||||
if (item.type === DatasetTypeEnum.folder) {
|
||||
setParentId(item._id);
|
||||
} else if (item.type === DatasetTypeEnum.dataset) {
|
||||
const vectorModel = selectedKbList[0]?.vectorModel?.model;
|
||||
|
||||
if (vectorModel && vectorModel !== item.vectorModel.model) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: '仅能选择同一个索引模型的知识库'
|
||||
});
|
||||
}
|
||||
setSelectedKbList((state) => [
|
||||
...state,
|
||||
{ datasetId: item._id, vectorModel: item.vectorModel }
|
||||
]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Flex alignItems={'center'} h={'38px'}>
|
||||
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
||||
<Box
|
||||
flex={'1 0 0'}
|
||||
w={0}
|
||||
className="textEllipsis"
|
||||
ml={3}
|
||||
fontWeight={'bold'}
|
||||
fontSize={['md', 'lg', 'xl']}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
|
||||
{item.type === DatasetTypeEnum.folder ? (
|
||||
<Box color={'myGray.500'}>{t('Folder')}</Box>
|
||||
) : (
|
||||
<>
|
||||
<MyIcon mr={1} name="kbTest" w={'12px'} />
|
||||
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Card>
|
||||
</MyTooltip>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</Grid>
|
||||
{filterKbList.unSelected.length === 0 && (
|
||||
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} mt={'20vh'} color={'transparent'} />
|
||||
<Box mt={2} color={'myGray.500'}>
|
||||
这个目录已经没东西可选了~
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Grid>
|
||||
{filterKbList.unSelected.length === 0 && (
|
||||
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} mt={'20vh'} color={'transparent'} />
|
||||
<Box mt={2} color={'myGray.500'}>
|
||||
这个目录已经没东西可选了~
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</ModalBody>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
// filter out the dataset that is not in the kList
|
||||
const filterKbList = selectedKbList.filter((dataset) => {
|
||||
return allDatasets.find((item) => item._id === dataset.datasetId);
|
||||
});
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
// filter out the dataset that is not in the kList
|
||||
const filterKbList = selectedKbList.filter((dataset) => {
|
||||
return allDatasets.find((item) => item._id === dataset.datasetId);
|
||||
});
|
||||
|
||||
onClose();
|
||||
onChange(filterKbList);
|
||||
}}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
onClose();
|
||||
onChange(filterKbList);
|
||||
}}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Flex>
|
||||
</DatasetSelectContainer>
|
||||
);
|
||||
};
|
||||
|
@@ -47,7 +47,6 @@ import { getCollectionIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
import EditFolderModal, { useEditFolder } from '../../component/EditFolderModal';
|
||||
import { TabEnum } from '..';
|
||||
import ParentPath from '@/components/common/ParentPaths';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useDrag } from '@/web/common/hooks/useDrag';
|
||||
import SelectCollections from '@/web/core/dataset/components/SelectCollections';
|
||||
@@ -68,7 +67,6 @@ const CollectionCard = () => {
|
||||
const { isPc } = useSystemStore();
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const { setLoading } = useSystemStore();
|
||||
const { datasetDetail, loadDatasetDetail } = useDatasetStore();
|
||||
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
content: t('dataset.Confirm to delete the file')
|
||||
@@ -114,8 +112,7 @@ const CollectionCard = () => {
|
||||
}
|
||||
});
|
||||
|
||||
const { moveDataId, setMoveDataId, dragStartId, setDragStartId, dragTargetId, setDragTargetId } =
|
||||
useDrag();
|
||||
const { dragStartId, setDragStartId, dragTargetId, setDragTargetId } = useDrag();
|
||||
|
||||
// change search
|
||||
const debounceRefetch = useCallback(
|
||||
@@ -209,7 +206,6 @@ const CollectionCard = () => {
|
||||
getDatasetCollectionPathById(parentId)
|
||||
);
|
||||
|
||||
useQuery(['loadDatasetDetail', datasetId], () => loadDatasetDetail(datasetId));
|
||||
useQuery(
|
||||
['refreshCollection'],
|
||||
() => {
|
||||
@@ -279,7 +275,7 @@ const CollectionCard = () => {
|
||||
</Flex>
|
||||
)}
|
||||
<MyMenu
|
||||
offset={[-10, 10]}
|
||||
offset={[-40, 10]}
|
||||
width={120}
|
||||
Button={
|
||||
<MenuButton
|
||||
@@ -290,15 +286,17 @@ const CollectionCard = () => {
|
||||
>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
border={theme.borders.base}
|
||||
px={5}
|
||||
py={2}
|
||||
borderRadius={'md'}
|
||||
cursor={'pointer'}
|
||||
bg={'myBlue.600'}
|
||||
overflow={'hidden'}
|
||||
color={'white'}
|
||||
h={['28px', '35px']}
|
||||
>
|
||||
<AddIcon mr={2} />
|
||||
<Box>{t('Create New')}</Box>
|
||||
<MyIcon name={'importLight'} mr={2} w={'14px'} />
|
||||
<Box>{t('dataset.collections.Create And Import')}</Box>
|
||||
</Flex>
|
||||
</MenuButton>
|
||||
}
|
||||
@@ -410,7 +408,7 @@ const CollectionCard = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Td maxW={['200px', '300px']} draggable>
|
||||
<Td minW={'150px'} maxW={['200px', '300px']} draggable>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image src={collection.icon} w={'16px'} mr={2} alt={''} />
|
||||
<MyTooltip label={t('common.folder.Drag Tip')} shouldWrapChildren={false}>
|
||||
|
@@ -100,14 +100,23 @@ const DataCard = () => {
|
||||
}
|
||||
/>
|
||||
<Flex className="textEllipsis" flex={'1 0 0'} mr={[3, 5]} alignItems={'center'}>
|
||||
<Image src={fileIcon || '/imgs/files/file.svg'} w={'16px'} mr={2} alt={''} />
|
||||
<RawSourceText
|
||||
sourceName={collection?.name}
|
||||
sourceId={collection?.metadata?.fileId || collection?.metadata?.rawLink}
|
||||
fontSize={['md', 'lg']}
|
||||
color={'black'}
|
||||
textDecoration={'none'}
|
||||
/>
|
||||
<Image src={fileIcon || '/imgs/files/file.svg'} w={['16px', '18px']} mr={2} alt={''} />
|
||||
|
||||
<Box lineHeight={1.2}>
|
||||
<RawSourceText
|
||||
sourceName={collection?.name}
|
||||
sourceId={collection?.metadata?.fileId || collection?.metadata?.rawLink}
|
||||
fontSize={['md', 'lg']}
|
||||
color={'black'}
|
||||
textDecoration={'none'}
|
||||
/>
|
||||
<Box fontSize={'sm'} color={'myGray.500'}>
|
||||
文件ID:{' '}
|
||||
<Box as={'span'} userSelect={'all'}>
|
||||
{collection?._id}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box>
|
||||
<Button
|
||||
|
@@ -4,6 +4,7 @@ import { useConfirm } from '@/web/common/hooks/useConfirm';
|
||||
import { useImportStore, SelectorContainer, PreviewFileOrChunk } from './Provider';
|
||||
|
||||
const fileExtension = '.csv';
|
||||
const csvTemplate = `index,content\n"被索引的内容","对应的答案。CSV 中请注意内容不能包含双引号,双引号是列分割符号"\n"什么是 laf","laf 是一个云函数开发平台……",""\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……"`;
|
||||
|
||||
const CsvImport = () => {
|
||||
const { successChunks, totalChunks, isUnselectedFile, onclickUpload, uploading } =
|
||||
@@ -15,7 +16,15 @@ const CsvImport = () => {
|
||||
|
||||
return (
|
||||
<Box display={['block', 'flex']} h={['auto', '100%']}>
|
||||
<SelectorContainer fileExtension={fileExtension} showUrlFetch={false}>
|
||||
<SelectorContainer
|
||||
fileExtension={fileExtension}
|
||||
showUrlFetch={false}
|
||||
fileTemplate={{
|
||||
filename: 'csv 模板.csv',
|
||||
value: csvTemplate,
|
||||
type: 'text/csv'
|
||||
}}
|
||||
>
|
||||
<Flex mt={3}>
|
||||
<Button isDisabled={uploading} onClick={openConfirm(onclickUpload)}>
|
||||
{uploading ? <Box>{Math.round((successChunks / totalChunks) * 100)}%</Box> : '确认导入'}
|
||||
|
@@ -13,7 +13,7 @@ import {
|
||||
readDocContent
|
||||
} from '@/web/common/file/utils';
|
||||
import { Box, Flex, useDisclosure, type BoxProps } from '@chakra-ui/react';
|
||||
import { DragEvent, useCallback, useState } from 'react';
|
||||
import React, { DragEvent, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -33,7 +33,6 @@ const UrlFetchModal = dynamic(() => import('./UrlFetchModal'));
|
||||
const CreateFileModal = dynamic(() => import('./CreateFileModal'));
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||
const csvTemplate = `index,content\n"被索引的内容","对应的答案。CSV 中请注意内容不能包含双引号,双引号是列分割符号"\n"什么是 laf","laf 是一个云函数开发平台……",""\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……"`;
|
||||
|
||||
export type FileItemType = {
|
||||
id: string; // fileId / raw Link
|
||||
@@ -46,12 +45,16 @@ export type FileItemType = {
|
||||
metadata: DatasetCollectionSchemaType['metadata'];
|
||||
};
|
||||
|
||||
interface Props extends BoxProps {
|
||||
export interface Props extends BoxProps {
|
||||
fileExtension: string;
|
||||
onPushFiles: (files: FileItemType[]) => void;
|
||||
tipText?: string;
|
||||
chunkLen?: number;
|
||||
isCsv?: boolean;
|
||||
fileTemplate?: {
|
||||
type: string;
|
||||
filename: string;
|
||||
value: string;
|
||||
};
|
||||
showUrlFetch?: boolean;
|
||||
showCreateFile?: boolean;
|
||||
}
|
||||
@@ -61,7 +64,7 @@ const FileSelect = ({
|
||||
onPushFiles,
|
||||
tipText,
|
||||
chunkLen = 500,
|
||||
isCsv = false,
|
||||
fileTemplate,
|
||||
showUrlFetch = true,
|
||||
showCreateFile = true,
|
||||
...props
|
||||
@@ -396,7 +399,7 @@ const FileSelect = ({
|
||||
{t(tipText)}
|
||||
</Box>
|
||||
)}
|
||||
{isCsv && (
|
||||
{!!fileTemplate && (
|
||||
<Box
|
||||
mt={1}
|
||||
cursor={'pointer'}
|
||||
@@ -405,13 +408,13 @@ const FileSelect = ({
|
||||
fontSize={'12px'}
|
||||
onClick={() =>
|
||||
fileDownload({
|
||||
text: csvTemplate,
|
||||
type: 'text/csv',
|
||||
filename: 'template.csv'
|
||||
text: fileTemplate.value,
|
||||
type: fileTemplate.type,
|
||||
filename: fileTemplate.filename
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('file.Click to download CSV template')}
|
||||
{t('file.Click to download file template', { name: fileTemplate.filename })}
|
||||
</Box>
|
||||
)}
|
||||
{selectingText !== undefined && (
|
||||
|
@@ -76,7 +76,7 @@ const ImportData = ({
|
||||
<Flex flexDirection={'column'} flex={'1 0 0'}>
|
||||
<Box pb={[5, 7]} px={[4, 8]} borderBottom={theme.borders.base}>
|
||||
<MyRadio
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(3, 350px)']}
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(3,1fr)']}
|
||||
list={[
|
||||
{
|
||||
icon: 'indexImport',
|
||||
|
@@ -8,7 +8,7 @@ import React, {
|
||||
useMemo,
|
||||
useEffect
|
||||
} from 'react';
|
||||
import FileSelect, { FileItemType } from './FileSelect';
|
||||
import FileSelect, { FileItemType, Props as FileSelectProps } from './FileSelect';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { postDatasetCollection } from '@/web/core/dataset/api';
|
||||
import { formatPrice } from '@fastgpt/global/common/bill/tools';
|
||||
@@ -394,11 +394,13 @@ export const SelectorContainer = ({
|
||||
fileExtension,
|
||||
showUrlFetch,
|
||||
showCreateFile,
|
||||
fileTemplate,
|
||||
children
|
||||
}: {
|
||||
fileExtension: string;
|
||||
showUrlFetch?: boolean;
|
||||
showCreateFile?: boolean;
|
||||
fileTemplate?: FileSelectProps['fileTemplate'];
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { files, setPreviewFile, isUnselectedFile, setFiles, chunkLen } = useImportStore();
|
||||
@@ -422,6 +424,7 @@ export const SelectorContainer = ({
|
||||
chunkLen={chunkLen}
|
||||
showUrlFetch={showUrlFetch}
|
||||
showCreateFile={showCreateFile}
|
||||
fileTemplate={fileTemplate}
|
||||
py={isUnselectedFile ? '100px' : 5}
|
||||
/>
|
||||
{!isUnselectedFile && (
|
||||
|
@@ -185,6 +185,7 @@ const Info = (
|
||||
</Box>
|
||||
<Input
|
||||
flex={[1, '0 0 300px']}
|
||||
maxLength={30}
|
||||
{...register('name', {
|
||||
required: '知识库名称不能为空'
|
||||
})}
|
||||
|
@@ -99,6 +99,7 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
|
||||
flex={1}
|
||||
autoFocus
|
||||
bg={'myWhite.600'}
|
||||
maxLength={30}
|
||||
{...register('name', {
|
||||
required: '知识库名称不能为空~'
|
||||
})}
|
||||
|
@@ -363,7 +363,9 @@ const Kb = () => {
|
||||
/>
|
||||
<Flex alignItems={'center'} h={'38px'}>
|
||||
<Avatar src={dataset.avatar} borderRadius={'lg'} w={'28px'} />
|
||||
<Box ml={3}>{dataset.name}</Box>
|
||||
<Box mx={3} className="textEllipsis3">
|
||||
{dataset.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box flex={'1 0 0'} overflow={'hidden'} pt={2}>
|
||||
<Flex>
|
||||
|
Reference in New Issue
Block a user