Files
wxauto_api/static/js/api.js
T
2026-04-07 14:11:45 +08:00

156 lines
4.9 KiB
JavaScript

const API_KEY = 'your-external-api-key';
const API_BASE = '/api/v1';
const REQUEST_TIMEOUT = 30000;
let pendingRequests = 0;
let loadingToast = null;
let loadingTimer = null;
function showNonBlockingLoading(message = '加载中...') {
pendingRequests++;
updateLoadingUI(message);
if (loadingTimer) clearTimeout(loadingTimer);
loadingTimer = setTimeout(() => {
console.warn('Some requests may be stuck, force clearing');
}, 60000);
}
function hideNonBlockingLoading() {
pendingRequests = Math.max(0, pendingRequests - 1);
updateLoadingUI(pendingRequests > 0 ? '处理中...' : null);
}
function updateLoadingUI(message) {
if (!message) {
if (loadingToast) {
loadingToast.remove();
loadingToast = null;
}
return;
}
if (!loadingToast) {
loadingToast = document.createElement('div');
loadingToast.id = 'loading-toast';
loadingToast.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.8);
color: white;
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
z-index: 10000;
display: flex;
align-items: center;
gap: 10px;
`;
document.body.appendChild(loadingToast);
}
const spinner = '<div style="width:16px;height:16px;border:2px solid #fff;border-top-color:transparent;border-radius:50%;animation:spin 0.8s linear infinite"></div>';
const style = '<style>@keyframes spin{to{transform:rotate(360deg)}}</style>';
if (!document.getElementById('loading-spinner-style')) {
document.head.insertAdjacentHTML('beforeend', style);
}
loadingToast.innerHTML = spinner + `<span>${message}</span><span style="font-size:12px;opacity:0.7">(${pendingRequests})</span>`;
}
async function api(endpoint, options = {}) {
const method = options.method || 'GET';
const isMutation = ['POST', 'PUT', 'DELETE'].includes(method);
const startMsg = `请求${isMutation ? '修改' : '获取'}中...`;
showNonBlockingLoading(startMsg);
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
showToast('请求超时,请稍后重试', 'error');
}, REQUEST_TIMEOUT);
try {
const headers = {
'X-API-Key': API_KEY,
'Content-Type': 'application/json',
...options.headers
};
const response = await fetch(API_BASE + endpoint, {
...options,
headers,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
const error = `HTTP ${response.status}: ${response.statusText}`;
if (isMutation) {
showToast('操作失败: ' + error, 'error');
}
return { success: false, error };
}
const data = await response.json();
if (isMutation && data.success === false && data.error) {
showToast(data.error, 'error');
}
return data;
} catch (err) {
clearTimeout(timeoutId);
if (err.name === 'AbortError') {
return { success: false, error: '请求超时' };
}
const errorMsg = err.message || '网络错误';
if (isMutation) {
showToast(errorMsg, 'error');
}
return { success: false, error: errorMsg };
} finally {
hideNonBlockingLoading();
}
}
const apiClient = {
get: (endpoint) => api(endpoint),
post: (endpoint, data) => api(endpoint, { method: 'POST', body: JSON.stringify(data) }),
put: (endpoint, data) => api(endpoint, { method: 'PUT', body: JSON.stringify(data) }),
delete: (endpoint) => api(endpoint, { method: 'DELETE' }),
nodes: {
list: () => api('/nodes/'),
get: (name) => api(`/nodes/${name}`),
create: (data) => api('/nodes/', { method: 'POST', body: JSON.stringify(data) }),
update: (name, data) => api(`/nodes/${name}`, { method: 'PUT', body: JSON.stringify(data) }),
delete: (name) => api(`/nodes/${name}`, { method: 'DELETE' }),
status: (name) => api(`/nodes/${name}/status`)
},
messages: {
send: (data) => api('/messages/send', { method: 'POST', body: JSON.stringify(data) }),
broadcast: (data) => api('/messages/broadcast', { method: 'POST', body: JSON.stringify(data) })
},
config: {
getMonitor: () => api('/config/monitor'),
updateMonitor: (data) => api('/config/monitor', { method: 'POST', body: JSON.stringify(data) }),
getWebhook: () => api('/config/webhook'),
updateWebhook: (data) => api('/config/webhook', { method: 'POST', body: JSON.stringify(data) })
},
logs: {
recent: (count = 50) => api(`/logs/recent?count=${count}`)
}
};
window.api = api;
window.apiClient = apiClient;