first commit
This commit is contained in:
commit
40a78f0032
77 files changed
+102842
No files matched your search
@@ -0,0 +1,69 @@
|
||||
/* 关于页 - 支持作者卡片 */
|
||||
|
||||
.about-donate-card {
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 2rem 1.5rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.about-donate-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 1.25rem;
|
||||
}
|
||||
|
||||
.about-donate-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 2rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.about-donate-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.about-donate-img {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
max-height: 360px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
border-radius: 10px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.about-donate-sub {
|
||||
font-size: 0.82rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 1rem 0 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.about-donate-card {
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.about-donate-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.about-donate-item {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.about-donate-img {
|
||||
max-width: 300px;
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,241 @@
|
||||
/* ================================
|
||||
管理员功能样式 - 用户管理和数据管理
|
||||
================================ */
|
||||
|
||||
/* 用户管理样式 */
|
||||
.user-card {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.user-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.user-card .card-body {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.user-card .badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
/* 数据管理样式 */
|
||||
.table-container {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.table th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: var(--dark-color);
|
||||
color: white;
|
||||
z-index: 10;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background-color: rgba(79, 70, 229, 0.05);
|
||||
}
|
||||
|
||||
/* 数据统计卡片 */
|
||||
.data-stats {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.data-stats .card-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
/* 表格状态样式 */
|
||||
#loadingTable,
|
||||
#noTableSelected,
|
||||
#noTableData {
|
||||
padding: 3rem 1rem;
|
||||
}
|
||||
|
||||
#loadingTable .spinner-border {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-width: 0.3em;
|
||||
}
|
||||
|
||||
#noTableSelected i,
|
||||
#noTableData i {
|
||||
font-size: 4rem;
|
||||
color: var(--secondary-color);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 按钮组样式 */
|
||||
.btn-group .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-group .btn:first-child {
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-group .btn:last-child {
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
||||
/* 表格选择器样式 */
|
||||
#tableSelect {
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
#tableSelect:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 0.2rem rgba(79, 70, 229, 0.25);
|
||||
}
|
||||
|
||||
/* 记录计数样式 */
|
||||
#recordCount {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.user-card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
border-radius: 6px !important;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载状态动画 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.user-card,
|
||||
.table-container {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* 表格滚动条样式 */
|
||||
.table-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.table-container::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-container::-webkit-scrollbar-thumb {
|
||||
background: var(--secondary-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-container::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 文本截断样式 */
|
||||
.text-truncate-custom {
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* 状态徽章样式 */
|
||||
.status-badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* 工具提示样式增强 */
|
||||
[title] {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* 空状态图标样式 */
|
||||
.empty-state-icon {
|
||||
font-size: 4rem;
|
||||
color: var(--secondary-color);
|
||||
opacity: 0.3;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.action-buttons .btn {
|
||||
margin: 0 0.125rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.action-buttons .btn-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* 统计卡片渐变背景 */
|
||||
.stats-card-primary {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
}
|
||||
|
||||
.stats-card-info {
|
||||
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
|
||||
}
|
||||
|
||||
.stats-card-success {
|
||||
background: linear-gradient(135deg, var(--success-color) 0%, #0d9488 100%);
|
||||
}
|
||||
|
||||
.stats-card-warning {
|
||||
background: linear-gradient(135deg, var(--warning-color) 0%, #d97706 100%);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@import url('variables.css');
|
||||
@import url('layout.css');
|
||||
@import url('dashboard.css');
|
||||
@import url('logs.css');
|
||||
@import url('keywords.css');
|
||||
@import url('accounts.css');
|
||||
@import url('items.css');
|
||||
@import url('notifications.css');
|
||||
@import url('components.css');
|
||||
@import url('about.css');
|
||||
@import url('admin.css');
|
||||
|
||||
|
||||
@@ -0,0 +1,848 @@
|
||||
/* ================================
|
||||
通用卡片样式 - 适用于所有菜单的卡片
|
||||
================================ */
|
||||
|
||||
/* 旋转动画 */
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 16px 16px 0 0 !important;
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, var(--success-color), #059669);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, var(--danger-color), #dc2626);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
border: 2px solid var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-success {
|
||||
border: 2px solid var(--success-color);
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.btn-outline-success:hover {
|
||||
background: var(--success-color);
|
||||
border-color: var(--success-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-info {
|
||||
border: 2px solid #0dcaf0;
|
||||
color: #0dcaf0;
|
||||
}
|
||||
|
||||
.btn-outline-info:hover {
|
||||
background: #0dcaf0;
|
||||
border-color: #0dcaf0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-danger {
|
||||
border: 2px solid var(--danger-color);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.btn-outline-danger:hover {
|
||||
background: var(--danger-color);
|
||||
border-color: var(--danger-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
通用表格样式 - 适用于所有菜单的表格
|
||||
================================ */
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.table th {
|
||||
border-top: none;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
font-weight: 600;
|
||||
color: var(--dark-color);
|
||||
background: rgba(79, 70, 229, 0.05);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: rgba(79, 70, 229, 0.02);
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-weight: 500;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.cookie-value {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
|
||||
font-size: 0.85rem;
|
||||
background: #f8fafc;
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e5e7eb;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(248, 250, 252, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .loading {
|
||||
background-color: rgba(15, 23, 42, 0.45);
|
||||
}
|
||||
|
||||
.keyword-editor {
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.cookie-id {
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem 2rem;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--border-color);
|
||||
transition: all 0.3s ease;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
color: var(--dark-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
通用模态框样式 - 适用于所有菜单的模态框
|
||||
================================ */
|
||||
.modal-content {
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.toast {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.btn-group .btn:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
AI回复配置 - 全量界面视觉增强
|
||||
================================ */
|
||||
#aiReplyConfigModal .modal-dialog {
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-title-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-subtitle {
|
||||
font-size: 0.82rem;
|
||||
color: rgba(255, 255, 255, 0.84);
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-modal-intro {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
padding: 12px 14px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(79, 70, 229, 0.16);
|
||||
background: linear-gradient(120deg, rgba(99, 102, 241, 0.1), rgba(6, 182, 212, 0.1));
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-intro-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 8px;
|
||||
background: rgba(79, 70, 229, 0.16);
|
||||
color: var(--primary-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-intro-title {
|
||||
font-weight: 700;
|
||||
color: var(--dark-color);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-intro-text {
|
||||
font-size: 0.86rem;
|
||||
color: var(--secondary-color);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-section-card {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-section-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-section-desc {
|
||||
font-size: 0.76rem;
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-section-tag {
|
||||
font-size: 0.68rem;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-basic-card .card-body {
|
||||
background:
|
||||
radial-gradient(circle at right top, rgba(79, 70, 229, 0.09), transparent 45%),
|
||||
radial-gradient(circle at left bottom, rgba(6, 182, 212, 0.09), transparent 42%);
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-basic-overview {
|
||||
display: grid;
|
||||
grid-template-columns: 1.25fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-account-panel,
|
||||
#aiReplyConfigModal .ai-enable-panel,
|
||||
#aiReplyConfigModal .ai-config-block,
|
||||
#aiReplyConfigModal .ai-config-grid {
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(79, 70, 229, 0.14);
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-account-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-account-field i {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-account-field .form-control {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-enable-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-enable-switch {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-enable-switch .form-check-input {
|
||||
width: 2.7rem;
|
||||
height: 1.4rem;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-config-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-preset-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-preset-controls .form-select {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-preset-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-config-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-config-item {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-config-item-wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-endpoint-preview {
|
||||
margin-top: 8px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px dashed rgba(16, 185, 129, 0.45);
|
||||
background: rgba(16, 185, 129, 0.07);
|
||||
color: #047857;
|
||||
font-family: 'SFMono-Regular', Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.4;
|
||||
word-break: break-all;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-bargain-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-metric-card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(79, 70, 229, 0.14);
|
||||
background: rgba(255, 255, 255, 0.76);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-tip {
|
||||
border-radius: 12px;
|
||||
border: 1px dashed rgba(14, 165, 233, 0.42);
|
||||
background: rgba(14, 165, 233, 0.08);
|
||||
color: #0369a1;
|
||||
font-size: 0.86rem;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(79, 70, 229, 0.14);
|
||||
background: rgba(255, 255, 255, 0.76);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-card-wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-dot-price {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-dot-tech {
|
||||
background: #06b6d4;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-dot-default {
|
||||
background: #6366f1;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-prompt-textarea {
|
||||
height: 132px;
|
||||
min-height: 132px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-test-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-test-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-test-btn {
|
||||
min-width: 124px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-test-result .alert {
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(59, 130, 246, 0.28);
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-footer-note {
|
||||
font-size: 0.82rem;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-footer-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#aiReplyConfigModal .ai-reply-header {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-reply-subtitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-modal-intro {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-bargain-grid,
|
||||
#aiReplyConfigModal .ai-prompt-grid,
|
||||
#aiReplyConfigModal .ai-test-grid,
|
||||
#aiReplyConfigModal .ai-basic-overview,
|
||||
#aiReplyConfigModal .ai-config-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-preset-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-preset-actions {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-test-actions,
|
||||
#aiReplyConfigModal .ai-reply-footer {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#aiReplyConfigModal .ai-footer-actions {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
margin-top: 1rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.cookie-value {
|
||||
font-size: 0.75rem;
|
||||
max-height: 80px;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
margin-right: 0;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
/* 移动端商品表格优化 */
|
||||
#itemsTableBody .btn-group {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#itemsTableBody .btn-group .btn {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================
|
||||
API参数提示样式
|
||||
================================ */
|
||||
.param-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.param-name {
|
||||
background-color: #f8f9fa;
|
||||
color: #495057;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
border: 1px solid #dee2e6;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 120px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.param-name:hover {
|
||||
background-color: #e9ecef;
|
||||
border-color: #adb5bd;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
#postParamsHelp,
|
||||
#editPostParamsHelp {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#postParamsHelp .alert,
|
||||
#editPostParamsHelp .alert {
|
||||
margin-bottom: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 参数项点击效果 */
|
||||
.param-name:active {
|
||||
transform: translateY(0);
|
||||
background-color: #dee2e6;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
API参数提示样式
|
||||
================================ */
|
||||
.param-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.param-name {
|
||||
background-color: #f8f9fa;
|
||||
color: #495057;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
border: 1px solid #dee2e6;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 120px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.param-name:hover {
|
||||
background-color: #e9ecef;
|
||||
border-color: #adb5bd;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
#postParamsHelp,
|
||||
#editPostParamsHelp {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#postParamsHelp .alert,
|
||||
#editPostParamsHelp .alert {
|
||||
margin-bottom: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 参数项点击效果 */
|
||||
.param-name:active {
|
||||
transform: translateY(0);
|
||||
background-color: #dee2e6;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
菜单排序列表样式
|
||||
================================ */
|
||||
.menu-sort-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.menu-sort-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
cursor: default;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-sort-item:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
|
||||
.menu-sort-item.dragging {
|
||||
opacity: 0.5;
|
||||
background: #d1d5db;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.menu-sort-item.drag-over {
|
||||
border-color: var(--primary-color);
|
||||
background: rgba(79, 70, 229, 0.1);
|
||||
}
|
||||
|
||||
.menu-sort-item .drag-handle {
|
||||
cursor: grab;
|
||||
padding: 5px 10px 5px 0;
|
||||
color: #6b7280;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.menu-sort-item .drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.menu-sort-item .menu-icon {
|
||||
width: 24px;
|
||||
margin-right: 10px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.menu-sort-item .menu-name {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.menu-sort-item .menu-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.menu-sort-item .badge {
|
||||
margin-left: 8px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,52 @@
|
||||
/* ================================
|
||||
【商品管理菜单】表格样式
|
||||
================================ */
|
||||
#itemsTableBody .btn-group {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#itemsTableBody .btn-group .btn {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
#itemsTableBody .btn-group .btn:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
#itemsTableBody .btn-group .btn:last-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
#itemsTableBody .btn-group .btn i {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* 表格操作列样式 */
|
||||
.table td:last-child {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
/* 表格文本截断优化 */
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table td[title] {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* 商品表格特定优化 */
|
||||
#itemsTableBody td:nth-child(4),
|
||||
#itemsTableBody td:nth-child(5) {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -0,0 +1,940 @@
|
||||
/* 关键词管理现代化样式 */
|
||||
.keyword-container {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.keyword-header {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
color: white;
|
||||
padding: 1.5rem 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.keyword-header h3 {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.keyword-header .account-badge {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.keyword-input-area {
|
||||
padding: 2rem;
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.keyword-input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.keyword-input-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.keyword-btn-row {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-field label {
|
||||
position: absolute;
|
||||
top: -0.5rem;
|
||||
left: 0.75rem;
|
||||
background: #f8fafc;
|
||||
padding: 0 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: #6b7280;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.input-field input, .input-field select, .input-field textarea {
|
||||
width: 100%;
|
||||
padding: 1rem 0.75rem 0.75rem;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.input-field input:focus, .input-field select:focus, .input-field textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.input-field textarea {
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 多选下拉框样式 */
|
||||
.input-field select[multiple] {
|
||||
min-height: 120px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.input-field select[multiple] option {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-field select[multiple] option:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.input-field select[multiple] option:checked {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 1rem 1.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.add-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
【自动回复菜单】相关样式
|
||||
================================ */
|
||||
.keywords-list {
|
||||
padding: 1.5rem 2rem 2rem;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.keyword-item {
|
||||
background: white;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.keyword-item:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.keyword-item-header {
|
||||
padding: 1rem 1.5rem;
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.keyword-tag {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.keyword-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
background: #f3f4f6;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.edit-btn:hover {
|
||||
background: #e5e7eb;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.edit-btn-disabled {
|
||||
background: #f9fafb !important;
|
||||
color: #9ca3af !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.edit-btn-disabled:hover {
|
||||
background: #f9fafb !important;
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #fef2f2;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #fee2e2;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.keyword-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.reply-text {
|
||||
color: #374151;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
margin: 0 0 2rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.quick-add-btn {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.quick-add-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
/* 账号选择器现代化 */
|
||||
.account-selector {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.selector-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.selector-title {
|
||||
margin: 0;
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.selector-subtitle {
|
||||
margin: 0;
|
||||
color: #6b7280;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.account-select-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.account-select {
|
||||
width: 100%;
|
||||
padding: 1rem 1.25rem;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.account-select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
【自动回复菜单】图片关键词样式
|
||||
================================ */
|
||||
.btn-image {
|
||||
background: linear-gradient(135deg, #28a745, #20c997) !important;
|
||||
border: none !important;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.btn-image:hover {
|
||||
background: linear-gradient(135deg, #218838, #1ea085) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.keyword-input-group .add-btn {
|
||||
white-space: nowrap;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
padding: 10px;
|
||||
border: 2px dashed #ddd;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.keyword-type-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.keyword-type-text {
|
||||
background-color: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.keyword-type-image {
|
||||
background-color: #f3e5f5;
|
||||
color: #7b1fa2;
|
||||
}
|
||||
|
||||
/* 关键词列表中的图片预览 */
|
||||
.keyword-image-preview {
|
||||
max-width: 60px;
|
||||
max-height: 60px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.keyword-image-preview:hover {
|
||||
border-color: #007bff;
|
||||
transform: scale(1.05);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
【关键词分组显示】新样式
|
||||
================================ */
|
||||
.keyword-group-item {
|
||||
background: white;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1.5rem;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.keyword-group-item:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
.keyword-group-header {
|
||||
padding: 1rem 1.5rem;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.keyword-group-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.keyword-count-badge {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.keyword-group-reply {
|
||||
padding: 1rem 1.5rem;
|
||||
background: #fefce8;
|
||||
border-bottom: 1px solid #fef08a;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.keyword-group-reply strong {
|
||||
color: #854d0e;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.reply-text-content {
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.keyword-group-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.keyword-section, .item-section {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.keyword-section:last-child, .item-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #6b7280;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.chips-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.keyword-chip, .item-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: linear-gradient(135deg, #ede9fe 0%, #ddd6fe 100%);
|
||||
color: #5b21b6;
|
||||
border-radius: 20px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.keyword-chip:hover, .item-chip:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(91, 33, 182, 0.2);
|
||||
}
|
||||
|
||||
.item-chip {
|
||||
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
|
||||
color: #1e40af;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.item-chip:hover {
|
||||
box-shadow: 0 4px 8px rgba(30, 64, 175, 0.2);
|
||||
}
|
||||
|
||||
.chip-remove-btn {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.chip-remove-btn:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.chip-remove-btn i {
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 回复内容编辑按钮 */
|
||||
.reply-edit-btn {
|
||||
background: none;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 6px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin-left: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.reply-edit-btn:hover {
|
||||
background: #f3f4f6;
|
||||
color: #374151;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.reply-edit-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.reply-edit-textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #667eea;
|
||||
border-radius: 8px;
|
||||
font-size: 0.875rem;
|
||||
font-family: inherit;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
min-height: 60px;
|
||||
background: white;
|
||||
color: #374151;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.reply-edit-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
.reply-edit-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.reply-save-btn, .reply-cancel-btn {
|
||||
padding: 0.375rem 0.75rem;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.reply-save-btn {
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.reply-save-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.reply-cancel-btn {
|
||||
background: #f3f4f6;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.reply-cancel-btn:hover {
|
||||
background: #e5e7eb;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
暗色模式适配 - 关键词管理
|
||||
================================ */
|
||||
[data-theme="dark"] .keyword-container {
|
||||
background: var(--card-bg);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-input-area {
|
||||
background: var(--bg-secondary);
|
||||
border-bottom-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field label {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field input,
|
||||
[data-theme="dark"] .input-field select,
|
||||
[data-theme="dark"] .input-field textarea {
|
||||
background: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field input:focus,
|
||||
[data-theme="dark"] .input-field select:focus,
|
||||
[data-theme="dark"] .input-field textarea:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field input::placeholder,
|
||||
[data-theme="dark"] .input-field textarea::placeholder {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field select[multiple] option {
|
||||
background: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field select[multiple] option:hover {
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field select[multiple] option:checked {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 暗色 - 关键词列表 */
|
||||
[data-theme="dark"] .keyword-item {
|
||||
background: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-item-header {
|
||||
background: var(--bg-secondary);
|
||||
border-bottom-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-content {
|
||||
background: var(--card-bg);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-text {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色 - 关键词分组 */
|
||||
[data-theme="dark"] .keyword-group-item {
|
||||
background: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-group-header {
|
||||
background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--input-bg) 100%);
|
||||
border-bottom-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-group-title {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-group-reply {
|
||||
background: rgba(133, 77, 14, 0.15);
|
||||
border-bottom-color: rgba(254, 240, 138, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-group-reply strong {
|
||||
color: #fcd34d;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-text-content {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-group-content {
|
||||
background: var(--card-bg);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .section-title {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 暗色 - Chips */
|
||||
[data-theme="dark"] .keyword-chip {
|
||||
background: linear-gradient(135deg, rgba(139, 92, 246, 0.25) 0%, rgba(124, 58, 237, 0.25) 100%);
|
||||
color: #c4b5fd;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-chip:hover {
|
||||
box-shadow: 0 4px 8px rgba(139, 92, 246, 0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .item-chip {
|
||||
background: linear-gradient(135deg, rgba(59, 130, 246, 0.25) 0%, rgba(37, 99, 235, 0.25) 100%);
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .item-chip:hover {
|
||||
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .chip-remove-btn {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .chip-remove-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
/* 暗色 - 操作按钮 */
|
||||
[data-theme="dark"] .edit-btn {
|
||||
background: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .edit-btn:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .delete-btn {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .delete-btn:hover {
|
||||
background: rgba(239, 68, 68, 0.25);
|
||||
color: #f87171;
|
||||
}
|
||||
|
||||
/* 暗色 - 空状态 */
|
||||
[data-theme="dark"] .empty-state {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .empty-state h3 {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色 - 账号选择器 */
|
||||
[data-theme="dark"] .account-selector {
|
||||
background: var(--card-bg);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .selector-title {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .selector-subtitle {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .account-select {
|
||||
background: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .account-select:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
/* 暗色 - 图片预览 */
|
||||
[data-theme="dark"] .preview-container {
|
||||
background-color: var(--bg-secondary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-image-preview {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-image-preview:hover {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 暗色 - 类型徽章 */
|
||||
[data-theme="dark"] .keyword-type-text {
|
||||
background-color: rgba(25, 118, 210, 0.2);
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-type-image {
|
||||
background-color: rgba(123, 31, 162, 0.2);
|
||||
color: #d8b4fe;
|
||||
}
|
||||
|
||||
/* 暗色 - 回复编辑 */
|
||||
[data-theme="dark"] .reply-edit-btn {
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-edit-btn:hover {
|
||||
background: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
border-color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-edit-textarea {
|
||||
background: var(--input-bg);
|
||||
border-color: #667eea;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-edit-textarea:focus {
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-cancel-btn {
|
||||
background: var(--input-bg);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .reply-cancel-btn:hover {
|
||||
background: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色 - Modal 内关键词样式 */
|
||||
[data-theme="dark"] .modal .input-field input,
|
||||
[data-theme="dark"] .modal .input-field textarea {
|
||||
background: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.keyword-input-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.keyword-btn-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.keyword-item-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.keyword-actions {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.keyword-input-group {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.keyword-input-group .add-btn {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.keyword-group-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.chips-container {
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.keyword-chip, .item-chip {
|
||||
font-size: 0.8125rem;
|
||||
padding: 0.375rem 0.625rem;
|
||||
}
|
||||
|
||||
.item-chip {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/* ================================
|
||||
通用布局 - 侧边栏和导航
|
||||
================================ */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 2px 0 10px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1.5rem 1rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 暗色模式切换按钮 */
|
||||
.dark-mode-toggle {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border: none;
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dark-mode-toggle:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.dark-mode-toggle i {
|
||||
font-size: 1rem;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.dark-mode-toggle:hover i {
|
||||
transform: rotate(15deg);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
padding: 1rem 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 侧边栏滚动条样式 */
|
||||
.sidebar-nav::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.sidebar-nav::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sidebar-nav::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-nav::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Firefox滚动条样式 */
|
||||
.sidebar-nav {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.3) rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1.5rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-left-color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-left-color: white;
|
||||
}
|
||||
|
||||
.nav-link i {
|
||||
margin-right: 0.75rem;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-divider {
|
||||
padding: 0.5rem 1rem;
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-divider small {
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
min-height: 100vh;
|
||||
background: var(--bg-color);
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
background: white;
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.content-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.sidebar.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
z-index: 1001;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* 侧边栏折叠样式 */
|
||||
.sidebar.collapsed {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-brand span,
|
||||
.sidebar.collapsed .nav-link span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-brand {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-brand img {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-link {
|
||||
justify-content: center;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-link i {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-divider {
|
||||
text-align: center;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-divider small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-collapse-btn {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-collapse-btn span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-collapse-btn i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 折叠状态下的主内容区域 */
|
||||
body.sidebar-collapsed .main-content {
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
/* 侧边栏底部折叠按钮 */
|
||||
.sidebar-footer {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar-collapse-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0.5rem 1rem;
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: rgba(255,255,255,0.8);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapse-btn:hover {
|
||||
background: rgba(255,255,255,0.2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidebar-collapse-btn i {
|
||||
margin-right: 0.5rem;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
/* 内容区域样式 */
|
||||
.content-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content-section.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 在线客服页面特殊样式 - 使用flex布局填满高度 */
|
||||
#online-im-section.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh);
|
||||
}
|
||||
|
||||
#online-im-section .content-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#online-im-section .content-body {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,875 @@
|
||||
/* ================================
|
||||
【日志管理菜单】相关样式
|
||||
================================ */
|
||||
.log-container {
|
||||
height: 70vh;
|
||||
overflow-y: auto;
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 0px;
|
||||
padding: 1px 0;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.log-entry.DEBUG {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.log-entry.INFO {
|
||||
color: #4ec9b0;
|
||||
}
|
||||
|
||||
.log-entry.WARNING {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
.log-entry.ERROR {
|
||||
color: #f48771;
|
||||
}
|
||||
|
||||
.log-entry.CRITICAL {
|
||||
color: #ff6b6b;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-timestamp {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.log-level {
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.log-source {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.log-message {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.log-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.log-container::-webkit-scrollbar-track {
|
||||
background: #2d2d30;
|
||||
}
|
||||
|
||||
.log-container::-webkit-scrollbar-thumb {
|
||||
background: #464647;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.log-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #5a5a5c;
|
||||
}
|
||||
|
||||
/* 日志过滤标签样式 */
|
||||
.filter-badge {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0.25rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 32px;
|
||||
padding: 0.45rem 0.8rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.filter-badge:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.filter-badge.active {
|
||||
box-shadow: 0 0 0 2px rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.toolbar-count-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 32px;
|
||||
padding: 0.5rem 0.875rem;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.risk-event-category-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 28px;
|
||||
padding: 0.38rem 0.68rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.74rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.risk-event-category-trigger {
|
||||
background: rgba(79, 70, 229, 0.1);
|
||||
color: var(--primary-color);
|
||||
border-color: rgba(79, 70, 229, 0.14);
|
||||
}
|
||||
|
||||
.risk-event-category-refresh {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: var(--success-color);
|
||||
border-color: rgba(16, 185, 129, 0.14);
|
||||
}
|
||||
|
||||
.risk-event-category-error {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: var(--danger-color);
|
||||
border-color: rgba(239, 68, 68, 0.14);
|
||||
}
|
||||
|
||||
.risk-event-category-neutral {
|
||||
background: rgba(100, 116, 139, 0.1);
|
||||
color: #475569;
|
||||
border-color: rgba(100, 116, 139, 0.14);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-event-category-trigger {
|
||||
background: rgba(99, 102, 241, 0.22);
|
||||
color: #c7d2fe;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-event-category-refresh {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: #a7f3d0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-event-category-error {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #fecaca;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-event-category-neutral {
|
||||
background: rgba(100, 116, 139, 0.22);
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.risk-log-toolbar-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.risk-tool-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(250, 251, 252, 0.96));
|
||||
box-shadow: 0 14px 30px -28px rgba(15, 23, 42, 0.25);
|
||||
padding: 0.82rem 0.9rem;
|
||||
}
|
||||
|
||||
.risk-tool-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--primary-light));
|
||||
opacity: 0.9;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.risk-slider-stats-panel,
|
||||
.risk-log-filter-panel {
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 250, 252, 0.96));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.risk-tool-card-header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 0.7rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.risk-tool-card-meta {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.7rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.risk-tool-card-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.52);
|
||||
}
|
||||
|
||||
.risk-tool-card-icon.primary,
|
||||
.risk-tool-card-icon.warning {
|
||||
background: rgba(79, 70, 229, 0.1);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.risk-tool-card-kicker {
|
||||
font-size: 0.64rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
.risk-tool-card h6 {
|
||||
font-size: 0.94rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.risk-tool-card p {
|
||||
font-size: 0.76rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.risk-tool-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.38rem 0.72rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
background: rgba(79, 70, 229, 0.05);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.risk-slider-range-group {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.2rem;
|
||||
padding: 0.18rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||
background: rgba(79, 70, 229, 0.04);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.risk-slider-range-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 29px;
|
||||
padding: 0.3rem 0.62rem;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
|
||||
.risk-slider-range-btn:hover {
|
||||
background: rgba(79, 70, 229, 0.08);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.risk-slider-range-btn.is-active {
|
||||
background: var(--primary-color);
|
||||
color: #fff;
|
||||
box-shadow: 0 6px 16px rgba(79, 70, 229, 0.18);
|
||||
}
|
||||
|
||||
.risk-slider-hero {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: 0.72rem;
|
||||
margin-bottom: 0.62rem;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.risk-slider-rate-label {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
.risk-slider-rate {
|
||||
font-size: clamp(1.72rem, 2.7vw, 2.15rem);
|
||||
line-height: 0.95;
|
||||
font-weight: 800;
|
||||
color: var(--primary-hover);
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.risk-slider-total {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 32px;
|
||||
padding: 0.38rem 0.68rem;
|
||||
border-radius: 12px;
|
||||
background: rgba(79, 70, 229, 0.05);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.risk-slider-stats-grid {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.risk-slider-stat-chip {
|
||||
border-radius: 12px;
|
||||
min-height: 72px;
|
||||
padding: 0.72rem 0.78rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.55rem;
|
||||
background: rgba(248, 250, 252, 0.95);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.risk-slider-stat-chip span {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.risk-slider-stat-chip strong {
|
||||
font-size: 1.18rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.risk-slider-stat-chip.success strong {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.risk-slider-stat-chip.danger strong {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.risk-slider-stats-foot {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: 0.6rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.risk-slider-foot-item {
|
||||
padding-top: 0.42rem;
|
||||
border-top: 1px dashed rgba(148, 163, 184, 0.34);
|
||||
}
|
||||
|
||||
.risk-slider-foot-item span {
|
||||
display: block;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.risk-slider-foot-item strong {
|
||||
font-size: 0.84rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.risk-filter-panel-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: 0.72rem;
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
gap: 0.58rem;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.risk-filter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
|
||||
grid-template-areas:
|
||||
"account account"
|
||||
"limit event";
|
||||
gap: 0.72rem 0.62rem;
|
||||
align-content: space-between;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.risk-filter-field,
|
||||
.risk-filter-action {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.risk-filter-field-account {
|
||||
grid-area: account;
|
||||
}
|
||||
|
||||
.risk-filter-field-limit {
|
||||
grid-area: limit;
|
||||
}
|
||||
|
||||
.risk-filter-field-event {
|
||||
grid-area: event;
|
||||
}
|
||||
|
||||
.risk-log-filter-panel .form-label {
|
||||
font-size: 0.64rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.risk-filter-field .form-select,
|
||||
.risk-filter-field .form-control {
|
||||
min-height: 38px;
|
||||
padding-top: 0.42rem;
|
||||
padding-bottom: 0.42rem;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
.risk-filter-action {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.risk-filter-action > div {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.risk-filter-refresh-btn {
|
||||
min-height: 38px;
|
||||
width: auto;
|
||||
min-width: 136px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.risk-filter-status-bar {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
padding-top: 0.42rem;
|
||||
border-top: 1px solid rgba(148, 163, 184, 0.18);
|
||||
}
|
||||
|
||||
.risk-filter-status-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.risk-log-filter-badges {
|
||||
gap: 0.45rem !important;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.risk-filter-status-refresh {
|
||||
min-width: 118px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge {
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
min-height: 30px;
|
||||
padding: 0.36rem 0.68rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
font-size: 0.74rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge.active {
|
||||
box-shadow: 0 10px 22px -18px rgba(15, 23, 42, 0.55);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-neutral {
|
||||
background: rgba(100, 116, 139, 0.08);
|
||||
color: #475569;
|
||||
border-color: rgba(100, 116, 139, 0.12);
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-neutral.active {
|
||||
background: #64748b;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-processing {
|
||||
background: rgba(245, 158, 11, 0.08);
|
||||
color: #b45309;
|
||||
border-color: rgba(245, 158, 11, 0.12);
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-processing.active {
|
||||
background: #f59e0b;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-success {
|
||||
background: rgba(16, 185, 129, 0.08);
|
||||
color: #0f9b76;
|
||||
border-color: rgba(16, 185, 129, 0.12);
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-success.active {
|
||||
background: #10b981;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-danger {
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
color: #dc2626;
|
||||
border-color: rgba(239, 68, 68, 0.12);
|
||||
}
|
||||
|
||||
.risk-log-filter-badges .filter-badge-danger.active {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.risk-log-table-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.risk-log-table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.risk-log-table th,
|
||||
.risk-log-table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.risk-log-table td {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.risk-log-col-time {
|
||||
width: 12%;
|
||||
}
|
||||
|
||||
.risk-log-col-account {
|
||||
width: 11%;
|
||||
}
|
||||
|
||||
.risk-log-col-category {
|
||||
width: 8%;
|
||||
}
|
||||
|
||||
.risk-log-col-scene {
|
||||
width: 8%;
|
||||
}
|
||||
|
||||
.risk-log-col-status {
|
||||
width: 6%;
|
||||
}
|
||||
|
||||
.risk-log-col-summary {
|
||||
width: 14%;
|
||||
}
|
||||
|
||||
.risk-log-col-outcome {
|
||||
width: 14%;
|
||||
}
|
||||
|
||||
.risk-log-col-duration {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.risk-log-col-session {
|
||||
width: 16%;
|
||||
}
|
||||
|
||||
.risk-log-col-action {
|
||||
width: 6%;
|
||||
}
|
||||
|
||||
.risk-log-summary-cell,
|
||||
.risk-log-outcome-cell {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.risk-log-summary-cell {
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.risk-log-cell-summary,
|
||||
.risk-log-cell-outcome {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.risk-log-cell-session {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.35;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.risk-log-outcome-cell details {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.risk-log-outcome-cell pre {
|
||||
max-width: 100%;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-tool-card {
|
||||
border-color: rgba(148, 163, 184, 0.12);
|
||||
background: linear-gradient(180deg, rgba(30, 41, 59, 0.95), rgba(15, 23, 42, 0.92));
|
||||
box-shadow: 0 18px 34px -30px rgba(2, 6, 23, 0.92);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-tool-card-icon.primary,
|
||||
[data-theme="dark"] .risk-tool-card-icon.warning {
|
||||
background: rgba(99, 102, 241, 0.22);
|
||||
color: #c7d2fe;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-tool-chip {
|
||||
background: rgba(99, 102, 241, 0.16);
|
||||
border-color: rgba(148, 163, 184, 0.14);
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-range-group {
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
border-color: rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-range-btn {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-range-btn:hover {
|
||||
background: rgba(129, 140, 248, 0.14);
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-range-btn.is-active {
|
||||
background: rgba(129, 140, 248, 0.92);
|
||||
color: #0f172a;
|
||||
box-shadow: 0 6px 16px rgba(99, 102, 241, 0.26);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-rate {
|
||||
color: #c7d2fe;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-total {
|
||||
background: rgba(99, 102, 241, 0.14);
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-stat-chip {
|
||||
background: rgba(15, 23, 42, 0.68);
|
||||
border-color: rgba(148, 163, 184, 0.12);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-slider-foot-item,
|
||||
[data-theme="dark"] .risk-filter-status-bar {
|
||||
border-top-color: rgba(148, 163, 184, 0.12);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-filter-field .form-select {
|
||||
background: rgba(15, 23, 42, 0.68);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-log-filter-badges .filter-badge-neutral {
|
||||
background: rgba(100, 116, 139, 0.22);
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-log-filter-badges .filter-badge-processing {
|
||||
background: rgba(245, 158, 11, 0.22);
|
||||
color: #fde68a;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-log-filter-badges .filter-badge-success {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: #a7f3d0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .risk-log-filter-badges .filter-badge-danger {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #fecaca;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.risk-log-table {
|
||||
min-width: 1080px;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
.risk-tool-card-header,
|
||||
.risk-slider-hero {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.risk-filter-status-controls {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.risk-filter-panel-body {
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
|
||||
.risk-filter-grid {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(100px, 0.78fr);
|
||||
grid-template-areas:
|
||||
"account account"
|
||||
"limit event";
|
||||
align-content: start;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.risk-filter-action {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.risk-filter-status-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.risk-filter-status-refresh {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.risk-tool-card {
|
||||
padding: 0.78rem;
|
||||
}
|
||||
|
||||
.risk-filter-grid,
|
||||
.risk-slider-stats-grid,
|
||||
.risk-slider-stats-foot {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.risk-filter-panel-body {
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
|
||||
.risk-filter-grid {
|
||||
grid-template-areas:
|
||||
"account"
|
||||
"limit"
|
||||
"event";
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.risk-filter-action {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 自动刷新指示器 */
|
||||
.auto-refresh-indicator {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* 日志行悬停效果 */
|
||||
.log-entry:hover {
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
/* 商品搜索相关样式 */
|
||||
.item-card {
|
||||
transition: transform 0.2s;
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.item-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.price {
|
||||
color: #e74c3c;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.seller-name {
|
||||
color: #6c757d;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.want-count {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.want-count .badge {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/* ================================
|
||||
【通知渠道菜单】卡片样式
|
||||
================================ */
|
||||
.channel-type-card {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
min-height: 180px; /* 设置最小高度,让卡片更紧凑 */
|
||||
}
|
||||
|
||||
.channel-type-card .card-body {
|
||||
padding: 1rem; /* 减少内边距 */
|
||||
}
|
||||
|
||||
.channel-type-card .channel-icon {
|
||||
transition: transform 0.3s ease;
|
||||
margin-bottom: 0.75rem !important; /* 减少图标下方间距 */
|
||||
}
|
||||
|
||||
.channel-type-card .channel-icon i {
|
||||
font-size: 2.2rem !important; /* 减小图标大小 */
|
||||
}
|
||||
|
||||
.channel-type-card .card-title {
|
||||
font-size: 1.1rem; /* 减小标题字体 */
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.channel-type-card .card-text {
|
||||
font-size: 0.85rem; /* 减小描述文字 */
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.channel-type-card .btn {
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.85rem; /* 减小按钮文字 */
|
||||
padding: 0.375rem 0.75rem; /* 减小按钮内边距 */
|
||||
}
|
||||
|
||||
.channel-type-card:hover {
|
||||
transform: translateY(-3px); /* 减少悬停位移 */
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||
border-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
.channel-type-card:hover .channel-icon {
|
||||
transform: scale(1.05); /* 减少悬停缩放 */
|
||||
}
|
||||
|
||||
.channel-type-card:hover .btn {
|
||||
transform: scale(1.02); /* 减少按钮悬停缩放 */
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.channel-type-card {
|
||||
margin-bottom: 10px;
|
||||
min-height: 160px; /* 移动端稍微减小高度 */
|
||||
}
|
||||
|
||||
.channel-type-card .card-body {
|
||||
padding: 0.75rem; /* 移动端减少内边距 */
|
||||
}
|
||||
|
||||
.channel-type-card .channel-icon i {
|
||||
font-size: 2rem !important; /* 移动端图标更小 */
|
||||
}
|
||||
|
||||
.channel-type-card .card-title {
|
||||
font-size: 1rem; /* 移动端标题更小 */
|
||||
}
|
||||
|
||||
.channel-type-card .card-text {
|
||||
font-size: 0.8rem; /* 移动端描述文字更小 */
|
||||
}
|
||||
}
|
||||
|
||||
/* 大屏幕优化 */
|
||||
@media (min-width: 1400px) {
|
||||
.channel-type-card {
|
||||
min-height: 200px; /* 大屏幕稍微增加高度 */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,550 @@
|
||||
/* ================================
|
||||
全局变量和基础样式
|
||||
================================ */
|
||||
:root {
|
||||
--primary-color: #4f46e5;
|
||||
--primary-hover: #4338ca;
|
||||
--primary-light: #6366f1;
|
||||
--secondary-color: #6b7280;
|
||||
--success-color: #10b981;
|
||||
--danger-color: #ef4444;
|
||||
--warning-color: #f59e0b;
|
||||
--light-color: #f9fafb;
|
||||
--dark-color: #1f2937;
|
||||
--border-color: #e5e7eb;
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
/* 暗色模式变量 */
|
||||
--bg-color: #f8fafc;
|
||||
--bg-secondary: #ffffff;
|
||||
--text-color: #1f2937;
|
||||
--text-secondary: #6b7280;
|
||||
--card-bg: #ffffff;
|
||||
--input-bg: #ffffff;
|
||||
--input-border: #d1d5db;
|
||||
--table-stripe: rgba(0, 0, 0, 0.02);
|
||||
--modal-bg: #ffffff;
|
||||
}
|
||||
|
||||
/* 暗色模式 */
|
||||
[data-theme="dark"] {
|
||||
--bg-color: #0f172a;
|
||||
--bg-secondary: #1e293b;
|
||||
--text-color: #f1f5f9;
|
||||
--text-secondary: #94a3b8;
|
||||
--card-bg: #1e293b;
|
||||
--input-bg: #334155;
|
||||
--input-border: #475569;
|
||||
--border-color: #334155;
|
||||
--light-color: #1e293b;
|
||||
--dark-color: #f1f5f9;
|
||||
--table-stripe: rgba(255, 255, 255, 0.02);
|
||||
--modal-bg: #1e293b;
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
/* 暗色模式下的通用样式覆盖 */
|
||||
[data-theme="dark"] .card {
|
||||
background: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .form-control,
|
||||
[data-theme="dark"] .form-select {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .form-control:focus,
|
||||
[data-theme="dark"] .form-select:focus {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--primary-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .form-control::placeholder {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .table {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .table > :not(caption) > * > * {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .table-striped > tbody > tr:nth-of-type(odd) > * {
|
||||
background-color: var(--table-stripe);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .modal-content {
|
||||
background-color: var(--modal-bg);
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .modal-header {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-account-panel,
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-enable-panel,
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-config-block,
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-config-grid,
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-metric-card,
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-prompt-card {
|
||||
background: rgba(30, 41, 59, 0.78);
|
||||
border-color: rgba(99, 102, 241, 0.32);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-modal-intro {
|
||||
background: linear-gradient(120deg, rgba(99, 102, 241, 0.22), rgba(6, 182, 212, 0.22));
|
||||
border-color: rgba(99, 102, 241, 0.38);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-intro-icon {
|
||||
background: rgba(99, 102, 241, 0.3);
|
||||
color: #c7d2fe;
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-intro-title {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-intro-text,
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-footer-note {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-section-tag {
|
||||
border-color: rgba(255, 255, 255, 0.35);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-basic-card .card-body {
|
||||
background:
|
||||
radial-gradient(circle at right top, rgba(99, 102, 241, 0.2), transparent 45%),
|
||||
radial-gradient(circle at left bottom, rgba(6, 182, 212, 0.2), transparent 42%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-account-field i {
|
||||
box-shadow: 0 6px 18px rgba(79, 70, 229, 0.35);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-endpoint-preview {
|
||||
border-color: rgba(16, 185, 129, 0.42);
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #6ee7b7;
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-prompt-tip {
|
||||
border-color: rgba(14, 165, 233, 0.4);
|
||||
background: rgba(14, 165, 233, 0.14);
|
||||
color: #7dd3fc;
|
||||
}
|
||||
|
||||
[data-theme="dark"] #aiReplyConfigModal .ai-test-result .alert {
|
||||
border-color: rgba(59, 130, 246, 0.4);
|
||||
background-color: rgba(30, 58, 138, 0.25);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .modal-footer {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-close {
|
||||
filter: invert(1) grayscale(100%) brightness(200%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .dropdown-menu {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .dropdown-item {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .dropdown-item:hover {
|
||||
background-color: var(--input-bg);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .list-group-item {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .badge.bg-light {
|
||||
background-color: var(--input-bg) !important;
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .text-muted {
|
||||
color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .border {
|
||||
border-color: var(--border-color) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] hr {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-white {
|
||||
background-color: var(--card-bg) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-light {
|
||||
background-color: var(--bg-secondary) !important;
|
||||
}
|
||||
|
||||
/* 暗色模式下的主内容区域 */
|
||||
[data-theme="dark"] .main-content {
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .content-header {
|
||||
background: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的统计卡片 */
|
||||
[data-theme="dark"] .stat-card {
|
||||
background: var(--card-bg);
|
||||
}
|
||||
|
||||
/* 暗色模式下的输入框组 */
|
||||
[data-theme="dark"] .input-group-text {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的表单标签 */
|
||||
[data-theme="dark"] .form-label,
|
||||
[data-theme="dark"] .form-check-label,
|
||||
[data-theme="dark"] .col-form-label {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的卡片标题和文字 */
|
||||
[data-theme="dark"] .card-title,
|
||||
[data-theme="dark"] .card-text,
|
||||
[data-theme="dark"] .card-header,
|
||||
[data-theme="dark"] .card-body {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的纯文本表单控件 */
|
||||
[data-theme="dark"] .form-control-plaintext {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的表格表头 */
|
||||
[data-theme="dark"] .table th {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的 Bootstrap 文字颜色类 */
|
||||
[data-theme="dark"] .text-dark {
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .text-body {
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
/* 暗色模式下的 alert 组件 */
|
||||
[data-theme="dark"] .alert {
|
||||
color: var(--text-color);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .alert-info {
|
||||
background-color: rgba(59, 130, 246, 0.2);
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .alert-warning {
|
||||
background-color: rgba(245, 158, 11, 0.2);
|
||||
border-color: rgba(245, 158, 11, 0.3);
|
||||
color: #fcd34d;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .alert-danger {
|
||||
background-color: rgba(239, 68, 68, 0.2);
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .alert-success {
|
||||
background-color: rgba(16, 185, 129, 0.2);
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
color: #6ee7b7;
|
||||
}
|
||||
|
||||
/* 暗色模式下的状态徽章 */
|
||||
[data-theme="dark"] .status-badge.enabled {
|
||||
background-color: rgba(16, 185, 129, 0.2);
|
||||
color: #6ee7b7;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .status-badge.disabled {
|
||||
background-color: rgba(239, 68, 68, 0.2);
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
/* 暗色模式下的空状态图标 */
|
||||
[data-theme="dark"] .empty-state i,
|
||||
[data-theme="dark"] .text-center i[style*="color: #ccc"] {
|
||||
color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
/* 暗色模式下的标题和段落 */
|
||||
[data-theme="dark"] h1, [data-theme="dark"] h2, [data-theme="dark"] h3,
|
||||
[data-theme="dark"] h4, [data-theme="dark"] h5, [data-theme="dark"] h6 {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] p {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的小字和次要文字 */
|
||||
[data-theme="dark"] small,
|
||||
[data-theme="dark"] .small,
|
||||
[data-theme="dark"] .text-secondary {
|
||||
color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
/* 暗色模式下的账号禁用状态 */
|
||||
[data-theme="dark"] .account-row.disabled {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 暗色模式下的 Cookie 值显示 */
|
||||
[data-theme="dark"] .cookie-value {
|
||||
background-color: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的选择器标题 */
|
||||
[data-theme="dark"] .selector-title,
|
||||
[data-theme="dark"] .selector-subtitle {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的关键词管理 */
|
||||
[data-theme="dark"] .keyword-container,
|
||||
[data-theme="dark"] .keyword-input-area {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .keyword-group-title,
|
||||
[data-theme="dark"] .section-title,
|
||||
[data-theme="dark"] .reply-text,
|
||||
[data-theme="dark"] .reply-text-content {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .empty-state,
|
||||
[data-theme="dark"] .empty-state h3 {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .edit-btn {
|
||||
background-color: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input-field label {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 内容区域的 nav-tabs 样式修复(覆盖侧边栏样式) */
|
||||
.content-body .nav-tabs .nav-link {
|
||||
color: #495057;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-top-left-radius: 0.375rem;
|
||||
border-top-right-radius: 0.375rem;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.content-body .nav-tabs .nav-link:hover {
|
||||
color: #0d6efd;
|
||||
border-color: #e9ecef #e9ecef #dee2e6;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.content-body .nav-tabs .nav-link.active {
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
border-color: #dee2e6 #dee2e6 #fff;
|
||||
}
|
||||
|
||||
/* 暗色模式下的 nav-tabs */
|
||||
[data-theme="dark"] .nav-tabs {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-tabs .nav-link,
|
||||
[data-theme="dark"] .content-body .nav-tabs .nav-link {
|
||||
color: var(--text-secondary);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-tabs .nav-link.active,
|
||||
[data-theme="dark"] .content-body .nav-tabs .nav-link.active {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-tabs .nav-link:hover,
|
||||
[data-theme="dark"] .content-body .nav-tabs .nav-link:hover {
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的 tab-content */
|
||||
[data-theme="dark"] .tab-content {
|
||||
border-color: var(--border-color) !important;
|
||||
background-color: var(--card-bg);
|
||||
}
|
||||
|
||||
/* 暗色模式下的模板预览区域 */
|
||||
[data-theme="dark"] .bg-light {
|
||||
background-color: var(--bg-color) !important;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的 accordion */
|
||||
[data-theme="dark"] .accordion-item {
|
||||
background-color: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .accordion-button {
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .accordion-button:not(.collapsed) {
|
||||
background-color: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .accordion-body {
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的账号选择器 */
|
||||
[data-theme="dark"] .account-selector {
|
||||
background-color: var(--card-bg);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .account-select {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .account-select:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .selector-title {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .selector-subtitle {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .account-select-wrapper select option {
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的表单帮助文字 */
|
||||
[data-theme="dark"] .form-text {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 暗色模式下的菜单管理列表 */
|
||||
[data-theme="dark"] .menu-sort-item {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .menu-sort-item:hover {
|
||||
background-color: var(--card-bg);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .menu-sort-item.dragging {
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .menu-sort-item .drag-handle {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .menu-sort-item .menu-name {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 暗色模式下的全局滚动条 */
|
||||
[data-theme="dark"] {
|
||||
scrollbar-color: #475569 #1e293b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar-track {
|
||||
background: #1e293b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar-thumb {
|
||||
background: #475569;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover {
|
||||
background: #64748b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar-corner {
|
||||
background: #1e293b;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.8 KiB |
+5032
File diff suppressed because it is too large.
Load diff
+20248
File diff suppressed because it is too large.
Load diff
+2078
File diff suppressed because it is too large.
Load diff
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v5.3.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slLine truncated
|
||||
//# sourceMappingURL=bootstrap.bundle.min.js.map
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
@charset "UTF-8";/*!
|
||||
* Bootstrap v5.3.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2023 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherLine truncated
|
||||
/*# sourceMappingURL=bootstrap.min.css.map */
|
||||
@@ -0,0 +1,961 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>登录 - 闲鱼管理系统</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<link rel="stylesheet" href="/static/lib/bootstrap/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/static/lib/bootstrap-icons/bootstrap-icons.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #0d6efd;
|
||||
--secondary-color: #6c757d;
|
||||
--success-color: #198754;
|
||||
--danger-color: #dc3545;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-header h2 {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
margin: 10px 0 0 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.login-body {
|
||||
padding: 40px 30px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 10px;
|
||||
border: 2px solid #e9ecef;
|
||||
padding: 12px 15px;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
background: var(--primary-color);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
background: #0b5ed7;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.input-group i {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--secondary-color);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.input-group .form-control {
|
||||
padding-left: 45px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.loading.show {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<img src="/static/favicon.png" alt="Logo" height="64" class="mb-3">
|
||||
<h2>欢迎登录</h2>
|
||||
<p>闲鱼管理系统</p>
|
||||
</div>
|
||||
|
||||
<div class="login-body">
|
||||
<!-- 登录方式选择 -->
|
||||
<div class="login-tabs mb-4">
|
||||
<div class="btn-group w-100" role="group">
|
||||
<input type="radio" class="btn-check" name="loginType" id="usernameLogin" value="username" checked>
|
||||
<label class="btn btn-outline-primary" for="usernameLogin">用户名/密码</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="loginType" id="emailPasswordLogin" value="email-password">
|
||||
<label class="btn btn-outline-primary" for="emailPasswordLogin">邮箱/密码</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="loginType" id="emailCodeLogin" value="email-code">
|
||||
<label class="btn btn-outline-primary" for="emailCodeLogin">邮箱/验证码</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="loginForm">
|
||||
<!-- 用户名/密码登录 -->
|
||||
<div id="usernameLoginForm" class="login-form-section">
|
||||
<div class="input-group">
|
||||
<i class="bi bi-person-fill"></i>
|
||||
<input type="text" class="form-control" id="username" placeholder="请输入用户名">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<i class="bi bi-lock-fill"></i>
|
||||
<input type="password" class="form-control" id="password" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<!-- 登录验证码 -->
|
||||
<div id="securityCaptchaSection" class="mb-3">
|
||||
<label class="form-label"><i class="bi bi-shield-check text-primary me-1"></i>安全验证</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control" id="securityCaptchaCode" placeholder="输入验证码" maxlength="4" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div class="captcha-container" style="position: relative; height: 38px;">
|
||||
<img id="securityCaptchaImage" src="" alt="安全验证码"
|
||||
style="width: 100%; height: 100%; border: 1px solid #ddd; border-radius: 5px; cursor: pointer; object-fit: contain; background: #f8f9fa;"
|
||||
onclick="refreshSecurityCaptcha()" title="点击刷新验证码">
|
||||
<div id="securityCaptchaLoading" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text text-muted">
|
||||
<i class="bi bi-info-circle me-1"></i>请输入图中验证码,点击图片可刷新
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 邮箱/密码登录 -->
|
||||
<div id="emailPasswordLoginForm" class="login-form-section" style="display: none;">
|
||||
<div class="input-group">
|
||||
<i class="bi bi-envelope-fill"></i>
|
||||
<input type="email" class="form-control" id="emailForPassword" placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<i class="bi bi-lock-fill"></i>
|
||||
<input type="password" class="form-control" id="emailPassword" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<!-- 登录验证码(邮箱密码登录用) -->
|
||||
<div id="emailSecurityCaptchaSection" class="mb-3">
|
||||
<label class="form-label"><i class="bi bi-shield-check text-primary me-1"></i>安全验证</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control" id="emailSecurityCaptchaCode" placeholder="输入验证码" maxlength="4" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div class="captcha-container" style="position: relative; height: 38px;">
|
||||
<img id="emailSecurityCaptchaImage" src="" alt="安全验证码"
|
||||
style="width: 100%; height: 100%; border: 1px solid #ddd; border-radius: 5px; cursor: pointer; object-fit: contain; background: #f8f9fa;"
|
||||
onclick="refreshEmailSecurityCaptcha()" title="点击刷新验证码">
|
||||
<div id="emailSecurityCaptchaLoading" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text text-muted">
|
||||
<i class="bi bi-info-circle me-1"></i>请输入图中验证码,点击图片可刷新
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 邮箱/验证码登录 -->
|
||||
<div id="emailCodeLoginForm" class="login-form-section" style="display: none;">
|
||||
<div class="input-group">
|
||||
<i class="bi bi-envelope-fill"></i>
|
||||
<input type="email" class="form-control" id="emailForCode" placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<!-- 图形验证码 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">图形验证码</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control" id="captchaCode" placeholder="输入4位验证码" maxlength="4">
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div class="captcha-container" style="position: relative;">
|
||||
<img id="captchaImage" src="" alt="图形验证码"
|
||||
style="width: 100%; height: 38px; border: 1px solid #ddd; border-radius: 5px; cursor: pointer;"
|
||||
onclick="refreshCaptcha()">
|
||||
<div id="captchaLoading" class="text-center" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<span id="captchaStatus">请输入图形验证码,点击图片可刷新</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 邮箱验证码 -->
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="verificationCode" placeholder="输入6位验证码" maxlength="6">
|
||||
<button type="button" class="btn btn-outline-secondary" id="sendCodeBtn" onclick="sendVerificationCode()" disabled>
|
||||
发送验证码
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-text mb-3">
|
||||
<span id="codeStatus">请先验证图形验证码</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="errorAlert" class="alert alert-danger d-none" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<span id="errorMessage"></span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-login">
|
||||
<span class="loading spinner-border spinner-border-sm me-2" role="status"></span>
|
||||
<span id="loginText">登录</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- 注册链接 -->
|
||||
<div id="registerSection" class="text-center mt-3">
|
||||
<span class="text-muted">还没有账号?</span>
|
||||
<a href="/register.html" class="text-decoration-none">
|
||||
<i class="bi bi-person-plus me-1"></i>立即注册
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 默认账号提示 -->
|
||||
<div id="defaultLoginInfo" class="mt-4 p-3 bg-light rounded-3">
|
||||
<div class="text-center">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
默认登录账号
|
||||
</small>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<small class="text-muted">用户名:</small>
|
||||
<code class="bg-white px-2 py-1 rounded">admin</code>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">密码:</small>
|
||||
<code class="bg-white px-2 py-1 rounded">admin123</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="fillDefaultCredentials()">
|
||||
<i class="bi bi-arrow-down-circle me-1"></i>
|
||||
使用默认账号
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/lib/bootstrap/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const errorAlert = document.getElementById('errorAlert');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
const loading = document.querySelector('.loading');
|
||||
const loginText = document.getElementById('loginText');
|
||||
|
||||
// 安全验证码相关变量
|
||||
let securityCaptchaId = null;
|
||||
let emailSecurityCaptchaId = null;
|
||||
let securityCaptchaRequired = true; // 默认需要验证码,会通过API检查
|
||||
let loginCaptchaEnabled = true; // 登录验证码是否启用
|
||||
|
||||
// 检查登录验证码是否启用
|
||||
async function checkLoginCaptchaEnabled() {
|
||||
try {
|
||||
const response = await fetch('/api/login-captcha-enabled');
|
||||
const result = await response.json();
|
||||
loginCaptchaEnabled = result.enabled;
|
||||
securityCaptchaRequired = result.enabled;
|
||||
|
||||
// 根据设置显示或隐藏验证码区域
|
||||
const securityCaptchaSection = document.getElementById('securityCaptchaSection');
|
||||
const emailSecurityCaptchaSection = document.getElementById('emailSecurityCaptchaSection');
|
||||
|
||||
if (!loginCaptchaEnabled) {
|
||||
if (securityCaptchaSection) securityCaptchaSection.style.display = 'none';
|
||||
if (emailSecurityCaptchaSection) emailSecurityCaptchaSection.style.display = 'none';
|
||||
} else {
|
||||
// 根据当前登录方式显示验证码
|
||||
const loginType = document.querySelector('input[name="loginType"]:checked')?.value;
|
||||
if (loginType === 'username' && securityCaptchaSection) {
|
||||
securityCaptchaSection.style.display = 'block';
|
||||
} else if (loginType === 'email-password' && emailSecurityCaptchaSection) {
|
||||
emailSecurityCaptchaSection.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
return loginCaptchaEnabled;
|
||||
} catch (error) {
|
||||
console.error('检查登录验证码状态失败:', error);
|
||||
return true; // 出错时默认需要验证码
|
||||
}
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
errorMessage.textContent = message;
|
||||
errorAlert.classList.remove('d-none');
|
||||
}
|
||||
|
||||
function hideError() {
|
||||
errorAlert.classList.add('d-none');
|
||||
}
|
||||
|
||||
function setLoading(isLoading) {
|
||||
if (isLoading) {
|
||||
loading.classList.add('show');
|
||||
loginText.textContent = '登录中...';
|
||||
loginForm.querySelector('button').disabled = true;
|
||||
} else {
|
||||
loading.classList.remove('show');
|
||||
loginText.textContent = '登录';
|
||||
loginForm.querySelector('button').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否需要安全验证码
|
||||
async function checkSecurityCaptchaRequired() {
|
||||
try {
|
||||
const response = await fetch('/captcha/check-required');
|
||||
const result = await response.json();
|
||||
securityCaptchaRequired = result.required;
|
||||
|
||||
if (result.required) {
|
||||
showSecurityCaptcha();
|
||||
}
|
||||
|
||||
return result.required;
|
||||
} catch (error) {
|
||||
console.error('检查验证码状态失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示安全验证码
|
||||
function showSecurityCaptcha() {
|
||||
const loginType = document.querySelector('input[name="loginType"]:checked').value;
|
||||
|
||||
if (loginType === 'username') {
|
||||
document.getElementById('securityCaptchaSection').style.display = 'block';
|
||||
loadSecurityCaptcha();
|
||||
} else if (loginType === 'email-password') {
|
||||
document.getElementById('emailSecurityCaptchaSection').style.display = 'block';
|
||||
loadEmailSecurityCaptcha();
|
||||
}
|
||||
|
||||
securityCaptchaRequired = true;
|
||||
}
|
||||
|
||||
// 加载安全验证码(用户名登录)
|
||||
async function loadSecurityCaptcha() {
|
||||
const captchaImage = document.getElementById('securityCaptchaImage');
|
||||
const captchaLoading = document.getElementById('securityCaptchaLoading');
|
||||
|
||||
captchaLoading.style.display = 'block';
|
||||
captchaImage.style.opacity = '0.5';
|
||||
|
||||
try {
|
||||
const response = await fetch('/captcha/generate');
|
||||
|
||||
// 获取验证码ID
|
||||
securityCaptchaId = response.headers.get('X-Captcha-Id');
|
||||
|
||||
// 获取图片
|
||||
const blob = await response.blob();
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
captchaImage.src = imageUrl;
|
||||
|
||||
// 清空输入框
|
||||
document.getElementById('securityCaptchaCode').value = '';
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载安全验证码失败:', error);
|
||||
} finally {
|
||||
captchaLoading.style.display = 'none';
|
||||
captchaImage.style.opacity = '1';
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新安全验证码
|
||||
function refreshSecurityCaptcha() {
|
||||
loadSecurityCaptcha();
|
||||
}
|
||||
|
||||
// 加载安全验证码(邮箱密码登录)
|
||||
async function loadEmailSecurityCaptcha() {
|
||||
const captchaImage = document.getElementById('emailSecurityCaptchaImage');
|
||||
const captchaLoading = document.getElementById('emailSecurityCaptchaLoading');
|
||||
|
||||
captchaLoading.style.display = 'block';
|
||||
captchaImage.style.opacity = '0.5';
|
||||
|
||||
try {
|
||||
const response = await fetch('/captcha/generate');
|
||||
|
||||
// 获取验证码ID
|
||||
emailSecurityCaptchaId = response.headers.get('X-Captcha-Id');
|
||||
|
||||
// 获取图片
|
||||
const blob = await response.blob();
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
captchaImage.src = imageUrl;
|
||||
|
||||
// 清空输入框
|
||||
document.getElementById('emailSecurityCaptchaCode').value = '';
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载安全验证码失败:', error);
|
||||
} finally {
|
||||
captchaLoading.style.display = 'none';
|
||||
captchaImage.style.opacity = '1';
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新邮箱登录安全验证码
|
||||
function refreshEmailSecurityCaptcha() {
|
||||
loadEmailSecurityCaptcha();
|
||||
}
|
||||
|
||||
// 填充默认登录凭据
|
||||
function fillDefaultCredentials() {
|
||||
document.getElementById('username').value = 'admin';
|
||||
document.getElementById('password').value = 'admin123';
|
||||
hideError();
|
||||
|
||||
// 添加一个小动画效果
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
|
||||
usernameInput.style.backgroundColor = '#e3f2fd';
|
||||
passwordInput.style.backgroundColor = '#e3f2fd';
|
||||
|
||||
setTimeout(() => {
|
||||
usernameInput.style.backgroundColor = '';
|
||||
passwordInput.style.backgroundColor = '';
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
loginForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
hideError();
|
||||
setLoading(true);
|
||||
|
||||
const loginType = document.querySelector('input[name="loginType"]:checked').value;
|
||||
let loginData = {};
|
||||
|
||||
try {
|
||||
if (loginType === 'username') {
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showError('请输入用户名和密码');
|
||||
return;
|
||||
}
|
||||
|
||||
loginData = { username, password };
|
||||
|
||||
// 只有在验证码启用时才验证
|
||||
if (loginCaptchaEnabled) {
|
||||
const captchaCode = document.getElementById('securityCaptchaCode').value.trim();
|
||||
if (!captchaCode) {
|
||||
showError('请输入验证码');
|
||||
return;
|
||||
}
|
||||
loginData.captcha_id = securityCaptchaId;
|
||||
loginData.captcha_code = captchaCode;
|
||||
}
|
||||
|
||||
} else if (loginType === 'email-password') {
|
||||
const email = document.getElementById('emailForPassword').value;
|
||||
const password = document.getElementById('emailPassword').value;
|
||||
|
||||
if (!email || !password) {
|
||||
showError('请输入邮箱和密码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
showError('请输入有效的邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
loginData = { email, password };
|
||||
|
||||
// 只有在验证码启用时才验证
|
||||
if (loginCaptchaEnabled) {
|
||||
const emailCaptchaCode = document.getElementById('emailSecurityCaptchaCode').value.trim();
|
||||
if (!emailCaptchaCode) {
|
||||
showError('请输入验证码');
|
||||
return;
|
||||
}
|
||||
loginData.captcha_id = emailSecurityCaptchaId;
|
||||
loginData.captcha_code = emailCaptchaCode;
|
||||
}
|
||||
|
||||
} else if (loginType === 'email-code') {
|
||||
const email = document.getElementById('emailForCode').value;
|
||||
const verificationCode = document.getElementById('verificationCode').value;
|
||||
|
||||
if (!captchaVerified) {
|
||||
showError('请先验证图形验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
showError('请输入邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
showError('请输入有效的邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!verificationCode || verificationCode.length !== 6) {
|
||||
showError('请输入6位验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
loginData = { email, verification_code: verificationCode };
|
||||
}
|
||||
|
||||
const response = await fetch('/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(loginData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// 保存token到localStorage
|
||||
localStorage.setItem('auth_token', result.token);
|
||||
|
||||
// 保存用户信息到localStorage
|
||||
const userInfo = {
|
||||
user_id: result.user_id,
|
||||
username: result.username,
|
||||
is_admin: result.is_admin
|
||||
};
|
||||
localStorage.setItem('user_info', JSON.stringify(userInfo));
|
||||
|
||||
// 检查是否有重定向URL
|
||||
const redirectUrl = localStorage.getItem('redirectAfterLogin');
|
||||
if (redirectUrl) {
|
||||
// 清除重定向URL
|
||||
localStorage.removeItem('redirectAfterLogin');
|
||||
// 跳转到原来的页面
|
||||
window.location.href = redirectUrl;
|
||||
} else {
|
||||
// 默认跳转到管理页面
|
||||
window.location.href = '/admin';
|
||||
}
|
||||
} else {
|
||||
showError(result.message);
|
||||
|
||||
// 登录失败,刷新验证码(仅在验证码启用时)
|
||||
if (loginCaptchaEnabled) {
|
||||
if (loginType === 'username') {
|
||||
refreshSecurityCaptcha();
|
||||
} else if (loginType === 'email-password') {
|
||||
refreshEmailSecurityCaptcha();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
showError('登录失败,请检查网络连接');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查是否已经登录
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (token) {
|
||||
// 验证token是否有效
|
||||
fetch('/verify', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (result.authenticated) {
|
||||
window.location.href = '/admin';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// token无效,清除
|
||||
localStorage.removeItem('auth_token');
|
||||
});
|
||||
}
|
||||
|
||||
// 登录方式切换相关变量
|
||||
let captchaVerified = false;
|
||||
let countdownTimer = null;
|
||||
let countdownSeconds = 0;
|
||||
let sessionId = generateSessionId();
|
||||
|
||||
// 生成会话ID
|
||||
function generateSessionId() {
|
||||
return 'session_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
|
||||
}
|
||||
|
||||
// 登录方式切换
|
||||
function switchLoginType() {
|
||||
const loginType = document.querySelector('input[name="loginType"]:checked').value;
|
||||
|
||||
// 隐藏所有表单
|
||||
document.querySelectorAll('.login-form-section').forEach(section => {
|
||||
section.style.display = 'none';
|
||||
});
|
||||
|
||||
// 显示对应表单并加载验证码(仅在验证码启用时)
|
||||
if (loginType === 'username') {
|
||||
document.getElementById('usernameLoginForm').style.display = 'block';
|
||||
// 根据验证码设置显示/隐藏验证码区域
|
||||
const securityCaptchaSection = document.getElementById('securityCaptchaSection');
|
||||
if (loginCaptchaEnabled) {
|
||||
securityCaptchaSection.style.display = 'block';
|
||||
loadSecurityCaptcha();
|
||||
} else {
|
||||
securityCaptchaSection.style.display = 'none';
|
||||
}
|
||||
} else if (loginType === 'email-password') {
|
||||
document.getElementById('emailPasswordLoginForm').style.display = 'block';
|
||||
// 根据验证码设置显示/隐藏验证码区域
|
||||
const emailSecurityCaptchaSection = document.getElementById('emailSecurityCaptchaSection');
|
||||
if (loginCaptchaEnabled) {
|
||||
emailSecurityCaptchaSection.style.display = 'block';
|
||||
loadEmailSecurityCaptcha();
|
||||
} else {
|
||||
emailSecurityCaptchaSection.style.display = 'none';
|
||||
}
|
||||
} else if (loginType === 'email-code') {
|
||||
document.getElementById('emailCodeLoginForm').style.display = 'block';
|
||||
// 每次切换到邮箱验证码登录时都加载验证码
|
||||
loadCaptcha();
|
||||
}
|
||||
|
||||
hideError();
|
||||
}
|
||||
|
||||
// 加载图形验证码
|
||||
async function loadCaptcha() {
|
||||
const captchaImage = document.getElementById('captchaImage');
|
||||
const captchaLoading = document.getElementById('captchaLoading');
|
||||
const captchaStatus = document.getElementById('captchaStatus');
|
||||
|
||||
captchaLoading.style.display = 'block';
|
||||
captchaImage.style.display = 'none';
|
||||
|
||||
try {
|
||||
const response = await fetch('/generate-captcha', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ session_id: sessionId })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
captchaImage.src = result.captcha_image;
|
||||
captchaStatus.textContent = '请输入图形验证码,点击图片可刷新';
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
} else {
|
||||
captchaStatus.textContent = '图形验证码加载失败,请刷新页面重试';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载图形验证码失败:', error);
|
||||
captchaStatus.textContent = '图形验证码加载失败,请检查网络连接';
|
||||
} finally {
|
||||
captchaLoading.style.display = 'none';
|
||||
captchaImage.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新图形验证码
|
||||
function refreshCaptcha() {
|
||||
loadCaptcha();
|
||||
}
|
||||
|
||||
// 验证图形验证码
|
||||
async function verifyCaptcha() {
|
||||
const captchaCode = document.getElementById('captchaCode').value.trim();
|
||||
const captchaStatus = document.getElementById('captchaStatus');
|
||||
|
||||
if (!captchaCode) {
|
||||
captchaStatus.textContent = '请输入图形验证码';
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/verify-captcha', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_id: sessionId,
|
||||
captcha_code: captchaCode
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
captchaStatus.innerHTML = '<span style="color: green;">✓ 图形验证码验证成功</span>';
|
||||
captchaVerified = true;
|
||||
} else {
|
||||
captchaStatus.innerHTML = '<span style="color: red;">✗ 图形验证码错误,请重新输入</span>';
|
||||
captchaVerified = false;
|
||||
refreshCaptcha(); // 自动刷新验证码
|
||||
}
|
||||
|
||||
updateSendCodeButton();
|
||||
|
||||
} catch (error) {
|
||||
console.error('验证图形验证码失败:', error);
|
||||
captchaStatus.innerHTML = '<span style="color: red;">验证失败,请检查网络连接</span>';
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新发送验证码按钮状态
|
||||
function updateSendCodeButton() {
|
||||
const sendCodeBtn = document.getElementById('sendCodeBtn');
|
||||
const email = document.getElementById('emailForCode').value.trim();
|
||||
const codeStatus = document.getElementById('codeStatus');
|
||||
|
||||
if (captchaVerified && email && validateEmail(email)) {
|
||||
sendCodeBtn.disabled = false;
|
||||
codeStatus.textContent = '图形验证码已验证,可以发送邮箱验证码';
|
||||
} else {
|
||||
sendCodeBtn.disabled = true;
|
||||
if (!captchaVerified) {
|
||||
codeStatus.textContent = '请先验证图形验证码';
|
||||
} else if (!email) {
|
||||
codeStatus.textContent = '请先输入邮箱地址';
|
||||
} else if (!validateEmail(email)) {
|
||||
codeStatus.textContent = '请输入有效的邮箱地址';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 邮箱格式验证
|
||||
function validateEmail(email) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
// 开始倒计时
|
||||
function startCountdown() {
|
||||
countdownSeconds = 60;
|
||||
const sendCodeBtn = document.getElementById('sendCodeBtn');
|
||||
const codeStatus = document.getElementById('codeStatus');
|
||||
|
||||
sendCodeBtn.disabled = true;
|
||||
|
||||
countdownTimer = setInterval(() => {
|
||||
countdownSeconds--;
|
||||
sendCodeBtn.textContent = `重新发送 (${countdownSeconds}s)`;
|
||||
codeStatus.innerHTML = `<span class="countdown">验证码已发送,${countdownSeconds}秒后可重新发送</span>`;
|
||||
|
||||
if (countdownSeconds <= 0) {
|
||||
clearInterval(countdownTimer);
|
||||
sendCodeBtn.disabled = false;
|
||||
sendCodeBtn.textContent = '发送验证码';
|
||||
codeStatus.textContent = '可以重新发送验证码';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 发送邮箱验证码
|
||||
async function sendVerificationCode() {
|
||||
const email = document.getElementById('emailForCode').value.trim();
|
||||
|
||||
if (!captchaVerified) {
|
||||
showError('请先验证图形验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
showError('请先输入邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
showError('请输入有效的邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/send-verification-code', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
type: 'login',
|
||||
session_id: sessionId
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
startCountdown();
|
||||
} else {
|
||||
showError(result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送验证码失败:', error);
|
||||
showError('发送验证码失败,请检查网络连接');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查注册状态
|
||||
async function checkRegistrationStatus() {
|
||||
try {
|
||||
const response = await fetch('/registration-status');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const registerSection = document.getElementById('registerSection');
|
||||
if (registerSection) {
|
||||
registerSection.style.display = data.enabled ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查注册状态失败:', error);
|
||||
// 出错时默认显示注册链接
|
||||
}
|
||||
}
|
||||
|
||||
// 检查默认登录信息显示状态
|
||||
async function checkLoginInfoStatus() {
|
||||
try {
|
||||
const response = await fetch('/login-info-status');
|
||||
const result = await response.json();
|
||||
|
||||
const defaultLoginInfo = document.getElementById('defaultLoginInfo');
|
||||
if (result.enabled) {
|
||||
defaultLoginInfo.style.display = 'block';
|
||||
} else {
|
||||
defaultLoginInfo.style.display = 'none';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查登录信息显示状态失败:', error);
|
||||
// 出错时默认显示
|
||||
document.getElementById('defaultLoginInfo').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 事件监听器
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
// 检查注册状态
|
||||
checkRegistrationStatus();
|
||||
|
||||
// 检查默认登录信息显示状态
|
||||
checkLoginInfoStatus();
|
||||
|
||||
// 检查登录验证码是否启用
|
||||
await checkLoginCaptchaEnabled();
|
||||
|
||||
// 如果验证码启用,加载登录验证码(用户名登录是默认选中的)
|
||||
if (loginCaptchaEnabled) {
|
||||
loadSecurityCaptcha();
|
||||
}
|
||||
|
||||
// 登录方式切换
|
||||
document.querySelectorAll('input[name="loginType"]').forEach(radio => {
|
||||
radio.addEventListener('change', switchLoginType);
|
||||
});
|
||||
|
||||
// 图形验证码输入框事件(邮箱验证码登录用)
|
||||
const captchaCodeInput = document.getElementById('captchaCode');
|
||||
if (captchaCodeInput) {
|
||||
captchaCodeInput.addEventListener('input', function() {
|
||||
if (this.value.length === 4) {
|
||||
verifyCaptcha();
|
||||
} else {
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
document.getElementById('captchaStatus').textContent = '请输入4位图形验证码';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 邮箱输入框事件
|
||||
const emailForCodeInput = document.getElementById('emailForCode');
|
||||
if (emailForCodeInput) {
|
||||
emailForCodeInput.addEventListener('input', updateSendCodeButton);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,570 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户注册 - 闲鱼管理系统</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<link rel="stylesheet" href="/static/lib/bootstrap/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/static/lib/bootstrap-icons/bootstrap-icons.css">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
}
|
||||
.register-container {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
max-width: 420px;
|
||||
width: 100%;
|
||||
max-height: 95vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.register-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 1.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
.register-header h2 {
|
||||
margin-bottom: 0.3rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.register-header p {
|
||||
margin-bottom: 0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.register-body {
|
||||
padding: 1.2rem;
|
||||
}
|
||||
.form-control {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
padding: 8px 12px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.form-control:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.15rem rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
.btn-register {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.btn-register:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
.btn-code {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #667eea;
|
||||
color: #667eea;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
padding: 8px 12px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.btn-code:hover {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
.btn-code:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.login-link {
|
||||
text-align: center;
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
.login-link a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.login-link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.alert {
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.input-group {
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
.countdown {
|
||||
color: #6c757d;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.mb-3 {
|
||||
margin-bottom: 0.8rem !important;
|
||||
}
|
||||
.form-label {
|
||||
margin-bottom: 0.3rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
color: #495057;
|
||||
}
|
||||
.form-text {
|
||||
margin-top: 0.2rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.captcha-container img {
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-height: 700px) {
|
||||
.register-header {
|
||||
padding: 1rem;
|
||||
}
|
||||
.register-header h2 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.register-header p {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.register-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.mb-3 {
|
||||
margin-bottom: 0.6rem !important;
|
||||
}
|
||||
.form-control {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
.btn-code {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.register-container {
|
||||
margin: 5px;
|
||||
max-width: none;
|
||||
}
|
||||
.row.g-2 > * {
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="register-container">
|
||||
<div class="register-header">
|
||||
<h2 class="mb-0">
|
||||
<i class="bi bi-person-plus-fill me-2"></i>
|
||||
用户注册
|
||||
</h2>
|
||||
<p class="mb-0 mt-2 opacity-75">注册后即可使用闲鱼管理系统</p>
|
||||
</div>
|
||||
|
||||
<div class="register-body">
|
||||
<div id="alertContainer"></div>
|
||||
|
||||
<form id="registerForm">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">
|
||||
<i class="bi bi-person me-1"></i>用户名
|
||||
</label>
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
placeholder="3-20个字符,支持字母、数字、下划线" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">
|
||||
<i class="bi bi-envelope me-1"></i>邮箱地址
|
||||
</label>
|
||||
<input type="email" class="form-control" id="email" name="email"
|
||||
placeholder="用于接收验证码和重要通知" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="captchaCode" class="form-label">
|
||||
<i class="bi bi-shield-lock me-1"></i>图形验证码
|
||||
</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control" id="captchaCode" name="captchaCode"
|
||||
placeholder="输入4位验证码" maxlength="4" required>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div class="captcha-container" style="position: relative;">
|
||||
<img id="captchaImage" src="" alt="图形验证码"
|
||||
style="width: 100%; height: 32px; border: 1px solid #ddd; border-radius: 5px; cursor: pointer;"
|
||||
onclick="refreshCaptcha()">
|
||||
<div id="captchaLoading" class="text-center" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<span id="captchaStatus">请输入图形验证码,点击图片可刷新</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="verificationCode" class="form-label">
|
||||
<i class="bi bi-shield-check me-1"></i>邮箱验证码
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="verificationCode" name="verificationCode"
|
||||
placeholder="输入6位验证码" maxlength="6" required>
|
||||
<button type="button" class="btn btn-code" id="sendCodeBtn" onclick="sendVerificationCode()" disabled>
|
||||
发送验证码
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<span id="codeStatus">请先验证图形验证码</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">
|
||||
<i class="bi bi-lock me-1"></i>设置密码
|
||||
</label>
|
||||
<input type="password" class="form-control" id="password" name="password"
|
||||
placeholder="至少6个字符" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="confirmPassword" class="form-label">
|
||||
<i class="bi bi-lock-fill me-1"></i>确认密码
|
||||
</label>
|
||||
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword"
|
||||
placeholder="再次输入密码" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-register btn-primary w-100 mt-2">
|
||||
<i class="bi bi-person-plus me-1"></i>立即注册
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="login-link">
|
||||
<span class="text-muted">已有账号?</span>
|
||||
<a href="/login.html">立即登录</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/lib/bootstrap/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
let countdownTimer = null;
|
||||
let countdownSeconds = 0;
|
||||
let sessionId = generateSessionId();
|
||||
let captchaVerified = false;
|
||||
|
||||
function generateSessionId() {
|
||||
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
|
||||
function showAlert(message, type = 'danger') {
|
||||
const alertContainer = document.getElementById('alertContainer');
|
||||
const alertHtml = `
|
||||
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-${type === 'success' ? 'check-circle' : 'exclamation-triangle'}-fill me-2"></i>
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
`;
|
||||
alertContainer.innerHTML = alertHtml;
|
||||
}
|
||||
|
||||
function validateEmail(email) {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
async function loadCaptcha() {
|
||||
const captchaImage = document.getElementById('captchaImage');
|
||||
const captchaLoading = document.getElementById('captchaLoading');
|
||||
const captchaStatus = document.getElementById('captchaStatus');
|
||||
|
||||
try {
|
||||
captchaLoading.style.display = 'block';
|
||||
captchaImage.style.display = 'none';
|
||||
|
||||
const response = await fetch('/generate-captcha', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ session_id: sessionId })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
captchaImage.src = result.captcha_image;
|
||||
captchaStatus.textContent = '请输入图形验证码,点击图片可刷新';
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
} else {
|
||||
captchaStatus.textContent = '图形验证码加载失败,请刷新页面重试';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载图形验证码失败:', error);
|
||||
captchaStatus.textContent = '图形验证码加载失败,请检查网络连接';
|
||||
} finally {
|
||||
captchaLoading.style.display = 'none';
|
||||
captchaImage.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function refreshCaptcha() {
|
||||
sessionId = generateSessionId(); // 生成新的session ID
|
||||
loadCaptcha();
|
||||
// 不再自动清空验证码输入框,让用户自己决定是否清空
|
||||
}
|
||||
|
||||
async function verifyCaptcha() {
|
||||
const captchaCode = document.getElementById('captchaCode').value.trim();
|
||||
const captchaStatus = document.getElementById('captchaStatus');
|
||||
|
||||
if (!captchaCode) {
|
||||
captchaStatus.textContent = '请输入图形验证码';
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/verify-captcha', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_id: sessionId,
|
||||
captcha_code: captchaCode
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
captchaStatus.innerHTML = '<span style="color: green;">✓ 图形验证码验证成功</span>';
|
||||
captchaVerified = true;
|
||||
} else {
|
||||
captchaStatus.innerHTML = '<span style="color: red;">✗ 图形验证码错误,请重新输入</span>';
|
||||
captchaVerified = false;
|
||||
refreshCaptcha(); // 自动刷新验证码
|
||||
}
|
||||
|
||||
updateSendCodeButton();
|
||||
|
||||
} catch (error) {
|
||||
console.error('验证图形验证码失败:', error);
|
||||
captchaStatus.innerHTML = '<span style="color: red;">验证失败,请检查网络连接</span>';
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
}
|
||||
}
|
||||
|
||||
function updateSendCodeButton() {
|
||||
const sendCodeBtn = document.getElementById('sendCodeBtn');
|
||||
const email = document.getElementById('email').value.trim();
|
||||
const codeStatus = document.getElementById('codeStatus');
|
||||
|
||||
if (captchaVerified && email && validateEmail(email)) {
|
||||
sendCodeBtn.disabled = false;
|
||||
codeStatus.textContent = '图形验证码已验证,可以发送邮箱验证码';
|
||||
} else {
|
||||
sendCodeBtn.disabled = true;
|
||||
if (!captchaVerified) {
|
||||
codeStatus.textContent = '请先验证图形验证码';
|
||||
} else if (!email) {
|
||||
codeStatus.textContent = '请先输入邮箱地址';
|
||||
} else if (!validateEmail(email)) {
|
||||
codeStatus.textContent = '请输入有效的邮箱地址';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startCountdown() {
|
||||
countdownSeconds = 60;
|
||||
const sendCodeBtn = document.getElementById('sendCodeBtn');
|
||||
const codeStatus = document.getElementById('codeStatus');
|
||||
|
||||
sendCodeBtn.disabled = true;
|
||||
|
||||
countdownTimer = setInterval(() => {
|
||||
countdownSeconds--;
|
||||
sendCodeBtn.textContent = `重新发送 (${countdownSeconds}s)`;
|
||||
codeStatus.innerHTML = `<span class="countdown">验证码已发送,${countdownSeconds}秒后可重新发送</span>`;
|
||||
|
||||
if (countdownSeconds <= 0) {
|
||||
clearInterval(countdownTimer);
|
||||
sendCodeBtn.disabled = false;
|
||||
sendCodeBtn.textContent = '发送验证码';
|
||||
codeStatus.textContent = '可以重新发送验证码';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function sendVerificationCode() {
|
||||
const email = document.getElementById('email').value.trim();
|
||||
|
||||
if (!captchaVerified) {
|
||||
showAlert('请先验证图形验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
showAlert('请先输入邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
showAlert('请输入有效的邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/send-verification-code', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
session_id: sessionId
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showAlert(result.message, 'success');
|
||||
startCountdown();
|
||||
} else {
|
||||
showAlert(result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送验证码失败:', error);
|
||||
showAlert('发送验证码失败,请检查网络连接');
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('registerForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const username = formData.get('username').trim();
|
||||
const email = formData.get('email').trim();
|
||||
const captchaCode = formData.get('captchaCode').trim();
|
||||
const verificationCode = formData.get('verificationCode').trim();
|
||||
const password = formData.get('password');
|
||||
const confirmPassword = formData.get('confirmPassword');
|
||||
|
||||
// 前端验证
|
||||
if (!captchaVerified) {
|
||||
showAlert('请先验证图形验证码');
|
||||
return;
|
||||
}
|
||||
if (username.length < 3 || username.length > 20) {
|
||||
showAlert('用户名长度必须在3-20个字符之间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(username)) {
|
||||
showAlert('用户名只能包含字母、数字和下划线');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
showAlert('请输入有效的邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (verificationCode.length !== 6) {
|
||||
showAlert('请输入6位验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
showAlert('密码长度至少6个字符');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showAlert('两次输入的密码不一致');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
email: email,
|
||||
verification_code: verificationCode,
|
||||
password: password
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showAlert(result.message, 'success');
|
||||
setTimeout(() => {
|
||||
window.location.href = '/login.html';
|
||||
}, 2000);
|
||||
} else {
|
||||
showAlert(result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error);
|
||||
showAlert('注册失败,请检查网络连接');
|
||||
}
|
||||
});
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadCaptcha();
|
||||
});
|
||||
|
||||
// 图形验证码输入框事件
|
||||
document.getElementById('captchaCode').addEventListener('input', function() {
|
||||
if (this.value.length === 4) {
|
||||
verifyCaptcha();
|
||||
} else {
|
||||
captchaVerified = false;
|
||||
updateSendCodeButton();
|
||||
document.getElementById('captchaStatus').textContent = '请输入4位图形验证码';
|
||||
}
|
||||
});
|
||||
|
||||
// 邮箱输入框事件
|
||||
document.getElementById('email').addEventListener('input', function() {
|
||||
updateSendCodeButton();
|
||||
});
|
||||
|
||||
document.getElementById('email').addEventListener('blur', function() {
|
||||
const email = this.value.trim();
|
||||
updateSendCodeButton();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,149 @@
|
||||
'🎉1.发布 v1.9.2:修正账号运行态总览布局与链路判定,补强真实实例运行态读取,减少业务消息流误判未就绪;',
|
||||
'🔧2.修复链路就绪统计:运行态总览统一按 WS / Session / Token / 业务流 四条主链路统计,避免出现 1 / 5 与 0 / 4 混用;',
|
||||
'🔧3.修复运行态来源:优先读取账号真实活跃实例,临时 XianyuLive 实例不再注册全局实例表,避免业务消息流状态被临时实例覆盖;',
|
||||
'⚡4.优化运行态总览布局:账号详情改为左侧四个状态卡、右侧链路就绪摘要卡,业务消息流补充连接未就绪与恢复中的兜底展示;',
|
||||
'🎉1.发布 v1.9.1:新增业务消息流假在线检测与恢复展示,补强账号运行态诊断与前端自动刷新能力;',
|
||||
'🎉2.新增业务消息流看门狗:区分心跳包与真实业务包,长时间只有心跳时自动关闭旧 WebSocket 并触发重连;',
|
||||
'🎉3.新增消息流运行态诊断:账号运行态补充最近业务包、同步包、真实买家消息与假在线重连时间,支持识别“连接已通但消息流停滞”;',
|
||||
'⚡4.优化运行态展示与刷新:仪表盘和账号详情新增“消息流”状态摘要,并在连接恢复中的短时间窗口自动重试刷新,减少状态滞后;',
|
||||
'🎉1.发布 v1.9.0:升级账号保活诊断与历史订单同步,新增仪表盘运行态摘要和 GitHub 公告栏,并补齐多项登录与通知链路修复;',
|
||||
'🎉2.新增账号诊断与运行态摘要:账号页按实际链路展示 WS / Session / Token / 轻保活 等状态,仪表盘账号卡片新增运行态摘要;',
|
||||
'🎉3.重写历史订单同步:切换卖家工作台接口,补齐订单平台时间字段链路,并新增同步入口、状态面板与进度展示;',
|
||||
'🔧4.修复历史订单同步稳定性:收紧同步时间范围与数量限制,降低大范围抓取导致的异常与超时;',
|
||||
'🔧5.修复登录识别与恢复状态:收紧登录表单识别,找不到账号框/密码框时先复检已登录态和验证页;验证类型不明时不再默认按人脸处理,前后端展示与通知统一按实际验证类型显示;',
|
||||
'🔧6.修复账号重新启用后资料被清空的问题,避免恢复启用时覆盖已有配置;',
|
||||
'⚡7.优化自动回复优先级顺序,减少多规则命中时的回复偏差;',
|
||||
'🎉8.新增 GitHub 公告栏:仪表盘支持展示公告横幅,并可点击查看历史公告记录;',
|
||||
'🎉1.发布 v1.8.4:修复订单详情规格解析问题,新增滑块验证统计范围筛选,并优化风控日志与账号管理文案;',
|
||||
'🔧2.修复订单详情规格解析失败导致自动发货被阻断的问题,补强规格提取链路的稳定性(by @82762294);',
|
||||
'⚡3.优化滑块验证统计:新增当日 / 7天 / 所有范围筛选,统计卡片文案与交互更清晰;',
|
||||
'⚡4.优化风控日志展示:处理结果列去除无用元数据展开,前端信息更简洁,排查更直观;',
|
||||
'⚡5.优化账号管理入口与说明文案:明确扫码登录、账密登录、手动刷新 Cookie 与导入 Cookie 的使用场景,并补充账密配置指引;',
|
||||
'🎉1.发布 v1.8.3:修复有头模式白屏、自动刷新Session过期导致滑块连败,并优化滑块验证策略与Token预检重试机制;',
|
||||
'🔧2.修复有头模式白屏:完整反检测脚本会覆盖 document.fonts / EventTarget / Performance.now 等浏览器核心API导致页面无法渲染,有头模式改用轻量反检测脚本;',
|
||||
'🔧3.修复自动刷新Session过期滑块连败:自动Token刷新路径改用干净上下文(force_clean_context),避免持久化上下文中过期Session数据导致风控升级和滑块反复失败;',
|
||||
'🔧4.修复刷新模式登录状态假象:注入旧Cookie可能让前端显示"已登录"但服务端Session已过期,新增服务端Session有效性验证,过期时自动清除Cookie并重新登录;',
|
||||
'⚡5.优化Token预检重试机制:手动刷新后的Token预检新增最多3次重试(渐进等待),应对密码登录Cookie在服务端生效延迟;',
|
||||
'⚡6.优化滑块验证策略权重:降低历史成功率低的conservative策略权重(0.18→0.08),提高standard策略权重(0.47→0.57);',
|
||||
'⚡7.优化滑块重试策略:第3次及以后优先使用学习参数加大抖动变体,无学习数据时才轮换固定策略;增加重试间隔(2-3秒→4-6秒)降低反爬触发风险;',
|
||||
'⚡8.优化密码登录浏览器画像:复用完整浏览器画像配置(viewport/UA/locale)与captcha验证流程保持一致,自动刷新路径同步启用策略学习;',
|
||||
'🔧9.修复持久化上下文页面异常:无iframe且无已登录态时自动清除Cookie和缓存并重新加载,恢复登录表单;',
|
||||
'🔧1.发布 v1.8.2:修复 Token 刷新循环因属性未初始化导致崩溃,以及手动刷新认证预检因 asyncio 变量遮蔽导致失败的问题;',
|
||||
'🔧2.修复 Token 刷新循环中 last_token_refresh_status 属性未在 __init__ 初始化,首次访问时抛出 AttributeError 导致刷新循环异常退出;',
|
||||
'🔧3.修复手动刷新认证预检中 asyncio.run() 因函数内后续局部 import asyncio 遮蔽全局导入,触发 UnboundLocalError 导致预检失败;',
|
||||
'🎉1.发布 v1.8.1:补齐 v1.8.0 之后的稳定性更新,重点收口滑块/Token/会话恢复、手动刷新交接、通知链路、订单买家识别与多数量文本卡券发送;',
|
||||
'🔧2.修复滑块与会话恢复链路:稳定滑块恢复与令牌刷新,滑块成功后回写有效会话 Cookie,并保护关键会话字段不被不完整快照覆盖;',
|
||||
'🔧3.修复刷新交接与初始化鉴权:手动刷新后新增 Token 预检、交接恢复窗口、鉴权失败冷静期与恢复锁,减少 WebSocket 已连通但因 Token 获取失败反复重试;',
|
||||
'🔧4.修复通知与验证收口:统一通知派发路径,修复推送冷却、人脸验证通知类型/文案/模板渲染,并收口定时刷新误报;',
|
||||
'⚡5.优化风控统计口径:账密登录/手动刷新流程里的滑块验证也会记录到风控日志与滑块统计,排查更完整;',
|
||||
'🔧6.修复订单买家识别:取消订单后的系统卡片不再覆盖真实 buyer_id,避免订单买家信息被异常值污染;',
|
||||
'⚡7.优化多数量纯文本卡券消息:支持 API 文本卡券批量合并发送,减少重复刷屏,卡券发货提示更简洁;',
|
||||
'🎉1.发布 v1.8.0:风控日志升级为结构化会话链路,滑块验证、Token过期、账密登录和扫码刷新均支持统一追踪;',
|
||||
'⚡2.优化风控看板:滑块统计改为结构化会话口径,新增更准确的成功/失败统计、筛选与详情展示;',
|
||||
'⚡3.优化滑块验证反检测:引入稳定指纹配置、拟人轨迹、Cookie预热和多轮重试策略,提升通过率与稳定性;',
|
||||
'🔧4.修复手动刷新与自动刷新互踩:新增手动刷新保护期、扫码登录缓冲和Cookie有效性校验;',
|
||||
'🔧5.修复密码登录风控收口不稳定:新增失败退避、状态管理和浏览器侧兜底判定,减少登录回滚误判;',
|
||||
'⚡6.优化图片上传错误追踪:新增错误类型、状态码和错误信息记录,便于前端给出更准确提示;',
|
||||
'🔧7.修复商品同步后详情不更新:同步指定页/所有页时会强制刷新已有商品详情;',
|
||||
'⚡8.优化商品管理文案:明确区分“同步商品”和“刷新列表”,避免误操作;',
|
||||
'🔧1.修复扫码登录人脸验证会话丢失:风控场景改为在服务端保持原始会话并生成验证二维码;',
|
||||
'🔧2.修复扫码成功后再次进入滑块验证:新增真实Cookie合并与首次Token预热保护;',
|
||||
'⚡3.优化扫码风控状态收口:增加浏览器侧兜底判定,验证完成后可更稳定进入登录成功;',
|
||||
'⚡4.优化扫码登录前端提示:减少重复提示并统一验证过程中的状态反馈;',
|
||||
'🔧1.修复订单号误识别:收紧消息中的订单号提取规则,避免普通消息里的 messageId 被误判成订单并生成处理中假订单;',
|
||||
'🔧2.修复销售额卡片获取失败:统一销售统计口径并跳过空金额、脏金额订单,避免看板接口报错;',
|
||||
'⚡3.优化仪表盘展示:重构账号概览、订单数据看板、销售趋势与发货日志布局,关键信息更清晰易读;',
|
||||
'⚡4.优化发货日志:拆分规则、匹配结果、触发方式和规格状态列,并简化规格状态显示便于快速排查;',
|
||||
'🔧1.修复热更新历史清单读取逻辑:优先读取上一版 Release 资产中的 update_files.json,避免 deleted_files 丢失;',
|
||||
'🔧2.修复同版本热更新可能回滚清单生成脚本:补齐删除清单并完善后续版本生成逻辑;',
|
||||
'🎉1.新增账号列表商品一键擦亮入口:可批量执行当前在售商品擦亮;',
|
||||
'🎉2.新增每日定时擦亮任务:支持按账号配置启用状态、执行时段与随机延迟;',
|
||||
'⚡3.优化后台定时任务调度:新增执行结果记录,可查看下次执行时间和最近运行情况;',
|
||||
'⚡4.优化管理端擦亮操作体验:补充擦亮入口与设置弹窗,日常运营更方便;',
|
||||
'⚡5.优化账号管理页列宽与仪表盘赞助按钮样式:提升界面可读性与交互一致性;',
|
||||
'🔧1.修复订单规格识别混乱:优先从结构化订单响应提取真实skuInfo与数量,减少页面元素缺失导致的漏识别;',
|
||||
'🔧2.修复单规格误判为双规格:正文兜底改为去重并在候选歧义时直接放弃,阻止标题/描述污染spec2;',
|
||||
'⚡3.优化正文规格过滤:仅允许更像真实SKU字段的规格名进入兜底,过滤时间、广告文案和无关冒号文本;',
|
||||
'🔧4.修复历史残留第二规格污染:订单明确解析为单规格时,自动清空旧spec_name_2/spec_value_2;',
|
||||
'⚡5.优化多规格商品缓存复用条件:刷新订单详情时要求金额、状态和主规格同时有效,降低脏缓存复用风险;',
|
||||
'🔧1.修复系统消息状态优先级与消息分流逻辑:阻止订单状态回退,并减少系统通知噪音;',
|
||||
'🔧2.修复订单消息漏单:扩展订单号提取来源,增强sid兜底查单与近邻回退;',
|
||||
'⚡3.优化订单详情补刷:优先采用结构化状态信号,并增加状态门控与冷却;',
|
||||
'🔧4.修复无规格商品误发:收紧规则匹配与sid兜底发货链路,降低串单风险;',
|
||||
'🔧5.修复小刀订单漏发:新增成功证据持久化,缺少完整待发货卡片时仍可自动发货兜底;',
|
||||
'🔧6.修复闲鱼币抵扣金额误判:优先保留或推导真实实付金额,避免把原价当成交价;',
|
||||
'⚡7.优化发货日志展示:新增“已跳过”状态,并过滤成功后的重复skipped记录;',
|
||||
'🔧1.修复简化消息buyer_id误提取:区分sid与PNM格式消息,不再将会话ID误提取为buyer_id,避免防串单校验误判;',
|
||||
'🔧2.修复无效buyer_id污染订单数据:新增buyer_id可信度校验,识别unknown_user等无效占位符,防串单校验时自动跳过不可信值;',
|
||||
'🔧3.修复数据库buyer_id被占位符覆盖:更新订单时跳过无效buyer_id覆盖已有有效值,新增订单时自动净化为空值;',
|
||||
'🎉4.新增PNM格式消息解析:从message[4]提取buyer_id、买家昵称和商品ID,提升订单信息完整度;',
|
||||
'🔧5.修复订单详情锁事件循环不匹配:Web API调用时自动重建锁,避免跨事件循环死锁;',
|
||||
'⚡6.统一buyer_id/user_id默认值:各处unknown_user占位符改为None,避免脏数据进入订单与发货链路;',
|
||||
'🔧1.修复买家昵称污染:拦截系统文案进入订单与发货日志,避免记录错误昵称;',
|
||||
'🔧2.修复小刀商品归属误判:缓存未命中时回退刷新商品列表,避免误跳过自动发货;',
|
||||
'🔧3.修复连续下单串单:旧关单消息不再污染后续新订单状态;',
|
||||
'🔧4.修复sid简化消息误判:已发货/已完成/已关闭订单不再误报“未找到订单”;',
|
||||
'⚡5.统一北京时间读取:前端时间显示与销售统计、日期筛选统一按北京时间口径处理;',
|
||||
'🎉1.热更新弹窗新增“本次跳过”和“忽略此版本”操作,支持按版本跳过当前更新提示;',
|
||||
'⚡2.优化仪表盘更新入口:检查更新按钮新增自动检查开关和忽略版本管理,设置仅当前浏览器生效;',
|
||||
'🔧1.优化单规格自动发货策略:优先匹配单规格规则,精确未命中时可降级匹配普通关键字规则;',
|
||||
'⚡2.新增唯一普通规则兜底:单规格精确匹配失败时,仅在唯一命中一条普通规则时自动发货;',
|
||||
'⚡3.发货日志新增“单规兜底”标记,便于区分精确命中和普通规则降级命中;',
|
||||
'🔧1.补发 captcha_control.html 热更新资源,避免 v1.5.5 下该文件因 Release 资源未刷新而反复提示更新;',
|
||||
'⚡2.优化热更新检查日志:不再逐文件输出“文件已是最新”,改为输出更新检查汇总;',
|
||||
'🎉1.优化热更新清单生成:自动扫描 Python/HTML/静态资源与前端源码文件,无需手动维护白名单;',
|
||||
'🎉2.新增发版预检查脚本:发布前可检查版本号、改名/删除文件及未跟踪热更新文件;',
|
||||
'🎉3.支持热更新删除旧文件:更新前自动备份,再按清单安全清理历史旧文件;',
|
||||
'⚡4.优化发版流程:update_files.json 改为由 GitHub Actions 自动生成并上传 Release,无需提交到仓库;',
|
||||
'🔧1.统一热更新相关文本文件行尾:补充 .gitattributes 规则,固定 Python/JS/HTML/CSS 为 LF;',
|
||||
'🔧2.修复同版本误报更新:避免因 CRLF/LF 差异导致热更新将同版本文件判定为可更新;',
|
||||
'🔧1.修复热更新管理员权限判断:执行更新时按管理员身份校验,不再强依赖用户名必须为 admin;',
|
||||
'🔧2.修复热更新失败提示:前端优先展示后端返回的 detail 信息,避免只显示“未知错误”;',
|
||||
'🎉1.GitHub Actions 自动生成更新清单:发布 Release 前自动生成并上传 update_files.json;',
|
||||
'⚡2.优化热更新检测:本地版本变更后无需重启服务即可重新检查更新;',
|
||||
'🔧3.修复热更新权限判断:按管理员身份校验,不再强依赖用户名必须为 admin;',
|
||||
'🔧4.优化更新失败提示:前端可直接显示后端 detail 错误信息,便于排查;',
|
||||
'🎉1.接入 GitHub Releases 在线更新:支持从最新 Release 读取 update_files.json 检查热更新;',
|
||||
'🎉2.新增仪表盘检查更新入口:管理员可在版本区直接检查并执行热更新;',
|
||||
'⚡3.优化更新清单解析:兼容 GitHub 资产返回 application/octet-stream,避免检查更新失败;',
|
||||
'⚡4.优化版本区显示:版本号与检查更新入口统一为 badge 样式并修复垂直居中;',
|
||||
'🎉5.新增 GitHub Actions 自动发布工作流:push 到 main 且版本变化后可自动创建 tag 和 Release;',
|
||||
'🎉1.新增仪表盘发货日志面板:与账号详情5:5并排展示,可查看最近发货事件;',
|
||||
'🎉2.新增发货日志接口:支持读取最近发货日志(/delivery-logs/recent);',
|
||||
'⚡3.优化发货追踪:自动发货与手动发货统一记录真实发货事件;',
|
||||
'⚡4.优化日志字段:新增规则关键词、匹配模式(精确/兜底)、渠道(自动/手动)与失败原因;',
|
||||
'🔧5.修复自动确认发货失败后仍继续发送发货内容的问题,现已改为失败即阻断;',
|
||||
'🔧6.修复简化消息路径重复确认导致漏发风险,统一由_auto_delivery执行一次确认;',
|
||||
'🔧7.修复小刀流程误发货:待刀成仅执行免拼,成功小刀待发货才触发发货;',
|
||||
'🔧8.修复非系统消息可触发自动发货的问题,并增强sid兜底订单一致性校验;',
|
||||
'🎉1.新增AI回复配置API类型能力:支持OpenAI Chat/Responses、Gemini、Anthropic、Azure OpenAI、Ollama;',
|
||||
'🔧2.修复DashScope兼容模式误判为百炼应用导致“未找到app_id”报错的问题;',
|
||||
'⚡3.优化AI配置预设:增加api_type维度,预设保存/切换/自动匹配更准确;',
|
||||
'⚡4.优化AI回复配置弹窗:按连接层/策略层/语义层/验证层重构布局,提升设计感;',
|
||||
'⚡5.优化AI配置暗色模式和移动端适配,视觉层级更清晰;',
|
||||
'⚡6.优化API类型:下线“DashScope(百炼应用)”入口,历史值自动映射为自动识别;',
|
||||
'⚡7.优化提示词编辑区:三个输入框高度统一;',
|
||||
'⚡8.优化页面标题:浏览器标题统一为“闲鱼管理系统”;',
|
||||
'🎉1.新增回复延迟配置:账号列表支持设置1-10秒防抖延迟,修改后实时生效;',
|
||||
'⚡2.优化系统消息过滤:关键字改为部分匹配,减少符号/空格差异导致的漏匹配;',
|
||||
'⚡3.优化系统消息过滤精确度:避免误匹配买家正常消息(如“你已发货”);',
|
||||
'⚡4.优化账号列表布局:表格内容居中并调整列宽分配;',
|
||||
'🎉1.新增暗色模式支持,可在系统设置中切换主题;',
|
||||
'🔧2.修复暗色模式刷新页面闪烁问题;',
|
||||
'🔧3.修复点击导航链接会刷新页面的问题;',
|
||||
'🎉4.下单时自动获取并保存买家昵称;',
|
||||
'🔧5.修复下单时买家昵称提取错误的问题;',
|
||||
'🔧6.修复订单管理商品ID提取错误的问题;',
|
||||
'🔧7.修复递归搜索误提取tradeId等非商品ID的问题;',
|
||||
'⚡8.优化滑块验证重试策略,缩短等待时间;',
|
||||
'🔧9.修复交易关闭时订单状态不更新的问题;',
|
||||
'🎉10.新增qq直接回复闲鱼消息,无需在进入闲鱼(注意只能有公网访问的才行);
|
||||
①回复哪条消息引用即可
|
||||
②系统设置维护qq回复秘钥
|
||||
③在qq上给机器人发送 咸鱼绑定 key:url 其中key是你系统设置配置的秘钥,url 是你公网服务器的 ip加端口,例如 zheshimiyao:http://10.12.13.14:8080
|
||||
④如果没有公网,可联系我购买内网穿透服务,2块一个月,物美价廉',
|
||||
'🎉2.支持自动回复暂停时间支持设置为0;',
|
||||
'🎉3.修复自动回复关键词无法修改的问题;',
|
||||
'🎉4.修复订单管理普通用户无法查看的问题;',
|
||||
'🎉5.删除自动发货界面的发货数量,防止误解,想实现多数量发货在商品管理开启;',
|
||||
'🎉6.增加项目使用人数统计;',
|
||||
@@ -0,0 +1,421 @@
|
||||
// ==UserScript==
|
||||
// @name 闲鱼聊天暗色模式
|
||||
// @namespace https://github.com/xianyu-auto-reply
|
||||
// @version 2.0.0
|
||||
// @description 为闲鱼聊天页面添加暗色模式支持
|
||||
// @author XianyuAutoReply
|
||||
// @match https://www.goofish.com/im*
|
||||
// @match https://www.goofish.com/personal*
|
||||
// @match https://goofish.com/im*
|
||||
// @grant GM_addStyle
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @grant GM_registerMenuCommand
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const STORAGE_KEY = 'goofish_dark_mode';
|
||||
|
||||
function isSystemDarkMode() {
|
||||
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
function getDarkModeSetting() {
|
||||
const saved = GM_getValue(STORAGE_KEY, 'auto');
|
||||
if (saved === 'auto') return isSystemDarkMode();
|
||||
return saved === 'true';
|
||||
}
|
||||
|
||||
// 简洁的暗色配色 - 参考 macOS/Discord 风格
|
||||
const darkModeCSS = `
|
||||
/* ==================== 闲鱼聊天暗色模式 v2.0 ==================== */
|
||||
|
||||
/* CSS 变量定义 */
|
||||
:root {
|
||||
--dark-bg-1: #1a1a1a;
|
||||
--dark-bg-2: #242424;
|
||||
--dark-bg-3: #2d2d2d;
|
||||
--dark-bg-4: #363636;
|
||||
--dark-bg-hover: #3d3d3d;
|
||||
--dark-border: #404040;
|
||||
--dark-text-1: #ffffff;
|
||||
--dark-text-2: #b3b3b3;
|
||||
--dark-text-3: #808080;
|
||||
--dark-accent: #0a84ff;
|
||||
--dark-accent-hover: #409cff;
|
||||
--dark-msg-self: #0a84ff;
|
||||
--dark-msg-other: #363636;
|
||||
--dark-success: #30d158;
|
||||
--dark-warning: #ff9f0a;
|
||||
--dark-danger: #ff453a;
|
||||
}
|
||||
|
||||
/* ===== 全局重置 ===== */
|
||||
html, body {
|
||||
background-color: var(--dark-bg-1) !important;
|
||||
color: var(--dark-text-1) !important;
|
||||
}
|
||||
|
||||
/* ===== 所有容器 ===== */
|
||||
body > div,
|
||||
#ice-container,
|
||||
#ice-container *,
|
||||
#root,
|
||||
[class*="content-container"],
|
||||
[class*="page-im"],
|
||||
[class*="im-main"],
|
||||
[class*="chat-container"],
|
||||
[class*="chat-main"],
|
||||
.ant-layout,
|
||||
.ant-layout-has-sider,
|
||||
.ant-layout-content,
|
||||
.ant-layout-sider,
|
||||
.ant-layout-sider-children {
|
||||
background-color: var(--dark-bg-1) !important;
|
||||
}
|
||||
|
||||
/* ===== 会话列表 ===== */
|
||||
[class*="conversation-list"],
|
||||
[class*="conv-list-scroll"],
|
||||
[class*="conv-header"] {
|
||||
background-color: var(--dark-bg-1) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
}
|
||||
|
||||
[class*="conversation-item"] {
|
||||
background-color: var(--dark-bg-1) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
[class*="conversation-item"]:hover {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
}
|
||||
|
||||
/* ===== 聊天区域 ===== */
|
||||
[class*="message-list"],
|
||||
[class*="scroll-container"],
|
||||
.infinite-scroll-component,
|
||||
.infinite-scroll-component__outerdiv {
|
||||
background-color: var(--dark-bg-2) !important;
|
||||
}
|
||||
|
||||
/* ===== 消息气泡 ===== */
|
||||
[class*="message-text-left"],
|
||||
[style*="background-color: rgb(242, 242, 244)"],
|
||||
[style*="background-color: rgb(242,242,244)"] {
|
||||
background-color: var(--dark-msg-other) !important;
|
||||
color: var(--dark-text-1) !important;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
|
||||
[class*="message-text-right"],
|
||||
[style*="background-color: rgb(255, 230, 15)"],
|
||||
[style*="background-color: rgb(255,230,15)"] {
|
||||
background-color: var(--dark-msg-self) !important;
|
||||
color: #ffffff !important;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
|
||||
[class*="message-text"] *,
|
||||
[class*="message-text-left"] *,
|
||||
[class*="message-text-right"] * {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* ===== 文字颜色 ===== */
|
||||
/* 用户昵称等主要文字 */
|
||||
[class*="text1--"],
|
||||
[class*="nick"],
|
||||
[class*="name"] {
|
||||
color: var(--dark-text-1) !important;
|
||||
}
|
||||
|
||||
/* 次要文字(摘要、时间等)*/
|
||||
[class*="text2--"],
|
||||
[class*="time"],
|
||||
[class*="desc"] {
|
||||
color: var(--dark-text-2) !important;
|
||||
}
|
||||
|
||||
/* 强制覆盖内联深色文字 */
|
||||
[style*="color: rgb(0, 0, 0)"],
|
||||
[style*="color: rgb(31, 31, 31)"],
|
||||
[style*="color: rgb(51, 51, 51)"],
|
||||
[style*="color:rgb(0,"],
|
||||
[style*="color:rgb(31,"],
|
||||
[style*="color:rgb(51,"] {
|
||||
color: var(--dark-text-1) !important;
|
||||
}
|
||||
|
||||
[style*="color: rgb(102, 102, 102)"],
|
||||
[style*="color: rgb(153, 153, 153)"],
|
||||
[style*="color: rgb(163, 163, 163)"] {
|
||||
color: var(--dark-text-2) !important;
|
||||
}
|
||||
|
||||
/* ===== 发送框 ===== */
|
||||
[class*="sendbox"] {
|
||||
background-color: var(--dark-bg-2) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
}
|
||||
|
||||
[class*="sendbox-bottom"],
|
||||
[class*="sendbox-topbar"] {
|
||||
background-color: var(--dark-bg-2) !important;
|
||||
}
|
||||
|
||||
/* ===== 输入框 ===== */
|
||||
textarea,
|
||||
input[type="text"],
|
||||
.ant-input,
|
||||
[class*="textarea"] {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
color: var(--dark-text-1) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
textarea::placeholder,
|
||||
.ant-input::placeholder {
|
||||
color: var(--dark-text-3) !important;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
.ant-input:focus {
|
||||
border-color: var(--dark-accent) !important;
|
||||
box-shadow: 0 0 0 2px rgba(10, 132, 255, 0.2) !important;
|
||||
}
|
||||
|
||||
/* ===== 按钮 ===== */
|
||||
.ant-btn {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
color: var(--dark-text-1) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
}
|
||||
|
||||
.ant-btn:hover {
|
||||
background-color: var(--dark-bg-hover) !important;
|
||||
}
|
||||
|
||||
[class*="sendbox-bottom"] .ant-btn:not([disabled]) {
|
||||
background-color: var(--dark-accent) !important;
|
||||
color: #ffffff !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
[class*="sendbox-bottom"] .ant-btn:not([disabled]):hover {
|
||||
background-color: var(--dark-accent-hover) !important;
|
||||
}
|
||||
|
||||
[class*="sendbox-bottom"] .ant-btn[disabled] {
|
||||
background-color: var(--dark-bg-4) !important;
|
||||
color: var(--dark-text-3) !important;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* ===== 订单状态标签 ===== */
|
||||
[class*="order-wait"] {
|
||||
background-color: rgba(255, 159, 10, 0.15) !important;
|
||||
color: var(--dark-warning) !important;
|
||||
}
|
||||
|
||||
[class*="order-success"] {
|
||||
background-color: rgba(48, 209, 88, 0.15) !important;
|
||||
color: var(--dark-success) !important;
|
||||
}
|
||||
|
||||
[class*="order-close"] {
|
||||
background-color: rgba(255, 69, 58, 0.15) !important;
|
||||
color: var(--dark-danger) !important;
|
||||
}
|
||||
|
||||
/* ===== 顶部商品栏 ===== */
|
||||
[class*="message-topbar"],
|
||||
[class*="messageHeadBtnContainer"] {
|
||||
background-color: var(--dark-bg-2) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
}
|
||||
|
||||
/* ===== 价格 ===== */
|
||||
[class*="price"],
|
||||
[class*="money"] {
|
||||
color: var(--dark-warning) !important;
|
||||
}
|
||||
|
||||
/* ===== 链接 ===== */
|
||||
a {
|
||||
color: var(--dark-accent) !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--dark-accent-hover) !important;
|
||||
}
|
||||
|
||||
/* ===== 下拉菜单 ===== */
|
||||
.ant-dropdown,
|
||||
.ant-dropdown-menu {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
}
|
||||
|
||||
.ant-dropdown-menu-item {
|
||||
color: var(--dark-text-1) !important;
|
||||
}
|
||||
|
||||
.ant-dropdown-menu-item:hover {
|
||||
background-color: var(--dark-bg-hover) !important;
|
||||
}
|
||||
|
||||
/* ===== 右侧工具栏 ===== */
|
||||
[class*="sidebar-container"],
|
||||
[class*="sidebar-item-wrap"],
|
||||
[class*="sidebar-item-container"] {
|
||||
background-color: var(--dark-bg-1) !important;
|
||||
}
|
||||
|
||||
[class*="sidebar-item-wrap"]:hover {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
}
|
||||
|
||||
[class*="sidebar-item-text"] {
|
||||
color: var(--dark-text-2) !important;
|
||||
}
|
||||
|
||||
/* ===== 图标 ===== */
|
||||
.anticon,
|
||||
[class*="Icon"],
|
||||
[class*="icon"] {
|
||||
color: var(--dark-text-1) !important;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* 图片类图标(如清除未读等)增加亮度 */
|
||||
img[alt],
|
||||
img[src*="icon"],
|
||||
img[src*="Icon"],
|
||||
[class*="icon"] img,
|
||||
[class*="header"] img {
|
||||
filter: brightness(1.3) contrast(1.1) !important;
|
||||
}
|
||||
|
||||
/* 头像图片不处理 */
|
||||
img[style*="border-radius"],
|
||||
[class*="avatar"] img {
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
/* ===== 骨架屏 ===== */
|
||||
.ant-skeleton-avatar,
|
||||
.ant-skeleton-button {
|
||||
background: linear-gradient(90deg, var(--dark-bg-3) 25%, var(--dark-bg-4) 50%, var(--dark-bg-3) 75%) !important;
|
||||
background-size: 200% 100% !important;
|
||||
}
|
||||
|
||||
/* ===== 滚动条 ===== */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--dark-bg-4);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--dark-text-3);
|
||||
}
|
||||
|
||||
.rc-virtual-list-scrollbar-thumb {
|
||||
background-color: var(--dark-bg-4) !important;
|
||||
}
|
||||
|
||||
/* ===== 强制覆盖白色背景 ===== */
|
||||
[style*="background-color: rgb(255, 255, 255)"],
|
||||
[style*="background-color: rgb(252, 252, 252)"],
|
||||
[style*="background-color: rgb(247, 248, 250)"],
|
||||
[style*="background: rgb(255, 255, 255)"],
|
||||
[style*="background: white"],
|
||||
[style*="background-color: white"] {
|
||||
background-color: var(--dark-bg-2) !important;
|
||||
}
|
||||
|
||||
/* ===== 图片容器 ===== */
|
||||
.ant-image,
|
||||
[class*="image-container"] {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ===== 文字通用继承 ===== */
|
||||
p, span, div, li, label, h1, h2, h3, h4, h5, h6 {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* ===== 表单元素 ===== */
|
||||
.ant-select,
|
||||
.ant-select-selector,
|
||||
.ant-input-group-addon {
|
||||
background-color: var(--dark-bg-3) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
color: var(--dark-text-1) !important;
|
||||
}
|
||||
|
||||
/* ===== 模态框 ===== */
|
||||
.ant-modal-content,
|
||||
.ant-modal-header,
|
||||
.ant-modal-body,
|
||||
.ant-modal-footer {
|
||||
background-color: var(--dark-bg-2) !important;
|
||||
border-color: var(--dark-border) !important;
|
||||
color: var(--dark-text-1) !important;
|
||||
}
|
||||
|
||||
.ant-modal-close-x {
|
||||
color: var(--dark-text-2) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
function injectDarkMode() {
|
||||
if (getDarkModeSetting()) {
|
||||
GM_addStyle(darkModeCSS);
|
||||
console.log('[闲鱼暗色模式] v2.0 已启用');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDarkMode() {
|
||||
const current = GM_getValue(STORAGE_KEY, 'auto');
|
||||
let next;
|
||||
if (current === 'auto') {
|
||||
next = 'true';
|
||||
} else if (current === 'true') {
|
||||
next = 'false';
|
||||
} else {
|
||||
next = 'auto';
|
||||
}
|
||||
GM_setValue(STORAGE_KEY, next);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
const currentSetting = GM_getValue(STORAGE_KEY, 'auto');
|
||||
const statusText = currentSetting === 'auto' ? '跟随系统' : (currentSetting === 'true' ? '已开启' : '已关闭');
|
||||
GM_registerMenuCommand(`切换暗色模式 (当前: ${statusText})`, toggleDarkMode);
|
||||
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (GM_getValue(STORAGE_KEY, 'auto') === 'auto') {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
injectDarkMode();
|
||||
})();
|
||||
@@ -0,0 +1 @@
|
||||
v1.9.2
|
||||
@@ -0,0 +1,568 @@
|
||||
const crypto = require('crypto')
|
||||
|
||||
const generate_mid = () => {
|
||||
return "" + Math.floor(1e3 * Math.random()) + new Date().getTime() + " 0"
|
||||
}
|
||||
|
||||
const generate_uuid = () => {
|
||||
return "-" + Date.now() + "1"
|
||||
}
|
||||
|
||||
const generate_device_id = (user_id) => {
|
||||
for (var ee, et = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""), en = [], eo = Math.random, ei = 0; ei < 36; ei++)
|
||||
8 === ei || 13 === ei || 18 === ei || 23 === ei ? en[ei] = "-" : 14 === ei ? en[ei] = "4" : (ee = 0 | 16 * eo(),
|
||||
en[ei] = et[19 === ei ? 3 & ee | 8 : ee]);
|
||||
return en.join("") + "-" + user_id
|
||||
}
|
||||
|
||||
|
||||
const generate_sign = (t, token, data) => {
|
||||
const j = t
|
||||
const h = 34839810
|
||||
const msg = token + "&" + j + "&" + h + "&" + data
|
||||
const md5 = crypto.createHash('md5')
|
||||
md5.update(msg)
|
||||
return md5.digest('hex')
|
||||
}
|
||||
eM = new TextDecoder("utf-8");
|
||||
eL = null
|
||||
var sv = Array(4096);
|
||||
|
||||
function sg() {
|
||||
var ee, et = eI[aG++];
|
||||
if (!(et >= 160) || !(et < 192))
|
||||
return aG--,
|
||||
sy(a5());
|
||||
if (et -= 160,
|
||||
aX >= aG)
|
||||
return eR.slice(aG - aq, (aG += et) - aq);
|
||||
if (!(0 == aX && eO < 180))
|
||||
return se(et);
|
||||
var en = (et << 5 ^ (et > 1 ? eZ.getUint16(aG) : et > 0 ? eI[aG] : 0)) & 4095
|
||||
, eo = sv[en]
|
||||
, ei = aG
|
||||
, ec = aG + et - 3
|
||||
, eu = 0;
|
||||
if (eo && eo.bytes == et) {
|
||||
for (; ei < ec;) {
|
||||
if ((ee = eZ.getUint32(ei)) != eo[eu++]) {
|
||||
ei = 1879048192;
|
||||
break
|
||||
}
|
||||
ei += 4
|
||||
}
|
||||
for (ec += 3; ei < ec;)
|
||||
if ((ee = eI[ei++]) != eo[eu++]) {
|
||||
ei = 1879048192;
|
||||
break
|
||||
}
|
||||
if (ei === ec)
|
||||
return aG = ei,
|
||||
eo.string;
|
||||
ec -= 3,
|
||||
ei = aG
|
||||
}
|
||||
for (eo = [],
|
||||
sv[en] = eo,
|
||||
eo.bytes = et; ei < ec;)
|
||||
ee = eZ.getUint32(ei),
|
||||
eo.push(ee),
|
||||
ei += 4;
|
||||
for (ec += 3; ei < ec;)
|
||||
ee = eI[ei++],
|
||||
eo.push(ee);
|
||||
var ed = et < 16 ? su(et) : sl(et);
|
||||
return null != ed ? eo.string = ed : eo.string = se(et)
|
||||
}
|
||||
|
||||
function sy(ee) {
|
||||
if ("string" == typeof ee)
|
||||
return ee;
|
||||
if ("number" == typeof ee || "boolean" == typeof ee || "bigint" == typeof ee)
|
||||
return ee.toString();
|
||||
if (null == ee)
|
||||
return ee + "";
|
||||
throw Error("Invalid property type for record", typeof ee)
|
||||
}
|
||||
|
||||
r9 = function (ee) {
|
||||
return atob(rW(ee))
|
||||
}
|
||||
rW = function (ee) {
|
||||
return ee.replace(/[^A-Za-z0-9\+\/]/g, "")
|
||||
}
|
||||
aW = {
|
||||
"useRecords": false,
|
||||
"mapsAsObjects": true
|
||||
}
|
||||
aJ = false
|
||||
eP = []
|
||||
|
||||
function a3(ee) {
|
||||
if (!aW.trusted && !aJ) {
|
||||
var et, en, eo = eP.sharedLength || 0;
|
||||
eo < eP.length && (eP.length = eo)
|
||||
}
|
||||
if (aW.randomAccessStructure && eI[aG] < 64 && eI[aG] >= 32 && ej ? (et = ej(eI, aG, eO, aW),
|
||||
eI = null,
|
||||
!(ee && ee.lazy) && et && (et = et.toJSON()),
|
||||
aG = eO) : et = a5(),
|
||||
eD && (aG = eD.postBundlePosition,
|
||||
eD = null),
|
||||
aJ && (eP.restoreStructures = null),
|
||||
aG == eO)
|
||||
eP && eP.restoreStructures && a4(),
|
||||
eP = null,
|
||||
eI = null,
|
||||
eL && (eL = null);
|
||||
else if (aG > eO)
|
||||
throw Error("Unexpected end of MessagePack data");
|
||||
else if (!aJ) {
|
||||
try {
|
||||
en = JSON.stringify(et, function (ee, et) {
|
||||
return "bigint" == typeof et ? "".concat(et, "n") : et
|
||||
}).slice(0, 100)
|
||||
} catch (ee) {
|
||||
en = "(JSON view not available " + ee + ")"
|
||||
}
|
||||
throw Error("Data read, but end of buffer not reached " + en)
|
||||
}
|
||||
return et
|
||||
|
||||
}
|
||||
|
||||
function so(ee) {
|
||||
if (ee < 16 && (et = su(ee)))
|
||||
return et;
|
||||
if (ee > 64 && eM)
|
||||
return eM.decode(eI.subarray(aG, aG += ee));
|
||||
var et, en = aG + ee, eo = [];
|
||||
for (et = ""; aG < en;) {
|
||||
var ei = eI[aG++];
|
||||
if ((128 & ei) === 0)
|
||||
eo.push(ei);
|
||||
else if ((224 & ei) === 192) {
|
||||
var ec = 63 & eI[aG++];
|
||||
eo.push((31 & ei) << 6 | ec)
|
||||
} else if ((240 & ei) === 224) {
|
||||
var eu = 63 & eI[aG++]
|
||||
, ed = 63 & eI[aG++];
|
||||
eo.push((31 & ei) << 12 | eu << 6 | ed)
|
||||
} else if ((248 & ei) === 240) {
|
||||
var ef = (7 & ei) << 18 | (63 & eI[aG++]) << 12 | (63 & eI[aG++]) << 6 | 63 & eI[aG++];
|
||||
ef > 65535 && (ef -= 65536,
|
||||
eo.push(ef >>> 10 & 1023 | 55296),
|
||||
ef = 56320 | 1023 & ef),
|
||||
eo.push(ef)
|
||||
} else
|
||||
eo.push(ei);
|
||||
eo.length >= 4096 && (et += sc.apply(String, eo),
|
||||
eo.length = 0)
|
||||
}
|
||||
return eo.length > 0 && (et += sc.apply(String, eo)),
|
||||
et
|
||||
}
|
||||
|
||||
se = so;
|
||||
var sc = String.fromCharCode;
|
||||
|
||||
function sl(ee) {
|
||||
for (var et = aG, en = Array(ee), eo = 0; eo < ee; eo++) {
|
||||
var ei = eI[aG++];
|
||||
if ((128 & ei) > 0) {
|
||||
aG = et;
|
||||
return
|
||||
}
|
||||
en[eo] = ei
|
||||
}
|
||||
return sc.apply(String, en)
|
||||
}
|
||||
|
||||
function su(ee) {
|
||||
if (ee < 4) {
|
||||
if (ee < 2) {
|
||||
if (0 === ee)
|
||||
return "";
|
||||
var et = eI[aG++];
|
||||
if ((128 & et) > 1) {
|
||||
aG -= 1;
|
||||
return
|
||||
}
|
||||
return sc(et)
|
||||
}
|
||||
var en = eI[aG++]
|
||||
, eo = eI[aG++];
|
||||
if ((128 & en) > 0 || (128 & eo) > 0) {
|
||||
aG -= 2;
|
||||
return
|
||||
}
|
||||
if (ee < 3)
|
||||
return sc(en, eo);
|
||||
var ei = eI[aG++];
|
||||
if ((128 & ei) > 0) {
|
||||
aG -= 3;
|
||||
return
|
||||
}
|
||||
return sc(en, eo, ei)
|
||||
}
|
||||
var ec = eI[aG++]
|
||||
, eu = eI[aG++]
|
||||
, ed = eI[aG++]
|
||||
, ef = eI[aG++];
|
||||
if ((128 & ec) > 0 || (128 & eu) > 0 || (128 & ed) > 0 || (128 & ef) > 0) {
|
||||
aG -= 4;
|
||||
return
|
||||
}
|
||||
if (ee < 6) {
|
||||
if (4 === ee)
|
||||
return sc(ec, eu, ed, ef);
|
||||
var ep = eI[aG++];
|
||||
if ((128 & ep) > 0) {
|
||||
aG -= 5;
|
||||
return
|
||||
}
|
||||
return sc(ec, eu, ed, ef, ep)
|
||||
}
|
||||
if (ee < 8) {
|
||||
var em = eI[aG++]
|
||||
, eh = eI[aG++];
|
||||
if ((128 & em) > 0 || (128 & eh) > 0) {
|
||||
aG -= 6;
|
||||
return
|
||||
}
|
||||
if (ee < 7)
|
||||
return sc(ec, eu, ed, ef, em, eh);
|
||||
var ev = eI[aG++];
|
||||
if ((128 & ev) > 0) {
|
||||
aG -= 7;
|
||||
return
|
||||
}
|
||||
return sc(ec, eu, ed, ef, em, eh, ev)
|
||||
}
|
||||
var eg = eI[aG++]
|
||||
, eC = eI[aG++]
|
||||
, eS = eI[aG++]
|
||||
, ew = eI[aG++];
|
||||
if ((128 & eg) > 0 || (128 & eC) > 0 || (128 & eS) > 0 || (128 & ew) > 0) {
|
||||
aG -= 8;
|
||||
return
|
||||
}
|
||||
if (ee < 10) {
|
||||
if (8 === ee)
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew);
|
||||
var eE = eI[aG++];
|
||||
if ((128 & eE) > 0) {
|
||||
aG -= 9;
|
||||
return
|
||||
}
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, eE)
|
||||
}
|
||||
if (ee < 12) {
|
||||
var ek = eI[aG++]
|
||||
, e_ = eI[aG++];
|
||||
if ((128 & ek) > 0 || (128 & e_) > 0) {
|
||||
aG -= 10;
|
||||
return
|
||||
}
|
||||
if (ee < 11)
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, ek, e_);
|
||||
var eT = eI[aG++];
|
||||
if ((128 & eT) > 0) {
|
||||
aG -= 11;
|
||||
return
|
||||
}
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, ek, e_, eT)
|
||||
}
|
||||
var eN = eI[aG++]
|
||||
, eM = eI[aG++]
|
||||
, eO = eI[aG++]
|
||||
, eP = eI[aG++];
|
||||
if ((128 & eN) > 0 || (128 & eM) > 0 || (128 & eO) > 0 || (128 & eP) > 0) {
|
||||
aG -= 12;
|
||||
return
|
||||
}
|
||||
if (ee < 14) {
|
||||
if (12 === ee)
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, eN, eM, eO, eP);
|
||||
var eR = eI[aG++];
|
||||
if ((128 & eR) > 0) {
|
||||
aG -= 13;
|
||||
return
|
||||
}
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, eN, eM, eO, eP, eR)
|
||||
}
|
||||
var eD = eI[aG++]
|
||||
, eL = eI[aG++];
|
||||
if ((128 & eD) > 0 || (128 & eL) > 0) {
|
||||
aG -= 14;
|
||||
return
|
||||
}
|
||||
if (ee < 15)
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, eN, eM, eO, eP, eD, eL);
|
||||
var eZ = eI[aG++];
|
||||
if ((128 & eZ) > 0) {
|
||||
aG -= 15;
|
||||
return
|
||||
}
|
||||
return sc(ec, eu, ed, ef, eg, eC, eS, ew, eN, eM, eO, eP, eD, eL, eZ)
|
||||
}
|
||||
|
||||
function ss(ee) {
|
||||
if (aW.mapsAsObjects) {
|
||||
for (var et = {}, en = 0; en < ee; en++) {
|
||||
var eo = sg();
|
||||
"__proto__" === eo && (eo = "__proto_"),
|
||||
et[eo] = a5()
|
||||
}
|
||||
return et
|
||||
}
|
||||
for (var ei = new Map, ec = 0; ec < ee; ec++)
|
||||
ei.set(a5(), a5());
|
||||
return ei
|
||||
}
|
||||
|
||||
function a5() {
|
||||
var ee, et = eI[aG++];
|
||||
if (et < 160) {
|
||||
if (et < 128) {
|
||||
if (et < 64)
|
||||
return et;
|
||||
var en = eP[63 & et] || aW.getStructures && a9()[63 & et];
|
||||
return en ? (en.read || (en.read = a8(en, 63 & et)),
|
||||
en.read()) : et
|
||||
}
|
||||
if (et < 144) {
|
||||
if (et -= 128,
|
||||
aW.mapsAsObjects) {
|
||||
for (var eo = {}, ei = 0; ei < et; ei++) {
|
||||
var ec = sg();
|
||||
"__proto__" === ec && (ec = "__proto_"),
|
||||
eo[ec] = a5()
|
||||
}
|
||||
return eo
|
||||
}
|
||||
for (var eu = new Map, ed = 0; ed < et; ed++)
|
||||
eu.set(a5(), a5());
|
||||
return eu
|
||||
}
|
||||
for (var ef = Array(et -= 144), ep = 0; ep < et; ep++)
|
||||
ef[ep] = a5();
|
||||
return aW.freezeData ? Object.freeze(ef) : ef
|
||||
}
|
||||
if (et < 192) {
|
||||
var em = et - 160;
|
||||
if (aX >= aG)
|
||||
return eR.slice(aG - aq, (aG += em) - aq);
|
||||
if (0 == aX && eO < 140) {
|
||||
var eh = em < 16 ? su(em) : sl(em);
|
||||
if (null != eh)
|
||||
return eh
|
||||
}
|
||||
return se(em)
|
||||
}
|
||||
switch (et) {
|
||||
case 192:
|
||||
return null;
|
||||
case 193:
|
||||
if (eD) {
|
||||
if ((ee = a5()) > 0)
|
||||
return eD[1].slice(eD.position1, eD.position1 += ee);
|
||||
return eD[0].slice(eD.position0, eD.position0 -= ee)
|
||||
}
|
||||
return aY;
|
||||
case 194:
|
||||
return !1;
|
||||
case 195:
|
||||
return !0;
|
||||
case 196:
|
||||
if (void 0 === (ee = eI[aG++]))
|
||||
throw Error("Unexpected end of buffer");
|
||||
return sm(ee);
|
||||
case 197:
|
||||
return ee = eZ.getUint16(aG),
|
||||
aG += 2,
|
||||
sm(ee);
|
||||
case 198:
|
||||
return ee = eZ.getUint32(aG),
|
||||
aG += 4,
|
||||
sm(ee);
|
||||
case 199:
|
||||
return sh(eI[aG++]);
|
||||
case 200:
|
||||
return ee = eZ.getUint16(aG),
|
||||
aG += 2,
|
||||
sh(ee);
|
||||
case 201:
|
||||
return ee = eZ.getUint32(aG),
|
||||
aG += 4,
|
||||
sh(ee);
|
||||
case 202:
|
||||
if (ee = eZ.getFloat32(aG),
|
||||
aW.useFloat32 > 2) {
|
||||
var ev = sT[(127 & eI[aG]) << 1 | eI[aG + 1] >> 7];
|
||||
return aG += 4,
|
||||
(ev * ee + (ee > 0 ? .5 : -.5) >> 0) / ev
|
||||
}
|
||||
return aG += 4,
|
||||
ee;
|
||||
case 203:
|
||||
return ee = eZ.getFloat64(aG),
|
||||
aG += 8,
|
||||
ee;
|
||||
case 204:
|
||||
return eI[aG++];
|
||||
case 205:
|
||||
return ee = eZ.getUint16(aG),
|
||||
aG += 2,
|
||||
ee;
|
||||
case 206:
|
||||
return ee = eZ.getUint32(aG),
|
||||
aG += 4,
|
||||
ee;
|
||||
case 207:
|
||||
return "number" === aW.int64AsType ? ee = 4294967296 * eZ.getUint32(aG) + eZ.getUint32(aG + 4) : "string" === aW.int64AsType ? ee = eZ.getBigUint64(aG).toString() : "auto" === aW.int64AsType ? (ee = eZ.getBigUint64(aG)) <= BigInt(2) << BigInt(52) && (ee = Number(ee)) : ee = eZ.getBigUint64(aG),
|
||||
aG += 8,
|
||||
ee;
|
||||
case 208:
|
||||
return eZ.getInt8(aG++);
|
||||
case 209:
|
||||
return ee = eZ.getInt16(aG),
|
||||
aG += 2,
|
||||
ee;
|
||||
case 210:
|
||||
return ee = eZ.getInt32(aG),
|
||||
aG += 4,
|
||||
ee;
|
||||
case 211:
|
||||
return "number" === aW.int64AsType ? ee = 4294967296 * eZ.getInt32(aG) + eZ.getUint32(aG + 4) : "string" === aW.int64AsType ? ee = eZ.getBigInt64(aG).toString() : "auto" === aW.int64AsType ? (ee = eZ.getBigInt64(aG)) >= BigInt(-2) << BigInt(52) && ee <= BigInt(2) << BigInt(52) && (ee = Number(ee)) : ee = eZ.getBigInt64(aG),
|
||||
aG += 8,
|
||||
ee;
|
||||
case 212:
|
||||
if (114 == (ee = eI[aG++]))
|
||||
return sC(63 & eI[aG++]);
|
||||
var eg = aK[ee];
|
||||
if (eg) {
|
||||
if (eg.read)
|
||||
return aG++,
|
||||
eg.read(a5());
|
||||
if (eg.noBuffer)
|
||||
return aG++,
|
||||
eg();
|
||||
return eg(eI.subarray(aG, ++aG))
|
||||
}
|
||||
throw Error("Unknown extension " + ee);
|
||||
case 213:
|
||||
if (114 == (ee = eI[aG]))
|
||||
return aG++,
|
||||
sC(63 & eI[aG++], eI[aG++]);
|
||||
return sh(2);
|
||||
case 214:
|
||||
return sh(4);
|
||||
case 215:
|
||||
return sh(8);
|
||||
case 216:
|
||||
return sh(16);
|
||||
case 217:
|
||||
if (ee = eI[aG++],
|
||||
aX >= aG)
|
||||
return eR.slice(aG - aq, (aG += ee) - aq);
|
||||
return st(ee);
|
||||
case 218:
|
||||
if (ee = eZ.getUint16(aG),
|
||||
aG += 2,
|
||||
aX >= aG)
|
||||
return eR.slice(aG - aq, (aG += ee) - aq);
|
||||
return sn(ee);
|
||||
case 219:
|
||||
if (ee = eZ.getUint32(aG),
|
||||
aG += 4,
|
||||
aX >= aG)
|
||||
return eR.slice(aG - aq, (aG += ee) - aq);
|
||||
return sr(ee);
|
||||
case 220:
|
||||
return ee = eZ.getUint16(aG),
|
||||
aG += 2,
|
||||
si(ee);
|
||||
case 221:
|
||||
return ee = eZ.getUint32(aG),
|
||||
aG += 4,
|
||||
si(ee);
|
||||
case 222:
|
||||
return ee = eZ.getUint16(aG),
|
||||
aG += 2,
|
||||
ss(ee);
|
||||
case 223:
|
||||
return ee = eZ.getUint32(aG),
|
||||
aG += 4,
|
||||
ss(ee);
|
||||
default:
|
||||
if (et >= 224)
|
||||
return et - 256;
|
||||
if (void 0 === et) {
|
||||
var eC = Error("Unexpected end of MessagePack data");
|
||||
throw eC.incomplete = !0,
|
||||
eC
|
||||
}
|
||||
throw Error("Unknown MessagePack token " + et)
|
||||
}
|
||||
}
|
||||
|
||||
sn = so
|
||||
var a6 = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
|
||||
const decrypt = (ee) => {
|
||||
for (var et, en, eo = r9(ee), ei = eo.length, ec = new Uint8Array(ei), eu = 0; eu < ei; eu++) {
|
||||
var ed = eo.charCodeAt(eu);
|
||||
ec[eu] = ed
|
||||
}
|
||||
en = {}
|
||||
et = ec;
|
||||
et.buffer || et.constructor !== ArrayBuffer || (et = "undefined" != typeof Buffer ? Buffer.from(et) : new Uint8Array(et)),
|
||||
"object" == typeof en ? (eO = en.end || et.length,
|
||||
aG = en.start || 0) : (aG = 0,
|
||||
eO = en > -1 ? en : et.length),
|
||||
aX = 0,
|
||||
eR = null,
|
||||
eD = null,
|
||||
eI = et;
|
||||
eM = new TextDecoder("utf-8");
|
||||
eL = null
|
||||
|
||||
eP = [];
|
||||
eZ = et.dataView || (et.dataView = new DataView(et.buffer, et.byteOffset, et.byteLength))
|
||||
res = a3(en)
|
||||
// ec = ecc;
|
||||
// rA = 0;
|
||||
// rB = 0;
|
||||
// ed = [];
|
||||
// eu = ec.length;
|
||||
// rj = {
|
||||
// "useRecords": false,
|
||||
// "mapsAsObjects": true
|
||||
// }
|
||||
// // ei 是 TextDecoder
|
||||
// ei = new TextDecoder("utf-8");
|
||||
// eh = new DataView(ec.buffer, ec.byteOffset, ec.byteLength);
|
||||
// ec.dataView = eh;
|
||||
// let res = rK()
|
||||
const replacer = (key, value) => {
|
||||
if (typeof value === 'bigint') {
|
||||
return value.toString();
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
res = JSON.stringify(res, replacer);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// console.log(generate_mid())
|
||||
// console.log(generate_uuid())
|
||||
// console.log(generate_device_id())
|
||||
// console.log(generate_sign('5f65b00f83994987e334f97360d69557', '{"sessionTypes":"1,19"}'))
|
||||
// let msg = "ggGLAYEBtTIyMDI2NDA5MTgwNzlAZ29vZmlzaAKzNDc4MTI4NzAwMDBAZ29vZmlzaAOxMzQwMjM5MTQ3MjUwMy5QTk0EAAXPAAABlYW04bIGggFlA4UBoAKjMTExA6AEAQXaADR7ImF0VXNlcnMiOltdLCJjb250ZW50VHlwZSI6MSwidGV4dCI6eyJ0ZXh0IjoiMTExIn19BwIIAQkACoupX3BsYXRmb3Jtp2FuZHJvaWSmYml6VGFn2gBBeyJzb3VyY2VJZCI6IlM6MSIsIm1lc3NhZ2VJZCI6ImYzNjkwMmVmZjQ1NDQ1YmRiMmQxYjBmZDE2OGY4MjY0In2sZGV0YWlsTm90aWNlozExMadleHRKc29u2gBLeyJxdWlja1JlcGx5IjoiMSIsIm1lc3NhZ2VJZCI6ImYzNjkwMmVmZjQ1NDQ1YmRiMmQxYjBmZDE2OGY4MjY0IiwidGFnIjoidSJ9r3JlbWluZGVyQ29udGVudKMxMTGucmVtaW5kZXJOb3RpY2W15Y+R5p2l5LiA5p2h5paw5raI5oGvrXJlbWluZGVyVGl0bGWmc2hh5L+uq3JlbWluZGVyVXJs2gCbZmxlYW1hcmtldDovL21lc3NhZ2VfY2hhdD9pdGVtSWQ9ODk3NzQyNzQ4MDExJnBlZXJVc2VySWQ9MjIwMjY0MDkxODA3OSZwZWVyVXNlck5pY2s9dCoqKjEmc2lkPTQ3ODEyODcwMDAwJm1lc3NhZ2VJZD1mMzY5MDJlZmY0NTQ0NWJkYjJkMWIwZmQxNjhmODI2NCZhZHY9bm+sc2VuZGVyVXNlcklkrTIyMDI2NDA5MTgwNzmuc2VuZGVyVXNlclR5cGWhMKtzZXNzaW9uVHlwZaExDAEDgahuZWVkUHVzaKR0cnVl";
|
||||
//
|
||||
// msg = "ggGLAYEBsjMxNDk2MzcwNjNAZ29vZmlzaAKzNDc5ODMzODkwOTZAZ29vZmlzaAOxMzQxNjU2NTI3NDU0Mi5QTk0EAAXPAAABlbKji20GggFlA4UBoAK6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl0DoAQaBdoEKnsiY29udGVudFR5cGUiOjI2LCJkeENhcmQiOnsiaXRlbSI6eyJtYWluIjp7ImNsaWNrUGFyYW0iOnsiYXJnMSI6Ik1zZ0NhcmQiLCJhcmdzIjp7InNvdXJjZSI6ImltIiwidGFza19pZCI6IjNleFFKSE9UbVBVMSIsIm1zZ19pZCI6ImNjOGJjMmRmN2M5MzRkZjA4NmUwNTY3Y2I2OWYxNTczIn19LCJleENvbnRlbnQiOnsiYmdDb2xvciI6IiNGRkZGRkYiLCJidXR0b24iOnsiYmdDb2xvciI6IiNGRkU2MEYiLCJib3JkZXJDb2xvciI6IiNGRkU2MEYiLCJjbGlja1BhcmFtIjp7ImFyZzEiOiJNc2dDYXJkQWN0aW9uIiwiYXJncyI6eyJzb3VyY2UiOiJpbSIsInRhc2tfaWQiOiIzZXhRSkhPVG1QVTEiLCJtc2dfaWQiOiJjYzhiYzJkZjdjOTM0ZGYwODZlMDU2N2NiNjlmMTU3MyJ9fSwiZm9udENvbG9yIjoiIzMzMzMzMyIsInRhcmdldFVybCI6ImZsZWFtYXJrZXQ6Ly9hZGp1c3RfcHJpY2U/Zmx1dHRlcj10cnVlJmJpek9yZGVySWQ9MjUwMzY4ODEyNjM1NjYzNjM3MCIsInRleHQiOiLkv67mlLnku7fmoLwifSwiZGVzYyI6Iuivt+WPjOaWueayn+mAmuWPiuaXtuehruiupOS7t+agvCIsImRlc2NDb2xvciI6IiNBM0EzQTMiLCJ0aXRsZSI6IuaIkeW3suaLjeS4i++8jOW+heS7mOasviIsInVwZ3JhZGUiOnsidGFyZ2V0VXJsIjoiaHR0cHM6Ly9oNS5tLmdvb2Zpc2guY29tL2FwcC9pZGxlRmlzaC1GMmUvZm0tZG93bmxhb2QvaG9tZS5odG1sP25vUmVkcmllY3Q9dHJ1ZSZjYW5CYWNrPXRydWUmY2hlY2tWZXJzaW9uPXRydWUiLCJ2ZXJzaW9uIjoiNy43LjkwIn19LCJ0YXJnZXRVcmwiOiJmbGVhbWFya2V0Oi8vb3JkZXJfZGV0YWlsP2lkPTI1MDM2ODgxMjYzNTY2MzYzNzAmcm9sZT1zZWxsZXIifX0sInRlbXBsYXRlIjp7Im5hbWUiOiJpZGxlZmlzaF9tZXNzYWdlX3RyYWRlX2NoYXRfY2FyZCIsInVybCI6Imh0dHBzOi8vZGluYW1pY3guYWxpYmFiYXVzZXJjb250ZW50LmNvbS9wdWIvaWRsZWZpc2hfbWVzc2FnZV90cmFkZV9jaGF0X2NhcmQvMTY2NzIyMjA1Mjc2Ny9pZGxlZmlzaF9tZXNzYWdlX3RyYWRlX2NoYXRfY2FyZC56aXAiLCJ2ZXJzaW9uIjoiMTY2NzIyMjA1Mjc2NyJ9fX0HAQgBCQAK3gAQpmJpelRhZ9oAe3sic291cmNlSWQiOiJDMkM6M2V4UUpIT1RtUFUxIiwidGFza05hbWUiOiLlt7Lmi43kuItf5pyq5LuY5qy+X+WNluWutiIsIm1hdGVyaWFsSWQiOiIzZXhRSkhPVG1QVTEiLCJ0YXNrSWQiOiIzZXhRSkhPVG1QVTEifbFjbG9zZVB1c2hSZWNlaXZlcqVmYWxzZbFjbG9zZVVucmVhZE51bWJlcqVmYWxzZaxkZXRhaWxOb3RpY2W6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl2nZXh0SnNvbtoBr3sibXNnQXJncyI6eyJ0YXNrX2lkIjoiM2V4UUpIT1RtUFUxIiwic291cmNlIjoiaW0iLCJtc2dfaWQiOiJjYzhiYzJkZjdjOTM0ZGYwODZlMDU2N2NiNjlmMTU3MyJ9LCJxdWlja1JlcGx5IjoiMSIsIm1zZ0FyZzEiOiJNc2dDYXJkIiwidXBkYXRlS2V5IjoiNDc5ODMzODkwOTY6MjUwMzY4ODEyNjM1NjYzNjM3MDoxX25vdF9wYXlfc2VsbGVyIiwibWVzc2FnZUlkIjoiY2M4YmMyZGY3YzkzNGRmMDg2ZTA1NjdjYjY5ZjE1NzMiLCJtdWx0aUNoYW5uZWwiOnsiaHVhd2VpIjoiRVhQUkVTUyIsInhpYW9taSI6IjEwODAwMCIsIm9wcG8iOiJFWFBSRVNTIiwiaG9ub3IiOiJOT1JNQUwiLCJhZ29vIjoicHJvZHVjdCIsInZpdm8iOiJPUkRFUiJ9LCJjb250ZW50VHlwZSI6IjI2IiwiY29ycmVsYXRpb25Hcm91cElkIjoiM2V4UUpIT1RtUFUxX0ZGcjRHT1NuOE9RbyJ9qHJlY2VpdmVyrTIyMDI2NDA5MTgwNzmrcmVkUmVtaW5kZXKy562J5b6F5Lmw5a625LuY5qy+sHJlZFJlbWluZGVyU3R5bGWhMa9yZW1pbmRlckNvbnRlbnS6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl2ucmVtaW5kZXJOb3RpY2W75Lmw5a625bey5ouN5LiL77yM5b6F5LuY5qy+rXJlbWluZGVyVGl0bGW75Lmw5a625bey5ouN5LiL77yM5b6F5LuY5qy+q3JlbWluZGVyVXJs2gCaZmxlYW1hcmtldDovL21lc3NhZ2VfY2hhdD9pdGVtSWQ9OTAwMDUyNjQ0Mjc3JnBlZXJVc2VySWQ9MzE0OTYzNzA2MyZwZWVyVXNlck5pY2s955S3KioqeSZzaWQ9NDc5ODMzODkwOTYmbWVzc2FnZUlkPWNjOGJjMmRmN2M5MzRkZjA4NmUwNTY3Y2I2OWYxNTczJmFkdj1ub6xzZW5kZXJVc2VySWSqMzE0OTYzNzA2M65zZW5kZXJVc2VyVHlwZaEwq3Nlc3Npb25UeXBloTGqdXBkYXRlSGVhZKR0cnVlDAEDgahuZWVkUHVzaKR0cnVl"
|
||||
// msg = "hAGzNDc5ODMzODkwOTZAZ29vZmlzaAIBA4KrcmVkUmVtaW5kZXKy562J5b6F5Lmw5a625LuY5qy+sHJlZFJlbWluZGVyU3R5bGWhMQTPAAABlbMlNng="
|
||||
// res = decrypt(msg);
|
||||
// console.log(res);
|
||||
Reference in new issue
Block a user