diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts
index 0696b6b7fbe..bed30fa3c3e 100644
--- a/packages/web/components/common/Icon/constants.ts
+++ b/packages/web/components/common/Icon/constants.ts
@@ -39,6 +39,8 @@ export const iconPaths = {
'common/inviteLight': () => import('./icons/common/inviteLight.svg'),
'common/language/en': () => import('./icons/common/language/en.svg'),
'common/language/zh': () => import('./icons/common/language/zh.svg'),
+ 'common/language/China': () => import('./icons/common/language/China.svg'),
+ 'common/language/America': () => import('./icons/common/language/America.svg'),
'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'),
'common/line': () => import('./icons/common/line.svg'),
'common/lineChange': () => import('./icons/common/lineChange.svg'),
diff --git a/packages/web/components/common/Icon/icons/common/language/America.svg b/packages/web/components/common/Icon/icons/common/language/America.svg
new file mode 100644
index 00000000000..2e63b188f07
--- /dev/null
+++ b/packages/web/components/common/Icon/icons/common/language/America.svg
@@ -0,0 +1,56 @@
+
\ No newline at end of file
diff --git a/packages/web/components/common/Icon/icons/common/language/China.svg b/packages/web/components/common/Icon/icons/common/language/China.svg
new file mode 100644
index 00000000000..8b01b208972
--- /dev/null
+++ b/packages/web/components/common/Icon/icons/common/language/China.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/packages/web/i18n/en/login.json b/packages/web/i18n/en/login.json
index a8297cf2f6e..7959b85b116 100644
--- a/packages/web/i18n/en/login.json
+++ b/packages/web/i18n/en/login.json
@@ -1,13 +1,16 @@
{
+ "Chinese_ip_tip": "It is detected that you are a mainland Chinese IP, click to jump to visit the mainland China version.",
"Login": "Login",
"forget_password": "Find password",
"login_failed": "Login failed",
"login_success": "Login successful",
+ "no_remind": "Don't remind again",
"password_condition": "Password maximum 60 characters",
"policy_tip": "By useing, you agree to our",
"privacy": "Privacy policy",
+ "redirect": "Jump",
"register": "Register",
"root_password_placeholder": "The root user password is the value of the environment variable DEFAULT_ROOT_PSW",
"terms": "Terms",
"use_root_login": "Log in as root user"
-}
\ No newline at end of file
+}
diff --git a/packages/web/i18n/zh/login.json b/packages/web/i18n/zh/login.json
index c66304e06c8..4b4e36ff2aa 100644
--- a/packages/web/i18n/zh/login.json
+++ b/packages/web/i18n/zh/login.json
@@ -9,5 +9,8 @@
"register": "注册账号",
"root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值",
"terms": "服务协议",
- "use_root_login": "使用 root 用户登录"
-}
\ No newline at end of file
+ "use_root_login": "使用 root 用户登录",
+ "redirect": "跳转",
+ "no_remind": "不再提醒",
+ "Chinese_ip_tip": "检测到您是中国大陆 IP,点击跳转访问中国大陆版。"
+}
diff --git a/projects/app/src/components/Select/I18nLngSelector.tsx b/projects/app/src/components/Select/I18nLngSelector.tsx
new file mode 100644
index 00000000000..42c34a3550b
--- /dev/null
+++ b/projects/app/src/components/Select/I18nLngSelector.tsx
@@ -0,0 +1,40 @@
+import { langMap } from '@/web/common/utils/i18n';
+import { Avatar, Box, Flex } from '@chakra-ui/react';
+import MySelect from '@fastgpt/web/components/common/MySelect';
+import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
+import { useTranslation } from 'next-i18next';
+import { useMemo } from 'react';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+
+const I18nLngSelector = () => {
+ const { i18n } = useTranslation();
+ const { onChangeLng } = useI18nLng();
+
+ const list = useMemo(() => {
+ return Object.entries(langMap).map(([key, lang]) => ({
+ label: (
+
+
+ {lang.label}
+
+ ),
+ value: key
+ }));
+ }, []);
+
+ return (
+ {
+ const lang = val;
+ onChangeLng(lang);
+ }}
+ />
+ );
+};
+
+export default I18nLngSelector;
diff --git a/projects/app/src/pages/account/components/Individuation.tsx b/projects/app/src/pages/account/components/Individuation.tsx
index 2055900fb0f..425e73f47f9 100644
--- a/projects/app/src/pages/account/components/Individuation.tsx
+++ b/projects/app/src/pages/account/components/Individuation.tsx
@@ -7,18 +7,13 @@ import { UserType } from '@fastgpt/global/support/user/type';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user';
-import { langMap } from '@/web/common/utils/i18n';
-import { useRouter } from 'next/router';
-import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
-
-import MySelect from '@fastgpt/web/components/common/MySelect';
import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect';
+import I18nLngSelector from '@/components/Select/I18nLngSelector';
const Individuation = () => {
- const { t, i18n } = useTranslation();
+ const { t } = useTranslation();
const { userInfo, updateUserInfo } = useUserStore();
const { toast } = useToast();
- const { onChangeLng } = useI18nLng();
const { reset } = useForm({
defaultValues: userInfo as UserType
@@ -49,17 +44,7 @@ const Individuation = () => {
{t('common:user.Language')}:
- ({
- label: lang.label,
- value: key
- }))}
- onchange={(val: any) => {
- const lang = val;
- onChangeLng(lang);
- }}
- />
+
diff --git a/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx b/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx
index 7323dbc8c8b..ea83bc4b2c6 100644
--- a/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx
+++ b/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx
@@ -9,6 +9,8 @@ import { useRouter } from 'next/router';
import { Dispatch, useRef } from 'react';
import { useTranslation } from 'next-i18next';
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider';
+import I18nLngSelector from '@/components/Select/I18nLngSelector';
+import { useSystem } from '@fastgpt/web/hooks/useSystem';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
interface Props {
@@ -24,6 +26,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
const state = useRef(nanoid());
const redirectUri = `${location.origin}/login/provider`;
+ const { isPc } = useSystem();
const oAuthList = [
...(feConfigs?.oauth?.wechat && pageType !== LoginPageTypeEnum.wechat
@@ -72,22 +75,25 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
return (
-
-
-
+
+
+
+
+
+
+ {feConfigs?.systemTitle}
+
-
- {feConfigs?.systemTitle}
-
+ {!isPc && }
{children}
{show_oauth && (
diff --git a/projects/app/src/pages/login/index.tsx b/projects/app/src/pages/login/index.tsx
index f719aeb6f6e..3274c623a89 100644
--- a/projects/app/src/pages/login/index.tsx
+++ b/projects/app/src/pages/login/index.tsx
@@ -1,5 +1,15 @@
import React, { useState, useCallback, useEffect } from 'react';
-import { Box, Center, Flex, useDisclosure } from '@chakra-ui/react';
+import {
+ Box,
+ Button,
+ Center,
+ Drawer,
+ DrawerCloseButton,
+ DrawerContent,
+ DrawerOverlay,
+ Flex,
+ useDisclosure
+} from '@chakra-ui/react';
import { LoginPageTypeEnum } from '@/web/support/user/login/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { ResLogin } from '@/global/support/api/userRes.d';
@@ -12,22 +22,31 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
import { clearToken, setToken } from '@/web/support/user/auth';
import Script from 'next/script';
import Loading from '@fastgpt/web/components/common/MyLoading';
-import { useMount } from 'ahooks';
-import { t } from 'i18next';
+import { useLocalStorageState, useMount } from 'ahooks';
+import { useTranslation } from 'next-i18next';
+import I18nLngSelector from '@/components/Select/I18nLngSelector';
+import { useSystem } from '@fastgpt/web/hooks/useSystem';
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
const WechatForm = dynamic(() => import('./components/LoginForm/WechatForm'));
const CommunityModal = dynamic(() => import('@/components/CommunityModal'));
-const Login = () => {
+const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
const router = useRouter();
+ const { t } = useTranslation();
const { lastRoute = '' } = router.query as { lastRoute: string };
const { feConfigs } = useSystemStore();
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}`>();
const { setUserInfo } = useUserStore();
const { setLastChatId, setLastChatAppId } = useChatStore();
const { isOpen, onOpen, onClose } = useDisclosure();
+ const { isPc } = useSystem();
+ const {
+ isOpen: isOpenRedirect,
+ onOpen: onOpenRedirect,
+ onClose: onCloseRedirect
+ } = useDisclosure();
const loginSuccess = useCallback(
(res: ResLogin) => {
@@ -69,6 +88,25 @@ const Login = () => {
router.prefetch('/app/list');
});
+ const [showRedirect, setShowRedirect] = useLocalStorageState('showRedirect', {
+ defaultValue: true
+ });
+
+ const checkIpInChina = useCallback(() => {
+ const onSuccess = (res: any) => {
+ if (!res.country.iso_code) {
+ return;
+ }
+
+ const country = res.country.iso_code.toLowerCase();
+ if (country === 'cn') {
+ onOpenRedirect();
+ }
+ };
+ const onError = (e: any) => console.log(e);
+ geoip2 && geoip2.country(onSuccess, onError);
+ }, [onOpenRedirect]);
+
return (
<>
{feConfigs.googleClientVerKey && (
@@ -76,6 +114,15 @@ const Login = () => {
src={`https://www.recaptcha.net/recaptcha/api.js?render=${feConfigs.googleClientVerKey}`}
>
)}
+
+ {ChineseRedirectUrl && showRedirect && (
+
+ )}
+
{
h={'100%'}
px={[0, '10vw']}
>
+ {isPc && (
+
+
+
+ )}
{
{isOpen && }
+ {showRedirect && (
+ router.push(ChineseRedirectUrl)}
+ disableDrawer={() => setShowRedirect(false)}
+ />
+ )}
>
);
};
+function RedirectDrawer({
+ isOpen,
+ onClose,
+ disableDrawer,
+ onRedirect
+}: {
+ isOpen: boolean;
+ onClose: () => void;
+ disableDrawer: () => void;
+ onRedirect: () => void;
+}) {
+ const { t } = useTranslation();
+ return (
+
+
+
+
+
+
+
+ {t('login:Chinese_ip_tip')}
+
+
+ {t('login:no_remind')}
+
+
+
+
+
+
+ );
+}
+
export async function getServerSideProps(context: any) {
return {
- props: { ...(await serviceSideProps(context, ['app', 'user', 'login'])) }
+ props: {
+ ChineseRedirectUrl: process.env.CHINESE_IP_REDIRECT_URL,
+ ...(await serviceSideProps(context, ['app', 'user', 'login']))
+ }
};
}
diff --git a/projects/app/src/types/index.d.ts b/projects/app/src/types/index.d.ts
index f96bb1827e1..f388be3508a 100644
--- a/projects/app/src/types/index.d.ts
+++ b/projects/app/src/types/index.d.ts
@@ -22,7 +22,7 @@ export type RequestPaging = { pageNum: number; pageSize: number; [key]: any };
declare global {
var qaQueueLen: number;
var vectorQueueLen: number;
-
+ var geoip2: any;
interface Window {
grecaptcha: any;
QRCode: any;
diff --git a/projects/app/src/web/common/utils/i18n.ts b/projects/app/src/web/common/utils/i18n.ts
index 3e2d2102ec6..27f2ab75572 100644
--- a/projects/app/src/web/common/utils/i18n.ts
+++ b/projects/app/src/web/common/utils/i18n.ts
@@ -7,12 +7,14 @@ export enum LangEnum {
}
export const langMap = {
[LangEnum.en]: {
- label: 'English',
- icon: 'common/language/en'
+ label: 'English(US)',
+ icon: 'common/language/en',
+ avatar: 'common/language/America'
},
[LangEnum.zh]: {
label: '简体中文',
- icon: 'common/language/zh'
+ icon: 'common/language/zh',
+ avatar: 'common/language/China'
}
};