This commit is contained in:
Baobhan Sith
2025-04-25 17:19:25 +08:00
parent e15789d1d3
commit c02556dc0b
6 changed files with 308 additions and 93 deletions
+72 -23
View File
@@ -11,54 +11,103 @@ export const useAuditLogStore = defineStore('auditLog', () => {
const currentPage = ref(1);
const logsPerPage = ref(50); // Default page size
// fetchLogs 现在接受一个选项对象作为参数
// fetchLogs 现在接受一个选项对象作为参数,并增加了缓存逻辑
const fetchLogs = async (options: {
page?: number;
limit?: number; // 新增 limit 参数
limit?: number;
searchTerm?: string;
actionType?: AuditLogActionType | '';
sortOrder?: 'asc' | 'desc'; // 新增 sortOrder 参数
sortOrder?: 'asc' | 'desc';
// 新增一个标志,明确指示是否为仪表盘调用,以启用缓存
isDashboardRequest?: boolean;
} = {}) => {
const {
page = 1,
limit = logsPerPage.value, // 优先使用传入的 limit,否则使用 store 的默认值
limit = logsPerPage.value,
searchTerm,
actionType,
sortOrder
sortOrder,
isDashboardRequest = false // 默认为 false
} = options;
isLoading.value = true;
error.value = null;
currentPage.value = page; // 仍然更新当前页码状态
const offset = (page - 1) * limit; // offset 计算基于实际使用的 limit
const cacheKey = 'dashboardAuditLogsCache';
error.value = null; // 重置错误
// --- 缓存逻辑 (仅当 isDashboardRequest 为 true 时触发) ---
if (isDashboardRequest) {
try {
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
console.log('[AuditLogStore] Loading dashboard logs from cache.');
// 仪表盘只关心日志列表,不关心 totalLogs 或 currentPage
logs.value = JSON.parse(cachedData);
isLoading.value = false; // 先显示缓存
} else {
isLoading.value = true; // 无缓存,初始加载
}
} catch (e) {
console.error('[AuditLogStore] Failed to load or parse dashboard logs cache:', e);
localStorage.removeItem(cacheKey);
isLoading.value = true; // 缓存无效,需要加载
}
} else {
// 非仪表盘请求(如完整日志页),总是显示加载状态
isLoading.value = true;
currentPage.value = page; // 更新分页状态
}
// --- API 请求逻辑 ---
isLoading.value = true; // 标记正在获取(或后台获取)
const offset = (page - 1) * limit;
try {
const params: Record<string, any> = {
limit: limit, // 使用实际的 limit
limit: limit,
offset: offset,
// 条件性添加其他参数
...(searchTerm && { search: searchTerm }),
...(actionType && { action_type: actionType }),
...(sortOrder && { sort_order: sortOrder }), // 添加 sort_order 参数
...(sortOrder && { sort_order: sortOrder }),
};
const response = await apiClient.get<AuditLogApiResponse>('/audit-logs', { params }); // 使用 apiClient
// 注意:如果 fetchLogs 被用于分页,这里直接赋值 logs.value 可能不是最佳实践
// 但对于仪表盘只获取少量最新日志的场景是可行的。
// 如果需要支持加载更多,需要修改这里的逻辑为追加或替换。
logs.value = response.data.logs;
totalLogs.value = response.data.total;
console.log(`[AuditLogStore] Fetching logs from server (isDashboard: ${isDashboardRequest}). Params:`, params);
const response = await apiClient.get<AuditLogApiResponse>('/audit-logs', { params });
const freshLogs = response.data.logs;
const freshTotal = response.data.total;
// --- 更新状态和缓存 ---
if (isDashboardRequest) {
const freshLogsString = JSON.stringify(freshLogs);
const currentLogsString = JSON.stringify(logs.value);
if (currentLogsString !== freshLogsString) {
console.log('[AuditLogStore] Dashboard logs data changed, updating state and cache.');
logs.value = freshLogs;
localStorage.setItem(cacheKey, freshLogsString); // 更新缓存
} else {
console.log('[AuditLogStore] Dashboard logs data is up-to-date.');
}
// 仪表盘请求不更新 totalLogs 或 currentPage
} else {
// 非仪表盘请求,直接更新日志和总数
console.log('[AuditLogStore] Updating logs for full view.');
logs.value = freshLogs;
totalLogs.value = freshTotal;
}
error.value = null; // 清除错误
} catch (err: any) {
console.error('Error fetching audit logs:', err);
console.error('[AuditLogStore] Error fetching audit logs:', err);
error.value = err.response?.data?.message || '获取审计日志失败';
logs.value = [];
totalLogs.value = 0;
// 如果是仪表盘请求失败,保留缓存数据;否则清空
if (!isDashboardRequest) {
logs.value = [];
totalLogs.value = 0;
}
} finally {
isLoading.value = false;
isLoading.value = false; // 加载完成
}
};
// Function to change page size and refetch
// Function to change page size and refetch (非仪表盘场景)
const setLogsPerPage = (size: number) => {
logsPerPage.value = size;
fetchLogs({ page: 1 }); // 重置到第一页,使用默认 limit
@@ -58,26 +58,57 @@ export const useCommandHistoryStore = defineStore('commandHistory', () => {
selectedIndex.value = (selectedIndex.value - 1 + history.length) % history.length;
};
// 从后端获取历史记录
// 从后端获取历史记录 (带缓存)
const fetchHistory = async () => {
isLoading.value = true;
error.value = null;
const cacheKey = 'commandHistoryCache';
error.value = null; // 重置错误
// 1. 尝试从 localStorage 加载缓存
try {
const response = await apiClient.get<CommandHistoryEntryBE[]>('/command-history'); // 使用 apiClient
// 后端返回的是按时间戳升序 (旧->新)
// 前端需要按时间戳降序 (新->旧),所以反转数组
historyList.value = response.data.reverse();
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
console.log('[CmdHistoryStore] Loading history from cache.');
historyList.value = JSON.parse(cachedData); // 缓存中已是降序
isLoading.value = false; // 先显示缓存
} else {
isLoading.value = true; // 无缓存,初始加载
}
} catch (e) {
console.error('[CmdHistoryStore] Failed to load or parse history cache:', e);
localStorage.removeItem(cacheKey); // 解析失败则移除缓存
isLoading.value = true; // 缓存无效,需要加载
}
// 2. 后台获取最新数据
isLoading.value = true; // 标记正在后台获取
try {
console.log('[CmdHistoryStore] Fetching latest history from server...');
const response = await apiClient.get<CommandHistoryEntryBE[]>('/command-history');
// 后端返回升序,前端需要降序
const freshData = response.data.reverse();
const freshDataString = JSON.stringify(freshData);
// 3. 对比并更新
const currentDataString = JSON.stringify(historyList.value);
if (currentDataString !== freshDataString) {
console.log('[CmdHistoryStore] History data changed, updating state and cache.');
historyList.value = freshData;
localStorage.setItem(cacheKey, freshDataString); // 更新缓存 (存降序)
} else {
console.log('[CmdHistoryStore] History data is up-to-date.');
}
error.value = null; // 清除错误
} catch (err: any) {
console.error('获取命令历史记录失败:', err);
console.error('[CmdHistoryStore] 获取命令历史记录失败:', err);
error.value = err.response?.data?.message || '获取历史记录时发生错误';
// 确保传递给 showError 的是字符串
uiNotificationsStore.showError(error.value ?? '未知错误'); // 显示错误通知
// 保留缓存数据,仅设置错误状态
uiNotificationsStore.showError(error.value ?? '未知错误');
} finally {
isLoading.value = false;
isLoading.value = false; // 加载完成
}
};
// 添加命令到历史记录 (由 CommandInputBar 调用)
// 添加命令到历史记录 (由 CommandInputBar 调用, 添加后清除缓存)
const addCommand = async (command: string) => {
if (!command || command.trim().length === 0) {
return; // 不添加空命令
@@ -85,8 +116,9 @@ export const useCommandHistoryStore = defineStore('commandHistory', () => {
try {
const response = await apiClient.post<{ id: number }>('/command-history', { command: command.trim() }); // 使用 apiClient
// 添加成功后,重新获取列表以保证顺序和 ID 正确
// 或者,可以在本地模拟添加,但为了简单和一致性,重新获取更好
await fetchHistory();
// 添加成功后,清除缓存并重新获取
localStorage.removeItem('commandHistoryCache');
await fetchHistory(); // fetchHistory 会处理获取和缓存更新
} catch (err: any) {
console.error('添加命令历史记录失败:', err);
const message = err.response?.data?.message || '添加历史记录时发生错误';
@@ -98,8 +130,9 @@ export const useCommandHistoryStore = defineStore('commandHistory', () => {
// 删除单条历史记录
const deleteCommand = async (id: number) => {
try {
await apiClient.delete(`/command-history/${id}`); // 使用 apiClient
// 本地列表中移除
await apiClient.delete(`/command-history/${id}`);
// 删除成功后,清除缓存并更新本地列表
localStorage.removeItem('commandHistoryCache');
const index = historyList.value.findIndex(entry => entry.id === id);
if (index !== -1) {
historyList.value.splice(index, 1);
@@ -116,8 +149,10 @@ export const useCommandHistoryStore = defineStore('commandHistory', () => {
const clearAllHistory = async () => {
// 可以在调用前添加确认逻辑 (例如在组件层)
try {
await apiClient.delete('/command-history'); // 使用 apiClient
historyList.value = []; // 清空本地列表
await apiClient.delete('/command-history');
// 清空成功后,清除缓存并清空本地列表
localStorage.removeItem('commandHistoryCache');
historyList.value = [];
uiNotificationsStore.showSuccess('所有历史记录已清空');
} catch (err: any) {
console.error('清空命令历史记录失败:', err);
@@ -31,28 +31,60 @@ export const useConnectionsStore = defineStore('connections', {
error: null,
}),
actions: {
// 获取连接列表 Action
// 获取连接列表 Action (带缓存)
async fetchConnections() {
this.isLoading = true;
this.error = null;
const cacheKey = 'connectionsCache';
this.error = null; // 重置错误状态
// 1. 尝试从 localStorage 加载缓存
try {
// 注意:axios 默认会携带 cookie,因此如果用户已登录,会话 cookie 会被发送
const response = await apiClient.get<ConnectionInfo[]>('/connections'); // 使用 apiClient
this.connections = response.data;
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
console.log('[ConnectionsStore] Loading connections from cache.');
this.connections = JSON.parse(cachedData);
this.isLoading = false; // 先显示缓存,设置为 false
} else {
// 没有缓存时,初始加载状态设为 true
this.isLoading = true;
}
} catch (e) {
console.error('[ConnectionsStore] Failed to load or parse connections cache:', e);
localStorage.removeItem(cacheKey); // 解析失败则移除缓存
this.isLoading = true; // 缓存无效,需要加载
}
// 2. 后台获取最新数据
this.isLoading = true; // 标记正在后台获取
try {
console.log('[ConnectionsStore] Fetching latest connections from server...');
const response = await apiClient.get<ConnectionInfo[]>('/connections');
const freshData = response.data;
const freshDataString = JSON.stringify(freshData);
// 3. 对比并更新
const currentDataString = JSON.stringify(this.connections);
if (currentDataString !== freshDataString) {
console.log('[ConnectionsStore] Connections data changed, updating state and cache.');
this.connections = freshData;
localStorage.setItem(cacheKey, freshDataString); // 更新缓存
} else {
console.log('[ConnectionsStore] Connections data is up-to-date.');
}
this.error = null; // 清除之前的错误(如果有)
} catch (err: any) {
console.error('获取连接列表失败:', err);
console.error('[ConnectionsStore] 获取连接列表失败:', err);
this.error = err.response?.data?.message || err.message || '获取连接列表时发生未知错误。';
// 如果是 401 未授权,可能需要触发重新登录逻辑
// 保留缓存数据,仅设置错误状态
if (err.response?.status === 401) {
// TODO: 处理未授权情况,例如跳转到登录页
console.warn('未授权,需要登录才能获取连接列表。');
console.warn('[ConnectionsStore] 未授权,需要登录才能获取连接列表。');
// 可能需要触发全局的未授权处理逻辑
}
} finally {
this.isLoading = false;
this.isLoading = false; // 无论成功失败,最终加载完成
}
},
// 添加新连接 Action
// 添加新连接 Action (添加后应清除缓存或重新获取)
// 更新参数类型以接受新的认证字段
async addConnection(newConnectionData: {
name: string;
@@ -70,8 +102,11 @@ export const useConnectionsStore = defineStore('connections', {
this.error = null;
try {
const response = await apiClient.post<{ message: string; connection: ConnectionInfo }>('/connections', newConnectionData); // 使用 apiClient
// 添加成功后,将新连接添加到列表前面 (或重新获取整个列表)
this.connections.unshift(response.data.connection);
// 添加成功后,清除缓存以便下次获取最新数据
localStorage.removeItem('connectionsCache');
// 可以选择重新获取整个列表,或者仅在本地添加
// this.connections.unshift(response.data.connection); // 本地添加可能导致与缓存不一致,建议重新获取
await this.fetchConnections(); // 推荐重新获取以保证数据一致性
return true; // 表示成功
} catch (err: any) {
console.error('添加连接失败:', err);
@@ -102,8 +137,14 @@ export const useConnectionsStore = defineStore('connections', {
// 注意:后端返回的 connection 可能不包含敏感信息,但应包含更新后的非敏感字段
this.connections[index] = { ...this.connections[index], ...response.data.connection };
} else {
// 如果本地找不到,可能需要重新获取列表
await this.fetchConnections();
// 如果本地找不到,fetchConnections 会处理
// await this.fetchConnections(); // fetchConnections 内部会处理
}
// 更新成功后,清除缓存以便下次获取最新数据
localStorage.removeItem('connectionsCache');
// 重新获取以确保数据同步(如果上面没有找到 index 并调用 fetchConnections
if (index !== -1) { // 只有在本地找到并更新后才需要手动触发刷新缓存
await this.fetchConnections(); // 重新获取以更新缓存和状态
}
return true; // 表示成功
} catch (err: any) {
@@ -126,8 +167,12 @@ export const useConnectionsStore = defineStore('connections', {
// 发送 DELETE 请求到 /api/v1/connections/:id
await apiClient.delete(`/connections/${connectionId}`); // 使用 apiClient
// 删除成功后,从本地列表中移除该连接
// 删除成功后,清除缓存以便下次获取最新数据
localStorage.removeItem('connectionsCache');
// 从本地列表中移除该连接
this.connections = this.connections.filter(conn => conn.id !== connectionId);
// 可以选择重新获取,但 filter 已经更新了本地状态,下次 fetch 会自动更新缓存
// await this.fetchConnections();
return true; // 表示成功
} catch (err: any) {
console.error(`删除连接 ${connectionId} 失败:`, err);
@@ -74,29 +74,75 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
selectedIndex.value = (selectedIndex.value - 1 + commands.length) % commands.length;
};
// 从后端获取快捷指令 (带排序)
// 从后端获取快捷指令 (带缓存和排序)
const fetchQuickCommands = async () => {
isLoading.value = true;
error.value = null;
const cacheKey = 'quickCommandsCache';
// 将排序方式加入缓存键,确保不同排序有不同缓存
const cacheKeyWithSort = `${cacheKey}_${sortBy.value}`;
error.value = null; // 重置错误
// 1. 尝试从 localStorage 加载缓存
try {
const response = await apiClient.get<QuickCommandFE[]>('/quick-commands', { // 使用 apiClient
params: { sortBy: sortBy.value } // 将排序参数传递给后端
const cachedData = localStorage.getItem(cacheKeyWithSort);
if (cachedData) {
console.log(`[QuickCmdStore] Loading commands from cache (sort: ${sortBy.value}).`);
quickCommandsList.value = JSON.parse(cachedData);
isLoading.value = false; // 先显示缓存
} else {
isLoading.value = true; // 无缓存,初始加载
}
} catch (e) {
console.error('[QuickCmdStore] Failed to load or parse commands cache:', e);
localStorage.removeItem(cacheKeyWithSort); // 解析失败则移除缓存
isLoading.value = true; // 缓存无效,需要加载
}
// 2. 后台获取最新数据
isLoading.value = true; // 标记正在后台获取
try {
console.log(`[QuickCmdStore] Fetching latest commands from server (sort: ${sortBy.value})...`);
const response = await apiClient.get<QuickCommandFE[]>('/quick-commands', {
params: { sortBy: sortBy.value }
});
quickCommandsList.value = response.data;
const freshData = response.data;
const freshDataString = JSON.stringify(freshData);
// 3. 对比并更新
const currentDataString = JSON.stringify(quickCommandsList.value);
if (currentDataString !== freshDataString) {
console.log('[QuickCmdStore] Commands data changed, updating state and cache.');
quickCommandsList.value = freshData;
localStorage.setItem(cacheKeyWithSort, freshDataString); // 更新对应排序的缓存
} else {
console.log('[QuickCmdStore] Commands data is up-to-date.');
}
error.value = null; // 清除错误
} catch (err: any) {
console.error('获取快捷指令失败:', err);
console.error('[QuickCmdStore] 获取快捷指令失败:', err);
error.value = err.response?.data?.message || '获取快捷指令时发生错误';
// 保留缓存数据,仅设置错误状态
uiNotificationsStore.showError(error.value ?? '未知错误');
} finally {
isLoading.value = false;
isLoading.value = false; // 加载完成
}
};
// 添加快捷指令
// 清除所有排序的快捷指令缓存
const clearQuickCommandsCache = () => {
const cacheKeyBase = 'quickCommandsCache';
// 移除两种排序的缓存
localStorage.removeItem(`${cacheKeyBase}_name`);
localStorage.removeItem(`${cacheKeyBase}_usage_count`);
console.log('[QuickCmdStore] Cleared all quick commands caches.');
};
// 添加快捷指令 (添加后清除缓存)
const addQuickCommand = async (name: string | null, command: string): Promise<boolean> => {
try {
await apiClient.post('/quick-commands', { name, command }); // 使用 apiClient
await fetchQuickCommands(); // 添加成功后刷新列表
await apiClient.post('/quick-commands', { name, command });
clearQuickCommandsCache(); // 清除所有排序缓存
await fetchQuickCommands(); // 刷新当前排序的列表和缓存
uiNotificationsStore.showSuccess('快捷指令已添加');
return true;
} catch (err: any) {
@@ -110,8 +156,9 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
// 更新快捷指令
const updateQuickCommand = async (id: number, name: string | null, command: string): Promise<boolean> => {
try {
await apiClient.put(`/quick-commands/${id}`, { name, command }); // 使用 apiClient
await fetchQuickCommands(); // 更新成功后刷新列表
await apiClient.put(`/quick-commands/${id}`, { name, command });
clearQuickCommandsCache(); // 清除所有排序缓存
await fetchQuickCommands(); // 刷新当前排序的列表和缓存
uiNotificationsStore.showSuccess('快捷指令已更新');
return true;
} catch (err: any) {
@@ -125,8 +172,9 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
// 删除快捷指令
const deleteQuickCommand = async (id: number) => {
try {
await apiClient.delete(`/quick-commands/${id}`); // 使用 apiClient
// 从本地列表中移除,避免重新请求
await apiClient.delete(`/quick-commands/${id}`);
clearQuickCommandsCache(); // 清除所有排序缓存
// 从本地列表中移除
const index = quickCommandsList.value.findIndex(cmd => cmd.id === id);
if (index !== -1) {
quickCommandsList.value.splice(index, 1);
@@ -149,7 +197,8 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
command.usage_count += 1;
// 如果当前是按使用次数排序,可能需要重新排序或刷新列表
if (sortBy.value === 'usage_count') {
// 简单起见,重新获取排序
// 清除所有排序缓存并重新获取当前排序
clearQuickCommandsCache();
await fetchQuickCommands();
}
}
@@ -169,7 +218,8 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
const setSortBy = async (newSortBy: QuickCommandSortByType) => {
if (sortBy.value !== newSortBy) {
sortBy.value = newSortBy;
await fetchQuickCommands(); // 排序方式改变,重新获取数据
// 排序方式改变,不需要清除缓存,fetchQuickCommands 会读取对应排序的缓存或重新获取
await fetchQuickCommands();
}
};
+50 -16
View File
@@ -15,33 +15,65 @@ export const useTagsStore = defineStore('tags', () => {
const isLoading = ref(false);
const error = ref<string | null>(null);
// 获取标签列表
// 获取标签列表 (带缓存)
async function fetchTags() {
isLoading.value = true;
error.value = null;
const cacheKey = 'tagsCache';
error.value = null; // 重置错误
// 1. 尝试从 localStorage 加载缓存
try {
const response = await apiClient.get<TagInfo[]>('/tags'); // 使用 apiClient 并移除 base URL
tags.value = response.data;
return true;
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
console.log('[TagsStore] Loading tags from cache.');
tags.value = JSON.parse(cachedData);
isLoading.value = false; // 先显示缓存
} else {
isLoading.value = true; // 无缓存,初始加载
}
} catch (e) {
console.error('[TagsStore] Failed to load or parse tags cache:', e);
localStorage.removeItem(cacheKey); // 解析失败则移除缓存
isLoading.value = true; // 缓存无效,需要加载
}
// 2. 后台获取最新数据
isLoading.value = true; // 标记正在后台获取
try {
console.log('[TagsStore] Fetching latest tags from server...');
const response = await apiClient.get<TagInfo[]>('/tags');
const freshData = response.data;
const freshDataString = JSON.stringify(freshData);
// 3. 对比并更新
const currentDataString = JSON.stringify(tags.value);
if (currentDataString !== freshDataString) {
console.log('[TagsStore] Tags data changed, updating state and cache.');
tags.value = freshData;
localStorage.setItem(cacheKey, freshDataString); // 更新缓存
} else {
console.log('[TagsStore] Tags data is up-to-date.');
}
error.value = null; // 清除错误
return true; // 表示获取成功(即使数据未变)
} catch (err: any) {
console.error('Failed to fetch tags:', err);
console.error('[TagsStore] Failed to fetch tags:', err);
error.value = err.response?.data?.message || err.message || '获取标签列表失败';
return false;
// 保留缓存数据,仅设置错误状态
return false; // 表示获取失败
} finally {
isLoading.value = false;
isLoading.value = false; // 加载完成
}
}
// 添加新标签
// 添加新标签 (添加后清除缓存)
async function addTag(name: string): Promise<boolean> {
isLoading.value = true;
error.value = null;
try {
const response = await apiClient.post<{ message: string, tag: TagInfo }>('/tags', { name }); // 使用 apiClient 并移除 base URL
// 添加成功后,重新获取列表以保证数据同步 (或者直接将新标签添加到 ref)
await fetchTags(); // 简单起见,重新获取
// tags.value.push(response.data.tag); // 另一种方式
// tags.value.sort((a, b) => a.name.localeCompare(b.name)); // 保持排序
// 添加成功后,清除缓存并重新获取
localStorage.removeItem('tagsCache');
await fetchTags(); // fetchTags 会处理获取和缓存更新
return true;
} catch (err: any) {
console.error('Failed to add tag:', err);
@@ -58,7 +90,8 @@ export const useTagsStore = defineStore('tags', () => {
error.value = null;
try {
await apiClient.put(`/tags/${id}`, { name }); // 使用 apiClient 并移除 base URL
// 更新成功后,重新获取列表
// 更新成功后,清除缓存并重新获取
localStorage.removeItem('tagsCache');
await fetchTags();
return true;
} catch (err: any) {
@@ -76,7 +109,8 @@ export const useTagsStore = defineStore('tags', () => {
error.value = null;
try {
await apiClient.delete(`/tags/${id}`); // 使用 apiClient 并移除 base URL
// 删除成功后,重新获取列表
// 删除成功后,清除缓存并重新获取
localStorage.removeItem('tagsCache');
await fetchTags();
return true;
} catch (err: any) {
@@ -68,10 +68,12 @@ onMounted(async () => {
// 加载最新的审计日志
try {
// 只需要加载少量日志用于摘要,并按时间倒序
// 调用 fetchLogs 并明确指示这是仪表盘请求以启用缓存
await auditLogStore.fetchLogs({
page: 1,
limit: maxRecentLogs, // 传递 limit
sortOrder: 'desc' // 传递 sortOrder
limit: maxRecentLogs,
sortOrder: 'desc',
isDashboardRequest: true // <--- 添加此标志
});
} catch (error) {
console.error("加载审计日志失败:", error);