新增管理器

This commit is contained in:
2026-05-26 11:53:28 +08:00
parent e9ac2ae1a8
commit 03d7eca139
7 changed files with 391 additions and 0 deletions

View File

@@ -1,3 +1,136 @@
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>管理器 - Dashboard</title>
<link rel="stylesheet" href="/static/css/my-style.css">
<style>
.tabs { display:flex; gap:8px; margin-bottom:12px; }
.tab { padding:8px 12px; border:1px solid #ddd; cursor:pointer; }
.active { background:#f0f0f0; }
</style>
</head>
<body>
<header>
<h1>網站管理器</h1>
</header>
<div class="tabs">
<div class="tab active" data-tab="overview">概覽</div>
<div class="tab" data-tab="settings">網站設定</div>
<div class="tab" data-tab="pages">頁面管理</div>
<div class="tab" data-tab="media">媒體庫</div>
</div>
<div id="content">
<div id="overview" class="panel">選擇一個專案在右側編輯。</div>
<div id="settings" class="panel" style="display:none">
<h2>網站設定</h2>
<form id="settingsForm">
<label>網站標題<br><input id="siteTitle" type="text"></label><br>
<label>描述<br><textarea id="siteDesc"></textarea></label><br>
<button id="saveSettings">儲存設定</button>
</form>
</div>
<div id="pages" class="panel" style="display:none">
<h2>頁面管理</h2>
<div id="pagesTree"></div>
</div>
<div id="media" class="panel" style="display:none">
<h2>媒體庫</h2>
<input type="file" id="mediaFile"><button id="uploadBtn">上傳</button>
<div id="mediaGrid"></div>
</div>
</div>
<script>
document.querySelectorAll('.tab').forEach(t=>t.addEventListener('click',e=>{
document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
t.classList.add('active');
document.querySelectorAll('.panel').forEach(p=>p.style.display='none');
document.getElementById(t.dataset.tab).style.display='block';
}));
// Dashboard logic: load settings and media for ?slug=...
(function(){
const params = new URLSearchParams(location.search);
const slug = params.get('slug');
if(!slug){
document.getElementById('overview').innerText = '請在網址列加入 ?slug=your-site 以管理專案。';
return;
}
// load settings
async function loadSettings(){
const res = await fetch(`/api/projects/${encodeURIComponent(slug)}/settings`);
if(!res.ok) return;
const data = await res.json();
document.getElementById('siteTitle').value = data.title || '';
document.getElementById('siteDesc').value = data.description || '';
}
document.getElementById('saveSettings').addEventListener('click', async function(e){
e.preventDefault();
const body = {
title: document.getElementById('siteTitle').value,
description: document.getElementById('siteDesc').value
};
const res = await fetch(`/api/projects/${encodeURIComponent(slug)}/settings`, {
method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body)
});
if(res.ok) alert('設定已儲存'); else alert('儲存失敗');
});
// media upload
async function listMedia(){
const res = await fetch(`/api/projects/${encodeURIComponent(slug)}/media/list`);
const grid = document.getElementById('mediaGrid');
grid.innerHTML = '';
if(!res.ok) return;
const items = await res.json();
items.reverse();
items.forEach(it => {
const card = document.createElement('div');
card.className = 'media-card';
card.style.border = '1px solid #ddd'; card.style.padding='8px'; card.style.display='inline-block'; card.style.margin='6px';
const img = document.createElement('img');
img.src = it.thumb || it.url;
img.style.width = '160px'; img.style.height='auto';
const info = document.createElement('div');
info.innerHTML = `<div>${it.original_name || it.filename}</div><div style="font-size:12px;color:#666">${it.uploaded_at||''}</div>`;
const btnCopy = document.createElement('button'); btnCopy.textContent='複製連結';
btnCopy.addEventListener('click', ()=>{ navigator.clipboard.writeText(it.url); alert('已複製連結'); });
const btnDel = document.createElement('button'); btnDel.textContent='刪除'; btnDel.style.marginLeft='6px';
btnDel.addEventListener('click', async ()=>{
if(!confirm('確定要刪除此媒體檔?')) return;
// rel_path expected like media/images/YYYY-MM/filename
const rel = `media/images/${it.date}/${it.filename}`;
const del = await fetch(`/api/projects/${encodeURIComponent(slug)}/media/${encodeURIComponent(rel)}`, {method:'DELETE'});
if(del.ok){ alert('刪除成功'); listMedia(); } else alert('刪除失敗');
});
card.appendChild(img); card.appendChild(info); card.appendChild(btnCopy); card.appendChild(btnDel);
grid.appendChild(card);
});
}
document.getElementById('uploadBtn').addEventListener('click', async ()=>{
const fileInput = document.getElementById('mediaFile');
if(!fileInput.files.length){ alert('請選擇檔案'); return; }
const fd = new FormData(); fd.append('file', fileInput.files[0]);
const res = await fetch(`/api/projects/${encodeURIComponent(slug)}/media/upload`, {method:'POST', body: fd});
if(res.ok){ alert('上傳完成'); fileInput.value=''; listMedia(); } else { alert('上傳失敗'); }
});
// init
loadSettings(); listMedia();
})();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-TW">
<head>