udpate
This commit is contained in:
14
package.json
14
package.json
@@ -17,13 +17,13 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/x": "^2.0.0",
|
"@ant-design/x": "^2.0.1",
|
||||||
"@astrojs/mdx": "^4.3.12",
|
"@astrojs/mdx": "^4.3.12",
|
||||||
"@astrojs/react": "^4.4.2",
|
"@astrojs/react": "^4.4.2",
|
||||||
"@astrojs/sitemap": "^3.6.0",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
"@floating-ui/dom": "^1.7.4",
|
"@floating-ui/dom": "^1.7.4",
|
||||||
"@kevisual/context": "^0.0.4",
|
"@kevisual/context": "^0.0.4",
|
||||||
"@kevisual/kv-login": "^0.0.6",
|
"@kevisual/kv-login": "^0.0.7",
|
||||||
"@kevisual/query": "0.0.29",
|
"@kevisual/query": "0.0.29",
|
||||||
"@kevisual/query-login": "^0.0.7",
|
"@kevisual/query-login": "^0.0.7",
|
||||||
"@kevisual/registry": "^0.0.1",
|
"@kevisual/registry": "^0.0.1",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"astro": "^5.16.3",
|
"astro": "^5.16.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
"lucide-react": "^0.555.0",
|
"lucide-react": "^0.555.0",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.1",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.1",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"zustand": "^5.0.9"
|
"zustand": "^5.0.9"
|
||||||
@@ -56,10 +56,10 @@
|
|||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-basic-ssl": "^2.1.0",
|
"@vitejs/plugin-basic-ssl": "^2.1.0",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.1",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"vite": "^7.2.4"
|
"vite": "^7.2.6"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|||||||
15
packages/kv-login/bun.config.ts
Normal file
15
packages/kv-login/bun.config.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { build } from 'bun';
|
||||||
|
|
||||||
|
await build({
|
||||||
|
entrypoints: ["./src/main.ts"],
|
||||||
|
outdir: './dist',
|
||||||
|
target: 'browser',
|
||||||
|
format: 'esm',
|
||||||
|
naming: {
|
||||||
|
entry: 'app.js',
|
||||||
|
},
|
||||||
|
minify: false,
|
||||||
|
sourcemap: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Build complete: dist/app.js');
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@kevisual/kv-login",
|
"name": "@kevisual/kv-login",
|
||||||
"version": "0.0.6",
|
"version": "0.0.7",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build --config vite-lib.config.ts",
|
"build": "bun bun.config.ts",
|
||||||
|
"postbuild": "dts -i src/main.ts -o app.d.ts",
|
||||||
"build:test": "vite build",
|
"build:test": "vite build",
|
||||||
"prepub": "rm -rf ./dist && pnpm run build:test",
|
"prepub": "rm -rf ./dist && pnpm run build:test",
|
||||||
"pub": "ev deploy ./dist -k kv-login-test -v 0.0.6 -u -y yes"
|
"pub": "ev deploy ./dist -k kv-login-test -v 0.0.6 -u -y yes"
|
||||||
@@ -25,10 +26,11 @@
|
|||||||
"qrcode": "^1.5.4"
|
"qrcode": "^1.5.4"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/kv-login.es.js",
|
".": "./dist/app.js",
|
||||||
"./kv-login.es.js": "./dist/kv-login.es.js",
|
|
||||||
"./kv-login.umd.js": "./dist/kv-login.umd.js",
|
|
||||||
"./types": "./types/index.d.ts"
|
"./types": "./types/index.d.ts"
|
||||||
},
|
},
|
||||||
"types": "./types/index.d.ts"
|
"types": "./types/index.d.ts",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "^1.3.3"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,11 @@ export const redirectHome = () => {
|
|||||||
const href = decodeURIComponent(redirect);
|
const href = decodeURIComponent(redirect);
|
||||||
window.open(href, '_self');
|
window.open(href, '_self');
|
||||||
}
|
}
|
||||||
|
// 从url上清除 code 参数, 清除 state 参数
|
||||||
emit({ type: 'login-success', data: {} });
|
emit({ type: 'login-success', data: {} });
|
||||||
|
setTimeout(() => {
|
||||||
|
clearCode();
|
||||||
|
}, 1500);
|
||||||
}
|
}
|
||||||
export const loginHandle = async (opts: LoginOpts) => {
|
export const loginHandle = async (opts: LoginOpts) => {
|
||||||
const { loginMethod, data, el } = opts
|
const { loginMethod, data, el } = opts
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { defineConfig } from 'vite';
|
|
||||||
|
|
||||||
const entry = './src/main.ts';
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry,
|
|
||||||
name: 'KvLogin',
|
|
||||||
fileName: (format) => `kv-login.${format}.js`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
944
pnpm-lock.yaml
generated
944
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
15
src/apps/home/chat.ts
Normal file
15
src/apps/home/chat.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { query } from '@/modules/query';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
export const postChat = async (question: string) => {
|
||||||
|
const res = await query.post({
|
||||||
|
path: 'noco-life',
|
||||||
|
key: 'chat',
|
||||||
|
payload: { question },
|
||||||
|
});
|
||||||
|
if (res.code === 200) {
|
||||||
|
return res.data?.content;
|
||||||
|
} else {
|
||||||
|
toast.error(res.message || 'Failed to get chat response');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
@@ -2,9 +2,12 @@ import { app } from '../ai';
|
|||||||
|
|
||||||
|
|
||||||
import { Sender, XProvider } from '@ant-design/x';
|
import { Sender, XProvider } from '@ant-design/x';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { postChat } from './chat';
|
||||||
import { Nav } from '../nav';
|
import { Nav } from '../nav';
|
||||||
|
import { ToastContainer } from 'react-toastify';
|
||||||
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
|
import { useHomeStore } from './store';
|
||||||
|
|
||||||
const useFocus = () => {
|
const useFocus = () => {
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -31,17 +34,29 @@ const useFocus = () => {
|
|||||||
return inputRef;
|
return inputRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
const inputRef = useFocus();
|
const inputRef = useFocus();
|
||||||
|
const [content, setContent] = useState<string>('');
|
||||||
|
const { inputValue, setInputValue, isLoading, setLoading } = useHomeStore();
|
||||||
|
|
||||||
|
return <div className='container mx-auto px-4 py-4 md:p-4'>
|
||||||
|
<div className='md:top-20 left-0 mb-4 right-0 w-full mx-auto px-4 md:px-0' ref={inputRef}>
|
||||||
return <div className='container mx-auto p-4'>
|
<Sender
|
||||||
<div className='fixed bottom-8 w-1/2 justify-self-center' ref={inputRef}>
|
allowSpeech
|
||||||
<Sender allowSpeech onSubmit={() => {
|
value={inputValue}
|
||||||
console.log('Submitted');
|
onChange={setInputValue}
|
||||||
}} />
|
loading={isLoading}
|
||||||
|
onSubmit={async (message) => {
|
||||||
|
console.log('Submitted', message);
|
||||||
|
setLoading(true);
|
||||||
|
const res = await postChat(message);
|
||||||
|
setContent(res || ' ');
|
||||||
|
setLoading(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mb-20 md:mb-16 px-2 md:px-0'>
|
||||||
|
{content}
|
||||||
</div>
|
</div>
|
||||||
</div >;
|
</div >;
|
||||||
}
|
}
|
||||||
@@ -66,5 +81,6 @@ export const AppProvider = () => {
|
|||||||
>
|
>
|
||||||
<Nav />
|
<Nav />
|
||||||
<App />
|
<App />
|
||||||
|
<ToastContainer />
|
||||||
</XProvider>;
|
</XProvider>;
|
||||||
}
|
}
|
||||||
27
src/apps/home/store.ts
Normal file
27
src/apps/home/store.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* @title Home Store
|
||||||
|
* @description 管理 home 页面的输入框数据和加载状态
|
||||||
|
* @tags zustand, state-management, input, loading
|
||||||
|
* @createdAt 2025-12-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface HomeState {
|
||||||
|
// 输入框内容
|
||||||
|
inputValue: string;
|
||||||
|
// 加载状态
|
||||||
|
isLoading: boolean;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
setInputValue: (value: string) => void;
|
||||||
|
setLoading: (loading: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useHomeStore = create<HomeState>((set) => ({
|
||||||
|
inputValue: '',
|
||||||
|
isLoading: false,
|
||||||
|
|
||||||
|
setInputValue: (value) => set({ inputValue: value }),
|
||||||
|
setLoading: (loading) => set({ isLoading: loading }),
|
||||||
|
}));
|
||||||
@@ -12,6 +12,7 @@ export const LoginComponent = ({ onLoginSuccess }: { onLoginSuccess: () => void
|
|||||||
const handleLoginSuccess = () => {
|
const handleLoginSuccess = () => {
|
||||||
console.log('监听到登录成功事件,关闭弹窗');
|
console.log('监听到登录成功事件,关闭弹窗');
|
||||||
onLoginSuccess();
|
onLoginSuccess();
|
||||||
|
|
||||||
};
|
};
|
||||||
const loginEmitter = useContextKey('login-emitter')
|
const loginEmitter = useContextKey('login-emitter')
|
||||||
console.log('KvLogin Types:', loginEmitter);
|
console.log('KvLogin Types:', loginEmitter);
|
||||||
@@ -28,23 +29,24 @@ export const LoginComponent = ({ onLoginSuccess }: { onLoginSuccess: () => void
|
|||||||
return (<kv-login><div id="weixinLogin"></div></kv-login>)
|
return (<kv-login><div id="weixinLogin"></div></kv-login>)
|
||||||
}
|
}
|
||||||
export const Nav = () => {
|
export const Nav = () => {
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
||||||
|
|
||||||
const store = useUserStore(useShallow((state) => ({
|
const store = useUserStore(useShallow((state) => ({
|
||||||
user: state.user,
|
user: state.user,
|
||||||
|
open: state.open,
|
||||||
|
setOpen: state.setOpen,
|
||||||
setUser: state.setUser,
|
setUser: state.setUser,
|
||||||
clearUser: state.clearUser,
|
clearUser: state.clearUser,
|
||||||
queryUser: state.queryUser
|
queryUser: state.queryUser,
|
||||||
|
init: state.init,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
store.queryUser();
|
store.queryUser();
|
||||||
|
store.init();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLoginSuccess = () => {
|
const handleLoginSuccess = () => {
|
||||||
// 关闭弹窗
|
store.setOpen(false);
|
||||||
setIsDialogOpen(false);
|
store.queryUser();
|
||||||
// 重新查询用户信息
|
|
||||||
};
|
};
|
||||||
return <header>
|
return <header>
|
||||||
<nav className="bg-black p-4 text-white flex justify-between">
|
<nav className="bg-black p-4 text-white flex justify-between">
|
||||||
@@ -62,7 +64,7 @@ export const Nav = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
<Dialog open={store.open} onOpenChange={store.setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<button className="bg-gray-700 text-white px-3 py-1 rounded hover:bg-gray-600 transition-colors">
|
<button className="bg-gray-700 text-white px-3 py-1 rounded hover:bg-gray-600 transition-colors">
|
||||||
登录
|
登录
|
||||||
|
|||||||
@@ -11,23 +11,50 @@ interface UserState {
|
|||||||
type?: string;
|
type?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
} | null;
|
} | null;
|
||||||
|
open: boolean;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
setUser: (user: UserState['user']) => void;
|
setUser: (user: UserState['user']) => void;
|
||||||
clearUser: () => void;
|
clearUser: () => Promise<void>;
|
||||||
queryUser: () => void;
|
queryUser: () => void;
|
||||||
queryMe: () => void;
|
queryMe: (token?: string) => void;
|
||||||
|
init: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUserStore = create<UserState>((set) => ({
|
export const useUserStore = create<UserState>((set, get) => ({
|
||||||
user: null,
|
user: null,
|
||||||
|
open: false,
|
||||||
|
setOpen: (open) => set({ open }),
|
||||||
setUser: (user) => set({ user }),
|
setUser: (user) => set({ user }),
|
||||||
clearUser: () => set({ user: null }),
|
clearUser: async () => {
|
||||||
|
await queryLogin.logout()
|
||||||
|
set({ user: null });
|
||||||
|
},
|
||||||
queryUser: async () => {
|
queryUser: async () => {
|
||||||
const user = await queryLogin.checkLocalUser();
|
const user = await queryLogin.checkLocalUser();
|
||||||
set({ user });
|
console.log('查询到的用户信息:', user);
|
||||||
|
if (!user) {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (token) {
|
||||||
|
get().queryMe(token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set({ user });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
queryMe: async () => {
|
queryMe: async (token?: string) => {
|
||||||
const user = await queryLogin.getMe();
|
const res = await queryLogin.getMe(token);
|
||||||
set({ user });
|
console.log('获取到的用户信息:', res);
|
||||||
|
if (res.code === 200) {
|
||||||
|
set({ user: res.data || null });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: () => {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
const code = url.searchParams.get('code');
|
||||||
|
const state = url.searchParams.get('state');
|
||||||
|
if (code && state) {
|
||||||
|
set({ open: true })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Query } from "@kevisual/query";
|
import { QueryClient } from "@kevisual/query";
|
||||||
import { QueryLoginBrowser } from '@kevisual/query-login';
|
import { QueryLoginBrowser } from '@kevisual/query-login';
|
||||||
|
|
||||||
export const query = new Query();
|
export const query = new QueryClient();
|
||||||
|
|
||||||
export const queryLogin = new QueryLoginBrowser({
|
export const queryLogin = new QueryLoginBrowser({
|
||||||
query
|
query
|
||||||
})
|
})
|
||||||
export const local = new Query({
|
export const local = new QueryClient({
|
||||||
url: '/client/router'
|
url: '/client/router'
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user