mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-20 03:30:50 +08:00
feat(app.js, i18n, index.html, styles): implement Vertex AI credential import feature
- Added functionality for importing Vertex AI credentials, including file selection and upload handling in the CLIProxyManager. - Updated UI components in index.html to support the new Vertex AI credential import feature. - Enhanced internationalization strings to provide appropriate labels and messages for the Vertex AI import functionality in both English and Chinese. - Introduced new styles for the Vertex AI credential import section to ensure a consistent user experience.
This commit is contained in:
159
app.js
159
app.js
@@ -31,6 +31,13 @@ class CLIProxyManager {
|
|||||||
this.currentAuthFileFilter = 'all';
|
this.currentAuthFileFilter = 'all';
|
||||||
this.cachedAuthFiles = [];
|
this.cachedAuthFiles = [];
|
||||||
|
|
||||||
|
// Vertex AI credential import state
|
||||||
|
this.vertexImportState = {
|
||||||
|
file: null,
|
||||||
|
loading: false,
|
||||||
|
result: null
|
||||||
|
};
|
||||||
|
|
||||||
// 主题管理
|
// 主题管理
|
||||||
this.currentTheme = 'light';
|
this.currentTheme = 'light';
|
||||||
|
|
||||||
@@ -610,6 +617,24 @@ class CLIProxyManager {
|
|||||||
authFileInput.addEventListener('change', (e) => this.handleFileUpload(e));
|
authFileInput.addEventListener('change', (e) => this.handleFileUpload(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vertex AI credential import
|
||||||
|
const vertexSelectFile = document.getElementById('vertex-select-file');
|
||||||
|
const vertexFileInput = document.getElementById('vertex-file-input');
|
||||||
|
const vertexImportBtn = document.getElementById('vertex-import-btn');
|
||||||
|
|
||||||
|
if (vertexSelectFile) {
|
||||||
|
vertexSelectFile.addEventListener('click', () => this.openVertexFilePicker());
|
||||||
|
}
|
||||||
|
if (vertexFileInput) {
|
||||||
|
vertexFileInput.addEventListener('change', (e) => this.handleVertexFileSelection(e));
|
||||||
|
}
|
||||||
|
if (vertexImportBtn) {
|
||||||
|
vertexImportBtn.addEventListener('click', () => this.importVertexCredential());
|
||||||
|
}
|
||||||
|
this.updateVertexFileDisplay();
|
||||||
|
this.updateVertexImportButtonState();
|
||||||
|
this.renderVertexImportResult(this.vertexImportState.result);
|
||||||
|
|
||||||
// Codex OAuth
|
// Codex OAuth
|
||||||
const codexOauthBtn = document.getElementById('codex-oauth-btn');
|
const codexOauthBtn = document.getElementById('codex-oauth-btn');
|
||||||
const codexOpenLink = document.getElementById('codex-open-link');
|
const codexOpenLink = document.getElementById('codex-open-link');
|
||||||
@@ -3914,6 +3939,9 @@ class CLIProxyManager {
|
|||||||
case 'iflow':
|
case 'iflow':
|
||||||
typeDisplayKey = 'auth_files.type_iflow';
|
typeDisplayKey = 'auth_files.type_iflow';
|
||||||
break;
|
break;
|
||||||
|
case 'vertex':
|
||||||
|
typeDisplayKey = 'auth_files.type_vertex';
|
||||||
|
break;
|
||||||
case 'empty':
|
case 'empty':
|
||||||
typeDisplayKey = 'auth_files.type_empty';
|
typeDisplayKey = 'auth_files.type_empty';
|
||||||
break;
|
break;
|
||||||
@@ -3992,6 +4020,7 @@ class CLIProxyManager {
|
|||||||
{ type: 'claude', labelKey: 'auth_files.filter_claude' },
|
{ type: 'claude', labelKey: 'auth_files.filter_claude' },
|
||||||
{ type: 'codex', labelKey: 'auth_files.filter_codex' },
|
{ type: 'codex', labelKey: 'auth_files.filter_codex' },
|
||||||
{ type: 'iflow', labelKey: 'auth_files.filter_iflow' },
|
{ type: 'iflow', labelKey: 'auth_files.filter_iflow' },
|
||||||
|
{ type: 'vertex', labelKey: 'auth_files.filter_vertex' },
|
||||||
{ type: 'empty', labelKey: 'auth_files.filter_empty' }
|
{ type: 'empty', labelKey: 'auth_files.filter_empty' }
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -4233,6 +4262,136 @@ class CLIProxyManager {
|
|||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Vertex AI Credential Import =====
|
||||||
|
openVertexFilePicker() {
|
||||||
|
const fileInput = document.getElementById('vertex-file-input');
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleVertexFileSelection(event) {
|
||||||
|
const fileInput = event?.target;
|
||||||
|
const file = fileInput?.files?.[0] || null;
|
||||||
|
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file && !file.name.toLowerCase().endsWith('.json')) {
|
||||||
|
this.showNotification(i18n.t('vertex_import.file_required'), 'error');
|
||||||
|
this.vertexImportState.file = null;
|
||||||
|
this.updateVertexFileDisplay();
|
||||||
|
this.updateVertexImportButtonState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vertexImportState.file = file;
|
||||||
|
this.updateVertexFileDisplay(file ? file.name : '');
|
||||||
|
this.updateVertexImportButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVertexFileDisplay(filename = '') {
|
||||||
|
const displayInput = document.getElementById('vertex-file-display');
|
||||||
|
if (!displayInput) return;
|
||||||
|
displayInput.value = filename || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVertexImportButtonState() {
|
||||||
|
const importBtn = document.getElementById('vertex-import-btn');
|
||||||
|
if (!importBtn) return;
|
||||||
|
const disabled = !this.vertexImportState.file || this.vertexImportState.loading;
|
||||||
|
importBtn.disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
async importVertexCredential() {
|
||||||
|
if (!this.vertexImportState.file) {
|
||||||
|
this.showNotification(i18n.t('vertex_import.file_required'), 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locationInput = document.getElementById('vertex-location');
|
||||||
|
const location = locationInput ? locationInput.value.trim() : '';
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', this.vertexImportState.file, this.vertexImportState.file.name);
|
||||||
|
if (location) {
|
||||||
|
formData.append('location', location);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.vertexImportState.loading = true;
|
||||||
|
this.updateVertexImportButtonState();
|
||||||
|
|
||||||
|
const response = await fetch(`${this.apiUrl}/vertex/import`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${this.managementKey}`
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let errorMessage = `HTTP ${response.status}`;
|
||||||
|
try {
|
||||||
|
const errorData = await response.json();
|
||||||
|
errorMessage = errorData?.message || errorData?.error || errorMessage;
|
||||||
|
} catch (parseError) {
|
||||||
|
const text = await response.text();
|
||||||
|
if (text) {
|
||||||
|
errorMessage = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
this.showNotification(i18n.t('vertex_import.success'), 'success');
|
||||||
|
this.renderVertexImportResult(result);
|
||||||
|
this.vertexImportState.file = null;
|
||||||
|
this.updateVertexFileDisplay();
|
||||||
|
this.clearCache();
|
||||||
|
await this.loadAuthFiles();
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`${i18n.t('notification.upload_failed')}: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
this.vertexImportState.loading = false;
|
||||||
|
this.updateVertexImportButtonState();
|
||||||
|
const fileInput = document.getElementById('vertex-file-input');
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderVertexImportResult(result = null) {
|
||||||
|
const container = document.getElementById('vertex-import-result');
|
||||||
|
const projectEl = document.getElementById('vertex-result-project');
|
||||||
|
const emailEl = document.getElementById('vertex-result-email');
|
||||||
|
const locationEl = document.getElementById('vertex-result-location');
|
||||||
|
const fileEl = document.getElementById('vertex-result-file');
|
||||||
|
|
||||||
|
if (!container || !projectEl || !emailEl || !locationEl || !fileEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
this.vertexImportState.result = null;
|
||||||
|
container.style.display = 'none';
|
||||||
|
projectEl.textContent = '-';
|
||||||
|
emailEl.textContent = '-';
|
||||||
|
locationEl.textContent = '-';
|
||||||
|
fileEl.textContent = '-';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vertexImportState.result = result;
|
||||||
|
projectEl.textContent = result.project_id || '-';
|
||||||
|
emailEl.textContent = result.email || '-';
|
||||||
|
locationEl.textContent = result.location || 'us-central1';
|
||||||
|
fileEl.textContent = result['auth-file'] || result.auth_file || '-';
|
||||||
|
container.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
// 上传认证文件
|
// 上传认证文件
|
||||||
uploadAuthFile() {
|
uploadAuthFile() {
|
||||||
document.getElementById('auth-file-input').click();
|
document.getElementById('auth-file-input').click();
|
||||||
|
|||||||
42
i18n.js
42
i18n.js
@@ -217,7 +217,7 @@ const i18n = {
|
|||||||
// 认证文件管理
|
// 认证文件管理
|
||||||
'auth_files.title': '认证文件管理',
|
'auth_files.title': '认证文件管理',
|
||||||
'auth_files.title_section': '认证文件',
|
'auth_files.title_section': '认证文件',
|
||||||
'auth_files.description': '这里管理 Qwen 和 Gemini 的认证配置文件。上传 JSON 格式的认证文件以启用相应的 AI 服务。',
|
'auth_files.description': '这里集中管理 CLI Proxy 支持的所有 JSON 认证文件(如 Qwen、Gemini、Vertex 等),上传后即可在运行时启用相应的 AI 服务。',
|
||||||
'auth_files.upload_button': '上传文件',
|
'auth_files.upload_button': '上传文件',
|
||||||
'auth_files.delete_all_button': '删除全部',
|
'auth_files.delete_all_button': '删除全部',
|
||||||
'auth_files.empty_title': '暂无认证文件',
|
'auth_files.empty_title': '暂无认证文件',
|
||||||
@@ -246,6 +246,7 @@ const i18n = {
|
|||||||
'auth_files.filter_claude': 'Claude',
|
'auth_files.filter_claude': 'Claude',
|
||||||
'auth_files.filter_codex': 'Codex',
|
'auth_files.filter_codex': 'Codex',
|
||||||
'auth_files.filter_iflow': 'iFlow',
|
'auth_files.filter_iflow': 'iFlow',
|
||||||
|
'auth_files.filter_vertex': 'Vertex',
|
||||||
'auth_files.filter_empty': '空文件',
|
'auth_files.filter_empty': '空文件',
|
||||||
'auth_files.filter_unknown': '其他',
|
'auth_files.filter_unknown': '其他',
|
||||||
'auth_files.type_qwen': 'Qwen',
|
'auth_files.type_qwen': 'Qwen',
|
||||||
@@ -255,8 +256,26 @@ const i18n = {
|
|||||||
'auth_files.type_claude': 'Claude',
|
'auth_files.type_claude': 'Claude',
|
||||||
'auth_files.type_codex': 'Codex',
|
'auth_files.type_codex': 'Codex',
|
||||||
'auth_files.type_iflow': 'iFlow',
|
'auth_files.type_iflow': 'iFlow',
|
||||||
|
'auth_files.type_vertex': 'Vertex',
|
||||||
'auth_files.type_empty': '空文件',
|
'auth_files.type_empty': '空文件',
|
||||||
'auth_files.type_unknown': '其他',
|
'auth_files.type_unknown': '其他',
|
||||||
|
'vertex_import.title': 'Vertex AI 凭证导入',
|
||||||
|
'vertex_import.description': '上传 Google 服务账号 JSON,使用 CLI vertex-import 同步规则写入 auth-dir/vertex-<project>.json。',
|
||||||
|
'vertex_import.location_label': '目标区域 (可选)',
|
||||||
|
'vertex_import.location_placeholder': 'us-central1',
|
||||||
|
'vertex_import.location_hint': '留空表示使用默认区域 us-central1。',
|
||||||
|
'vertex_import.file_label': '服务账号密钥 JSON',
|
||||||
|
'vertex_import.file_hint': '仅支持 Google Cloud service account key JSON 文件,私钥会自动规范化。',
|
||||||
|
'vertex_import.file_placeholder': '尚未选择文件',
|
||||||
|
'vertex_import.choose_file': '选择文件',
|
||||||
|
'vertex_import.import_button': '导入 Vertex 凭证',
|
||||||
|
'vertex_import.file_required': '请先选择 .json 凭证文件',
|
||||||
|
'vertex_import.success': 'Vertex 凭证导入成功',
|
||||||
|
'vertex_import.result_title': '凭证已保存',
|
||||||
|
'vertex_import.result_project': '项目 ID',
|
||||||
|
'vertex_import.result_email': '服务账号',
|
||||||
|
'vertex_import.result_location': '区域',
|
||||||
|
'vertex_import.result_file': '存储文件',
|
||||||
|
|
||||||
|
|
||||||
// Codex OAuth
|
// Codex OAuth
|
||||||
@@ -685,7 +704,7 @@ const i18n = {
|
|||||||
// Auth files management
|
// Auth files management
|
||||||
'auth_files.title': 'Auth Files Management',
|
'auth_files.title': 'Auth Files Management',
|
||||||
'auth_files.title_section': 'Auth Files',
|
'auth_files.title_section': 'Auth Files',
|
||||||
'auth_files.description': 'Here you can manage authentication configuration files for Qwen and Gemini. Upload JSON format authentication files to enable the corresponding AI services.',
|
'auth_files.description': 'Manage all CLI Proxy JSON auth files here (e.g. Qwen, Gemini, Vertex). Uploading a credential immediately enables the corresponding AI integration.',
|
||||||
'auth_files.upload_button': 'Upload File',
|
'auth_files.upload_button': 'Upload File',
|
||||||
'auth_files.delete_all_button': 'Delete All',
|
'auth_files.delete_all_button': 'Delete All',
|
||||||
'auth_files.empty_title': 'No Auth Files',
|
'auth_files.empty_title': 'No Auth Files',
|
||||||
@@ -714,6 +733,7 @@ const i18n = {
|
|||||||
'auth_files.filter_claude': 'Claude',
|
'auth_files.filter_claude': 'Claude',
|
||||||
'auth_files.filter_codex': 'Codex',
|
'auth_files.filter_codex': 'Codex',
|
||||||
'auth_files.filter_iflow': 'iFlow',
|
'auth_files.filter_iflow': 'iFlow',
|
||||||
|
'auth_files.filter_vertex': 'Vertex',
|
||||||
'auth_files.filter_empty': 'Empty',
|
'auth_files.filter_empty': 'Empty',
|
||||||
'auth_files.filter_unknown': 'Other',
|
'auth_files.filter_unknown': 'Other',
|
||||||
'auth_files.type_qwen': 'Qwen',
|
'auth_files.type_qwen': 'Qwen',
|
||||||
@@ -723,8 +743,26 @@ const i18n = {
|
|||||||
'auth_files.type_claude': 'Claude',
|
'auth_files.type_claude': 'Claude',
|
||||||
'auth_files.type_codex': 'Codex',
|
'auth_files.type_codex': 'Codex',
|
||||||
'auth_files.type_iflow': 'iFlow',
|
'auth_files.type_iflow': 'iFlow',
|
||||||
|
'auth_files.type_vertex': 'Vertex',
|
||||||
'auth_files.type_empty': 'Empty',
|
'auth_files.type_empty': 'Empty',
|
||||||
'auth_files.type_unknown': 'Other',
|
'auth_files.type_unknown': 'Other',
|
||||||
|
'vertex_import.title': 'Vertex AI Credential Import',
|
||||||
|
'vertex_import.description': 'Upload a Google service account JSON to store it as auth-dir/vertex-<project>.json using the same rules as the CLI vertex-import helper.',
|
||||||
|
'vertex_import.location_label': 'Region (optional)',
|
||||||
|
'vertex_import.location_placeholder': 'us-central1',
|
||||||
|
'vertex_import.location_hint': 'Leave empty to use the default region us-central1.',
|
||||||
|
'vertex_import.file_label': 'Service account key JSON',
|
||||||
|
'vertex_import.file_hint': 'Only Google Cloud service account key JSON files are accepted.',
|
||||||
|
'vertex_import.file_placeholder': 'No file selected',
|
||||||
|
'vertex_import.choose_file': 'Choose File',
|
||||||
|
'vertex_import.import_button': 'Import Vertex Credential',
|
||||||
|
'vertex_import.file_required': 'Select a .json credential file first',
|
||||||
|
'vertex_import.success': 'Vertex credential imported successfully',
|
||||||
|
'vertex_import.result_title': 'Credential saved',
|
||||||
|
'vertex_import.result_project': 'Project ID',
|
||||||
|
'vertex_import.result_email': 'Service account',
|
||||||
|
'vertex_import.result_location': 'Region',
|
||||||
|
'vertex_import.result_file': 'Persisted file',
|
||||||
|
|
||||||
// Codex OAuth
|
// Codex OAuth
|
||||||
'auth_login.codex_oauth_title': 'Codex OAuth',
|
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||||
|
|||||||
46
index.html
46
index.html
@@ -458,6 +458,7 @@
|
|||||||
<button class="filter-btn" data-type="claude" data-i18n-text="auth_files.filter_claude">Claude</button>
|
<button class="filter-btn" data-type="claude" data-i18n-text="auth_files.filter_claude">Claude</button>
|
||||||
<button class="filter-btn" data-type="codex" data-i18n-text="auth_files.filter_codex">Codex</button>
|
<button class="filter-btn" data-type="codex" data-i18n-text="auth_files.filter_codex">Codex</button>
|
||||||
<button class="filter-btn" data-type="iflow" data-i18n-text="auth_files.filter_iflow">iFlow</button>
|
<button class="filter-btn" data-type="iflow" data-i18n-text="auth_files.filter_iflow">iFlow</button>
|
||||||
|
<button class="filter-btn" data-type="vertex" data-i18n-text="auth_files.filter_vertex">Vertex</button>
|
||||||
<button class="filter-btn" data-type="empty" data-i18n-text="auth_files.filter_empty">Empty</button>
|
<button class="filter-btn" data-type="empty" data-i18n-text="auth_files.filter_empty">Empty</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -478,6 +479,51 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Vertex AI Credential Import -->
|
||||||
|
<div class="card" id="vertex-import-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3><i class="fas fa-cloud-upload-alt"></i> <span data-i18n="vertex_import.title">Vertex AI 凭证导入</span></h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="form-hint" data-i18n="vertex_import.description">
|
||||||
|
上传 Google 服务账号 JSON 并保存为 vertex-<project>.json。
|
||||||
|
</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="vertex-location" data-i18n="vertex_import.location_label">区域 (可选)</label>
|
||||||
|
<input type="text" id="vertex-location" data-i18n-placeholder="vertex_import.location_placeholder" value="us-central1">
|
||||||
|
<p class="form-hint" data-i18n="vertex_import.location_hint">留空则使用默认 us-central1。</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label data-i18n="vertex_import.file_label">服务账号密钥 JSON</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="vertex-file-display" readonly data-i18n-placeholder="vertex_import.file_placeholder" placeholder="尚未选择文件">
|
||||||
|
<input type="file" id="vertex-file-input" accept=".json" style="display: none;">
|
||||||
|
<button type="button" id="vertex-select-file" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-file-upload"></i> <span data-i18n="vertex_import.choose_file">选择文件</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="form-hint" data-i18n="vertex_import.file_hint">仅支持 Google Cloud service account JSON。</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions vertex-import-actions">
|
||||||
|
<button type="button" id="vertex-import-btn" class="btn btn-primary" disabled>
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i> <span data-i18n="vertex_import.import_button">导入 Vertex 凭证</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="vertex-import-result" class="vertex-import-result" style="display: none;">
|
||||||
|
<div class="vertex-import-result-header">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span data-i18n="vertex_import.result_title">凭证已保存</span>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li><span data-i18n="vertex_import.result_project">项目 ID</span>: <code id="vertex-result-project">-</code></li>
|
||||||
|
<li><span data-i18n="vertex_import.result_email">服务账号</span>: <code id="vertex-result-email">-</code></li>
|
||||||
|
<li><span data-i18n="vertex_import.result_location">区域</span>: <code id="vertex-result-location">-</code></li>
|
||||||
|
<li><span data-i18n="vertex_import.result_file">存储文件</span>: <code id="vertex-result-file">-</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Codex OAuth -->
|
<!-- Codex OAuth -->
|
||||||
<div class="card" id="codex-oauth-card">
|
<div class="card" id="codex-oauth-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
|||||||
54
styles.css
54
styles.css
@@ -3758,6 +3758,60 @@ input:checked+.slider:before {
|
|||||||
color: #f472b6;
|
color: #f472b6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Vertex AI Credential Import */
|
||||||
|
.vertex-import-actions {
|
||||||
|
text-align: left;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertex-import-result {
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 1px dashed var(--border-primary);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--bg-quaternary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertex-import-result-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: var(--success-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertex-import-result ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertex-import-result li {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertex-import-result code {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .vertex-import-result {
|
||||||
|
border-color: rgba(96, 165, 250, 0.4);
|
||||||
|
background: rgba(15, 23, 42, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .vertex-import-result code {
|
||||||
|
background: rgba(7, 11, 22, 0.8);
|
||||||
|
color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== AI提供商统计徽章样式 ===== */
|
/* ===== AI提供商统计徽章样式 ===== */
|
||||||
|
|
||||||
/* 统计信息容器 */
|
/* 统计信息容器 */
|
||||||
|
|||||||
Reference in New Issue
Block a user