update mark

This commit is contained in:
abearxiong 2025-03-29 23:16:44 +08:00
parent 508ec96029
commit a99d9c2322
6 changed files with 124 additions and 37 deletions

View File

@ -17,6 +17,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@kevisual/query-mark": "workspace:*", "@kevisual/query-mark": "workspace:*",
"@kevisual/components": "workspace:*",
"@kevisual/router": "0.0.9", "@kevisual/router": "0.0.9",
"@kevisual/store": "workspace:*", "@kevisual/store": "workspace:*",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
@ -28,6 +29,7 @@
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.54.2",
"react-i18next": "^15.4.1",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"zustand": "^5.0.3" "zustand": "^5.0.3"
}, },

3
src/Module.tsx Normal file
View File

@ -0,0 +1,3 @@
import { App as Manager } from './manager/Manager';
export { Manager };

View File

@ -2,7 +2,7 @@ import { useManagerStore } from './store';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { useShallow } from 'zustand/shallow'; import { useShallow } from 'zustand/shallow';
import { ManagerProvider } from './Provider'; import { ManagerProvider } from './Provider';
import { ChevronDown, ChevronLeft, Edit, Plus, Search, Trash, Menu as MenuIcon, MenuSquare } from 'lucide-react'; import { ChevronDown, ChevronLeft, X, Edit, Plus, Search, Trash, Menu as MenuIcon, MenuSquare } from 'lucide-react';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EditMark as EditMarkComponent } from './edit/Edit'; import { EditMark as EditMarkComponent } from './edit/Edit';
@ -16,17 +16,19 @@ type ManagerProps = {
showAdd?: boolean; showAdd?: boolean;
onClick?: (data?: any) => void; onClick?: (data?: any) => void;
markType?: MarkType; markType?: MarkType;
showSelect?: boolean;
}; };
export const Manager = (props: ManagerProps) => { export const Manager = (props: ManagerProps) => {
const { showSearch = true, showAdd = false, onClick } = props; const { showSearch = true, showAdd = false, onClick, showSelect = true } = props;
const { control } = useForm({ defaultValues: { search: '' } }); const { control } = useForm({ defaultValues: { search: '' } });
const { list, init, setCurrentMarkId, currentMarkId, deleteMark, getMark, setMarkData, pagination, setPagination, getList, search, setSearch } = const { list, init, setCurrentMarkId, currentMarkId, markData, deleteMark, getMark, setMarkData, pagination, setPagination, getList, search, setSearch } =
useManagerStore( useManagerStore(
useShallow((state) => { useShallow((state) => {
return { return {
list: state.list, list: state.list,
init: state.init, init: state.init,
markData: state.markData,
currentMarkId: state.currentMarkId, currentMarkId: state.currentMarkId,
setCurrentMarkId: state.setCurrentMarkId, setCurrentMarkId: state.setCurrentMarkId,
deleteMark: state.deleteMark, deleteMark: state.deleteMark,
@ -87,8 +89,9 @@ export const Manager = (props: ManagerProps) => {
} }
}; };
console.log('list', list.length, pagination.total); console.log('list', list.length, pagination.total);
return ( return (
<div className='w-full h-full p-4 relative'> <div className='w-full h-full p-4 bg-white border-r border-r-gray-200 relative'>
<div className='flex px-4 mb-4 justify-between items-center absolute top-0 left-0 h-[56px] w-full'> <div className='flex px-4 mb-4 justify-between items-center absolute top-0 left-0 h-[56px] w-full'>
<div className='flex ml-12 items-center space-x-2 '> <div className='flex ml-12 items-center space-x-2 '>
<Controller <Controller
@ -122,34 +125,38 @@ export const Manager = (props: ManagerProps) => {
/> />
</div> </div>
<div className={'flex items-center space-x-2'}> <div className={'flex items-center space-x-2'}>
<IconButton onClick={handleClick}> {showSelect && (
<MenuIcon className='w-4 h-4' /> <>
</IconButton> <IconButton onClick={handleClick}>
<Menu <MenuIcon className='w-4 h-4' />
anchorEl={anchorEl} </IconButton>
open={open} <Menu
onClose={handleClose} anchorEl={anchorEl}
onClick={(e) => { open={open}
console.log('e', e); onClose={handleClose}
}}> onClick={(e) => {
{['md', 'mdx', 'wallnote', 'excalidraw'].map((option) => ( console.log('e', e);
<MenuItem
key={option}
value={option}
onClick={() => {
handleMenuItemClick(option);
}}> }}>
{option} {['md', 'mdx', 'wallnote', 'excalidraw'].map((option) => (
</MenuItem> <MenuItem
))} key={option}
</Menu> value={option}
onClick={() => {
handleMenuItemClick(option);
}}>
{option}
</MenuItem>
))}
</Menu>
</>
)}
<button <button
className={clsx( className={clsx(
'text-blue-500 cursor-pointer hover:underline flex items-center p-2 rounded-md hover:bg-blue-100 transition duration-200', 'text-blue-500 cursor-pointer hover:underline flex items-center p-2 rounded-md hover:bg-blue-100 transition duration-200',
showAdd ? '' : 'hidden', showAdd ? '' : 'hidden',
)}> )}>
<Plus <Plus
className={clsx('w-4 h-4 ', currentMarkId ? 'rotate-12' : 'rotate-0')} className={clsx('w-4 h-4 ')}
onClick={() => { onClick={() => {
setCurrentMarkId(''); setCurrentMarkId('');
@ -157,7 +164,7 @@ export const Manager = (props: ManagerProps) => {
id: '', id: '',
title: '', title: '',
description: '', description: '',
markType: 'md' as any, markType: props.markType || ('md' as any),
summary: '', summary: '',
tags: [], tags: [],
link: '', link: '',
@ -165,6 +172,16 @@ export const Manager = (props: ManagerProps) => {
}} }}
/> />
</button> </button>
{markData && (
<button
className='text-blue-500 cursor-pointer hover:underline flex items-center p-2 rounded-md hover:bg-blue-100 transition duration-200'
onClick={() => {
setCurrentMarkId('');
setMarkData(undefined);
}}>
<X className='w-4 h-4 ' />
</button>
)}
</div> </div>
</div> </div>
<div className='mt-[56px] overflow-auto scrollbar' style={{ height: 'calc(100% - 56px)' }}> <div className='mt-[56px] overflow-auto scrollbar' style={{ height: 'calc(100% - 56px)' }}>
@ -259,11 +276,25 @@ 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 }) => { export const LayoutMain = (props: { children?: React.ReactNode; expandChildren?: React.ReactNode }) => {
const [openMenu, setOpenMenu] = useState(false); const [openMenu, setOpenMenu] = useState(false);
const getDocumentHeight = () => {
return document.documentElement.scrollHeight;
};
const markData = useManagerStore((state) => state.markData);
const isEdit = !!markData;
const hasExpandChildren = !!props.expandChildren;
const style = useMemo(() => {
if (!hasExpandChildren || openMenu) {
return {};
}
return {
top: getDocumentHeight() / 2 + 10,
};
}, [getDocumentHeight, hasExpandChildren, openMenu]);
return ( return (
<div className='w-full h-full flex'> <div className='w-full h-full flex'>
<div className='absolute top-4 left-4 z-10'> <div className={clsx('absolute top-4 z-10', openMenu ? 'left-4' : '-left-4')} style={style}>
<Button <Button
variant='contained' variant='contained'
color={openMenu ? 'info' : 'primary'} color={openMenu ? 'info' : 'primary'}
@ -278,9 +309,12 @@ export const LayoutMain = (props: { children?: React.ReactNode }) => {
</Button> </Button>
</div> </div>
<div className={clsx('h-full w-full sm:w-1/3', openMenu ? '' : 'hidden')}>{props.children}</div> <div className={clsx('h-full w-full sm:w-1/3', openMenu ? '' : 'hidden')}>{props.children}</div>
<div className={clsx('h-full hidden sm:block sm:w-2/3', openMenu ? '' : 'hidden')}> {(!props.expandChildren || isEdit) && (
<EditMark /> <div className={clsx('h-full hidden sm:block sm:w-2/3', openMenu ? '' : 'hidden')}>
</div> <EditMark />
</div>
)}
{props.expandChildren && <div className='h-full grow'>{props.expandChildren}</div>}
</div> </div>
); );
}; };
@ -305,12 +339,19 @@ export type AppProps = {
* id, store的id * id, store的id
*/ */
managerId?: string; managerId?: string;
children?: React.ReactNode;
showSelect?: boolean;
}; };
export const App = (props: AppProps) => { export const App = (props: AppProps) => {
return ( return (
<ManagerProvider id={props.managerId}> <ManagerProvider id={props.managerId}>
<LayoutMain> <LayoutMain expandChildren={props.children}>
<Manager markType={props.markType} showSearch={props.showSearch} showAdd={props.showAdd} onClick={props.onClick} /> <Manager
markType={props.markType}
showSearch={props.showSearch}
showAdd={props.showAdd}
onClick={props.onClick}
showSelect={props.showSelect}></Manager>
</LayoutMain> </LayoutMain>
</ManagerProvider> </ManagerProvider>
); );

View File

@ -91,9 +91,20 @@ export const EditMark = () => {
defaultValue={mark?.thumbnail || ''} defaultValue={mark?.thumbnail || ''}
render={({ field }) => <TextField {...field} label={t('thumbnail')} variant='outlined' fullWidth margin='normal' />} render={({ field }) => <TextField {...field} label={t('thumbnail')} variant='outlined' fullWidth margin='normal' />}
/> />
<Button type='submit' variant='contained' color='primary'> <div className='flex gap-2'>
{t('save')} <Button type='submit' variant='contained' color='primary'>
</Button> {t('save')}
</Button>
<Button
variant='contained'
color='secondary'
onClick={() => {
setCurrentMarkId('');
setMarkData(undefined);
}}>
{t('cancel')}
</Button>
</div>
</Box> </Box>
); );
}; };

View File

@ -61,7 +61,7 @@ export const createManagerStore: StateCreator<ManagerStore, [], [], any> = (set,
}, },
updateMark: async (mark: Mark) => { updateMark: async (mark: Mark) => {
const queryMark = get().queryMark; const queryMark = get().queryMark;
const res = await queryMark.updateMark(mark.id, mark); const res = await queryMark.updateMark(mark);
if (res.code === 200) { if (res.code === 200) {
set((state) => { set((state) => {
const oldList = state.list; const oldList = state.list;

30
vite.lib.config.ts Normal file
View File

@ -0,0 +1,30 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import tailwindcss from '@tailwindcss/vite';
import pkgs from './package.json' with { type: 'json' };
const version = pkgs.version || '0.0.1';
const isDev = process.env.NODE_ENV === 'development';
const basename = isDev ? '/' : pkgs?.basename || '/';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
base: basename,
define: {
BASE_NAME: JSON.stringify(basename),
},
build: {
target: 'esnext',
lib: {
entry: path.resolve(__dirname, './src/pages/App.tsx'),
name: 'Mark',
fileName: (format) => `mark.${format}.js`,
},
},
});