Files
Web_BLS_ProjectConsole/src/frontend/App.vue
XuJiacheng a8faa7dcaa feat: 重构项目心跳数据结构并实现相关功能
- 重构Redis心跳数据结构,使用统一的项目列表键
- 新增数据迁移工具和API端点
- 更新前端以使用真实项目数据
- 添加系统部署配置和文档
- 修复代码格式和样式问题
2026-01-15 14:14:10 +08:00

309 lines
6.1 KiB
Vue
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.
<template>
<div class="app-container">
<header class="app-header">
<button v-if="isMobile" class="menu-toggle" @click="toggleSidebar">
<span class="menu-icon" />
</button>
<h1>BLS Project Console</h1>
<div class="service-status"
:class="{ 'status-ok': serviceStatus === 'ok', 'status-error': serviceStatus === 'error' }">
{{ serviceStatusText }}
</div>
</header>
<div class="app-content">
<!-- 左侧选择区域 -->
<aside class="sidebar" :class="{ 'sidebar-closed': !sidebarOpen && isMobile }">
<router-view name="sidebar" />
</aside>
<!-- 右侧调试区域 -->
<main class="main-content">
<div v-if="serviceStatus === 'error'" class="service-error-message">
<h3>服务不可用</h3>
<p>无法连接到后端服务请检查服务是否正常运行</p>
</div>
<router-view v-else name="main" />
</main>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const sidebarOpen = ref(true);
const isMobile = ref(false);
const serviceStatus = ref('checking');
const serviceStatusText = ref('检查服务状态...');
let checkInterval = null;
// 检测窗口大小变化
const checkMobile = () => {
isMobile.value = window.innerWidth < 768;
if (isMobile.value) {
sidebarOpen.value = false;
} else {
sidebarOpen.value = true;
}
};
// 切换侧边栏
const toggleSidebar = () => {
sidebarOpen.value = !sidebarOpen.value;
};
// 检查服务健康状态
const checkServiceHealth = async () => {
try {
console.log('=== 开始检查服务健康状态 ===');
const response = await fetch('http://localhost:3001/api/health', {
method: 'GET',
credentials: 'omit',
referrerPolicy: 'no-referrer',
});
console.log('响应状态码:', response.status);
console.log('响应头:', Object.fromEntries(response.headers));
const responseText = await response.text();
console.log('响应文本:', responseText);
if (response.ok) {
serviceStatus.value = 'ok';
serviceStatusText.value = '服务正常';
console.log('=== 服务健康状态检查通过 ===');
} else {
serviceStatus.value = 'error';
serviceStatusText.value = `服务异常 (${response.status})`;
console.log('=== 服务健康状态检查失败: 状态码异常 ===');
}
} catch (error) {
console.error('=== 服务健康状态检查异常 ===');
console.error('错误类型:', error.name);
console.error('错误消息:', error.message);
console.error('错误堆栈:', error.stack);
serviceStatus.value = 'error';
serviceStatusText.value = '服务不可用: ' + error.message;
}
};
// 监听窗口大小变化
onMounted(() => {
checkMobile();
window.addEventListener('resize', checkMobile);
// 初始检查服务状态
checkServiceHealth();
// 定时检查服务状态每5秒
checkInterval = setInterval(checkServiceHealth, 5000);
});
onUnmounted(() => {
window.removeEventListener('resize', checkMobile);
// 清除定时检查
if (checkInterval) {
clearInterval(checkInterval);
}
});
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #121212;
color: #e0e0e0;
overflow: hidden;
}
.app-container {
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: #1e1e1e;
}
.app-header {
background-color: #008C8C;
color: white;
padding: 0.6rem 1rem;
display: flex;
align-items: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.menu-toggle {
background: none;
border: none;
color: white;
font-size: 1.2rem;
cursor: pointer;
margin-right: 1rem;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 4px;
transition: background-color 0.2s;
}
.menu-toggle:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.menu-icon {
display: block;
width: 20px;
height: 2px;
background-color: white;
position: relative;
transition: background-color 0.2s;
}
.menu-icon::before,
.menu-icon::after {
content: '';
position: absolute;
width: 20px;
height: 2px;
background-color: white;
transition: transform 0.2s, top 0.2s;
}
.menu-icon::before {
top: -6px;
}
.menu-icon::after {
top: 6px;
}
.app-header h1 {
font-size: 1.5rem;
font-weight: 600;
margin: 0;
flex: 1;
}
/* 服务状态指示器 */
.service-status {
font-size: 0.9rem;
padding: 0.3rem 0.8rem;
border-radius: 12px;
font-weight: 500;
margin-right: 1rem;
display: inline-block;
}
.service-status.status-ok {
background-color: #e8f5e8;
color: #2e7d32;
}
.service-status.status-error {
background-color: #ffebee;
color: #c62828;
}
/* 服务错误消息 */
.service-error-message {
background-color: #ffebee;
border: 1px solid #ffcdd2;
border-radius: 8px;
padding: 2rem;
text-align: center;
margin: 2rem;
color: #c62828;
}
.service-error-message h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.service-error-message p {
font-size: 1rem;
opacity: 0.8;
}
.app-content {
flex: 1;
display: flex;
overflow: hidden;
}
/* 左侧选择区域 */
.sidebar {
width: 280px;
background-color: #252526;
border-right: 1px solid #3e3e42;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
overflow-y: auto;
z-index: 50;
color: #e0e0e0;
}
/* 移动端侧边栏关闭状态 */
.sidebar-closed {
width: 0;
border-right: none;
overflow: hidden;
}
/* 右侧调试区域 */
.main-content {
flex: 1;
padding: 0;
overflow: hidden;
background-color: #1e1e1e;
}
/* 响应式设计 */
@media (max-width: 768px) {
.menu-toggle {
display: flex;
}
.sidebar {
position: fixed;
left: 0;
top: 60px;
height: calc(100vh - 60px);
width: 280px;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
}
.sidebar-closed {
left: -280px;
width: 280px;
}
.main-content {
padding: 0.5rem;
overflow: auto;
}
}
@media (min-width: 769px) {
.menu-toggle {
display: none;
}
.sidebar {
width: 280px;
}
}
</style>