Files
gemini-balance/app/static/css/config_editor.css
snaily 619f81cce4 feat: 添加Web配置编辑器界面
新增 `/config` 路由,提供一个可视化的配置编辑页面 (`config_editor.html`)。
用户现在可以通过网页界面管理:
- API 密钥(包括批量添加和重置确认)
- API 基础配置 (允许的令牌, 认证令牌, 基础URL, 最大失败次数, 超时)
- 模型相关配置 (测试模型, 图像/搜索/过滤模型列表, 代码执行/搜索链接/思考过程开关)
- 图像生成配置 (付费密钥, 模型, 上传提供商及相关密钥/URL)
- 流式输出优化器配置 (开关, 延迟, 阈值, 分块大小)

同时更新了 `/keys` 页面 (`keys_status.html`):
- 页面主标题更改为 "Gemini Balance"。
- 添加了顶部导航选项卡,方便在 "配置编辑" (`/config`) 和 "密钥管理" (`/keys`) 之间切换。
2025-04-05 21:52:58 +08:00

793 lines
15 KiB
CSS

* {
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 900px;
width: 95%;
background: rgba(255, 255, 255, 0.95);
padding: 40px;
border-radius: 20px;
box-shadow: 0 15px 35px rgba(0,0,0,0.2);
backdrop-filter: blur(10px);
position: relative;
margin: 20px auto;
overflow-y: auto;
max-height: calc(100vh - 40px);
scrollbar-width: none;
-ms-overflow-style: none;
}
.container::-webkit-scrollbar {
display: none;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
font-weight: 700;
font-size: 32px;
position: relative;
padding-bottom: 15px;
}
h1::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100px;
height: 4px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 2px;
}
h2 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 1.5em;
padding-bottom: 10px;
border-bottom: 2px solid rgba(0,0,0,0.1);
display: flex;
align-items: center;
gap: 10px;
}
/* 导航标签样式 */
.nav-tabs {
display: flex;
justify-content: center;
margin-bottom: 30px;
border-bottom: 2px solid rgba(0,0,0,0.1);
padding-bottom: 10px;
width: 100%;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
.tab-link {
color: #2c3e50;
text-decoration: none;
padding: 12px 25px;
border-radius: 8px 8px 0 0;
font-weight: bold;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
position: relative;
margin: 0 5px;
flex: 1;
text-align: center;
}
.tab-link:hover {
background: rgba(102, 126, 234, 0.1);
}
.tab-link.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
box-shadow: 0 5px 15px rgba(118, 75, 162, 0.3);
}
.tab-link.active::after {
content: '';
position: absolute;
bottom: -12px;
left: 0;
width: 100%;
height: 4px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 2px;
}
.tab-link i {
font-size: 16px;
}
.config-tabs {
display: flex;
justify-content: center;
margin-bottom: 30px;
flex-wrap: wrap;
gap: 10px;
}
.tab-btn {
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(0,0,0,0.1);
padding: 10px 20px;
border-radius: 30px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
color: #2c3e50;
font-size: 14px;
}
.tab-btn:hover {
background: rgba(255, 255, 255, 0.9);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.tab-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
box-shadow: 0 5px 15px rgba(118, 75, 162, 0.3);
}
.config-section {
display: none;
animation: fadeIn 0.5s ease forwards;
background: rgba(248, 249, 250, 0.9);
padding: 25px;
border-radius: 15px;
margin-bottom: 30px;
border: 1px solid rgba(0,0,0,0.1);
}
.config-section.active {
display: block;
}
.config-item {
margin-bottom: 25px;
position: relative;
}
.config-item label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #2c3e50;
}
.config-item input[type="text"],
.config-item input[type="number"],
.config-item select {
width: 100%;
padding: 12px 15px;
border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px;
font-size: 16px;
transition: all 0.3s ease;
background: white;
box-sizing: border-box;
}
.config-item input[type="text"]:focus,
.config-item input[type="number"]:focus,
.config-item select:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
outline: none;
}
.help-text {
display: block;
margin-top: 5px;
font-size: 12px;
color: #7f8c8d;
}
.array-container {
border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px;
padding: 15px;
background: white;
/* margin-bottom: 10px; */ /* Removed as controls are now outside */
width: 100%;
min-height: 60px;
}
/* Specific style for API Keys container to make it scrollable */
#API_KEYS_container {
max-height: 300px; /* Adjust this value as needed */
overflow-y: auto;
/* Optional: Add some padding to the right for the scrollbar */
padding-right: 5px;
margin-bottom: 10px; /* Add margin below the container */
}
/* Search Input Styles */
.search-container {
margin-bottom: 10px;
}
#apiKeySearchInput {
width: 100%;
padding: 10px 15px;
border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px;
font-size: 14px;
box-sizing: border-box;
}
#apiKeySearchInput:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
outline: none;
}
.array-item {
display: flex;
margin-bottom: 10px;
gap: 10px;
background-color: rgba(248, 249, 250, 0.5);
padding: 8px;
border-radius: 8px;
border: 1px dashed rgba(102, 126, 234, 0.3);
transition: all 0.3s ease;
}
.array-item:hover {
background-color: rgba(248, 249, 250, 0.8);
border-color: rgba(102, 126, 234, 0.5);
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.array-item input {
flex: 1;
box-sizing: border-box;
padding: 12px 15px;
border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px;
font-size: 16px;
transition: all 0.3s ease;
background: white;
}
.array-item input:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
outline: none;
}
.array-controls {
display: flex;
justify-content: flex-end;
margin-top: 10px; /* Increase margin-top for spacing */
}
.add-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 8px 15px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 5px;
}
.add-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(118, 75, 162, 0.3);
}
.remove-btn {
background: #e74c3c;
color: white;
border: none;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.remove-btn:hover {
background: #c0392b;
transform: scale(1.1);
}
.toggle {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: nowrap;
}
.toggle label {
margin-bottom: 0;
flex: 1;
padding-right: 15px;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
flex-shrink: 0;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
input:checked + .toggle-slider {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
input:focus + .toggle-slider {
box-shadow: 0 0 1px #667eea;
}
input:checked + .toggle-slider:before {
transform: translateX(26px);
}
.form-actions {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 40px;
}
.save-btn, .reset-btn {
padding: 12px 30px;
border-radius: 30px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
border: none;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.save-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.save-btn:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(118, 75, 162, 0.3);
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
}
.save-btn:active {
transform: translateY(1px);
box-shadow: 0 5px 10px rgba(118, 75, 162, 0.2);
}
.reset-btn {
background: linear-gradient(135deg, #e0e0e0 0%, #bdc3c7 100%);
color: #2c3e50;
}
.reset-btn:hover {
background: linear-gradient(135deg, #bdc3c7 0%, #e0e0e0 100%);
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.reset-btn:active {
transform: translateY(1px);
box-shadow: 0 5px 10px rgba(0,0,0,0.05);
}
.save-status {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(39, 174, 96, 0.9);
color: white;
padding: 10px 20px;
border-radius: 30px;
display: flex;
align-items: center;
gap: 10px;
font-weight: bold;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
z-index: 1000;
opacity: 0;
transition: all 0.3s ease;
}
.save-status.show {
opacity: 1;
}
.save-status.error {
background: rgba(231, 76, 60, 0.9);
}
.provider-config {
display: none;
}
.provider-config.active {
display: block;
}
.notification {
position: fixed;
bottom: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 8px;
background: rgba(0,0,0,0.8);
color: white;
font-weight: bold;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
z-index: 1000;
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease;
}
.notification.show {
opacity: 1;
transform: translateY(0);
}
.notification.success {
background: rgba(39, 174, 96, 0.9);
}
.notification.error {
background: rgba(231, 76, 60, 0.9);
}
.scroll-buttons {
position: fixed;
right: 20px;
bottom: 20px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 1000;
}
.scroll-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
width: 40px;
height: 40px;
border: none;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
transition: all 0.3s ease;
backdrop-filter: blur(5px);
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
.scroll-btn:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
transform: scale(1.1);
}
.scroll-btn:active {
transform: scale(0.95);
}
.refresh-btn {
position: fixed;
top: 20px;
right: 20px;
z-index: 1000;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.refresh-btn:hover {
transform: scale(1.05);
box-shadow: 0 8px 20px rgba(118, 75, 162, 0.3);
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
}
.refresh-btn:active {
transform: scale(0.95);
}
.refresh-btn i {
transition: transform 0.5s ease;
}
.refresh-btn.loading i {
animation: spin 1s linear infinite;
}
.copyright {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: rgba(255, 255, 255, 0.9);
padding: 10px 0;
text-align: center;
font-size: 14px;
color: #2c3e50;
backdrop-filter: blur(5px);
border-top: 1px solid rgba(0,0,0,0.1);
}
.copyright a {
color: #764ba2;
text-decoration: none;
transition: color 0.3s ease;
}
.copyright a:hover {
color: #667eea;
}
.copyright img {
width: 20px;
height: 20px;
border-radius: 50%;
vertical-align: middle;
margin-right: 5px;
}
/* Modal Styles */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1001; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgba(0,0,0,0.6); /* Black w/ opacity */
backdrop-filter: blur(5px);
}
.modal.show {
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: #fefefe;
padding: 30px;
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
max-width: 600px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
position: relative;
animation: slideIn 0.3s ease-out;
margin: auto;
}
.modal-content h2 {
margin-top: 0;
color: #2c3e50;
border-bottom: none; /* Remove border from h2 inside modal */
padding-bottom: 0;
}
.modal-content p {
color: #7f8c8d;
font-size: 14px;
margin-bottom: 15px;
}
.modal-content textarea {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 8px;
margin-bottom: 20px;
font-family: monospace; /* Use monospace for keys */
font-size: 14px;
resize: vertical; /* Allow vertical resizing */
min-height: 150px;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 15px;
margin-top: 10px;
}
.close-btn {
color: #aaa;
position: absolute;
top: 10px;
right: 20px;
font-size: 28px;
font-weight: bold;
line-height: 1;
}
.close-btn:hover,
.close-btn:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
@keyframes slideIn {
from { transform: translateY(-50px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@media (max-width: 768px) {
.container {
width: 100%;
padding: 20px;
margin: 10px auto;
}
body {
padding: 10px;
}
h1 {
font-size: 24px;
}
.nav-tabs {
flex-direction: column;
align-items: center;
gap: 10px;
}
.tab-link {
width: 100%;
justify-content: center;
border-radius: 8px;
}
.tab-link.active::after {
display: none;
}
.config-tabs {
flex-direction: column;
}
.tab-btn {
width: 100%;
text-align: center;
}
.toggle {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.form-actions {
flex-direction: column;
gap: 15px;
}
.save-btn, .reset-btn {
width: 100%;
justify-content: center;
}
.scroll-buttons {
right: 10px;
bottom: 10px;
}
.scroll-btn {
width: 35px;
height: 35px;
font-size: 16px;
}
.refresh-btn {
top: 10px;
right: 10px;
padding: 8px 16px;
font-size: 12px;
}
}
@media (max-width: 480px) {
.container {
padding: 15px;
}
h1 {
font-size: 20px;
}
.config-section {
padding: 15px;
}
.config-item input[type="text"],
.config-item input[type="number"],
.config-item select {
padding: 10px;
font-size: 14px;
}
}