mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-04 08:37:14 +00:00
feat: finish login page
This commit is contained in:
@@ -46,8 +46,8 @@ export interface LLMModel {
|
|||||||
extra_args: object;
|
extra_args: object;
|
||||||
api_keys: string[];
|
api_keys: string[];
|
||||||
abilities: string[];
|
abilities: string[];
|
||||||
created_at: string;
|
// created_at: string;
|
||||||
updated_at: string;
|
// updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPipelines {
|
export interface ApiRespPipelines {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class HttpClient {
|
|||||||
// 同步获取Session
|
// 同步获取Session
|
||||||
private getSessionSync() {
|
private getSessionSync() {
|
||||||
// NOT IMPLEMENT
|
// NOT IMPLEMENT
|
||||||
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoicm9ja2NoaW5xQGdtYWlsLmNvbSIsImlzcyI6IkxhbmdCb3QtY29tbXVuaXR5IiwiZXhwIjoyMzUwNDk1NTQ3fQ.d6r0lNGud1OecOLMM-ADDDwiABmek3hkMIFH7ZBkaX4"
|
return localStorage.getItem("token")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拦截器配置
|
// 拦截器配置
|
||||||
@@ -323,7 +323,7 @@ class HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============ User API ============
|
// ============ User API ============
|
||||||
public checkIfInited(): Promise<object> {
|
public checkIfInited(): Promise<{initialized: boolean}> {
|
||||||
return this.get('/api/v1/user/init')
|
return this.get('/api/v1/user/init')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,4 +340,4 @@ class HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const httpClient = new HttpClient()
|
export const httpClient = new HttpClient("https://version-4.langbot.dev")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
.login {
|
.login {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 80%;
|
width: 30%;
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@@ -26,14 +26,6 @@
|
|||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
|
||||||
flex: 1.5;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
border-top-right-radius: 16px;
|
|
||||||
border-bottom-right-radius: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loginForm {
|
.loginForm {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -76,6 +68,10 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
.forgetPassword {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 修改Logo样式,调整位置确保正确对齐 */
|
/* 修改Logo样式,调整位置确保正确对齐 */
|
||||||
|
|||||||
+113
-31
@@ -1,12 +1,62 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Button, Input, Form, Checkbox, Divider } from 'antd';
|
import { Button, Input, Form, Checkbox, Divider } from 'antd';
|
||||||
import { GoogleOutlined, AppleOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
|
import {GoogleOutlined, AppleOutlined, LockOutlined, UserOutlined, QqCircleFilled, QqOutlined} from '@ant-design/icons';
|
||||||
import styles from './login.module.css';
|
import styles from './login.module.css';
|
||||||
import { useState } from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
import {httpClient} from "@/app/infra/http/HttpClient";
|
||||||
|
import '@ant-design/v5-patch-for-react-19';
|
||||||
|
import {useRouter} from "next/navigation";
|
||||||
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [form] = Form.useForm();
|
const router = useRouter();
|
||||||
|
const [form] = Form.useForm<LoginField>();
|
||||||
const [rememberMe, setRememberMe] = useState(false);
|
const [rememberMe, setRememberMe] = useState(false);
|
||||||
|
const [isRegisterMode, setIsRegisterMode] = useState(false);
|
||||||
|
const [isInitialized, setIsInitialized] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getIsInitialized()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
// 检查是否为首次启动项目,只为首次启动的用户提供注册资格
|
||||||
|
function getIsInitialized() {
|
||||||
|
httpClient.checkIfInited().then(res => {
|
||||||
|
setIsInitialized(res.initialized)
|
||||||
|
}).catch(err => {
|
||||||
|
console.log("error at getIsInitialized: ", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFormSubmit(formField: LoginField) {
|
||||||
|
if (isRegisterMode) {
|
||||||
|
handleRegister(formField.email, formField.password);
|
||||||
|
} else {
|
||||||
|
handleLogin(formField.email, formField.password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRegister(username: string, password: string) {
|
||||||
|
httpClient.initUser(username, password).then(res => {
|
||||||
|
console.log("init user success: ", res)
|
||||||
|
}).catch(err => {
|
||||||
|
console.log("init user error: ", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLogin(username: string, password: string) {
|
||||||
|
httpClient.authUser(username, password).then(res => {
|
||||||
|
localStorage.setItem("token", res.token)
|
||||||
|
console.log("login success: ", res)
|
||||||
|
router.push("/home")
|
||||||
|
}).catch(err => {
|
||||||
|
console.log("login error: ", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// 使用 Ant Design 的组件库,使用 antd 的样式
|
// 使用 Ant Design 的组件库,使用 antd 的样式
|
||||||
@@ -18,8 +68,21 @@ export default function Home() {
|
|||||||
{/* left 为注册的表单,需要填入的内容有:邮箱,密码 */}
|
{/* left 为注册的表单,需要填入的内容有:邮箱,密码 */}
|
||||||
<div className={styles.left}>
|
<div className={styles.left}>
|
||||||
<div className={styles.loginForm}>
|
<div className={styles.loginForm}>
|
||||||
<h1 className={styles.title}>欢迎回来</h1>
|
{
|
||||||
<Form form={form} layout="vertical">
|
isRegisterMode &&
|
||||||
|
<h1 className={styles.title}>注册 LangBot 账号</h1>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!isRegisterMode &&
|
||||||
|
<h1 className={styles.title}>欢迎回到 LangBot</h1>
|
||||||
|
}
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={(values) => {
|
||||||
|
handleFormSubmit(values)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="email"
|
name="email"
|
||||||
rules={[
|
rules={[
|
||||||
@@ -54,13 +117,46 @@ export default function Home() {
|
|||||||
>
|
>
|
||||||
30天内自动登录
|
30天内自动登录
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<a href="#">忘记密码?</a>
|
<span>
|
||||||
|
<a href="#" className={`${styles.forgetPassword}`}>忘记密码?</a>
|
||||||
|
{
|
||||||
|
!isRegisterMode &&
|
||||||
|
<a href=""
|
||||||
|
onClick={(event) => {
|
||||||
|
setIsRegisterMode(true)
|
||||||
|
event.preventDefault()
|
||||||
|
}}
|
||||||
|
>去注册?</a>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isRegisterMode &&
|
||||||
|
<a href=""
|
||||||
|
onClick={(event) => {
|
||||||
|
setIsRegisterMode(false)
|
||||||
|
event.preventDefault()
|
||||||
|
}}
|
||||||
|
>去登录</a>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type="primary" size="large" className={styles.loginButton} block>
|
|
||||||
登录
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
className={styles.loginButton}
|
||||||
|
block
|
||||||
|
htmlType="submit"
|
||||||
|
disabled={isRegisterMode && isInitialized}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
isRegisterMode ? (
|
||||||
|
isInitialized ? "暂不提供注册" : "注册"
|
||||||
|
) : "登录"
|
||||||
|
}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
<Divider className={styles.divider}>或</Divider>
|
<Divider className={styles.divider}>或</Divider>
|
||||||
|
|
||||||
<div className={styles.socialLogin}>
|
<div className={styles.socialLogin}>
|
||||||
@@ -68,6 +164,7 @@ export default function Home() {
|
|||||||
className={styles.socialButton}
|
className={styles.socialButton}
|
||||||
icon={<GoogleOutlined />}
|
icon={<GoogleOutlined />}
|
||||||
size="large"
|
size="large"
|
||||||
|
disabled={true}
|
||||||
>
|
>
|
||||||
使用谷歌账号登录
|
使用谷歌账号登录
|
||||||
</Button>
|
</Button>
|
||||||
@@ -76,37 +173,22 @@ export default function Home() {
|
|||||||
<div className={styles.socialLogin}>
|
<div className={styles.socialLogin}>
|
||||||
<Button
|
<Button
|
||||||
className={styles.socialButton}
|
className={styles.socialButton}
|
||||||
icon={<AppleOutlined />}
|
icon={<QqOutlined />}
|
||||||
size="large"
|
size="large"
|
||||||
|
disabled={true}
|
||||||
>
|
>
|
||||||
使用苹果账号登录
|
使用QQ账号登录
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* right 为左侧布局,显示的是应用截图,测试阶段使用 picsum.photos 代替 */}
|
|
||||||
<div className={styles.right}>
|
|
||||||
<img
|
|
||||||
src="https://picsum.photos/800/1000"
|
|
||||||
alt="应用预览"
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
objectFit: 'cover',
|
|
||||||
objectPosition: 'left center'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/* 在右上角添加logo */}
|
|
||||||
<div className={styles.logoContainer}>
|
|
||||||
<img
|
|
||||||
src="https://picsum.photos/100/100"
|
|
||||||
alt="Logo"
|
|
||||||
className={styles.logo}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoginField {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user