Files
ALL-teach_sys/index.html
KQL 5988d75329 改进端口状态检测逻辑
- 修复误报问题:默认为停止状态,需要确认后才显示运行中
- 简化检测逻辑,使用更可靠的Promise处理
- 添加简化版状态检测页面 status-checker.html
- 减少误判,提高检测准确性
2025-09-24 15:14:38 +08:00

638 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>教务系统 - 产业导航</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.header {
text-align: center;
color: white;
margin-bottom: 30px;
animation: fadeInDown 0.8s ease;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.header p {
font-size: 1.1rem;
opacity: 0.95;
margin-bottom: 20px;
}
.control-panel {
background: white;
border-radius: 15px;
padding: 25px;
margin-bottom: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
animation: fadeIn 0.8s ease;
}
.control-panel h2 {
color: #333;
margin-bottom: 20px;
font-size: 1.3rem;
}
.control-buttons {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
}
.control-btn {
padding: 12px 25px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
color: white;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-start-all {
background: linear-gradient(135deg, #667eea, #764ba2);
}
.btn-start-all:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.btn-stop-all {
background: linear-gradient(135deg, #f093fb, #f5576c);
}
.btn-stop-all:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(245, 87, 108, 0.4);
}
.btn-refresh {
background: linear-gradient(135deg, #4facfe, #00f2fe);
}
.btn-refresh:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(79, 172, 254, 0.4);
}
.btn-launcher {
background: linear-gradient(135deg, #fa709a, #fee140);
}
.btn-launcher:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(250, 112, 154, 0.4);
}
.platform-info {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 15px;
margin-top: 20px;
color: white;
text-align: center;
}
.platform-info code {
background: rgba(0, 0, 0, 0.2);
padding: 2px 8px;
border-radius: 4px;
font-family: 'Consolas', 'Monaco', monospace;
}
.container {
max-width: 1400px;
width: 100%;
}
.industry-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 25px;
margin-bottom: 30px;
animation: fadeInUp 0.8s ease;
}
.industry-card {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.industry-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 5px;
background: linear-gradient(90deg, #667eea, #764ba2);
}
.industry-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 50px rgba(0,0,0,0.15);
}
.industry-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.industry-name {
font-size: 1.2rem;
font-weight: 600;
color: #333;
}
.industry-number {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
font-weight: bold;
}
.industry-info {
margin-bottom: 20px;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 8px;
color: #666;
font-size: 0.95rem;
}
.info-label {
font-weight: 500;
margin-right: 8px;
color: #999;
}
.status {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 500;
}
.status-running {
background: #d4f4dd;
color: #2e7d46;
}
.status-stopped {
background: #ffeaa7;
color: #d63031;
}
.status-checking {
background: #e3f2fd;
color: #1976d2;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
animation: pulse 2s infinite;
}
.status-running .status-dot {
background: #2e7d46;
}
.status-stopped .status-dot {
background: #d63031;
animation: none;
}
.industry-actions {
display: flex;
gap: 10px;
}
.btn {
flex: 1;
padding: 10px;
border: none;
border-radius: 8px;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #f5f5f5;
color: #666;
}
.btn-secondary:hover {
background: #e0e0e0;
}
.footer {
text-align: center;
color: white;
margin-top: 40px;
padding: 20px;
opacity: 0.9;
}
.instruction-box {
background: rgba(255, 255, 255, 0.95);
border-radius: 10px;
padding: 20px;
margin-bottom: 25px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.instruction-box h3 {
color: #333;
margin-bottom: 15px;
font-size: 1.1rem;
}
.instruction-box ol {
margin-left: 20px;
color: #666;
line-height: 1.8;
}
.instruction-box code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
color: #d63031;
font-family: Consolas, Monaco, monospace;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(46, 125, 70, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(46, 125, 70, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(46, 125, 70, 0);
}
}
</style>
</head>
<body>
<div class="header">
<h1>🎓 教务系统产业管理中心</h1>
<p>智能化多产业教务管理平台 - 支持12个产业独立运行</p>
</div>
<div class="container">
<!-- Windows用户使用说明 -->
<div class="instruction-box">
<h3>💡 Windows 用户快速启动指南</h3>
<ol>
<li><strong>方法一:</strong>双击运行 <code>start-industry.bat</code> 文件</li>
<li><strong>方法二:</strong>打开命令提示符CMD输入 <code>start-industry.bat</code></li>
<li><strong>方法三:</strong>使用 PowerShell 运行 <code>.\start-industry.ps1</code></li>
<li><strong>方法四:</strong>直接在本页面点击下方的控制按钮</li>
</ol>
</div>
<!-- 控制面板 -->
<div class="control-panel">
<h2>🎛️ 快速控制中心</h2>
<div class="control-buttons">
<button class="control-btn btn-start-all" onclick="startAllIndustries()">
▶️ 启动所有产业
</button>
<button class="control-btn btn-stop-all" onclick="stopAllIndustries()">
⏹️ 停止所有产业
</button>
<button class="control-btn btn-refresh" onclick="checkAllStatus()">
🔄 刷新状态
</button>
<a href="#" class="control-btn btn-launcher" onclick="openLauncher()">
🚀 打开启动器 (Windows)
</a>
</div>
<div class="platform-info">
<p>💻 当前系统: <span id="platformInfo"></span></p>
<p>📁 项目路径: <code id="projectPath"></code></p>
</div>
</div>
<!-- 产业卡片网格 -->
<div class="industry-grid" id="industryGrid"></div>
<!-- 页脚 -->
<div class="footer">
<p>⏰ 状态每10秒自动刷新 | 📖 <a href="README.md" style="color: white;">查看文档</a></p>
<p style="margin-top: 10px; opacity: 0.8;">© 2024 教务系统 - 智能产业管理平台</p>
</div>
</div>
<script>
// 产业配置
const industries = [
{ id: 1, name: '文旅产业', dir: 'frontend', port: 5150, icon: '🏛️' },
{ id: 2, name: '智能制造', dir: 'frontend_智能制造', port: 5151, icon: '🏭' },
{ id: 3, name: '智能开发', dir: 'frontend_智能开发', port: 5152, icon: '💻' },
{ id: 4, name: '财经商贸', dir: 'frontend_财经商贸', port: 5153, icon: '💰' },
{ id: 5, name: '视觉设计', dir: 'frontend_视觉设计', port: 5154, icon: '🎨' },
{ id: 6, name: '交通物流', dir: 'frontend_交通物流', port: 5155, icon: '🚚' },
{ id: 7, name: '大健康', dir: 'frontend_大健康', port: 5156, icon: '🏥' },
{ id: 8, name: '土木水利', dir: 'frontend_土木水利', port: 5157, icon: '🏗️' },
{ id: 9, name: '食品产业', dir: 'frontend_食品', port: 5158, icon: '🍽️' },
{ id: 10, name: '化工产业', dir: 'frontend_化工', port: 5159, icon: '⚗️' },
{ id: 11, name: '能源产业', dir: 'frontend_能源', port: 5160, icon: '⚡' },
{ id: 12, name: '环保产业', dir: 'frontend_环保', port: 5161, icon: '🌱' }
];
// 检测平台信息
function detectPlatform() {
const platform = navigator.platform;
const userAgent = navigator.userAgent;
let osInfo = '未知系统';
if (platform.indexOf('Win') !== -1) {
osInfo = 'Windows';
} else if (platform.indexOf('Mac') !== -1) {
osInfo = 'macOS';
} else if (platform.indexOf('Linux') !== -1) {
osInfo = 'Linux';
}
document.getElementById('platformInfo').textContent = osInfo;
document.getElementById('projectPath').textContent = window.location.pathname.replace('/index.html', '');
}
// 创建产业卡片
function createIndustryCard(industry) {
return `
<div class="industry-card">
<div class="industry-header">
<span class="industry-name">${industry.icon} ${industry.name}</span>
<span class="industry-number">${industry.id}</span>
</div>
<div class="industry-info">
<div class="info-item">
<span class="info-label">端口:</span>
<span>${industry.port}</span>
</div>
<div class="info-item">
<span class="info-label">目录:</span>
<span>${industry.dir}</span>
</div>
<div class="info-item">
<span class="info-label">状态:</span>
<span class="status status-checking" id="status-${industry.port}">
<span class="status-dot"></span>
检查中...
</span>
</div>
</div>
<div class="industry-actions">
<button class="btn btn-primary" onclick="openIndustry(${industry.port})">
访问系统
</button>
<button class="btn btn-secondary" onclick="toggleIndustry(${industry.id})">
启动/停止
</button>
</div>
</div>
`;
}
// 初始化页面
function initPage() {
detectPlatform();
const grid = document.getElementById('industryGrid');
grid.innerHTML = industries.map(industry => createIndustryCard(industry)).join('');
checkAllStatus();
}
// 检查单个产业状态
async function checkStatus(port) {
const statusEl = document.getElementById(`status-${port}`);
// 默认设置为停止状态
let isRunning = false;
// 创建一个Promise来处理超时
const checkPromise = new Promise((resolve) => {
// 使用Image对象检测这是最可靠的跨域方法
const img = new Image();
let hasResponded = false;
// 设置超时
const timeout = setTimeout(() => {
if (!hasResponded) {
hasResponded = true;
resolve(false); // 超时则认为服务停止
}
}, 1500);
img.onload = () => {
if (!hasResponded) {
hasResponded = true;
clearTimeout(timeout);
resolve(true); // 加载成功说明服务运行中
}
};
img.onerror = () => {
if (!hasResponded) {
hasResponded = true;
clearTimeout(timeout);
// 图片加载失败尝试fetch检测
fetch(`http://localhost:${port}/`, {
mode: 'no-cors',
cache: 'no-cache'
}).then(() => {
// 如果fetch没有抛出错误说明服务可能在运行
// 但由于CORS我们无法获取响应内容
// 这种情况下我们需要更谨慎的判断
// 创建一个iframe来尝试加载页面
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = `http://localhost:${port}/`;
const iframeTimeout = setTimeout(() => {
document.body.removeChild(iframe);
resolve(false); // iframe也无法加载服务停止
}, 1000);
iframe.onload = () => {
clearTimeout(iframeTimeout);
document.body.removeChild(iframe);
resolve(true); // iframe加载成功服务运行中
};
iframe.onerror = () => {
clearTimeout(iframeTimeout);
document.body.removeChild(iframe);
resolve(false); // iframe加载失败服务停止
};
document.body.appendChild(iframe);
}).catch(() => {
// fetch失败服务未运行
resolve(false);
});
}
};
// 尝试加载favicon或vite.svg
img.src = `http://localhost:${port}/vite.svg?t=${Date.now()}`;
});
// 等待检测结果
isRunning = await checkPromise;
// 更新UI
if (isRunning) {
statusEl.className = 'status status-running';
statusEl.innerHTML = '<span class="status-dot"></span> 运行中';
} else {
statusEl.className = 'status status-stopped';
statusEl.innerHTML = '<span class="status-dot"></span> 已停止';
}
}
// 检查所有状态
function checkAllStatus() {
industries.forEach(industry => {
checkStatus(industry.port);
});
}
// 打开产业系统
function openIndustry(port) {
window.open(`http://localhost:${port}`, '_blank');
}
// 启动/停止产业(提示用户使用启动器)
function toggleIndustry(id) {
alert(`请使用启动器控制产业 ${id}:\n\nWindows用户:\n- 运行 start-industry.bat\n- 选择选项 ${id}\n\nMac/Linux用户:\n- 运行 ./start-industry.sh\n- 选择选项 ${id}`);
}
// 启动所有产业
function startAllIndustries() {
if (confirm('启动所有产业需要使用系统启动器。\n\nWindows: 运行 start-industry.bat 选择 [0]\nMac/Linux: 运行 ./start-industry.sh 选择 [0]\n\n是否打开启动器')) {
openLauncher();
}
}
// 停止所有产业
function stopAllIndustries() {
if (confirm('停止所有产业需要使用系统启动器。\n\nWindows: 运行 start-industry.bat 选择 [A]\nMac/Linux: 运行 ./start-industry.sh 选择 [A]\n\n是否打开启动器')) {
openLauncher();
}
}
// 打开启动器
function openLauncher() {
const platform = navigator.platform;
if (platform.indexOf('Win') !== -1) {
alert('Windows 启动方法:\n\n1. 打开文件资源管理器\n2. 找到 start-industry.bat 文件\n3. 双击运行\n\n或者:\n1. 按 Win+R 打开运行对话框\n2. 输入 cmd 并回车\n3. 导航到项目目录\n4. 输入 start-industry.bat 并回车');
} else {
alert('Mac/Linux 启动方法:\n\n1. 打开终端\n2. 导航到项目目录\n3. 运行: ./start-industry.sh\n\n如果没有执行权限:\nchmod +x start-industry.sh');
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', initPage);
// 自动刷新状态每10秒
setInterval(checkAllStatus, 10000);
</script>
</body>
</html>