mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 02:12:38 +00:00
V4.6.6-1 (#656)
This commit is contained in:
@@ -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(() => {
|
||||
|
@@ -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>
|
||||
|
@@ -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')}: </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>
|
||||
|
@@ -59,7 +59,7 @@ const BillTable = () => {
|
||||
w={'5px'}
|
||||
h={'5px'}
|
||||
borderRadius={'10px'}
|
||||
bg={'myRead.600'}
|
||||
bg={'red.600'}
|
||||
position={'absolute'}
|
||||
bottom={'8px'}
|
||||
right={'8px'}
|
||||
|
@@ -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))}>
|
||||
|
@@ -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}
|
||||
|
@@ -93,7 +93,7 @@ const Promotion = () => {
|
||||
</Flex>
|
||||
<Button
|
||||
mt={4}
|
||||
variant={'base'}
|
||||
variant={'whitePrimary'}
|
||||
fontSize={'sm'}
|
||||
onClick={() => {
|
||||
copyData(`${location.origin}/?hiId=${userInfo?._id}`);
|
||||
|
@@ -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))}>
|
||||
|
@@ -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}/分钟 |`
|
||||
: ''
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
33
projects/app/src/pages/api/common/system/refreshConfig.ts
Normal file
33
projects/app/src/pages/api/common/system/refreshConfig.ts
Normal 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);
|
||||
}
|
@@ -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`);
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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}>
|
||||
|
@@ -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);
|
||||
}}
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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) => {
|
||||
|
@@ -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={''}
|
||||
/>
|
||||
|
@@ -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))}>
|
||||
|
@@ -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>
|
||||
|
@@ -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}`)}
|
||||
>
|
||||
|
@@ -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 ? (
|
||||
|
@@ -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={''}
|
||||
/>
|
||||
|
@@ -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={''}
|
||||
/>
|
||||
|
@@ -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 ? (
|
||||
|
@@ -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 ? (
|
||||
|
@@ -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'
|
||||
};
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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]}
|
||||
|
@@ -71,7 +71,7 @@ const Navbar = () => {
|
||||
borderRadius: 'xl',
|
||||
fontSize: 'lg',
|
||||
_hover: {
|
||||
bg: 'myGray.100'
|
||||
bg: 'myGray.200'
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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}>
|
||||
|
@@ -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'
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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}>
|
||||
|
@@ -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({
|
||||
|
@@ -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'
|
||||
}
|
||||
|
@@ -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>
|
||||
) : (
|
||||
|
@@ -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))}>
|
||||
|
@@ -79,7 +79,7 @@ const WebsiteConfigModal = ({
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} onClick={onClose}>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
|
@@ -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)}
|
||||
/>
|
||||
)}
|
||||
|
@@ -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')}>
|
||||
|
@@ -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}
|
||||
|
@@ -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={''}
|
||||
/>
|
||||
|
@@ -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))}>
|
||||
|
@@ -79,7 +79,7 @@ const MoveModal = ({
|
||||
: {
|
||||
cursor: 'pointer',
|
||||
_hover: {
|
||||
color: 'blue.500'
|
||||
color: 'primary.500'
|
||||
},
|
||||
onClick: () => {
|
||||
setParentId(item.parentId);
|
||||
|
@@ -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 && (
|
||||
|
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -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} />}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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()} />}
|
||||
/>
|
||||
) : (
|
||||
|
@@ -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 ? (
|
||||
|
@@ -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 />}
|
||||
|
Reference in New Issue
Block a user