Files
ylt_diy/public/static/js/common.js

355 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 全局错误处理防止JavaScript错误中断执行
window.onerror = function(msg, url, line, col, error) {
console.error('JavaScript错误:', msg, 'at', url, 'line:', line);
return true; // 阻止错误传播
};
// ============== Toast 消息提示系统 ==============
// 非阻塞消息提示,替代 alert() 弹窗
// 用法: showToast('消息内容', 'success'); // type: success/error/warning/info
// showToast('消息内容'); // 默认 auto 自动判断类型
const TOAST_ICONS = {
success: '✓',
error: '✕',
warning: '!',
info: 'i'
};
let toastContainer = null;
function getToastContainer() {
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.className = 'toast-container';
document.body.appendChild(toastContainer);
}
return toastContainer;
}
/**
* 显示消息提示(非阻塞)
* @param {string} message - 消息内容
* @param {string} type - 类型: success/error/warning/info/auto
* @param {number} duration - 显示时长(毫秒),默认 3000mserror 默认 5000ms
*/
function showToast(message, type = 'auto', duration = null) {
// 自动判断类型
if (type === 'auto') {
const msg = String(message);
if (/成功|完成|已保存|已删除|已更新|已启用|已禁用|已复制|已导入|已导出|已采集/.test(msg)) {
type = 'success';
} else if (/失败|错误|异常|无法|不能|出错/.test(msg)) {
type = 'error';
} else if (/请|警告|注意|必须|需要/.test(msg)) {
type = 'warning';
} else {
type = 'info';
}
}
// 默认时长error 5000ms其他 3000ms
if (duration === null) {
duration = type === 'error' ? 5000 : 3000;
}
const container = getToastContainer();
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
const icon = TOAST_ICONS[type] || '';
toast.innerHTML = `
<span class="toast-icon">${icon}</span>
<span class="toast-content"></span>
<button class="toast-close">&times;</button>
`;
// 安全设置消息内容(防 XSS
toast.querySelector('.toast-content').textContent = String(message);
// 关闭按钮
toast.querySelector('.toast-close').addEventListener('click', function() {
removeToast(toast);
});
container.appendChild(toast);
// 自动消失
if (duration > 0) {
setTimeout(function() {
removeToast(toast);
}, duration);
}
return toast;
}
function removeToast(toast) {
if (!toast || !toast.parentNode) return;
toast.classList.add('toast-fade-out');
setTimeout(function() {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 300);
}
// 便捷方法
function showSuccess(message, duration) {
return showToast(message, 'success', duration);
}
function showError(message, duration) {
return showToast(message, 'error', duration);
}
function showWarning(message, duration) {
return showToast(message, 'warning', duration);
}
function showInfo(message, duration) {
return showToast(message, 'info', duration);
}
// 全局变量
let editingConfigId = null;
let allFields = [];
let currentPage = 1;
let pageSize = 10;
let currentEntityType = '';
let currentSearch = '';
let entityPage = 1;
let entityPageSize = 20;
let selectedEntityIds = new Set();
let fieldCustomNames = {};
// ============== 登录相关 ==============
const AUTH_TOKEN_KEY = 'daily_report_auth_token';
const AUTH_USER_KEY = 'daily_report_auth_user';
// 获取token
function getToken() {
return localStorage.getItem(AUTH_TOKEN_KEY) || '';
}
// 保存登录状态
function setAuth(token, user) {
localStorage.setItem(AUTH_TOKEN_KEY, token);
localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user));
}
// 清除登录状态
function clearAuth() {
localStorage.removeItem(AUTH_TOKEN_KEY);
localStorage.removeItem(AUTH_USER_KEY);
}
// 检查是否已登录
function isLoggedIn() {
return !!getToken();
}
// 获取当前用户
function getCurrentUser() {
const userStr = localStorage.getItem(AUTH_USER_KEY);
return userStr ? JSON.parse(userStr) : null;
}
// 全局拦截 fetch自动加 token
const originalFetch = window.fetch;
window.fetch = function(url, options = {}) {
const token = getToken();
if (token) {
options.headers = options.headers || {};
options.headers['Authorization'] = 'Bearer ' + token;
}
return originalFetch(url, options).then(response => {
if (response.status === 401) {
clearAuth();
showLoginPage();
}
return response;
});
};
// 显示登录页
function showLoginPage() {
document.getElementById('login-overlay').style.display = 'flex';
document.getElementById('main-container').style.display = 'none';
document.getElementById('login-password').value = '';
document.getElementById('login-password').focus();
}
// 隐藏登录页
function hideLoginPage() {
document.getElementById('login-overlay').style.display = 'none';
document.getElementById('main-container').style.display = 'block';
const user = getCurrentUser();
if (user) {
document.getElementById('user-display').textContent = user.real_name || user.username;
}
}
// 登录
async function handleLogin(event) {
event.preventDefault();
const username = document.getElementById('login-username').value.trim();
const password = document.getElementById('login-password').value.trim();
if (!username || !password) {
showToast('请输入用户名和密码');
return;
}
const btn = document.getElementById('login-btn');
btn.disabled = true;
btn.textContent = '登录中...';
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const result = await response.json();
if (result.success) {
setAuth(result.data.token, {
username: result.data.username,
real_name: result.data.real_name
});
hideLoginPage();
// 登录成功后加载数据
loadConfigs();
} else {
showToast(result.message || '登录失败');
}
} catch (error) {
showToast('登录失败: ' + error.message);
} finally {
btn.disabled = false;
btn.textContent = '登 录';
}
}
// 退出登录
async function handleLogout() {
if (!confirm('确定要退出登录吗?')) return;
try {
await fetch('/api/auth/logout', { method: 'POST' });
} catch (e) {
// 忽略错误
}
clearAuth();
showLoginPage();
}
// 修改密码弹窗
function showChangePasswordModal() {
document.getElementById('old-password').value = '';
document.getElementById('new-password').value = '';
document.getElementById('confirm-password').value = '';
document.getElementById('change-password-modal').style.display = 'block';
}
function closeChangePasswordModal() {
document.getElementById('change-password-modal').style.display = 'none';
}
async function saveChangePassword() {
const oldPassword = document.getElementById('old-password').value.trim();
const newPassword = document.getElementById('new-password').value.trim();
const confirmPassword = document.getElementById('confirm-password').value.trim();
if (!oldPassword || !newPassword || !confirmPassword) {
showToast('请填写完整信息');
return;
}
if (newPassword.length < 6) {
showToast('新密码长度不能少于6位');
return;
}
if (newPassword !== confirmPassword) {
showToast('两次输入的新密码不一致');
return;
}
try {
const response = await fetch('/api/auth/change-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
old_password: oldPassword,
new_password: newPassword
})
});
const result = await response.json();
if (result.success) {
showToast('密码修改成功,请重新登录');
closeChangePasswordModal();
clearAuth();
showLoginPage();
} else {
showToast(result.message || '修改失败');
}
} catch (error) {
showToast('修改失败: ' + error.message);
}
}
// 页面加载时检查登录状态
document.addEventListener('DOMContentLoaded', function() {
if (isLoggedIn()) {
hideLoginPage();
} else {
showLoginPage();
}
});
// 标签切换
function switchTab(tabName) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.querySelector(`.tab[onclick="switchTab('${tabName}')"]`).classList.add('active');
document.getElementById(`${tabName}-tab`).classList.add('active');
if (tabName === 'config') {
loadConfigs();
} else if (tabName === 'generate') {
loadGenerateConfigs();
} else if (tabName === 'history') {
loadHistory();
} else if (tabName === 'sum-data') {
loadSumData();
}
}
// 格式化日期时间
function formatDateTime(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 页面加载时自动加载配置列表
document.addEventListener('DOMContentLoaded', function() {
loadConfigs();
});