增加登陆和注册页面

This commit is contained in:
zhangkuo
2024-07-10 15:47:32 +08:00
parent 88097173ea
commit 77eecb00e7
10 changed files with 13562 additions and 1233 deletions

View File

@@ -1,36 +1,36 @@
.auth-page {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
flex-direction: column;
// .auth-page {
// display: flex;
// justify-content: center;
// align-items: center;
// height: 100%;
// width: 100%;
// flex-direction: column;
.auth-logo {
transform: scale(1.4);
}
// .auth-logo {
// transform: scale(1.4);
// }
.auth-title {
font-size: 24px;
font-weight: bold;
line-height: 2;
}
// .auth-title {
// font-size: 24px;
// font-weight: bold;
// line-height: 2;
// }
.auth-tips {
font-size: 14px;
}
// .auth-tips {
// font-size: 14px;
// }
.auth-input {
margin: 3vh 0;
}
// .auth-input {
// margin: 3vh 0;
// }
.auth-actions {
display: flex;
justify-content: center;
flex-direction: column;
// .auth-actions {
// display: flex;
// justify-content: center;
// flex-direction: column;
button:not(:last-child) {
margin-bottom: 10px;
}
}
}
// button:not(:last-child) {
// margin-bottom: 10px;
// }
// }
// }

View File

