712 lines
33 KiB
HTML
712 lines
33 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>系统首页</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
|
||
<script>
|
||
tailwind.config = {
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
primary: '#E3F2FD',
|
||
secondary: '#BBDEFB',
|
||
accent: '#64B5F6',
|
||
dark: '#1976D2',
|
||
},
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
<style type="text/tailwindcss">
|
||
@layer utilities {
|
||
.content-auto {
|
||
content-visibility: auto;
|
||
}
|
||
|
||
.scrollbar-thin {
|
||
scrollbar-width: thin;
|
||
}
|
||
|
||
.scrollbar-thin::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||
background-color: rgba(100, 181, 246, 0.5);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.status-online {
|
||
@apply bg-green-500;
|
||
}
|
||
|
||
.status-offline {
|
||
@apply bg-red-500;
|
||
}
|
||
|
||
.status-warning {
|
||
@apply bg-yellow-500;
|
||
}
|
||
|
||
/* 自定义提示弹窗样式 */
|
||
.custom-toast {
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
padding: 12px 20px;
|
||
border-radius: 6px;
|
||
color: white;
|
||
font-size: 14px;
|
||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
||
z-index: 9999;
|
||
opacity: 0;
|
||
transform: translateY(-20px);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.toast-success {
|
||
background-color: #10B981; /* 成功绿色 */
|
||
}
|
||
|
||
.toast-error {
|
||
background-color: #EF4444; /* 错误红色 */
|
||
}
|
||
|
||
.toast-show {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="bg-primary h-screen overflow-hidden flex flex-col">
|
||
<!-- 顶部区域 -->
|
||
<header class="bg-primary border-b border-accent/30 py-3 px-6 flex items-center justify-between shadow-sm">
|
||
<div class="flex items-center space-x-3">
|
||
<div class="w-10 h-10 bg-dark rounded-lg flex items-center justify-center text-white">
|
||
<i class="fa fa-cogs text-xl"></i>
|
||
</div>
|
||
<h1 class="text-xl font-bold text-dark"><a th:href="@{/biz/index}">智慧门户</a></h1>
|
||
</div>
|
||
|
||
<!-- 中间模块导航 -->
|
||
<nav class="flex items-center space-x-8">
|
||
<a href="javascript:loadContent('dataMap')"
|
||
class="text-dark hover:text-accent transition-colors duration-200 flex items-center cursor-pointer">
|
||
<i class="fa fa-map-o mr-2"></i>
|
||
<span>数据地图</span>
|
||
</a>
|
||
<a href="javascript:loadContent('dataDoc')"
|
||
class="text-dark hover:text-accent transition-colors duration-200 flex items-center cursor-pointer">
|
||
<i class="fa fa-book mr-2"></i>
|
||
<span>文档中心</span>
|
||
</a>
|
||
</nav>
|
||
|
||
<!-- 右侧账号信息 -->
|
||
<div class="relative">
|
||
<button id="userMenuBtn" class="flex items-center space-x-2 focus:outline-none">
|
||
<img th:src="@{/images/user.jpg}" alt="用户头像"
|
||
class="w-8 h-8 rounded-full border-2 border-accent">
|
||
<span class="text-dark font-medium" th:text="${uname}"></span>
|
||
<i class="fa fa-chevron-down text-xs text-dark"></i>
|
||
</button>
|
||
|
||
<div id="userMenu" class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 hidden z-10">
|
||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary transition-colors duration-200">
|
||
<i class="fa fa-user mr-2"></i>个人信息
|
||
</a>
|
||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary transition-colors duration-200">
|
||
<i class="fa fa-cog mr-2"></i>系统设置
|
||
</a>
|
||
<div class="border-t border-gray-200 my-1"></div>
|
||
<a href="javascript:void(0);" id="logoutLink"
|
||
class="block px-4 py-2 text-sm text-red-600 hover:bg-red-50 transition-colors duration-200">
|
||
<i class="fa fa-sign-out mr-2"></i>退出登录
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 内容区域 -->
|
||
<main class="flex-1 bg-primary overflow-hidden flex flex-col">
|
||
<!-- 状态栏 -->
|
||
<div class="bg-secondary/50 px-6 py-2 flex justify-between items-center border-b border-accent/20">
|
||
<div class="flex space-x-6">
|
||
<div class="flex items-center text-dark">
|
||
<i class="fa fa-clock-o mr-2"></i>
|
||
<span id="systemUptime">系统运行时长: <span th:text="${times}"></span> </span>
|
||
</div>
|
||
<div class="flex items-center text-dark">
|
||
<i class="fa fa-users mr-2"></i>
|
||
<span>在线人数: <span id="onlineUsers">24</span></span>
|
||
</div>
|
||
</div>
|
||
<div class="text-dark font-medium" id="currentTime"></div>
|
||
</div>
|
||
|
||
<!-- 主题区域 -->
|
||
<div class="flex-1 px-6 py-4 overflow-hidden mb-15">
|
||
<!-- 内容切换核心容器 -->
|
||
<div id="contentContainer" class="h-full bg-white rounded-lg shadow-sm overflow-hidden">
|
||
<!-- 默认首页内容 -->
|
||
<div id="defaultContent" class="h-full p-4 overflow-hidden">
|
||
<div class="flex h-full space-x-4">
|
||
<!-- 第一部分 (30%) -->
|
||
<div class="w-[30%] bg-secondary/50 rounded-lg shadow-sm flex flex-col overflow-hidden">
|
||
<!-- 日历 (50%) -->
|
||
<div class="h-[50%] p-4 border-b border-accent/20 overflow-hidden">
|
||
<h2 class="text-dark font-semibold mb-3 flex items-center">
|
||
<i class="fa fa-calendar mr-2"></i>日历
|
||
</h2>
|
||
<div id="calendar" class="bg-white/70 rounded-lg p-3 h-[calc(100%-30px)] flex flex-col">
|
||
<div class="flex justify-between items-center mb-2">
|
||
<button class="text-dark hover:text-accent"><i class="fa fa-chevron-left"></i>
|
||
</button>
|
||
<h3 class="font-medium text-dark">2025年11月</h3>
|
||
<button class="text-dark hover:text-accent"><i class="fa fa-chevron-right"></i>
|
||
</button>
|
||
</div>
|
||
<div class="grid grid-cols-7 gap-1 text-center text-xs text-gray-600 mb-1">
|
||
<div>日</div>
|
||
<div>一</div>
|
||
<div>二</div>
|
||
<div>三</div>
|
||
<div>四</div>
|
||
<div>五</div>
|
||
<div>六</div>
|
||
</div>
|
||
<div class="grid grid-cols-7 gap-1 text-center flex-1"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主机列表 (50%) -->
|
||
<div class="h-[50%] p-4 overflow-hidden">
|
||
<h2 class="text-dark font-semibold mb-3 flex items-center">
|
||
<i class="fa fa-server mr-2"></i>主机列表
|
||
</h2>
|
||
<div class="bg-white/70 rounded-lg p-3 h-[calc(100%-30px)] overflow-y-auto scrollbar-thin">
|
||
<div class="space-y-3">
|
||
<div class="flex items-center justify-between p-2 border-b border-gray-100"
|
||
th:each="host : ${hosts}">
|
||
<div>
|
||
<div class="font-medium text-dark">
|
||
<a class="text-primary" style="cursor: pointer;"
|
||
data-bs-toggle="modal"
|
||
data-bs-target="#serverDetailModal"
|
||
th:data-hostid="${host.getHostId()}">
|
||
<span th:text="${host.getHostname()}">主机名</span>
|
||
</a>
|
||
</div>
|
||
<div class="text-xs text-gray-500"><p>主机地址:<span
|
||
th:text="${host.getIpAddress()}"></span></p>
|
||
</div>
|
||
<div class="text-xs text-gray-500"><p>在线时间:<span
|
||
th:text="${host.getLastOnlineTime()}"></span></p>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center">
|
||
<span class="w-2 h-2 rounded-full status-online mr-2"></span>
|
||
<span class="text-sm text-green-600"
|
||
th:text="${host.getUstatus() == '1' ? '运行中' : '已离线' }"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第二部分 (45%) -->
|
||
<div class="w-[45%] bg-secondary/50 rounded-lg shadow-sm flex flex-col overflow-hidden">
|
||
<div class="h-full p-4 overflow-hidden">
|
||
<div class="flex border-b border-accent/20 pb-2 mb-3">
|
||
<button id="todoBtn" class="px-4 py-1 bg-accent text-white rounded-md mr-2 font-medium">
|
||
<i class="fa fa-tasks mr-1"></i>待办信息
|
||
</button>
|
||
<button id="notificationBtn"
|
||
class="px-4 py-1 bg-white/50 text-dark hover:bg-accent/30 rounded-md font-medium transition-colors duration-200">
|
||
<i class="fa fa-bell mr-1"></i>通知信息
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 待办信息内容 -->
|
||
<div id="todoContent"
|
||
class="bg-white/70 rounded-lg p-3 h-[calc(100%-50px)] overflow-y-auto scrollbar-thin">
|
||
<div class="space-y-4">
|
||
<div class="p-3 border-l-4 border-accent bg-primary/50 rounded-r"
|
||
th:each="todo : ${todoViews}" th:data-task-id="${todo.getSubTaskId()}">
|
||
<div class="flex justify-between items-start">
|
||
<h3 class="font-medium text-dark" th:text="${todo.getTaskName()}"></h3>
|
||
<span class="text-xs bg-red-100 text-red-600 px-2 py-0.5 rounded"
|
||
th:text="${todo.getPriority()}"></span>
|
||
</div>
|
||
<p class="text-sm text-gray-600 mt-1" th:text="${todo.getTaskDesc()}"></p>
|
||
<div class="flex justify-between items-center mt-2">
|
||
<span class="text-xs text-gray-500">截止日期:<span
|
||
th:text="${todo.getDeadline()}"></span></span>
|
||
<button class="text-xs text-accent hover:underline">处理</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 通知信息内容 (默认隐藏) -->
|
||
<div id="notificationContent"
|
||
class="bg-white/70 rounded-lg p-3 h-[calc(100%-50px)] overflow-y-auto scrollbar-thin hidden">
|
||
<div class="space-y-4">
|
||
<div class="p-3 border-b border-gray-100">
|
||
<div class="flex justify-between items-start">
|
||
<h3 class="font-medium text-dark">数据库备份成功</h3>
|
||
<span class="text-xs text-gray-500">今天 08:30</span>
|
||
</div>
|
||
<p class="text-sm text-gray-600 mt-1">
|
||
主数据库自动备份已完成,备份文件大小:2.4GB,存储路径:/backup/db/20251115/</p>
|
||
</div>
|
||
|
||
<div class="p-3 border-b border-gray-100">
|
||
<div class="flex justify-between items-start">
|
||
<h3 class="font-medium text-dark">用户登录提醒</h3>
|
||
<span class="text-xs text-gray-500">昨天 16:45</span>
|
||
</div>
|
||
<p class="text-sm text-gray-600 mt-1">您的账号在新设备(Windows 10, Chrome
|
||
120)登录,IP地址:113.25.XX.XX,如非本人操作,请及时修改密码。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第三部分 (25%) -->
|
||
<div class="w-[25%] bg-secondary/50 rounded-lg shadow-sm flex flex-col overflow-hidden">
|
||
<!-- 快捷登录 -->
|
||
<div class="h-full p-4 overflow-hidden">
|
||
<h2 class="text-dark font-semibold mb-3 flex items-center">
|
||
<i class="fa fa-rocket mr-2"></i>快捷登录
|
||
</h2>
|
||
|
||
<!-- 搜索框 -->
|
||
<div class="relative mb-3">
|
||
<input type="text" id="quickSearch" placeholder="搜索快捷登录..."
|
||
class="w-full pl-9 pr-3 py-2 bg-white/70 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-accent">
|
||
<i class="fa fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
||
</div>
|
||
|
||
<!-- 快捷登录图标列表 -->
|
||
<div id="quickLinks"
|
||
class="bg-white/70 rounded-lg p-4 h-[calc(100%-80px)] overflow-y-auto scrollbar-thin">
|
||
<div class="grid grid-cols-4 gap-4">
|
||
<!-- 快捷登录项 -->
|
||
<a th:each="storage : ${storages}" th:href="${storage.getHomepageUrl()}"
|
||
target="_blank"
|
||
class="flex flex-col items-center p-3 hover:bg-primary rounded-lg transition-colors duration-200 group">
|
||
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center text-blue-600 group-hover:bg-blue-600 group-hover:text-white transition-colors duration-200">
|
||
<i th:class="${storage.getIconClass()}"></i>
|
||
</div>
|
||
<span class="text-xs mt-2 text-center text-dark"
|
||
th:text="${storage.getSystemName()}"></span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内容加载iframe(默认隐藏) -->
|
||
<iframe id="contentFrame" class="w-full h-full border-0 hidden" src=""></iframe>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- 服务器详情模态框 -->
|
||
<div class="modal fade" id="serverDetailModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered" style="width: 80%; max-width: 1400px;">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">服务器详情</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body p-0" style="max-height: 60vh; height: 60vh; overflow: hidden;">
|
||
<div style="height: 100%; overflow-y: auto; scrollbar-width: thin; position: relative;">
|
||
<!-- 加载中动画 -->
|
||
<div id="loadingMask"
|
||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.8); display: none; justify-content: center; align-items: center; z-index: 100;">
|
||
<div class="spinner-border text-primary" role="status">
|
||
<span class="visually-hidden">加载中...</span>
|
||
</div>
|
||
</div>
|
||
<iframe id="detailFrame" width="100%" height="100%" frameborder="0"
|
||
style="min-height: 600px;"></iframe>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 退出确认弹窗 -->
|
||
<div id="logoutModal"
|
||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 opacity-0 pointer-events-none transition-opacity duration-300">
|
||
<div class="bg-white rounded-lg w-full max-w-md p-6 transform scale-95 transition-transform duration-300">
|
||
<h3 class="text-lg font-medium text-gray-900 mb-4">温馨提示</h3>
|
||
<p class="text-gray-600 mb-6">您确定要退出当前系统吗?</p>
|
||
<div class="flex justify-end gap-3">
|
||
<button id="cancelLogout"
|
||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors">
|
||
取消
|
||
</button>
|
||
<button id="confirmLogout"
|
||
class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors">
|
||
确认
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 处理意见弹窗 -->
|
||
<div id="handleTaskModal"
|
||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 opacity-0 pointer-events-none transition-opacity duration-300">
|
||
<div class="bg-white rounded-lg w-full max-w-md p-6 transform scale-95 transition-transform duration-300">
|
||
<h3 class="text-lg font-medium text-gray-900 mb-4">待办信息处理意见:</h3>
|
||
<div class="mb-4">
|
||
<textarea id="taskOpinion" rows="3"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-accent"
|
||
placeholder="请输入处理意见..."></textarea>
|
||
</div>
|
||
<div class="flex justify-end gap-3">
|
||
<button id="cancelHandleTask"
|
||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors">
|
||
取消
|
||
</button>
|
||
<button id="confirmHandleTask"
|
||
class="px-4 py-2 bg-accent text-white rounded-md hover:bg-dark transition-colors">
|
||
确认
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 引入Bootstrap JS -->
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
// 提示弹窗工具函数 - 新增
|
||
function showToast(message, type = 'success') {
|
||
// 创建弹窗元素
|
||
const toast = document.createElement('div');
|
||
toast.className = `custom-toast toast-${type}`;
|
||
toast.textContent = message;
|
||
document.body.appendChild(toast);
|
||
|
||
// 显示弹窗
|
||
setTimeout(() => toast.classList.add('toast-show'), 10);
|
||
|
||
// 3秒后隐藏并移除
|
||
setTimeout(() => {
|
||
toast.classList.remove('toast-show');
|
||
setTimeout(() => document.body.removeChild(toast), 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// 动态日历渲染函数
|
||
function renderCalendar(year, month) {
|
||
const calendarContainer = document.querySelector('#calendar .grid:last-child');
|
||
const monthTitle = document.querySelector('#calendar h3');
|
||
|
||
calendarContainer.innerHTML = '';
|
||
monthTitle.textContent = `${year}年${month + 1}月`;
|
||
|
||
const firstDay = new Date(year, month, 1).getDay();
|
||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||
const lastDayOfPrevMonth = new Date(year, month, 0).getDate();
|
||
|
||
// 渲染上月剩余天数
|
||
for (let i = firstDay - 1; i >= 0; i--) {
|
||
const prevDay = document.createElement('div');
|
||
prevDay.className = 'py-2 text-gray-400 text-sm';
|
||
prevDay.textContent = lastDayOfPrevMonth - i;
|
||
calendarContainer.appendChild(prevDay);
|
||
}
|
||
|
||
// 渲染当月天数
|
||
const today = new Date();
|
||
const isCurrentMonth = today.getFullYear() === year && today.getMonth() === month;
|
||
const currentDate = today.getDate();
|
||
|
||
for (let i = 1; i <= daysInMonth; i++) {
|
||
const day = document.createElement('div');
|
||
day.className = 'py-2 text-sm';
|
||
if (isCurrentMonth && i === currentDate) {
|
||
day.classList.add('bg-accent', 'text-white', 'rounded-full', 'font-medium');
|
||
}
|
||
day.textContent = i;
|
||
calendarContainer.appendChild(day);
|
||
}
|
||
|
||
// 渲染下月起始天数
|
||
const totalDays = firstDay + daysInMonth;
|
||
const nextMonthDays = totalDays % 7 !== 0 ? 7 - (totalDays % 7) : 0;
|
||
for (let i = 1; i <= nextMonthDays; i++) {
|
||
const nextDay = document.createElement('div');
|
||
nextDay.className = 'py-2 text-gray-400 text-sm';
|
||
nextDay.textContent = i;
|
||
calendarContainer.appendChild(nextDay);
|
||
}
|
||
}
|
||
|
||
// 初始化日历
|
||
let currentDate = new Date();
|
||
let currentYear = currentDate.getFullYear();
|
||
let currentMonth = currentDate.getMonth();
|
||
renderCalendar(currentYear, currentMonth);
|
||
|
||
// 绑定月份切换事件
|
||
document.querySelectorAll('#calendar button')[0].addEventListener('click', () => {
|
||
currentMonth--;
|
||
if (currentMonth < 0) {
|
||
currentMonth = 11;
|
||
currentYear--;
|
||
}
|
||
renderCalendar(currentYear, currentMonth);
|
||
});
|
||
|
||
document.querySelectorAll('#calendar button')[1].addEventListener('click', () => {
|
||
currentMonth++;
|
||
if (currentMonth > 11) {
|
||
currentMonth = 0;
|
||
currentYear++;
|
||
}
|
||
renderCalendar(currentYear, currentMonth);
|
||
});
|
||
|
||
// 内容加载函数
|
||
function loadContent(pageUrl) {
|
||
const defaultContent = document.getElementById('defaultContent');
|
||
const contentFrame = document.getElementById('contentFrame');
|
||
defaultContent.classList.add('hidden');
|
||
contentFrame.classList.remove('hidden');
|
||
contentFrame.src = pageUrl;
|
||
}
|
||
|
||
// 返回首页函数
|
||
function backToHome() {
|
||
const defaultContent = document.getElementById('defaultContent');
|
||
const contentFrame = document.getElementById('contentFrame');
|
||
defaultContent.classList.remove('hidden');
|
||
contentFrame.classList.add('hidden');
|
||
contentFrame.src = '';
|
||
}
|
||
|
||
// 用户菜单切换
|
||
const userMenuBtn = document.getElementById('userMenuBtn');
|
||
const userMenu = document.getElementById('userMenu');
|
||
userMenuBtn.addEventListener('click', () => userMenu.classList.toggle('hidden'));
|
||
document.addEventListener('click', (e) => {
|
||
if (!userMenuBtn.contains(e.target) && !userMenu.contains(e.target)) {
|
||
userMenu.classList.add('hidden');
|
||
}
|
||
});
|
||
|
||
// 待办和通知切换
|
||
const todoBtn = document.getElementById('todoBtn');
|
||
const notificationBtn = document.getElementById('notificationBtn');
|
||
const todoContent = document.getElementById('todoContent');
|
||
const notificationContent = document.getElementById('notificationContent');
|
||
|
||
todoBtn.addEventListener('click', () => {
|
||
todoBtn.classList.remove('bg-white/50', 'text-dark');
|
||
todoBtn.classList.add('bg-accent', 'text-white');
|
||
notificationBtn.classList.remove('bg-accent', 'text-white');
|
||
notificationBtn.classList.add('bg-white/50', 'text-dark', 'hover:bg-accent/30');
|
||
todoContent.classList.remove('hidden');
|
||
notificationContent.classList.add('hidden');
|
||
});
|
||
|
||
notificationBtn.addEventListener('click', () => {
|
||
notificationBtn.classList.remove('bg-white/50', 'text-dark', 'hover:bg-accent/30');
|
||
notificationBtn.classList.add('bg-accent', 'text-white');
|
||
todoBtn.classList.remove('bg-accent', 'text-white');
|
||
todoBtn.classList.add('bg-white/50', 'text-dark');
|
||
notificationContent.classList.remove('hidden');
|
||
todoContent.classList.add('hidden');
|
||
});
|
||
|
||
// 快捷登录搜索功能
|
||
const quickSearch = document.getElementById('quickSearch');
|
||
const quickLinks = document.querySelectorAll('#quickLinks a');
|
||
quickSearch.addEventListener('input', (e) => {
|
||
const searchTerm = e.target.value.toLowerCase();
|
||
quickLinks.forEach(link => {
|
||
const text = link.querySelector('span').textContent.toLowerCase();
|
||
link.style.display = text.includes(searchTerm) ? 'flex' : 'none';
|
||
});
|
||
});
|
||
|
||
// 更新当前时间
|
||
function updateCurrentTime() {
|
||
const now = new Date();
|
||
const options = {
|
||
year: 'numeric',
|
||
month: 'long',
|
||
day: 'numeric',
|
||
weekday: 'long',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit'
|
||
};
|
||
document.getElementById('currentTime').textContent = now.toLocaleString('zh-CN', options);
|
||
}
|
||
|
||
updateCurrentTime();
|
||
setInterval(updateCurrentTime, 1000);
|
||
|
||
// 服务器详情模态框
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
const modal = new bootstrap.Modal(document.getElementById('serverDetailModal'));
|
||
const detailFrame = document.getElementById('detailFrame');
|
||
document.getElementById('serverDetailModal').addEventListener('show.bs.modal', function (e) {
|
||
const hostId = e.relatedTarget.getAttribute('data-hostid');
|
||
detailFrame.src = `getServerInfo?hostId=${hostId}`;
|
||
});
|
||
document.getElementById('serverDetailModal').addEventListener('hide.bs.modal', function () {
|
||
detailFrame.src = '';
|
||
});
|
||
});
|
||
|
||
// 服务器详情模态框加载动画
|
||
const detailModal = document.getElementById('serverDetailModal');
|
||
const loadingMask = document.getElementById('loadingMask');
|
||
const detailFrame = document.getElementById('detailFrame');
|
||
detailModal.addEventListener('show.bs.modal', function (e) {
|
||
loadingMask.style.display = 'flex';
|
||
const targetUrl = e.relatedTarget.dataset.url;
|
||
detailFrame.src = targetUrl;
|
||
});
|
||
detailFrame.addEventListener('load', function () {
|
||
loadingMask.style.display = 'none';
|
||
});
|
||
detailModal.addEventListener('hide.bs.modal', function () {
|
||
detailFrame.src = '';
|
||
loadingMask.style.display = 'none';
|
||
});
|
||
|
||
// 退出登录功能
|
||
const logoutLink = document.getElementById('logoutLink');
|
||
const logoutModal = document.getElementById('logoutModal');
|
||
const cancelLogout = document.getElementById('cancelLogout');
|
||
const confirmLogout = document.getElementById('confirmLogout');
|
||
|
||
function showModal() {
|
||
logoutModal.classList.remove('opacity-0', 'pointer-events-none');
|
||
logoutModal.querySelector('div').classList.remove('scale-95');
|
||
logoutModal.querySelector('div').classList.add('scale-100');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
function hideModal() {
|
||
logoutModal.classList.add('opacity-0', 'pointer-events-none');
|
||
logoutModal.querySelector('div').classList.remove('scale-100');
|
||
logoutModal.querySelector('div').classList.add('scale-95');
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
logoutLink.addEventListener('click', showModal);
|
||
cancelLogout.addEventListener('click', hideModal);
|
||
logoutModal.addEventListener('click', (e) => {
|
||
if (e.target === logoutModal) hideModal();
|
||
});
|
||
|
||
// 处理待办任务弹窗功能
|
||
const handleButtons = document.querySelectorAll('#todoContent button.text-accent');
|
||
const handleTaskModal = document.getElementById('handleTaskModal');
|
||
const cancelHandleTask = document.getElementById('cancelHandleTask');
|
||
const confirmHandleTask = document.getElementById('confirmHandleTask');
|
||
const taskOpinion = document.getElementById('taskOpinion');
|
||
let currentTaskId = null;
|
||
|
||
handleButtons.forEach(button => {
|
||
button.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
const taskItem = button.closest('.border-l-4');
|
||
currentTaskId = taskItem.dataset.taskId || 'default-task-id';
|
||
taskOpinion.value = '';
|
||
showHandleModal();
|
||
});
|
||
});
|
||
|
||
function showHandleModal() {
|
||
handleTaskModal.classList.remove('opacity-0', 'pointer-events-none');
|
||
handleTaskModal.querySelector('div').classList.remove('scale-95');
|
||
handleTaskModal.querySelector('div').classList.add('scale-100');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
function hideHandleModal() {
|
||
handleTaskModal.classList.add('opacity-0', 'pointer-events-none');
|
||
handleTaskModal.querySelector('div').classList.remove('scale-100');
|
||
handleTaskModal.querySelector('div').classList.add('scale-95');
|
||
document.body.style.overflow = '';
|
||
currentTaskId = null;
|
||
}
|
||
|
||
cancelHandleTask.addEventListener('click', hideHandleModal);
|
||
handleTaskModal.addEventListener('click', (e) => {
|
||
if (e.target === handleTaskModal) hideHandleModal();
|
||
});
|
||
|
||
// 确认处理(替换alert为弹窗)
|
||
confirmHandleTask.addEventListener('click', () => {
|
||
const opinion = taskOpinion.value.trim();
|
||
if (!opinion) {
|
||
showToast('请输入处理意见', 'error');
|
||
return;
|
||
}
|
||
|
||
fetch('finishTodoSub', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
taskId: currentTaskId,
|
||
opinion: opinion
|
||
})
|
||
})
|
||
.then(response => {
|
||
if (response.ok) {
|
||
showToast('处理成功');
|
||
hideHandleModal();
|
||
location.reload();
|
||
} else {
|
||
showToast('处理失败,请重试', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('处理请求失败:', error);
|
||
showToast('网络错误,处理失败', 'error');
|
||
});
|
||
});
|
||
|
||
// 确认退出(替换alert为弹窗)
|
||
confirmLogout.addEventListener('click', () => {
|
||
fetch('logout', {method: 'POST'})
|
||
.then(response => {
|
||
if (response.ok) {
|
||
sessionStorage.removeItem('token');
|
||
window.location.href = '/cApi/login';
|
||
} else {
|
||
showToast('退出失败,请重试', 'error'); // 替换alert
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('退出请求失败:', error);
|
||
showToast('网络错误,退出失败', 'error'); // 替换alert
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |