主要内容: - 包含12个产业的完整教务系统前端代码 - 智能启动脚本 (start-industry.sh) - 可视化产业导航页面 (index.html) - 项目文档 (README.md) 优化内容: - 删除所有node_modules和.yoyo文件夹,从7.5GB减少到2.7GB - 添加.gitignore文件避免上传不必要的文件 - 自动依赖管理和智能启动系统 产业列表: 1. 文旅产业 (5150) 2. 智能制造 (5151) 3. 智能开发 (5152) 4. 财经商贸 (5153) 5. 视觉设计 (5154) 6. 交通物流 (5155) 7. 大健康 (5156) 8. 土木水利 (5157) 9. 食品产业 (5158) 10. 化工产业 (5159) 11. 能源产业 (5160) 12. 环保产业 (5161) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
472 lines
15 KiB
HTML
472 lines
15 KiB
HTML
<!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: -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: 40px;
|
|
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;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1400px;
|
|
width: 100%;
|
|
}
|
|
|
|
.industry-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
gap: 25px;
|
|
animation: fadeInUp 0.8s ease;
|
|
}
|
|
|
|
.industry-card {
|
|
background: white;
|
|
border-radius: 12px;
|
|
padding: 25px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.industry-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 4px;
|
|
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
transform: scaleX(0);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.industry-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 15px 40px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.industry-card:hover::before {
|
|
transform: scaleX(1);
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.industry-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.5rem;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
}
|
|
|
|
.industry-name {
|
|
font-size: 1.3rem;
|
|
font-weight: 600;
|
|
color: #333;
|
|
flex: 1;
|
|
margin-left: 15px;
|
|
}
|
|
|
|
.status-indicator {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
.status-dot.online {
|
|
background: #10b981;
|
|
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
|
|
}
|
|
|
|
.status-dot.offline {
|
|
background: #ef4444;
|
|
animation: none;
|
|
}
|
|
|
|
.status-dot.checking {
|
|
background: #f59e0b;
|
|
animation: pulse 1s infinite;
|
|
}
|
|
|
|
.status-text {
|
|
font-size: 0.9rem;
|
|
color: #666;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
.card-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
color: #666;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.info-label {
|
|
font-weight: 500;
|
|
min-width: 60px;
|
|
}
|
|
|
|
.info-value {
|
|
color: #333;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
|
|
.card-footer {
|
|
margin-top: 20px;
|
|
padding-top: 15px;
|
|
border-top: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.launch-btn {
|
|
width: 100%;
|
|
padding: 10px;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.launch-btn:hover {
|
|
transform: scale(1.02);
|
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
.launch-btn:disabled {
|
|
background: #e5e7eb;
|
|
color: #9ca3af;
|
|
cursor: not-allowed;
|
|
transform: scale(1);
|
|
}
|
|
|
|
.controls {
|
|
display: flex;
|
|
gap: 15px;
|
|
margin-bottom: 30px;
|
|
animation: fadeInUp 0.8s ease;
|
|
}
|
|
|
|
.control-btn {
|
|
padding: 12px 24px;
|
|
background: white;
|
|
color: #667eea;
|
|
border: 2px solid white;
|
|
border-radius: 8px;
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.control-btn:hover {
|
|
background: transparent;
|
|
color: white;
|
|
}
|
|
|
|
@keyframes fadeInDown {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.toast {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
padding: 15px 20px;
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
|
display: none;
|
|
animation: slideIn 0.3s ease;
|
|
}
|
|
|
|
.toast.show {
|
|
display: block;
|
|
}
|
|
|
|
@keyframes slideIn {
|
|
from {
|
|
transform: translateX(100%);
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>🎓 教务系统产业导航</h1>
|
|
<p>选择您要访问的产业系统</p>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<div class="controls">
|
|
<button class="control-btn" onclick="checkAllStatus()">🔄 刷新状态</button>
|
|
<button class="control-btn" onclick="launchAll()">🚀 启动全部</button>
|
|
<button class="control-btn" onclick="openTerminal()">💻 打开终端</button>
|
|
</div>
|
|
|
|
<div class="industry-grid" id="industryGrid">
|
|
<!-- 产业卡片将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toast" id="toast"></div>
|
|
|
|
<script>
|
|
// 产业配置
|
|
const industries = [
|
|
{ name: '文旅产业', dir: 'frontend', port: 5150, icon: '🏛️' },
|
|
{ name: '智能制造', dir: 'frontend_智能制造', port: 5151, icon: '🏭' },
|
|
{ name: '智能开发', dir: 'frontend_智能开发', port: 5152, icon: '💻' },
|
|
{ name: '财经商贸', dir: 'frontend_财经商贸', port: 5153, icon: '💰' },
|
|
{ name: '视觉设计', dir: 'frontend_视觉设计', port: 5154, icon: '🎨' },
|
|
{ name: '交通物流', dir: 'frontend_交通物流', port: 5155, icon: '🚚' },
|
|
{ name: '大健康', dir: 'frontend_大健康', port: 5156, icon: '🏥' },
|
|
{ name: '土木水利', dir: 'frontend_土木水利', port: 5157, icon: '🏗️' },
|
|
{ name: '食品产业', dir: 'frontend_食品', port: 5158, icon: '🍽️' },
|
|
{ name: '化工产业', dir: 'frontend_化工', port: 5159, icon: '⚗️' },
|
|
{ name: '能源产业', dir: 'frontend_能源', port: 5160, icon: '⚡' },
|
|
{ name: '环保产业', dir: 'frontend_环保', port: 5161, icon: '🌱' }
|
|
];
|
|
|
|
// 初始化页面
|
|
function initPage() {
|
|
renderIndustryCards();
|
|
checkAllStatus();
|
|
// 每10秒自动检查状态
|
|
setInterval(checkAllStatus, 10000);
|
|
}
|
|
|
|
// 渲染产业卡片
|
|
function renderIndustryCards() {
|
|
const grid = document.getElementById('industryGrid');
|
|
grid.innerHTML = '';
|
|
|
|
industries.forEach((industry, index) => {
|
|
const card = document.createElement('div');
|
|
card.className = 'industry-card';
|
|
card.innerHTML = `
|
|
<div class="card-header">
|
|
<div class="industry-icon">${industry.icon}</div>
|
|
<div class="industry-name">${industry.name}</div>
|
|
<div class="status-indicator">
|
|
<span class="status-dot checking" id="status-${industry.port}"></span>
|
|
<span class="status-text" id="text-${industry.port}">检测中</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-info">
|
|
<div class="info-item">
|
|
<span class="info-label">端口:</span>
|
|
<span class="info-value">${industry.port}</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">目录:</span>
|
|
<span class="info-value">${industry.dir}</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">地址:</span>
|
|
<span class="info-value">
|
|
<a href="http://localhost:${industry.port}"
|
|
target="_blank"
|
|
id="link-${industry.port}"
|
|
style="color: #667eea; text-decoration: none;">
|
|
http://localhost:${industry.port}
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<button class="launch-btn"
|
|
id="btn-${industry.port}"
|
|
onclick="launchIndustry(${index})">
|
|
访问系统
|
|
</button>
|
|
</div>
|
|
`;
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
// 检查所有产业状态
|
|
async function checkAllStatus() {
|
|
industries.forEach(industry => {
|
|
checkPortStatus(industry.port);
|
|
});
|
|
}
|
|
|
|
// 检查单个端口状态
|
|
async function checkPortStatus(port) {
|
|
const statusDot = document.getElementById(`status-${port}`);
|
|
const statusText = document.getElementById(`text-${port}`);
|
|
const btn = document.getElementById(`btn-${port}`);
|
|
const link = document.getElementById(`link-${port}`);
|
|
|
|
statusDot.className = 'status-dot checking';
|
|
statusText.textContent = '检测中';
|
|
|
|
try {
|
|
// 尝试访问端口
|
|
const response = await fetch(`http://localhost:${port}`, {
|
|
method: 'HEAD',
|
|
mode: 'no-cors',
|
|
cache: 'no-cache'
|
|
});
|
|
|
|
// 如果能访问,认为是运行中
|
|
statusDot.className = 'status-dot online';
|
|
statusText.textContent = '运行中';
|
|
statusText.style.color = '#10b981';
|
|
btn.textContent = '访问系统';
|
|
btn.disabled = false;
|
|
btn.onclick = () => window.open(`http://localhost:${port}`, '_blank');
|
|
link.style.pointerEvents = 'auto';
|
|
link.style.color = '#667eea';
|
|
} catch (error) {
|
|
// 无法访问,认为是未启动
|
|
statusDot.className = 'status-dot offline';
|
|
statusText.textContent = '未启动';
|
|
statusText.style.color = '#ef4444';
|
|
btn.textContent = '启动服务';
|
|
btn.disabled = false;
|
|
btn.onclick = () => startIndustry(port);
|
|
link.style.pointerEvents = 'none';
|
|
link.style.color = '#9ca3af';
|
|
}
|
|
}
|
|
|
|
// 启动产业服务
|
|
function startIndustry(port) {
|
|
const industry = industries.find(i => i.port === port);
|
|
if (industry) {
|
|
showToast(`⚠️ 请在终端运行: ./start-industry.sh ${industries.indexOf(industry) + 1}`, 'warning');
|
|
openTerminal();
|
|
}
|
|
}
|
|
|
|
// 启动单个产业
|
|
function launchIndustry(index) {
|
|
const industry = industries[index];
|
|
const statusDot = document.getElementById(`status-${industry.port}`);
|
|
|
|
if (statusDot.className.includes('online')) {
|
|
window.open(`http://localhost:${industry.port}`, '_blank');
|
|
} else {
|
|
showToast(`⚠️ ${industry.name}未启动,请先在终端启动服务`, 'warning');
|
|
}
|
|
}
|
|
|
|
// 启动所有产业
|
|
function launchAll() {
|
|
showToast('📋 请在终端运行: ./start-industry.sh all', 'info');
|
|
openTerminal();
|
|
}
|
|
|
|
// 打开终端(提示)
|
|
function openTerminal() {
|
|
showToast('💡 提示:请手动打开终端并运行启动脚本', 'info');
|
|
// 复制命令到剪贴板
|
|
navigator.clipboard.writeText('./start-industry.sh');
|
|
setTimeout(() => {
|
|
showToast('✅ 启动命令已复制到剪贴板', 'success');
|
|
}, 500);
|
|
}
|
|
|
|
// 显示提示消息
|
|
function showToast(message, type = 'info') {
|
|
const toast = document.getElementById('toast');
|
|
toast.textContent = message;
|
|
toast.className = `toast show ${type}`;
|
|
|
|
setTimeout(() => {
|
|
toast.className = 'toast';
|
|
}, 3000);
|
|
}
|
|
|
|
// 页面加载完成后初始化
|
|
document.addEventListener('DOMContentLoaded', initPage);
|
|
</script>
|
|
</body>
|
|
</html> |