feat(web): login ui

This commit is contained in:
zhaoying
2026-04-28 15:41:40 +08:00
parent 9598bd5905
commit 03d2228d87
11 changed files with 72 additions and 21 deletions

View File

@@ -102,7 +102,7 @@ const Index = () => {
<Flex gap={12} wrap="nowrap" className="rb:w-full! rb:h-full! rb:overflow-y-auto">
<div className="rb:flex-1 rb:min-w-0">
<Flex vertical>
<div className='rb:w-full rb:h-26 rb:p-4 rb:bg-cover rb:bg-[url("@/assets/images/index/index_bg@2x.png")] rb:rounded-xl rb:overflow-hidden'>
<div className='rb:w-full rb:h-26 rb:p-4 rb:bg-cover rb:bg-[url("@/assets/images/index/index_bg.png")] rb:rounded-xl rb:overflow-hidden'>
<div className="rb:font-[MiSans-Bold] rb:font-bold rb:text-white rb:text-[18px] rb:leading-7">
{t('index.spaceTitle')}
</div>

View File

@@ -14,26 +14,30 @@ import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Input, Form, App } from 'antd';
import type { FormProps } from 'antd';
import clsx from 'clsx';
import { useUser, type LoginInfo } from '@/store/user';
import { login } from '@/api/user'
import loginBg from '@/assets/images/login/loginBg.png'
import check from '@/assets/images/login/check.png'
import loginBg from '@/assets/images/login/bg.mp4'
import check from '@/assets/images/login/check.svg'
import email from '@/assets/images/login/email.svg'
import lock from '@/assets/images/login/lock.svg'
import type { LoginForm } from './types';
import { useI18n } from '@/store/locale'
/**
* Input field styling
*/
const inputClassName = "rb:rounded-[8px]! rb:p-[12px]! rb:h-[44px]!"
const inputClassName = "login-input rb:rounded-[8px]! rb:p-[12px]! rb:h-[44px]! rb:bg-transparent! rb:text-[#FFFFFF]! [&_input]:rb:text-[#FFFFFF]! [&_input]:rb:caret-[#FFFFFF]!"
/**
* Login page component
*/const LoginPage: React.FC = () => {
const { t } = useTranslation();
const { clearUserInfo, updateLoginInfo, getUserInfo } = useUser();
const { language } = useI18n()
const [loading, setLoading] = useState(false);
const [canLogin, setCanLogin] = useState(false);
const [form] = Form.useForm<LoginForm>();
const { message } = App.useApp();
@@ -43,6 +47,7 @@ const inputClassName = "rb:rounded-[8px]! rb:p-[12px]! rb:h-[44px]!"
/** Handle login form submission */
const handleLogin: FormProps<LoginForm>['onFinish'] = async (values) => {
if (!canLogin) return;
if (!values.email) {
message.warning(t('login.emailPlaceholder'));
return;
@@ -64,42 +69,46 @@ const inputClassName = "rb:rounded-[8px]! rb:p-[12px]! rb:h-[44px]!"
return (
<div className="rb:min-h-screen rb:flex rb:h-screen">
<div className="rb:min-h-screen rb:flex rb:h-screen rb:bg-[#0A0A0A] rb:text-[#FFFFFF]">
<div className="rb:relative rb:w-1/2 rb:h-screen rb:overflow-hidden">
<img src={loginBg} alt="loginBg" className="rb:w-full rb:h-full rb:object-cover rb:absolute rb:top-1/2 rb:-translate-y-1/2 rb:left-0" />
<div className="rb:absolute rb:top-14 rb:left-16">
<div className="rb:text-[28px] rb:leading-8.25 rb:font-bold rb:font-[AlimamaShuHeiTi,AlimamaShuHeiTi] rb:mb-4">{t('login.title')}</div>
<div className="rb:text-[18px] rb:leading-6.25 rb:font-regular">{t('login.subTitle')}</div>
<video src={loginBg} loop autoPlay muted className="rb:w-full rb:h-full rb:object-cover"></video>
<div className="rb:absolute rb:top-10 rb:left-12">
<div className={clsx("rb:h-8.25 rb:bg-cover", {
"rb:w-89 rb:bg-[url('@/assets/images/login/title_en.png')]": language !== 'zh',
"rb:w-42 rb:bg-[url('@/assets/images/login/title_zh.png')]": language === 'zh'
})}></div>
<div className="rb:text-[18px] rb:text-[rgba(255,255,255,0.7)] rb:leading-6.25 rb:font-regular rb:mt-3">{t('login.subTitle')}</div>
</div>
<div className="rb:absolute rb:bottom-20.25 rb:left-16 rb:grid rb:grid-cols-2 rb:gap-x-30 rb:gap-y-10.75">
{['intelligentMemory', 'instantRecall', 'knowledgeAssociation'].map(key => (
<div key={key} className="rb:flex">
<div className="rb:absolute rb:bottom-14 rb:left-12 rb:right-12 rb:grid rb:grid-cols-2 rb:gap-x-30 rb:gap-y-10.75">
{['intelligentMemory', 'instantRecall', 'knowledgeAssociation'].map((key, index) => (
<div key={key} className={`rb:flex${index === 0 ? ' rb:col-span-2' : ''}`}>
<img src={check} className="rb:w-4 rb:h-4 rb:mr-2 rb:mt-0.75" />
<div className="rb:text-[16px] rb:leading-5.5">
<div className="rb:font-medium">{t(`login.${key}`)}</div>
<div className="rb:text-[#5B6167] rb:text-[14px] rb:leading-5 rb:font-regular! rb:mt-2">{t(`login.${key}Desc`)}</div>
<div className="rb:text-[14px] rb:text-[rgba(255,255,255,0.7)] rb:leading-5 rb:font-regular! rb:mt-2">{t(`login.${key}Desc`)}</div>
</div>
</div>
))}
</div>
</div>
<div className="rb:bg-[#FFFFFF] rb:flex rb:items-center rb:justify-center rb:flex-[1_1_auto]">
<div className="rb:w-100 rb:mx-auto">
<div className="rb:text-center rb:text-[28px] rb:font-semibold rb:leading-8 rb:mb-12">{t('login.welcome')}</div>
<div className="rb:flex rb:items-center rb:justify-center rb:flex-[1_1_auto]">
<div className="rb:w-110 rb:mx-auto">
<div className="rb:text-center rb:text-[24px] rb:font-[MiSans-Bold] rb:font-bold rb:leading-8 rb:mb-12">{t('login.welcome')}</div>
<Form
form={form}
onFinish={handleLogin}
onValuesChange={(_, all) => setCanLogin(!!(all.email && all.password))}
>
<Form.Item name="email" className="rb:mb-5!">
<Form.Item name="email" className="rb:mb-6!">
<Input
prefix={<img src={email} className="rb:w-5 rb:h-5 rb:mr-2" />}
placeholder={t('login.emailPlaceholder')}
className={inputClassName}
/>
</Form.Item>
<Form.Item name="password">
<Form.Item name="password" className="rb:mb-0!">
<Input.Password
prefix={<img src={lock} className="rb:w-5 rb:h-5 rb:mr-2" />}
placeholder={t('login.passwordPlaceholder')}
@@ -111,7 +120,11 @@ const inputClassName = "rb:rounded-[8px]! rb:p-[12px]! rb:h-[44px]!"
block
loading={loading}
htmlType="submit"
className="rb:h-10! rb:rounded-lg! rb:mt-4"
disabled={!canLogin}
className={clsx("rb:h-11.5! rb:rounded-lg! rb:mt-12", {
'rb:hover:bg-[#2d6ef1]! rb:bg-[#155EEF]! rb:border-[#155EEF]!': canLogin,
'rb:bg-[#171719]! rb:border-[#171719]!': !canLogin
})}
>
{t('login.loginIn')}
</Button>

View File

@@ -361,7 +361,7 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () =>
)}
</Flex>
<div>
<div className="rb:font-[MiSans Bold] rb:font-bold rb:text-[16px] rb:leading-5.5">{source.name}</div>
<div className="rb:font-[MiSans-Bold] rb:font-bold rb:text-[16px] rb:leading-5.5">{source.name}</div>
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4.5">{t('tool.availableMcp')} ({mcpTotal})</div>
</div>
</Flex>