@@ -1680,264 +1830,216 @@
// === TOKEN FUNCTIONS ===
+ // 创建令牌编辑模式管理器
+ const tokenModeManager = new EditModeManager(
+ 'tokenFormMode',
+ 'tokenJsonMode',
+ 'tokenModeToggle',
+ '📋 切换到表单模式',
+ '📝 切换到JSON模式'
+ );
+
// 切换令牌编辑模式
function toggleTokenEditMode() {
- const formMode = document.getElementById('tokenFormMode');
- const jsonMode = document.getElementById('tokenJsonMode');
- const toggleBtn = document.getElementById('tokenModeToggle');
-
- if (tokenEditMode === 'form') {
- // 切换到JSON模式
- const formData = collectTokenFormData();
- if (formData.key) {
- document.getElementById('tokenKeyJson').value = formData.key;
- delete formData.key; // 从配置中移除key
- document.getElementById('tokenValueJson').value = JSON.stringify(formData, null, 2);
+ tokenModeManager.toggle(
+ () => {
+ const formData = collectTokenFormData();
+ delete formData.key; // 从配置中移除key,因为key现在是共用的
+ return formData;
+ },
+ (parsed) => {
+ const key = DOMUtils.getValue('tokenKey');
+ fillTokenForm(key, parsed);
}
-
- formMode.style.display = 'none';
- jsonMode.style.display = 'block';
- toggleBtn.textContent = '📋 切换到表单模式';
- tokenEditMode = 'json';
- } else {
- // 切换到表单模式
- try {
- const key = document.getElementById('tokenKeyJson').value;
- const jsonValue = document.getElementById('tokenValueJson').value;
- if (jsonValue) {
- const parsed = JSON.parse(jsonValue);
- fillTokenForm(key, parsed);
- }
- } catch (error) {
- console.warn('JSON格式错误,无法转换到表单模式:', error);
- }
-
- formMode.style.display = 'block';
- jsonMode.style.display = 'none';
- toggleBtn.textContent = '📝 切换到JSON模式';
- tokenEditMode = 'form';
- }
+ );
+ tokenEditMode = tokenModeManager.getCurrentMode();
}
// 收集令牌表单数据
function collectTokenFormData() {
- const name = document.getElementById('tokenName').value.trim();
- const key = document.getElementById('tokenKey').value.trim();
- const channelKeysInput = document.getElementById('channelKeysInput').value.trim();
- const totalQuotaInput = document.getElementById('totalQuotaInput').value.trim();
-
+ const channelKeysInput = DOMUtils.getValue('channelKeysInput');
const channelKeys = channelKeysInput
? channelKeysInput.split(',').map(key => key.trim()).filter(key => key.length > 0)
: [];
return {
- key: key,
- name: name,
+ key: DOMUtils.getValue('tokenKey'),
+ name: DOMUtils.getValue('tokenName'),
channel_keys: channelKeys,
- total_quota: totalQuotaInput ? parseFloat(totalQuotaInput) : 0
+ total_quota: parseFloat(DOMUtils.getValue('totalQuotaInput')) || 0
};
}
// 填充令牌表单
function fillTokenForm(key, config) {
- document.getElementById('tokenName').value = config.name || '';
- document.getElementById('tokenKey').value = key;
+ DOMUtils.setValue('tokenName', config.name);
+ DOMUtils.setValue('tokenKey', key);
const channelKeysStr = config.channel_keys && Array.isArray(config.channel_keys)
? config.channel_keys.join(', ')
: '';
- document.getElementById('channelKeysInput').value = channelKeysStr;
- document.getElementById('totalQuotaInput').value = config.total_quota || '';
+ DOMUtils.setValue('channelKeysInput', channelKeysStr);
+ DOMUtils.setValue('totalQuotaInput', config.total_quota);
+ }
+
+ // Token按钮状态管理类
+ class TokenButtonManager {
+ static setEditMode(isEdit) {
+ const tokenKeyInput = DOMUtils.getElement('tokenKey');
+ const generateBtn = DOMUtils.getElement('generateTokenBtn');
+ const copyBtn = DOMUtils.getElement('copyTokenBtn');
+
+ if (isEdit) {
+ // 编辑模式:只显示复制按钮,token key 不可编辑
+ FormUtils.setReadonly('tokenKey', true);
+ DOMUtils.hide(generateBtn);
+ DOMUtils.show(copyBtn);
+ } else {
+ // 创建模式:显示生成按钮,token key 可编辑
+ FormUtils.setReadonly('tokenKey', false);
+ DOMUtils.show(generateBtn);
+ DOMUtils.hide(copyBtn);
+ if (tokenKeyInput) {
+ tokenKeyInput.placeholder = '输入令牌标识或点击生成';
+ }
+ }
+ }
}
// 显示令牌列表
function showTokenList() {
- document.getElementById('tokenListView').style.display = 'block';
- document.getElementById('tokenFormView').style.display = 'none';
+ DOMUtils.show('tokenListView');
+ DOMUtils.hide('tokenFormView');
currentEditingToken = null;
}
// 显示令牌表单
function showTokenForm(isEdit = false) {
- document.getElementById('tokenListView').style.display = 'none';
- document.getElementById('tokenFormView').style.display = 'block';
- document.getElementById('tokenFormTitle').textContent = isEdit ? '编辑令牌' : '添加令牌';
+ DOMUtils.hide('tokenListView');
+ DOMUtils.show('tokenFormView');
- // 根据是否为编辑模式控制按钮显示
- const tokenKeyActions = document.getElementById('tokenKeyActions');
- const tokenKeyInput = document.getElementById('tokenKey');
- const generateBtn = document.getElementById('generateTokenBtn');
- const copyBtn = document.getElementById('copyTokenBtn');
+ const titleElement = DOMUtils.getElement('tokenFormTitle');
+ if (titleElement) titleElement.textContent = isEdit ? '编辑令牌' : '添加令牌';
- if (isEdit) {
- // 编辑模式:只显示复制按钮,token key 为只读
- tokenKeyActions.style.display = 'flex';
- generateBtn.style.display = 'none';
- copyBtn.style.display = 'inline-block';
- tokenKeyInput.style.cursor = 'not-allowed';
- tokenKeyInput.placeholder = '编辑时不可修改';
- } else {
- // 创建模式:只显示生成按钮,token key 可操作
- tokenKeyActions.style.display = 'flex';
- generateBtn.style.display = 'inline-block';
- copyBtn.style.display = 'none';
- tokenKeyInput.style.cursor = 'text';
- tokenKeyInput.placeholder = '点击生成令牌';
+ TokenButtonManager.setEditMode(isEdit);
+
+ if (!isEdit) {
clearTokenForm();
- // 自动生成token标识
- generateTokenKey();
+ generateTokenKey(); // 自动生成token标识
}
}
// 清空令牌表单
function clearTokenForm() {
- // 清空表单模式字段
- document.getElementById('tokenName').value = '';
- document.getElementById('tokenKey').value = '';
- document.getElementById('channelKeysInput').value = '';
- document.getElementById('totalQuotaInput').value = '';
-
- // 清空JSON模式字段
- document.getElementById('tokenKeyJson').value = '';
- document.getElementById('tokenValueJson').value = '';
+ // 清空所有字段
+ const fieldIds = ['tokenName', 'channelKeysInput', 'totalQuotaInput', 'tokenValueJson'];
+ fieldIds.forEach(id => DOMUtils.clearInput(id));
// 重置token key输入框状态
- const tokenKeyInput = document.getElementById('tokenKey');
- const tokenKeyActions = document.getElementById('tokenKeyActions');
- const generateBtn = document.getElementById('generateTokenBtn');
- const copyBtn = document.getElementById('copyTokenBtn');
+ FormUtils.saveOriginalPlaceholder('tokenKey');
+ FormUtils.setReadonly('tokenKey', false);
+ DOMUtils.clearInput('tokenKey');
- tokenKeyInput.disabled = false;
- tokenKeyInput.style.cursor = 'text';
- tokenKeyInput.placeholder = '点击生成令牌';
- tokenKeyActions.style.display = 'flex';
- generateBtn.style.display = 'inline-block';
- copyBtn.style.display = 'none';
+ const tokenKeyInput = DOMUtils.getElement('tokenKey');
+ if (tokenKeyInput) {
+ tokenKeyInput.placeholder = '输入令牌标识或点击生成';
+ }
- // 重置到表单模式
- document.getElementById('tokenFormMode').style.display = 'block';
- document.getElementById('tokenJsonMode').style.display = 'none';
- document.getElementById('tokenModeToggle').textContent = '📝 切换到JSON模式';
+ // 重置编辑模式
+ tokenModeManager.resetToFormMode();
tokenEditMode = 'form';
-
currentEditingToken = null;
}
// 生成token key
function generateTokenKey() {
- // 生成32位随机字符串
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = 'sk-';
for (let i = 0; i < 48; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
- document.getElementById('tokenKey').value = result;
+ DOMUtils.setValue('tokenKey', result);
}
// 复制token key
function copyTokenKey() {
- const tokenKeyInput = document.getElementById('tokenKey');
- if (!tokenKeyInput.value) {
+ const tokenKey = DOMUtils.getValue('tokenKey');
+ if (!tokenKey) {
showAlert('请先生成令牌标识', 'error');
return;
}
-
- navigator.clipboard.writeText(tokenKeyInput.value).then(() => {
- showAlert('令牌标识已复制到剪贴板', 'success', 2000);
- }).catch(() => {
- // 降级方案
- tokenKeyInput.select();
- document.execCommand('copy');
- showAlert('令牌标识已复制到剪贴板', 'success', 2000);
- });
+ copyToClipboard(tokenKey);
}
// 设置配额值
function setQuotaValue(value) {
- document.getElementById('totalQuotaInput').value = value;
+ DOMUtils.setValue('totalQuotaInput', value);
}
- // 从列表编辑令牌(处理原始value字符串)
+ // 从列表编辑令牌
function editTokenFromList(key, originalValue) {
currentEditingToken = key;
- try {
- const parsed = JSON.parse(originalValue);
+ const parsed = FormUtils.validateJSON(originalValue, '令牌配置格式错误');
+ if (!parsed) return;
- if (tokenEditMode === 'form') {
- // 表单模式
- fillTokenForm(key, parsed);
+ // 设置key字段状态
+ FormUtils.saveOriginalPlaceholder('tokenKey');
+ FormUtils.setReadonly('tokenKey', true);
+ DOMUtils.setValue('tokenKey', key);
- // 设置编辑模式的UI状态
- const tokenKeyInput = document.getElementById('tokenKey');
- const tokenKeyActions = document.getElementById('tokenKeyActions');
- const generateBtn = document.getElementById('generateTokenBtn');
- const copyBtn = document.getElementById('copyTokenBtn');
-
- tokenKeyInput.disabled = true; // 编辑时不允许修改key
- tokenKeyInput.style.cursor = 'not-allowed';
- tokenKeyInput.placeholder = '编辑时不可修改';
- tokenKeyActions.style.display = 'flex';
- generateBtn.style.display = 'none'; // 隐藏生成按钮
- copyBtn.style.display = 'inline-block'; // 显示复制按钮
- } else {
- // JSON模式
- document.getElementById('tokenKeyJson').value = key;
- document.getElementById('tokenValueJson').value = JSON.stringify(parsed, null, 2);
- }
-
- showTokenForm(true);
- } catch (error) {
- console.error('解析令牌配置失败:', error);
- showAlert('令牌配置格式错误', 'error');
+ // 根据模式填充数据
+ if (tokenEditMode === 'form') {
+ fillTokenForm(key, parsed);
+ } else {
+ DOMUtils.setValue('tokenValueJson', JSON.stringify(parsed, null, 2));
}
+
+ TokenButtonManager.setEditMode(true);
+ showTokenForm(true);
}
// 保存令牌
async function saveToken() {
- let key, config;
+ const key = DOMUtils.getValue('tokenKey');
+ if (!key) {
+ showAlert('请填写令牌标识', 'error');
+ return;
+ }
+ let config;
if (tokenEditMode === 'form') {
- // 表单模式
const formData = collectTokenFormData();
- key = formData.key;
config = {
name: formData.name,
channel_keys: formData.channel_keys,
total_quota: formData.total_quota
};
- if (!formData.name || !key || !formData.total_quota) {
- showAlert('请填写令牌名称、令牌标识和总配额', 'error');
+ // 验证必填字段
+ if (!FormUtils.validateRequired([
+ { id: 'tokenName', name: '令牌名称', message: '请填写令牌名称' }
+ ])) {
return;
}
- if (isNaN(formData.total_quota) || formData.total_quota < 0) {
+ if (!formData.total_quota || isNaN(formData.total_quota) || formData.total_quota < 0) {
showAlert('请输入有效的总配额数值', 'error');
return;
}
} else {
- // JSON模式
- key = document.getElementById('tokenKeyJson').value.trim();
- const jsonValue = document.getElementById('tokenValueJson').value.trim();
-
- if (!key || !jsonValue) {
- showAlert('请填写令牌标识和配置JSON', 'error');
- return;
- }
-
- try {
- config = JSON.parse(jsonValue);
- } catch (error) {
- showAlert('JSON格式错误,请检查配置格式', 'error');
+ const jsonValue = DOMUtils.getValue('tokenValueJson');
+ if (!jsonValue) {
+ showAlert('请填写配置JSON', 'error');
return;
}
+ config = FormUtils.validateJSON(jsonValue, 'JSON格式错误,请检查配置格式');
+ if (!config) return;
}
try {
await apiRequest(`/api/admin/token/${encodeURIComponent(key)}`, 'POST', config, true, '正在保存令牌...');
showAlert('令牌保存成功!', 'success');
-
clearTokenForm();
showTokenList();
loadTokens();
@@ -1952,7 +2054,7 @@
const tokens = await apiRequest('/api/admin/token', 'GET', null, true);
displayTokens(tokens);
} catch (error) {
- document.getElementById('tokensList').innerHTML = `
加载令牌失败: ${error.message}
`;
+ DOMUtils.getElement('tokensList').innerHTML = `
加载令牌失败: ${error.message}
`;
}
}
@@ -1968,8 +2070,10 @@
}
}
+ // 显示令牌列表的通用函数
function displayTokens(tokens) {
- const container = document.getElementById('tokensList');
+ const container = DOMUtils.getElement('tokensList');
+ if (!container) return;
// 处理空数据的情况
if (!tokens || !Array.isArray(tokens) || tokens.length === 0) {
@@ -1980,7 +2084,6 @@
container.innerHTML = tokens.map(item => {
let parsedValue = {};
try {
- // 解析 value 字段中的 JSON 字符串
parsedValue = JSON.parse(item.value);
} catch {
parsedValue = { name: 'Invalid JSON', channel_keys: ['Invalid JSON'], total_quota: 0 };
@@ -2019,46 +2122,39 @@
// === PRICING FUNCTIONS ===
+ // 创建定价编辑模式管理器
+ const pricingModeManager = new EditModeManager(
+ 'pricingTableMode',
+ 'pricingJsonMode',
+ 'pricingModeToggle',
+ '📊 切换到表格模式',
+ '📝 切换到JSON模式'
+ );
+
// 切换定价编辑模式
function togglePricingEditMode() {
- const tableMode = document.getElementById('pricingTableMode');
- const jsonMode = document.getElementById('pricingJsonMode');
- const toggleBtn = document.getElementById('pricingModeToggle');
- const addBtn = document.getElementById('addPricingModelBtn');
+ const addBtn = DOMUtils.getElement('addPricingModelBtn');
- if (pricingEditMode === 'table') {
- // 切换到JSON模式
- const config = collectPricingTableData();
- document.getElementById('pricingConfigJson').value = JSON.stringify(config, null, 2);
+ pricingModeManager.toggle(
+ collectPricingTableData,
+ (parsed) => displayPricingTable(parsed)
+ );
- tableMode.style.display = 'none';
- jsonMode.style.display = 'block';
- if (addBtn) addBtn.style.display = 'none'; // 隐藏添加模型按钮
- toggleBtn.textContent = '📊 切换到表格模式';
- pricingEditMode = 'json';
- } else {
- // 切换到表格模式
- try {
- const jsonValue = document.getElementById('pricingConfigJson').value;
- if (jsonValue) {
- const parsed = JSON.parse(jsonValue);
- displayPricingTable(parsed);
- }
- } catch (error) {
- console.warn('JSON格式错误,无法转换到表格模式:', error);
+ pricingEditMode = pricingModeManager.getCurrentMode();
+
+ // 控制添加模型按钮的显示
+ if (addBtn) {
+ if (pricingEditMode === 'table') {
+ DOMUtils.show(addBtn);
+ } else {
+ DOMUtils.hide(addBtn);
}
-
- tableMode.style.display = 'block';
- jsonMode.style.display = 'none';
- if (addBtn) addBtn.style.display = 'inline-block'; // 显示添加模型按钮
- toggleBtn.textContent = '📝 切换到JSON模式';
- pricingEditMode = 'table';
}
}
// 收集定价表格数据
function collectPricingTableData() {
- const tbody = document.getElementById('pricingTableBody');
+ const tbody = DOMUtils.getElement('pricingTableBody');
const config = {};
if (!tbody) return config;
@@ -2084,9 +2180,10 @@
// 添加新的定价模型行
function addPricingModel() {
- const tbody = document.getElementById('pricingTableBody');
- const rowIndex = tbody.children.length;
+ const tbody = DOMUtils.getElement('pricingTableBody');
+ if (!tbody) return;
+ const rowIndex = tbody.children.length;
const row = document.createElement('tr');
row.style.borderBottom = '1px solid #e9ecef';
row.innerHTML = `
@@ -2112,7 +2209,7 @@
// 删除定价模型行
function removePricingRow(button) {
const row = button.closest('tr');
- row.remove();
+ if (row) row.remove();
}
// 验证定价行数据
@@ -2124,46 +2221,49 @@
async function loadPricing() {
try {
const pricing = await apiRequest('/api/admin/pricing', 'GET', null, true);
- const addBtn = document.getElementById('addPricingModelBtn');
+ const addBtn = DOMUtils.getElement('addPricingModelBtn');
if (pricingEditMode === 'table') {
displayPricingTable(pricing);
- if (addBtn) addBtn.style.display = 'inline-block';
+ if (addBtn) DOMUtils.show(addBtn);
} else {
- document.getElementById('pricingConfigJson').value = JSON.stringify(pricing, null, 2);
- if (addBtn) addBtn.style.display = 'none';
+ DOMUtils.setValue('pricingConfigJson', JSON.stringify(pricing, null, 2));
+ if (addBtn) DOMUtils.hide(addBtn);
}
} catch (error) {
- // 失败时显示错误信息
+ // 失败时显示错误信息并重新创建表格结构
if (pricingEditMode === 'table') {
- document.getElementById('pricingContent').innerHTML = `
-
加载定价配置失败: ${error.message}
-
-
-
-
- | 模型名称 |
- 输入倍率 |
- 输出倍率 |
- 操作 |
-
-
-
-
-
-
-
-
-
- `;
+
+
+
+
+
+ 配置格式:模型名称 -> {input: 输入倍率, output: 输出倍率}。倍率基于1百万token=$1.00计算
+
+
+
+ `;
+ }
} else {
showAlert(`加载定价配置失败: ${error.message}`, 'error');
}
@@ -2172,7 +2272,7 @@
// 显示定价配置表格
function displayPricingTable(pricing) {
- const tbody = document.getElementById('pricingTableBody');
+ const tbody = DOMUtils.getElement('pricingTableBody');
if (!tbody) return;
tbody.innerHTML = '';
@@ -2212,26 +2312,23 @@
});
}
- async function updatePricing() {
- let config;
-
- if (pricingEditMode === 'table') {
- // 表格模式
- const tbody = document.getElementById('pricingTableBody');
-
+ // 定价验证类
+ class PricingValidator {
+ static validateTableData() {
+ const tbody = DOMUtils.getElement('pricingTableBody');
if (!tbody) {
- showAlert('定价表格未找到,请刷新页面重试', 'error');
- return;
+ return { isValid: false, error: '定价表格未找到,请刷新页面重试' };
}
- config = {};
- let hasError = false;
+ const config = {};
let errorMessage = '';
// 遍历所有行收集数据
- Array.from(tbody.children).forEach((row, index) => {
+ const rows = Array.from(tbody.children);
+ for (let index = 0; index < rows.length; index++) {
+ const row = rows[index];
const inputs = row.querySelectorAll('input');
- if (inputs.length < 3) return;
+ if (inputs.length < 3) continue;
const modelName = inputs[0].value.trim();
const inputPrice = parseFloat(inputs[1].value);
@@ -2239,66 +2336,67 @@
// 验证数据
if (!modelName) {
- hasError = true;
errorMessage = `第 ${index + 1} 行:模型名称不能为空`;
- return;
+ break;
}
if (isNaN(inputPrice) || inputPrice < 0) {
- hasError = true;
errorMessage = `第 ${index + 1} 行:输入倍率必须是有效的非负数`;
- return;
+ break;
}
if (isNaN(outputPrice) || outputPrice < 0) {
- hasError = true;
errorMessage = `第 ${index + 1} 行:输出倍率必须是有效的非负数`;
- return;
+ break;
}
// 检查模型名称是否重复
if (config[modelName]) {
- hasError = true;
errorMessage = `模型名称 "${modelName}" 重复`;
- return;
+ break;
}
config[modelName] = {
input: inputPrice,
output: outputPrice
};
- });
+ }
- if (hasError) {
- showAlert(errorMessage, 'error');
- return;
+ if (errorMessage) {
+ return { isValid: false, error: errorMessage };
}
if (Object.keys(config).length === 0) {
- showAlert('请至少添加一个模型的定价配置', 'error');
+ return { isValid: false, error: '请至少添加一个模型的定价配置' };
+ }
+
+ return { isValid: true, data: config };
+ }
+ }
+
+ async function updatePricing() {
+ let config;
+
+ if (pricingEditMode === 'table') {
+ const validation = PricingValidator.validateTableData();
+ if (!validation.isValid) {
+ showAlert(validation.error, 'error');
return;
}
+ config = validation.data;
} else {
- // JSON模式
- const configText = document.getElementById('pricingConfigJson').value.trim();
-
+ const configText = DOMUtils.getValue('pricingConfigJson');
if (!configText) {
showAlert('请填写定价配置', 'error');
return;
}
-
- try {
- config = JSON.parse(configText);
- } catch (error) {
- showAlert('配置格式错误,请检查JSON格式', 'error');
- return;
- }
+ config = FormUtils.validateJSON(configText, '配置格式错误,请检查JSON格式');
+ if (!config) return;
}
try {
await apiRequest('/api/admin/pricing', 'POST', config, true, '正在更新定价配置...');
showAlert('定价配置更新成功!', 'success');
- // 更新成功后重新加载配置
loadPricing();
} catch (error) {
showAlert(`更新定价配置失败: ${error.message}`, 'error');
@@ -2307,88 +2405,96 @@
// === API TEST FUNCTIONS ===
- // 发送 API 测试请求
- async function sendApiTest() {
- const token = document.getElementById('apiTestToken').value.trim();
- const requestText = document.getElementById('apiTestRequest').value.trim();
- const responseTextarea = document.getElementById('apiTestResponse');
- const statusDiv = document.getElementById('apiTestStatus');
+ // API测试管理类
+ class ApiTestManager {
+ static async sendRequest() {
+ const token = DOMUtils.getValue('apiTestToken');
+ const requestText = DOMUtils.getValue('apiTestRequest');
+ const responseTextarea = DOMUtils.getElement('apiTestResponse');
+ const statusDiv = DOMUtils.getElement('apiTestStatus');
- if (!token) {
- showAlert('请输入 Bearer Token', 'error');
- return;
+ if (!FormUtils.validateRequired([
+ { id: 'apiTestToken', name: 'Bearer Token', message: '请输入 Bearer Token' },
+ { id: 'apiTestRequest', name: '请求 JSON', message: '请输入请求 JSON' }
+ ])) {
+ return;
+ }
+
+ const requestJson = FormUtils.validateJSON(requestText, '请求 JSON 格式错误');
+ if (!requestJson) return;
+
+ // 显示加载状态
+ if (responseTextarea) responseTextarea.value = '正在发送请求...';
+ if (statusDiv) statusDiv.style.display = 'none';
+
+ try {
+ const startTime = Date.now();
+
+ const response = await fetch('/v1/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ body: JSON.stringify(requestJson)
+ });
+
+ const endTime = Date.now();
+ const duration = endTime - startTime;
+
+ this._displayTestResult(response, duration, responseTextarea, statusDiv);
+ } catch (error) {
+ this._displayTestError(error, responseTextarea, statusDiv);
+ }
}
- if (!requestText) {
- showAlert('请输入请求 JSON', 'error');
- return;
- }
-
- let requestJson;
- try {
- requestJson = JSON.parse(requestText);
- } catch (error) {
- showAlert('请求 JSON 格式错误: ' + error.message, 'error');
- return;
- }
-
- // 显示加载状态
- responseTextarea.value = '正在发送请求...';
- statusDiv.style.display = 'none';
-
- try {
- const startTime = Date.now();
-
- const response = await fetch('/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${token}`
- },
- body: JSON.stringify(requestJson)
- });
-
- const endTime = Date.now();
- const duration = endTime - startTime;
-
- // 显示状态信息
- statusDiv.style.display = 'block';
+ static async _displayTestResult(response, duration, responseTextarea, statusDiv) {
+ if (statusDiv) statusDiv.style.display = 'block';
if (response.ok) {
- // 状态码正常,解析 JSON 响应
const responseData = await response.json();
- responseTextarea.value = JSON.stringify(responseData, null, 2);
+ if (responseTextarea) {
+ responseTextarea.value = JSON.stringify(responseData, null, 2);
+ }
- statusDiv.className = 'alert alert-success';
- statusDiv.innerHTML = `
-
✅ 请求成功
- 状态码: ${response.status}
- 响应时间: ${duration}ms
- `;
+ if (statusDiv) {
+ statusDiv.className = 'alert alert-success';
+ statusDiv.innerHTML = `
+
✅ 请求成功
+ 状态码: ${response.status}
+ 响应时间: ${duration}ms
+ `;
+ }
} else {
- // 状态码异常,显示纯文本错误
const errorText = await response.text();
- responseTextarea.value = errorText;
+ if (responseTextarea) responseTextarea.value = errorText;
- statusDiv.className = 'alert alert-error';
- statusDiv.innerHTML = `
-
❌ 请求失败
- 状态码: ${response.status}
- 响应时间: ${duration}ms
- `;
+ if (statusDiv) {
+ statusDiv.className = 'alert alert-error';
+ statusDiv.innerHTML = `
+
❌ 请求失败
+ 状态码: ${response.status}
+ 响应时间: ${duration}ms
+ `;
+ }
}
- } catch (error) {
- responseTextarea.value = `请求失败: ${error.message}`;
- statusDiv.style.display = 'block';
- statusDiv.className = 'alert alert-error';
- statusDiv.innerHTML = `
❌ 网络错误${error.message}`;
}
- }
- // 清空 API 测试数据
- function clearApiTest() {
- document.getElementById('apiTestToken').value = '';
- document.getElementById('apiTestRequest').value = `{
+ static _displayTestError(error, responseTextarea, statusDiv) {
+ if (responseTextarea) {
+ responseTextarea.value = `请求失败: ${error.message}`;
+ }
+
+ if (statusDiv) {
+ statusDiv.style.display = 'block';
+ statusDiv.className = 'alert alert-error';
+ statusDiv.innerHTML = `
❌ 网络错误${error.message}`;
+ }
+ }
+
+ static clear() {
+ DOMUtils.clearInput('apiTestToken');
+ DOMUtils.setValue('apiTestRequest', `{
"model": "gpt-4",
"messages": [
{
@@ -2396,18 +2502,38 @@
"content": "Hello, world!"
}
]
-}`;
- document.getElementById('apiTestResponse').value = '';
- document.getElementById('apiTestStatus').style.display = 'none';
+}`);
+ DOMUtils.clearInput('apiTestResponse');
+ const statusDiv = DOMUtils.getElement('apiTestStatus');
+ if (statusDiv) statusDiv.style.display = 'none';
+ }
+ }
+
+ // 发送 API 测试请求
+ async function sendApiTest() {
+ await ApiTestManager.sendRequest();
+ }
+
+ // 清空 API 测试数据
+ function clearApiTest() {
+ ApiTestManager.clear();
}
// === UTILITY FUNCTIONS ===
- function copyToClipboard(text) {
- navigator.clipboard.writeText(text).then(() => {
- showAlert('已复制到剪贴板', 'success', 2000);
- }).catch(() => {
- // 降级方案
+ // 剪贴板管理类
+ class ClipboardManager {
+ static async copy(text) {
+ try {
+ await navigator.clipboard.writeText(text);
+ showAlert('已复制到剪贴板', 'success', 2000);
+ } catch (error) {
+ // 降级方案
+ this._fallbackCopy(text);
+ }
+ }
+
+ static _fallbackCopy(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
@@ -2415,25 +2541,42 @@
document.execCommand('copy');
document.body.removeChild(textArea);
showAlert('已复制到剪贴板', 'success', 2000);
- });
+ }
+ }
+
+ function copyToClipboard(text) {
+ ClipboardManager.copy(text);
+ }
+
+ // 消息提示管理类
+ class AlertManager {
+ static show(message, type, duration = 5000) {
+ // 移除现有提示
+ this._removeExistingAlerts();
+
+ const alertDiv = document.createElement('div');
+ alertDiv.className = `alert alert-${type} alert-top`;
+ alertDiv.textContent = message;
+
+ const container = document.querySelector('.container');
+ if (container) {
+ container.insertBefore(alertDiv, container.firstChild);
+ }
+
+ // 自动移除
+ setTimeout(() => {
+ alertDiv.remove();
+ }, duration);
+ }
+
+ static _removeExistingAlerts() {
+ const existingAlerts = document.querySelectorAll('.alert');
+ existingAlerts.forEach(alert => alert.remove());
+ }
}
function showAlert(message, type, duration = 5000) {
- // Remove existing alerts
- const existingAlerts = document.querySelectorAll('.alert');
- existingAlerts.forEach(alert => alert.remove());
-
- const alertDiv = document.createElement('div');
- alertDiv.className = `alert alert-${type} alert-top`;
- alertDiv.textContent = message;
-
- const container = document.querySelector('.container');
- container.insertBefore(alertDiv, container.firstChild);
-
- // Auto remove after specified duration
- setTimeout(() => {
- alertDiv.remove();
- }, duration);
+ AlertManager.show(message, type, duration);
}
// Loading 管理对象
@@ -2550,44 +2693,60 @@
LoadingManager.hide();
}
- // Initialize on page load
- document.addEventListener('DOMContentLoaded', function () {
- const savedToken = localStorage.getItem('adminToken');
- if (savedToken) {
- adminToken = savedToken;
- showMainContent();
- } else {
- // 默认显示首页(未登录状态)
- showHomePage();
+ // === 页面初始化和事件处理 ===
+
+ // 页面初始化管理类
+ class PageInitializer {
+ static init() {
+ this._initAuth();
+ this._initEventListeners();
+ this._initKeyboardShortcuts();
}
- // 为登录输入框添加回车键监听
- const tokenInput = document.getElementById('adminToken');
- if (tokenInput) {
- tokenInput.addEventListener('keypress', function (event) {
- if (event.key === 'Enter') {
- authenticate();
+ static _initAuth() {
+ if (AuthManager.restoreToken()) {
+ showMainContent();
+ } else {
+ showHomePage();
+ }
+ }
+
+ static _initEventListeners() {
+ // 为登录输入框添加回车键监听
+ const tokenInput = DOMUtils.getElement('adminToken');
+ if (tokenInput) {
+ tokenInput.addEventListener('keypress', (event) => {
+ if (event.key === 'Enter') {
+ authenticate();
+ }
+ });
+ }
+
+ // 点击模态框外部关闭
+ window.onclick = (event) => {
+ const modal = DOMUtils.getElement('loginModal');
+ if (event.target === modal) {
+ closeLoginModal();
+ }
+ };
+ }
+
+ static _initKeyboardShortcuts() {
+ // ESC键关闭模态框
+ document.addEventListener('keydown', (event) => {
+ if (event.key === 'Escape') {
+ const modal = DOMUtils.getElement('loginModal');
+ if (modal && modal.style.display === 'block') {
+ closeLoginModal();
+ }
}
});
}
- });
-
- // 点击模态框外部关闭
- window.onclick = function (event) {
- const modal = document.getElementById('loginModal');
- if (event.target == modal) {
- closeLoginModal();
- }
}
- // 添加ESC键关闭模态框
- document.addEventListener('keydown', function (event) {
- if (event.key === 'Escape') {
- const modal = document.getElementById('loginModal');
- if (modal.style.display === 'block') {
- closeLoginModal();
- }
- }
+ // Initialize on page load
+ document.addEventListener('DOMContentLoaded', () => {
+ PageInitializer.init();
});