Files
Docker-Proxy/hubcmdui/web/admin.html

3082 lines
100 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>Docker 镜像代理加速 - 管理面板</title>
<link rel="icon" href="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/docker-proxy.png" type="image/png">
<!-- 引入前端样式表 -->
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- 引入Bootstrap CSS和JS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- 引入 jQuery (Editor.md 需要) -->
<script src="https://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<!-- 引入 Markdown 编辑器 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- 引入 Editor.md -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/editor.md@1.5.0/css/editormd.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/addon/mode/overlay.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/markdown/markdown.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/gfm/gfm.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/editor.md@1.5.0/editormd.min.js"></script>
<!-- 自定义样式 -->
<link rel="stylesheet" href="css/admin.css">
<style>
/* 管理面板特定样式 */
.admin-container {
display: flex;
min-height: 100vh;
background-color: var(--background-color);
}
.sidebar {
width: 280px;
background-color: var(--container-bg);
box-shadow: var(--shadow-md);
padding: 1.5rem 0;
z-index: 10;
transition: all 0.3s ease;
}
.sidebar-header {
text-align: center;
margin-bottom: 1rem;
}
/* 文档管理新建文档徽章 */
.new-badge {
display: inline-block;
padding: 2px 8px;
background-color: #28a745;
color: white;
border-radius: 12px;
font-size: 0.75rem;
font-weight: bold;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.7; }
100% { opacity: 1; }
}
/* 用户信息部分样式 */
.user-profile {
padding: 1rem 1.5rem;
margin-bottom: 1.5rem;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.user-avatar {
width: 70px;
height: 70px;
border-radius: 50%;
background-color: var(--primary-light);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
color: var(--primary-color);
margin-bottom: 0.8rem;
box-shadow: var(--shadow-sm);
}
.user-info {
text-align: center;
margin-bottom: 0.5rem;
}
.user-name {
font-weight: 600;
font-size: 1.1rem;
color: var(--text-primary);
margin-bottom: 0.25rem;
}
.user-role {
color: var(--text-secondary);
font-size: 0.85rem;
margin-bottom: 0.5rem;
}
.user-actions {
display: flex;
gap: 0.5rem;
margin-top: 0.5rem;
}
.user-action-btn {
padding: 0.4rem 0.8rem;
font-size: 0.85rem;
border-radius: var(--radius-sm);
background-color: rgba(61, 124, 244, 0.1);
color: var(--primary-color);
cursor: pointer;
transition: all var(--transition-fast);
}
.user-action-btn:hover {
background-color: var(--primary-color);
color: white;
}
.user-action-btn.logout {
background-color: rgba(255, 82, 82, 0.1);
color: var(--danger-color);
}
.user-action-btn.logout:hover {
background-color: var(--danger-color);
color: white;
}
/* 仪表盘卡片样式 */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.dashboard-card {
background-color: var(--container-bg);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
padding: 1.5rem;
transition: all var(--transition-fast);
border: 1px solid var(--border-light);
position: relative;
overflow: hidden;
}
.dashboard-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-md);
border-color: var(--primary-light);
}
.dashboard-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background-color: var(--primary-color);
}
.card-icon {
width: 50px;
height: 50px;
border-radius: 12px;
background-color: rgba(61, 124, 244, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
color: var(--primary-color);
font-size: 1.4rem;
}
.card-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-primary);
}
.card-value {
font-size: 1.8rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.card-description {
color: var(--text-secondary);
font-size: 0.9rem;
margin-bottom: 1rem;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid var(--border-light);
padding-top: 1rem;
margin-top: 0.5rem;
}
.trend {
display: flex;
align-items: center;
gap: 0.3rem;
font-size: 0.9rem;
}
.trend.up {
color: var(--success-color);
}
.trend.down {
color: var(--danger-color);
}
.card-action {
color: var(--primary-color);
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
}
.welcome-banner {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
border-radius: var(--radius-lg);
padding: 2rem;
margin-bottom: 2rem;
color: white;
position: relative;
overflow: hidden;
box-shadow: var(--shadow-md);
}
.welcome-content {
position: relative;
z-index: 1;
}
.welcome-title {
font-size: 1.8rem;
margin-bottom: 0.5rem;
font-weight: 600;
}
.welcome-subtitle {
font-size: 1rem;
margin-bottom: 1.5rem;
opacity: 0.9;
}
.welcome-action {
background-color: rgba(255, 255, 255, 0.2);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: var(--radius-md);
font-weight: 500;
cursor: pointer;
transition: all var(--transition-fast);
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.welcome-action:hover {
background-color: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.welcome-banner::after {
content: '';
position: absolute;
top: -50%;
right: -10%;
width: 300px;
height: 300px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
.welcome-banner::before {
content: '';
position: absolute;
bottom: -30%;
left: -5%;
width: 200px;
height: 200px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
/* 用户中心样式 */
.user-center-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.user-center-title {
font-size: 1.8rem;
font-weight: 600;
color: var(--text-primary);
}
.user-center-subtitle {
color: var(--text-secondary);
margin-top: 0.5rem;
}
.user-center-card {
background-color: var(--container-bg);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
padding: 2rem;
margin-bottom: 2rem;
transition: all 0.3s ease;
}
.user-center-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.user-center-section {
margin-bottom: 1rem;
}
.user-center-section-title {
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 1.5rem;
color: var(--text-primary);
position: relative;
padding-bottom: 0.75rem;
}
.user-center-section-title::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 60px;
height: 2px;
background: var(--primary-color);
border-radius: 2px;
}
.user-stats {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 1rem;
}
.stat-card {
background-color: var(--container-bg);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
padding: 1.5rem;
border: 1px solid var(--border-light);
text-align: center;
transition: all 0.3s ease;
}
.stat-card:hover {
border-color: var(--primary-color);
transform: translateY(-3px);
box-shadow: var(--shadow-md);
}
.stat-icon {
font-size: 1.8rem;
color: var(--primary-color);
margin-bottom: 0.75rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 0.5rem;
}
.stat-label {
color: var(--text-secondary);
font-size: 0.9rem;
}
/* 新增用户个人资料卡片样式 */
.user-profile-card {
display: flex;
align-items: center;
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
color: white;
border-radius: var(--radius-lg);
padding: 2rem;
margin-bottom: 2rem;
box-shadow: var(--shadow-md);
position: relative;
overflow: hidden;
}
.user-profile-card::before {
content: '';
position: absolute;
top: -50px;
right: -50px;
width: 200px;
height: 200px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
z-index: 0;
}
.user-profile-card::after {
content: '';
position: absolute;
bottom: -60px;
left: 30%;
width: 150px;
height: 150px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
z-index: 0;
}
.user-profile-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
font-size: 2.5rem;
margin-right: 1.5rem;
z-index: 1;
}
.user-profile-info {
flex: 1;
z-index: 1;
}
.user-profile-name {
font-size: 1.8rem;
font-weight: 600;
margin: 0 0 0.3rem 0;
}
.user-profile-role {
font-size: 1rem;
opacity: 0.8;
margin: 0 0 1rem 0;
}
.user-profile-badges {
display: flex;
gap: 0.75rem;
}
.user-badge {
padding: 0.3rem 0.75rem;
border-radius: 20px;
font-size: 0.85rem;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.user-badge.admin {
background-color: rgba(255, 193, 7, 0.3);
}
.user-badge.active {
background-color: rgba(40, 167, 69, 0.3);
}
.user-profile-actions {
z-index: 1;
}
.btn-outline {
background-color: transparent;
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 0.6rem 1rem;
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.btn-outline:hover {
background-color: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.5);
}
/* 用户信息网格布局 */
.user-dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 450px), 1fr));
gap: 2rem;
}
/* 系统使用情况样式 */
.system-usage-stats {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.usage-stat {
width: 100%;
}
.usage-label {
display: flex;
justify-content: space-between;
margin-bottom: 0.6rem;
color: var(--text-secondary);
font-size: 0.9rem;
}
.usage-label i {
color: var(--primary-color);
margin-right: 0.5rem;
}
.usage-value {
font-weight: 600;
color: var(--text-primary);
}
.progress-bar-container {
height: 8px;
background-color: rgba(0, 0, 0, 0.05);
border-radius: 4px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, var(--primary-color) 0%, var(--primary-light) 100%);
border-radius: 4px;
}
/* 密码表单样式 */
.password-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.password-input-group {
display: flex;
position: relative;
}
.password-input {
flex: 1;
padding: 0.9rem 1rem;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
color: var(--text-primary);
background-color: var(--container-bg);
padding-right: 40px;
}
.password-toggle-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
transition: color 0.2s;
}
.password-toggle-btn:hover {
color: var(--primary-color);
}
.password-hint {
font-size: 0.8rem;
color: var(--text-secondary);
line-height: 1.3;
}
.password-strength-meter {
margin-top: 0.75rem;
}
.strength-bar {
height: 6px;
background-color: #f0f0f0;
border-radius: 3px;
margin-bottom: 0.4rem;
position: relative;
}
.strength-bar::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
border-radius: 3px;
width: 0;
transition: width 0.3s, background-color 0.3s;
}
.strength-text {
font-size: 0.8rem;
}
.password-submit-btn {
align-self: flex-start;
margin-top: 0.5rem;
}
/* 活动列表样式 */
.activity-list {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.activity-item {
display: flex;
align-items: center;
gap: 1rem;
padding-bottom: 1.25rem;
border-bottom: 1px solid var(--border-light);
}
.activity-item:last-child {
border-bottom: none;
padding-bottom: 0;
}
.activity-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1rem;
}
.activity-icon.login {
background-color: #4361ee;
}
.activity-icon.container {
background-color: #3a86ff;
}
.activity-icon.password {
background-color: #f72585;
}
.activity-content {
flex: 1;
}
.activity-title {
font-weight: 500;
color: var(--text-primary);
margin-bottom: 0.25rem;
}
.activity-time {
font-size: 0.8rem;
color: var(--text-secondary);
}
.sidebar h2 {
color: var(--text-primary);
padding: 0 1.5rem;
margin-bottom: 2rem;
font-size: 1.3rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.75rem;
position: relative;
}
.sidebar h2::after {
content: '';
position: absolute;
bottom: -10px;
left: 1.5rem;
right: 1.5rem;
height: 1px;
background: linear-gradient(to right, var(--primary-light), transparent);
}
.sidebar h2 i {
color: var(--primary-color);
font-size: 1.5rem;
}
.sidebar ul {
list-style: none;
padding: 0;
margin: 0;
}
.sidebar li {
padding: 1rem 1.5rem;
cursor: pointer;
transition: all var(--transition-fast);
color: var(--text-secondary);
display: flex;
align-items: center;
gap: 1rem;
font-weight: 500;
margin: 0.25rem 0;
border-left: 3px solid transparent;
}
.sidebar li:hover {
background-color: rgba(61, 124, 244, 0.08);
color: var(--primary-color);
}
.sidebar li.active {
background-color: rgba(61, 124, 244, 0.1);
color: var(--primary-color);
border-left: 3px solid var(--primary-color);
font-weight: 600;
}
.sidebar li i {
font-size: 1.2rem;
width: 1.5rem;
text-align: center;
}
.content-area {
flex: 1;
padding: 2rem;
overflow-y: auto;
background: linear-gradient(135deg, var(--background-color) 0%, rgba(247, 250, 255, 0.8) 100%);
}
.content-section {
background-color: var(--container-bg);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
padding: 2rem;
margin-bottom: 1.5rem;
display: none;
transform: translateY(10px);
opacity: 0;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.content-section.active {
display: block;
transform: translateY(0);
opacity: 1;
}
.admin-title {
color: var(--text-primary);
font-size: 1.6rem;
font-weight: 600;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-light);
position: relative;
}
.admin-title::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 80px;
height: 3px;
background: var(--primary-color);
border-radius: 2px;
}
.menu-label {
color: var(--text-primary);
font-size: 1.4rem;
font-weight: 600;
margin-bottom: 1.5rem;
position: relative;
padding-bottom: 0.5rem;
}
.menu-label::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 60px;
height: 2px;
background: var(--primary-color);
border-radius: 2px;
}
/* 表单元素样式 */
.content-section input[type="text"],
.content-section input[type="url"],
.content-section input[type="password"],
.content-section input[type="number"],
.content-section select {
width: 100%;
max-width: 500px;
padding: 0.9rem 1rem;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background-color: var(--container-bg);
color: var(--text-primary);
font-size: 0.95rem;
margin-bottom: 1.2rem;
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.content-section input:focus,
.content-section select:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(61, 124, 244, 0.2);
outline: none;
}
.content-section button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0.9rem 1.4rem;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 0.95rem;
font-weight: 500;
transition: all var(--transition-fast);
margin-top: 0.5rem;
margin-right: 0.5rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.content-section button:hover {
background-color: var(--primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.content-section button:active {
transform: translateY(0);
}
.content-section label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-primary);
font-weight: 500;
font-size: 1rem;
}
/* 表格样式 - 增强 Excel 效果 */
.content-section table {
width: 100%;
border-collapse: collapse; /* 合并边框 */
border-spacing: 0;
margin-top: 1.5rem;
margin-bottom: 2rem;
border: 1px solid #ccc; /* 表格外边框 */
font-size: 0.9rem; /* 稍微减小字体 */
box-shadow: 0 2px 5px rgba(0,0,0,0.1); /* 轻微阴影 */
}
.content-section th {
background-color: #f2f2f2; /* Excel 灰色表头背景 */
color: #333;
font-weight: bold; /* 加粗 */
text-align: left;
padding: 0.6rem 0.8rem; /* 调整内边距 */
border: 1px solid #ccc; /* 单元格边框 */
}
.content-section td {
padding: 0.6rem 0.8rem; /* 调整内边距 */
border: 1px solid #ddd; /* 单元格边框,比表头稍浅 */
vertical-align: middle;
background-color: #ffffff;
color: #444; /* 单元格文字颜色 */
word-break: break-word; /* 防止长 ID 撑开单元格 */
}
/* 可选:添加斑马纹效果 */
/*
.content-section tr:nth-child(even) td {
background-color: #f9f9f9;
}
*/
.content-section tr:hover td {
background-color: #e9f5ff; /* 鼠标悬停高亮 */
}
/* 表格头部悬停效果 */
.content-section th:hover {
background-color: #e8e8e8;
}
/* Excel风格的表格滚动条 */
.table-container {
overflow-x: auto;
max-width: 100%;
margin-bottom: 1.5rem;
border: 1px solid #e0e0e0;
border-radius: 4px;
}
/* 操作列样式 */
.action-cell {
text-align: center;
min-width: 120px;
}
/* 状态列样式 */
.status-running {
background-color: #28a745;
color: white;
padding: 0.3rem 0.6rem;
border-radius: 4px;
font-size: 0.8rem;
display: inline-block;
text-align: center;
}
.status-stopped, .status-exited {
background-color: #dc3545;
color: white;
padding: 0.3rem 0.6rem;
border-radius: 4px;
font-size: 0.8rem;
display: inline-block;
text-align: center;
}
.status-created {
background-color: #17a2b8;
color: white;
padding: 0.3rem 0.6rem;
border-radius: 4px;
font-size: 0.8rem;
display: inline-block;
text-align: center;
}
.status-paused {
background-color: #ffc107;
color: #212529;
padding: 0.3rem 0.6rem;
border-radius: 4px;
font-size: 0.8rem;
display: inline-block;
text-align: center;
}
.status-unknown {
background-color: #6c757d;
color: white;
padding: 0.3rem 0.6rem;
border-radius: 4px;
font-size: 0.8rem;
display: inline-block;
text-align: center;
}
/* 下拉菜单样式 */
.action-dropdown .dropdown-toggle {
border-color: #d0d0d0;
background-color: #fff;
color: #333;
font-size: 0.9rem;
padding: 0.4rem 0.8rem;
}
.action-dropdown .dropdown-toggle:hover,
.action-dropdown .dropdown-toggle:focus {
background-color: #f0f0f0;
box-shadow: none;
}
.action-dropdown .dropdown-menu {
min-width: 150px;
padding: 0.3rem 0;
border: 1px solid #d0d0d0;
border-radius: 4px;
box-shadow: 0 3px 6px rgba(0,0,0,0.1);
font-size: 0.9rem;
}
.action-dropdown .dropdown-item {
padding: 0.5rem 1rem;
color: #333;
}
.action-dropdown .dropdown-item:hover {
background-color: #f0f7ff;
}
.action-dropdown .dropdown-item i {
width: 1rem;
text-align: center;
margin-right: 0.5rem;
}
.action-dropdown .text-danger {
color: #dc3545 !important;
}
.action-dropdown .dropdown-divider {
margin: 0.3rem 0;
}
/* 登录模态框 */
.login-modal {
display: flex;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background: url('/images/login-bg.jpg') center/cover no-repeat;
justify-content: center;
align-items: center;
animation: fadeIn 0.5s ease-in-out;
}
.login-modal::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(26, 35, 126, 0.85) 0%, rgba(61, 124, 244, 0.85) 100%);
backdrop-filter: blur(5px);
}
.login-content {
background-color: var(--container-bg);
padding: 2.5rem;
border-radius: var(--radius-lg);
width: 380px;
box-shadow: var(--shadow-lg);
position: relative;
animation: slideUp 0.5s ease-out;
border: 1px solid rgba(255, 255, 255, 0.2);
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.login-header h2 {
color: var(--text-primary);
font-size: 1.8rem;
margin-bottom: 2rem;
text-align: center;
font-weight: 600;
position: relative;
padding-bottom: 1rem;
}
.login-header h2::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 2px;
background: var(--primary-color);
}
.login-form input[type="text"],
.login-form input[type="password"] {
width: 100%;
padding: 1rem 1.2rem;
margin: 0.75rem 0;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
box-sizing: border-box;
font-size: 1rem;
background-color: var (--container-bg);
color: var(--text-primary);
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.login-form input:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(61, 124, 244, 0.2);
outline: none;
}
.login-form button {
width: 100%;
padding: 1rem;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: var (--radius-md);
cursor: pointer;
font-size: 1.1rem;
font-weight: 500;
margin-top: 1.8rem;
transition: all var(--transition-fast);
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.login-form button:hover {
background-color: var(--primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.login-form button:active {
transform: translateY(0);
}
.login-form button:before {
content: "\f2f6";
font-family: "Font Awesome 6 Free";
font-weight: 900;
}
.captcha-container {
display: flex;
align-items: center;
margin: 0.75rem 0;
gap: 0.75rem;
}
.captcha-container input {
flex: 1;
}
.captcha-container span {
padding: 0.85rem;
background-color: var(--background-color);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
cursor: pointer;
font-size: 0.95rem;
color: var(--text-secondary);
transition: all var(--transition-fast);
display: flex;
align-items: center;
justify-content: center;
}
.captcha-container span:hover {
background-color: var(--border-light);
color: var(--primary-color);
}
/* 加载指示器 */
.loading-spinner {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50px;
height: 50px;
border: 5px solid rgba(61, 124, 244, 0.1);
border-top: 5px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 9999;
}
@keyframes spin {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
#loadingIndicator {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: var(--background-color);
color: var(--text-secondary);
font-size: 1.1rem;
}
/* 状态指示样式 */
.disabled {
opacity: 0.5;
pointer-events: none;
}
.status-cell {
position: relative;
min-height: 24px;
}
.status-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
text-align: center;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(61, 124, 244, 0.1);
border-radius: 50%;
border-top: 3px solid var(--primary-color);
animation: spin 1s linear infinite;
}
/* 监控配置部分 */
.monitoring-status {
margin-bottom: 1.5rem;
font-size: 1rem;
color: var(--text-secondary);
background-color: rgba(61, 124, 244, 0.05);
padding: 1rem 1.5rem;
border-radius: var(--radius-md);
display: flex;
align-items: center;
gap: 0.75rem;
}
.monitoring-status:before {
content: "\f021";
font-family: "Font Awesome 6 Free";
font-weight: 900;
color: var(--primary-color);
}
.status-indicator {
font-weight: 600;
}
.config-form {
background-color: var (--container-bg);
padding: 1.8rem;
border-radius: var(--radius-md);
margin-bottom: 1.5rem;
border: 1px solid var(--border-light);
box-shadow: var(--shadow-sm);
}
.form-group {
margin-bottom: 1.2rem;
}
.form-control {
width: 100%;
padding: 0.9rem 1rem;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
color: var(--text-primary);
background-color: var(--container-bg);
}
.button-group {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
margin-top: 1.5rem;
}
.btn {
padding: 0.8rem 1.2rem;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
font-size: 0.95rem;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
/* Fix 4: Remove icon from user center buttons */
.user-center-header .btn-primary,
.user-center-section .btn-primary {
background-color: var(--primary-color);
color: white;
padding: 0.8rem 1.2rem;
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
font-size: 0.95rem;
font-weight: 500;
display: inline-block; /* Change to inline-block */
}
/* Remove the :before pseudo-element for these specific buttons */
.user-center-header .btn-primary:before,
.user-center-section .btn-primary:before {
display: none;
}
.btn-secondary {
background-color: var(--text-secondary);
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
}
.btn:active {
transform: translateY(0);
}
.section-title {
color: var(--text-primary);
font-size: 1.3rem;
font-weight: 600;
margin: 2rem 0 1.2rem;
position: relative;
padding-left: 1rem;
border-left: 3px solid var(--primary-color);
}
.container-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin-bottom: 1.5rem;
border-radius: var(--radius-md);
overflow: hidden;
box-shadow: var(--shadow-sm);
}
.container-table th {
background-color: var(--primary-color);
color: white;
padding: 0.9rem 1rem;
text-align: left;
font-weight: 500;
}
.container-table td {
padding: 0.9rem 1rem;
border-bottom: 1px solid var(--border-light);
}
.container-table tr:last-child td {
border-bottom: none;
}
.error-message {
color: var(--danger-color);
margin-top: 0.75rem;
padding: 0.75rem;
border-radius: var (--radius-md);;
background-color: rgba(255, 82, 82, 0.1);
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.error-message:before {
content: "\f071";
font-family: "Font Awesome 6 Free";
font-weight: 900;
}
.success-message {
color: var (--success-color);
margin-top: 0.75rem;
padding: 0.75rem;
border-radius: var (--radius-md);
background-color: rgba(50, 213, 131, 0.1);
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.success-message:before {
content: "\f00c";
font-family: "Font Awesome 6 Free";
font-weight: 900;
}
/* 网络测试部分 */
#network-test .input-group {
margin-bottom: 1.2rem;
max-width: 600px;
background-color: rgba(61, 124, 244, 0.03);
padding: 1.5rem;
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
#network-test label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-secondary);
font-weight: 500;
}
#testResults {
margin-top: 1.5rem;
padding: 1rem;
background-color: #1e2532;
color: #e9ecef;
border-radius: var(--radius-md);
white-space: pre-wrap;
font-family: 'JetBrains Mono', monospace;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
border: none;
box-shadow: var(--shadow-md);
position: relative;
}
/* 文档编辑器部分 */
#editor #editorContainer {
margin-top: 2rem;
border-radius: var(--radius-md);
background-color: var(--container-bg);
overflow: hidden;
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
}
#documentTitle {
width: 100%;
padding: 1rem;
margin-bottom: 0;
border: 1px solid var(--border-color);
border-radius: var(--radius-md) var(--radius-md) 0 0;
font-size: 1.1rem;
font-weight: 500;
box-shadow: none;
background-color: var(--container-bg);
color: var(--text-primary);
border-bottom: 1px solid var(--border-light);
}
#documentTitle:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(61, 124, 244, 0.2);
outline: none;
}
/* Editor.md 样式覆盖 */
#editor #editorContainer .editormd {
border: 1px solid var(--border-light) !important;
border-top: none !important;
border-radius: 0 0 var(--radius-md) var(--radius-md) !important;
box-shadow: none !important;
margin-top: 0 !important;
width: 100% !important;
height: auto !important;
}
#editor #editorContainer .editormd-toolbar {
background-color: var(--container-bg) !important;
border-bottom: 1px solid var(--border-light) !important;
padding: 0.5rem !important;
width: 100% !important;
box-sizing: border-box !important;
}
#editor #editorContainer .editormd-toolbar button {
background-color: transparent !important;
border: 1px solid var(--border-light) !important;
color: var(--text-primary) !important;
border-radius: var(--radius-sm) !important;
margin: 0 2px !important;
padding: 0.4rem 0.6rem !important;
transition: all 0.2s ease !important;
}
#editor #editorContainer .editormd-toolbar button:hover {
background-color: var(--primary-color) !important;
color: white !important;
border-color: var(--primary-color) !important;
}
#editor #editorContainer .editormd-toolbar button.active {
background-color: var(--primary-color) !important;
color: white !important;
border-color: var(--primary-color) !important;
}
/* 编辑区域和预览区域等宽等高 */
#editor #editorContainer .editormd > .editormd-editor {
width: 50% !important;
float: left !important;
box-sizing: border-box !important;
}
#editor #editorContainer .editormd > .editormd-preview {
width: 50% !important;
float: right !important;
box-sizing: border-box !important;
}
#editor #editorContainer .CodeMirror {
background-color: var(--container-bg) !important;
color: var(--text-primary) !important;
border: none !important;
font-family: 'Monaco', 'Consolas', 'Courier New', monospace !important;
font-size: 14px !important;
line-height: 1.6 !important;
height: 500px !important;
width: 100% !important;
box-sizing: border-box !important;
}
#editor #editorContainer .CodeMirror .CodeMirror-cursor {
border-left: 1px solid var(--text-primary) !important;
}
#editor #editorContainer .CodeMirror .CodeMirror-selected {
background-color: rgba(61, 124, 244, 0.2) !important;
}
#editor #editorContainer .editormd-preview {
background-color: var(--container-bg) !important;
color: var(--text-primary) !important;
border-left: 1px solid var(--border-light) !important;
padding: 1rem !important;
height: 500px !important;
overflow-y: auto !important;
box-sizing: border-box !important;
}
/* 确保容器清除浮动 */
#editor #editorContainer .editormd::after {
content: "" !important;
display: table !important;
clear: both !important;
}
#editor #editorContainer .editormd-preview h1,
#editor #editorContainer .editormd-preview h2,
#editor #editorContainer .editormd-preview h3,
#editor #editorContainer .editormd-preview h4,
#editor #editorContainer .editormd-preview h5,
#editor #editorContainer .editormd-preview h6 {
color: var(--text-primary) !important;
border-bottom: 1px solid var(--border-light) !important;
padding-bottom: 0.5rem !important;
margin-bottom: 1rem !important;
}
#editor #editorContainer .editormd-preview pre {
background-color: rgba(0, 0, 0, 0.05) !important;
border: 1px solid var(--border-light) !important;
border-radius: var(--radius-sm) !important;
padding: 1rem !important;
overflow-x: auto !important;
}
#editor #editorContainer .editormd-preview blockquote {
border-left: 4px solid var(--primary-color) !important;
background-color: rgba(61, 124, 244, 0.05) !important;
margin: 1rem 0 !important;
padding: 0.5rem 1rem !important;
}
#editor #editorContainer .editormd-preview table {
border-collapse: collapse !important;
width: 100% !important;
margin: 1rem 0 !important;
}
#editor #editorContainer .editormd-preview table th,
#editor #editorContainer .editormd-preview table td {
border: 1px solid var(--border-light) !important;
padding: 0.5rem 1rem !important;
text-align: left !important;
}
#editor #editorContainer .editormd-preview table th {
background-color: rgba(61, 124, 244, 0.1) !important;
font-weight: 600 !important;
}
/* 编辑器操作按钮样式 */
.editor-actions {
padding: 1rem !important;
background-color: var(--container-bg) !important;
border-top: 1px solid var(--border-light) !important;
display: flex !important;
gap: 0.75rem !important;
justify-content: flex-end !important;
}
.editor-actions .btn {
padding: 0.75rem 1.5rem !important;
border-radius: var(--radius-md) !important;
font-weight: 500 !important;
transition: all 0.2s ease !important;
}
.editor-actions .btn-primary {
background-color: var(--primary-color) !important;
color: white !important;
border: 1px solid var(--primary-color) !important;
}
.editor-actions .btn-primary:hover {
background-color: var(--primary-dark) !important;
border-color: var(--primary-dark) !important;
transform: translateY(-2px) !important;
box-shadow: var(--shadow-md) !important;
}
.editor-actions .btn-secondary {
background-color: transparent !important;
color: var(--text-secondary) !important;
border: 1px solid var(--border-color) !important;
}
.editor-actions .btn-secondary:hover {
background-color: var(--text-secondary) !important;
color: white !important;
transform: translateY(-2px) !important;
box-shadow: var(--shadow-sm) !important;
}
/* 响应式 Editor.md 样式 */
@media (max-width: 768px) {
#editor #editorContainer .editormd {
height: auto !important;
}
/* 移动端编辑区域和预览区域上下布局 */
#editor #editorContainer .editormd > .editormd-editor {
width: 100% !important;
float: none !important;
margin-bottom: 1px !important;
}
#editor #editorContainer .editormd > .editormd-preview {
width: 100% !important;
float: none !important;
border-left: none !important;
border-top: 1px solid var(--border-light) !important;
}
#editor #editorContainer .CodeMirror {
height: 300px !important;
}
#editor #editorContainer .editormd-preview {
height: 300px !important;
}
#editor #editorContainer .editormd-toolbar {
padding: 0.3rem !important;
flex-wrap: wrap !important;
}
#editor #editorContainer .editormd-toolbar button {
margin: 1px !important;
padding: 0.3rem 0.4rem !important;
font-size: 0.85rem !important;
}
.editor-actions {
flex-direction: column !important;
gap: 0.5rem !important;
}
.editor-actions .btn {
width: 100% !important;
text-align: center !important;
}
}
/* 中等屏幕适配 */
@media (max-width: 1024px) and (min-width: 769px) {
#editor #editorContainer .CodeMirror {
height: 450px !important;
}
#editor #editorContainer .editormd-preview {
height: 450px !important;
}
}
.password-hint {
color: var(--text-secondary);
font-size: 0.9rem;
margin: 0.5rem 0 1rem;
display: block;
background-color: rgba(61, 124, 244, 0.05);
padding: 0.75rem;
border-radius: var(--radius-md);
}
#passwordStrength {
font-size: 0.9rem;
margin: 0.5rem 0;
display: block;
}
/* 警告和确认对话框样式 */
.swal2-popup {
font-size: 1rem !important;
border-radius: var(--radius-lg) !important;
}
.swal2-title {
font-weight: 600 !important;
color: var(--text-primary) !important;
}
.swal2-styled.swal2-confirm {
background-color: var(--primary-color) !important;
border-radius: var(--radius-md) !important;
}
/* 添加条目按钮 */
.add-btn {
background-color: var(--success-color);
color: white;
padding: 0.8rem 1.2rem;
border-radius: var (--radius-md);
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
margin-top: 1rem;
box-shadow: var(--shadow-sm);
}
.add-btn:before {
content: "";
/* 移除 Font Awesome 图标,以避免和 HTML 中的图标重复 */
}
.add-btn:hover {
background-color: #29b873;
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
/* Docker容器操作按钮样式 */
.action-cell {
min-width: 200px;
}
.action-buttons {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.action-buttons button {
padding: 3px 8px;
font-size: 0.8rem;
margin-right: 3px;
white-space: nowrap;
}
.action-buttons button i {
margin-right: 3px;
font-size: 0.75rem;
}
/* 改善按钮在小屏幕上的显示 */
@media (max-width: 768px) {
.action-buttons {
flex-direction: column;
}
.action-buttons button {
margin-bottom: 3px;
width: 100%;
}
}
/* 响应式设计 */
@media (max-width: 992px) {
.admin-container {
flex-direction: column;
}
.sidebar {
width: 100%;
padding: 1rem 0;
}
.sidebar ul {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.sidebar li {
padding: 0.5rem 1rem;
border-left: none;
border-bottom: 3px solid transparent;
}
.sidebar li.active {
border-left: none;
border-bottom: 3px solid var(--primary-color);
}
.content-area {
padding: 1rem;
}
.content-section {
padding: 1.5rem;
}
}
@media (max-width: 768px) {
.content-section th,
.content-section td {
padding: 0.75rem 0.5rem;
font-size: 0.9rem;
}
.button-group {
flex-direction: column;
}
.btn, .action-btn {
width: 100%;
margin-bottom: 0.5rem;
}
.action-buttons {
flex-direction: column;
}
.action-buttons button {
margin-bottom: 3px;
width: 100%;
}
}
@media (max-width: 480px) {
.login-content {
width: 90%;
padding: 1.5rem;
}
}
/* Excel风格表格 */
.excel-table {
border-collapse: collapse;
width: 100%;
font-size: 14px;
border: 1px solid #ccc;
}
.excel-table th {
background-color: #f2f2f2;
border: 1px solid #ccc;
padding: 8px;
text-align: center;
position: sticky;
top: 0;
z-index: 10;
}
.excel-table td {
border: 1px solid #ccc;
padding: 6px 8px;
text-align: left;
}
.excel-table tr:nth-child(even) {
background-color: #f9f9f9;
}
.excel-table tr:hover {
background-color: #f0f7ff;
}
/* 容器更新样式 */
.update-progress {
padding: 1rem;
text-align: center;
}
.update-progress p {
margin-bottom: 1rem;
font-size: 1rem;
}
.update-progress strong {
color: #007bff;
font-weight: 600;
}
.progress-status {
margin: 1rem 0;
font-size: 0.9rem;
color: #555;
font-weight: 500;
}
.progress-container {
width: 100%;
height: 10px;
background-color: #f0f0f0;
border-radius: 5px;
overflow: hidden;
margin: 1rem 0;
}
.progress-bar {
height: 100%;
background-color: #4CAF50;
width: 0%;
transition: width 0.3s ease-in-out;
border-radius: 5px;
position: relative;
}
/* 修改SweetAlert样式 */
.swal2-popup.update-popup {
border-radius: 10px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}
.swal2-title.update-title {
font-size: 1.5rem;
font-weight: 600;
color: #333;
}
.swal2-input.update-input {
box-shadow: none;
border: 1px solid #ddd;
border-radius: 5px;
padding: 0.75rem;
font-size: 0.95rem;
}
.swal2-input.update-input:focus {
border-color: #3085d6;
box-shadow: 0 0 0 3px rgba(48, 133, 214, 0.2);
}
/* 容器日志样式 */
.container-logs {
max-height: 70vh;
overflow-y: auto;
background: #1e1e1e;
color: #f0f0f0;
padding: 1rem;
border-radius: 5px;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
white-space: pre-wrap;
word-break: break-all;
}
.swal2-logs-container {
max-width: 100%;
padding: 0 !important;
}
.swal2-logs-popup {
max-width: 80% !important;
}
/* 容器状态标签样式 */
.badge.status-running {
background-color: #28a745;
color: white;
padding: 5px 8px;
border-radius: 4px;
display: inline-block;
min-width: 70px;
text-align: center;
}
.badge.status-stopped, .badge.status-exited {
background-color: #dc3545;
color: white;
padding: 5px 8px;
border-radius: 4px;
display: inline-block;
min-width: 70px;
text-align: center;
}
.badge.status-created {
background-color: #17a2b8;
color: white;
padding: 5px 8px;
border-radius: 4px;
display: inline-block;
min-width: 70px;
text-align: center;
}
.badge.status-unknown, .badge.status-paused {
background-color: #6c757d;
color: white;
padding: 5px 8px;
border-radius: 4px;
display: inline-block;
min-width: 70px;
text-align: center;
}
/* 容器表格标题样式 */
.docker-table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.docker-table-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0;
}
/* 菜单管理 - 新增行样式 */
#new-menu-item-row input[type="text"],
#new-menu-item-row select {
/* 使用 Bootstrap 的 form-control-sm 已经减小了 padding 和 font-size */
margin-bottom: 0; /* 移除下方外边距,使其在表格行内更紧凑 */
}
#new-menu-item-row .form-control-sm {
padding: 0.3rem 0.6rem; /* 微调内边距 */
font-size: 0.875rem; /* 统一字体大小 */
}
#new-menu-item-row .form-select-sm {
padding: 0.3rem 1.5rem 0.3rem 0.6rem; /* 微调 select 内边距以适应箭头 */
font-size: 0.875rem;
}
.action-buttons-new-menu .btn {
margin-right: 5px; /* 按钮间距 */
min-width: 80px; /* 给按钮一个最小宽度 */
}
.action-buttons-new-menu .btn:last-child {
margin-right: 0;
}
.action-buttons-new-menu .btn i {
margin-right: 4px; /* 图标和文字间距 */
}
/* 使新行单元格垂直居中 */
#new-menu-item-row td {
vertical-align: middle;
}
/* 直接覆盖#testResults.loading的样式防止旋转 */
#network-test #testResults.loading {
animation: none !important;
border: none !important;
display: block !important;
position: relative !important;
text-align: center !important;
padding: 15px !important;
color: var(--text-secondary, #6c757d) !important;
font-size: 1rem !important;
width: auto !important;
height: auto !important;
}
/* 使用:before添加文本内容 */
#network-test #testResults.loading:before {
content: "测试进行中..." !important;
animation: none !important;
border: none !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
color: var(--text-light, #e9ecef) !important;
font-size: 1rem !important;
font-weight: 500 !important;
padding: 0 !important;
background-color: transparent !important;
z-index: 10 !important;
text-align: center !important;
}
#network-test .form-row {
display: flex;
flex-wrap: wrap; /* 允许换行到小屏幕 */
gap: 1.5rem; /* 各个元素之间的间距 */
margin-bottom: 1.5rem;
align-items: flex-end; /* 使得按钮和选择框底部对齐 */
}
#network-test .form-group {
flex: 1; /* 让表单组占据可用空间 */
min-width: 250px; /* 避免在小屏幕上过于挤压 */
background-color: transparent; /* 移除之前 #network-test .input-group 的背景色 */
padding: 0; /* 移除内边距 */
box-shadow: none; /* 移除阴影 */
border-radius: 0;
}
#network-test label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-primary);
font-weight: 500;
font-size: 0.95rem;
}
#network-test .form-control,
#network-test .form-select {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background-color: var(--input-bg, var(--container-bg)); /* 允许自定义输入背景或使用容器背景 */
color: var(--text-primary);
font-size: 0.95rem;
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
#network-test .form-control:focus,
#network-test .form-select:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(var(--primary-rgb, 61, 124, 244), 0.25); /* 使用RGB变量 */
outline: none;
}
#network-test .test-controls-container {
/* 这个容器包裹选择器和按钮 */
display: flex;
flex-wrap: wrap; /* 允许换行 */
gap: 1rem; /* 控件之间的间距 */
align-items: flex-end; /* 使得按钮和选择框底部对齐 */
margin-bottom: 1.5rem;
}
#network-test .test-controls-container .form-group {
flex-grow: 1;
flex-basis: 200px; /* 给每个控件一个基础宽度 */
}
#network-test .start-test-btn {
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
white-space: nowrap; /* 防止按钮文字换行 */
height: fit-content; /* 与调整后的 select 高度匹配 */
flex-shrink: 0; /* 防止按钮在 flex 布局中被压缩 */
}
#network-test .start-test-btn i {
margin-right: 0.5rem;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
padding: 0.5rem 0;
border-bottom: 1px solid var(--border-light);
}
.results-header h3 {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-light, #e9ecef); /* Changed to light color for dark background */
margin: 0;
}
#clearTestResultsBtn {
font-size: 0.85rem;
padding: 0.3rem 0.8rem;
}
#testResultsContainer {
background-color: var(--container-bg-dark, #1e2532); /* 使用变量或默认暗色 */
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
margin-top: 1rem;
padding: 0; /* 移除外层padding让头部和内容区自己控制 */
position: relative; /* Add this to be a positioning context for absolute children if needed by headers etc. */
}
#testResults {
padding: 1rem;
color: var(--text-light, #e9ecef);
font-family: var(--font-mono, 'JetBrains Mono', monospace);
font-size: 0.9rem;
white-space: pre-wrap;
word-break: break-all;
min-height: 200px;
max-height: 400px;
overflow-y: auto;
background-color: transparent;
border-radius: 0 0 var(--radius-md) var(--radius-md);
box-shadow: none;
border: none;
margin-top: 0;
position: relative;
}
/* Use higher specificity and !important to force override */
#network-test #testResults.loading::before {
content: "测试进行中..."; /* Force text content */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* Center the text block */
color: var(--text-secondary, #6c757d);
font-size: 1rem;
font-weight: 500;
padding: 0.75rem 1.25rem;
background-color: transparent !important;
border: none !important; /* 移除边框 */
border-radius: var(--radius-md, 0.375rem);
z-index: 10;
text-align: center;
/* Force removal of spinner styles */
animation: none !important;
border-top-color: transparent !important; /* Force override any spinner head */
width: auto !important; /* Force width */
height: auto !important; /* Force height */
box-sizing: border-box; /* Ensure padding is included correctly */
}
/* Ensure keyframes for the spinner are removed or commented out */
/* @keyframes testResultSpinner { to { transform: rotate(360deg); } } */
/* Style for the <pre> tag inside #testResults for consistent output formatting */
#testResults pre {
margin: 0; /* Remove default pre margin */
font-family: inherit; /* Inherit from #testResults (monospace) */
font-size: inherit; /* Inherit from #testResults */
color: inherit; /* Inherit from #testResults */
white-space: pre-wrap; /* Ensure wrapping */
word-break: break-all; /* Ensure long lines break */
background-color: transparent; /* Ensure no pre background interferes */
padding: 0; /* No extra padding for pre, parent div handles it */
}
#testResults pre.text-danger {
color: var(--danger-color, #dc3545); /* Ensure error text is colored */
}
/* Placeholder text styling (if using <p class="text-muted...">) */
#testResults .text-muted.text-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(100% - 2rem); /* Consider padding of #testResults */
}
/* 保持容器监控页面已停止容器列表的表头在鼠标悬停时背景色不变 */
#docker-monitoring .container-table th:hover {
background-color: var(--primary-color);
color: white;
}
/* SweetAlert2 弹窗内容文本居中 */
#swal2-html-container, /* 使用 ID 提高特异性 */
.swal2-html-container { /* 保留类选择器作为备用 */
text-align: center !important;
}
#swal2-html-container p,
.swal2-html-container p {
text-align: center !important;
}
#swal2-html-container div,
.swal2-html-container div {
text-align: center !important;
}
/* 下拉菜单样式修复 */
.btn-group {
position: relative;
display: inline-flex;
vertical-align: middle;
}
.dropdown-menu {
position: absolute;
z-index: 1050;
display: none;
min-width: 180px;
width: auto;
padding: 0;
margin: 0.325rem 0 0;
font-size: 0.9rem;
color: var(--text-primary);
text-align: left;
list-style: none;
background-color: #ffffff;
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
opacity: 0;
visibility: hidden;
transform: translateY(10px) scale(0.98);
transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s ease;
}
.dropdown-menu.show {
display: block;
opacity: 1;
visibility: visible;
transform: translateY(0) scale(1);
}
/* 当下拉菜单在按钮右侧显示时 */
.dropdown-menu.dropdown-menu-right {
transform: translateX(10px) scale(0.98);
}
/* 当下拉菜单在按钮上方显示时 */
.dropdown-menu.dropdown-menu-top {
transform: translateY(-10px) scale(0.98);
}
/* 当下拉菜单在按钮右侧且显示时 */
.dropdown-menu.dropdown-menu-right.show {
transform: translateX(0) scale(1);
}
/* 当下拉菜单在按钮上方且显示时 */
.dropdown-menu.dropdown-menu-top.show {
transform: translateY(0) scale(1);
}
/* 右对齐的下拉菜单 */
.dropdown-menu-end {
right: 0;
left: auto;
}
.dropdown-header {
display: block;
padding: 0.5rem 1rem;
margin-bottom: 0;
font-size: 0.8rem;
color: #6c757d;
white-space: nowrap;
background-color: #f8f9fa;
font-weight: 500;
}
.dropdown-menu .dropdown-item {
display: flex;
align-items: center;
width: 100%;
padding: 0.7rem 1.25rem;
clear: both;
font-weight: 400;
color: #495057;
text-align: inherit;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border: 0;
transition: all 0.2s ease;
}
.dropdown-menu .dropdown-item i {
margin-right: 10px;
width: 18px;
text-align: center;
font-size: 0.95rem;
opacity: 0.8;
}
.dropdown-menu .dropdown-item:hover,
.dropdown-menu .dropdown-item:focus {
color: #1e70eb;
background-color: #f1f7ff;
text-decoration: none;
}
.dropdown-menu .dropdown-item.active,
.dropdown-menu .dropdown-item:active {
background-color: #e8f1ff;
color: #1e70eb;
}
.dropdown-menu .dropdown-item:hover i,
.dropdown-menu .dropdown-item:focus i {
opacity: 1;
}
.dropdown-divider {
height: 0;
margin: 0;
overflow: hidden;
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
/* 美化操作按钮 */
.action-cell .btn-group .btn-primary {
background: linear-gradient(to bottom, #4a7bff, #3d66e3);
border: none;
box-shadow: 0 2px 5px rgba(61, 124, 244, 0.2);
padding: 0.45rem 1rem;
font-weight: 500;
transition: all 0.2s ease;
border-radius: 6px;
}
.action-cell .btn-group .btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(61, 124, 244, 0.3);
background: linear-gradient(to bottom, #5a88ff, #4a7bff);
}
.action-cell .btn-group .btn-primary:active {
transform: translateY(0);
}
/* 右对齐的下拉菜单 */
.dropdown-menu-end {
right: 0;
left: auto;
}
/* 美化表格中的操作列,确保有足够空间显示弹出菜单 */
.action-cell {
min-width: 120px;
position: relative;
}
/* 确保菜单项样式美观 */
.dropdown-item {
display: flex;
align-items: center;
width: 100%;
padding: 0.7rem 1.25rem;
clear: both;
font-weight: 400;
color: #495057;
text-align: inherit;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border: 0;
transition: all 0.2s ease;
}
.dropdown-item i {
margin-right: 10px;
width: 18px;
text-align: center;
font-size: 0.95rem;
opacity: 0.8;
}
.dropdown-item:hover, .dropdown-item:focus {
color: #1e70eb;
background-color: rgba(30, 112, 235, 0.08);
text-decoration: none;
}
.dropdown-item:hover i, .dropdown-item:focus i {
opacity: 1;
}
.dropdown-divider {
height: 0;
margin: 0.5rem 0;
overflow: hidden;
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
/* 菜单项图标美化 */
.dropdown-item .fa-file-alt {
color: #17a2b8;
}
.dropdown-item .fa-info-circle {
color: #6c757d;
}
.dropdown-item .fa-stop {
color: #dc3545;
}
.dropdown-item .fa-play {
color: #28a745;
}
.dropdown-item .fa-sync-alt {
color: #ffc107;
}
.dropdown-item .fa-trash-alt {
color: #dc3545;
}
.dropdown-item .fa-cloud-download-alt {
color: #17a2b8;
}
/* 原生select下拉框样式美化 */
.simple-dropdown {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23495057' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 12px;
width: 100%;
padding: 0.45rem 2rem 0.45rem 0.75rem;
font-size: 0.875rem;
font-weight: 400;
color: #495057;
border: 1px solid #ced4da;
border-radius: 6px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: all 0.2s ease-in-out;
cursor: pointer;
}
.simple-dropdown:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,0.25);
}
.simple-dropdown:hover {
border-color: #adb5bd;
}
.simple-dropdown optgroup {
font-weight: 600;
color: #343a40;
background-color: #f8f9fa;
padding: 5px;
}
.simple-dropdown option {
font-weight: normal;
padding: 8px;
background-color: #fff;
color: #495057;
}
.simple-dropdown option:hover,
.simple-dropdown option:focus {
background-color: #f1f7ff;
color: #1e70eb;
}
/* Action按钮样式保留以便保持兼容 */
.action-cell .btn-group {
display: inline-block;
width: 100%;
}
.action-cell .btn-group .btn-primary {
display: none; /* 隐藏原始按钮由select替代 */
}
</style>
</head>
<body>
<div id="loadingSpinner" class="loading-spinner" style="display: none;"></div>
<div id="loadingIndicator" style="display: flex; justify-content: center; align-items: center; height: 100vh;">
<p>加载中...</p>
</div>
<div class="admin-container" id="adminContainer" style="display: none;">
<div class="sidebar">
<div class="sidebar-header">
<h2><i class="fas fa-cogs"></i>管理面板</h2>
</div>
<div class="user-profile">
<div class="user-avatar">
<i class="fas fa-user"></i>
</div>
<div class="user-info">
<div class="user-name" id="currentUsername">管理员</div>
<div class="user-role">系统管理员</div>
</div>
<div class="user-actions">
<div class="user-action-btn" id="userCenterBtn">个人中心</div>
<div class="user-action-btn logout" id="logoutBtn">退出登录</div>
</div>
</div>
<ul class="sidebar-nav">
<li data-section="dashboard" class="active">
<i class="fas fa-tachometer-alt"></i>控制面板
</li>
<li data-section="basic-config">
<i class="fas fa-wrench"></i>基本配置
</li>
<li data-section="menu-management">
<i class="fas fa-bars"></i>菜单管理
</li>
<li data-section="documentation-management">
<i class="fas fa-file-alt"></i>文档管理
</li>
<li data-section="network-test">
<i class="fas fa-network-wired"></i>网络测试
</li>
<li data-section="docker-status">
<i class="fab fa-docker"></i>容器管理
</li>
<li data-section="docker-monitoring">
<i class="fas fa-chart-line"></i>容器监控
</li>
<li data-section="user-center">
<i class="fas fa-user-cog"></i>用户中心
</li>
</ul>
</div>
<div class="content-area">
<div class="container">
<!-- 控制面板 -->
<div id="dashboard" class="content-section active">
<div class="welcome-banner">
<div class="welcome-content">
<h1 class="welcome-title">欢迎回来,<span id="welcomeUsername">管理员</span></h1>
<p class="welcome-subtitle">这里是 Docker 镜像代理加速系统控制面板</p>
<button class="welcome-action" id="refreshSystemBtn">
<i class="fas fa-sync-alt"></i> 刷新系统状态
</button>
</div>
<div class="docker-status" id="dockerStatusIndicator" style="position: absolute; top: 10px; right: 10px; padding: 5px 10px; border-radius: 4px; font-size: 0.9rem; color: white;">
<i class="fab fa-docker"></i>
<span id="dockerStatusText">正在检查...</span>
</div>
</div>
<div class="dashboard-grid">
<!-- 仪表板卡片将由 systemStatus.initDashboard() 动态生成 -->
</div>
</div>
<!-- 基本配置 -->
<div id="basic-config" class="content-section">
<h1 class="admin-title">基本配置</h1>
<div class="config-form">
<div class="form-group">
<label for="logoUrl">Logo URL: (可选)</label>
<input type="url" id="logoUrl" name="logoUrl" class="form-control">
</div>
<button type="button" class="btn btn-primary" onclick="validateAndSaveConfig('logo')">保存 Logo</button>
<div class="form-group" style="margin-top: 2rem;">
<label for="proxyDomain">Docker镜像代理地址: (必填)</label>
<input type="text" id="proxyDomain" name="proxyDomain" class="form-control" required>
</div>
<button type="button" class="btn btn-primary" onclick="validateAndSaveConfig('proxy')">保存代理地址</button>
</div>
</div>
<!-- 菜单管理 -->
<div id="menu-management" class="content-section">
<h2 class="menu-label">菜单项管理</h2>
<table id="menuTable">
<thead>
<!-- 表头将由 menuManager.renderMenuItems() 动态生成 -->
</thead>
<tbody id="menuTableBody">
<!-- 菜单项将在这里动态添加 -->
</tbody>
</table>
<button type="button" class="add-btn" onclick="menuManager.showNewMenuItemRow()">
<i class="fas fa-plus"></i> 添加菜单项
</button>
</div>
<!-- 文档管理部分 -->
<div id="documentation-management" class="content-section">
<h2 class="menu-label">文档管理</h2>
<table id="documentTable">
<thead>
<!-- 表头将由 documentManager.renderDocumentList() 动态生成 -->
</thead>
<tbody id="documentTableBody">
<!-- 文档列表将在这里动态添加 -->
</tbody>
</table>
<div class="action-bar" style="margin-top: 15px; margin-bottom: 15px;">
<button type="button" class="btn btn-primary add-btn" onclick="documentManager.newDocument()">
<i class="fas fa-plus"></i> 新建文档
</button>
</div>
</div>
<!-- 修改密码部分 -->
<div id="password-change" class="content-section">
<h2 class="menu-label">修改密码</h2>
<label for="currentPassword">当前密码</label>
<input type="password" id="currentPassword" name="currentPassword">
<label for="newPassword">新密码</label>
<span class="password-hint" id="passwordHint">密码必须包含至少一个字母、一个数字和一个特殊字符长度在8到16个字符之间</span>
<input type="password" id="newPassword" name="newPassword" oninput="userCenter.checkPasswordStrength()">
<span id="passwordStrength" style="color: red;"></span>
<button type="button" onclick="userCenter.changePassword()">修改密码</button>
</div>
<!-- 网络测试 -->
<div id="network-test" class="content-section">
<h1 class="admin-title">网络测试</h1>
<div class="test-controls-container">
<div class="form-group">
<label for="domainSelect">目标域名:</label>
<select id="domainSelect" class="form-select">
<!-- 选项将由 networkTest.initNetworkTest() 动态生成 -->
</select>
</div>
<div class="form-group" id="customDomainContainer" style="display: none;">
<label for="customDomain">自定义域名:</label>
<input type="text" id="customDomain" class="form-control" placeholder="请输入域名,如 example.com">
</div>
<div class="form-group">
<label for="testType">测试类型:</label>
<select id="testType" class="form-select">
<!-- 选项将由 networkTest.initNetworkTest() 动态生成 -->
</select>
</div>
<button id="startTestBtn" class="btn btn-primary start-test-btn">
<i class="fas fa-play"></i> 开始测试
</button>
</div>
<div id="testResultsContainer">
<div class="results-header">
<h3>测试结果</h3>
<button id="clearTestResultsBtn" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-times-circle"></i> 清空结果
</button>
</div>
<div id="testResults">
<!-- 测试结果将显示在这里 -->
</div>
</div>
</div>
<!-- Docker服务状态 -->
<div id="docker-status" class="content-section">
<div class="section-heading">
<h2 class="section-title">Docker 服务状态</h2>
<div class="section-description">
查看和管理Docker容器状态和操作
</div>
</div>
<!-- 新增表格容器以支持标题和操作区域 -->
<div id="dockerTableContainer" class="table-responsive">
<table id="dockerStatusTable" class="excel-table">
<thead></thead>
<tbody id="dockerStatusTableBody"></tbody>
</table>
</div>
</div>
<!-- Docker监控配置 -->
<div id="docker-monitoring" class="content-section">
<h1 class="admin-title">Docker 容器监控</h1>
<div class="monitoring-status">
<span>监控状态:</span>
<span id="monitoringStatus" class="status-indicator">加载中...</span>
</div>
<div class="config-form">
<div class="form-group">
<label for="notificationType">通知方式:</label>
<select id="notificationType" class="form-control">
<option value="wechat">企业微信群机器人</option>
<option value="telegram">Telegram Bot</option>
</select>
</div>
<div id="wechatFields">
<div class="form-group">
<label for="webhookUrl">企业微信机器人 Webhook URL:</label>
<input type="text" id="webhookUrl" name="webhookUrl" class="form-control">
</div>
</div>
<div id="telegramFields" style="display: none;">
<div class="form-group">
<label for="telegramToken">Telegram Bot Token:</label>
<input type="text" id="telegramToken" name="telegramToken" class="form-control">
</div>
<div class="form-group">
<label for="telegramChatId">Telegram Chat ID:</label>
<input type="text" id="telegramChatId" name="telegramChatId" class="form-control">
</div>
</div>
<div class="form-group">
<label for="monitorInterval">监控间隔 (秒):</label>
<input type="number" id="monitorInterval" name="monitorInterval" min="1" value="60" class="form-control">
</div>
<div class="button-group">
<button class="btn btn-secondary" id="testNotifyBtn" onclick="app.testNotification()">测试通知</button>
<button class="btn btn-primary" id="saveConfigBtn" onclick="app.saveMonitoringConfig()">保存配置</button>
<button class="btn btn-secondary" id="toggleMonitoringBtn" onclick="app.toggleMonitoring()">开启/关闭监控</button>
</div>
</div>
<div id="messageContainer"></div>
<h2 class="section-title">已停止的容器</h2>
<div class="container-list">
<table id="stoppedContainersTable" class="container-table">
<thead>
<tr>
<th>容器ID</th>
<th>容器名称</th>
<th>镜像名称</th>
<th>运行状态</th>
</tr>
</thead>
<tbody id="stoppedContainersBody"></tbody>
</table>
</div>
</div>
<!-- 用户中心部分 -->
<div id="user-center" class="content-section">
<div class="user-center-header">
<div>
<h1 class="user-center-title">用户中心</h1>
<p class="user-center-subtitle">管理您的个人信息和账户安全</p>
</div>
<button class="btn btn-primary" id="ucLogoutBtn" style="display: inline-block;">退出登录</button>
</div>
<!-- 个人资料卡片 -->
<div class="user-profile-card">
<div class="user-profile-avatar">
<i class="fas fa-user-circle"></i>
</div>
<div class="user-profile-info">
<h2 class="user-profile-name" id="profileUsername">管理员</h2>
<p class="user-profile-role">系统管理员</p>
<div class="user-profile-badges">
<span class="user-badge admin"><i class="fas fa-shield-alt"></i> 管理员</span>
<span class="user-badge active"><i class="fas fa-check-circle"></i> 活跃</span>
</div>
</div>
<div class="user-profile-actions">
<button class="btn btn-outline" onclick="userCenter.refreshUserInfo()"><i class="fas fa-sync-alt"></i> 刷新</button>
</div>
</div>
<div class="user-dashboard-grid">
<!-- 账户信息卡片 -->
<div class="user-center-card">
<div class="user-center-section">
<h2 class="user-center-section-title">账户信息</h2>
<div class="user-stats">
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-sign-in-alt"></i></div>
<div class="stat-value" id="loginCount">--</div>
<div class="stat-label">登录次数</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-clock"></i></div>
<div class="stat-value" id="lastLogin">--</div>
<div class="stat-label">上次登录</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-calendar-alt"></i></div>
<div class="stat-value" id="accountAge">--</div>
<div class="stat-label">账户天数</div>
</div>
</div>
</div>
</div>
<!-- 密码修改卡片 -->
<div class="user-center-card">
<div class="user-center-section">
<h2 class="user-center-section-title">修改密码</h2>
<form id="changePasswordForm">
<label for="ucCurrentPassword">当前密码</label>
<input type="password" id="ucCurrentPassword" name="currentPassword">
<label for="ucNewPassword">新密码</label>
<span class="password-hint" id="ucPasswordHint">密码必须包含至少一个字母、一个数字和一个特殊字符长度在8到16个字符之间</span>
<input type="password" id="ucNewPassword" name="newPassword" oninput="userCenter.checkUcPasswordStrength()">
<label for="ucConfirmPassword">确认新密码</label>
<input type="password" id="ucConfirmPassword" name="confirmPassword">
<div style="display: flex; align-items: center; margin-top: 10px;">
<button type="submit" class="btn btn-primary">修改密码</button>
<span id="ucPasswordStrength" style="color: red; white-space: nowrap; display: inline-block; margin-left: 15px;"></span>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="login-modal" id="loginModal" style="display: none;">
<div class="login-content">
<div class="login-header">
<h2>管理员登录</h2>
</div>
<form id="loginForm" class="login-form">
<input type="text" id="username" name="username" placeholder="用户名" required>
<input type="password" id="password" name="password" placeholder="密码" required>
<!-- 修复这里的结构问题 - 添加了缺失的开始标签 -->
<div class="captcha-container">
<input type="text" id="captcha" name="captcha" placeholder="验证码" required>
<span id="captchaText" onclick="auth.refreshCaptcha()">点击刷新验证码</span>
</div>
<button type="submit" id="loginButton">登录</button>
</form>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<!-- 添加 Bootstrap 5 JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<!-- Toast UI Editor - 添加编辑器需要的依赖 -->
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
<!-- 模块化JS文件 - 按依赖顺序加载 -->
<script src="js/core.js"></script>
<script src="js/auth.js"></script>
<script src="js/userCenter.js"></script>
<script src="js/menuManager.js"></script>
<script src="js/documentManager.js"></script>
<script src="js/systemStatus.js"></script>
<script src="js/dockerManager.js"></script>
<script src="js/networkTest.js"></script>
<script src="js/app.js"></script>
<script>
// 在DOM加载完成后初始化登录表单
document.addEventListener('DOMContentLoaded', function() {
const loginForm = document.getElementById('loginForm');
if (loginForm) {
loginForm.addEventListener('submit', function(e) {
e.preventDefault();
auth.login();
});
}
const changePasswordForm = document.getElementById('changePasswordForm');
if (changePasswordForm) {
changePasswordForm.addEventListener('submit', function(e) {
e.preventDefault();
userCenter.changePassword();
});
}
// 设置超时自动显示登录框,避免一直显示"加载中..."
setTimeout(function() {
const loadingIndicator = document.getElementById('loadingIndicator');
if (loadingIndicator && loadingIndicator.style.display !== 'none') {
console.log('超时自动显示登录框');
if (core && typeof core.hideLoadingIndicator === 'function') {
core.hideLoadingIndicator();
}
const loginModal = document.getElementById('loginModal');
if (loginModal) {
loginModal.style.display = 'flex';
if (window.auth && typeof window.auth.refreshCaptcha === 'function') {
window.auth.refreshCaptcha();
}
}
}
}, 3000); // 3秒后如果仍在加载则显示登录框
// 确保saveConfig可用
window.saveConfig = window.app ? window.app.saveConfig : function(data) {
console.error('saveConfig未定义');
};
// 确保validateAndSaveConfig可用
window.validateAndSaveConfig = window.app ? window.app.validateAndSaveConfig : function(type) {
console.error('validateAndSaveConfig未定义');
};
});
</script>
</body>
</html>