Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
070044f131 |
+81
-3
@@ -28,6 +28,12 @@
|
||||
.nav-btn i { font-size: 20px; color: #94a3b8; }
|
||||
.nav-btn:hover i { color: #f1f5f9; }
|
||||
.filter-bar { position: sticky; top: 0; z-index: 50; display: flex; gap: 8px; padding: 12px 0; background: #0f172a; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.filter-search { padding: 8px 12px; border-radius: 8px; background: #1e293b; border: 1px solid #334155; color: #f1f5f9; font-size: 14px; outline: none; min-width: 200px; transition: border-color 0.2s; }
|
||||
.filter-search:focus { border-color: #3b82f6; }
|
||||
.filter-search::placeholder { color: #64748b; }
|
||||
.filter-divider { width: 1px; height: 24px; background: #334155; margin: 0 4px; }
|
||||
.filter-bar-btn[data-status].active { background: #8b5cf6; border-color: #8b5cf6; }
|
||||
.filter-bar-btn.sort-active { background: #0ea5e9; border-color: #0ea5e9; color: #fff; }
|
||||
.filter-bar-btn { display: flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 8px; background: #1e293b; border: 1px solid #334155; color: #94a3b8; font-size: 14px; cursor: pointer; transition: all 0.2s; }
|
||||
.filter-bar-btn:hover { background: #334155; color: #f1f5f9; }
|
||||
.filter-bar-btn.active { background: #3b82f6; border-color: #3b82f6; color: #fff; }
|
||||
@@ -171,11 +177,18 @@
|
||||
|
||||
<!-- 筛选器(顶部固定) -->
|
||||
<div class="filter-bar">
|
||||
<input type="text" id="projectSearch" class="filter-search" placeholder="🔍 搜索服务名称/描述/端口..." oninput="setSearch(this.value)" />
|
||||
<button onclick="filterType('all')" class="filter-bar-btn active" data-type="all"><i class="ri-apps-line"></i> 全部</button>
|
||||
<button onclick="filterType('web')" class="filter-bar-btn" data-type="web"><i class="ri-server-line"></i> Web服务</button>
|
||||
<button onclick="filterType('custom')" class="filter-bar-btn" data-type="custom"><i class="ri-global-line"></i> 自定义</button>
|
||||
<button onclick="filterType('cli')" class="filter-bar-btn" data-type="cli"><i class="ri-terminal-box-line"></i> CLI工具</button>
|
||||
<button onclick="filterType('extension')" class="filter-bar-btn" data-type="extension"><i class="ri-chrome-line"></i> 插件</button>
|
||||
<span class="filter-divider"></span>
|
||||
<button onclick="setStatusFilter('')" class="filter-bar-btn" data-status=""><i class="ri-globe-line"></i> 全部状态</button>
|
||||
<button onclick="setStatusFilter('online')" class="filter-bar-btn" data-status="online"><i class="ri-wifi-line"></i> 在线中</button>
|
||||
<button onclick="setStatusFilter('admin')" class="filter-bar-btn" data-status="admin"><i class="ri-settings-3-line"></i> 有后台</button>
|
||||
<span class="filter-divider"></span>
|
||||
<button onclick="togglePortSort()" id="portSortBtn" class="filter-bar-btn" title="按端口号排序"><i class="ri-sort-asc"></i><span>端口排序</span></button>
|
||||
<button onclick="showAddServiceModal()" class="filter-bar-btn add-btn"><i class="ri-add-circle-line"></i> 新增服务</button>
|
||||
</div>
|
||||
|
||||
@@ -1001,6 +1014,9 @@
|
||||
let cronTasks = [];
|
||||
let currentTab = 'projects';
|
||||
let currentFilter = 'all';
|
||||
let searchKeyword = '';
|
||||
let statusFilter = ''; // '' = all, 'online' = running/partial, 'admin' = has admin_url
|
||||
let portSort = ''; // '' = none, 'asc', 'desc'
|
||||
let externalIp = localStorage.getItem('externalIp') || '192.168.2.17';
|
||||
let enableLogs = localStorage.getItem('enableLogs') === 'true';
|
||||
|
||||
@@ -1526,11 +1542,40 @@
|
||||
function filterType(type) {
|
||||
currentFilter = type;
|
||||
document.querySelectorAll('.filter-bar-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.type === type);
|
||||
if (btn.dataset.type !== undefined) {
|
||||
btn.classList.toggle('active', btn.dataset.type === type);
|
||||
}
|
||||
});
|
||||
renderProjects();
|
||||
}
|
||||
|
||||
function setSearch(val) {
|
||||
searchKeyword = (val || '').trim().toLowerCase();
|
||||
renderProjects();
|
||||
}
|
||||
|
||||
function setStatusFilter(status) {
|
||||
statusFilter = status;
|
||||
document.querySelectorAll('.filter-bar-btn[data-status]').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.status === status);
|
||||
});
|
||||
renderProjects();
|
||||
}
|
||||
|
||||
function togglePortSort() {
|
||||
if (!portSort) portSort = 'asc';
|
||||
else if (portSort === 'asc') portSort = 'desc';
|
||||
else portSort = '';
|
||||
const btn = document.getElementById('portSortBtn');
|
||||
btn.classList.toggle('sort-active', !!portSort);
|
||||
const icon = btn.querySelector('i');
|
||||
const label = btn.querySelector('span');
|
||||
if (portSort === 'asc') { icon.className = 'ri-sort-asc'; label.textContent = '端口↑'; }
|
||||
else if (portSort === 'desc') { icon.className = 'ri-sort-desc'; label.textContent = '端口↓'; }
|
||||
else { icon.className = 'ri-sort-asc'; label.textContent = '端口排序'; }
|
||||
renderProjects();
|
||||
}
|
||||
|
||||
function renderProjects() {
|
||||
const list = document.getElementById('projectsList');
|
||||
|
||||
@@ -1538,10 +1583,43 @@
|
||||
const customServices = getCustomServices();
|
||||
const allProjects = [...projects, ...customServices];
|
||||
|
||||
const filtered = currentFilter === 'all' ? allProjects : allProjects.filter(p => p.type === currentFilter || (currentFilter === 'web' && p.isCustom));
|
||||
let filtered = currentFilter === 'all' ? allProjects : allProjects.filter(p => p.type === currentFilter || (currentFilter === 'web' && p.isCustom));
|
||||
|
||||
// 关键字搜索:名称/描述/id/端口
|
||||
if (searchKeyword) {
|
||||
filtered = filtered.filter(p => {
|
||||
const haystack = [
|
||||
p.name || '',
|
||||
p.description || '',
|
||||
p.id || '',
|
||||
...(p.ports || []),
|
||||
p.url || ''
|
||||
].join(' ').toLowerCase();
|
||||
return haystack.includes(searchKeyword);
|
||||
});
|
||||
}
|
||||
|
||||
// 状态筛选:在线中(running/partial)/ 有后台(admin_url)
|
||||
if (statusFilter === 'online') {
|
||||
filtered = filtered.filter(p => {
|
||||
const st = p.status?.status;
|
||||
return st === 'running' || st === 'partial';
|
||||
});
|
||||
} else if (statusFilter === 'admin') {
|
||||
filtered = filtered.filter(p => !!p.admin_url);
|
||||
}
|
||||
|
||||
// 端口排序:升/降序
|
||||
if (portSort) {
|
||||
filtered = filtered.slice().sort((a, b) => {
|
||||
const pa = a.ports && a.ports.length ? a.ports[0] : 0;
|
||||
const pb = b.ports && b.ports.length ? b.ports[0] : 0;
|
||||
return portSort === 'asc' ? pa - pb : pb - pa;
|
||||
});
|
||||
}
|
||||
|
||||
if (filtered.length === 0) {
|
||||
list.innerHTML = '<div class="text-center py-12 text-gray-400"><i class="ri-folder-open-line text-4xl"></i><p class="mt-2">暂无项目</p></div>';
|
||||
list.innerHTML = '<div class="text-center py-12 text-gray-400"><i class="ri-folder-open-line text-4xl"></i><p class="mt-2">暂无匹配项目</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user