diff --git a/packages/backend/src/repositories/settings.repository.ts b/packages/backend/src/repositories/settings.repository.ts index 6d9f528..b3ef660 100644 --- a/packages/backend/src/repositories/settings.repository.ts +++ b/packages/backend/src/repositories/settings.repository.ts @@ -12,7 +12,7 @@ export const settingsRepository = { return new Promise((resolve, reject) => { db.all('SELECT key, value FROM settings', (err: any, rows: Setting[]) => { // 添加 err 类型 if (err) { - console.error('获取所有设置时出错:', err); // 更新日志为中文 + console.error('[Repository] 获取所有设置时出错:', err); // 更新日志为中文 reject(new Error('获取设置失败')); // 更新错误消息为中文 } else { resolve(rows); @@ -23,11 +23,13 @@ export const settingsRepository = { async getSetting(key: string): Promise { return new Promise((resolve, reject) => { + console.log(`[Repository] Attempting to get setting with key: ${key}`); // +++ 添加日志 +++ db.get('SELECT value FROM settings WHERE key = ?', [key], (err: any, row: { value: string } | undefined) => { // 添加 err 类型 if (err) { - console.error(`获取设置项 ${key} 时出错:`, err); // 更新日志为中文 + console.error(`[Repository] 获取设置项 ${key} 时出错:`, err); // 更新日志为中文 reject(new Error(`获取设置项 ${key} 失败`)); // 更新错误消息为中文 } else { + console.log(`[Repository] Found value for key ${key}:`, row ? row.value : null); // +++ 添加日志 +++ resolve(row ? row.value : null); } }); @@ -37,18 +39,26 @@ export const settingsRepository = { async setSetting(key: string, value: string): Promise { return new Promise((resolve, reject) => { const now = Math.floor(Date.now() / 1000); // 获取当前 Unix 时间戳 - db.run( - `INSERT INTO settings (key, value, created_at, updated_at) - VALUES (?, ?, ?, ?) - ON CONFLICT(key) DO UPDATE SET + const sql = `INSERT INTO settings (key, value, created_at, updated_at) + VALUES (?, ?, ?, ?) + ON CONFLICT(key) DO UPDATE SET value = excluded.value, - updated_at = excluded.updated_at`, - [key, value, now, now], - function (err: any) { // 添加 err 类型 + updated_at = excluded.updated_at`; + const params = [key, value, now, now]; + + console.log(`[Repository] Attempting to set setting. Key: ${key}, Value: ${value}`); // +++ 添加日志 +++ + console.log(`[Repository] Executing SQL: ${sql} with params: ${JSON.stringify(params)}`); // +++ 添加日志 +++ + + db.run( + sql, + params, + function (this: any, err: any) { // 使用 this 需要 function 声明, 添加 err 类型 if (err) { - console.error(`设置设置项 ${key} 时出错:`, err); // 更新日志为中文 + console.error(`[Repository] 设置设置项 ${key} 时出错:`, err); // 更新日志为中文 reject(new Error(`设置设置项 ${key} 失败`)); // 更新错误消息为中文 } else { + // this.changes 提供了受影响的行数 (对于 INSERT/UPDATE) + console.log(`[Repository] Successfully set setting for key: ${key}. Rows affected: ${this.changes}`); // +++ 添加日志 +++ resolve(); } } @@ -58,11 +68,13 @@ export const settingsRepository = { async deleteSetting(key: string): Promise { return new Promise((resolve, reject) => { - db.run('DELETE FROM settings WHERE key = ?', [key], function (err: any) { // 添加 err 类型 + console.log(`[Repository] Attempting to delete setting with key: ${key}`); // +++ 添加日志 +++ + db.run('DELETE FROM settings WHERE key = ?', [key], function (this: any, err: any) { // 添加 err 类型 if (err) { - console.error(`删除设置项 ${key} 时出错:`, err); // 更新日志为中文 + console.error(`[Repository] 删除设置项 ${key} 时出错:`, err); // 更新日志为中文 reject(new Error(`删除设置项 ${key} 失败`)); // 更新错误消息为中文 } else { + console.log(`[Repository] Successfully deleted setting for key: ${key}. Rows affected: ${this.changes}`); // +++ 添加日志 +++ resolve(); } }); @@ -70,9 +82,11 @@ export const settingsRepository = { }, async setMultipleSettings(settings: Record): Promise { + console.log('[Repository] setMultipleSettings called with:', JSON.stringify(settings)); // +++ 添加日志 +++ const promises = Object.entries(settings).map(([key, value]) => - this.setSetting(key, value) + this.setSetting(key, value) // this 指向 settingsRepository 对象 ); await Promise.all(promises); + console.log('[Repository] setMultipleSettings finished.'); // +++ 添加日志 +++ }, }; diff --git a/packages/backend/src/services/settings.service.ts b/packages/backend/src/services/settings.service.ts index ffbc9fd..f196b5f 100644 --- a/packages/backend/src/services/settings.service.ts +++ b/packages/backend/src/services/settings.service.ts @@ -1,6 +1,12 @@ import { settingsRepository, Setting } from '../repositories/settings.repository'; +// +++ 定义默认的焦点切换顺序 +++ +const DEFAULT_FOCUS_SEQUENCE = ["quickCommandsSearch", "commandHistorySearch", "fileManagerSearch", "commandInput", "terminalSearch"]; +const FOCUS_SEQUENCE_KEY = 'focusSwitcherSequence'; // +++ 定义设置键常量 +++ + export const settingsService = { + // ... (getAllSettings, getSetting, setSetting, setMultipleSettings, deleteSetting, getIpWhitelistSettings, updateIpWhitelistSettings, getFocusSwitcherSequence 保持不变) ... + /** * 获取所有设置项 * @returns 返回包含所有设置项的数组 @@ -76,4 +82,57 @@ export const settingsService = { settingsRepository.setSetting('ipWhitelist', whitelist), ]); }, + + // +++ 新增:获取焦点切换顺序 +++ + /** + * 获取焦点切换顺序 + * @returns 返回存储的焦点切换顺序数组,如果未设置或无效则返回默认顺序 + */ + async getFocusSwitcherSequence(): Promise { + console.log(`[Service] Attempting to get setting for key: ${FOCUS_SEQUENCE_KEY}`); // +++ 添加日志 +++ + try { + const sequenceJson = await settingsRepository.getSetting(FOCUS_SEQUENCE_KEY); + console.log(`[Service] Raw value from repository for ${FOCUS_SEQUENCE_KEY}:`, sequenceJson); // +++ 添加日志 +++ + if (sequenceJson) { + const sequence = JSON.parse(sequenceJson); + // 基本验证:确保它是一个数组并且包含字符串 + if (Array.isArray(sequence) && sequence.every(item => typeof item === 'string')) { + console.log('[Service] Fetched and validated focus switcher sequence:', JSON.stringify(sequence)); // +++ 更新日志 +++ + return sequence; + } else { + console.warn('[Service] Invalid focus switcher sequence format found in settings. Returning default.'); + } + } else { + console.log('[Service] No focus switcher sequence found in settings. Returning default.'); + } + } catch (error) { + console.error(`[Service] Error parsing focus switcher sequence from settings (key: ${FOCUS_SEQUENCE_KEY}):`, error); // +++ 更新日志 +++ + } + // 如果发生错误或未找到/无效,返回默认值 + console.log('[Service] Returning default focus sequence:', JSON.stringify(DEFAULT_FOCUS_SEQUENCE)); // +++ 添加日志 +++ + return [...DEFAULT_FOCUS_SEQUENCE]; // 返回默认值的副本 + }, + + // +++ 新增:设置焦点切换顺序 +++ + /** + * 设置焦点切换顺序 + * @param sequence 要保存的焦点切换顺序数组 + */ + async setFocusSwitcherSequence(sequence: string[]): Promise { + console.log('[Service] setFocusSwitcherSequence called with:', JSON.stringify(sequence)); // +++ 添加日志 +++ + // 基本验证 + if (!Array.isArray(sequence) || !sequence.every(item => typeof item === 'string')) { + console.error('[Service] Attempted to save invalid focus switcher sequence format:', sequence); + throw new Error('Invalid sequence format provided.'); // 抛出错误阻止保存无效数据 + } + try { + const sequenceJson = JSON.stringify(sequence); + console.log(`[Service] Attempting to save setting. Key: ${FOCUS_SEQUENCE_KEY}, Value: ${sequenceJson}`); // +++ 添加日志 +++ + await settingsRepository.setSetting(FOCUS_SEQUENCE_KEY, sequenceJson); + console.log(`[Service] Successfully saved setting for key: ${FOCUS_SEQUENCE_KEY}`); // +++ 添加日志 +++ + } catch (error) { + console.error(`[Service] Error calling settingsRepository.setSetting for key ${FOCUS_SEQUENCE_KEY}:`, error); // +++ 更新日志 +++ + throw new Error('Failed to save focus switcher sequence.'); // 重新抛出错误 + } + }, }; diff --git a/packages/backend/src/settings/settings.controller.ts b/packages/backend/src/settings/settings.controller.ts index 5254afa..11d86a3 100644 --- a/packages/backend/src/settings/settings.controller.ts +++ b/packages/backend/src/settings/settings.controller.ts @@ -6,6 +6,8 @@ import { ipBlacklistService } from '../services/ip-blacklist.service'; // 引入 const auditLogService = new AuditLogService(); // 实例化 AuditLogService export const settingsController = { + // ... (getAllSettings, updateSettings, getFocusSwitcherSequence 保持不变) ... + /** * 获取所有设置项 */ @@ -31,11 +33,11 @@ export const settingsController = { return; } - // --- 过滤掉外观设置相关的键 --- + // --- 过滤掉外观设置和焦点切换顺序相关的键 --- const allowedSettingsKeys = [ 'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration', - 'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled' // 添加 ipWhitelistEnabled - // 在这里添加其他允许的通用设置键 + 'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled' + // 不在此处处理 'focusSwitcherSequence' ]; const filteredSettings: Record = {}; for (const key in settingsToUpdate) { @@ -51,29 +53,75 @@ export const settingsController = { } // 记录审计日志 - // 区分 IP 白名单更新和其他设置更新 - // 注意:现在审计日志可能需要更精细的逻辑,因为它只记录了实际更新的通用设置 - const updatedKeys = Object.keys(filteredSettings); // 使用过滤后的键 - if (updatedKeys.length > 0) { // 只有实际更新了才记录 + const updatedKeys = Object.keys(filteredSettings); + if (updatedKeys.length > 0) { if (updatedKeys.includes('ipWhitelist') || updatedKeys.includes('ipWhitelistEnabled')) { auditLogService.logAction('IP_WHITELIST_UPDATED', { updatedKeys }); } else { auditLogService.logAction('SETTINGS_UPDATED', { updatedKeys }); } } - res.status(200).json({ message: '设置已成功更新' }); // 即使没有更新通用设置也返回成功 + res.status(200).json({ message: '设置已成功更新' }); } catch (error: any) { console.error('更新设置时出错:', error); res.status(500).json({ message: '更新设置失败', error: error.message }); } }, - // 注意:通常不直接通过 API 提供单个设置项的获取、设置或删除, - // 而是通过批量获取/更新来管理。如果需要单独操作,可以添加相应方法。 - // 例如: - // async getSetting(req: Request, res: Response): Promise { ... } - // async setSetting(req: Request, res: Response): Promise { ... } - // async deleteSetting(req: Request, res: Response): Promise { ... } + // +++ 新增:获取焦点切换顺序 +++ + /** + * 获取焦点切换顺序 + */ + async getFocusSwitcherSequence(req: Request, res: Response): Promise { + try { + console.log('[Controller] Received request to get focus switcher sequence.'); // +++ 添加日志 +++ + const sequence = await settingsService.getFocusSwitcherSequence(); + console.log('[Controller] Sending focus switcher sequence to client:', JSON.stringify(sequence)); // +++ 添加日志 +++ + res.json(sequence); + } catch (error: any) { + console.error('[Controller] 获取焦点切换顺序时出错:', error); // +++ 更新日志前缀 +++ + res.status(500).json({ message: '获取焦点切换顺序失败', error: error.message }); + } + }, + + // +++ 新增:设置焦点切换顺序 +++ + /** + * 设置焦点切换顺序 + */ + async setFocusSwitcherSequence(req: Request, res: Response): Promise { + console.log('[Controller] Received request to set focus switcher sequence.'); // +++ 添加日志 +++ + try { + const { sequence } = req.body; + console.log('[Controller] Request body sequence:', JSON.stringify(sequence)); // +++ 添加日志 +++ + + // 输入验证 + if (!Array.isArray(sequence) || !sequence.every(item => typeof item === 'string')) { + console.warn('[Controller] Invalid sequence format received:', sequence); // +++ 添加日志 +++ + res.status(400).json({ message: '无效的请求体,"sequence" 必须是一个字符串数组' }); + return; + } + + console.log('[Controller] Calling settingsService.setFocusSwitcherSequence...'); // +++ 添加日志 +++ + await settingsService.setFocusSwitcherSequence(sequence); + console.log('[Controller] settingsService.setFocusSwitcherSequence completed successfully.'); // +++ 添加日志 +++ + + // 记录审计日志 (可选) + console.log('[Controller] Logging audit action: FOCUS_SWITCHER_SEQUENCE_UPDATED'); // +++ 添加日志 +++ + auditLogService.logAction('FOCUS_SWITCHER_SEQUENCE_UPDATED', { sequence }); + + console.log('[Controller] Sending success response.'); // +++ 添加日志 +++ + res.status(200).json({ message: '焦点切换顺序已成功更新' }); + } catch (error: any) { + console.error('[Controller] 设置焦点切换顺序时出错:', error); // +++ 更新日志前缀 +++ + // 区分是服务层抛出的验证错误还是其他错误 + if (error.message === 'Invalid sequence format provided.') { + res.status(400).json({ message: '设置焦点切换顺序失败: 无效的格式', error: error.message }); + } else { + res.status(500).json({ message: '设置焦点切换顺序失败', error: error.message }); + } + } + }, + /** * 获取 IP 黑名单列表 (分页) diff --git a/packages/backend/src/settings/settings.routes.ts b/packages/backend/src/settings/settings.routes.ts index ad985dc..f8763c1 100644 --- a/packages/backend/src/settings/settings.routes.ts +++ b/packages/backend/src/settings/settings.routes.ts @@ -11,6 +11,13 @@ router.use(isAuthenticated); router.get('/', settingsController.getAllSettings); // GET /api/v1/settings router.put('/', settingsController.updateSettings); // PUT /api/v1/settings +// +++ 新增:焦点切换顺序路由 +++ +// GET /api/v1/settings/focus-switcher-sequence - 获取焦点切换顺序 +router.get('/focus-switcher-sequence', settingsController.getFocusSwitcherSequence); +// PUT /api/v1/settings/focus-switcher-sequence - 更新焦点切换顺序 +router.put('/focus-switcher-sequence', settingsController.setFocusSwitcherSequence); + + // --- IP 黑名单管理路由 --- // GET /api/v1/settings/ip-blacklist - 获取 IP 黑名单列表 (需要认证) router.get('/ip-blacklist', settingsController.getIpBlacklist); diff --git a/packages/backend/src/types/audit.types.ts b/packages/backend/src/types/audit.types.ts index fa6d92f..9178ba1 100644 --- a/packages/backend/src/types/audit.types.ts +++ b/packages/backend/src/types/audit.types.ts @@ -31,6 +31,7 @@ export type AuditLogActionType = // Settings | 'SETTINGS_UPDATED' // General settings update | 'IP_WHITELIST_UPDATED' // Specific setting update + | 'FOCUS_SWITCHER_SEQUENCE_UPDATED' // +++ 新增:焦点切换顺序更新 +++ // Notifications | 'NOTIFICATION_SETTING_CREATED' diff --git a/packages/frontend/src/components/FocusSwitcherConfigurator.vue b/packages/frontend/src/components/FocusSwitcherConfigurator.vue index 165f52d..d9c9838 100644 --- a/packages/frontend/src/components/FocusSwitcherConfigurator.vue +++ b/packages/frontend/src/components/FocusSwitcherConfigurator.vue @@ -102,9 +102,8 @@ const saveConfiguration = () => { // 从本地副本提取 ID 序列 const newSequenceIds = localSequence.value.map(item => item.id); console.log('[FocusSwitcherConfigurator] Saving configuration. Sequence IDs to save:', newSequenceIds); // +++ Log: Saving IDs +++ - focusSwitcherStore.updateSequence(newSequenceIds); // 更新 Store 中的序列 - focusSwitcherStore.saveConfiguration(); // 持久化保存 - console.log('[FocusSwitcherConfigurator] Configuration save process completed.'); // +++ Log: Save completed +++ + focusSwitcherStore.updateSequence(newSequenceIds); // 更新 Store 中的序列 (这会触发保存到后端) + console.log('[FocusSwitcherConfigurator] Configuration save process triggered via updateSequence.'); // +++ Log: Save triggered +++ hasChanges.value = false; emit('close'); // 保存后关闭 }; diff --git a/packages/frontend/src/stores/focusSwitcher.store.ts b/packages/frontend/src/stores/focusSwitcher.store.ts index ed8234f..bda289d 100644 --- a/packages/frontend/src/stores/focusSwitcher.store.ts +++ b/packages/frontend/src/stores/focusSwitcher.store.ts @@ -1,12 +1,13 @@ import { defineStore } from 'pinia'; -import { ref, computed, nextTick } from 'vue'; // +++ 将 nextTick 导入移到这里 +++ +import { ref, computed, nextTick } from 'vue'; import { useI18n } from 'vue-i18n'; +// 假设有一个 API 客户端或辅助函数,这里我们直接使用 fetch +// import apiClient from '@/services/api'; // 理想情况下 // 定义输入框的接口 export interface FocusableInput { id: string; // 唯一标识符 label: string; // 用户友好的名称 - // 可以添加其他元数据,例如组件路径或选择器,以便将来查找元素 componentPath?: string; selector?: string; } @@ -17,155 +18,185 @@ interface FocusSwitcherState { configuredSequence: string[]; // 只存储 ID 序列 isConfiguratorVisible: boolean; activateFileManagerSearchTrigger: number; - activateTerminalSearchTrigger: number; // +++ 新增:终端搜索触发器 +++ + activateTerminalSearchTrigger: number; } -const LOCAL_STORAGE_KEY = 'focusSwitcherSequence'; +// --- 移除 localStorage Key --- +// const LOCAL_STORAGE_KEY = 'focusSwitcherSequence'; export const useFocusSwitcherStore = defineStore('focusSwitcher', () => { - const { t } = useI18n(); // 在 store setup 中获取 t 函数 + const { t } = useI18n(); // --- State --- - // 所有可供配置的输入框列表 - // 注意:label 使用 t() 函数获取初始值 const availableInputs = ref([ { id: 'commandHistorySearch', label: t('focusSwitcher.input.commandHistorySearch', '命令历史搜索'), componentPath: 'CommandHistoryView.vue', selector: 'input[placeholder*="搜索历史记录"]' }, { id: 'quickCommandsSearch', label: t('focusSwitcher.input.quickCommandsSearch', '快捷指令搜索'), componentPath: 'QuickCommandsView.vue', selector: 'input[placeholder*="搜索名称或指令"]' }, - { id: 'fileManagerSearch', label: t('focusSwitcher.input.fileManagerSearch', '文件管理器搜索'), componentPath: 'FileManager.vue', selector: '.search-input' }, // FileManager 的搜索框 class 是 search-input + { id: 'fileManagerSearch', label: t('focusSwitcher.input.fileManagerSearch', '文件管理器搜索'), componentPath: 'FileManager.vue', selector: '.search-input' }, { id: 'commandInput', label: t('focusSwitcher.input.commandInput', '命令输入'), componentPath: 'CommandInputBar.vue', selector: '.command-input' }, - { id: 'terminalSearch', label: t('focusSwitcher.input.terminalSearch', '终端内搜索'), componentPath: 'CommandInputBar.vue', selector: '.search-input' }, // CommandInputBar 的搜索框 class 也是 search-input - // 注意:CommandInputBar 和 FileManager 都有 .search-input,需要更精确的选择器或逻辑来区分,暂时先这样 + { id: 'terminalSearch', label: t('focusSwitcher.input.terminalSearch', '终端内搜索'), componentPath: 'CommandInputBar.vue', selector: '.search-input' }, ]); - - // 用户配置的切换顺序 (存储 ID) const configuredSequence = ref([]); - - // 控制配置弹窗可见性 - // 控制配置弹窗可见性 const isConfiguratorVisible = ref(false); - // +++ 触发器状态 +++ const activateFileManagerSearchTrigger = ref(0); - const activateTerminalSearchTrigger = ref(0); // +++ 终端搜索触发器状态 +++ + const activateTerminalSearchTrigger = ref(0); // --- Actions --- - // +++ 新增:触发终端搜索激活的 Action +++ + + // +++ 新增:从后端加载配置 +++ + async function loadSequenceFromBackend() { + const apiUrl = '/api/v1/settings/focus-switcher-sequence'; // +++ 定义 API URL +++ + console.log(`[FocusSwitcherStore] Attempting to load sequence from backend via: ${apiUrl}`); // +++ 更新日志 +++ + try { + // 注意:需要根据实际项目配置调整 API 路径和认证处理 + const response = await fetch(apiUrl); // +++ 使用变量 +++ + console.log(`[FocusSwitcherStore] Received response from ${apiUrl}. Status: ${response.status}`); // +++ 添加日志:响应状态 +++ + + if (!response.ok) { + // +++ 添加日志:记录非 OK 状态 +++ + console.error(`[FocusSwitcherStore] HTTP error from ${apiUrl}. Status: ${response.status}`); + throw new Error(`HTTP error! status: ${response.status}`); + } + + const sequence = await response.json(); + // +++ 添加日志:记录原始 JSON 数据 +++ + console.log(`[FocusSwitcherStore] Raw JSON received from backend for sequence:`, JSON.stringify(sequence)); + + if (Array.isArray(sequence) && sequence.every(item => typeof item === 'string')) { + console.log('[FocusSwitcherStore] Sequence format is valid (string array). Filtering against available inputs...'); // +++ 添加日志 +++ + // 验证加载的 ID 是否仍然存在于 availableInputs 中 + const availableIds = new Set(availableInputs.value.map(input => input.id)); + const filteredSequence = sequence.filter((id: string) => { + const isValid = availableIds.has(id); + if (!isValid) { + console.warn(`[FocusSwitcherStore] Filtered out invalid ID during load from backend: ${id}`); + } + return isValid; + }); + configuredSequence.value = filteredSequence; + // +++ 更新日志:显示最终设置的值 +++ + console.log('[FocusSwitcherStore] Successfully loaded and set configuredSequence:', JSON.stringify(configuredSequence.value)); + } else { + console.error('[FocusSwitcherStore] Invalid sequence format received from backend:', sequence); + configuredSequence.value = []; // 使用空数组作为回退 + console.log('[FocusSwitcherStore] Set configuredSequence to empty array due to invalid format.'); // +++ 添加日志 +++ + } + } catch (error) { + // +++ 添加日志:记录 fetch 或 JSON 解析错误 +++ + console.error(`[FocusSwitcherStore] Failed to load or parse sequence from backend (${apiUrl}):`, error); + // 加载失败时可以考虑使用默认值或保持为空 + configuredSequence.value = []; + console.log('[FocusSwitcherStore] Set configuredSequence to empty array due to loading error.'); // +++ 添加日志 +++ + } + } + + // +++ 新增:保存配置到后端 +++ + async function saveSequenceToBackend() { + const apiUrl = '/api/v1/settings/focus-switcher-sequence'; // +++ 定义 API URL +++ + console.log(`[FocusSwitcherStore] Attempting to save sequence to backend via PUT: ${apiUrl}`); // +++ 更新日志 +++ + try { + const sequenceToSave = configuredSequence.value; + console.log('[FocusSwitcherStore] Sequence data to save:', JSON.stringify(sequenceToSave)); // +++ 添加日志 +++ + // 注意:需要根据实际项目配置调整 API 路径和认证处理 + const response = await fetch(apiUrl, { // +++ 使用变量 +++ + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + // 如果需要认证,添加 Authorization header + // 'Authorization': `Bearer ${your_token}` + }, + body: JSON.stringify({ sequence: sequenceToSave }), + }); + console.log(`[FocusSwitcherStore] Received response from PUT ${apiUrl}. Status: ${response.status}`); // +++ 添加日志 +++ + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); // 尝试解析错误体 + console.error(`[FocusSwitcherStore] Save failed. Status: ${response.status}, Error data:`, errorData); // +++ 添加日志 +++ + throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.message || 'Unknown error'}`); + } + + const result = await response.json(); + console.log('[FocusSwitcherStore] Configuration successfully saved to backend. Response message:', result.message); // +++ 更新日志 +++ + } catch (error) { + console.error(`[FocusSwitcherStore] Failed to save sequence to backend (${apiUrl}):`, error); // +++ 更新日志 +++ + // 可以在这里触发 UI 通知用户保存失败 + } + } + + function triggerTerminalSearchActivation() { activateTerminalSearchTrigger.value++; console.log('[FocusSwitcherStore] Triggering Terminal search activation.'); } - // +++ 新增:触发文件管理器搜索激活的 Action +++ function triggerFileManagerSearchActivation() { - activateFileManagerSearchTrigger.value++; // 简单地增加计数器来触发监听 + activateFileManagerSearchTrigger.value++; console.log('[FocusSwitcherStore] Triggering FileManager search activation.'); } - // 控制配置器显示/隐藏 function toggleConfigurator(visible?: boolean) { isConfiguratorVisible.value = visible === undefined ? !isConfiguratorVisible.value : visible; console.log(`[FocusSwitcherStore] Configurator visibility set to: ${isConfiguratorVisible.value}`); } - // 从 localStorage 加载配置 + // --- 移除旧的 loadConfiguration --- + /* function loadConfiguration() { - console.log('[FocusSwitcherStore] Attempting to load configuration...'); // +++ Log: Start loading +++ - const savedSequence = localStorage.getItem(LOCAL_STORAGE_KEY); - console.log(`[FocusSwitcherStore] Raw data from localStorage ('${LOCAL_STORAGE_KEY}'):`, savedSequence); // +++ Log: Raw data +++ - if (savedSequence) { - try { - const parsedSequence = JSON.parse(savedSequence); - console.log('[FocusSwitcherStore] Parsed sequence from localStorage:', parsedSequence); // +++ Log: Parsed data +++ - // 验证加载的 ID 是否仍然存在于 availableInputs 中 - const availableIds = new Set(availableInputs.value.map(input => input.id)); // +++ Get available IDs +++ - const filteredSequence = parsedSequence.filter((id: string) => { // +++ Filter and log +++ - const isValid = availableIds.has(id); - if (!isValid) { - console.warn(`[FocusSwitcherStore] Filtered out invalid ID during load: ${id}`); - } - return isValid; - }); - configuredSequence.value = filteredSequence; - console.log('[FocusSwitcherStore] Final loaded & filtered configuration:', configuredSequence.value); // +++ Log: Final loaded value +++ - } catch (error) { - console.error('[FocusSwitcherStore] Failed to parse saved configuration:', error); - configuredSequence.value = []; // 解析失败则重置 - localStorage.removeItem(LOCAL_STORAGE_KEY); // 移除损坏的数据 - } - } else { - // 如果没有保存的配置,可以设置一个默认顺序 - // configuredSequence.value = ['commandInput', 'terminalSearch']; // 例如 - configuredSequence.value = []; // 或者默认为空 - console.log('[FocusSwitcherStore] No saved configuration found, using default (empty).'); - } + // ... localStorage logic ... } + */ - // 保存配置到 localStorage + // --- 移除旧的 saveConfiguration --- + /* function saveConfiguration() { - try { - const sequenceToSave = configuredSequence.value; // +++ Get value to save +++ - const jsonStringToSave = JSON.stringify(sequenceToSave); // +++ Stringify +++ - console.log(`[FocusSwitcherStore] Attempting to save sequence to localStorage ('${LOCAL_STORAGE_KEY}'):`, sequenceToSave); // +++ Log: Value being saved +++ - console.log(`[FocusSwitcherStore] JSON string being saved:`, jsonStringToSave); // +++ Log: String being saved +++ - localStorage.setItem(LOCAL_STORAGE_KEY, jsonStringToSave); - console.log('[FocusSwitcherStore] Configuration successfully saved.'); // +++ Log: Success +++ - } catch (error) { - console.error('[FocusSwitcherStore] Failed to save configuration:', error); - } + // ... localStorage logic ... } + */ - // 更新切换顺序 + // 更新切换顺序 (现在也触发保存到后端) function updateSequence(newSequence: string[]) { - console.log('[FocusSwitcherStore] updateSequence called with:', newSequence); // +++ Log: Action called +++ - // 确保新序列中的 ID 都是有效的 - const availableIds = new Set(availableInputs.value.map(input => input.id)); // +++ Get available IDs +++ - const filteredSequence = newSequence.filter(id => availableIds.has(id)); // +++ Filter +++ + console.log('[FocusSwitcherStore] updateSequence called with:', JSON.stringify(newSequence)); // +++ 更新日志 +++ + const availableIds = new Set(availableInputs.value.map(input => input.id)); + const filteredSequence = newSequence.filter(id => availableIds.has(id)); configuredSequence.value = filteredSequence; - console.log('[FocusSwitcherStore] configuredSequence updated to:', configuredSequence.value); // +++ Log: State updated +++ - // 可以在这里直接保存,或者让用户点击保存按钮再调用 saveConfiguration - // saveConfiguration(); // 取决于设计 + console.log('[FocusSwitcherStore] configuredSequence updated locally to:', JSON.stringify(configuredSequence.value)); // +++ 更新日志 +++ + // 更新后立即保存到后端 + saveSequenceToBackend(); } // --- Getters --- - // 获取已配置的输入框完整信息 (按顺序) const getConfiguredInputs = computed((): FocusableInput[] => { return configuredSequence.value .map(id => availableInputs.value.find(input => input.id === id)) - .filter((input): input is FocusableInput => input !== undefined); // 类型守卫确保过滤掉 undefined + .filter((input): input is FocusableInput => input !== undefined); }); - // 获取在配置器中可用的输入框 (未被配置的) const getAvailableInputsForConfigurator = computed((): FocusableInput[] => { const configuredIds = new Set(configuredSequence.value); return availableInputs.value.filter(input => !configuredIds.has(input.id)); }); - // 获取下一个要聚焦的输入框 ID (用于实际切换逻辑) function getNextFocusTargetId(currentFocusedId: string | null): string | null { if (configuredSequence.value.length === 0) { - return null; // 没有配置顺序 + return null; } if (currentFocusedId === null) { - // 如果当前没有焦点在配置列表内,返回第一个 return configuredSequence.value[0]; } - const currentIndex = configuredSequence.value.indexOf(currentFocusedId); if (currentIndex === -1) { - // 如果当前焦点不在配置列表内,返回第一个 return configuredSequence.value[0]; } - - // 返回下一个,如果到末尾则循环回第一个 const nextIndex = (currentIndex + 1) % configuredSequence.value.length; return configuredSequence.value[nextIndex]; } // --- Initialization --- - // Store 创建时自动加载配置 (使用 nextTick 确保响应式系统就绪) - // --- import { nextTick } from 'vue'; // --- 移除这里的导入 --- + // Store 创建时自动从后端加载配置 + console.log('[FocusSwitcherStore] Initializing store and scheduling loadSequenceFromBackend...'); // +++ 添加日志 +++ nextTick(() => { - loadConfiguration(); + console.log('[FocusSwitcherStore] nextTick triggered, calling loadSequenceFromBackend.'); // +++ 添加日志 +++ + loadSequenceFromBackend(); // +++ 调用新的加载函数 +++ }); return { @@ -174,13 +205,13 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => { configuredSequence, isConfiguratorVisible, activateFileManagerSearchTrigger, - activateTerminalSearchTrigger, // +++ 导出终端搜索触发器状态 +++ + activateTerminalSearchTrigger, // Actions toggleConfigurator, triggerFileManagerSearchActivation, - triggerTerminalSearchActivation, // +++ 确保导出终端搜索触发器 Action +++ - loadConfiguration, - saveConfiguration, + triggerTerminalSearchActivation, + loadSequenceFromBackend, // +++ 导出新的加载函数 +++ + saveSequenceToBackend, // +++ 导出新的保存函数 +++ updateSequence, // Getters getConfiguredInputs,