@@ -1,97 +1,97 @@
import styles from "./auth.module.scss";
import { IconButton } from "./button";
// import styles from "./auth.module.scss";
// import { IconButton } from "./button";
import { useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { useAccessStore } from "../store";
import Locale from "../locales";
// import { useNavigate } from "react-router-dom";
// import { Path } from "../constant";
// import { useAccessStore } from "../store";
// import Locale from "../locales";
import BotIcon from "../icons/bot.svg";
import { useEffect } from "react";
import { getClientConfig } from "../config/client";
// import BotIcon from "../icons/bot.svg";
// import { useEffect } from "react";
// import { getClientConfig } from "../config/client";
export function AuthPage() {
const navigate = useNavigate();
const accessStore = useAccessStore();
// export function AuthPage() {
// const navigate = useNavigate();
// const accessStore = useAccessStore();
const goHome = () => navigate(Path.Home);
const goChat = () => navigate(Path.Chat);
const resetAccessCode = () => {
accessStore.update((access) => {
access.openaiApiKey = "";
access.accessCode = "";
});
}; // Reset access code to empty string
// const goHome = () => navigate(Path.Home);
// const goChat = () => navigate(Path.Chat);
// const resetAccessCode = () => {
// accessStore.update((access) => {
// access.openaiApiKey = "";
// access.accessCode = "";
// });
// }; // Reset access code to empty string
useEffect(() => {
if (getClientConfig()?.isApp) {
navigate(Path.Settings);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// useEffect(() => {
// if (getClientConfig()?.isApp) {
// navigate(Path.Settings);
// }
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, []);
return (
<div className={styles["auth-page"]}>
<div className={`no-dark ${styles["auth-logo"]}`}>
<BotIcon />
</div>
// return (
// <div className={styles["auth-page"]}>
// <div className={`no-dark ${styles["auth-logo"]}`}>
// <BotIcon />
// </div>
<div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
<div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
// <div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
// <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Auth.Input}
value={accessStore.accessCode}
onChange={(e) => {
accessStore.update(
(access) => (access.accessCode = e.currentTarget.value),
);
}}
/>
{!accessStore.hideUserApiKey ? (
<>
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
value={accessStore.openaiApiKey}
onChange={(e) => {
accessStore.update(
(access) => (access.openaiApiKey = e.currentTarget.value),
);
}}
/>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
value={accessStore.googleApiKey}
onChange={(e) => {
accessStore.update(
(access) => (access.googleApiKey = e.currentTarget.value),
);
}}
/>
</>
) : null}
// <input
// className={styles["auth-input"]}
// type="password"
// placeholder={Locale.Auth.Input}
// value={accessStore.accessCode}
// onChange={(e) => {
// accessStore.update(
// (access) => (access.accessCode = e.currentTarget.value),
// );
// }}
// />
// {!accessStore.hideUserApiKey ? (
// <>
// <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
// <input
// className={styles["auth-input"]}
// type="password"
// placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
// value={accessStore.openaiApiKey}
// onChange={(e) => {
// accessStore.update(
// (access) => (access.openaiApiKey = e.currentTarget.value),
// );
// }}
// />
// <input
// className={styles["auth-input"]}
// type="password"
// placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
// value={accessStore.googleApiKey}
// onChange={(e) => {
// accessStore.update(
// (access) => (access.googleApiKey = e.currentTarget.value),
// );
// }}
// />
// </>
// ) : null}
<div className={styles["auth-actions"]}>
<IconButton
text={Locale.Auth.Confirm}
type="primary"
onClick={goChat}
/>
<IconButton
text={Locale.Auth.Later}
onClick={() => {
resetAccessCode();
goHome();
}}
/>
</div>
</div>
);
}
// <div className={styles["auth-actions"]}>
// <IconButton
// text={Locale.Auth.Confirm}
// type="primary"
// onClick={goChat}
// />
// <IconButton
// text={Locale.Auth.Later}
// onClick={() => {
// resetAccessCode();
// goHome();
// }}
// />
// </div>
// </div>
// );
// }

View File

@@ -25,7 +25,7 @@ import {
} from "react-router-dom";
import { SideBar } from "./sidebar";
import { useAppConfig } from "../store/config";
import { AuthPage } from "./auth";
import { AuthPage } from "./login";
import { getClientConfig } from "../config/client";
import { type ClientApi, getClientApi } from "../client/api";
import { useAccessStore } from "../store";
@@ -126,7 +126,7 @@ function Screen() {
const config = useAppConfig();
const location = useLocation();
const isHome = location.pathname === Path.Home;
const isAuth = location.pathname === Path.Auth;
const isAuth = location.pathname === Path.Login;
const isMobileScreen = useMobileScreen();
const shouldTightBorder =
getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);

View File

@@ -0,0 +1,36 @@
.auth-page {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
flex-direction: column;
.auth-logo {
transform: scale(1.4);
}
.auth-title {
font-size: 24px;
font-weight: bold;
line-height: 2;
}
.auth-tips {
font-size: 14px;
}
.auth-input {
margin: 1vh 0;
}
.auth-actions {
display: flex;
justify-content: center;
flex-direction: column;
button:not(:last-child) {
margin-bottom: 10px;
}
}
}

174
app/components/login.tsx Normal file
View File

@@ -0,0 +1,174 @@
import styles from "./login.module.scss";
import { IconButton } from "./button";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { Path, Login, BASE_URL } from "../constant";
import { useAccessStore } from "../store";
import Locale from "../locales";
import BotIcon from "../icons/bot.svg";
import { getClientConfig } from "../config/client";
import { showToast } from "./ui-lib";
export function AuthPage() {
const navigate = useNavigate();
const accessStore = useAccessStore();
const [isLogin, setIsLogin] = useState(true);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const goHome = () => navigate(Path.Home);
const goChat = () => navigate(Path.Chat);
const resetAccessCode = () => {
accessStore.update((access) => {
access.openaiApiKey = "";
access.accessCode = "";
});
};
useEffect(() => {
if (getClientConfig()?.isApp) {
navigate(Path.Settings);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleLogin = async () => {
try {
const url = BASE_URL + Login.Login;
const formData = new URLSearchParams();
formData.append("username", username);
formData.append("password", password);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: formData.toString(),
});
const data = await response.json();
if (data.status.code === 0 && data.result === true) {
// 登录成功逻辑
accessStore.update((access) => {
access.accessCode = data.accessCode; // 如果需要从响应中获取 accessCode 的话
});
goChat(); // 导航到聊天页面或其他逻辑
} else {
// 登录失败
showToast(Locale.Login.LoginFail);
}
} catch (error) {
console.error("API 请求失败", error);
showToast(Locale.Login.SystemFail);
}
};
const handleRegister = async () => {
try {
const url = BASE_URL + Login.Register;
const formData = new URLSearchParams();
formData.append("username", username);
formData.append("password", password);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: formData.toString(),
});
const data = await response.json();
if (data.status.code === 0 && data.result === true) {
// 注册成功逻辑
showToast(Locale.Login.ResisterSuc);
setIsLogin(true); // 切换到登录状态
} else {
// 注册失败
showToast(Locale.Login.ResisterFail);
}
} catch (error) {
console.error("API 请求失败", error);
showToast(Locale.Login.SystemFail);
}
};
const handleSubmit = () => {
if (isLogin) {
handleLogin();
} else {
if (password !== confirmPassword) {
showToast(Locale.Login.PasswordFail);
return;
}
handleRegister();
}
};
return (
<div className={styles["auth-page"]}>
<div className={`no-dark ${styles["auth-logo"]}`}>
<BotIcon />
</div>
<div className={styles["auth-title"]}>
{isLogin ? Locale.Login.LoginTitle : Locale.Login.RegisterTitle}
</div>
<div className={styles["auth-tips"]}>
{isLogin ? Locale.Login.LoginTips : Locale.Login.RegisterTips}
</div>
<input
className={styles["auth-input"]}
type="text"
placeholder={Locale.Login.Username}
value={username}
onChange={(e) => setUsername(e.currentTarget.value)}
/>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Login.Password}
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
/>
{!isLogin && (
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Login.ConfirmPassword}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
/>
)}
<div className={styles["auth-actions"]}>
<IconButton
text={isLogin ? Locale.Login.Login : Locale.Login.Register}
type="primary"
onClick={handleSubmit}
/>
<IconButton
text={Locale.Login.Switch}
onClick={() => setIsLogin(!isLogin)}
/>
<IconButton
text={Locale.Login.Later}
onClick={() => {
resetAccessCode();
goHome();
}}
/>
</div>
</div>
);
}

