新增管理器
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -108,6 +108,9 @@
|
||||
<i class="la la-external-link-alt fs-6"></i>
|
||||
</a>
|
||||
|
||||
<!-- 快速設定按鈕(點擊開啟儀表板的設定) -->
|
||||
<button id="quick-settings-btn" class="btn btn-light px-1" title="快速設定">⚙️</button>
|
||||
|
||||
<div class="btn-group responsive-btns" role="group">
|
||||
|
||||
<button type="button" class="btn btn-light btn-sm px-1 me-1" data-bs-toggle="dropdown"
|
||||
@@ -2308,6 +2311,21 @@ window.VVVEB_PROJECT_SLUG = "{% endraw %}{{ slug | safe }}{% raw %}";
|
||||
</script>
|
||||
<script src="/static/js/my-editor.js?v={% endraw %}{{ range(1, 100000) | random }}{% raw %}"></script>
|
||||
|
||||
<script>
|
||||
// Quick settings button opens dashboard for current project
|
||||
try{
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
var btn = document.getElementById('quick-settings-btn');
|
||||
if(!btn) return;
|
||||
btn.addEventListener('click', function(e){
|
||||
var slug = window.VVVEB_PROJECT_SLUG || '';
|
||||
var url = '/dashboard' + (slug ? ('?slug='+encodeURIComponent(slug)) : '');
|
||||
window.open(url, '_blank');
|
||||
});
|
||||
});
|
||||
}catch(e){console.warn(e)}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user