This commit is contained in:
Baobhan Sith
2025-04-21 22:52:50 +08:00
parent 9f0d7d18cf
commit 19e488abfd
9 changed files with 280 additions and 103 deletions
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, watch, nextTick } from 'vue';
import { ref, watch, nextTick, onMounted, onBeforeUnmount, defineExpose } from 'vue';
import { useI18n } from 'vue-i18n';
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // 导入 Store
// 假设你有一个图标库,例如 unplugin-icons 或类似库
@@ -63,6 +63,7 @@ watch(searchTerm, (newValue) => {
// 可以在这里添加一个 ref 用于聚焦搜索框
const searchInputRef = ref<HTMLInputElement | null>(null);
const commandInputRef = ref<HTMLInputElement | null>(null); // Ref for command input
// Removed debug computed property
@@ -84,6 +85,46 @@ watch(() => focusSwitcherStore.activateTerminalSearchTrigger, () => {
}
});
// --- Focus Actions ---
const focusCommandInput = (): boolean => {
if (commandInputRef.value) {
commandInputRef.value.focus();
return true;
}
return false;
};
const focusSearchInput = (): boolean => {
if (!isSearching.value) {
// If search is not active, activate it first
toggleSearch(); // This might need nextTick if toggleSearch is async
nextTick(() => { // Ensure DOM is updated after toggleSearch
if (searchInputRef.value) {
searchInputRef.value.focus();
}
});
// Since focusing might be async after toggle, we optimistically return true
// or adjust based on toggleSearch's behavior. For simplicity, assume it works.
return true;
} else if (searchInputRef.value) {
searchInputRef.value.focus();
return true;
}
return false;
};
defineExpose({ focusCommandInput, focusSearchInput });
// --- Register/Unregister Focus Actions ---
onMounted(() => {
focusSwitcherStore.registerFocusAction('commandInput', focusCommandInput);
focusSwitcherStore.registerFocusAction('terminalSearch', focusSearchInput);
});
onBeforeUnmount(() => {
focusSwitcherStore.unregisterFocusAction('commandInput');
focusSwitcherStore.unregisterFocusAction('terminalSearch');
});
</script>
<template>
@@ -99,6 +140,7 @@ watch(() => focusSwitcherStore.activateTerminalSearchTrigger, () => {
v-model="commandInput"
:placeholder="t('commandInputBar.placeholder')"
class="command-input"
ref="commandInputRef"
data-focus-id="commandInput"
@keydown.enter="sendCommand"
@keydown="handleCommandInputKeydown"
@@ -1,13 +1,15 @@
<script setup lang="ts">
import { computed, type PropType, ref, watch } from 'vue'; // 添加 ref watch
import { computed, type PropType, ref, watch, defineExpose, onMounted, onBeforeUnmount } from 'vue'; // 添加 ref, watch, defineExpose, onMounted, onBeforeUnmount
import { useI18n } from 'vue-i18n';
// import { storeToRefs } from 'pinia'; // 移除 storeToRefs
import MonacoEditor from './MonacoEditor.vue'; // 导入 Monaco Editor 组件
import FileEditorTabs from './FileEditorTabs.vue'; // 导入标签栏组件 (路径确认无误)
// import { useFileEditorStore } from '../stores/fileEditor.store'; // 移除 Store 导入
import type { FileTab } from '../stores/fileEditor.store'; // 保留类型导入
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
const { t } = useI18n();
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
// --- Props ---
const props = defineProps({
@@ -92,6 +94,28 @@ const handleSaveRequest = () => {
// const handleCloseContainer = () => { ... };
// const handleMinimizeContainer = () => { ... };
// 新增:Monaco Editor 组件的引用
const monacoEditorRef = ref<InstanceType<typeof MonacoEditor> | null>(null);
// 新增:聚焦活动编辑器的方法
const focusActiveEditor = (): boolean => {
if (monacoEditorRef.value) {
monacoEditorRef.value.focus();
return true; // 聚焦成功
}
return false; // 聚焦失败
};
// 新增:暴露聚焦方法
defineExpose({ focusActiveEditor });
// +++ 注册/注销自定义聚焦动作 +++
onMounted(() => {
focusSwitcherStore.registerFocusAction('fileEditorActive', focusActiveEditor);
});
onBeforeUnmount(() => {
focusSwitcherStore.unregisterFocusAction('fileEditorActive');
});
</script>
<template>
@@ -135,6 +159,7 @@ const handleSaveRequest = () => {
<div v-else-if="currentTabLoadingError" class="editor-error">{{ currentTabLoadingError }}</div>
<MonacoEditor
v-else-if="activeTab"
ref="monacoEditorRef"
:key="activeTab.id"
v-model="localEditorContent"
:language="currentTabLanguage"
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch, watchEffect, type PropType, readonly } from 'vue'; // 恢复导入, 添加 watch
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch, watchEffect, type PropType, readonly, defineExpose } from 'vue'; // 恢复导入, 添加 watch, defineExpose
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; // 保留用于生成下载 URL (如果下载逻辑移动则可移除)
import { storeToRefs } from 'pinia'; // 导入 storeToRefs
@@ -536,6 +536,14 @@ onBeforeUnmount(() => {
cleanupSftpHandlers();
});
// +++ 注册/注销自定义聚焦动作 +++
onMounted(() => {
focusSwitcherStore.registerFocusAction('fileManagerSearch', focusSearchInput);
});
onBeforeUnmount(() => {
focusSwitcherStore.unregisterFocusAction('fileManagerSearch');
});
// --- 列宽调整逻辑 (保持不变) ---
const getColumnKeyByIndex = (index: number): keyof typeof colWidths.value | null => {
const keys = Object.keys(colWidths.value) as Array<keyof typeof colWidths.value>;
@@ -653,6 +661,25 @@ const handleWheel = (event: WheelEvent) => {
}
};
// +++ 新增:聚焦搜索框的方法 +++
const focusSearchInput = (): boolean => {
if (!isSearchActive.value) {
activateSearch(); // Activate search first
nextTick(() => { // Wait for DOM update
if (searchInputRef.value) {
searchInputRef.value.focus();
}
});
// Assume activation and focus will likely succeed
return true;
} else if (searchInputRef.value) {
searchInputRef.value.focus();
return true;
}
return false;
};
defineExpose({ focusSearchInput });
</script>
<template>
@@ -3,7 +3,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import { ref, onMounted, onBeforeUnmount, watch, defineExpose } from 'vue';
import * as monaco from 'monaco-editor';
import { useAppearanceStore } from '../stores/appearance.store'; // <-- Store
import { storeToRefs } from 'pinia'; // <-- storeToRefs
@@ -227,6 +227,11 @@ onBeforeUnmount(() => {
// getValue: () => editorInstance?.getValue()
// });
// Expose the focus method
defineExpose({
focus: () => editorInstance?.focus()
});
</script>
<style scoped>
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'; // ref
import { ref, computed, onMounted, onBeforeUnmount, defineExpose } from 'vue'; // ref, defineExpose, onBeforeUnmount
import { storeToRefs } from 'pinia';
// import { useRouter } from 'vue-router'; // router
import { useI18n } from 'vue-i18n';
import { useConnectionsStore, ConnectionInfo } from '../stores/connections.store';
import { useTagsStore, TagInfo } from '../stores/tags.store';
import { useSessionStore } from '../stores/session.store'; // session store
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ Store +++
//
const emit = defineEmits([
@@ -20,12 +21,14 @@ const { t } = useI18n();
const connectionsStore = useConnectionsStore();
const tagsStore = useTagsStore();
const sessionStore = useSessionStore(); // session store
const focusSwitcherStore = useFocusSwitcherStore(); // +++ Store +++
const { connections, isLoading: connectionsLoading, error: connectionsError } = storeToRefs(connectionsStore);
const { tags, isLoading: tagsLoading, error: tagsError } = storeToRefs(tagsStore);
//
const searchTerm = ref('');
const searchInputRef = ref<HTMLInputElement | null>(null); // ref
//
const contextMenuVisible = ref(false);
@@ -168,6 +171,14 @@ onMounted(() => {
tagsStore.fetchTags();
});
// +++ / +++
onMounted(() => {
focusSwitcherStore.registerFocusAction('connectionListSearch', focusSearchInput);
});
onBeforeUnmount(() => {
focusSwitcherStore.unregisterFocusAction('connectionListSearch');
});
//
const handleOpenInNewTab = (connectionId: number) => {
console.log(`[WkspConnList] handleOpenInNewTab (中键/辅助键) called for ID: ${connectionId}`);
@@ -176,6 +187,16 @@ const handleOpenInNewTab = (connectionId: number) => {
closeContextMenu(); //
return false; //
};
//
const focusSearchInput = (): boolean => {
if (searchInputRef.value) {
searchInputRef.value.focus();
return true; //
}
return false; //
};
defineExpose({ focusSearchInput });
</script>
<template>
@@ -193,7 +214,9 @@ const handleOpenInNewTab = (connectionId: number) => {
type="text"
v-model="searchTerm"
:placeholder="t('workspaceConnectionList.searchPlaceholder')"
ref="searchInputRef"
class="search-input"
data-focus-id="connectionListSearch"
/>
<button
class="add-button"