This commit is contained in:
Baobhan Sith
2025-04-22 09:13:54 +08:00
parent 66a39e9428
commit 8412be76d5
3 changed files with 160 additions and 78 deletions
@@ -521,27 +521,51 @@ watchEffect((onCleanup) => {
});
// +++ 监听 Store 中的触发器以激活搜索 +++
watch(() => focusSwitcherStore.activateFileManagerSearchTrigger, () => {
// 确保只在触发器值大于 0 时执行(避免初始加载时触发)
if (focusSwitcherStore.activateFileManagerSearchTrigger > 0) {
console.log('[FileManager] Received search activation trigger from store.');
watch(() => focusSwitcherStore.activateFileManagerSearchTrigger, (newValue, oldValue) => { // 修改监听器
// 确保只在触发器值增加时执行(避免初始加载或重置时触发)
// 并且当前组件的 sessionId 与活动 sessionId 匹配
// 检查 newValue > oldValue 确保是递增触发,避免重复执行
if (newValue > (oldValue ?? 0) && props.sessionId === sessionStore.activeSessionId) {
console.log(`[FileManager ${props.sessionId}] Received search activation trigger for active session.`);
activateSearch(); // 调用组件内部的激活搜索方法
}
});
}, { immediate: false }); // 添加 immediate: false 避免初始值为 0 时触发
onBeforeUnmount(() => {
console.log(`[FileManager ${props.sessionId}] 组件即将卸载。`);
// 调用注入的 SFTP 管理器提供的清理函数
cleanupSftpHandlers();
});
// onBeforeUnmount 中 cleanupSftpHandlers 的调用已移至新的 onBeforeUnmount 逻辑中
// +++ 注册/注销自定义聚焦动作 +++
let unregisterFocusAction: (() => void) | null = null; // 用于存储注销函数
onMounted(() => {
focusSwitcherStore.registerFocusAction('fileManagerSearch', focusSearchInput);
// 注册一个包装函数,而不是直接注册 focusSearchInput
// 使其成为 async 函数以兼容 Promise 返回类型
const focusActionWrapper = async (): Promise<boolean | undefined> => {
if (props.sessionId === sessionStore.activeSessionId) {
// 如果是活动会话,调用原始聚焦函数并返回其结果
// 由于 focusSearchInput 是同步的,我们直接返回它的 boolean 结果
// async 函数会自动将其包装在 Promise 中(如果需要,但这里不需要)
return focusSearchInput();
}
// 如果不是活动会话,返回 undefined,表示跳过
// async 函数返回 undefined 会被包装成 Promise<undefined>
console.log(`[FileManager ${props.sessionId}] Focus action skipped (async undefined) for inactive session.`);
return undefined; // 返回 undefined 表示跳过
};
// 调用新的 registerFocusAction 并存储返回的注销函数
unregisterFocusAction = focusSwitcherStore.registerFocusAction('fileManagerSearch', focusActionWrapper);
});
onBeforeUnmount(() => {
focusSwitcherStore.unregisterFocusAction('fileManagerSearch');
// 调用存储的注销函数
if (unregisterFocusAction) {
unregisterFocusAction();
console.log(`[FileManager ${props.sessionId}] Unregistered focus action on unmount.`);
}
// 清理对函数的引用
unregisterFocusAction = null;
// 调用注入的 SFTP 管理器提供的清理函数 (移到这里确保注销后清理)
cleanupSftpHandlers();
});
// --- 列宽调整逻辑 (保持不变) ---
@@ -663,19 +687,30 @@ const handleWheel = (event: WheelEvent) => {
// +++ 新增:聚焦搜索框的方法 +++
const focusSearchInput = (): boolean => {
// 检查当前会话是否激活,防止后台实例响应
if (props.sessionId !== sessionStore.activeSessionId) {
console.log(`[FileManager ${props.sessionId}] Ignoring focus request for inactive session.`);
return false;
}
if (!isSearchActive.value) {
activateSearch(); // Activate search first
nextTick(() => { // Wait for DOM update
// nextTick 确保 DOM 更新后再聚焦
nextTick(() => {
if (searchInputRef.value) {
searchInputRef.value.focus();
console.log(`[FileManager ${props.sessionId}] Search activated and input focused.`);
} else {
console.warn(`[FileManager ${props.sessionId}] Search activated but input ref not found after nextTick.`);
}
});
// Assume activation and focus will likely succeed
return true;
return true; // 假设会成功
} else if (searchInputRef.value) {
searchInputRef.value.focus();
console.log(`[FileManager ${props.sessionId}] Search already active, input focused.`);
return true;
}
console.warn(`[FileManager ${props.sessionId}] Could not focus search input.`);
return false;
};
defineExpose({ focusSearchInput });
@@ -221,18 +221,26 @@ const handleBlur = () => {
}, 150); // 150ms 延迟可能更稳妥
};
// 获取数据
onMounted(() => {
connectionsStore.fetchConnections();
tagsStore.fetchTags();
});
// 获取数据的 onMounted 调用已移至新的 onMounted 逻辑中
// +++ 注册/注销自定义聚焦动作 +++
let unregisterFocusAction: (() => void) | null = null; // 用于存储注销函数
onMounted(() => {
focusSwitcherStore.registerFocusAction('connectionListSearch', focusSearchInput);
// 调用新的 registerFocusAction 并存储返回的注销函数
// focusSearchInput 返回 boolean,符合 () => boolean | Promise<boolean | undefined> 类型
unregisterFocusAction = focusSwitcherStore.registerFocusAction('connectionListSearch', focusSearchInput);
connectionsStore.fetchConnections(); // 移到 onMounted
tagsStore.fetchTags(); // 移到 onMounted
});
onBeforeUnmount(() => {
focusSwitcherStore.unregisterFocusAction('connectionListSearch');
// 调用存储的注销函数
if (unregisterFocusAction) {
unregisterFocusAction();
console.log(`[WkspConnList] Unregistered focus action on unmount.`);
}
unregisterFocusAction = null;
});
// 处理中键点击(在新标签页打开)
@@ -4,11 +4,11 @@ import { useI18n } from 'vue-i18n';
// 假设有一个 API 客户端或辅助函数,这里我们直接使用 fetch
// import apiClient from '@/services/api';
// 基础输入框接口 (保持不变)
// 基础输入框接口 (移除 focusAction)
export interface FocusableInput {
id: string;
label: string;
focusAction: () => boolean | Promise<boolean>;
// focusAction: () => boolean | Promise<boolean>; // 移除,动作将单独管理
}
// --- 移除 ConfiguredFocusableInput ---
@@ -28,12 +28,14 @@ export interface FocusSwitcherFullConfig {
// Store State 接口
interface FocusSwitcherState {
availableInputs: FocusableInput[]; // 所有可用项的基础信息
availableInputs: FocusableInput[]; // 所有可用项的基础信息 (无 focusAction)
sequenceOrder: string[]; // 顺序切换的 ID 列表
itemConfigs: Record<string, FocusItemConfig>; // 所有项目的配置 (id -> config)
isConfiguratorVisible: boolean;
activateFileManagerSearchTrigger: number;
activateTerminalSearchTrigger: number;
// 新增:存储注册的聚焦动作
registeredActions: Map<string, Array<() => boolean | Promise<boolean | undefined>>>;
}
// --- 移除 localStorage Key ---
@@ -44,14 +46,14 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
// --- State ---
const availableInputs = ref<FocusableInput[]>([
// 简化定义,移除 componentPath 和 selectorfocusAction 将由组件注册
{ id: 'commandHistorySearch', label: t('focusSwitcher.input.commandHistorySearch', '命令历史搜索'), focusAction: () => false },
{ id: 'quickCommandsSearch', label: t('focusSwitcher.input.quickCommandsSearch', '快捷指令搜索'), focusAction: () => false },
{ id: 'fileManagerSearch', label: t('focusSwitcher.input.fileManagerSearch', '文件管理器搜索'), focusAction: () => false },
{ id: 'commandInput', label: t('focusSwitcher.input.commandInput', '命令输入'), focusAction: () => false },
{ id: 'terminalSearch', label: t('focusSwitcher.input.terminalSearch', '终端内搜索'), focusAction: () => false },
{ id: 'connectionListSearch', label: t('focusSwitcher.input.connectionListSearch', '连接列表搜索'), focusAction: () => false },
{ id: 'fileEditorActive', label: t('focusSwitcher.input.fileEditorActive', '文件编辑器'), focusAction: () => false },
// 移除 focusAction 初始化
{ id: 'commandHistorySearch', label: t('focusSwitcher.input.commandHistorySearch', '命令历史搜索') },
{ id: 'quickCommandsSearch', label: t('focusSwitcher.input.quickCommandsSearch', '快捷指令搜索') },
{ id: 'fileManagerSearch', label: t('focusSwitcher.input.fileManagerSearch', '文件管理器搜索') },
{ id: 'commandInput', label: t('focusSwitcher.input.commandInput', '命令输入') },
{ id: 'terminalSearch', label: t('focusSwitcher.input.terminalSearch', '终端内搜索') },
{ id: 'connectionListSearch', label: t('focusSwitcher.input.connectionListSearch', '连接列表搜索') },
{ id: 'fileEditorActive', label: t('focusSwitcher.input.fileEditorActive', '文件编辑器') },
]);
const sequenceOrder = ref<string[]>([]); // +++ 新增:存储顺序 +++
const itemConfigs = ref<Record<string, FocusItemConfig>>({}); // +++ 新增:存储所有配置 +++
@@ -59,8 +61,8 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
const activateFileManagerSearchTrigger = ref(0);
const activateTerminalSearchTrigger = ref(0);
// 新增:存储自定义聚焦动作
const focusActions = ref<Record<string, () => boolean | Promise<boolean>>>({});
// 新增:存储注册的聚焦动作 (Map: id -> Array of actions)
const registeredActions = ref<Map<string, Array<() => boolean | Promise<boolean | undefined>>>>(new Map());
// --- Actions ---
@@ -224,55 +226,92 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
saveConfigurationToBackend();
}
// 注册聚焦动作 (现在更新 availableInputs 中的 focusAction)
function registerFocusAction(id: string, action: () => boolean | Promise<boolean>) {
const targetInput = availableInputs.value.find(input => input.id === id);
if (targetInput) {
targetInput.focusAction = action;
console.log(`[FocusSwitcherStore] Registered focus action for ID: ${id}`);
} else {
// 注册聚焦动作 (添加到 Map 中)
// 返回一个注销函数,以便组件可以方便地注销自己添加的动作
function registerFocusAction(id: string, action: () => boolean | Promise<boolean | undefined>): () => void {
if (!availableInputs.value.some(input => input.id === id)) {
console.warn(`[FocusSwitcherStore] Attempted to register focus action for unknown ID: ${id}`);
return () => {}; // 返回一个无操作的注销函数
}
const actions = registeredActions.value.get(id) || [];
actions.push(action);
registeredActions.value.set(id, actions);
console.log(`[FocusSwitcherStore] Registered focus action for ID: ${id}. Total actions for this ID: ${actions.length}`);
// 返回一个用于注销此特定动作的函数
const unregister = () => {
const currentActions = registeredActions.value.get(id);
if (currentActions) {
const index = currentActions.indexOf(action);
if (index > -1) {
currentActions.splice(index, 1);
console.log(`[FocusSwitcherStore] Unregistered a focus action for ID: ${id}. Remaining actions: ${currentActions.length}`);
// 如果数组为空,可以从 Map 中移除该 ID
if (currentActions.length === 0) {
registeredActions.value.delete(id);
console.log(`[FocusSwitcherStore] Removed ID ${id} from registeredActions map as it has no more actions.`);
}
} else {
console.warn(`[FocusSwitcherStore] Attempted to unregister an action for ID ${id} that was not found.`);
}
}
};
return unregister;
}
// 注销聚焦动作 (重置为默认返回 false 的函数)
function unregisterFocusAction(id: string) {
const targetInput = availableInputs.value.find(input => input.id === id);
if (targetInput) {
targetInput.focusAction = () => false; // Reset to default non-functional action
console.log(`[FocusSwitcherStore] Unregistered focus action for ID: ${id}`);
}
}
// 注销聚焦动作 (现在由 registerFocusAction 返回的函数处理)
// 保留一个空的 unregisterFocusAction 以防万一旧代码调用,但标记为废弃或移除
// function unregisterFocusAction(id: string) {
// console.warn("[FocusSwitcherStore] unregisterFocusAction(id) is deprecated. Use the function returned by registerFocusAction instead.");
// }
// 新增:统一的聚焦目标 Action
// 修改:统一的聚焦目标 Action,现在迭代 Map 中的动作数组
async function focusTarget(id: string): Promise<boolean> {
console.log(`[FocusSwitcherStore] Attempting to focus target ID: ${id}`);
const targetInput = availableInputs.value.find(input => input.id === id);
if (targetInput?.focusAction) {
try {
const result = await targetInput.focusAction();
if (result) {
console.log(`[FocusSwitcherStore] Successfully focused ${id} via action.`);
return true;
} else {
console.log(`[FocusSwitcherStore] Focus action for ${id} returned false.`);
// 尝试激活搜索框(如果适用)
if (id === 'fileManagerSearch') {
triggerFileManagerSearchActivation();
// 激活后可能需要短暂延迟再尝试聚焦,但这部分逻辑移到 App.vue 或组件内部更合适
} else if (id === 'terminalSearch') {
triggerTerminalSearchActivation();
}
return false;
}
} catch (error) {
console.error(`[FocusSwitcherStore] Error executing focus action for ${id}:`, error);
return false;
}
} else {
console.warn(`[FocusSwitcherStore] No focus action registered for ID: ${id}`);
const actions = registeredActions.value.get(id);
if (!actions || actions.length === 0) {
console.warn(`[FocusSwitcherStore] No focus actions registered for ID: ${id}`);
return false;
}
console.log(`[FocusSwitcherStore] Found ${actions.length} action(s) for ID: ${id}. Iterating...`);
for (const action of actions) {
try {
// 执行动作,可能是同步或异步的
const result = await action();
if (result === true) {
// 如果动作返回 true,表示成功聚焦,停止迭代并返回 true
console.log(`[FocusSwitcherStore] Successfully focused ${id} via one of its actions.`);
return true;
} else if (result === false) {
// 如果动作返回 false,表示尝试但失败,记录日志并继续下一个动作
console.log(`[FocusSwitcherStore] An action for ${id} returned false (failed). Trying next action if available.`);
} else if (result === undefined) {
// 如果动作返回 undefined,表示跳过(例如非活动实例),记录日志并继续下一个动作
console.log(`[FocusSwitcherStore] An action for ${id} returned undefined (skipped). Trying next action if available.`);
}
// 如果 result 是其他值,也视为跳过或未处理
} catch (error) {
console.error(`[FocusSwitcherStore] Error executing a focus action for ${id}:`, error);
// 即使出错,也继续尝试下一个动作
}
}
// 如果遍历完所有动作都没有成功聚焦 (没有返回 true)
console.log(`[FocusSwitcherStore] All actions for ${id} executed, but none returned true. Focus failed.`);
// 尝试激活搜索框(如果适用),这里的逻辑可能需要重新审视,
// 因为激活应该由返回 false 的动作内部触发,或者由调用 focusTarget 的地方处理
if (id === 'fileManagerSearch') {
// triggerFileManagerSearchActivation(); // 考虑移除这里的触发,让组件内部处理失败后的激活
} else if (id === 'terminalSearch') {
// triggerTerminalSearchActivation(); // 同上
}
return false; // 返回聚焦失败
}
// --- 修改 Getters ---
@@ -378,8 +417,8 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
getAvailableInputsForConfigurator, // 已修改
getNextFocusTargetId, // 已修改
getFocusTargetIdByShortcut, // 已修改
registerFocusAction,
unregisterFocusAction,
focusTarget,
registerFocusAction, // 返回注销函数
// unregisterFocusAction, // 废弃旧的注销函数
focusTarget, // 已更新
};
});