feat: 实现首次登录功能,添加用户名和密码输入,更新状态管理

This commit is contained in:
2025-12-18 03:47:58 +08:00
parent 6e1ffe173a
commit ca1c3706b2
4 changed files with 60 additions and 23 deletions

View File

@@ -1,22 +1,20 @@
import { useEffect } from "react"; import { use, useEffect, useState } from "react";
import { Layout } from "./layout" import { Layout } from "./layout"
import { useStore } from "./store"; import { useStore } from "./store";
export const FirstLogin = () => { export const FirstLogin = () => {
const store = useStore(); const store = useStore();
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
useEffect(() => { useEffect(() => {
store.initAdmin(); store.initAdmin();
}, []); }, []);
const onClickLogin = async () => { useEffect(() => {
const accountInput = document.getElementById('account') as HTMLInputElement; if (store.username) {
const passwordInput = document.getElementById('password') as HTMLInputElement; setUsername(store.username);
const username = accountInput.value;
const password = passwordInput.value;
const res = await store.login(username, password);
if (res.code === 200) {
window.location.reload();
} else {
alert(res.message || '登录失败');
} }
}, [store.username]);
const onClickLogin = async () => {
await store.login(username, password);
} }
return ( return (
<Layout> <Layout>
@@ -27,12 +25,14 @@ export const FirstLogin = () => {
<a className="text-gray-700 mx-1" href="https://kevisual.cn">kevisual</a> "全局设置" <a className="text-gray-700 mx-1" href="https://kevisual.cn">kevisual</a> "全局设置"
<a className="text-gray-700 mx-1 underline" href="../docs/01-login-first/"></a> <a className="text-gray-700 mx-1 underline" href="../docs/01-login-first/"></a>
</blockquote> </blockquote>
<div className='space-y-4 mt-8'> <form className='space-y-4 mt-8'>
<div> <div>
<label htmlFor='account' className='block text-sm font-medium text-gray-900 mb-2'> </label> <label htmlFor='account' className='block text-sm font-medium text-gray-900 mb-2'> </label>
<input <input
type='text' type='text'
id='account' id='account'
onChange={e => setUsername(e.target.value)}
value={username}
placeholder='请输入账号' placeholder='请输入账号'
className='w-full px-4 py-2 border-2 border-black bg-white text-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent' className='w-full px-4 py-2 border-2 border-black bg-white text-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent'
/> />
@@ -42,6 +42,8 @@ export const FirstLogin = () => {
<input <input
type='password' type='password'
id='password' id='password'
onChange={e => setPassword(e.target.value)}
value={password}
placeholder='请输入密码' placeholder='请输入密码'
className='w-full px-4 py-2 border-2 border-black bg-white text-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent' className='w-full px-4 py-2 border-2 border-black bg-white text-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent'
/> />
@@ -52,9 +54,25 @@ export const FirstLogin = () => {
className='w-full px-4 py-2 bg-black text-white font-medium hover:bg-gray-800 active:bg-gray-900 transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2'> className='w-full px-4 py-2 bg-black text-white font-medium hover:bg-gray-800 active:bg-gray-900 transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2'>
</button> </button>
</div> </form>
</div> </div>
</div> </div>
</Layout> </Layout>
); );
}
export const Config = () => {
const store = useStore();
useEffect(() => {
store.initAdmin();
}, []);
return (
<Layout>
<div className="p-4">
<pre className="bg-gray-100 p-4 rounded-lg overflow-x-auto">
{JSON.stringify(store.config, null, 2)}
</pre>
</div>
</Layout>
)
} }

View File

@@ -1,31 +1,42 @@
import { query } from '@/modules/query'; import { query, queryLogin } from '@/modules/query';
import { create } from 'zustand'; import { create } from 'zustand';
type SettingState = { type SettingState = {
username?: string; username?: string;
initAdmin: () => any; initAdmin: () => any;
login: (username: string, password: string) => any; login: (username: string, password: string) => any;
config?: any;
} }
export const useStore = create<SettingState>((set => ({ export const useStore = create<SettingState>((set => ({
username: undefined, username: undefined,
config: undefined,
initAdmin: async () => { initAdmin: async () => {
const res = await query.post({ const res = await query.post({
path: 'config' path: 'config'
}) })
console.log('initAdmin', res); console.log('initAdmin', res);
if (res.code === 200) {
const auth = res.data.auth || {}
if (auth.username) {
set({ username: auth.username });
}
set({ config: res.data });
}
}, },
login: async (username: string, password: string) => { login: async (username: string, password: string) => {
const res = await query.post({ const res = await query.post({
path: 'admin', path: 'admin',
key: 'login', key: 'login',
data: { username,
username, password
password
}
}); });
if (res.code === 200) { if (res.code === 200) {
set({ username }); set({ username });
const setToken = await queryLogin.setLoginToken(res.data)
console.log('setToken', setToken);
} }
console.log('login res', res);
return res; return res;
} }
}))); })));

View File

@@ -1,5 +1,5 @@
import { Query } from '@kevisual/query' import { QueryClient, Query } from '@kevisual/query'
import { QueryLoginBrowser } from '@kevisual/query-login'
const getUrl = () => { const getUrl = () => {
const host = window.location.host const host = window.location.host
const isKevisual = host.includes('kevisual'); const isKevisual = host.includes('kevisual');
@@ -10,6 +10,14 @@ const getUrl = () => {
return '/client/router' return '/client/router'
} }
export const query = new Query({ export const query = new QueryClient({
url: getUrl() url: getUrl()
});
export const remoteQuery = new Query({
url: '/api/router'
});
export const queryLogin = new QueryLoginBrowser({
query: remoteQuery
}); });

View File

@@ -1,8 +1,8 @@
--- ---
import Html from '@/components/html.astro'; import Html from '@/components/html.astro';
import { FirstLogin } from '@/apps/setting/index.tsx'; import { Config } from '@/apps/setting/index.tsx';
--- ---
<Html> <Html>
<FirstLogin client:only /> <Config client:only />
</Html> </Html>