Skip to content

Commit 30b0300

Browse files
committed
♻️ 改进粘贴功能的代码结构
1 parent 51da953 commit 30b0300

File tree

4 files changed

+154
-134
lines changed

4 files changed

+154
-134
lines changed

app/src/assets/projectGraphTips.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@
757757
},
758758
{
759759
"location": [1609, -1159],
760-
"size": [787, 513],
760+
"size": [796, 513],
761761
"uuid": "8e0f17f9-09a3-4328-9eb9-e35bd7cae71e",
762762
"text": "质点/折线",
763763
"color": [0, 0, 0, 0],
@@ -3300,7 +3300,7 @@
33003300
"type": "core:text_node"
33013301
},
33023302
{
3303-
"location": [7791, -1038],
3303+
"location": [7800, -1029],
33043304
"size": [322, 518],
33053305
"uuid": "6a08d73c-7084-4296-b89c-60ecb31933c1",
33063306
"text": "连线颜色含义",

app/src/core/service/dataManageService/copyEngine/copyEngine.tsx

Lines changed: 4 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
import { v4 as uuidv4 } from "uuid";
2-
import { Dialog } from "../../../../components/dialog";
31
import { Serialized } from "../../../../types/node";
4-
import { writeFileBase64 } from "../../../../utils/fs";
5-
import { PathString } from "../../../../utils/pathString";
62
import { Rectangle } from "../../../dataStruct/shape/Rectangle";
73
import { Vector } from "../../../dataStruct/Vector";
84
import { Renderer } from "../../../render/canvas2d/renderer";
9-
import { Stage } from "../../../stage/Stage";
105
import { StageDumper } from "../../../stage/StageDumper";
116
import { StageSerializedAdder } from "../../../stage/stageManager/concreteMethods/StageSerializedAdder";
127
import { StageManager } from "../../../stage/stageManager/StageManager";
138
import { Entity } from "../../../stage/stageObject/abstract/StageEntity";
149
import { ImageNode } from "../../../stage/stageObject/entity/ImageNode";
1510
import { TextNode } from "../../../stage/stageObject/entity/TextNode";
16-
import { UrlNode } from "../../../stage/stageObject/entity/UrlNode";
1711
import { MouseLocation } from "../../controlService/MouseLocation";
18-
import { SectionMethods } from "../../../stage/stageManager/basicMethods/SectionMethods";
19-
import { RectanglePushInEffect } from "../../feedbackService/effectEngine/concrete/RectanglePushInEffect";
12+
import { copyEnginePasteImage } from "./pasteImage";
13+
import { copyEnginePastePlainText } from "./pastePlainText";
2014

2115
/**
2216
* 专门用来管理节点复制的引擎
@@ -185,136 +179,14 @@ async function readClipboardItems(mouseLocation: Vector) {
185179
navigator.clipboard.read().then(async (items) => {
186180
for (const item of items) {
187181
if (item.types.includes("image/png")) {
188-
// 图片在草稿情况下不能粘贴
189-
if (Stage.path.isDraft()) {
190-
Dialog.show({
191-
title: "草稿状态下不要粘贴图片",
192-
content: "请先另存为,再粘贴图片,因为粘贴的图片会和保存的工程文件在同一目录下,而草稿在内存中,没有路径",
193-
});
194-
return;
195-
}
196-
const blob = await item.getType(item.types[0]); // 获取 Blob 对象
197-
const imageUUID = uuidv4();
198-
const folder = PathString.dirPath(Stage.path.getFilePath());
199-
const imagePath = `${folder}${PathString.getSep()}${imageUUID}.png`;
200-
201-
// 2024.12.31 测试发现这样的写法会导致读取时base64解码失败
202-
// writeFile(imagePath, new Uint8Array(await blob.arrayBuffer()));
203-
// 下面这样的写法是没有问题的
204-
writeFileBase64(imagePath, await convertBlobToBase64(blob));
205-
206-
// 要延迟一下,等待保存完毕
207-
setTimeout(() => {
208-
const imageNode = new ImageNode({
209-
uuid: imageUUID,
210-
location: [mouseLocation.x, mouseLocation.y],
211-
path: `${imageUUID}.png`,
212-
});
213-
// imageNode.setBase64StringForced(base64String);
214-
StageManager.addImageNode(imageNode);
215-
}, 100);
182+
copyEnginePasteImage(item, mouseLocation);
216183
}
217184
if (item.types.includes("text/plain")) {
218-
const blob = await item.getType("text/plain"); // 获取文本内容
219-
// const text = await blob.text();
220-
const clipboardText = await blobToText(blob); // 将 Blob 转换为文本
221-
if (PathString.isValidURL(clipboardText)) {
222-
// 是URL类型
223-
const urlNode = new UrlNode({
224-
title: "链接",
225-
uuid: uuidv4(),
226-
url: clipboardText,
227-
location: [mouseLocation.x, mouseLocation.y],
228-
});
229-
StageManager.addUrlNode(urlNode);
230-
231-
// 添加到section
232-
const mouseSections = SectionMethods.getSectionsByInnerLocation(mouseLocation);
233-
if (mouseSections.length > 0) {
234-
StageManager.goInSection([urlNode], mouseSections[0]);
235-
Stage.effectMachine.addEffect(
236-
RectanglePushInEffect.sectionGoInGoOut(
237-
urlNode.collisionBox.getRectangle(),
238-
mouseSections[0].collisionBox.getRectangle(),
239-
),
240-
);
241-
}
242-
} else {
243-
const { valid, text, url } = PathString.isMarkdownUrl(clipboardText);
244-
if (valid) {
245-
// 是Markdown链接类型
246-
const urlNode = new UrlNode({
247-
title: text,
248-
uuid: uuidv4(),
249-
url: url,
250-
location: [mouseLocation.x, mouseLocation.y],
251-
});
252-
StageManager.addUrlNode(urlNode);
253-
254-
// 添加到section
255-
const mouseSections = SectionMethods.getSectionsByInnerLocation(mouseLocation);
256-
if (mouseSections.length > 0) {
257-
StageManager.goInSection([urlNode], mouseSections[0]);
258-
Stage.effectMachine.addEffect(
259-
RectanglePushInEffect.sectionGoInGoOut(
260-
urlNode.collisionBox.getRectangle(),
261-
mouseSections[0].collisionBox.getRectangle(),
262-
),
263-
);
264-
}
265-
} else {
266-
// 只是普通的文本
267-
const textNode = new TextNode({
268-
uuid: uuidv4(),
269-
text: clipboardText,
270-
location: [mouseLocation.x, mouseLocation.y],
271-
size: [100, 100],
272-
color: [0, 0, 0, 0],
273-
});
274-
textNode.move(new Vector(-textNode.rectangle.size.x / 2, -textNode.rectangle.size.y / 2));
275-
StageManager.addTextNode(textNode);
276-
277-
// 添加到section
278-
const mouseSections = SectionMethods.getSectionsByInnerLocation(mouseLocation);
279-
if (mouseSections.length > 0) {
280-
StageManager.goInSection([textNode], mouseSections[0]);
281-
Stage.effectMachine.addEffect(
282-
RectanglePushInEffect.sectionGoInGoOut(
283-
textNode.collisionBox.getRectangle(),
284-
mouseSections[0].collisionBox.getRectangle(),
285-
),
286-
);
287-
}
288-
}
289-
}
185+
copyEnginePastePlainText(item, mouseLocation);
290186
}
291187
}
292188
});
293189
} catch (err) {
294190
console.error("Failed to read clipboard contents: ", err);
295191
}
296192
}
297-
298-
function blobToText(blob: Blob): Promise<string> {
299-
return new Promise((resolve, reject) => {
300-
const reader = new FileReader();
301-
reader.onload = () => resolve(reader.result as string); // 读取完成时返回结果
302-
reader.onerror = () => reject(reader.error); // 读取出错时返回错误
303-
reader.readAsText(blob); // 读取 Blob 对象作为文本
304-
});
305-
}
306-
307-
async function convertBlobToBase64(blob: Blob): Promise<string> {
308-
return new Promise((resolve, reject) => {
309-
const reader = new FileReader();
310-
reader.onloadend = () => {
311-
if (typeof reader.result === "string") {
312-
resolve(reader.result.split(",")[1]); // 去掉"data:image/png;base64,"前缀
313-
} else {
314-
reject(new Error("Invalid result type"));
315-
}
316-
};
317-
reader.onerror = reject;
318-
reader.readAsDataURL(blob);
319-
});
320-
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Dialog } from "../../../../components/dialog";
2+
import { writeFileBase64 } from "../../../../utils/fs";
3+
import { PathString } from "../../../../utils/pathString";
4+
import { Vector } from "../../../dataStruct/Vector";
5+
import { Stage } from "../../../stage/Stage";
6+
import { StageManager } from "../../../stage/stageManager/StageManager";
7+
import { ImageNode } from "../../../stage/stageObject/entity/ImageNode";
8+
import { v4 as uuidv4 } from "uuid";
9+
10+
export async function copyEnginePasteImage(item: ClipboardItem, mouseLocation: Vector) {
11+
// 图片在草稿情况下不能粘贴
12+
if (Stage.path.isDraft()) {
13+
Dialog.show({
14+
title: "草稿状态下不要粘贴图片",
15+
content: "请先另存为,再粘贴图片,因为粘贴的图片会和保存的工程文件在同一目录下,而草稿在内存中,没有路径",
16+
});
17+
return;
18+
}
19+
const blob = await item.getType(item.types[0]); // 获取 Blob 对象
20+
const imageUUID = uuidv4();
21+
const folder = PathString.dirPath(Stage.path.getFilePath());
22+
const fileName: string = imageUUID;
23+
const imagePath = `${folder}${PathString.getSep()}${fileName}.png`;
24+
25+
// 2024.12.31 测试发现这样的写法会导致读取时base64解码失败
26+
// writeFile(imagePath, new Uint8Array(await blob.arrayBuffer()));
27+
// 下面这样的写法是没有问题的
28+
writeFileBase64(imagePath, await convertBlobToBase64(blob));
29+
30+
// 要延迟一下,等待保存完毕
31+
setTimeout(() => {
32+
const imageNode = new ImageNode({
33+
uuid: imageUUID,
34+
location: [mouseLocation.x, mouseLocation.y],
35+
path: `${fileName}.png`,
36+
});
37+
// imageNode.setBase64StringForced(base64String);
38+
StageManager.addImageNode(imageNode);
39+
}, 100);
40+
}
41+
42+
async function convertBlobToBase64(blob: Blob): Promise<string> {
43+
return new Promise((resolve, reject) => {
44+
const reader = new FileReader();
45+
reader.onloadend = () => {
46+
if (typeof reader.result === "string") {
47+
resolve(reader.result.split(",")[1]); // 去掉"data:image/png;base64,"前缀
48+
} else {
49+
reject(new Error("Invalid result type"));
50+
}
51+
};
52+
reader.onerror = reject;
53+
reader.readAsDataURL(blob);
54+
});
55+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { PathString } from "../../../../utils/pathString";
2+
import { Vector } from "../../../dataStruct/Vector";
3+
import { Stage } from "../../../stage/Stage";
4+
import { SectionMethods } from "../../../stage/stageManager/basicMethods/SectionMethods";
5+
import { StageManager } from "../../../stage/stageManager/StageManager";
6+
import { TextNode } from "../../../stage/stageObject/entity/TextNode";
7+
import { UrlNode } from "../../../stage/stageObject/entity/UrlNode";
8+
import { RectanglePushInEffect } from "../../feedbackService/effectEngine/concrete/RectanglePushInEffect";
9+
import { v4 as uuidv4 } from "uuid";
10+
11+
export async function copyEnginePastePlainText(item: ClipboardItem, mouseLocation: Vector) {
12+
const blob = await item.getType("text/plain"); // 获取文本内容
13+
// const text = await blob.text();
14+
const clipboardText = await blobToText(blob); // 将 Blob 转换为文本
15+
if (PathString.isValidURL(clipboardText)) {
16+
// 是URL类型
17+
const urlNode = new UrlNode({
18+
title: "链接",
19+
uuid: uuidv4(),
20+
url: clipboardText,
21+
location: [mouseLocation.x, mouseLocation.y],
22+
});
23+
StageManager.addUrlNode(urlNode);
24+
25+
// 添加到section
26+
const mouseSections = SectionMethods.getSectionsByInnerLocation(mouseLocation);
27+
if (mouseSections.length > 0) {
28+
StageManager.goInSection([urlNode], mouseSections[0]);
29+
Stage.effectMachine.addEffect(
30+
RectanglePushInEffect.sectionGoInGoOut(
31+
urlNode.collisionBox.getRectangle(),
32+
mouseSections[0].collisionBox.getRectangle(),
33+
),
34+
);
35+
}
36+
} else {
37+
const { valid, text, url } = PathString.isMarkdownUrl(clipboardText);
38+
if (valid) {
39+
// 是Markdown链接类型
40+
const urlNode = new UrlNode({
41+
title: text,
42+
uuid: uuidv4(),
43+
url: url,
44+
location: [mouseLocation.x, mouseLocation.y],
45+
});
46+
StageManager.addUrlNode(urlNode);
47+
48+
// 添加到section
49+
const mouseSections = SectionMethods.getSectionsByInnerLocation(mouseLocation);
50+
if (mouseSections.length > 0) {
51+
StageManager.goInSection([urlNode], mouseSections[0]);
52+
Stage.effectMachine.addEffect(
53+
RectanglePushInEffect.sectionGoInGoOut(
54+
urlNode.collisionBox.getRectangle(),
55+
mouseSections[0].collisionBox.getRectangle(),
56+
),
57+
);
58+
}
59+
} else {
60+
// 只是普通的文本
61+
const textNode = new TextNode({
62+
uuid: uuidv4(),
63+
text: clipboardText,
64+
location: [mouseLocation.x, mouseLocation.y],
65+
size: [100, 100],
66+
color: [0, 0, 0, 0],
67+
});
68+
textNode.move(new Vector(-textNode.rectangle.size.x / 2, -textNode.rectangle.size.y / 2));
69+
StageManager.addTextNode(textNode);
70+
71+
// 添加到section
72+
const mouseSections = SectionMethods.getSectionsByInnerLocation(mouseLocation);
73+
if (mouseSections.length > 0) {
74+
StageManager.goInSection([textNode], mouseSections[0]);
75+
Stage.effectMachine.addEffect(
76+
RectanglePushInEffect.sectionGoInGoOut(
77+
textNode.collisionBox.getRectangle(),
78+
mouseSections[0].collisionBox.getRectangle(),
79+
),
80+
);
81+
}
82+
}
83+
}
84+
}
85+
86+
function blobToText(blob: Blob): Promise<string> {
87+
return new Promise((resolve, reject) => {
88+
const reader = new FileReader();
89+
reader.onload = () => resolve(reader.result as string); // 读取完成时返回结果
90+
reader.onerror = () => reject(reader.error); // 读取出错时返回错误
91+
reader.readAsText(blob); // 读取 Blob 对象作为文本
92+
});
93+
}

0 commit comments

Comments
 (0)