This commit is contained in:
Archer
2023-12-27 11:07:39 +08:00
committed by GitHub
parent 86286efb54
commit 759a2330e6
182 changed files with 3099 additions and 81685 deletions

View File

@@ -12,7 +12,7 @@ import { clientInitData, feConfigs } from '@/web/common/system/staticData';
import { appWithTranslation, useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { FeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import { change2DefaultLng, setLngStore } from '@/web/common/utils/i18n';
import 'nprogress/nprogress.css';
@@ -40,7 +40,7 @@ function App({ Component, pageProps }: AppProps) {
const { hiId } = router.query as { hiId?: string };
const { i18n } = useTranslation();
const { loadGitStar } = useSystemStore();
const [scripts, setScripts] = useState<FeConfigsType['scripts']>([]);
const [scripts, setScripts] = useState<FastGPTFeConfigsType['scripts']>([]);
const [title, setTitle] = useState(process.env.SYSTEM_NAME || 'AI');
useEffect(() => {

View File

@@ -72,7 +72,7 @@ const BillTable = () => {
<Td>{t(item.appName) || '-'}</Td>
<Td>{item.total}</Td>
<Td>
<Button size={'sm'} variant={'base'} onClick={() => setBillDetail(item)}>
<Button size={'sm'} variant={'whitePrimary'} onClick={() => setBillDetail(item)}>
</Button>
</Td>

View File

@@ -128,7 +128,6 @@ const UserInfo = () => {
py={[2, 10]}
justifyContent={'center'}
alignItems={'flex-start'}
fontSize={['lg', 'xl']}
>
<Flex
flexDirection={'column'}
@@ -230,7 +229,7 @@ const UserInfo = () => {
<Flex mt={6} alignItems={'center'} w={['85%', '300px']}>
<Box flex={'0 0 80px'}>{t('user.Password')}:&nbsp;</Box>
<Box flex={1}>*****</Box>
<Button size={['sm', 'md']} variant={'base'} ml={5} onClick={onOpenUpdatePsw}>
<Button size={['sm', 'md']} variant={'whitePrimary'} ml={5} onClick={onOpenUpdatePsw}>
{t('user.Change')}
</Button>
</Flex>

View File

@@ -59,7 +59,7 @@ const BillTable = () => {
w={'5px'}
h={'5px'}
borderRadius={'10px'}
bg={'myRead.600'}
bg={'red.600'}
position={'absolute'}
bottom={'8px'}
right={'8px'}

View File

@@ -54,7 +54,7 @@ const OpenAIAccountModal = ({
</Flex>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'base'} onClick={onClose}>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>

View File

@@ -110,8 +110,8 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
<ModalFooter>
{!payId && (
<>
<Button variant={'base'} onClick={onClose}>
<Button variant={'whiteBase'} onClick={onClose}>
{t('common.Close')}
</Button>
<Button
ml={3}

View File

@@ -93,7 +93,7 @@ const Promotion = () => {
</Flex>
<Button
mt={4}
variant={'base'}
variant={'whitePrimary'}
fontSize={'sm'}
onClick={() => {
copyData(`${location.origin}/?hiId=${userInfo?._id}`);

View File

@@ -78,7 +78,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
</Flex>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'base'} onClick={onClose}>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>

View File

@@ -1,15 +1,16 @@
import type { FeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { readFileSync, readdirSync } from 'fs';
import type { ConfigFileType, InitDateResponse } from '@/global/common/api/systemRes';
import type { InitDateResponse } from '@/global/common/api/systemRes';
import type { FastGPTConfigFileType } from '@fastgpt/global/common/system/types/index.d';
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
import { getTikTokenEnc } from '@fastgpt/global/common/string/tiktoken';
import { initHttpAgent } from '@fastgpt/service/common/middle/httpAgent';
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
import { getSimpleTemplatesFromPlus } from '@/service/core/app/utils';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { getFastGPTFeConfig } from '@fastgpt/service/common/system/config/controller';
import { getFastGPTConfigFromDB } from '@fastgpt/service/common/system/config/controller';
import { connectToDatabase } from '@/service/mongo';
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type';
import { readConfigData } from '@/service/common/system';
@@ -26,11 +27,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
cqModels: global.cqModels,
extractModels: global.extractModels,
vectorModels: global.vectorModels,
reRankModels: global.reRankModels.map((item) => ({
...item,
requestUrl: undefined,
requestAuth: undefined
})),
reRankModels:
global.reRankModels?.map((item) => ({
...item,
requestUrl: undefined,
requestAuth: undefined
})) || [],
audioSpeechModels: global.audioSpeechModels,
priceMd: global.priceMd,
systemVersion: global.systemVersion || '0.0.0',
@@ -39,7 +41,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
}
const defaultFeConfigs: FeConfigsType = {
const defaultFeConfigs: FastGPTFeConfigsType = {
show_emptyChat: true,
show_git: true,
docUrl: 'https://doc.fastgpt.in',
@@ -58,72 +60,87 @@ export async function getInitConfig() {
try {
if (global.feConfigs) return;
await connectToDatabase();
initGlobal();
// load config
const [dbConfig, fileConfig] = await Promise.all([
getFastGPTFeConfig(),
readConfigData('config.json')
]);
const fileRes = JSON.parse(fileConfig) as ConfigFileType;
// get config from database
const config: ConfigFileType = {
...fileRes,
FeConfig: {
...defaultFeConfigs,
...fileRes.FeConfig,
...dbConfig
}
};
// set config
global.feConfigs = {
isPlus: !!config.SystemParams.pluginBaseUrl,
concatMd: config.FeConfig.show_git ? config.FeConfig.concatMd : '',
...config.FeConfig
};
global.systemEnv = config.SystemParams;
global.chatModels = config.ChatModels || [];
global.qaModels = config.QAModels || [];
global.cqModels = config.CQModels || [];
global.extractModels = config.ExtractModels || [];
global.qgModels = config.QGModels || [];
global.vectorModels = config.VectorModels || [];
global.reRankModels = config.ReRankModels || [];
global.audioSpeechModels = config.AudioSpeechModels || [];
global.whisperModel = config.WhisperModel;
global.priceMd = '';
await initSystemConfig();
} catch (error) {
console.error('Load init config error', error);
exit(1);
if (!global.feConfigs) {
exit(1);
}
}
await getSimpleModeTemplates();
getSystemVersion();
getModelPrice();
countModelPrice();
getSystemPlugin();
console.log({
FeConfig: global.feConfigs,
SystemParams: global.systemEnv,
ChatModels: global.chatModels,
QAModels: global.qaModels,
CQModels: global.cqModels,
ExtractModels: global.extractModels,
QGModels: global.qgModels,
VectorModels: global.vectorModels,
ReRankModels: global.reRankModels,
AudioSpeechModels: global.reRankModels,
WhisperModel: global.whisperModel,
feConfigs: global.feConfigs,
systemEnv: global.systemEnv,
chatModels: global.chatModels,
qaModels: global.qaModels,
cqModels: global.cqModels,
extractModels: global.extractModels,
qgModels: global.qgModels,
vectorModels: global.vectorModels,
reRankModels: global.reRankModels,
audioSpeechModels: global.audioSpeechModels,
whisperModel: global.whisperModel,
price: global.priceMd,
simpleModeTemplates: global.simpleModeTemplates,
communityPlugins: global.communityPlugins
});
}
export async function initSystemConfig() {
// load config
const [dbConfig, fileConfig] = await Promise.all([
getFastGPTConfigFromDB(),
readConfigData('config.json')
]);
const fileRes = JSON.parse(fileConfig) as FastGPTConfigFileType;
// get config from database
const config: FastGPTConfigFileType = {
feConfigs: {
...defaultFeConfigs,
...(fileRes.feConfigs || {}),
...(dbConfig.feConfigs || {})
},
systemEnv: fileRes.systemEnv,
chatModels: dbConfig.chatModels || fileRes.chatModels || [],
qaModels: dbConfig.qaModels || fileRes.qaModels || [],
cqModels: dbConfig.cqModels || fileRes.cqModels || [],
extractModels: dbConfig.extractModels || fileRes.extractModels || [],
qgModels: dbConfig.qgModels || fileRes.qgModels || [],
vectorModels: dbConfig.vectorModels || fileRes.vectorModels || [],
reRankModels: dbConfig.reRankModels || fileRes.reRankModels || [],
audioSpeechModels: dbConfig.audioSpeechModels || fileRes.audioSpeechModels || [],
whisperModel: dbConfig.whisperModel || fileRes.whisperModel
};
// set config
global.feConfigs = {
isPlus: !!config.systemEnv.pluginBaseUrl,
...config.feConfigs
};
global.systemEnv = config.systemEnv;
global.chatModels = config.chatModels || [];
global.qaModels = config.qaModels || [];
global.cqModels = config.cqModels || [];
global.extractModels = config.extractModels || [];
global.qgModels = config.qgModels || [];
global.vectorModels = config.vectorModels || [];
global.reRankModels = config.reRankModels || [];
global.audioSpeechModels = config.audioSpeechModels || [];
global.whisperModel = config.whisperModel;
global.priceMd = '';
}
export function initGlobal() {
global.communityPlugins = [];
global.simpleModeTemplates = [];
@@ -151,8 +168,7 @@ export function getSystemVersion() {
}
}
function getModelPrice() {
if (global.priceMd) return;
export function countModelPrice() {
global.priceMd = `| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
| --- | --- |
${global.vectorModels
@@ -176,7 +192,11 @@ ${global.qgModels
${global.audioSpeechModels
?.map((item) => `| 语音播放-${item.name} | ${formatPrice(item.price, 1000)} |`)
.join('\n')}
${`| 语音输入-${global.whisperModel.name} | ${global.whisperModel.price}/分钟 |`}
${
global.whisperModel
? `| 语音输入-${global.whisperModel.name} | ${global.whisperModel.price}/分钟 |`
: ''
}
`;
}

View File

@@ -0,0 +1,33 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { countModelPrice, initSystemConfig } from './getInitData';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
await authCert({ req, authRoot: true });
await initSystemConfig();
countModelPrice();
console.log(`refresh config`);
console.log({
feConfigs: global.feConfigs,
systemEnv: global.systemEnv,
chatModels: global.chatModels,
qaModels: global.qaModels,
cqModels: global.cqModels,
extractModels: global.extractModels,
qgModels: global.qgModels,
vectorModels: global.vectorModels,
reRankModels: global.reRankModels,
audioSpeechModels: global.audioSpeechModels,
whisperModel: global.whisperModel,
price: global.priceMd
});
} catch (error) {
console.log(error);
}
jsonRes(res);
}

View File

@@ -68,7 +68,7 @@ export async function pushDataToDatasetCollection({
teamId: string;
tmbId: string;
} & PushDatasetDataProps): Promise<PushDataResponse> {
const { datasetId, model, maxToken } = await checkModelValid({
const { datasetId, model, maxToken, weight } = await checkModelValid({
mode,
collectionId
});
@@ -137,6 +137,7 @@ export async function pushDataToDatasetCollection({
q: item.q,
a: item.a,
chunkIndex: item.chunkIndex ?? i,
weight: weight ?? 0,
indexes: item.indexes
}))
);
@@ -167,10 +168,12 @@ export async function checkModelValid({
if (!vectorModelData) {
return Promise.reject(`Model ${vectorModel} is inValid`);
}
return {
datasetId,
maxToken: vectorModelData.maxToken * 1.5,
model: vectorModelData.model
model: vectorModelData.model,
weight: vectorModelData.weight
};
}
@@ -182,7 +185,8 @@ export async function checkModelValid({
return {
datasetId,
maxToken: qaModelData.maxContext * 0.8,
model: qaModelData.model
model: qaModelData.model,
weight: 0
};
}
return Promise.reject(`Mode ${mode} is inValid`);

View File

@@ -3,7 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { request } from '@fastgpt/service/common/api/plusRequest';
import type { Method } from 'axios';
import { setCookie } from '@fastgpt/service/support/permission/controller';
import { getInitConfig } from '../system/getInitData';
import { getInitConfig } from '../common/system/getInitData';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {

View File

@@ -12,7 +12,7 @@ import dynamic from 'next/dynamic';
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import ChatTest, { type ChatTestComponentRef } from '@/components/core/module/Flow/ChatTest';
import { useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider';
import { getFlowStore } from '@/components/core/module/Flow/FlowProvider';
import { flowNode2Modules, filterExportModules } from '@/components/core/module/utils';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { useToast } from '@/web/common/hooks/useToast';
@@ -43,9 +43,9 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
const { updateAppDetail } = useAppStore();
const { nodes, edges } = useFlowProviderStore();
const flow2ModulesAndCheck = useCallback(async () => {
const { nodes, edges } = await getFlowStore();
const flow2ModulesAndCheck = useCallback(() => {
const modules = flowNode2Modules({ nodes, edges });
// check required connect
for (let i = 0; i < modules.length; i++) {
@@ -72,7 +72,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
}
}
return modules;
}, [edges, nodes, t, toast]);
}, [t, toast]);
const { mutate: onclickSave, isLoading } = useRequest({
mutationFn: async (modules: ModuleItemType[]) => {
@@ -100,14 +100,13 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
>
<MyTooltip label={t('common.Back')} offset={[10, 10]}>
<IconButton
size={'sm'}
size={'smSquare'}
icon={<MyIcon name={'back'} w={'14px'} />}
borderRadius={'md'}
borderColor={'myGray.300'}
variant={'base'}
variant={'whiteBase'}
aria-label={''}
onClick={openConfirmOut(async () => {
const modules = flow2ModulesAndCheck();
const modules = await flow2ModulesAndCheck();
if (modules) {
await onclickSave(modules);
}
@@ -122,9 +121,9 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<MyTooltip label={t('app.Import Configs')}>
<IconButton
mr={[3, 6]}
size={'smSquare'}
icon={<MyIcon name={'importLight'} w={['14px', '16px']} />}
borderRadius={'lg'}
variant={'base'}
variant={'whitePrimary'}
aria-label={'save'}
onClick={onOpenImport}
/>
@@ -133,11 +132,11 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<IconButton
mr={[3, 6]}
icon={<MyIcon name={'export'} w={['14px', '16px']} />}
borderRadius={'lg'}
variant={'base'}
size={'smSquare'}
variant={'whitePrimary'}
aria-label={'save'}
onClick={() => {
const modules = flow2ModulesAndCheck();
onClick={async () => {
const modules = await flow2ModulesAndCheck();
if (modules) {
copyData(filterExportModules(modules), t('app.Export Config Successful'));
}
@@ -149,9 +148,8 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<IconButton
mr={[3, 6]}
icon={<SmallCloseIcon fontSize={'25px'} />}
variant={'base'}
color={'myGray.600'}
borderRadius={'lg'}
variant={'whitePrimary'}
size={'smSquare'}
aria-label={''}
onClick={() => setTestModules(undefined)}
/>
@@ -160,11 +158,11 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<IconButton
mr={[3, 6]}
icon={<MyIcon name={'chat'} w={['14px', '16px']} />}
borderRadius={'lg'}
size={'smSquare'}
aria-label={'save'}
variant={'base'}
onClick={() => {
const modules = flow2ModulesAndCheck();
variant={'whitePrimary'}
onClick={async () => {
const modules = await flow2ModulesAndCheck();
if (modules) {
setTestModules(modules);
}
@@ -176,11 +174,11 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<MyTooltip label={'保存配置'}>
<IconButton
icon={<MyIcon name={'save'} w={['14px', '16px']} />}
borderRadius={'lg'}
size={'smSquare'}
isLoading={isLoading}
aria-label={'save'}
onClick={() => {
const modules = flow2ModulesAndCheck();
onClick={async () => {
const modules = await flow2ModulesAndCheck();
if (modules) {
onclickSave(modules);
}

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { AppSchema } from '@fastgpt/global/core/app/type.d';
import Header from './Header';
import Flow from '@/components/core/module/Flow';
@@ -12,7 +12,7 @@ import { useQuery } from '@tanstack/react-query';
type Props = { app: AppSchema; onClose: () => void };
const Render = ({ app, onClose }: Props) => {
const { nodes } = useFlowProviderStore();
const { nodes, initData } = useFlowProviderStore();
const { pluginModuleTemplates, loadPluginTemplates } = usePluginStore();
const moduleTemplates = useMemo(() => {
@@ -40,13 +40,11 @@ const Render = ({ app, onClose }: Props) => {
useQuery(['getPlugTemplates'], () => loadPluginTemplates());
return (
<Flow
templates={moduleTemplates}
modules={app.modules}
Header={<Header app={app} onClose={onClose} />}
/>
);
useEffect(() => {
initData(JSON.parse(JSON.stringify(app.modules)));
}, [app.modules]);
return <Flow templates={moduleTemplates} Header={<Header app={app} onClose={onClose} />} />;
};
export default React.memo(function AdEdit(props: Props) {

View File

@@ -174,7 +174,7 @@ const InfoModal = ({
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
</Button>
<Button isLoading={btnLoading} onClick={saveUpdateModel}>

View File

@@ -189,7 +189,7 @@ const SelectUsingWayModal = ({ share, onClose }: { share: OutLinkSchema; onClose
w={'16px'}
color={'myGray.600'}
cursor={'pointer'}
_hover={{ color: 'blue.500' }}
_hover={{ color: 'primary.500' }}
onClick={() => {
copyData(wayMap[getValues('usingWay')].code);
}}

View File

@@ -76,7 +76,7 @@ const Share = ({ appId }: { appId: string }) => {
</MyTooltip>
</Box>
<Button
variant={'base'}
variant={'whitePrimary'}
colorScheme={'blue'}
size={['sm', 'md']}
{...(shareChatList.length >= 10
@@ -383,7 +383,7 @@ function EditLinkModal({
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
</Button>

View File

@@ -199,7 +199,7 @@ function ConfigForm({
ref={divRef}
position={'sticky'}
top={-4}
bg={'white'}
bg={'myGray.25'}
py={4}
justifyContent={'space-between'}
alignItems={'center'}
@@ -218,9 +218,8 @@ function ConfigForm({
</Box>
<Button
isLoading={isSaving}
fontSize={'sm'}
size={['sm', 'md']}
variant={appDetail.type === AppTypeEnum.simple ? 'primary' : 'base'}
variant={appDetail.type === AppTypeEnum.simple ? 'primary' : 'whitePrimary'}
onClick={() => {
if (appDetail.type !== AppTypeEnum.simple) {
openConfirmSave(handleSubmit((data) => onSubmitSave(data)))();
@@ -562,26 +561,26 @@ function Settings({ appId }: { appId: string }) {
overflow={'overlay'}
>
<Box px={4}>
<Flex alignItems={'flex-end'}>
<Flex alignItems={'center'} justifyContent={'space-between'}>
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
<PermissionIconText permission={appDetail.permission} />
</Box>
<Box ml={1} color={'myGray.500'} fontSize={'sm'}>
(AppId:{' '}
<Box color={'myGray.500'} fontSize={'sm'}>
AppId:{' '}
<Box as={'span'} userSelect={'all'}>
{appId}
</Box>
)
</Box>
</Flex>
{/* basic info */}
<Box
border={theme.borders.base}
borderWidth={'1px'}
borderColor={'primary.1'}
borderRadius={'lg'}
mt={2}
px={5}
py={4}
bg={'blue.50'}
bg={'primary.50'}
position={'relative'}
>
<Flex alignItems={'center'} py={2}>
@@ -595,15 +594,11 @@ function Settings({ appId }: { appId: string }) {
position={'absolute'}
top={4}
right={4}
size={'sm'}
size={'smSquare'}
icon={<MyIcon name={'delete'} w={'14px'} />}
variant={'base'}
variant={'whiteDanger'}
borderRadius={'md'}
aria-label={'delete'}
_hover={{
bg: 'myGray.100',
color: 'red.600'
}}
isLoading={isLoading}
onClick={openConfirmDel(handleDelModel)}
/>
@@ -621,7 +616,7 @@ function Settings({ appId }: { appId: string }) {
<Flex>
<Button
size={['sm', 'md']}
variant={'base'}
variant={'whitePrimary'}
leftIcon={<MyIcon name={'chat'} w={'16px'} />}
onClick={() => router.push(`/chat?appId=${appId}`)}
>
@@ -630,7 +625,7 @@ function Settings({ appId }: { appId: string }) {
<Button
mx={3}
size={['sm', 'md']}
variant={'base'}
variant={'whitePrimary'}
leftIcon={<MyIcon name={'shareLight'} w={'16px'} />}
onClick={() => {
router.replace({
@@ -646,7 +641,7 @@ function Settings({ appId }: { appId: string }) {
{appDetail.isOwner && (
<Button
size={['sm', 'md']}
variant={'base'}
variant={'whitePrimary'}
leftIcon={<MyIcon name={'settingLight'} w={'16px'} />}
onClick={() => setSettingAppInfo(appDetail)}
>
@@ -722,7 +717,14 @@ function ChatTest({ appId }: { appId: string }) {
}, [appDetail, resetChatBox]);
return (
<Flex position={'relative'} flexDirection={'column'} h={'100%'} py={4} overflowX={'auto'}>
<Flex
position={'relative'}
flexDirection={'column'}
h={'100%'}
py={4}
overflowX={'auto'}
bg={'white'}
>
<Flex px={[2, 5]}>
<Box fontSize={['md', 'xl']} fontWeight={'bold'} flex={1}>
{t('app.Chat Debug')}
@@ -730,9 +732,9 @@ function ChatTest({ appId }: { appId: string }) {
<MyTooltip label={t('core.chat.Restart')}>
<IconButton
className="chat"
size={'sm'}
size={'smSquare'}
icon={<MyIcon name={'clear'} w={'14px'} />}
variant={'base'}
variant={'whiteDanger'}
borderRadius={'md'}
aria-label={'delete'}
onClick={(e) => {

View File

@@ -109,7 +109,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
>
<Flex mb={4} alignItems={'center'}>
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'lg'} />
<Box ml={2} fontWeight={'bold'} fontSize={'sm'}>
<Box ml={2} fontWeight={'bold'}>
{appDetail.name}
</Box>
</Flex>
@@ -139,11 +139,10 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
>
<IconButton
mr={3}
icon={<MyIcon name={'backFill'} w={'18px'} color={'blue.500'} />}
icon={<MyIcon name={'backFill'} w={'18px'} color={'primary.500'} />}
bg={'white'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
h={'28px'}
size={'sm'}
size={'smSquare'}
borderRadius={'50%'}
aria-label={''}
/>

View File

@@ -180,7 +180,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
</Button>
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>

View File

@@ -71,21 +71,18 @@ const MyApps = () => {
});
return (
<PageContainer isLoading={isFetching}>
<Flex pt={3} px={5} alignItems={'center'}>
<Flex flex={1} alignItems={'center'}>
<Image src={'/imgs/module/ai.svg'} alt={''} mr={2} h={'24px'} />
<Box className="textlg" letterSpacing={1} fontSize={['20px', '24px']} fontWeight={'bold'}>
{t('app.My Apps')}
</Box>
</Flex>
<Button leftIcon={<AddIcon />} variant={'base'} onClick={onOpenCreateModal}>
<PageContainer isLoading={isFetching} insertProps={{ px: [5, '48px'] }}>
<Flex pt={[4, '30px']} alignItems={'center'} justifyContent={'space-between'}>
<Box letterSpacing={1} fontSize={['20px', '24px']} color={'myGray.900'}>
{t('app.My Apps')}
</Box>
<Button leftIcon={<AddIcon />} variant={'primaryOutline'} onClick={onOpenCreateModal}>
{t('common.New Create')}
</Button>
</Flex>
<Grid
p={5}
gridTemplateColumns={['1fr', 'repeat(3,1fr)', 'repeat(4,1fr)', 'repeat(5,1fr)']}
py={[4, 6]}
gridTemplateColumns={['1fr', 'repeat(2,1fr)', 'repeat(3,1fr)', 'repeat(4,1fr)']}
gridGap={5}
>
{myApps.map((app) => (
@@ -93,26 +90,28 @@ const MyApps = () => {
key={app._id}
label={userInfo?.team.canWrite ? t('app.To Settings') : t('app.To Chat')}
>
<Card
<Box
lineHeight={1.5}
h={'100%'}
py={3}
px={5}
cursor={'pointer'}
border={theme.borders.md}
boxShadow={'none'}
borderWidth={'1.5px'}
borderColor={'borderColor.low'}
bg={'white'}
borderRadius={'lg'}
userSelect={'none'}
position={'relative'}
display={'flex'}
flexDirection={'column'}
_hover={{
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)',
borderColor: 'transparent',
borderColor: 'primary.300',
boxShadow: '1.5',
'& .delete': {
display: 'block'
display: 'flex'
},
'& .chat': {
display: 'block'
display: 'flex'
}
}}
onClick={() => {
@@ -132,15 +131,11 @@ const MyApps = () => {
position={'absolute'}
top={4}
right={4}
size={'sm'}
size={'xsSquare'}
variant={'whiteDanger'}
icon={<MyIcon name={'delete'} w={'14px'} />}
variant={'base'}
borderRadius={'md'}
aria-label={'delete'}
display={['', 'none']}
_hover={{
bg: 'red.100'
}}
onClick={(e) => {
e.stopPropagation();
openConfirm(() => onclickDelApp(app._id))();
@@ -165,19 +160,15 @@ const MyApps = () => {
{userInfo?.team.canWrite && (
<IconButton
className="chat"
size={'sm'}
size={'xsSquare'}
variant={'whitePrimary'}
icon={
<MyTooltip label={'去聊天'}>
<MyIcon name={'chat'} w={'14px'} />
</MyTooltip>
}
variant={'base'}
borderRadius={'md'}
aria-label={'delete'}
aria-label={'chat'}
display={['', 'none']}
_hover={{
bg: 'myGray.100'
}}
onClick={(e) => {
e.stopPropagation();
router.push(`/chat?appId=${app._id}`);
@@ -185,7 +176,7 @@ const MyApps = () => {
/>
)}
</Flex>
</Card>
</Box>
</MyTooltip>
))}
</Grid>

View File

@@ -61,7 +61,7 @@ const ShareModelList = ({
<Flex
alignItems={'center'}
cursor={'pointer'}
color={model.isCollection ? 'blue.600' : 'blackAlpha.700'}
color={model.isCollection ? 'primary.600' : 'blackAlpha.700'}
onClick={() => onclickCollection(model._id)}
>
<MyIcon
@@ -74,7 +74,7 @@ const ShareModelList = ({
<Box>
<Button
size={'sm'}
variant={'base'}
variant={'whitePrimary'}
w={['60px', '70px']}
onClick={() => router.push(`/chat?appId=${model._id}`)}
>

View File

@@ -42,8 +42,7 @@ const ChatHeader = ({
alignItems={'center'}
px={[3, 5]}
h={['46px', '60px']}
borderBottom={theme.borders.base}
borderBottomColor={'gray.200'}
borderBottom={theme.borders.sm}
color={'myGray.900'}
>
{isPc ? (

View File

@@ -151,10 +151,10 @@ const ChatHistorySlider = ({
/>
)}
<Button
variant={'base'}
variant={'whitePrimary'}
flex={1}
h={'100%'}
color={'blue.600'}
color={'primary.600'}
borderRadius={'xl'}
leftIcon={<MyIcon name={'chat'} w={'16px'} />}
overflow={'hidden'}
@@ -167,7 +167,8 @@ const ChatHistorySlider = ({
<IconButton
ml={3}
h={'100%'}
variant={'base'}
variant={'whiteDanger'}
size={'mdSquare'}
aria-label={''}
borderRadius={'xl'}
onClick={openConfirm(onClearHistory)}
@@ -201,8 +202,8 @@ const ChatHistorySlider = ({
bg={item.top ? '#E6F6F6 !important' : ''}
{...(item.id === activeChatId
? {
backgroundColor: 'blue.50 !important',
color: 'blue.600'
backgroundColor: 'primary.50 !important',
color: 'primary.600'
}
: {
onClick: () => {
@@ -290,8 +291,8 @@ const ChatHistorySlider = ({
alignItems={'center'}
{...(item._id === appId
? {
backgroundColor: 'blue.50 !important',
color: 'blue.600'
backgroundColor: 'primary.50 !important',
color: 'primary.600'
}
: {
onClick: () => {
@@ -325,11 +326,10 @@ const ChatHistorySlider = ({
>
<IconButton
mr={3}
icon={<MyIcon name={'backFill'} w={'18px'} color={'blue.500'} />}
icon={<MyIcon name={'backFill'} w={'18px'} color={'primary.500'} />}
bg={'white'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
h={'28px'}
size={'sm'}
size={'smSquare'}
borderRadius={'50%'}
aria-label={''}
/>

View File

@@ -28,11 +28,10 @@ const SliderApps = ({ appId }: { appId: string }) => {
>
<IconButton
mr={3}
icon={<MyIcon name={'backFill'} w={'18px'} color={'blue.500'} />}
icon={<MyIcon name={'backFill'} w={'18px'} color={'primary.500'} />}
bg={'white'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
h={'28px'}
size={'sm'}
size={'smSquare'}
borderRadius={'50%'}
aria-label={''}
/>

View File

@@ -255,8 +255,8 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
</Box>
)}
<PageContainer flex={'1 0 0'} w={0} bg={'myWhite.600'} position={'relative'}>
<Flex h={'100%'} flexDirection={['column', 'row']}>
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
{/* pc always show history. */}
{((children: React.ReactNode) => {
return isPc || !appId ? (

View File

@@ -258,11 +258,21 @@ const OutLink = ({
}, [clearLocalHistory, localUId, router, shareChatHistory, shareId, t, toast]);
return (
<PageContainer {...(isEmbed ? { p: '0 !important', borderRadius: '0' } : {})}>
<PageContainer
{...(isEmbed
? { p: '0 !important', insertProps: { borderRadius: '0', boxShadow: 'none' } }
: { p: [0, 5] })}
>
<Head>
<title>{chatData.app.name}</title>
</Head>
<MyBox isLoading={isFetching} h={'100%'} display={'flex'} flexDirection={['column', 'row']}>
<MyBox
isLoading={isFetching}
h={'100%'}
display={'flex'}
flexDirection={['column', 'row']}
bg={'white'}
>
{showHistory === '1'
? ((children: React.ReactNode) => {
return isPc ? (

View File

@@ -16,14 +16,14 @@ const Ability = () => {
};
const TitleStyles: BoxProps = {
px: 4,
fontSize: ['xl', '30px'],
fontSize: ['xl', '28px'],
fontWeight: 'bold'
};
const DescStyles: BoxProps = {
px: 4,
mt: 2,
mb: 5,
fontSize: ['sm', 'lg'],
fontSize: ['sm', 'md'],
whiteSpace: 'pre-wrap'
};

View File

@@ -90,10 +90,10 @@ const Choice = () => {
<Image src={item.icon} w={'28px'} alt={''} loading={'lazy'} />
</Flex>
<Box ml={5}>
<Box fontSize={['lg', '2xl']} fontWeight={'bold'} color={'myGray.900'}>
<Box fontSize={['lg', 'xl']} fontWeight={'bold'} color={'myGray.900'}>
{item.title}
</Box>
<Box mt={1} fontSize={['md', 'lg']}>
<Box mt={1} fontSize={'md'}>
{item.desc}
</Box>
</Box>

View File

@@ -14,10 +14,10 @@ const Hero = () => {
return (
<Flex flexDirection={'column'} pt={['24px', '50px']} alignItems={'center'} userSelect={'none'}>
<Box fontSize={['38px', '60px']} fontWeight={'bold'}>
<Box fontSize={['38px', '54px']} fontWeight={'bold'}>
{t('home.slogan')}
</Box>
<Box fontSize={['xl', '3xl']} py={5} color={'myGray.600'} textAlign={'center'} maxW={'400px'}>
<Box fontSize={['xl', '2xl']} py={5} color={'myGray.600'} textAlign={'center'} maxW={'400px'}>
{t('home.desc')}
</Box>
<Flex zIndex={1} flexDirection={['column', 'row']} mt={[5, 8]}>
@@ -25,10 +25,10 @@ const Hero = () => {
<Button
mr={[0, 5]}
mb={[5, 0]}
fontSize={['xl', '3xl']}
fontSize={['xl', '2xl']}
h={'auto'}
py={[2, 3]}
variant={'base'}
variant={'whitePrimary'}
border={'2px solid'}
borderColor={'myGray.800'}
transition={'0.3s'}
@@ -44,7 +44,7 @@ const Hero = () => {
</Button>
)}
<Button
fontSize={['xl', '3xl']}
fontSize={['xl', '2xl']}
h={['38px', 'auto']}
borderRadius={'xl'}
py={[2, 3]}

View File

@@ -71,7 +71,7 @@ const Navbar = () => {
borderRadius: 'xl',
fontSize: 'lg',
_hover: {
bg: 'myGray.100'
bg: 'myGray.200'
}
};

View File

@@ -53,7 +53,7 @@ const EditFolderModal = ({
/>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'base'} onClick={onClose}>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('Cancel')}
</Button>
<Button isLoading={isLoading} onClick={onSave}>

View File

@@ -311,7 +311,7 @@ const CollectionCard = () => {
target="_blank"
mr={2}
textDecoration={'underline'}
color={'blue.600'}
color={'primary.600'}
>
{datasetDetail.websiteConfig.url}
</Link>
@@ -371,7 +371,7 @@ const CollectionCard = () => {
Button={
<MenuButton
_hover={{
color: 'blue.500'
color: 'primary.500'
}}
fontSize={['sm', 'md']}
>
@@ -381,7 +381,7 @@ const CollectionCard = () => {
py={2}
borderRadius={'md'}
cursor={'pointer'}
bg={'blue.500'}
bg={'primary.500'}
overflow={'hidden'}
color={'white'}
h={['28px', '35px']}
@@ -489,7 +489,7 @@ const CollectionCard = () => {
data-drag-id={
collection.type === DatasetCollectionTypeEnum.folder ? collection._id : undefined
}
bg={dragTargetId === collection._id ? 'blue.100' : ''}
bg={dragTargetId === collection._id ? 'primary.100' : ''}
userSelect={'none'}
onDragStart={(e) => {
setDragStartId(collection._id);
@@ -579,7 +579,7 @@ const CollectionCard = () => {
h={'22px'}
borderRadius={'md'}
_hover={{
color: 'blue.500',
color: 'primary.500',
'& .icon': {
bg: 'myGray.100'
}

View File

@@ -163,10 +163,9 @@ const DataCard = () => {
<Flex alignItems={'center'}>
<IconButton
mr={3}
icon={<MyIcon name={'backFill'} w={['14px', '18px']} color={'blue.500'} />}
bg={'white'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
size={'sm'}
icon={<MyIcon name={'backFill'} w={['14px', '18px']} color={'primary.500'} />}
variant={'whitePrimary'}
size={'smSquare'}
borderRadius={'50%'}
aria-label={''}
onClick={() =>
@@ -200,7 +199,7 @@ const DataCard = () => {
<Box>
<Button
mx={2}
variant={'base'}
variant={'whitePrimary'}
size={['sm', 'md']}
onClick={() => {
if (!collection) return;
@@ -217,7 +216,7 @@ const DataCard = () => {
{isPc && (
<MyTooltip label={t('core.dataset.collection.metadata.Read Metadata')}>
<IconButton
variant={'base'}
variant={'whiteBase'}
size={['sm', 'md']}
icon={<MyIcon name={'menu'} w={'18px'} />}
aria-label={''}
@@ -338,12 +337,9 @@ const DataCard = () => {
<IconButton
display={'flex'}
icon={<DeleteIcon />}
variant={'base'}
colorScheme={'gray'}
variant={'whiteDanger'}
size={'xsSquare'}
aria-label={'delete'}
size={'xs'}
borderRadius={'md'}
_hover={{ color: 'red.600' }}
onClick={(e) => {
e.stopPropagation();
openConfirm(async () => {
@@ -385,7 +381,7 @@ const DataCard = () => {
))}
{collection?.sourceId && (
<Button
variant={'base'}
variant={'whitePrimary'}
onClick={() => collection.sourceId && getFileAndOpen(collection.sourceId)}
>
{t('core.dataset.collection.metadata.read source')}
@@ -394,7 +390,7 @@ const DataCard = () => {
</DrawerBody>
<DrawerFooter>
<Button variant={'base'} onClick={onClose}>
<Button variant={'whitePrimary'} onClick={onClose}>
{t('common.Close')}
</Button>
</DrawerFooter>

View File

@@ -7,7 +7,8 @@ import {
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper
NumberDecrementStepper,
Input
} from '@chakra-ui/react';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
@@ -18,7 +19,7 @@ import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useImportStore, SelectorContainer, PreviewFileOrChunk } from './Provider';
import { useTranslation } from 'next-i18next';
const fileExtension = '.txt, .docx, .pdf, .md';
const fileExtension = '.txt, .docx, .pdf, .md, .html';
const ChunkImport = () => {
const { t } = useTranslation();
@@ -29,6 +30,7 @@ const ChunkImport = () => {
const {
chunkLen,
setChunkLen,
setCustomSplitChar,
successChunks,
totalChunks,
isUnselectedFile,
@@ -48,15 +50,15 @@ const ChunkImport = () => {
<Box display={['block', 'flex']} h={['auto', '100%']}>
<SelectorContainer fileExtension={fileExtension}>
{/* chunk size */}
<Flex py={4} alignItems={'center'}>
<Box mt={4} alignItems={'center'}>
<Box>
{t('core.dataset.import.Ideal chunk length')}
<MyTooltip label={t('core.dataset.import.Ideal chunk length Tips')} forceShow>
<QuestionOutlineIcon ml={1} />
<QuestionOutlineIcon />
</MyTooltip>
</Box>
<Box
flex={1}
mt={1}
css={{
'& > span': {
display: 'block'
@@ -69,7 +71,6 @@ const ChunkImport = () => {
})}
>
<NumberInput
ml={4}
defaultValue={chunkLen}
min={100}
max={datasetDetail.vectorModel.maxToken}
@@ -87,9 +88,28 @@ const ChunkImport = () => {
</NumberInput>
</MyTooltip>
</Box>
</Flex>
</Box>
{/* custom split char */}
<Box mt={4} alignItems={'center'}>
<Box>
{t('core.dataset.import.Custom split char')}
<MyTooltip label={t('core.dataset.import.Custom split char Tips')} forceShow>
<QuestionOutlineIcon />
</MyTooltip>
</Box>
<Box mt={1}>
<Input
defaultValue={''}
placeholder="\n;======;==SPLIT=="
onChange={(e) => {
setCustomSplitChar(e.target.value);
setReShowRePreview(true);
}}
/>
</Box>
</Box>
{/* price */}
<Flex py={4} alignItems={'center'}>
<Flex mt={4} alignItems={'center'}>
<Box>
{t('core.dataset.import.Estimated Price')}
<MyTooltip
@@ -105,12 +125,23 @@ const ChunkImport = () => {
</Flex>
<Flex mt={3}>
{showRePreview && (
<Button variant={'base'} mr={4} onClick={onReSplitChunks}>
<Button variant={'whitePrimary'} mr={4} onClick={onReSplitChunks}>
{t('core.dataset.import.Re Preview')}
</Button>
)}
<Button isDisabled={uploading} onClick={openConfirm(onclickUpload)}>
{uploading ? <Box>{Math.round((successChunks / totalChunks) * 100)}%</Box> : '确认导入'}
<Button
isDisabled={uploading}
onClick={() => {
onReSplitChunks();
openConfirm(onclickUpload)();
}}
>
{uploading ? (
<Box>{Math.round((successChunks / totalChunks) * 100)}%</Box>
) : (
t('common.Confirm Import')
)}
</Button>
</Flex>
</SelectorContainer>

View File

@@ -58,7 +58,7 @@ const CreateFileModal = ({
/>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={4} onClick={onClose}>
<Button variant={'whiteBase'} mr={4} onClick={onClose}>
{t('common.Close')}
</Button>
<Button isLoading={isLoading} onClick={mutate}>

View File

@@ -7,11 +7,11 @@ import { simpleText } from '@fastgpt/global/common/string/tools';
import {
fileDownload,
readCsvContent,
readTxtContent,
readPdfContent,
readDocContent
} from '@/web/common/file/utils';
import { uploadFiles } from '@/web/common/file/controller';
import { readFileRawText, readMdFile, readHtmlFile } from '@fastgpt/web/common/file/read';
import { getUploadMdImgController, uploadFiles } from '@/web/common/file/controller';
import { Box, Flex, useDisclosure, type BoxProps } from '@chakra-ui/react';
import React, { DragEvent, useCallback, useState } from 'react';
import { useTranslation } from 'next-i18next';
@@ -48,6 +48,7 @@ export interface Props extends BoxProps {
onPushFiles: (files: FileItemType[]) => void;
tipText?: string;
chunkLen?: number;
customSplitChar?: string;
overlapRatio?: number;
fileTemplate?: {
type: string;
@@ -64,6 +65,7 @@ const FileSelect = ({
onPushFiles,
tipText,
chunkLen = 500,
customSplitChar,
overlapRatio,
fileTemplate,
showUrlFetch = true,
@@ -133,7 +135,7 @@ const FileSelect = ({
});
const fileId = filesId[0];
/* csv file */
/* QA csv file */
if (extension === 'csv') {
const { header, data } = await readCsvContent(file);
if (header[0] !== 'index' || header[1] !== 'content') {
@@ -168,8 +170,19 @@ const FileSelect = ({
let text = await (async () => {
switch (extension) {
case 'txt':
return readFileRawText(file);
case 'md':
return readTxtContent(file);
return readMdFile({
file,
uploadImgController: (base64Img) =>
getUploadMdImgController({ base64Img, metadata: { fileId } })
});
case 'html':
return readHtmlFile({
file,
uploadImgController: (base64Img) =>
getUploadMdImgController({ base64Img, metadata: { fileId } })
});
case 'pdf':
return readPdfContent(file);
case 'docx':
@@ -185,7 +198,8 @@ const FileSelect = ({
const { chunks, tokens } = splitText2Chunks({
text,
chunkLen,
overlapRatio
overlapRatio,
customReg: customSplitChar ? [customSplitChar] : []
});
const fileItem: FileItemType = {
@@ -213,7 +227,7 @@ const FileSelect = ({
}
setSelectingText(undefined);
},
[chunkLen, datasetDetail._id, onPushFiles, overlapRatio, t, toast]
[chunkLen, customSplitChar, datasetDetail._id, onPushFiles, overlapRatio, t, toast]
);
// link fetch
const onUrlFetch = useCallback(
@@ -222,7 +236,8 @@ const FileSelect = ({
const { chunks, tokens } = splitText2Chunks({
text: content,
chunkLen,
overlapRatio
overlapRatio,
customReg: customSplitChar ? [customSplitChar] : []
});
return {
id: nanoid(),
@@ -240,7 +255,7 @@ const FileSelect = ({
});
onPushFiles(result);
},
[chunkLen, onPushFiles, overlapRatio]
[chunkLen, customSplitChar, onPushFiles, overlapRatio]
);
// manual create file and copy data
const onCreateFile = useCallback(
@@ -262,7 +277,8 @@ const FileSelect = ({
const { chunks, tokens } = splitText2Chunks({
text: content,
chunkLen,
overlapRatio
overlapRatio,
customReg: customSplitChar ? [customSplitChar] : []
});
onPushFiles([
@@ -281,7 +297,7 @@ const FileSelect = ({
}
]);
},
[chunkLen, datasetDetail._id, onPushFiles, overlapRatio]
[chunkLen, customSplitChar, datasetDetail._id, onPushFiles, overlapRatio]
);
const handleDragEnter = (e: DragEvent<HTMLDivElement>) => {
@@ -352,7 +368,7 @@ const FileSelect = ({
ml: 1,
as: 'span',
cursor: 'pointer',
color: 'blue.600',
color: 'primary.600',
_hover: {
textDecoration: 'underline'
}
@@ -417,7 +433,7 @@ const FileSelect = ({
mt={1}
cursor={'pointer'}
textDecoration={'underline'}
color={'blue.500'}
color={'primary.500'}
fontSize={'12px'}
onClick={() =>
fileDownload({

View File

@@ -43,18 +43,20 @@ type useImportStoreType = {
setSuccessChunks: Dispatch<SetStateAction<number>>;
isUnselectedFile: boolean;
totalChunks: number;
onclickUpload: (e: { prompt?: string }) => void;
onclickUpload: (e?: { prompt?: string }) => void;
onReSplitChunks: () => void;
price: number;
uploading: boolean;
chunkLen: number;
chunkOverlapRatio: number;
setChunkLen: Dispatch<number>;
customSplitChar?: string;
setCustomSplitChar: Dispatch<string>;
showRePreview: boolean;
setReShowRePreview: Dispatch<SetStateAction<boolean>>;
};
const StateContext = createContext<useImportStoreType>({
onclickUpload: function (e: { prompt?: string }): void {
onclickUpload: function (e?: { prompt?: string }): void {
throw new Error('Function not implemented.');
},
uploading: false,
@@ -72,6 +74,10 @@ const StateContext = createContext<useImportStoreType>({
price: 0,
chunkLen: 0,
chunkOverlapRatio: 0,
customSplitChar: undefined,
setCustomSplitChar: function (value: string): void {
throw new Error('Function not implemented.');
},
setChunkLen: function (value: number): void {
throw new Error('Function not implemented.');
},
@@ -123,6 +129,7 @@ const Provider = ({
const [files, setFiles] = useState<FileItemType[]>([]);
const [successChunks, setSuccessChunks] = useState(0);
const [chunkLen, setChunkLen] = useState(defaultChunkLen);
const [customSplitChar, setCustomSplitChar] = useState<string>();
const [previewFile, setPreviewFile] = useState<FileItemType>();
const [showRePreview, setReShowRePreview] = useState(false);
@@ -198,7 +205,8 @@ const Provider = ({
const { chunks, tokens } = splitText2Chunks({
text: file.rawText,
chunkLen,
overlapRatio: chunkOverlapRatio
overlapRatio: chunkOverlapRatio,
customReg: customSplitChar ? [customSplitChar] : []
});
return {
@@ -218,7 +226,7 @@ const Provider = ({
title: getErrText(error, t('core.dataset.import.Set Chunk Error'))
});
}
}, [chunkLen, chunkOverlapRatio, t, toast]);
}, [chunkLen, chunkOverlapRatio, customSplitChar, t, toast]);
const reset = useCallback(() => {
setFiles([]);
@@ -246,6 +254,8 @@ const Provider = ({
onclickUpload,
uploading,
chunkLen,
customSplitChar,
setCustomSplitChar,
chunkOverlapRatio,
setChunkLen,
showRePreview,
@@ -405,7 +415,7 @@ export const SelectorContainer = ({
{...(isUnselectedFile
? {}
: {
maxW: ['auto', '500px']
maxW: ['auto', '450px']
})}
p={[4, 8]}
>
@@ -437,7 +447,7 @@ export const SelectorContainer = ({
position={'relative'}
alignItems={'center'}
_hover={{
bg: 'blue.50',
bg: 'primary.50',
'& .delete': {
display: 'block'
}

View File

@@ -9,7 +9,7 @@ import { useImportStore, SelectorContainer, PreviewFileOrChunk } from './Provide
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useTranslation } from 'next-i18next';
const fileExtension = '.txt, .docx, .pdf, .md';
const fileExtension = '.txt, .docx, .pdf, .md .html';
const QAImport = () => {
const { t } = useTranslation();
@@ -71,11 +71,18 @@ const QAImport = () => {
</Flex>
<Flex mt={3}>
{showRePreview && (
<Button variant={'base'} mr={4} onClick={onReSplitChunks}>
<Button variant={'whitePrimary'} mr={4} onClick={onReSplitChunks}>
{t('core.dataset.import.Re Preview')}
</Button>
)}
<Button isDisabled={uploading} onClick={openConfirm(() => onclickUpload({ prompt }))}>
<Button
isDisabled={uploading}
onClick={() => {
onReSplitChunks();
openConfirm(() => onclickUpload({ prompt }))();
}}
>
{uploading ? (
<Box>{Math.round((successChunks / totalChunks) * 100)}%</Box>
) : (

View File

@@ -80,7 +80,7 @@ const UrlFetchModal = ({
</Box>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={4} onClick={onClose}>
<Button variant={'whiteBase'} mr={4} onClick={onClose}>
{t('common.Close')}
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => mutate(data))}>

View File

@@ -79,7 +79,7 @@ const WebsiteConfigModal = ({
</Box>
</ModalBody>
<ModalFooter>
<Button variant={'base'} onClick={onClose}>
<Button variant={'whiteBase'} onClick={onClose}>
{t('common.Close')}
</Button>
<Button

View File

@@ -187,12 +187,8 @@ const Info = ({ datasetId }: { datasetId: string }) => {
isLoading={btnLoading}
icon={<DeleteIcon />}
aria-label={''}
variant={'outline'}
size={'sm'}
_hover={{
color: 'red.600',
borderColor: 'red.600'
}}
variant={'whiteDanger'}
size={'smSquare'}
onClick={openConfirm(onclickDelete)}
/>
)}

View File

@@ -313,7 +313,7 @@ const InputDataModal = ({
borderColor={'transparent'}
px={0}
_focus={{
borderColor: 'blue.400',
borderColor: 'primary.400',
px: 3
}}
placeholder={t('dataset.data.Index Placeholder')}
@@ -332,7 +332,7 @@ const InputDataModal = ({
border={theme.borders.base}
cursor={'pointer'}
_hover={{
bg: 'blue.50'
bg: 'primary.50'
}}
minH={'100px'}
onClick={() =>
@@ -351,7 +351,7 @@ const InputDataModal = ({
)}
</Box>
<Flex justifyContent={'flex-end'} px={5} mt={4}>
<Button variant={'base'} mr={3} isLoading={loading} onClick={onClose}>
<Button variant={'whitePrimary'} mr={3} isLoading={loading} onClick={onClose}>
{t('common.Close')}
</Button>
<MyTooltip label={collection.canWrite ? '' : t('dataset.data.Can not edit')}>

View File

@@ -102,14 +102,14 @@ const Test = ({ datasetId }: { datasetId: string }) => {
py={4}
borderRight={['none', theme.borders.base]}
>
<Box border={'2px solid'} borderColor={'blue.500'} p={3} mx={4} borderRadius={'md'}>
<Box border={'2px solid'} borderColor={'primary.500'} p={3} mx={4} borderRadius={'md'}>
<Flex alignItems={'center'}>
<Box fontSize={'sm'} fontWeight={'bold'} flex={1}>
<MyIcon mr={2} name={'text'} w={'18px'} h={'18px'} color={'blue.600'} />
<MyIcon mr={2} name={'text'} w={'18px'} h={'18px'} color={'primary.600'} />
{t('core.dataset.test.Test Text')}
</Box>
<Button
variant={'base'}
variant={'whitePrimary'}
leftIcon={<MyIcon name={searchModeData.icon as any} w={'14px'} />}
size={'sm'}
onClick={onOpenSelectMode}

View File

@@ -181,11 +181,10 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
>
<IconButton
mr={3}
icon={<MyIcon name={'backFill'} w={'18px'} color={'blue.500'} />}
icon={<MyIcon name={'backFill'} w={'18px'} color={'primary.500'} />}
bg={'white'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
h={'28px'}
size={'sm'}
size={'smSquare'}
borderRadius={'50%'}
aria-label={''}
/>

View File

@@ -184,7 +184,7 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
{t('common.Close')}
</Button>
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>

View File

@@ -79,7 +79,7 @@ const MoveModal = ({
: {
cursor: 'pointer',
_hover: {
color: 'blue.500'
color: 'primary.500'
},
onClick: () => {
setParentId(item.parentId);

View File

@@ -7,7 +7,8 @@ import {
useDisclosure,
Card,
MenuButton,
Image
Image,
Button
} from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
@@ -131,8 +132,8 @@ const Kb = () => {
);
return (
<PageContainer isLoading={isFetching}>
<Flex pt={3} px={5} alignItems={'center'}>
<PageContainer isLoading={isFetching} insertProps={{ px: [5, '48px'] }}>
<Flex pt={[4, '30px']} alignItems={'center'} justifyContent={'space-between'}>
{/* url path */}
<ParentPaths
paths={paths.map((path, i) => ({
@@ -161,23 +162,14 @@ const Kb = () => {
offset={[-30, 10]}
width={120}
Button={
<MenuButton
_hover={{
color: 'blue.500'
}}
>
<Flex
alignItems={'center'}
border={theme.borders.base}
px={5}
py={2}
borderRadius={'md'}
cursor={'pointer'}
>
<AddIcon mr={2} />
<Box>{t('Create New')}</Box>
</Flex>
</MenuButton>
<Button variant={'primaryOutline'} px={0}>
<MenuButton h={'100%'}>
<Flex alignItems={'center'} px={'20px'}>
<AddIcon mr={2} />
<Box>{t('Create New')}</Box>
</Flex>
</MenuButton>
</Button>
}
menuList={[
{
@@ -203,25 +195,26 @@ const Kb = () => {
)}
</Flex>
<Grid
p={5}
gridTemplateColumns={['1fr', 'repeat(3,1fr)', 'repeat(4,1fr)', 'repeat(5,1fr)']}
py={5}
gridTemplateColumns={['1fr', 'repeat(2,1fr)', 'repeat(3,1fr)', 'repeat(4,1fr)']}
gridGap={5}
userSelect={'none'}
>
{formatDatasets.map((dataset) => (
<Card
<Box
display={'flex'}
flexDirection={'column'}
key={dataset._id}
py={3}
px={5}
cursor={'pointer'}
borderWidth={1.5}
borderColor={dragTargetId === dataset._id ? 'primary.600' : 'borderColor.low'}
bg={'white'}
borderRadius={'lg'}
minH={'130px'}
border={theme.borders.md}
boxShadow={'none'}
position={'relative'}
data-drag-id={dataset.type === DatasetTypeEnum.folder ? dataset._id : undefined}
borderColor={dragTargetId === dataset._id ? 'blue.500' : ''}
draggable
onDragStart={(e) => {
setDragStartId(dataset._id);
@@ -250,8 +243,8 @@ const Kb = () => {
setDragTargetId(undefined);
}}
_hover={{
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)',
borderColor: 'transparent',
borderColor: 'primary.300',
boxShadow: '1.5',
'& .delete': {
display: 'block'
}
@@ -287,7 +280,7 @@ const Kb = () => {
h={'22px'}
borderRadius={'md'}
_hover={{
color: 'blue.500',
color: 'primary.500',
'& .icon': {
bg: 'myGray.100'
}
@@ -419,7 +412,7 @@ const Kb = () => {
<MyIcon mr={1} name={dataset.icon as any} w={'12px'} />
<Box color={'myGray.500'}>{t(dataset.label)}</Box>
</Flex>
</Card>
</Box>
))}
</Grid>
{myDatasets.length === 0 && (

View File

@@ -76,11 +76,11 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
{feConfigs?.systemTitle}
</Box>
<form onSubmit={handleSubmit(onclickFindPassword)}>
<FormControl mt={5} isInvalid={!!errors.username}>
<Box mt={'42px'}>
<FormControl isInvalid={!!errors.username}>
<Input
bg={'myGray.50'}
placeholder="邮箱/手机号"
size={['md', 'lg']}
{...register('username', {
required: '邮箱/手机号不能为空',
pattern: {
@@ -90,41 +90,46 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.username && errors.username.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.username}>
<Flex>
<Input
flex={1}
placeholder="验证码"
size={['md', 'lg']}
{...register('code', {
required: '验证码不能为空'
})}
></Input>
<Button
ml={5}
w={'145px'}
maxW={'50%'}
size={['md', 'lg']}
onClick={onclickSendCode}
isDisabled={codeCountDown > 0}
isLoading={codeSending}
>
{sendCodeText}
</Button>
</Flex>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.code && errors.code.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.password}>
<FormControl
mt={6}
isInvalid={!!errors.code}
display={'flex'}
alignItems={'center'}
position={'relative'}
>
<Input
bg={'myGray.50'}
flex={1}
maxLength={8}
placeholder="验证码"
{...register('code', {
required: '验证码不能为空'
})}
></Input>
<Box
position={'absolute'}
right={3}
zIndex={1}
fontSize={'sm'}
{...(codeCountDown > 0
? {
color: 'myGray.500'
}
: {
color: 'primary.700',
cursor: 'pointer',
onClick: onclickSendCode
})}
>
{sendCodeText}
</Box>
</FormControl>
<FormControl mt={6} isInvalid={!!errors.password}>
<Input
bg={'myGray.50'}
type={'password'}
placeholder="新密码"
size={['md', 'lg']}
placeholder="新密码(4~20位)"
{...register('password', {
required: '密码不能为空',
minLength: {
@@ -137,45 +142,42 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.password && errors.password.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.password2}>
<FormControl mt={6} isInvalid={!!errors.password2}>
<Input
bg={'myGray.50'}
type={'password'}
placeholder="确认密码"
size={['md', 'lg']}
{...register('password2', {
validate: (val) => (getValues('password') === val ? true : '两次密码不一致')
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.password2 && errors.password2.message}
</FormErrorMessage>
</FormControl>
<Button
type="submit"
mt={10}
w={'100%'}
size={['md', 'lg']}
colorScheme="blue"
isLoading={requesting}
onClick={handleSubmit(onclickFindPassword)}
>
</Button>
<Box
float={'right'}
fontSize="sm"
mt={2}
color={'blue.500'}
mb={'50px'}
color={'primary.700'}
cursor={'pointer'}
_hover={{ textDecoration: 'underline' }}
onClick={() => setPageType('login')}
>
</Box>
<Button
type="submit"
mt={5}
w={'100%'}
size={['md', 'lg']}
colorScheme="blue"
isLoading={requesting}
>
</Button>
</form>
</Box>
</>
);
};

View File

@@ -1,5 +1,15 @@
import React, { useState, Dispatch, useCallback, useRef } from 'react';
import { FormControl, Flex, Input, Button, FormErrorMessage, Box, Link } from '@chakra-ui/react';
import {
FormControl,
Flex,
Input,
Button,
Divider,
AbsoluteCenter,
Box,
Link,
useTheme
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { useRouter } from 'next/router';
import { PageTypeEnum } from '@/constants/user';
@@ -12,6 +22,9 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@/components/Icon';
import { customAlphabet } from 'nanoid';
import { getDocPath } from '@/web/common/system/doc';
import Avatar from '@/components/Avatar';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { useTranslation } from 'next-i18next';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
interface Props {
@@ -25,7 +38,9 @@ interface LoginFormType {
}
const LoginForm = ({ setPageType, loginSuccess }: Props) => {
const { t } = useTranslation();
const router = useRouter();
const theme = useTheme();
const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
const { toast } = useToast();
const { setLoginStore } = useSystemStore();
@@ -69,6 +84,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
...(feConfigs?.oauth?.github
? [
{
label: t('support.user.login.Github'),
provider: OAuthEnum.github,
icon: 'gitFill',
redirectUrl: `https://github.com/login/oauth/authorize?client_id=${feConfigs?.oauth?.github}&redirect_uri=${redirectUri}&state=${state.current}&scope=user:email%20read:user`
@@ -78,6 +94,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
...(feConfigs?.oauth?.google
? [
{
label: t('support.user.login.Google'),
provider: OAuthEnum.google,
icon: 'googleFill',
redirectUrl: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${feConfigs?.oauth?.google}&redirect_uri=${redirectUri}&state=${state.current}&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&include_granted_scopes=true`
@@ -90,26 +107,37 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
return (
<>
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
{feConfigs?.systemTitle}
</Box>
<form onSubmit={handleSubmit(onclickLogin)}>
<FormControl mt={8} isInvalid={!!errors.username}>
<Flex alignItems={'center'}>
<Flex
w={['48px', '56px']}
h={['48px', '56px']}
bg={'myGray.25'}
borderRadius={'xl'}
borderWidth={'1.5px'}
borderColor={theme.borderColor.borderColor}
alignItems={'center'}
justifyContent={'center'}
>
<Avatar src={LOGO_ICON} w={'30px'} />
</Flex>
<Box ml={3} fontSize={['2xl', '3xl']} fontWeight={'bold'}>
{feConfigs?.systemTitle}
</Box>
</Flex>
<Box mt={'42px'}>
<FormControl isInvalid={!!errors.username}>
<Input
bg={'myGray.50'}
placeholder={isCommunityVersion ? '使用root用户登录' : '邮箱/手机号/用户名'}
size={['md', 'lg']}
{...register('username', {
required: '邮箱/手机号/用户名不能为空'
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.username && errors.username.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.password}>
<FormControl mt={6} isInvalid={!!errors.password}>
<Input
bg={'myGray.50'}
type={'password'}
size={['md', 'lg']}
placeholder={isCommunityVersion ? 'root密码为你设置的环境变量' : '密码'}
{...register('password', {
required: '密码不能为空',
@@ -119,13 +147,35 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
}
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.password && errors.password.message}
</FormErrorMessage>
</FormControl>
{feConfigs?.docUrl && (
<Box mt={7} fontSize={'sm'}>
使{' '}
<Link
href={getDocPath('/docs/agreement/disclaimer/')}
target={'_blank'}
color={'primary.500'}
>
</Link>
</Box>
)}
<Button
type="submit"
my={6}
w={'100%'}
size={['md', 'lg']}
colorScheme="blue"
isLoading={requesting}
onClick={handleSubmit(onclickLogin)}
>
{t('home.Login')}
</Button>
{feConfigs?.show_register && (
<>
<Flex align={'center'} justifyContent={'space-between'} mt={3} color={'blue.500'}>
<Flex align={'center'} justifyContent={'flex-end'} color={'primary.700'}>
<Box
cursor={'pointer'}
_hover={{ textDecoration: 'underline' }}
@@ -134,6 +184,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
>
?
</Box>
<Box mx={3} h={'16px'} w={'1.5px'} bg={'myGray.250'}></Box>
<Box
cursor={'pointer'}
_hover={{ textDecoration: 'underline' }}
@@ -143,55 +194,49 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
</Box>
</Flex>
{feConfigs?.docUrl && (
<Box textAlign={'center'} mt={2} fontSize={'sm'}>
使{' '}
<Link
href={getDocPath('/docs/agreement/disclaimer/')}
target={'_blank'}
color={'blue.500'}
>
</Link>
</Box>
)}
</>
)}
<Button
type="submit"
mt={5}
w={'100%'}
size={['md', 'lg']}
colorScheme="blue"
isLoading={requesting}
>
</Button>
{/* oauth */}
{feConfigs?.show_register && (
<>
<Flex mt={10} justifyContent={'space-around'} alignItems={'center'}>
<Box mt={'80px'} position={'relative'}>
<Divider />
<AbsoluteCenter bg="white" px="4" color={'myGray.500'}>
or
</AbsoluteCenter>
</Box>
<Box mt={8}>
{oAuthList.map((item) => (
<MyIcon
key={item.provider}
name={item.icon as any}
w={'34px'}
cursor={'pointer'}
color={'myGray.800'}
onClick={() => {
setLoginStore({
provider: item.provider,
lastRoute,
state: state.current
});
router.replace(item.redirectUrl, '_self');
}}
/>
<Box key={item.provider} _notFirst={{ mt: 4 }}>
<Button
variant={'whitePrimary'}
w={'100%'}
h={'42px'}
leftIcon={
<MyIcon
name={item.icon as any}
w={'20px'}
cursor={'pointer'}
color={'myGray.800'}
/>
}
onClick={() => {
setLoginStore({
provider: item.provider,
lastRoute,
state: state.current
});
router.replace(item.redirectUrl, '_self');
}}
>
{item.label}
</Button>
</Box>
))}
</Flex>
</Box>
</>
)}
</form>
</Box>
</>
);
};

View File

@@ -34,7 +34,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
mode: 'onBlur'
});
const { codeSending, sendCodeText, sendCode, codeCountDown } = useSendCode();
const { sendCodeText, sendCode, codeCountDown } = useSendCode();
const onclickSendCode = useCallback(async () => {
const check = await trigger('username');
@@ -90,11 +90,11 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
{feConfigs?.systemTitle}
</Box>
<form onSubmit={handleSubmit(onclickRegister)}>
<FormControl mt={5} isInvalid={!!errors.username}>
<Box mt={'42px'}>
<FormControl isInvalid={!!errors.username}>
<Input
bg={'myGray.50'}
placeholder="邮箱/手机号"
size={['md', 'lg']}
{...register('username', {
required: '邮箱/手机号不能为空',
pattern: {
@@ -104,41 +104,46 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.username && errors.username.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.username}>
<Flex>
<Input
flex={1}
size={['md', 'lg']}
placeholder="验证码"
{...register('code', {
required: '验证码不能为空'
})}
></Input>
<Button
ml={5}
w={'145px'}
maxW={'50%'}
size={['md', 'lg']}
onClick={onclickSendCode}
isDisabled={codeCountDown > 0}
isLoading={codeSending}
>
{sendCodeText}
</Button>
</Flex>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.code && errors.code.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.password}>
<FormControl
mt={6}
isInvalid={!!errors.code}
display={'flex'}
alignItems={'center'}
position={'relative'}
>
<Input
bg={'myGray.50'}
flex={1}
maxLength={8}
placeholder="验证码"
{...register('code', {
required: '验证码不能为空'
})}
></Input>
<Box
position={'absolute'}
right={3}
zIndex={1}
fontSize={'sm'}
{...(codeCountDown > 0
? {
color: 'myGray.500'
}
: {
color: 'primary.700',
cursor: 'pointer',
onClick: onclickSendCode
})}
>
{sendCodeText}
</Box>
</FormControl>
<FormControl mt={6} isInvalid={!!errors.password}>
<Input
bg={'myGray.50'}
type={'password'}
placeholder="密码"
size={['md', 'lg']}
placeholder="密码(4~20位)"
{...register('password', {
required: '密码不能为空',
minLength: {
@@ -151,45 +156,41 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.password && errors.password.message}
</FormErrorMessage>
</FormControl>
<FormControl mt={8} isInvalid={!!errors.password2}>
<FormControl mt={6} isInvalid={!!errors.password2}>
<Input
bg={'myGray.50'}
type={'password'}
placeholder="确认密码"
size={['md', 'lg']}
{...register('password2', {
validate: (val) => (getValues('password') === val ? true : '两次密码不一致')
})}
></Input>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.password2 && errors.password2.message}
</FormErrorMessage>
</FormControl>
<Button
type="submit"
mt={6}
w={'100%'}
size={['md', 'lg']}
colorScheme="blue"
isLoading={requesting}
onClick={handleSubmit(onclickRegister)}
>
</Button>
<Box
float={'right'}
fontSize="sm"
mt={2}
color={'blue.500'}
mb={'50px'}
color={'primary.700'}
cursor={'pointer'}
_hover={{ textDecoration: 'underline' }}
onClick={() => setPageType('login')}
>
</Box>
<Button
type="submit"
mt={5}
w={'100%'}
size={['md', 'lg']}
colorScheme="blue"
isLoading={requesting}
>
</Button>
</form>
</Box>
</>
);
};

View File

@@ -74,59 +74,34 @@ const Login = () => {
px={[0, '10vw']}
>
<Flex
height="100%"
w={'100%'}
maxW={'1240px'}
maxH={['auto', 'max(660px,80vh)']}
backgroundColor={'#fff'}
alignItems={'center'}
justifyContent={'center'}
py={[5, 10]}
px={'5vw'}
borderRadius={isPc ? 'md' : 'none'}
gap={5}
flexDirection={'column'}
w={['100%', 'auto']}
h={['100%', '80%']}
maxH={'700px'}
bg={'white'}
px={['5vw', '88px']}
py={'64px'}
borderRadius={[0, '24px']}
boxShadow={[
'',
'0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 32px 64px -12px rgba(19, 51, 107, 0.20)'
]}
>
{isPc && (
<Image
src={'/icon/loginLeft.svg'}
order={pageType === PageTypeEnum.login ? 0 : 2}
flex={'1 0 0'}
w="0"
maxW={'600px'}
height={'100%'}
maxH={'450px'}
alt=""
loading={'lazy'}
/>
)}
<Box
position={'relative'}
order={1}
flex={`0 0 ${isPc ? '400px' : '100%'}`}
height={'100%'}
border="1px"
borderColor="gray.200"
py={5}
px={10}
borderRadius={isPc ? 'md' : 'none'}
>
<Box w={['100%', '380px']}>
<DynamicComponent type={pageType} />
{feConfigs?.concatMd && (
<Box
fontWeight={'bold'}
color={'blue.600'}
cursor={'pointer'}
position={'absolute'}
right={5}
bottom={3}
onClick={onOpen}
>
</Box>
)}
</Box>
{feConfigs?.concatMd && (
<Box
mt={8}
color={'primary.700'}
cursor={'pointer'}
textAlign={'center'}
onClick={onOpen}
>
</Box>
)}
</Flex>
{isOpen && <CommunityModal onClose={onClose} />}

View File

@@ -7,7 +7,7 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
import dynamic from 'next/dynamic';
import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip';
import { useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider';
import { getFlowStore } from '@/components/core/module/Flow/FlowProvider';
import { filterExportModules, flowNode2Modules } from '@/components/core/module/utils';
import { putUpdatePlugin } from '@/web/core/plugin/api';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
@@ -25,10 +25,11 @@ const Header = ({ plugin, onClose }: Props) => {
const { toast } = useToast();
const { copyData } = useCopyData();
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
const { nodes, edges, onFixView } = useFlowProviderStore();
const [previewModules, setPreviewModules] = React.useState<ModuleItemType[]>();
const flow2ModulesAndCheck = useCallback(() => {
const flow2ModulesAndCheck = useCallback(async () => {
const { nodes, edges } = await getFlowStore();
const modules = flowNode2Modules({ nodes, edges });
// check required connect
@@ -97,7 +98,7 @@ const Header = ({ plugin, onClose }: Props) => {
}
return modules;
}, [edges, nodes, t, toast]);
}, [t, toast]);
const { mutate: onclickSave, isLoading } = useRequest({
mutationFn: (modules: ModuleItemType[]) => {
@@ -121,15 +122,12 @@ const Header = ({ plugin, onClose }: Props) => {
>
<MyTooltip label={t('common.Back')} offset={[10, 10]}>
<IconButton
size={'sm'}
size={'smSquare'}
icon={<MyIcon name={'back'} w={'14px'} />}
borderRadius={'md'}
borderColor={'myGray.300'}
variant={'base'}
variant={'whiteBase'}
aria-label={''}
onClick={() => {
onClose();
onFixView();
}}
/>
</MyTooltip>
@@ -141,8 +139,8 @@ const Header = ({ plugin, onClose }: Props) => {
<IconButton
mr={[3, 6]}
icon={<MyIcon name={'importLight'} w={['14px', '16px']} />}
borderRadius={'lg'}
variant={'base'}
variant={'whitePrimary'}
size={'smSquare'}
aria-label={'save'}
onClick={onOpenImport}
/>
@@ -151,11 +149,11 @@ const Header = ({ plugin, onClose }: Props) => {
<IconButton
mr={[3, 6]}
icon={<MyIcon name={'export'} w={['14px', '16px']} />}
borderRadius={'lg'}
variant={'base'}
size={'smSquare'}
variant={'whitePrimary'}
aria-label={'save'}
onClick={() => {
const modules = flow2ModulesAndCheck();
onClick={async () => {
const modules = await flow2ModulesAndCheck();
if (modules) {
copyData(filterExportModules(modules), t('app.Export Config Successful'));
}
@@ -166,11 +164,11 @@ const Header = ({ plugin, onClose }: Props) => {
<IconButton
mr={[3, 6]}
icon={<MyIcon name={'core/module/previewLight'} w={['14px', '16px']} />}
borderRadius={'lg'}
size={'smSquare'}
aria-label={'save'}
variant={'base'}
onClick={() => {
const modules = flow2ModulesAndCheck();
variant={'whitePrimary'}
onClick={async () => {
const modules = await flow2ModulesAndCheck();
if (modules) {
setPreviewModules(modules);
}
@@ -180,11 +178,11 @@ const Header = ({ plugin, onClose }: Props) => {
<MyTooltip label={t('module.Save Config')}>
<IconButton
icon={<MyIcon name={'save'} w={['14px', '16px']} />}
borderRadius={'lg'}
size={'smSquare'}
isLoading={isLoading}
aria-label={'save'}
onClick={() => {
const modules = flow2ModulesAndCheck();
onClick={async () => {
const modules = await flow2ModulesAndCheck();
if (modules) {
onclickSave(modules);
}

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { useRouter } from 'next/router';
import Header from './Header';
import Flow from '@/components/core/module/Flow';
@@ -21,7 +21,7 @@ const Render = ({ pluginId }: Props) => {
const { t } = useTranslation();
const router = useRouter();
const { toast } = useToast();
const { nodes = [] } = useFlowProviderStore();
const { nodes, initData } = useFlowProviderStore();
const { pluginModuleTemplates, loadPluginTemplates } = usePluginStore();
const moduleTemplates = useMemo(() => {
@@ -71,10 +71,13 @@ const Render = ({ pluginId }: Props) => {
useQuery(['getPlugTemplates'], () => loadPluginTemplates());
useEffect(() => {
initData(JSON.parse(JSON.stringify(pluginDetail?.modules || [])));
}, [pluginDetail?.modules]);
return pluginDetail ? (
<Flow
templates={moduleTemplates}
modules={pluginDetail?.modules || []}
Header={<Header plugin={pluginDetail} onClose={() => router.back()} />}
/>
) : (

View File

@@ -192,14 +192,13 @@ const CreateModal = ({
</Box>
</ModalBody>
<Flex px={5} py={4}>
<Flex px={5} py={4} alignItems={'center'}>
{!!defaultValue.id && (
<IconButton
className="delete"
size={'sm'}
size={'xsSquare'}
icon={<MyIcon name={'delete'} w={'14px'} />}
variant={'base'}
borderRadius={'md'}
variant={'whiteDanger'}
aria-label={'delete'}
_hover={{
bg: 'red.100'
@@ -211,7 +210,7 @@ const CreateModal = ({
/>
)}
<Box flex={1} />
<Button variant={'base'} mr={3} onClick={onClose}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
{t('common.Close')}
</Button>
{!!defaultValue.id ? (

View File

@@ -29,8 +29,8 @@ const MyModules = () => {
});
return (
<PageContainer isLoading={isLoading}>
<Flex pt={3} px={5} alignItems={'center'}>
<PageContainer isLoading={isLoading} insertProps={{ px: [5, '48px'] }}>
<Flex pt={[4, '30px']} alignItems={'center'} justifyContent={'space-between'}>
<Flex flex={1} alignItems={'center'}>
<Image src={'/imgs/module/plugin.svg'} alt={''} mr={2} h={'24px'} />
<Box className="textlg" letterSpacing={1} fontSize={['20px', '24px']} fontWeight={'bold'}>
@@ -39,36 +39,35 @@ const MyModules = () => {
</Flex>
<Button
leftIcon={<AddIcon />}
variant={'base'}
variant={'primaryOutline'}
onClick={() => setEditModalData(defaultForm)}
>
{t('common.New Create')}
</Button>
</Flex>
<Grid
p={5}
gridTemplateColumns={['1fr', 'repeat(3,1fr)', 'repeat(4,1fr)', 'repeat(5,1fr)']}
py={5}
gridTemplateColumns={['1fr', 'repeat(2,1fr)', 'repeat(3,1fr)', 'repeat(4,1fr)']}
gridGap={5}
>
{data.map((plugin) => (
<Card
<Box
key={plugin._id}
py={4}
py={3}
px={5}
cursor={'pointer'}
h={'140px'}
border={theme.borders.md}
boxShadow={'none'}
minH={'140px'}
borderWidth={'1.5px'}
borderColor={'borderColor.low'}
bg={'white'}
borderRadius={'lg'}
userSelect={'none'}
position={'relative'}
_hover={{
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)',
borderColor: 'transparent',
'& .delete': {
display: 'block'
},
'& .chat': {
display: 'block'
borderColor: 'primary.300',
boxShadow: '1.5',
'& .edit': {
display: 'flex'
}
}}
onClick={() => router.push(`/plugin/edit?pluginId=${plugin._id}`)}
@@ -77,18 +76,17 @@ const MyModules = () => {
<Avatar src={plugin.avatar} borderRadius={'md'} w={'28px'} />
<Box ml={3}>{plugin.name}</Box>
<IconButton
className="delete"
className="edit"
position={'absolute'}
top={4}
right={4}
size={'sm'}
size={'smSquare'}
icon={<MyIcon name={'edit'} w={'14px'} />}
variant={'base'}
borderRadius={'md'}
aria-label={'delete'}
variant={'whitePrimary'}
aria-label={'edit'}
display={['', 'none']}
_hover={{
bg: 'blue.100'
bg: 'primary.100'
}}
onClick={(e) => {
e.stopPropagation();
@@ -110,7 +108,7 @@ const MyModules = () => {
>
{plugin.intro || t('plugin.No Intro')}
</Box>
</Card>
</Box>
))}
</Grid>
{data.length === 0 && <EmptyTip />}