feat: save and load button

This commit is contained in:
bridge
2026-02-06 22:19:33 +08:00
parent 67b559ac5a
commit 2ffd6301f8
6 changed files with 89 additions and 18 deletions

View File

@@ -1697,6 +1697,9 @@ def validate_save_name(name: str) -> bool:
class SaveGameRequest(BaseModel):
custom_name: Optional[str] = None # 自定义存档名称
class DeleteSaveRequest(BaseModel):
filename: str
class LoadGameRequest(BaseModel):
filename: str
@@ -1752,6 +1755,37 @@ def api_save_game(req: SaveGameRequest):
else:
raise HTTPException(status_code=500, detail="Save failed")
@app.post("/api/game/delete")
def api_delete_game(req: DeleteSaveRequest):
"""删除存档及其关联文件"""
# 安全检查
if ".." in req.filename or "/" in req.filename or "\\" in req.filename:
raise HTTPException(status_code=400, detail="Invalid filename")
try:
saves_dir = CONFIG.paths.saves
target_path = saves_dir / req.filename
# 1. 删除 JSON 存档文件
if target_path.exists():
os.remove(target_path)
# 2. 删除对应的 SQL 数据库文件
events_db_path = get_events_db_path(target_path)
if os.path.exists(events_db_path):
try:
os.remove(events_db_path)
except Exception as e:
print(f"[Warning] Failed to delete db file {events_db_path}: {e}")
# 3. 删除可能存在的其他关联文件(如果有)
return {"status": "ok", "message": "Save deleted"}
except Exception as e:
import traceback
traceback.print_exc()
raise HTTPException(status_code=500, detail=f"Delete failed: {str(e)}")
@app.post("/api/game/load")
async def api_load_game(req: LoadGameRequest):
"""加载游戏(异步,支持进度更新)。"""

View File

@@ -26,6 +26,10 @@ export const systemApi = {
);
},
deleteSave(filename: string) {
return httpClient.post<{ status: string; message: string }>('/api/game/delete', { filename });
},
loadGame(filename: string) {
return httpClient.post<{ status: string; message: string }>('/api/game/load', { filename });
},

View File

@@ -112,6 +112,21 @@ async function handleLoad(filename: string) {
}
}
async function handleDelete(filename: string) {
if (!confirm(t('save_load.delete_confirm', { filename }))) return
loading.value = true
try {
await systemApi.deleteSave(filename)
message.success(t('save_load.delete_success'))
await fetchSaves()
} catch (e) {
message.error(t('save_load.delete_failed'))
} finally {
loading.value = false
}
}
// 格式化保存时间
function formatSaveTime(isoTime: string): string {
if (!isoTime) return ''
@@ -199,7 +214,22 @@ onMounted(() => {
<span class="version">v{{ save.version }}</span>
</div>
</div>
<div v-if="mode === 'load'" class="load-btn">{{ t('save_load.load') }}</div>
<div v-if="mode === 'load'" class="load-actions">
<NButton
type="error"
size="small"
secondary
@click.stop="handleDelete(save.filename)"
>
{{ t('save_load.delete') }}
</NButton>
<NButton
size="small"
@click.stop="handleLoad(save.filename)"
>
{{ t('save_load.load') }}
</NButton>
</div>
</div>
</div>
@@ -250,7 +280,7 @@ onMounted(() => {
flex-direction: column;
}
.save-panel {
.save-panel, .load-panel {
align-items: center;
padding-top: 2em;
}
@@ -392,19 +422,10 @@ onMounted(() => {
font-family: monospace;
}
.load-btn {
background: #333;
color: #ddd;
border: 1px solid #444;
padding: 0.4em 1em;
border-radius: 0.3em;
font-size: 0.9em;
transition: all 0.2s;
}
.load-btn:hover {
background: #444;
border-color: #555;
.load-actions {
display: flex;
gap: 1em;
align-items: center;
}
.loading {

View File

@@ -49,7 +49,11 @@
"name_placeholder": "Enter save name...",
"name_tip": "Leave empty to use auto-generated name",
"name_too_long": "Name cannot exceed 50 characters",
"name_invalid_chars": "Name can only contain letters, numbers, Chinese characters and underscores"
"name_invalid_chars": "Name can only contain letters, numbers, Chinese characters and underscores",
"delete": "Delete",
"delete_confirm": "Are you sure you want to permanently delete save {filename}? This action cannot be undone.",
"delete_success": "Save deleted",
"delete_failed": "Failed to delete"
},
"llm": {
"loading": "Loading...",

View File

@@ -49,7 +49,11 @@
"name_placeholder": "输入存档名称...",
"name_tip": "留空将使用自动生成的名称",
"name_too_long": "名称不能超过50个字符",
"name_invalid_chars": "名称只能包含中文、字母、数字和下划线"
"name_invalid_chars": "名称只能包含中文、字母、数字和下划线",
"delete": "删除",
"delete_confirm": "确定要彻底删除存档 {filename} 及其所有数据吗?此操作无法撤销。",
"delete_success": "存档已删除",
"delete_failed": "删除失败"
},
"llm": {
"loading": "加载中...",

View File

@@ -49,7 +49,11 @@
"name_placeholder": "輸入存檔名稱...",
"name_tip": "留空將使用自動生成的名稱",
"name_too_long": "名稱不能超過50個字元",
"name_invalid_chars": "名稱只能包含中文、字母、數字和底線"
"name_invalid_chars": "名稱只能包含中文、字母、數字和底線",
"delete": "刪除",
"delete_confirm": "確定要徹底刪除存檔 {filename} 及其所有資料嗎?此操作無法撤銷。",
"delete_success": "存檔已刪除",
"delete_failed": "刪除失敗"
},
"llm": {
"loading": "載入中...",