docs: add comprehensive README.md and fix bugs

- add README.md with usage and deployment guide
- fix category sync logic in backend
- fix URL overflow in admin services list
- fix data caching issues in front-end and back-end
- add 'View Front-end' button in admin dashboard
This commit is contained in:
OpenClaw Agent
2026-02-13 07:00:49 +08:00
parent 872526505e
commit 521cd9ba42
10 changed files with 2884 additions and 218 deletions

View File

@@ -10,7 +10,10 @@
<h1>📂 分类管理</h1>
<a href="/admin" class="back-link">← 返回首页</a>
</div>
<button class="btn btn-primary" onclick="showCreateModal()">+ 新建分类</button>
<div class="header-right">
<button class="btn btn-outline" onclick="window.open('/', '_blank')" style="margin-right: 10px;">查看前台 ↗</button>
<button class="btn btn-primary" onclick="showCreateModal()">+ 新建分类</button>
</div>
</div>
<!-- 分类列表 -->
@@ -272,6 +275,17 @@
gap: 10px;
}
.btn-outline {
background: transparent;
border: 1px solid rgba(255,255,255,0.3);
color: #fff;
}
.btn-outline:hover {
background: rgba(255,255,255,0.1);
border-color: #fff;
}
.btn {
padding: 10px 20px;
border: none;

View File

@@ -10,7 +10,10 @@
<h1>🧭 ToNav 管理后台</h1>
<span class="username" id="username">加载中...</span>
</div>
<button class="btn btn-primary" onclick="location.href='/admin/logout'">退出登录</button>
<div class="header-actions">
<button class="btn btn-outline" onclick="window.open('/', '_blank')">查看前台 ↗</button>
<button class="btn btn-primary" onclick="location.href='/admin/logout'">退出登录</button>
</div>
</div>
<!-- 主内容区 -->
@@ -230,6 +233,23 @@
gap: 10px;
}
.header-actions {
display: flex;
gap: 10px;
align-items: center;
}
.btn-outline {
background: transparent;
border: 1px solid rgba(255,255,255,0.3);
color: #fff;
}
.btn-outline:hover {
background: rgba(255,255,255,0.1);
border-color: #fff;
}
.btn {
padding: 12px 24px;
border: none;

View File

@@ -10,7 +10,10 @@
<h1>📡 服务管理</h1>
<a href="/admin" class="back-link">← 返回首页</a>
</div>
<button class="btn btn-primary" onclick="showCreateModal()">+ 新建服务</button>
<div class="header-right">
<button class="btn btn-outline" onclick="window.open('/', '_blank')" style="margin-right: 10px;">查看前台 ↗</button>
<button class="btn btn-primary" onclick="showCreateModal()">+ 新建服务</button>
</div>
</div>
<!-- 服务列表 -->
@@ -193,6 +196,10 @@
.service-url {
font-size: 13px;
color: #8c8c8c;
word-break: break-all;
overflow-wrap: break-word;
max-width: 100%;
display: block;
}
.service-meta {
@@ -386,6 +393,17 @@
border-top: 1px solid #f0f0f0;
}
.btn-outline {
background: transparent;
border: 1px solid rgba(255,255,255,0.3);
color: #fff;
}
.btn-outline:hover {
background: rgba(255,255,255,0.1);
border-color: #fff;
}
.btn {
padding: 10px 20px;
border: none;
@@ -450,7 +468,8 @@
// 加载服务列表
async function loadServices() {
try {
const response = await fetch('/api/admin/services');
// 添加时间戳防止缓存
const response = await fetch(`/api/admin/services?t=${new Date().getTime()}`);
if (!response.ok) {
window.location.href = '/admin/login';
return;
@@ -520,22 +539,55 @@
});
}
// 加载分类列表到下拉框
async function loadCategoriesToSelect() {
try {
const response = await fetch('/api/admin/categories');
const categories = await response.json();
const select = document.getElementById('serviceCategory');
const currentValue = select.value;
select.innerHTML = '';
categories.forEach(cat => {
const option = document.createElement('option');
option.value = cat.name;
option.textContent = cat.name;
select.appendChild(option);
});
// 尝试恢复之前选中的值
if (currentValue) {
const exists = categories.find(c => c.name === currentValue);
if (exists) {
select.value = currentValue;
}
}
} catch (err) {
console.error('加载分类失败:', err);
}
}
// 显示创建弹窗
function showCreateModal() {
async function showCreateModal() {
document.getElementById('modalTitle').textContent = '新建服务';
document.getElementById('serviceId').value = '';
document.getElementById('serviceForm').reset();
document.getElementById('serviceEnabled').checked = true;
document.getElementById('serviceSort').value = '0';
document.getElementById('healthUrlGroup').style.display = 'none';
await loadCategoriesToSelect();
document.getElementById('serviceModal').classList.add('active');
}
// 编辑服务
function editService(id) {
async function editService(id) {
const service = allServices.find(s => s.id === id);
if (!service) return;
await loadCategoriesToSelect();
document.getElementById('modalTitle').textContent = '编辑服务';
document.getElementById('serviceId').value = service.id;
document.getElementById('serviceName').value = service.name;