View File

@@ -18,13 +18,20 @@ export const BAIDU_BASE_URL = "https://aip.baidubce.com";
export const BAIDU_OATUH_URL = `${BAIDU_BASE_URL}/oauth/2.0/token`;
export const BASE_URL = `http://127.0.0.1:8080`;
export enum Login {
Register = "/login/register",
Login = "/login/username",
}
export enum Path {
Home = "/",
Chat = "/chat",
Settings = "/settings",
NewChat = "/new-chat",
Masks = "/masks",
Auth = "/auth",
Login = "/login",
}
export enum ApiPath {

View File

@@ -8,15 +8,27 @@ const cn = {
Error: {
Unauthorized: isApp
? "检测到无效 API Key请前往[设置](/#/settings)页检查 API Key 是否配置正确。"
: "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。",
: "请先登录后使用,请前往[登录](/#/login)页输入正确的用户名密码",
},
Auth: {
Title: "需要密码",
Tips: "管理员开启了密码验证,请在下方填入访问码",
SubTips: "或者输入你的 OpenAI 或 Google API 密钥",
Input: "在此处填写访问码",
Login: {
Title: "需要登陆",
Confirm: "确认",
Later: "稍后再说",
Switch: "切换注册/登录",
LoginTitle: "用户登录",
RegisterTitle: "用户注册",
LoginTips: "请输入您的用户名和密码登录",
RegisterTips: "请输入您的用户名和密码进行注册",
Username: "用户名",
Password: "密码",
ConfirmPassword: "确认密码",
Login: "登录",
Register: "注册",
LoginFail: "登录失败,请检查用户名和密码",
SystemFail: "请求失败,请重试",
ResisterSuc: "注册成功,请登录",
ResisterFail: "注册失败,请稍后重试",
PasswordFail: "密码前后不一致",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} 条对话`,