新的樹狀管理器

This commit is contained in:
2026-05-26 12:23:29 +08:00
parent 56f9a703b8
commit ee5a54ea2c
5 changed files with 246 additions and 3 deletions

View File

@@ -340,6 +340,24 @@
const tree = await res.json();
const container = document.getElementById('pagesTree');
container.innerHTML = '';
container.addEventListener('dragover', e => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
});
container.addEventListener('drop', e => {
e.preventDefault();
const nameEnc = e.dataTransfer.getData('text/name');
if (!nameEnc) return;
const name = decodeURIComponent(nameEnc);
const src = container.querySelector(`[data-name='${CSS.escape(name)}']`);
if (!src) return;
let rootUl = container.querySelector(':scope > ul');
if (!rootUl) {
rootUl = document.createElement('ul');
container.appendChild(rootUl);
}
rootUl.appendChild(src);
});
function createLi(item) {
const li = document.createElement('li');
@@ -347,6 +365,9 @@
li.draggable = true;
li.dataset.type = item.type || 'file';
li.dataset.name = item.name || item.title || '';
li.dataset.showInNav = item.show_in_nav !== false ? 'true' : 'false';
li.dataset.isHomepage = item.is_homepage ? 'true' : 'false';
li.dataset.requiresPassword = item.requires_password ? 'true' : 'false';
const titleWrap = document.createElement('span');
titleWrap.className = 'tree-title';
@@ -356,7 +377,56 @@
btns.className = 'tree-item-actions';
btns.innerHTML = "<button class='btn btn-ghost btn-sm btn-toggle' title='展開/收合'>▼</button>";
const meta = document.createElement('span');
meta.className = 'tree-item-meta';
meta.innerHTML = `
<button class='btn btn-ghost btn-sm btn-nav' title='顯示於導覽列'>☰</button>
<button class='btn btn-ghost btn-sm btn-homepage' title='設為首頁'>⭐</button>
<button class='btn btn-ghost btn-sm btn-password' title='需要密碼'>🔒</button>
`;
const btnNav = meta.querySelector('.btn-nav');
const btnHome = meta.querySelector('.btn-homepage');
const btnPassword = meta.querySelector('.btn-password');
const updateMetaState = () => {
const showNav = li.dataset.showInNav === 'true';
const isHomepage = li.dataset.isHomepage === 'true';
const requiresPassword = li.dataset.requiresPassword === 'true';
btnNav.classList.toggle('active', showNav);
btnNav.title = showNav ? '顯示於導覽列' : '從導覽列隱藏';
btnHome.classList.toggle('active', isHomepage);
btnHome.title = isHomepage ? '首頁' : '設為首頁';
btnPassword.classList.toggle('active', requiresPassword);
btnPassword.title = requiresPassword ? '需要密碼' : '不需要密碼';
if (li.dataset.type !== 'file') {
btnHome.disabled = true;
btnHome.title = '僅限單一頁面';
}
};
updateMetaState();
btnNav.addEventListener('click', () => {
li.dataset.showInNav = li.dataset.showInNav === 'true' ? 'false' : 'true';
updateMetaState();
});
btnPassword.addEventListener('click', () => {
li.dataset.requiresPassword = li.dataset.requiresPassword === 'true' ? 'false' : 'true';
updateMetaState();
});
btnHome.addEventListener('click', () => {
if (li.dataset.type !== 'file') return;
container.querySelectorAll('.tree-item').forEach(other => {
other.dataset.isHomepage = 'false';
});
container.querySelectorAll('.btn-homepage').forEach(button => button.classList.remove('active'));
li.dataset.isHomepage = 'true';
updateMetaState();
});
li.appendChild(titleWrap);
li.appendChild(meta);
li.appendChild(btns);
if (item.children && item.children.length) {
@@ -374,6 +444,7 @@
li.addEventListener('dragleave', () => { li.classList.remove('tree-drop-hover'); });
li.addEventListener('drop', e => {
e.preventDefault();
e.stopPropagation();
li.classList.remove('tree-drop-hover');
const nameEnc = e.dataTransfer.getData('text/name');
if (!nameEnc) return;
@@ -422,7 +493,14 @@
const title = li.querySelector('.tree-title')?.textContent || '';
const name = li.dataset.name || '';
const type = li.dataset.type || 'file';
const obj = { name, title, type };
const obj = {
name,
title,
type,
show_in_nav: li.dataset.showInNav === 'true',
is_homepage: li.dataset.isHomepage === 'true',
requires_password: li.dataset.requiresPassword === 'true',
};
const childUl = li.querySelector(':scope > ul');
if (childUl) obj.children = walk(childUl);
arr.push(obj);