nodejs / public /admin.html
clash-linux's picture
Upload 27 files
146bdba verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<title>Notion2API 管理面板</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
:root {
--primary-color: #0066cc;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--light-bg: #f8f9fa;
--border-color: #dee2e6;
--mobile-navbar-height: 56px;
}
body {
background-color: var(--light-bg);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
}
/* 移动端优化 */
@media (max-width: 768px) {
body {
padding-bottom: 60px; /* 为底部操作栏留出空间 */
}
.container {
padding-left: 12px;
padding-right: 12px;
}
/* 移动端导航栏优化 */
.navbar {
padding: 0.5rem 0;
position: sticky;
top: 0;
z-index: 1030;
}
.navbar-brand {
font-size: 1.1rem;
}
.navbar-brand i {
display: none; /* 移动端隐藏图标 */
}
/* 用户信息优化 */
.user-info {
gap: 0.5rem !important;
}
.user-info .avatar {
width: 28px !important;
height: 28px !important;
font-size: 0.875rem;
}
.user-info span {
font-size: 0.875rem;
}
/* 统计卡片移动端优化 */
.stats-card {
padding: 1rem !important;
margin-bottom: 0.75rem;
}
.stats-card h3 {
font-size: 1.5rem !important;
}
.stats-card p {
font-size: 0.875rem;
}
/* 表格移动端优化 */
.table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
margin: 0 -12px;
padding: 0 12px;
}
.table {
font-size: 0.875rem;
white-space: nowrap;
}
.table td, .table th {
padding: 0.5rem;
}
/* 隐藏次要列 */
.mobile-hide {
display: none !important;
}
/* 操作按钮优化 */
.action-buttons {
gap: 0.25rem !important;
}
.btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
/* 卡片移动端优化 */
.card {
margin-bottom: 1rem;
border-radius: 8px;
}
.card-header {
padding: 0.75rem 1rem;
font-size: 0.9rem;
}
.card-body {
padding: 1rem;
}
/* 模态框移动端优化 */
.modal-dialog {
margin: 0.5rem;
max-width: calc(100% - 1rem);
}
.modal-content {
border-radius: 12px;
}
/* 表单移动端优化 */
.form-control, .form-select {
font-size: 16px; /* 防止iOS缩放 */
padding: 0.75rem;
}
/* 底部浮动操作栏 */
.mobile-action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
border-top: 1px solid var(--border-color);
padding: 0.75rem;
display: flex;
gap: 0.5rem;
z-index: 1020;
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
}
.mobile-action-bar .btn {
flex: 1;
font-size: 0.875rem;
}
/* 空状态优化 */
.empty-state {
padding: 2rem 1rem;
}
.empty-state i {
font-size: 2rem;
}
/* Toast移动端位置 */
.toast-container {
top: var(--mobile-navbar-height) !important;
right: 12px !important;
}
}
/* 通用样式 */
.navbar {
background-color: white !important;
box-shadow: 0 2px 4px rgba(0,0,0,.1);
}
.navbar-brand {
font-weight: 600;
color: var(--primary-color) !important;
}
.main-container {
margin-top: 2rem;
}
.card {
border: none;
box-shadow: 0 2px 8px rgba(0,0,0,.08);
border-radius: 12px;
margin-bottom: 1.5rem;
}
.card-header {
background-color: white;
border-bottom: 1px solid var(--border-color);
padding: 1.25rem;
font-weight: 600;
}
.table {
margin-bottom: 0;
}
.table th {
border-bottom: 2px solid var(--border-color);
font-weight: 600;
color: var(--secondary-color);
text-transform: uppercase;
font-size: 0.875rem;
letter-spacing: 0.5px;
}
.badge {
padding: 0.375rem 0.75rem;
font-weight: 500;
}
.btn {
border-radius: 8px;
padding: 0.5rem 1rem;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,102,204,.25);
}
.btn-sm {
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
}
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0.5rem;
}
.status-active {
background-color: var(--success-color);
animation: pulse 2s infinite;
}
.status-inactive {
background-color: var(--danger-color);
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(40, 167, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0);
}
}
.modal-header {
border-bottom: 1px solid var(--border-color);
background-color: var(--light-bg);
}
.form-label {
font-weight: 500;
color: var(--secondary-color);
margin-bottom: 0.5rem;
}
.form-control, .form-select {
border-radius: 8px;
border: 1px solid var(--border-color);
padding: 0.625rem 0.875rem;
}
.form-control:focus, .form-select:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.2rem rgba(0, 102, 204, 0.25);
}
.alert {
border-radius: 8px;
border: none;
}
.text-muted {
color: #8492a6 !important;
}
.empty-state {
text-align: center;
padding: 3rem;
color: var(--secondary-color);
}
.empty-state i {
font-size: 3rem;
color: var(--border-color);
margin-bottom: 1rem;
}
.stats-card {
background: linear-gradient(135deg, var(--primary-color) 0%, #0056b3 100%);
color: white;
border: none;
border-radius: 12px;
padding: 1.5rem;
}
.stats-card h3 {
margin-bottom: 0.5rem;
font-size: 2rem;
font-weight: 700;
}
.stats-card p {
margin-bottom: 0;
opacity: 0.9;
}
.loading-spinner {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
}
.loading-spinner.active {
display: block;
}
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 1050;
}
.cookie-item {
transition: background-color 0.2s;
}
.cookie-item:hover {
background-color: var(--light-bg);
}
.action-buttons {
display: flex;
gap: 0.5rem;
}
.thread-id-input {
max-width: 200px;
}
/* 登录页面样式 */
.login-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 1rem;
}
.login-card {
width: 100%;
max-width: 400px;
padding: 2rem;
background: white;
border-radius: 16px;
box-shadow: 0 10px 25px rgba(0,0,0,.1);
}
.login-card .logo {
text-align: center;
margin-bottom: 2rem;
}
.login-card .logo i {
font-size: 3rem;
color: var(--primary-color);
}
.login-card h2 {
text-align: center;
margin-bottom: 1.5rem;
color: #333;
font-weight: 600;
}
.login-error {
display: none;
margin-bottom: 1rem;
}
#mainContent {
display: none;
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
}
.user-info .avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
}
/* 表格响应式滚动 */
.table-responsive {
-webkit-overflow-scrolling: touch;
}
/* 移动端触摸优化 */
@media (hover: none) {
.btn:hover {
transform: none;
box-shadow: none;
}
.cookie-item:hover {
background-color: transparent;
}
}
/* 桌面端隐藏移动操作栏 */
@media (min-width: 769px) {
.mobile-action-bar {
display: none !important;
}
.mobile-only {
display: none !important;
}
}
/* 改进的响应式断点 */
@media (min-width: 576px) and (max-width: 768px) {
.col-sm-6 {
flex: 0 0 50%;
max-width: 50%;
}
}
</style>
</head>
<body>
<!-- 登录页面 -->
<div id="loginContainer" class="login-container">
<div class="login-card">
<div class="logo">
<i class="bi bi-cloud-arrow-up-fill"></i>
</div>
<h2>Notion2API 管理登录</h2>
<div class="alert alert-danger login-error" id="loginError" role="alert">
<i class="bi bi-exclamation-circle me-2"></i>
<span id="loginErrorText">用户名或密码错误</span>
</div>
<form id="loginForm">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input type="text" class="form-control" id="username" required
placeholder="请输入用户名" autocomplete="username">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" class="form-control" id="password" required
placeholder="请输入密码" autocomplete="current-password">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="remember">
<label class="form-check-label" for="remember">
记住我
</label>
</div>
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-box-arrow-in-right me-2"></i>
登录
</button>
</form>
<div class="text-center mt-3 text-muted">
<small>默认用户名: admin</small>
</div>
</div>
</div>
<!-- 主内容区域 -->
<div id="mainContent">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light">
<div class="container">
<a class="navbar-brand" href="#">
<i class="bi bi-cloud-arrow-up-fill me-2"></i>
<span class="d-none d-sm-inline">Notion2API 管理面板</span>
<span class="d-inline d-sm-none">Notion2API</span>
</a>
<div class="ms-auto d-flex align-items-center">
<div class="user-info me-3">
<div class="avatar">
<span id="userAvatar">A</span>
</div>
<span class="text-muted d-none d-sm-inline" id="currentUser">admin</span>
</div>
<button class="btn btn-outline-secondary btn-sm" onclick="logout()">
<i class="bi bi-box-arrow-right me-1 d-none d-sm-inline"></i>
<span class="d-none d-sm-inline">退出</span>
<span class="d-inline d-sm-none">退出</span>
</button>
</div>
</div>
</nav>
<!-- 主容器 -->
<div class="container main-container">
<!-- 统计信息 -->
<div class="row mb-4">
<div class="col-12 col-sm-6 col-md-4 mb-3 mb-md-0">
<div class="card stats-card">
<div class="card-body">
<h3 id="totalCookies">0</h3>
<p>总Cookie数</p>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 mb-3 mb-md-0">
<div class="card stats-card">
<div class="card-body">
<h3 id="activeCookies">0</h3>
<p>有效Cookie数</p>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4">
<div class="card stats-card">
<div class="card-body">
<h3 id="threadIdCount">0</h3>
<p>已配置ThreadID</p>
</div>
</div>
</div>
</div>
<!-- Cookie管理 -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>
<i class="bi bi-key-fill me-2"></i>
Cookie 管理
</span>
<div class="d-none d-md-block">
<button class="btn btn-success btn-sm me-2" onclick="refreshCookies()">
<i class="bi bi-arrow-clockwise me-1"></i>
<span class="d-none d-sm-inline">刷新状态</span>
<span class="d-inline d-sm-none">刷新</span>
</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addCookieModal">
<i class="bi bi-plus-circle me-1"></i>
<span class="d-none d-sm-inline">添加Cookie</span>
<span class="d-inline d-sm-none">添加</span>
</button>
</div>
</div>
<div class="card-body p-0 p-md-3">
<div class="table-container">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th width="40">
<i class="bi bi-toggle-on" title="启用/禁用"></i>
</th>
<th width="50">#</th>
<th>用户ID</th>
<th class="mobile-hide">空间ID</th>
<th class="mobile-hide">Cookie预览</th>
<th width="120">状态</th>
<th class="mobile-hide" width="150">最后使用</th>
<th class="mobile-hide" width="150">Thread ID</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody id="cookieTableBody">
<!-- Cookie列表将通过JavaScript动态加载 -->
</tbody>
</table>
</div>
</div>
<div id="emptyCookieState" class="empty-state" style="display: none;">
<i class="bi bi-inbox d-block"></i>
<p>暂无Cookie数据</p>
<button class="btn btn-primary btn-sm mt-2" data-bs-toggle="modal" data-bs-target="#addCookieModal">
添加第一个Cookie
</button>
</div>
</div>
</div>
<!-- 操作日志(桌面端显示) -->
<div class="card d-none d-md-block">
<div class="card-header">
<i class="bi bi-journal-text me-2"></i>
操作日志
</div>
<div class="card-body">
<div id="logContainer" style="max-height: 300px; overflow-y: auto;">
<div class="text-muted text-center py-3">暂无操作日志</div>
</div>
</div>
</div>
</div>
<!-- 移动端底部操作栏 -->
<div class="mobile-action-bar d-md-none">
<button class="btn btn-success btn-sm" onclick="refreshCookies()">
<i class="bi bi-arrow-clockwise"></i>
刷新
</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addCookieModal">
<i class="bi bi-plus-circle"></i>
添加
</button>
</div>
<!-- 添加Cookie模态框 -->
<div class="modal fade" id="addCookieModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="bi bi-plus-circle me-2"></i>
添加新Cookie
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addCookieForm">
<div class="mb-3">
<label class="form-label">Cookie内容</label>
<textarea class="form-control" id="cookieContent" rows="4"
placeholder="请输入Cookie内容..." required></textarea>
<small class="text-muted">支持单个Cookie或使用 | 分隔的多个Cookie</small>
</div>
<div class="mb-3">
<label class="form-label">Thread ID(可选)</label>
<input type="text" class="form-control" id="cookieThreadId"
placeholder="如需指定Thread ID,请在此输入">
<small class="text-muted">Thread ID需要手动从Notion获取,系统不会自动创建</small>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="addCookie()">
<i class="bi bi-check-circle me-1"></i>
添加
</button>
</div>
</div>
</div>
</div>
<!-- 编辑ThreadID模态框 -->
<div class="modal fade" id="editThreadIdModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="bi bi-pencil-square me-2"></i>
编辑Thread ID
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="editThreadIdForm">
<input type="hidden" id="editCookieIndex">
<div class="mb-3">
<label class="form-label">用户ID</label>
<input type="text" class="form-control" id="editUserId" readonly>
</div>
<div class="mb-3">
<label class="form-label">Thread ID</label>
<input type="text" class="form-control" id="editThreadId"
placeholder="输入Thread ID">
<small class="text-muted">留空表示不使用特定的Thread ID</small>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="saveThreadId()">
<i class="bi bi-check-circle me-1"></i>
保存
</button>
</div>
</div>
</div>
</div>
<!-- 加载动画 -->
<div class="loading-spinner">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
<!-- Toast容器 -->
<div class="toast-container"></div>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="/admin.js"></script>
</div>
</body>
</html>