Compare commits

...
3 Commits
Author SHA1 Message Date
hz4th_coder 070044f131 feat: 项目服务列表新增搜索/状态筛选/端口排序
- 新增关键字搜索框:按服务名称/描述/id/端口实时过滤
- 新增状态筛选按钮:全部状态/在线中(running|partial)/有后台(admin_url)
- 新增端口排序按钮:点击三态切换 无排序→升序→降序
- 筛选栏分组 + 分隔线,样式与现有主题一致
2026-08-24 15:09:21 +08:00
hz4th_coder 201d31d25e feat: 收录全部24个Web服务项目到项目面板
- 新增 param-hub-python/param-auto-manager/llama-cmd-gen/webtest-agent 等 22 个 Web 项目
- 每个项目含端口/启动停止命令/健康检查/后台地址/Git仓库/版本号
- 外部服务补充 Chroma 16010 + Embedding 16011
- 从 2 个项目扩展到 24 个项目
2026-08-24 13:14:08 +08:00
hz4th_coder 40213494df CDN资源本地化:tailwindcss.js和remixicon(含字体)下载到static/vendor,页面不再依赖外网CDN(cdn.tailwindcss.com/cdn.jsdelivr.net),解决外网波动导致页面load/networkidle超时 2026-08-09 23:33:37 +08:00
7 changed files with 3167 additions and 29 deletions
+418 -24
View File
@@ -3,42 +3,416 @@
"name": "hz1-PowerEdge-R730xd",
"description": "黄庄1号服务器 - PowerEdge R730xd",
"python_path": "/home/hz1/miniconda3/envs/openclaw/bin/python3.12",
"updated": "2026-06-01"
"updated": "2026-08-24"
},
"projects": [
{
"id": "param-hub-python",
"name": "参数中心",
"ports": [
16041
],
"directory": "param-hub-python",
"start_cmd": "mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'param-hub-python.*app.py' || true",
"health_url": "http://localhost:16041/",
"admin_url": "http://localhost:16041/admin",
"description": "参数配置管理平台,多业务线参数统一管理",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/param-hub-python.git",
"version": "v2.1.1",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "param-auto-manager",
"name": "参数自动化管理",
"ports": [
16043
],
"directory": "param-auto-manager",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16043/",
"admin_url": "",
"description": "参数数据自动化管理系统,自动同步/校验/告警",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/param-auto-manager.git",
"version": "v2.1.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "llama-cmd-gen",
"name": "Llama命令生成器",
"ports": [
16052
],
"directory": "llama-cmd-gen",
"start_cmd": "mkdir -p logs && nohup /home/hz1/miniconda3/envs/openclaw/bin/python3 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'llama-cmd-gen.*app.py' || true",
"health_url": "http://localhost:16052/",
"admin_url": "http://localhost:16052/admin",
"description": "Llama 模型推理命令生成器,GPU/量化/参数智能匹配",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/llama-cmd-gen.git",
"version": "v2.0.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "webtest-agent",
"name": "网页测试智能体",
"ports": [
16061
],
"directory": "webtest-agent",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16061/",
"admin_url": "",
"description": "AI 网页测试智能体,LLM 驱动浏览器自动化测试(agent-browser",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/webtest-agent.git",
"version": "v1.1.1",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "project-panel",
"name": "项目服务管理面板",
"ports": [
16022
],
"directory": "project-panel",
"start_cmd": "mkdir -p logs && nohup /home/hz1/miniconda3/envs/openclaw/bin/python3 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'project-panel.*app.py' || true",
"health_url": "http://localhost:16022/",
"admin_url": "http://localhost:16022",
"description": "统一管理所有 Web 服务项目,支持 Docker 监控、系统资源监控、Cron 管理",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/project-panel.git",
"version": "v3.4.1",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "project-panel-dev",
"name": "项目面板(开发版)",
"ports": [
16023
],
"directory": "project-panel-dev",
"start_cmd": "mkdir -p logs && nohup /home/hz1/miniconda3/envs/openclaw/bin/python3 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'project-panel-dev.*app.py' || true",
"health_url": "http://localhost:16023/",
"admin_url": "http://localhost:16023",
"description": "项目服务管理面板开发版(新功能预览)",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/project-panel-dev.git",
"version": "v3.7.5",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "universal-crawler",
"name": "通用爬虫系统",
"ports": [
16062
],
"directory": "universal-crawler",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16062/",
"admin_url": "http://localhost:16062/admin",
"description": "批量/定时/自动网页爬取,反爬处理 + 邮件通知",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/universal-crawler.git",
"version": "v1.4.3",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "ai-worker-platform",
"name": "AI Worker 平台",
"ports": [
16071
],
"directory": "ai-worker-platform",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16071/",
"admin_url": "http://localhost:16071/admin",
"description": "AI Worker 项目管理:任务看板/DAG编排/多Agent协作/评估/企业版",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/ai-worker-platform.git",
"version": "v3.4.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "companion-assistant",
"name": "随身助手",
"ports": [
16080
],
"directory": "companion-assistant",
"start_cmd": "cd backend && ./start.sh",
"stop_cmd": "cd backend && ./start.sh stop",
"health_url": "http://localhost:16080/",
"admin_url": "http://localhost:16080/#/admin",
"description": "AI 随身助手:流式对话/智能体/多类型项目/语音/附件",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/companion-assistant.git",
"version": "v0.2.1",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "nba-fan-hub",
"name": "NBA球迷大全",
"ports": [
16090
],
"directory": "nba-fan-hub",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16090/",
"admin_url": "http://localhost:16090/admin",
"description": "NBA 数据问答 + 球队/球员/比赛/新闻/人物/排名六大浏览页",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/nba-fan-hub.git",
"version": "v1.2.13",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "data-chart-tool",
"name": "数据可视化图表生成器",
"ports": [
16016
],
"directory": "data-chart-tool",
"start_cmd": "bash keepalive.sh",
"stop_cmd": "pkill -f 'data-chart-tool.*server.js' || true",
"health_url": "http://localhost:16016/",
"admin_url": "",
"description": "柱状/折线/混合/饼图/雷达图/双Y轴/表格/多图合并,服务端出图",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/data-chart-tool.git",
"version": "v1.20.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "stock-advisor",
"name": "智能荐股系统",
"ports": [
16095
],
"directory": "stock-advisor",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16095/",
"admin_url": "http://localhost:16095/admin",
"description": "六因子评分 + AI 深度研报 + RAG 资讯 + 量化策略回测 + 舆情监控",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/stock-advisor.git",
"version": "v1.8.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "news-tracker",
"name": "新闻智能跟踪",
"ports": [
16100
],
"directory": "news-tracker",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16100/",
"admin_url": "",
"description": "自动采集→打分→DeepSeek 深析→重要资讯实时/每日汇总邮件",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/news-tracker.git",
"version": "v1.0.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "pdf-to-markdown",
"name": "PDF→Markdown 转换",
"ports": [
16082
],
"directory": "pdf-to-markdown",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16082/",
"admin_url": "",
"description": "PDF 转 Markdown:文本页/影印页/混合页自适应 + 图片裁剪 + 表格转录",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/pdf-to-markdown.git",
"version": "v1.0.2",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "llm-speed-tester",
"name": "LLM 速度测试台",
"ports": [
16097
],
"directory": "llm-speed-tester",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16097/",
"admin_url": "",
"description": "多提供商 LLM 速度基准:TTFT/预填充/解码 tok/s + Excel/JSON 导出",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/llm-speed-tester.git",
"version": "v2.1.1",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "tech-blog",
"name": "技术博客系统",
"ports": [
16012
],
"directory": "tech-blog",
"start_cmd": "mkdir -p logs && nohup python3 run.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'tech-blog.*run.py' || true",
"health_url": "http://localhost:16012/",
"admin_url": "http://localhost:16012/admin",
"description": "Flask 技术文章展示 + 后台管理(文章/分类/标签/仪表盘)",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/tech-blog.git",
"version": "v1.1.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "worldcup-predictor",
"name": "世界杯预测系统",
"ports": [
16015
],
"directory": "worldcup-predictor",
"start_cmd": "./start.sh",
"stop_cmd": "pkill -f 'worldcup-predictor.*app.py' || true",
"health_url": "http://localhost:16015/",
"admin_url": "http://localhost:16015/admin",
"description": "世界杯比赛多算法预测对比 + 管理后台",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/worldcup-predictor.git",
"version": "v1.0.5",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "remote-host-agent",
"name": "远程主机代理",
"ports": [
16018
],
"directory": "remote-host-agent",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16018/api/health",
"admin_url": "",
"description": "机器B(无固定IP)监控/控制:HTTP上报 + 命令长轮询",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/remote-host-agent.git",
"version": "v1.0.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "llm-forum",
"name": "LLM 论坛",
"ports": [
16037
],
"directory": "llm-forum",
"start_cmd": "mkdir -p logs && nohup node server.js > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'llm-forum.*server.js' || true",
"health_url": "http://localhost:16037/",
"admin_url": "http://localhost:16037/admin",
"description": "大模型交流论坛:主题/回复/Markdown/分类/管理后台",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/llm-forum.git",
"version": "v1.0.2",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "music-platform",
"name": "音乐展示平台",
"ports": [
16031
],
"directory": "music-platform",
"start_cmd": "mkdir -p logs && nohup node server.js > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'music-platform.*server.js' || true",
"health_url": "http://localhost:16031/",
"admin_url": "http://localhost:16031/admin",
"description": "音乐平台:播放/歌词同步/搜索/歌手专辑歌单 + 后台管理",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/music-platform.git",
"version": "v1.0.1",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "video-platform",
"name": "短视频展示平台",
"ports": [
16035
],
"directory": "video-platform",
"start_cmd": "./start.sh",
"stop_cmd": "./start.sh stop",
"health_url": "http://localhost:16035/",
"admin_url": "http://localhost:16035/admin",
"description": "短视频平台:弹幕(WS实时)/点赞/分类 + 后台管理",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/video-platform.git",
"version": "v1.0.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "web-capture-api",
"name": "网页截图API",
"ports": [
16025
],
"directory": "web-capture-api",
"start_cmd": "mkdir -p logs && nohup /home/hz1/miniconda3/envs/openclaw/bin/python3 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'web-capture-api.*app.py' || true",
"health_url": "http://localhost:16025/",
"admin_url": "",
"description": "网页截图/HTML 提取 REST API + Web 界面",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/web-capture-api.git",
"version": "v1.4.2",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "agent-sessions-viewer",
"name": "会话历史查看器",
"ports": [
16039
],
"directory": "agent-sessions-viewer",
"start_cmd": "mkdir -p logs && nohup node server.js > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'agent-sessions-viewer.*server.js' || true",
"health_url": "http://localhost:16039/",
"admin_url": "http://localhost:16039/admin",
"description": "OpenClaw 智能体历史会话/消息查看器",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/agent-sessions-viewer.git",
"version": "v1.1.0",
"log_file": "logs/app.log",
"type": "web"
},
{
"id": "pdf-translate-web",
"name": "PDF翻译助手",
"type": "web",
"ports": [
16021
],
"directory": "pdf-translate-web",
"start_cmd": "mkdir -p logs && nohup /home/hz1/miniconda3/envs/openclaw/bin/python3.12 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f 'python.*pdf-translate-web.*app.py' || true",
"stop_cmd": "pkill -f 'pdf-translate-web.*app.py' || true",
"health_url": "http://localhost:16021/",
"description": "英文PDF翻译中文网站,支持用户系统、会员体系",
"admin_url": "http://localhost:16021/admin",
"git_repo": "http://121.40.164.32:12007/coder/pdf-translate-web",
"version": "v2.0.0",
"log_file": ""
},
{
"id": "project-panel",
"name": "项目服务管理面板",
"type": "web",
"ports": [
16022
],
"directory": "project-panel",
"start_cmd": "mkdir -p logs && nohup /home/hz1/miniconda3/envs/openclaw/bin/python3.12 app.py > logs/app.log 2>&1 & disown",
"stop_cmd": "pkill -f python.*project-panel.*app.py || true",
"health_url": "http://localhost:16022/",
"description": "统一管理所有 Web 服务项目,支持 Docker 监控、系统资源监控、Cron 管理",
"admin_url": "http://localhost:16022",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/project-panel",
"version": "v3.0.0",
"log_file": "logs/app.log"
"description": "英文PDF翻译中文网站,支持用户系统、会员体系",
"git_repo": "http://121.40.164.32:12007/hz4th_coder/pdf-translate-web.git",
"version": "v2.10.1",
"log_file": "logs/app.log",
"type": "web"
}
],
"external_services": [
@@ -51,6 +425,26 @@
],
"health_url": "http://localhost:16001/health",
"description": "高性能搜索引擎,只读监控"
},
{
"id": "chroma",
"name": "Chroma 向量库",
"type": "external",
"ports": [
16010
],
"health_url": "http://localhost:16010/",
"description": "向量数据库(RAG 项目共用)"
},
{
"id": "embedding",
"name": "Embedding 服务",
"type": "external",
"ports": [
16011
],
"health_url": "http://localhost:16011/",
"description": "bge-large-zh-v1.5 向量化 + rerank 精排"
}
]
}
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
+83
View File
File diff suppressed because one or more lines are too long
+83 -5
View File
@@ -5,8 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目服务管理面板 v3.3.1</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📊</text></svg>">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
<script src="/static/vendor/tailwindcss.js"></script>
<link href="/static/vendor/remixicon/remixicon.css" rel="stylesheet">
<style>
body { background: #0f172a; }
.card { background: #1e293b; border: 1px solid #334155; }
@@ -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;
}