= ({ x, y, onClose }) => {
+ const reactFlowInstance = useReactFlow();
+ const store = useStore((state) => state);
+ const wallStore = useWallStore(
+ useShallow((state) => {
+ return {
+ setNodes: state.setNodes,
+ saveNodes: state.saveNodes,
+ };
+ }),
+ );
+ // const
+ const menuList: MenuItem[] = [
+ {
+ label: '粘贴',
+ icon: ,
+ key: 'paste',
+ onClick: async () => {
+ const readList = await clipboardRead();
+ const check = new HasTypeCheck(readList);
+ if (readList.length <= 0) {
+ message.error('粘贴为空');
+ return;
+ }
+ let content: string = '';
+ let hasContent = false;
+ const text = check.getText();
+ let width = 100;
+ let height = 100;
+ if (text.code === 200) {
+ content = text.data;
+ hasContent = true;
+ width = min([content.length * 16, 600])!;
+ height = max([200, (content.length * 16) / 400])!;
+ }
+ console.log('result', readList);
+ if (!hasContent) {
+ const json = check.getJson();
+ if (json.code === 200) {
+ content = JSON.stringify(json.data, null, 2);
+ hasContent = true;
+ }
+ }
+ let noEdit = false;
+ if (!hasContent) {
+ content = readList[0].data || '';
+ const base64 = readList[0].base64;
+ const rect = await getImageWidthHeightByBase64(base64);
+ width = rect.width;
+ height = rect.height;
+ noEdit = true;
+ }
+
+ const flowPosition = reactFlowInstance.screenToFlowPosition({ x, y });
+ const nodes = store.nodes;
+ const newNodeData: any = {
+ id: randomId(),
+ type: 'wallnote',
+ position: flowPosition,
+ data: {
+ width,
+ height,
+ html: content,
+ },
+ };
+ if (noEdit) {
+ newNodeData.data.noEdit = true;
+ }
+ const newNodes = [newNodeData];
+ const _nodes = [...nodes, ...newNodes];
+ wallStore.setNodes(_nodes);
+ wallStore.saveNodes(_nodes);
+ // reactFlowInstance.setNodes(_nodes);
+ },
+ }, //
+ ];
+ return (
+
+ {menuList.map((item) => (
+
{
+ item.onClick?.();
+ }}>
+ {item.children ? (
+ <>{item.children}>
+ ) : (
+ <>
+ {item.icon}
+ {item.label}
+ >
+ )}
+
+ ))}
+
+ );
+};
+
+export default ContextMenu;
diff --git a/src/pages/wall/modules/CustomNode.tsx b/src/pages/wall/modules/CustomNode.tsx
index c716046..12f95a0 100644
--- a/src/pages/wall/modules/CustomNode.tsx
+++ b/src/pages/wall/modules/CustomNode.tsx
@@ -34,7 +34,7 @@ const ShowContent = (props: { data: WallData; selected: boolean }) => {
return (
{
+ wallStore.saveNodes(nodes);
+ };
const store = useStore((state) => {
return {
updateWallRect: (id: string, rect: { width: number; height: number }) => {
@@ -66,7 +69,7 @@ export const CustomNode = (props: { id: string; data: WallData; selected: boolea
return node;
});
state.setNodes(nodes);
- wallStore.saveNodes(nodes);
+ save(nodes);
},
getNode: (id: string) => {
return state.nodes.find((node) => node.id === id);
@@ -74,21 +77,22 @@ export const CustomNode = (props: { id: string; data: WallData; selected: boolea
deleteNode: (id: string) => {
const nodes = state.nodes.filter((node) => node.id !== id);
state.setNodes(nodes);
- wallStore.saveNodes(nodes);
+ console.log('save', nodes, id);
+ save(nodes);
},
};
});
- useEffect(() => {
- if (selected) {
- const handleDelete = (e: KeyboardEvent) => {
- if (e.key === 'Delete') {
- store.deleteNode(props.id);
- }
- };
- window.addEventListener('keydown', handleDelete);
- return () => window.removeEventListener('keydown', handleDelete);
- }
- }, [selected]);
+ // useEffect(() => {
+ // if (selected) {
+ // const handleDelete = (e: KeyboardEvent) => {
+ // if (e.key === 'Delete') {
+ // store.deleteNode(props.id);
+ // }
+ // };
+ // window.addEventListener('keydown', handleDelete);
+ // return () => window.removeEventListener('keydown', handleDelete);
+ // }
+ // }, [selected]);
const width = data.width || 100;
const height = data.height || 100;
const style: React.CSSProperties = {};
@@ -96,7 +100,12 @@ export const CustomNode = (props: { id: string; data: WallData; selected: boolea
style.height = height;
const showOpen = () => {
const node = store.getNode(props.id);
+ console.log('node eidt', node);
if (node) {
+ if (node.data?.noEdit) {
+ message.error('不支持编辑');
+ return;
+ }
wallStore.setOpen(true);
wallStore.setSelectedNode(node);
} else {
@@ -146,5 +155,5 @@ export const CustomNode = (props: { id: string; data: WallData; selected: boolea
};
export const WallNoteNode = memo(CustomNode);
export const CustomNodeType = {
- wall: WallNoteNode,
+ wallnote: WallNoteNode,
};
diff --git a/src/pages/wall/modules/Drawer.tsx b/src/pages/wall/modules/Drawer.tsx
index e7eefab..eb929f4 100644
--- a/src/pages/wall/modules/Drawer.tsx
+++ b/src/pages/wall/modules/Drawer.tsx
@@ -61,11 +61,15 @@ const Drawer = () => {
const newNodes = storeApi.getState().nodes.map((node) => (node.id === selectedNode.id ? selectedNode : node));
storeApi.setState({ nodes: newNodes });
if (wallStore.id) {
- message.success('保存成功', {
+ message.success('保存到服务器成功', {
+ closeOnClick: true,
+ });
+ } else {
+ message.success('保存到本地成功', {
closeOnClick: true,
});
}
- wallStore.saveNodes(newNodes);
+ wallStore.saveNodes(newNodes, { showMessage: false });
}
};
let html = selectedNode?.data?.html || '';
diff --git a/src/pages/wall/modules/FormDialog.tsx b/src/pages/wall/modules/FormDialog.tsx
index 6a5c6e9..7cb30b3 100644
--- a/src/pages/wall/modules/FormDialog.tsx
+++ b/src/pages/wall/modules/FormDialog.tsx
@@ -104,18 +104,21 @@ export const SaveModal = () => {
description: values.description,
summary: values.summary,
tags: values.tags,
- markType: 'wall' as 'wall',
+ markType: 'wallnote' as 'wallnote',
data,
};
- const res = await userWallStore.saveWall(fromData, { refresh: true });
+ const loading = message.loading('保存中...');
+ const res = await userWallStore.saveWall(fromData, { refresh: false });
+ message.close(loading);
if (res.code === 200) {
setShowFormDialog(false);
if (!id) {
// 新创建
const data = res.data;
+ message.info('redirect to edit page');
wallStore.clear();
setTimeout(() => {
- navigate(`/wall/${data.id}`);
+ navigate(`/edit/${data.id}`);
}, 2000);
} else {
// 编辑
diff --git a/src/pages/wall/modules/toolbar/Toolbar.tsx b/src/pages/wall/modules/toolbar/Toolbar.tsx
index c2939b7..cea820e 100644
--- a/src/pages/wall/modules/toolbar/Toolbar.tsx
+++ b/src/pages/wall/modules/toolbar/Toolbar.tsx
@@ -1,4 +1,4 @@
-import { PanelTopOpen, PanelTopClose, Save, Download, Upload, User, Trash, Plus } from 'lucide-react';
+import { PanelTopOpen, PanelTopClose, Save, Download, Upload, User, Trash, Plus, BrickWall } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { useWallStore } from '../../store/wall';
@@ -26,6 +26,14 @@ export const ToolbarItem = ({
);
};
+export type MenuItem = {
+ label: string;
+ key: string;
+ icon?: React.ReactNode;
+ children?: React.ReactNode;
+ className?: string;
+ onClick: () => any;
+};
// 空白处点击,当不包函toolbar时候,关闭toolbar
export const useBlankClick = () => {
const { setToolbarOpen } = useWallStore(
@@ -61,14 +69,7 @@ export const ToolbarContent = ({ open }) => {
const store = useStore((state) => state);
const hasLogin = !!userWallStore.user;
const navigate = useNavigate();
- type MenuItem = {
- label: string;
- key: string;
- icon?: React.ReactNode;
- children?: React.ReactNode;
- className?: string;
- onClick: () => any;
- };
+
const menuList: MenuItem[] = [
{
label: '导出',
@@ -97,7 +98,7 @@ export const ToolbarContent = ({ open }) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
- reader.onload = (e) => {
+ reader.onload = async (e) => {
const data = e.target?.result;
const json = JSON.parse(data as string);
const keys = ['id', 'type', 'position', 'data'];
@@ -108,7 +109,10 @@ export const ToolbarContent = ({ open }) => {
});
const _nodes = [...nodes, ...newNodes];
store.setNodes(_nodes);
- wallStore.saveNodes(_nodes);
+ // window.location.reload();
+ wallStore.setNodes(_nodes);
+ await wallStore.saveNodes(_nodes);
+ message.success('导入成功');
} else {
message.error('文件格式错误');
}
@@ -139,7 +143,16 @@ export const ToolbarContent = ({ open }) => {
},
},
];
-
+ if (hasLogin) {
+ menuList.unshift({
+ label: '我的笔记',
+ key: 'myWall',
+ icon: ,
+ onClick: () => {
+ navigate('/list');
+ },
+ });
+ }
if (!hasLogin) {
menuList.push({
label: '登录',
@@ -218,6 +231,7 @@ export const ToolbarContent = ({ open }) => {
},
});
}
+
menuList.push({
label: '退出 ',
key: 'logout',
diff --git a/src/pages/wall/pages/List.tsx b/src/pages/wall/pages/List.tsx
index b4b921f..f49d089 100644
--- a/src/pages/wall/pages/List.tsx
+++ b/src/pages/wall/pages/List.tsx
@@ -22,7 +22,11 @@ export const List = () => {
};
return (
-
+
{
+ navigate('/');
+ }}>
Wall Note
@@ -33,7 +37,7 @@ export const List = () => {
key={wall.id}
className='p-4 border border-gray-200 w-80 rounded-md'
onClick={() => {
- navigate(`/wall/${wall.id}`);
+ navigate(`/edit/${wall.id}`);
}}>
{wall.title}
diff --git a/src/pages/wall/store/user-wall.ts b/src/pages/wall/store/user-wall.ts
index fe6fc0f..20d6057 100644
--- a/src/pages/wall/store/user-wall.ts
+++ b/src/pages/wall/store/user-wall.ts
@@ -58,7 +58,7 @@ export const useUserWallStore = create
((set, get) => ({
const res = await query.post({
path: 'mark',
key: 'list',
- markType: 'wall',
+ markType: 'wallnote',
page: 1,
pageSize: 10,
});
diff --git a/src/pages/wall/store/wall.ts b/src/pages/wall/store/wall.ts
index f5e7623..9c9255b 100644
--- a/src/pages/wall/store/wall.ts
+++ b/src/pages/wall/store/wall.ts
@@ -24,7 +24,7 @@ interface WallState {
// 只做传递
nodes: NodeData[];
setNodes: (nodes: NodeData[]) => void;
- saveNodes: (nodes: NodeData[]) => Promise;
+ saveNodes: (nodes: NodeData[], opts?: { showMessage?: boolean }) => Promise;
open: boolean;
setOpen: (open: boolean) => void;
selectedNode: NodeData | null;
@@ -67,10 +67,13 @@ export const useWallStore = create((set, get) => ({
setNodes: (nodes) => {
set({ nodes });
},
- saveNodes: async (nodes: NodeData[]) => {
+ saveNodes: async (nodes: NodeData[], opts) => {
+ console.log('nodes', nodes, opts, opts?.showMessage ?? true);
if (!get().id) {
const covertData = getNodeData(nodes);
setWallData({ nodes: covertData });
+ const showMessage = opts?.showMessage ?? true;
+ showMessage && message.success('保存到本地');
} else {
const { id } = get();
const userWallStore = useUserWallStore.getState();
diff --git a/src/pages/wall/utils/get-image-rect.ts b/src/pages/wall/utils/get-image-rect.ts
new file mode 100644
index 0000000..4737d02
--- /dev/null
+++ b/src/pages/wall/utils/get-image-rect.ts
@@ -0,0 +1,31 @@
+export const getImageWidthHeightByBase64 = async (
+ b64str: any,
+): Promise<{
+ width: number;
+ height: number;
+}> => {
+ return new Promise((resolve, reject) => {
+ // 创建 Canvas 对象
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d')!;
+
+ // 创建 Image 对象
+ const img = new Image();
+ img.onload = () => {
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage(img, 0, 0);
+ const width = img.width;
+ const height = img.height;
+ console.log(`宽度: ${width}, 高度: ${height}`);
+ resolve({ width, height });
+ canvas.remove();
+ };
+ img.onerror = () => {
+ console.error('无法加载图片');
+ reject(new Error('无法加载图片'));
+ canvas.remove();
+ };
+ img.src = b64str;
+ });
+};