This commit is contained in:
Baobhan Sith
2025-04-19 21:10:04 +08:00
parent 283fa02a18
commit d6cb9f1846
7 changed files with 276 additions and 117 deletions
@@ -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'); // 保存后关闭
};
@@ -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<FocusableInput[]>([
{ 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<string[]>([]);
// 控制配置弹窗可见性
// 控制配置弹窗可见性
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,