This commit is contained in:
2026-04-29 19:05:47 +08:00
parent b98c66ca21
commit 88c2fa5d03
40 changed files with 1781 additions and 2068 deletions
@@ -0,0 +1,199 @@
import { useCallback } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import {
Tree,
List,
Input,
Button,
ButtonGroup,
Dropdown,
Typography,
Toast,
} from '@douyinfe/semi-ui';
import {
IconCopyAdd,
IconDelete,
IconSearch,
} from '@douyinfe/semi-icons';
import { IconModal, IconToken as LabIconToken, IconTreeSelect } from '@douyinfe/semi-icons-lab';
import { useNoteStore } from '../../stores/useNoteStore';
import { useUIStore } from '../../stores/useUIStore';
import noteService from '../../services/note.service';
import type { INote, ITreeNode } from '../../types';
const NAV_ITEMS = ['首页', '设置', '已删除'] as const;
type NavItem = (typeof NAV_ITEMS)[number];
interface NoteTreeProps {
onRefreshTree: () => void;
onNavItemClick: (item: NavItem) => void;
onSearch: () => void;
}
export default function NoteTree({ onRefreshTree, onNavItemClick, onSearch }: NoteTreeProps) {
const { treeData, viewData, currentNoteId, setCurrentNote } = useNoteStore();
const { openDeleteConfirm, setSearchQuery, searchQuery, setActiveView } = useUIStore();
const loadNote = useCallback(
(id: number) => {
noteService.get(id).then(
(res) => {
const note = res.data;
setCurrentNote(note.id, note.title, note.context);
setActiveView('note');
},
() => Toast.error('加载笔记失败'),
);
},
[setCurrentNote, setActiveView],
);
const addNote = useCallback(
(pid: number) => {
noteService.add(pid).then(
() => {
Toast.success('已添加');
onRefreshTree();
},
() => Toast.error('添加失败'),
);
},
[onRefreshTree],
);
const treeStyle: CSSProperties = {
height: 550,
border: '1px solid var(--semi-color-border)',
position: 'sticky',
top: '85px',
};
const renderLabel = (label: ReactNode, item: ITreeNode) => (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography.Text
ellipsis={{ showTooltip: true }}
style={{ width: 'calc(100% - 68px)' }}
>
{label}
</Typography.Text>
<ButtonGroup size="small" theme="borderless">
<Button
size="small"
icon={<IconCopyAdd />}
onClick={(e) => {
addNote(item.key);
e.stopPropagation();
}}
/>
<Button
size="small"
type="warning"
icon={<IconDelete />}
onClick={(e) => {
openDeleteConfirm(item.key, String(label));
e.stopPropagation();
}}
/>
</ButtonGroup>
</div>
);
return (
<>
<br />
<div>
<Input
prefix={<IconSearch />}
showClear
value={searchQuery}
onChange={(v) => setSearchQuery(v)}
onEnterPress={onSearch}
/>
</div>
<h3
style={{
marginBottom: 16,
position: 'sticky',
top: '3px',
paddingLeft: '10px',
paddingTop: '3px',
}}
>
CYY Note
</h3>
<List
bordered
style={{
flexBasis: '100%',
flexShrink: 0,
borderBottom: '1px solid var(--semi-color-border)',
}}
dataSource={NAV_ITEMS as unknown as NavItem[]}
renderItem={(item: NavItem) => (
<div
style={{ margin: 5, border: '1px solid var(--semi-color-border)', cursor: 'pointer' }}
className="list-item"
onClick={() => onNavItemClick(item)}
>
{item === '首页' && <Button type="danger" theme="borderless" icon={<LabIconToken />} style={{ marginRight: 4 }} />}
{item === '设置' && <Button type="danger" theme="borderless" icon={<IconTreeSelect />} style={{ marginRight: 4 }} />}
{item === '已删除' && <Button type="danger" theme="borderless" icon={<IconModal />} style={{ marginRight: 4 }} />}
{item}
</div>
)}
/>
<div
style={{
marginBottom: 16,
marginTop: 16,
textAlign: 'center',
position: 'sticky',
top: '40px',
}}
>
<Button type="secondary" onClick={() => addNote(0)}>
</Button>
&nbsp;&nbsp;
<Dropdown
trigger="click"
showTick
position="bottomLeft"
render={
<Dropdown.Menu>
<Dropdown.Title></Dropdown.Title>
<Dropdown.Divider />
{viewData.map((item: INote) => (
<Dropdown.Item
key={item.id}
type="primary"
onClick={() => loadNote(item.id)}
>
{item.title}
</Dropdown.Item>
))}
</Dropdown.Menu>
}
>
<Button type="secondary"></Button>
</Dropdown>
</div>
<Tree
filterTreeNode
searchPlaceholder=" "
showFilteredOnly
directory
virtualize
value={currentNoteId}
treeData={treeData as unknown as Parameters<typeof Tree>[0]['treeData']}
renderLabel={renderLabel as Parameters<typeof Tree>[0]['renderLabel']}
style={treeStyle}
onSelect={(id) => loadNote(id as unknown as number)}
/>
</>
);
}