214 lines
7.2 KiB
JavaScript
214 lines
7.2 KiB
JavaScript
import {
|
||
CustomSelect,
|
||
modalManager,
|
||
taskCenterManager,
|
||
toastManager,
|
||
uiPatterns
|
||
} from "./chunk-EZAP7GR4.js";
|
||
import {
|
||
apiFetch,
|
||
apiFetchJson
|
||
} from "./chunk-PLQL6WIO.js";
|
||
|
||
// frontend/js/components/slidingTabs.js
|
||
var SlidingTabs = class {
|
||
/**
|
||
* @param {HTMLElement} containerElement - The main container element with the `data-sliding-tabs-container` attribute.
|
||
*/
|
||
constructor(containerElement) {
|
||
this.container = containerElement;
|
||
this.indicator = this.container.querySelector("[data-tab-indicator]");
|
||
this.tabs = this.container.querySelectorAll("[data-tab-item]");
|
||
this.activeTab = this.container.querySelector(".tab-active");
|
||
if (!this.indicator || this.tabs.length === 0) {
|
||
console.error("SlidingTabs component is missing required elements (indicator or items).", this.container);
|
||
return;
|
||
}
|
||
this.init();
|
||
}
|
||
init() {
|
||
if (this.activeTab) {
|
||
setTimeout(() => this.updateIndicator(this.activeTab), 50);
|
||
}
|
||
this.bindEvents();
|
||
}
|
||
updateIndicator(targetTab) {
|
||
if (!targetTab) return;
|
||
const containerRect = this.container.getBoundingClientRect();
|
||
const targetRect = targetTab.getBoundingClientRect();
|
||
const left = targetRect.left - containerRect.left;
|
||
const width = targetRect.width;
|
||
this.indicator.style.left = `${left}px`;
|
||
this.indicator.style.width = `${width}px`;
|
||
}
|
||
bindEvents() {
|
||
this.tabs.forEach((tab) => {
|
||
tab.addEventListener("click", (e) => {
|
||
if (this.activeTab) {
|
||
this.activeTab.classList.remove("tab-active");
|
||
}
|
||
tab.classList.add("tab-active");
|
||
this.activeTab = tab;
|
||
this.updateIndicator(this.activeTab);
|
||
});
|
||
tab.addEventListener("mouseenter", () => {
|
||
this.updateIndicator(tab);
|
||
});
|
||
});
|
||
this.container.addEventListener("mouseleave", () => {
|
||
this.updateIndicator(this.activeTab);
|
||
});
|
||
}
|
||
};
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const allTabContainers = document.querySelectorAll("[data-sliding-tabs-container]");
|
||
allTabContainers.forEach((container) => {
|
||
new SlidingTabs(container);
|
||
});
|
||
});
|
||
|
||
// frontend/js/components/themeManager.js
|
||
var themeManager = {
|
||
// 用于存储图标的 SVG HTML
|
||
icons: {},
|
||
init: function() {
|
||
this.html = document.documentElement;
|
||
this.buttons = document.querySelectorAll(".theme-btn");
|
||
this.cyclerBtn = document.getElementById("theme-cycler-btn");
|
||
this.cyclerIconContainer = document.getElementById("theme-cycler-icon");
|
||
if (!this.html || this.buttons.length === 0 || !this.cyclerBtn || !this.cyclerIconContainer) {
|
||
console.warn("ThemeManager init failed: one or more required elements not found.");
|
||
return;
|
||
}
|
||
this.mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||
this.storeIcons();
|
||
this.buttons.forEach((btn) => {
|
||
btn.addEventListener("click", () => this.setTheme(btn.dataset.theme));
|
||
});
|
||
this.cyclerBtn.addEventListener("click", () => this.cycleTheme());
|
||
this.mediaQuery.addEventListener("change", () => this.applyTheme());
|
||
this.applyTheme();
|
||
},
|
||
// 从现有按钮中提取并存储 SVG 图标
|
||
storeIcons: function() {
|
||
this.buttons.forEach((btn) => {
|
||
const theme = btn.dataset.theme;
|
||
const svg = btn.querySelector("svg");
|
||
if (theme && svg) {
|
||
this.icons[theme] = svg.outerHTML;
|
||
}
|
||
});
|
||
},
|
||
// 循环切换主题的核心逻辑
|
||
cycleTheme: function() {
|
||
const themes = ["system", "light", "dark"];
|
||
const currentTheme = this.getTheme();
|
||
const currentIndex = themes.indexOf(currentTheme);
|
||
const nextIndex = (currentIndex + 1) % themes.length;
|
||
this.setTheme(themes[nextIndex]);
|
||
},
|
||
applyTheme: function() {
|
||
let theme = this.getTheme();
|
||
if (theme === "system") {
|
||
theme = this.mediaQuery.matches ? "dark" : "light";
|
||
}
|
||
if (theme === "dark") {
|
||
this.html.classList.add("dark");
|
||
} else {
|
||
this.html.classList.remove("dark");
|
||
}
|
||
this.updateButtons();
|
||
this.updateCyclerIcon();
|
||
},
|
||
setTheme: function(theme) {
|
||
localStorage.setItem("theme", theme);
|
||
this.applyTheme();
|
||
},
|
||
getTheme: function() {
|
||
return localStorage.getItem("theme") || "system";
|
||
},
|
||
updateButtons: function() {
|
||
const currentTheme = this.getTheme();
|
||
this.buttons.forEach((btn) => {
|
||
if (btn.dataset.theme === currentTheme) {
|
||
btn.classList.add("bg-white", "dark:bg-zinc-700");
|
||
} else {
|
||
btn.classList.remove("bg-white", "dark:bg-zinc-700");
|
||
}
|
||
});
|
||
},
|
||
// 更新移动端循环按钮的图标
|
||
updateCyclerIcon: function() {
|
||
if (this.cyclerIconContainer) {
|
||
const currentTheme = this.getTheme();
|
||
if (this.icons[currentTheme]) {
|
||
this.cyclerIconContainer.innerHTML = this.icons[currentTheme];
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// frontend/js/layout/base.js
|
||
function initActiveNav() {
|
||
const currentPath = window.location.pathname;
|
||
const navLinks = document.querySelectorAll(".nav-link");
|
||
navLinks.forEach((link) => {
|
||
const linkPath = link.getAttribute("href");
|
||
if (linkPath && linkPath !== "/" && currentPath.startsWith(linkPath)) {
|
||
const wrapper = link.closest(".nav-item-wrapper");
|
||
if (wrapper) {
|
||
wrapper.dataset.active = "true";
|
||
}
|
||
}
|
||
});
|
||
}
|
||
function bridgeApiToGlobal() {
|
||
window.apiFetch = apiFetch;
|
||
window.apiFetchJson = apiFetchJson;
|
||
console.log("[Bridge] apiFetch and apiFetchJson are now globally available.");
|
||
}
|
||
function initLayout() {
|
||
console.log("[Init] Executing global layout JavaScript...");
|
||
initActiveNav();
|
||
themeManager.init();
|
||
bridgeApiToGlobal();
|
||
}
|
||
var base_default = initLayout;
|
||
|
||
// frontend/js/main.js
|
||
var pageModules = {
|
||
// 键 'dashboard' 对应一个函数,该函数调用 import() 返回一个 Promise
|
||
// esbuild 看到这个 import() 语法,就会自动将 dashboard.js 及其依赖打包成一个独立的 chunk 文件
|
||
"dashboard": () => import("./dashboard-CJJWKYPR.js"),
|
||
"keys": () => import("./keys-A2UAJYOX.js"),
|
||
"logs": () => import("./logs-4C4JG7BT.js")
|
||
// 'settings': () => import('./pages/settings.js'), // 未来启用 settings 页面
|
||
// 未来新增的页面,只需在这里添加一行映射,esbuild会自动处理
|
||
};
|
||
document.addEventListener("DOMContentLoaded", async () => {
|
||
base_default();
|
||
const allTabContainers = document.querySelectorAll("[data-sliding-tabs-container]");
|
||
allTabContainers.forEach((container) => new SlidingTabs(container));
|
||
const allSelectContainers = document.querySelectorAll("[data-custom-select-container]");
|
||
allSelectContainers.forEach((container) => new CustomSelect(container));
|
||
taskCenterManager.init();
|
||
const pageContainer = document.querySelector("[data-page-id]");
|
||
if (pageContainer) {
|
||
const pageId = pageContainer.dataset.pageId;
|
||
if (pageId && pageModules[pageId]) {
|
||
try {
|
||
const pageModule = await pageModules[pageId]();
|
||
if (pageModule.default && typeof pageModule.default === "function") {
|
||
pageModule.default();
|
||
}
|
||
} catch (error) {
|
||
console.error(`Failed to load module for page: ${pageId}`, error);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
window.modalManager = modalManager;
|
||
window.taskCenterManager = taskCenterManager;
|
||
window.toastManager = toastManager;
|
||
window.uiPatterns = uiPatterns;
|