Add map editor save and deploy actions

This commit is contained in:
2026-06-10 10:45:33 +08:00
parent 57caced672
commit 21f633658c
6 changed files with 309 additions and 12 deletions
+60
View File
@@ -10,6 +10,7 @@ const state = {
imageLoading: new Set(),
imageFailures: new Set(),
previewLoadToken: 0,
serverAvailable: false,
history: [],
dragging: null,
};
@@ -26,6 +27,7 @@ document.addEventListener("DOMContentLoaded", () => {
function bindElements() {
for (const id of [
"mapCanvas", "fileInput", "loadSampleButton", "downloadButton", "undoButton",
"saveProjectButton", "deployButton",
"graphicFileInput",
"mapIdInput", "worldWidthInput", "worldHeightInput", "gridSizeInput",
"hitFilterInput",
@@ -45,6 +47,8 @@ function bindEvents() {
elements.fileInput.addEventListener("change", openFile);
elements.graphicFileInput.addEventListener("change", importGraphicAsset);
elements.downloadButton.addEventListener("click", saveAndDownloadMap);
elements.saveProjectButton.addEventListener("click", saveProjectMap);
elements.deployButton.addEventListener("click", deployProjectMap);
elements.undoButton.addEventListener("click", undoLast);
elements.deleteButton.addEventListener("click", deleteSelected);
elements.moveLayerUpButton.addEventListener("click", () => moveSelectedLayer(-1));
@@ -93,6 +97,26 @@ function bindEvents() {
canvas.addEventListener("mouseup", onCanvasMouseUp);
canvas.addEventListener("mouseleave", onCanvasMouseUp);
canvas.addEventListener("wheel", onCanvasWheel, { passive: false });
detectServerFeatures();
}
async function detectServerFeatures() {
try {
const response = await fetch("/api/map-editor/status", { cache: "no-store" });
const data = response.ok ? await response.json() : null;
state.serverAvailable = Boolean(data && data.writable);
} catch (error) {
state.serverAvailable = false;
}
updateServerButtons();
}
function updateServerButtons() {
elements.saveProjectButton.disabled = !state.serverAvailable;
elements.deployButton.disabled = !state.serverAvailable;
elements.saveProjectButton.title = state.serverAvailable ? "保存到 game/assets/map/stage_01.map" : "需要用 node tools/map_editor/server.js 启动编辑器";
elements.deployButton.title = state.serverAvailable ? "保存并同步到已有 PC/Switch build 资源目录" : "需要用 node tools/map_editor/server.js 启动编辑器";
}
async function loadSampleMap() {
@@ -1120,6 +1144,42 @@ function saveAndDownloadMap() {
setStatus(`已保存导出文本并下载 ${state.map.id || "stage"}.map`);
}
async function saveProjectMap() {
await writeMapToServer("/api/map-editor/save", "已保存到项目资源目录。");
}
async function deployProjectMap() {
await writeMapToServer("/api/map-editor/deploy", "已保存并部署到项目和已有 build 资源目录。");
}
async function writeMapToServer(url, successPrefix) {
if (!state.serverAvailable) {
setStatus("当前是静态服务模式,不能直接写入项目文件。请用 node tools/map_editor/server.js 启动。");
return;
}
const mapText = serializeMap(state.map);
elements.saveProjectButton.disabled = true;
elements.deployButton.disabled = true;
try {
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mapText }),
});
const data = await response.json().catch(() => ({}));
if (!response.ok || !data.ok) {
throw new Error(data.error || `HTTP ${response.status}`);
}
const targets = Array.isArray(data.targets) ? data.targets.length : 0;
setStatus(targets ? `${successPrefix} 写入 ${targets} 个文件。` : successPrefix);
} catch (error) {
setStatus(`保存失败:${error.message}`);
} finally {
updateServerButtons();
}
}
function undoLast() {
const entry = state.history.pop();
if (!entry) return;