This commit is contained in:
熊潇 2025-06-04 15:22:20 +08:00
parent 0b19ccb700
commit 56e3d08869
11 changed files with 177 additions and 15 deletions

View File

@ -27,6 +27,7 @@ let proxy = {
'/client': apiProxy, '/client': apiProxy,
}; };
const basename = isDev ? '' : pkgs.basename;
export default defineConfig({ export default defineConfig({
// ... // ...
// site: 'https://kevisual.xiongxiao.me/root/astro', // site: 'https://kevisual.xiongxiao.me/root/astro',
@ -41,7 +42,7 @@ export default defineConfig({
vite: { vite: {
plugins, plugins,
define: { define: {
BASE_NAME: JSON.stringify(pkgs.basename), BASE_NAME: JSON.stringify(basename),
}, },
server: { server: {
port: 7008, port: 7008,

View File

@ -3,8 +3,8 @@ import { TextEditor } from '@kevisual/markdown-editor/tiptap/editor.ts';
import { Select } from '@/components/a/select.tsx'; import { Select } from '@/components/a/select.tsx';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { getSuggestionItems } from '../ai-chat/editor/suggestion/item'; import { getSuggestionItems } from '../ai-chat/editor/suggestion/item';
import { html2md } from '@kevisual/markdown-editor/tiptap/index.ts'; // import { html2md } from '@kevisual/markdown-editor/tiptap/index.ts';
import { chatId } from '../ai-chat/utils/uuid'; // import { chatId } from '../ai-chat/utils/uuid';
import '../ai-chat/index.css'; import '../ai-chat/index.css';
import { links } from './data/link.ts'; import { links } from './data/link.ts';
// const testImport = async () => { // const testImport = async () => {
@ -44,6 +44,10 @@ export const App = (props: any) => {
return ( return (
<div className='w-full h-full flex flex-col'> <div className='w-full h-full flex flex-col'>
<ShowLinks links={links} /> <ShowLinks links={links} />
<div>
<i>登陆的demo账号: demo 密码为: 123456 </i>
</div>
</div> </div>
); );
}; };

81
src/apps/layout/index.tsx Normal file
View File

@ -0,0 +1,81 @@
import { Layout, Header } from '@/components/b/layouts/main/layout';
import { basename } from '@/modules/basename';
import { queryLogin } from '@/modules/query';
import { useEffect, useState } from 'react';
type Props = {
children?: React.ReactNode;
};
export const AppLayout = (props: Props) => {
return (
<Layout
header={
<Header
title={
<div
className='px-2 cursor-pointer'
onClick={() => {
const url = new URL(window.location.href);
const pathname = url.pathname;
if (pathname === basename + '/') {
return;
}
// 如果当前路径不是根路径,则跳转到根路径
location.href = basename + '/';
}}>
AI Generative
</div>
}
className='bg-background border-b border-b-gray-200 text-foreground'
right={<UserInfo />}
/>
}>
{props.children}
</Layout>
);
};
type User = Awaited<ReturnType<typeof queryLogin.checkLocalUser>>;
export const UserInfo = () => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
init();
}, []);
const init = async () => {
const userInfo = await queryLogin.checkLocalUser();
const token = await queryLogin.getToken();
if (userInfo) {
setUser(userInfo);
} else if (token) {
const userinfo = await queryLogin.getLoginUserByToken(token);
if (userinfo) {
setUser(userinfo);
}
}
};
return (
<div className='flex items-center gap-2'>
{!user && (
<div
onClick={() => {
const currentUrl = new URL(window.location.href);
const loginUrl = '/user/login/';
if (currentUrl.pathname !== loginUrl) {
const newUrl = new URL(loginUrl, window.location.origin);
newUrl.searchParams.set('redirect', currentUrl.pathname + currentUrl.search);
location.href = newUrl.toString();
}
}}>
login
</div>
)}
{user && (
<>
<img src={user?.avatar || '/root/center/panda.jpg'} alt='User Avatar' className='w-8 h-8 rounded-full' />
<span className='text-sm text-gray-700'>{user?.username}</span>
</>
)}
</div>
);
};

View File

@ -237,7 +237,7 @@ export const EditMark = () => {
} }
return <div className='w-full h-full'></div>; return <div className='w-full h-full'></div>;
}; };
export const LayoutMain = (props: { children?: React.ReactNode; expandChildren?: React.ReactNode; open?: boolean }) => { export const LayoutMain = (props: { children?: React.ReactNode; expandChildren?: React.ReactNode; open?: boolean; hasTopTitle?: boolean }) => {
const getDocumentHeight = () => { const getDocumentHeight = () => {
return document.documentElement.scrollHeight; return document.documentElement.scrollHeight;
}; };
@ -261,13 +261,16 @@ export const LayoutMain = (props: { children?: React.ReactNode; expandChildren?:
const isEdit = !!markData; const isEdit = !!markData;
const hasExpandChildren = !!props.expandChildren; const hasExpandChildren = !!props.expandChildren;
const style = useMemo(() => { const style = useMemo(() => {
const top = props.hasTopTitle ? 70 : 0; // Adjust top based on whether there's a title
if (!hasExpandChildren || openMenu) { if (!hasExpandChildren || openMenu) {
return {}; return {
top,
};
} }
return { return {
top: getDocumentHeight() / 2 + 10, top: getDocumentHeight() / 2 + 10 + top,
}; };
}, [getDocumentHeight, hasExpandChildren, openMenu]); }, [getDocumentHeight, hasExpandChildren, openMenu, props.hasTopTitle]);
return ( return (
<div className='w-full h-full flex'> <div className='w-full h-full flex'>
<div className={clsx('absolute top-4 z-10', openMenu ? 'left-4' : '-left-4')} style={style}> <div className={clsx('absolute top-4 z-10', openMenu ? 'left-4' : '-left-4')} style={style}>
@ -313,12 +316,13 @@ export type AppProps = {
children?: React.ReactNode; children?: React.ReactNode;
showSelect?: boolean; showSelect?: boolean;
openMenu?: boolean; openMenu?: boolean;
hasTopTitle?: boolean; // 是否有顶部标题
}; };
export const ProviderManagerName = 'mark-manager'; export const ProviderManagerName = 'mark-manager';
export const App = (props: AppProps) => { export const App = (props: AppProps) => {
return ( return (
<ManagerProvider id={props.managerId}> <ManagerProvider id={props.managerId}>
<LayoutMain expandChildren={props.children} open={props.openMenu}> <LayoutMain expandChildren={props.children} open={props.openMenu} hasTopTitle={props.hasTopTitle}>
<Manager <Manager
markType={props.markType} markType={props.markType}
showSearch={props.showSearch} showSearch={props.showSearch}

View File

@ -0,0 +1,49 @@
import { cn } from '@/lib/utils';
type LayoutProps = {
children?: React.ReactNode;
header?: React.ReactNode;
footer?: React.ReactNode;
mainClassName?: string;
mainStyle?: React.CSSProperties;
};
export const Layout = (props: LayoutProps) => {
return (
<div className='w-full h-full'>
{props.header}
<main
className={cn(props.mainClassName, 'main-content')}
style={{
height: props.header ? 'calc(100vh - 64px)' : '100vh', // Assuming header height is 64px
overflowY: 'auto',
...props.mainStyle,
}}>
{props.children}
</main>
{props.footer}
</div>
);
};
type HeaderProps = {
title?: React.ReactNode;
className?: string;
style?: React.CSSProperties;
right?: React.ReactNode;
left?: React.ReactNode;
center?: React.ReactNode;
children?: React.ReactNode;
};
export const Header = (props: HeaderProps) => {
return (
<header className={cn('h-16 w-full flex items-center', props.className)} style={props.style}>
{props.title}
<div className='grow flex justify-between items-center px-4 '>
{props.left && <div className='flex-1'>{props.left}</div>}
{!props.left && <div>&nbsp;</div>}
{props.center && <div className='flex-1 text-center'>{props.center}</div>}
{props.right && <>{props.right}</>}
</div>
</header>
);
};

View File

@ -16,7 +16,7 @@ import { MarkdownPreview } from '@/components/html/md/Preview';
<Blank> <Blank>
<div class="p-4 h-full"> <div class="p-4 h-full">
<MarkdownPreview> <MarkdownPreview client:only>
<Readme /> <Readme />
</MarkdownPreview> </MarkdownPreview>
</div> </div>

View File

@ -2,8 +2,11 @@
import '@/styles/global.css'; import '@/styles/global.css';
import Blank from '@/components/html/blank.astro'; import Blank from '@/components/html/blank.astro';
import { App as AssistantHome } from '@/apps/assistant-home/index.tsx'; import { App as AssistantHome } from '@/apps/assistant-home/index.tsx';
import { AppLayout } from '@/apps/layout';
--- ---
<Blank> <Blank>
<AssistantHome client:only /> <AppLayout client:only>
<AssistantHome client:only />
</AppLayout>
</Blank> </Blank>

View File

@ -3,8 +3,11 @@ import '@/styles/theme.css';
import '@/styles/global.css'; import '@/styles/global.css';
import Blank from '@/components/html/blank.astro'; import Blank from '@/components/html/blank.astro';
import { App } from '@/apps/mark/manager/Manager'; import { App } from '@/apps/mark/manager/Manager';
import { AppLayout } from '@/apps/layout';
--- ---
<Blank> <Blank>
<App client:only openMenu={true} /> <AppLayout client:only>
<App client:only openMenu={true} hasTopTitle={true}/>
</AppLayout>
</Blank> </Blank>

View File

@ -3,6 +3,7 @@ import '@/styles/theme.css';
import '@/styles/global.css'; import '@/styles/global.css';
import Blank from '@/components/html/blank.astro'; import Blank from '@/components/html/blank.astro';
import { App } from '@/apps/preview/md.tsx'; import { App } from '@/apps/preview/md.tsx';
import { AppLayout } from '@/apps/layout';
--- ---
<link <link
@ -14,5 +15,7 @@ import { App } from '@/apps/preview/md.tsx';
/> />
<Blank> <Blank>
<App client:only /> <AppLayout client:only>
<App client:only />
</AppLayout>
</Blank> </Blank>

View File

@ -16,7 +16,7 @@ export interface Cache {
*/ */
init?: () => Promise<any>; init?: () => Promise<any>;
} }
type User = { export type User = {
avatar?: string; avatar?: string;
description?: string; description?: string;
id?: string; id?: string;

View File

@ -1,7 +1,7 @@
import { Query, BaseQuery } from '@kevisual/query'; import { Query, BaseQuery } from '@kevisual/query';
import type { Result, DataOpts } from '@kevisual/query/query'; import type { Result, DataOpts } from '@kevisual/query/query';
import { setBaseResponse } from '@kevisual/query/query'; import { setBaseResponse } from '@kevisual/query/query';
import { LoginCacheStore, CacheStore } from './login-cache.ts'; import { LoginCacheStore, CacheStore, User } from './login-cache.ts';
import { Cache } from './login-cache.ts'; import { Cache } from './login-cache.ts';
export type QueryLoginOpts = { export type QueryLoginOpts = {
@ -397,8 +397,22 @@ export class QueryLogin extends BaseQuery {
}, },
); );
} }
async getLoginUserByToken(token: string): Promise<User | null> {
const me = await this.getMe(token, false);
if (me.code === 200) {
const user = me.data;
this.cacheStore.setLoginUser({
user,
id: user.id,
accessToken: token,
refreshToken: me.data?.refreshToken || '',
});
return user;
}
return null;
}
/** /**
* * login by web
* @param token * @param token
* @returns * @returns
*/ */