|
|
@@ -39,6 +39,19 @@ const AUTO_SAVE_DELAY = 3000; // 3秒无操作后自动保存
|
|
39
|
39
|
const MAX_RETRY_ATTEMPTS = 3;
|
|
40
|
40
|
const MAX_HISTORY_ENTRIES = 100;
|
|
41
|
41
|
|
|
|
42
|
+function waitForStructuralOperations(isReady: () => boolean): Promise<void> {
|
|
|
43
|
+ return new Promise((resolve) => {
|
|
|
44
|
+ const check = () => {
|
|
|
45
|
+ if (isReady()) {
|
|
|
46
|
+ resolve();
|
|
|
47
|
+ return;
|
|
|
48
|
+ }
|
|
|
49
|
+ setTimeout(check, 16);
|
|
|
50
|
+ };
|
|
|
51
|
+ check();
|
|
|
52
|
+ });
|
|
|
53
|
+}
|
|
|
54
|
+
|
|
42
|
55
|
interface EditorHistoryEntry {
|
|
43
|
56
|
blocks: DocumentBlock[];
|
|
44
|
57
|
selectedBlockId: string | null;
|
|
|
@@ -959,13 +972,18 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
959
|
972
|
addBlock: async (partialBlock: PartialBlock, afterBlockId?: string) => {
|
|
960
|
973
|
const { blocks, blockHashes, documentId, isHistoryApplying, pendingStructuralOperations } = get();
|
|
961
|
974
|
|
|
962
|
|
- if (!documentId || isHistoryApplying || pendingStructuralOperations > 0) {
|
|
|
975
|
+ if (!documentId || isHistoryApplying) {
|
|
963
|
976
|
if (!documentId) {
|
|
964
|
|
- message.error('没有打开的文档');
|
|
|
977
|
+ message.error('没有打开的文档');
|
|
965
|
978
|
}
|
|
966
|
979
|
return;
|
|
967
|
980
|
}
|
|
968
|
981
|
|
|
|
982
|
+ if (pendingStructuralOperations > 0) {
|
|
|
983
|
+ await waitForStructuralOperations(() => get().pendingStructuralOperations === 0);
|
|
|
984
|
+ return get().addBlock(partialBlock, afterBlockId);
|
|
|
985
|
+ }
|
|
|
986
|
+
|
|
969
|
987
|
set({ pendingStructuralOperations: pendingStructuralOperations + 1 });
|
|
970
|
988
|
|
|
971
|
989
|
let orderedBlocks = blocks;
|
|
|
@@ -1093,9 +1111,9 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1093
|
1111
|
|
|
1094
|
1112
|
// ── updateBlock ─────────────────────────────────────────────────────────
|
|
1095
|
1113
|
updateBlock: (id: string, updates: BlockUpdate) => {
|
|
1096
|
|
- const { blocks, dirtyBlocks, blockHashes, autoSaveEnabled, isHistoryApplying, pendingStructuralOperations } = get();
|
|
|
1114
|
+ const { blocks, dirtyBlocks, blockHashes, autoSaveEnabled, isHistoryApplying } = get();
|
|
1097
|
1115
|
|
|
1098
|
|
- if (isHistoryApplying || pendingStructuralOperations > 0) {
|
|
|
1116
|
+ if (isHistoryApplying) {
|
|
1099
|
1117
|
return;
|
|
1100
|
1118
|
}
|
|
1101
|
1119
|
|
|
|
@@ -1146,13 +1164,18 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1146
|
1164
|
deleteBlock: async (id: string) => {
|
|
1147
|
1165
|
const { blocks, dirtyBlocks, blockHashes, documentId, isHistoryApplying, pendingStructuralOperations } = get();
|
|
1148
|
1166
|
|
|
1149
|
|
- if (!documentId || isHistoryApplying || pendingStructuralOperations > 0) {
|
|
|
1167
|
+ if (!documentId || isHistoryApplying) {
|
|
1150
|
1168
|
if (!documentId) {
|
|
1151
|
1169
|
message.error('没有打开的文档');
|
|
1152
|
1170
|
}
|
|
1153
|
1171
|
return;
|
|
1154
|
1172
|
}
|
|
1155
|
1173
|
|
|
|
1174
|
+ if (pendingStructuralOperations > 0) {
|
|
|
1175
|
+ await waitForStructuralOperations(() => get().pendingStructuralOperations === 0);
|
|
|
1176
|
+ return get().deleteBlock(id);
|
|
|
1177
|
+ }
|
|
|
1178
|
+
|
|
1156
|
1179
|
// 找到要删除的块
|
|
1157
|
1180
|
const blockToDelete = blocks.find(b => b.id === id);
|
|
1158
|
1181
|
if (!blockToDelete) {
|
|
|
@@ -1292,7 +1315,11 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1292
|
1315
|
// ── moveBlock ───────────────────────────────────────────────────────────
|
|
1293
|
1316
|
moveBlock: (id: string, targetOrder: number) => {
|
|
1294
|
1317
|
const { blocks, isHistoryApplying, pendingStructuralOperations } = get();
|
|
1295
|
|
- if (isHistoryApplying || pendingStructuralOperations > 0) {
|
|
|
1318
|
+ if (isHistoryApplying) {
|
|
|
1319
|
+ return;
|
|
|
1320
|
+ }
|
|
|
1321
|
+ if (pendingStructuralOperations > 0) {
|
|
|
1322
|
+ setTimeout(() => get().moveBlock(id, targetOrder), 16);
|
|
1296
|
1323
|
return;
|
|
1297
|
1324
|
}
|
|
1298
|
1325
|
const block = blocks.find((item) => item.id === id);
|
|
|
@@ -1325,7 +1352,11 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1325
|
1352
|
undo: async () => {
|
|
1326
|
1353
|
const { past, blocks, selectedBlockId, isHistoryApplying, pendingStructuralOperations } = get();
|
|
1327
|
1354
|
const previous = past[past.length - 1];
|
|
1328
|
|
- if (!previous || isHistoryApplying || pendingStructuralOperations > 0) return;
|
|
|
1355
|
+ if (!previous || isHistoryApplying) return;
|
|
|
1356
|
+ if (pendingStructuralOperations > 0) {
|
|
|
1357
|
+ await waitForStructuralOperations(() => get().pendingStructuralOperations === 0);
|
|
|
1358
|
+ return get().undo();
|
|
|
1359
|
+ }
|
|
1329
|
1360
|
|
|
1330
|
1361
|
set({ isHistoryApplying: true });
|
|
1331
|
1362
|
try {
|
|
|
@@ -1351,7 +1382,11 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1351
|
1382
|
redo: async () => {
|
|
1352
|
1383
|
const { future, blocks, selectedBlockId, isHistoryApplying, pendingStructuralOperations } = get();
|
|
1353
|
1384
|
const next = future[future.length - 1];
|
|
1354
|
|
- if (!next || isHistoryApplying || pendingStructuralOperations > 0) return;
|
|
|
1385
|
+ if (!next || isHistoryApplying) return;
|
|
|
1386
|
+ if (pendingStructuralOperations > 0) {
|
|
|
1387
|
+ await waitForStructuralOperations(() => get().pendingStructuralOperations === 0);
|
|
|
1388
|
+ return get().redo();
|
|
|
1389
|
+ }
|
|
1355
|
1390
|
|
|
1356
|
1391
|
set({ isHistoryApplying: true });
|
|
1357
|
1392
|
try {
|