Skip to content

Commit ba185f8

Browse files
committed
✨ 备份文件可以限制文件数量
1 parent 7d3ac38 commit ba185f8

File tree

8 files changed

+110
-25
lines changed

8 files changed

+110
-25
lines changed

app/src-tauri/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,27 @@ fn exists(path: String) -> bool {
1616
std::path::Path::new(&path).exists()
1717
}
1818

19+
/// 读取文件夹中的文件列表
20+
#[tauri::command]
21+
fn read_folder(path: String) -> Vec<String> {
22+
let mut files = Vec::new();
23+
for entry in std::fs::read_dir(path).unwrap() {
24+
let entry = entry.unwrap();
25+
if entry.path().is_file() {
26+
files.push(entry.file_name().to_str().unwrap().to_string());
27+
}
28+
}
29+
files
30+
}
31+
32+
/// 删除文件
33+
#[tauri::command]
34+
fn delete_file(path: String) -> Result<(), String> {
35+
std::fs::remove_file(path).map_err(|e| e.to_string())?;
36+
Ok(())
37+
}
38+
39+
1940
/// 读取文件,返回字符串
2041
#[tauri::command]
2142
fn read_text_file(path: String) -> String {
@@ -129,6 +150,8 @@ pub fn run() {
129150
})
130151
.invoke_handler(tauri::generate_handler![
131152
read_text_file,
153+
read_folder,
154+
delete_file,
132155
write_text_file,
133156
exists,
134157
read_file_base64,

app/src/core/service/Settings.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export namespace Settings {
7272
autoBackup: boolean;
7373
autoBackupInterval: number;
7474
autoBackupDraftPath: string;
75+
autoBackupLimitCount: number;
7576
generateTextNodeByStringTabCount: number; // 仅在生成节点面板中使用
7677
// 控制相关
7778
enableCollision: boolean; // 暂无
@@ -187,6 +188,7 @@ export namespace Settings {
187188
autoBackup: true,
188189
autoBackupInterval: 600,
189190
autoBackupDraftPath: "",
191+
autoBackupLimitCount: 10,
190192
generateTextNodeByStringTabCount: 4,
191193
// 控制相关
192194
enableCollision: true,

app/src/core/service/controlService/secretKeysEngine/secretKeysEngine.tsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { v4 } from "uuid";
22
import { Direction } from "../../../../types/directions";
3-
import { createFolder } from "../../../../utils/fs";
3+
import { createFolder, readFolder } from "../../../../utils/fs";
44
import { averageColors, Color } from "../../../dataStruct/Color";
55
import { Queue } from "../../../dataStruct/Queue";
66
import { Vector } from "../../../dataStruct/Vector";
@@ -26,13 +26,12 @@ import { ViewFlashEffect } from "../../feedbackService/effectEngine/concrete/Vie
2626
import { AutoLayoutFastTree } from "../autoLayoutEngine/autoLayoutFastTreeMode";
2727
import { MultiTargetUndirectedEdge } from "../../../stage/stageObject/association/MutiTargetUndirectedEdge";
2828
import { Random } from "../../../algorithm/random";
29-
import { Dialog } from "../../../../components/dialog";
30-
import { SvgNode } from "../../../stage/stageObject/entity/SvgNode";
3129

3230
interface SecretKeyItem {
3331
name: string;
3432
func: () => void;
3533
explain?: string;
34+
isHidden?: boolean;
3635
}
3736

3837
/**
@@ -65,6 +64,9 @@ export class SecretKeysEngine {
6564
public getAllSecretKeysList(): { keys: string; name: string; explain: string }[] {
6665
const result = [];
6766
for (const key in this.keyPressedTable) {
67+
if (this.keyPressedTable[key].isHidden) {
68+
continue;
69+
}
6870
result.push({
6971
keys: key,
7072
name: this.keyPressedTable[key].name,
@@ -170,6 +172,7 @@ export class SecretKeysEngine {
170172
},
171173
"* * *": {
172174
name: "切换专注模式",
175+
isHidden: true,
173176
async func() {
174177
Settings.set("isClassroomMode", !(await Settings.get("isClassroomMode")));
175178
},
@@ -530,6 +533,7 @@ export class SecretKeysEngine {
530533
// },
531534
"b o y n e x t d o o r": {
532535
name: "创建传送门",
536+
isHidden: true,
533537
func: () => {
534538
Stage.effectMachine.addEffect(ViewFlashEffect.SaveFile());
535539
const uuid = v4();
@@ -548,18 +552,21 @@ export class SecretKeysEngine {
548552
},
549553
"c o l l a b o r a t e": {
550554
name: "开始协作",
555+
isHidden: true,
551556
func: () => {
552557
CollaborationEngine.openStartCollaborationPanel();
553558
},
554559
},
555560
"c r e a t e f o l d e r w i n": {
556561
name: "在D盘创建“111”文件夹",
562+
isHidden: true,
557563
func: () => {
558564
createFolder("D:\\111\\111");
559565
},
560566
},
561567
"r o l l i n g 1": {
562568
name: "摄像机开始疯狂缩放",
569+
isHidden: true,
563570
func: () => {
564571
let tick = 0;
565572
setInterval(() => {
@@ -571,6 +578,7 @@ export class SecretKeysEngine {
571578
},
572579
"t r e e r e c t": {
573580
name: "获取选中根节点的整个树的外接矩形",
581+
isHidden: true,
574582
func: () => {
575583
Stage.effectMachine.addEffect(ViewFlashEffect.SaveFile());
576584
const selectNode = StageManager.getSelectedEntities()[0];
@@ -585,6 +593,7 @@ export class SecretKeysEngine {
585593
},
586594
"a l t": {
587595
name: "将所有选中的根节点所对应的树进行垂直对齐",
596+
isHidden: true,
588597
func: () => {
589598
const selectNodes = StageManager.getSelectedEntities().filter((node) => node instanceof ConnectableEntity);
590599
if (selectNodes.length === 0) {
@@ -595,6 +604,7 @@ export class SecretKeysEngine {
595604
},
596605
"m v e t": {
597606
name: "将选中的根节点对应的树移动到摄像机位置",
607+
isHidden: true,
598608
func: () => {
599609
AutoLayoutFastTree.moveTreeRectTo(
600610
StageManager.getSelectedEntities()[0] as ConnectableEntity,
@@ -613,6 +623,7 @@ export class SecretKeysEngine {
613623
},
614624
"c r p + +": {
615625
name: "将选中的CR曲线增加控制点",
626+
isHidden: true,
616627
func() {
617628
const selectedCREdge = StageManager.getSelectedAssociations().filter(
618629
(edge) => edge instanceof CublicCatmullRomSplineEdge,
@@ -647,6 +658,7 @@ export class SecretKeysEngine {
647658
},
648659
"= = =": {
649660
name: "将选中的可连接实体添加多源无向边",
661+
isHidden: true,
650662
explain: "测试中",
651663
func() {
652664
const selectedNodes = StageManager.getSelectedEntities().filter((node) => node instanceof ConnectableEntity);
@@ -730,33 +742,21 @@ export class SecretKeysEngine {
730742
},
731743
"contextmenu e r r o r": {
732744
name: "手动测试",
745+
isHidden: true,
733746
explain: "触发手动报错,用于观察红色的弹窗是否正常显示、内部报错文字是否可以复制等操作",
734747
func() {
735748
setTimeout(() => {
736749
throw new Error("您用秘籍键触发了手动报错,用于观察红色的弹窗是否正常显示、内部报错文字是否可以复制等操作");
737750
}, 1000);
738751
},
739752
},
740-
"s v g s v g": {
741-
name: "测试创建svg节点",
742-
explain: "测试创建svg节点,后续会删除",
753+
"g e t f o l d e r": {
754+
name: "获取文件夹路径下的文件",
755+
explain: "未来即将删除",
756+
isHidden: true,
743757
func() {
744-
Dialog.show({
745-
title: "创建svg",
746-
input: true,
747-
content: "请输入svg内容",
748-
}).then(({ button, value }) => {
749-
if (button === "确定" && value) {
750-
const svgNode = new SvgNode({
751-
uuid: v4(),
752-
content: value,
753-
location: [Camera.location.x, Camera.location.y],
754-
size: [100, 100],
755-
color: [0, 0, 0, 0],
756-
});
757-
StageManager.addEntity(svgNode);
758-
}
759-
});
758+
const files = readFolder("D:\\");
759+
console.log(files);
760760
},
761761
},
762762
};

app/src/core/service/dataFileService/StageSaveManager.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { PathString } from "../../../utils/pathString";
44
import { Stage } from "../../stage/Stage";
55
import { StageHistoryManager } from "../../stage/stageManager/StageHistoryManager";
66
import { ViewFlashEffect } from "../feedbackService/effectEngine/concrete/ViewFlashEffect";
7+
import { AutoBackupEngine } from "./autoSaveBackupEngine/autoBackupEngine";
78

89
/**
910
* 管理所有和保存相关的内容
@@ -72,8 +73,7 @@ export namespace StageSaveManager {
7273
}
7374
// 不能有冒号,空格,斜杠
7475
const dateTime = PathString.getTime();
75-
const fatherDirPath = PathString.dirPath(Stage.path.getFilePath());
76-
const backupFolderPath = `${fatherDirPath}${PathString.getSep()}backup_${PathString.getFileNameFromPath(Stage.path.getFilePath())}`;
76+
const backupFolderPath = AutoBackupEngine.getBackupFolderPath();
7777
if (!(await exists(backupFolderPath))) {
7878
const created = await createFolder(backupFolderPath);
7979
if (!created) {

app/src/core/service/dataFileService/autoSaveBackupEngine/autoBackupEngine.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { deleteFile, readFolder } from "../../../../utils/fs";
12
import { PathString } from "../../../../utils/pathString";
23
import { isWeb } from "../../../../utils/platform";
34
import { Stage } from "../../../stage/Stage";
@@ -13,6 +14,7 @@ export class AutoBackupEngine {
1314
autoBackupInterval: number = 30 * 60 * 1000; // 30分钟
1415
autoBackupDraftPath: string = "";
1516
private lastAutoBackupTime: number = performance.now();
17+
autoBackupLimitCount: number = 10;
1618

1719
init() {
1820
Settings.watch("autoBackup", (value) => {
@@ -24,6 +26,9 @@ export class AutoBackupEngine {
2426
Settings.watch("autoBackupDraftPath", (value) => {
2527
this.autoBackupDraftPath = value;
2628
});
29+
Settings.watch("autoBackupLimitCount", (value) => {
30+
this.autoBackupLimitCount = value;
31+
});
2732
}
2833

2934
public mainTick() {
@@ -42,9 +47,38 @@ export class AutoBackupEngine {
4247
StageSaveManager.backupHandle(backupDraftPath, StageDumper.dump());
4348
} else {
4449
StageSaveManager.backupHandleWithoutCurrentPath(StageDumper.dump(), false);
50+
this.limitBackupFilesAndDeleteOld(this.autoBackupLimitCount);
4551
}
4652
// 更新时间
4753
this.lastAutoBackupTime = now;
4854
}
4955
}
56+
57+
/**
58+
* 获取当前场景对应的备份文件夹路径
59+
* @returns
60+
*/
61+
static getBackupFolderPath() {
62+
const fatherDirPath = PathString.dirPath(Stage.path.getFilePath());
63+
return `${fatherDirPath}${PathString.getSep()}backup_${PathString.getFileNameFromPath(Stage.path.getFilePath())}`;
64+
}
65+
66+
/**
67+
* 限制备份文件的数量,并删除旧的备份文件
68+
* @param maxCount
69+
*/
70+
public async limitBackupFilesAndDeleteOld(maxCount: number) {
71+
//
72+
const backupFolderPath = AutoBackupEngine.getBackupFolderPath();
73+
const backupFiles = (await readFolder(backupFolderPath))
74+
.map((value) => backupFolderPath + PathString.getSep() + value)
75+
.filter((value) => value.endsWith(".backup.json"))
76+
.sort();
77+
if (backupFiles.length > maxCount) {
78+
// 开始限制
79+
for (let i = 0; i < backupFiles.length - maxCount; i++) {
80+
await deleteFile(backupFiles[i]);
81+
}
82+
}
83+
}
5084
}

app/src/locales/zh_CN.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ settings:
299299
windows系统注意使用反斜杠
300300
例如:C:\\Users\\username\\Documents\\DraftBackup
301301
结尾不要带路径分隔符
302+
autoBackupLimitCount:
303+
title: 自动备份最大数量
304+
description: |
305+
自动备份的最大数量,超过此数量将会删除旧的备份文件
302306
scaleExponent:
303307
title: 视角缩放速度
304308
description: |

app/src/pages/settings/automation.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Database, Folder, HardDrive, HardDriveDownload, Hourglass, Tag } from "lucide-react";
1+
import { Database, FileStack, Folder, HardDrive, HardDriveDownload, Hourglass, Tag } from "lucide-react";
22
import { SettingField } from "../../components/Field";
33

44
// 其实应该改成快捷操作相关
@@ -12,6 +12,7 @@ export default function AutoNamer() {
1212
<SettingField icon={<Hourglass />} settingKey="autoSaveInterval" type="slider" min={1} max={60} step={1} />
1313
<SettingField icon={<Database />} settingKey="autoBackup" type="switch" />
1414
<SettingField icon={<Hourglass />} settingKey="autoBackupInterval" type="slider" min={60} max={6000} step={60} />
15+
<SettingField icon={<FileStack />} settingKey="autoBackupLimitCount" type="slider" min={1} max={500} step={1} />
1516
<SettingField icon={<Folder />} settingKey="autoBackupDraftPath" type="text" />
1617
</>
1718
);

app/src/utils/fs.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ export async function exists(path: string): Promise<boolean> {
1515
}
1616
}
1717

18+
/**
19+
* 返回一个目录的子文件列表
20+
* @param path 绝对路径
21+
* @returns 返回每一个文件的绝对路径
22+
*/
23+
export async function readFolder(path: string): Promise<string[]> {
24+
if (isWeb) {
25+
return [];
26+
} else {
27+
return invoke("read_folder", { path });
28+
}
29+
}
30+
31+
export async function deleteFile(path: string): Promise<boolean> {
32+
if (isWeb) {
33+
return false;
34+
} else {
35+
return invoke("delete_file", { path });
36+
}
37+
}
38+
1839
/**
1940
* 读取文本文件内容
2041
* @param path 文件路径

0 commit comments

Comments
 (0)