Update: Js 4 Log.html 80%
This commit is contained in:
@@ -24,7 +24,7 @@ export default class FilterPopover {
|
||||
|
||||
_createPopoverHTML() {
|
||||
this.popoverElement = document.createElement('div');
|
||||
this.popoverElement.className = 'hidden z-50 min-w-[12rem] rounded-md border bg-popover bg-white dark:bg-zinc-800 p-2 text-popover-foreground shadow-md';
|
||||
this.popoverElement.className = 'hidden z-50 min-w-[12rem] rounded-md border-1 border-zinc-500/30 bg-popover bg-white dark:bg-zinc-900 p-2 text-popover-foreground shadow-md';
|
||||
this.popoverElement.innerHTML = `
|
||||
<div class="px-2 py-1.5 text-sm font-semibold">${this.title}</div>
|
||||
<div class="space-y-1 p-1">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Filename: frontend/js/pages/logs/index.js
|
||||
import { apiFetchJson } from '../../services/api.js';
|
||||
import LogList from './logList.js';
|
||||
import CustomSelectV2 from '../../components/customSelectV2.js';
|
||||
@@ -8,11 +7,13 @@ import { STATIC_ERROR_MAP, STATUS_CODE_MAP } from './logList.js';
|
||||
import SystemLogTerminal from './systemLog.js';
|
||||
import { initBatchActions } from './batchActions.js';
|
||||
import flatpickr from '../../vendor/flatpickr.js';
|
||||
import LogSettingsModal from './logSettingsModal.js';
|
||||
|
||||
const dataStore = {
|
||||
groups: new Map(),
|
||||
keys: new Map(),
|
||||
};
|
||||
|
||||
class LogsPage {
|
||||
constructor() {
|
||||
this.state = {
|
||||
@@ -40,6 +41,7 @@ class LogsPage {
|
||||
systemControls: document.getElementById('system-logs-controls'),
|
||||
errorTemplate: document.getElementById('error-logs-template'),
|
||||
systemTemplate: document.getElementById('system-logs-template'),
|
||||
settingsBtn: document.querySelector('button[aria-label="日志设置"]'),
|
||||
};
|
||||
this.initialized = !!this.elements.contentContainer;
|
||||
if (this.initialized) {
|
||||
@@ -48,15 +50,87 @@ class LogsPage {
|
||||
this.debouncedLoadAndRender = debounce(() => this.loadAndRenderLogs(), 300);
|
||||
this.fp = null;
|
||||
this.themeObserver = null;
|
||||
this.settingsModal = null;
|
||||
this.currentSettings = {};
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.initialized) return;
|
||||
this._initPermanentEventListeners();
|
||||
await this.loadCurrentSettings();
|
||||
this._initSettingsModal();
|
||||
await this.loadGroupsOnce();
|
||||
this.state.currentView = null;
|
||||
this.switchToView('error');
|
||||
}
|
||||
|
||||
_initSettingsModal() {
|
||||
if (!this.elements.settingsBtn) return;
|
||||
this.settingsModal = new LogSettingsModal({
|
||||
onSave: this.handleSaveSettings.bind(this)
|
||||
});
|
||||
this.elements.settingsBtn.addEventListener('click', () => {
|
||||
|
||||
const settingsForModal = {
|
||||
log_level: this.currentSettings.log_level,
|
||||
auto_cleanup: {
|
||||
enabled: this.currentSettings.log_auto_cleanup_enabled,
|
||||
retention_days: this.currentSettings.log_auto_cleanup_retention_days,
|
||||
exec_time: this.currentSettings.log_auto_cleanup_time,
|
||||
interval: 'daily',
|
||||
}
|
||||
};
|
||||
this.settingsModal.open(settingsForModal);
|
||||
});
|
||||
}
|
||||
|
||||
async loadCurrentSettings() {
|
||||
try {
|
||||
const { success, data } = await apiFetchJson('/admin/settings');
|
||||
if (success) {
|
||||
this.currentSettings = data;
|
||||
} else {
|
||||
console.error('Failed to load settings from server.');
|
||||
this.currentSettings = { log_auto_cleanup_time: '04:05' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load log settings:', error);
|
||||
this.currentSettings = { log_auto_cleanup_time: '04:05' };
|
||||
}
|
||||
}
|
||||
|
||||
async handleSaveSettings(settingsData) {
|
||||
const partialPayload = {
|
||||
"log_level": settingsData.log_level,
|
||||
"log_auto_cleanup_enabled": settingsData.auto_cleanup.enabled,
|
||||
"log_auto_cleanup_time": settingsData.auto_cleanup.exec_time,
|
||||
};
|
||||
if (settingsData.auto_cleanup.enabled) {
|
||||
let retentionDays = settingsData.auto_cleanup.retention_days;
|
||||
if (retentionDays === null || retentionDays <= 0) {
|
||||
retentionDays = 30;
|
||||
}
|
||||
partialPayload.log_auto_cleanup_retention_days = retentionDays;
|
||||
}
|
||||
|
||||
console.log('Sending PARTIAL settings update to /admin/settings:', partialPayload);
|
||||
try {
|
||||
const { success, message } = await apiFetchJson('/admin/settings', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(partialPayload)
|
||||
});
|
||||
if (!success) {
|
||||
throw new Error(message || 'Failed to save settings');
|
||||
}
|
||||
|
||||
Object.assign(this.currentSettings, partialPayload);
|
||||
} catch (error) {
|
||||
console.error('Error saving log settings:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
_initPermanentEventListeners() {
|
||||
this.elements.tabsContainer.addEventListener('click', (event) => {
|
||||
const tabItem = event.target.closest('[data-tab-target]');
|
||||
@@ -68,6 +142,7 @@ class LogsPage {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
switchToView(viewName) {
|
||||
if (this.state.currentView === viewName && this.elements.contentContainer.innerHTML !== '') return;
|
||||
if (this.systemLogTerminal) {
|
||||
@@ -147,7 +222,6 @@ class LogsPage {
|
||||
});
|
||||
this.themeObserver.observe(document.documentElement, { attributes: true });
|
||||
|
||||
// 确保初始状态正确
|
||||
applyTheme();
|
||||
}
|
||||
|
||||
@@ -218,7 +292,6 @@ class LogsPage {
|
||||
}
|
||||
},
|
||||
onReady: (selectedDates, dateStr, instance) => {
|
||||
// 暗黑模式和清除按钮的现有逻辑保持不变
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
instance.calendarContainer.classList.add('dark');
|
||||
}
|
||||
@@ -431,6 +504,7 @@ class LogsPage {
|
||||
console.error("Failed to load key groups:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadAndRenderLogs() {
|
||||
this.state.isLoading = true;
|
||||
this.state.selectedLogIds.clear();
|
||||
@@ -500,7 +574,7 @@ class LogsPage {
|
||||
}
|
||||
async enrichLogsWithKeyNames(logs) {
|
||||
const missingKeyIds = [...new Set(
|
||||
logs.filter(log => log.KeyID && !dataStore.keys.has(log.KeyID)).map(log => log.ID)
|
||||
logs.filter(log => log.KeyID && !dataStore.keys.has(log.KeyID)).map(log => log.KeyID)
|
||||
)];
|
||||
if (missingKeyIds.length === 0) return;
|
||||
try {
|
||||
@@ -514,7 +588,8 @@ class LogsPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function() {
|
||||
const page = new LogsPage();
|
||||
page.init();
|
||||
}
|
||||
}
|
||||
|
||||
148
frontend/js/pages/logs/logSettingsModal.js
Normal file
148
frontend/js/pages/logs/logSettingsModal.js
Normal file
@@ -0,0 +1,148 @@
|
||||
// Filename: frontend/js/pages/logs/logList.js
|
||||
import { modalManager } from '../../components/ui.js';
|
||||
|
||||
export default class LogSettingsModal {
|
||||
constructor({ onSave }) {
|
||||
this.modalId = 'log-settings-modal';
|
||||
this.onSave = onSave;
|
||||
const modal = document.getElementById(this.modalId);
|
||||
if (!modal) {
|
||||
throw new Error(`Modal with id "${this.modalId}" not found.`);
|
||||
}
|
||||
|
||||
this.elements = {
|
||||
modal: modal,
|
||||
title: document.getElementById('log-settings-modal-title'),
|
||||
saveBtn: document.getElementById('log-settings-save-btn'),
|
||||
|
||||
logLevelSelect: document.getElementById('log-level-select'),
|
||||
|
||||
cleanupEnableToggle: document.getElementById('log-cleanup-enable'),
|
||||
cleanupSettingsPanel: document.getElementById('log-cleanup-settings'),
|
||||
cleanupRetentionInput: document.getElementById('log-cleanup-retention-days'),
|
||||
retentionDaysGroup: document.getElementById('retention-days-group'),
|
||||
retentionPresetBtns: document.querySelectorAll('#retention-days-group button[data-days]'),
|
||||
cleanupExecTimeInput: document.getElementById('log-cleanup-exec-time'), // [NEW] 添加时间选择器元素
|
||||
};
|
||||
|
||||
this.activePresetClasses = ['!bg-primary', '!text-primary-foreground', '!border-primary', 'hover:!bg-primary/90'];
|
||||
this.inactivePresetClasses = ['modal-btn-secondary'];
|
||||
this._initEventListeners();
|
||||
}
|
||||
|
||||
open(settingsData = {}) {
|
||||
this._populateForm(settingsData);
|
||||
modalManager.show(this.modalId);
|
||||
}
|
||||
|
||||
close() {
|
||||
modalManager.hide(this.modalId);
|
||||
}
|
||||
|
||||
_initEventListeners() {
|
||||
this.elements.saveBtn.addEventListener('click', this._handleSave.bind(this));
|
||||
|
||||
this.elements.cleanupEnableToggle.addEventListener('change', (e) => {
|
||||
this.elements.cleanupSettingsPanel.classList.toggle('hidden', !e.target.checked);
|
||||
});
|
||||
|
||||
this._initRetentionPresets();
|
||||
|
||||
const closeAction = () => this.close();
|
||||
const closeTriggers = this.elements.modal.querySelectorAll(`[data-modal-close="${this.modalId}"]`);
|
||||
closeTriggers.forEach(trigger => trigger.addEventListener('click', closeAction));
|
||||
this.elements.modal.addEventListener('click', (event) => {
|
||||
if (event.target === this.elements.modal) closeAction();
|
||||
});
|
||||
}
|
||||
|
||||
_initRetentionPresets() {
|
||||
this.elements.retentionPresetBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const days = btn.dataset.days;
|
||||
this.elements.cleanupRetentionInput.value = days;
|
||||
this._updateActivePresetButton(days);
|
||||
});
|
||||
});
|
||||
|
||||
this.elements.cleanupRetentionInput.addEventListener('input', (e) => {
|
||||
this._updateActivePresetButton(e.target.value);
|
||||
});
|
||||
}
|
||||
|
||||
_updateActivePresetButton(currentValue) {
|
||||
this.elements.retentionPresetBtns.forEach(btn => {
|
||||
if (btn.dataset.days === currentValue) {
|
||||
btn.classList.remove(...this.inactivePresetClasses);
|
||||
btn.classList.add(...this.activePresetClasses);
|
||||
} else {
|
||||
btn.classList.remove(...this.activePresetClasses);
|
||||
btn.classList.add(...this.inactivePresetClasses);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async _handleSave() {
|
||||
const data = this._collectFormData();
|
||||
if (data.auto_cleanup.enabled && (!data.auto_cleanup.retention_days || data.auto_cleanup.retention_days <= 0)) {
|
||||
alert('启用自动清理时,保留天数必须是大于0的数字。');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onSave) {
|
||||
this.elements.saveBtn.disabled = true;
|
||||
this.elements.saveBtn.textContent = '保存中...';
|
||||
try {
|
||||
await this.onSave(data);
|
||||
this.close();
|
||||
} catch (error) {
|
||||
console.error("Failed to save log settings:", error);
|
||||
// 可以添加一个UI提示,比如 toast
|
||||
} finally {
|
||||
this.elements.saveBtn.disabled = false;
|
||||
this.elements.saveBtn.textContent = '保存设置';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [MODIFIED] - 更新此方法以填充新的时间选择器
|
||||
_populateForm(data) {
|
||||
this.elements.logLevelSelect.value = data.log_level || 'INFO';
|
||||
|
||||
const cleanup = data.auto_cleanup || {};
|
||||
const isCleanupEnabled = cleanup.enabled || false;
|
||||
this.elements.cleanupEnableToggle.checked = isCleanupEnabled;
|
||||
this.elements.cleanupSettingsPanel.classList.toggle('hidden', !isCleanupEnabled);
|
||||
|
||||
const retentionDays = cleanup.retention_days || '';
|
||||
this.elements.cleanupRetentionInput.value = retentionDays;
|
||||
this._updateActivePresetButton(retentionDays.toString());
|
||||
|
||||
// [NEW] 填充执行时间,提供一个安全的默认值
|
||||
this.elements.cleanupExecTimeInput.value = cleanup.exec_time || '04:05';
|
||||
}
|
||||
|
||||
// [MODIFIED] - 更新此方法以收集新的时间数据
|
||||
_collectFormData() {
|
||||
const parseIntOrNull = (value) => {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed === '') return null;
|
||||
const num = parseInt(trimmed, 10);
|
||||
return isNaN(num) ? null : num;
|
||||
};
|
||||
|
||||
const isCleanupEnabled = this.elements.cleanupEnableToggle.checked;
|
||||
|
||||
const formData = {
|
||||
log_level: this.elements.logLevelSelect.value,
|
||||
auto_cleanup: {
|
||||
enabled: isCleanupEnabled,
|
||||
interval: isCleanupEnabled ? 'daily' : null,
|
||||
retention_days: isCleanupEnabled ? parseIntOrNull(this.elements.cleanupRetentionInput.value) : null,
|
||||
exec_time: isCleanupEnabled ? this.elements.cleanupExecTimeInput.value : '04:05', // [NEW] 收集时间数据
|
||||
},
|
||||
};
|
||||
|
||||
return formData;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user