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,
};
const basename = isDev ? '' : pkgs.basename;
export default defineConfig({
// ...
// site: 'https://kevisual.xiongxiao.me/root/astro',
@ -41,7 +42,7 @@ export default defineConfig({
vite: {
plugins,
define: {
BASE_NAME: JSON.stringify(pkgs.basename),
BASE_NAME: JSON.stringify(basename),
},
server: {
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 { useEffect, useRef, useState } from 'react';
import { getSuggestionItems } from '../ai-chat/editor/suggestion/item';
import { html2md } from '@kevisual/markdown-editor/tiptap/index.ts';
import { chatId } from '../ai-chat/utils/uuid';
// import { html2md } from '@kevisual/markdown-editor/tiptap/index.ts';
// import { chatId } from '../ai-chat/utils/uuid';
import '../ai-chat/index.css';
import { links } from './data/link.ts';
// const testImport = async () => {
@ -44,6 +44,10 @@ export const App = (props: any) => {
return (
<div className='w-full h-full flex flex-col'>
<ShowLinks links={links} />
<div>
<i>登陆的demo账号: demo 密码为: 123456 </i>
</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>;
};
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 = () => {
return document.documentElement.scrollHeight;
};
@ -261,13 +261,16 @@ export const LayoutMain = (props: { children?: React.ReactNode; expandChildren?:
const isEdit = !!markData;
const hasExpandChildren = !!props.expandChildren;
const style = useMemo(() => {
const top = props.hasTopTitle ? 70 : 0; // Adjust top based on whether there's a title
if (!hasExpandChildren || openMenu) {
return {};
return {
top,
};
}
return {
top: getDocumentHeight() / 2 + 10,
top: getDocumentHeight() / 2 + 10 + top,
};
}, [getDocumentHeight, hasExpandChildren, openMenu]);
}, [getDocumentHeight, hasExpandChildren, openMenu, props.hasTopTitle]);
return (
<div className='w-full h-full flex'>
<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;
showSelect?: boolean;
openMenu?: boolean;
hasTopTitle?: boolean; // 是否有顶部标题
};
export const ProviderManagerName = 'mark-manager';
export const App = (props: AppProps) => {
return (
<ManagerProvider id={props.managerId}>
<LayoutMain expandChildren={props.children} open={props.openMenu}>
<LayoutMain expandChildren={props.children} open={props.openMenu} hasTopTitle={props.hasTopTitle}>
<Manager
markType={props.markType}
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>
<div class="p-4 h-full">
<MarkdownPreview>
<MarkdownPreview client:only>
<Readme />
</MarkdownPreview>
</div>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import { Query, BaseQuery } from '@kevisual/query';
import type { Result, DataOpts } 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';
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
* @returns
*/