Files
vvveb-cms/static/js/my-editor.js
2026-05-17 22:11:46 +08:00

169 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* my-editor.js — VvvebJS 儲存橋接腳本
* 覆蓋 vvvebjs 的 saveAjax 行為,附加 project slug 後送至 Flask /api/save
*/
(function () {
"use strict";
const SLUG = window.VVVEB_PROJECT_SLUG || "";
// ── Toast 通知 ──────────────────────────────────────────────────
function showToast(msg, type) {
const existing = document.getElementById("vvveb-save-toast");
if (existing) existing.remove();
const toast = document.createElement("div");
toast.id = "vvveb-save-toast";
const bg = type === "error" ? "#7f1d1d" : "#14532d";
const border = type === "error" ? "#ef444460" : "#22c55e60";
Object.assign(toast.style, {
position: "fixed",
top: "1rem",
right: "1rem",
zIndex: "99999",
background: bg,
border: "1px solid " + border,
color: "#fff",
borderRadius: "8px",
padding: "0.65rem 1.1rem",
fontSize: "0.85rem",
fontFamily: "Inter, system-ui, sans-serif",
fontWeight: "500",
boxShadow: "0 4px 20px rgba(0,0,0,0.5)",
opacity: "0",
transform: "translateY(-8px)",
transition: "opacity 0.3s ease, transform 0.3s ease",
pointerEvents: "none",
});
toast.textContent = msg;
document.body.appendChild(toast);
requestAnimationFrame(() => {
toast.style.opacity = "1";
toast.style.transform = "translateY(0)";
});
setTimeout(() => {
toast.style.opacity = "0";
toast.style.transform = "translateY(-8px)";
setTimeout(() => toast.remove(), 350);
}, 3000);
}
// ── 覆蓋 saveAjax ───────────────────────────────────────────────
function patchSaveAjax() {
if (typeof Vvveb === "undefined" || !Vvveb.Builder) {
// 等待 Vvveb 載入
setTimeout(patchSaveAjax, 200);
return;
}
const originalSaveAjax = Vvveb.Builder.saveAjax
? Vvveb.Builder.saveAjax.bind(Vvveb.Builder)
: null;
Vvveb.Builder.saveAjax = function (saveUrl) {
if (!SLUG) {
showToast("錯誤:找不到專案識別碼 (slug)", "error");
return;
}
// 取得目前編輯的頁面檔名
const currentPage = Vvveb.FileManager
? Vvveb.FileManager.getCurrentPage
? Vvveb.FileManager.getCurrentPage()
: null
: null;
let filename = "index.html";
if (currentPage && currentPage.filename) {
filename = currentPage.filename;
} else if (currentPage && currentPage.file) {
// 只取檔名部分
filename = currentPage.file.split("/").pop();
}
// 取得 HTML 內容
let html = "";
try {
html = Vvveb.Builder.getHtml ? Vvveb.Builder.getHtml() : "";
} catch (e) {
console.error("[my-editor] getHtml failed:", e);
}
if (!html) {
showToast("無法取得頁面內容", "error");
return;
}
// 顯示儲存中狀態
const saveBtn = document.querySelector(".save-btn");
if (saveBtn) {
saveBtn.querySelector(".loading")?.classList.remove("d-none");
saveBtn.querySelector(".button-text")?.classList.add("d-none");
}
const body = new URLSearchParams({
slug: SLUG,
file: filename,
html: html,
});
fetch("/api/save", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: body.toString(),
})
.then((res) => res.json())
.then((data) => {
if (data.ok) {
showToast("✓ 已儲存:" + (data.saved || filename));
} else {
showToast("儲存失敗:" + (data.error || "未知錯誤"), "error");
}
})
.catch((err) => {
showToast("網路錯誤:" + err.message, "error");
})
.finally(() => {
if (saveBtn) {
saveBtn.querySelector(".loading")?.classList.add("d-none");
saveBtn.querySelector(".button-text")?.classList.remove("d-none");
}
});
};
console.log("[my-editor] saveAjax patched for slug:", SLUG);
}
// ── 啟用 Save 按鈕vvvebjs 預設 disabled──────────────────────
function enableSaveBtn() {
const btn = document.querySelector(".save-btn");
if (btn) {
btn.removeAttribute("disabled");
} else {
setTimeout(enableSaveBtn, 300);
}
}
// ── Ctrl+S 快捷鍵 ────────────────────────────────────────────────
document.addEventListener("keydown", function (e) {
if ((e.ctrlKey || e.metaKey) && e.key === "s") {
e.preventDefault();
if (typeof Vvveb !== "undefined" && Vvveb.Builder && Vvveb.Builder.saveAjax) {
Vvveb.Builder.saveAjax("/api/save");
}
}
});
// ── 初始化 ────────────────────────────────────────────────────────
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
patchSaveAjax();
enableSaveBtn();
});
} else {
patchSaveAjax();
enableSaveBtn();
}
})();