update
This commit is contained in:
@@ -35,7 +35,9 @@ const underlineRef = ref<HTMLElement | null>(null);
|
|||||||
|
|
||||||
// +++ 新增:存储上一次由切换器聚焦的 ID +++
|
// +++ 新增:存储上一次由切换器聚焦的 ID +++
|
||||||
const lastFocusedIdBySwitcher = ref<string | null>(null);
|
const lastFocusedIdBySwitcher = ref<string | null>(null);
|
||||||
const isAltPressed = ref(false); // +++ 新增:跟踪 Alt 键是否按下 +++
|
const isAltPressed = ref(false); // 跟踪 Alt 键是否按下
|
||||||
|
const altShortcutKey = ref<string | null>(null);
|
||||||
|
const shortcutTriggeredInKeyDown = ref(false); // +++ 新增:标记快捷键是否在 keydown 中触发 +++
|
||||||
|
|
||||||
const updateUnderline = async () => {
|
const updateUnderline = async () => {
|
||||||
await nextTick(); // 等待 DOM 更新
|
await nextTick(); // 等待 DOM 更新
|
||||||
@@ -93,72 +95,113 @@ const closeStyleCustomizer = () => {
|
|||||||
appearanceStore.toggleStyleCustomizer(false);
|
appearanceStore.toggleStyleCustomizer(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// +++ 新增:处理 Alt 键按下的事件处理函数 +++
|
// +++ 修改:处理 Alt 键按下的事件处理函数,并记录快捷键 +++
|
||||||
const handleAltKeyDown = (event: KeyboardEvent) => {
|
const handleAltKeyDown = async (event: KeyboardEvent) => { // +++ 改为 async +++
|
||||||
// 只在 Alt 键首次按下时设置状态,忽略重复事件
|
// 只在 Alt 键首次按下时设置状态
|
||||||
if (event.key === 'Alt' && !event.repeat) {
|
if (event.key === 'Alt' && !event.repeat) {
|
||||||
isAltPressed.value = true;
|
isAltPressed.value = true;
|
||||||
|
altShortcutKey.value = null;
|
||||||
|
shortcutTriggeredInKeyDown.value = false; // +++ 重置标志位 +++
|
||||||
// console.log('[App] Alt key pressed down.');
|
// console.log('[App] Alt key pressed down.');
|
||||||
} else if (isAltPressed.value && event.key !== 'Alt') {
|
} else if (isAltPressed.value && !shortcutTriggeredInKeyDown.value && !['Control', 'Shift', 'Alt', 'Meta'].includes(event.key)) {
|
||||||
// 如果 Alt 正被按住,但按下了其他非 Alt 键,则取消本次 Alt 触发
|
// 如果 Alt 正被按住,快捷键尚未触发,且按下了非修饰键
|
||||||
|
let key = event.key;
|
||||||
|
if (key.length === 1) key = key.toUpperCase();
|
||||||
|
|
||||||
|
if (/^[a-zA-Z0-9]$/.test(key)) {
|
||||||
|
altShortcutKey.value = key; // 记录按键
|
||||||
|
const shortcutString = `Alt+${key}`;
|
||||||
|
console.log(`[App] KeyDown: Alt+${key} detected. Checking shortcut: ${shortcutString}`);
|
||||||
|
const targetId = focusSwitcherStore.getFocusTargetIdByShortcut(shortcutString);
|
||||||
|
|
||||||
|
if (targetId) {
|
||||||
|
console.log(`[App] KeyDown: Shortcut match found. Targeting ID: ${targetId}`);
|
||||||
|
event.preventDefault(); // 阻止默认行为 (如菜单)
|
||||||
|
const success = await focusSwitcherStore.focusTarget(targetId); // +++ 立即尝试聚焦 +++
|
||||||
|
if (success) {
|
||||||
|
console.log(`[App] KeyDown: Successfully focused ${targetId} via shortcut.`);
|
||||||
|
lastFocusedIdBySwitcher.value = targetId;
|
||||||
|
shortcutTriggeredInKeyDown.value = true; // +++ 设置标志位 +++
|
||||||
|
} else {
|
||||||
|
console.log(`[App] KeyDown: Failed to focus ${targetId} via shortcut action.`);
|
||||||
|
// 聚焦失败,可以选择是否取消 Alt 状态,暂时不处理,让 keyup 重置
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`[App] KeyDown: No configured shortcut found for ${shortcutString}.`);
|
||||||
|
// 没有匹配的快捷键,可以选择取消 Alt 状态以允许默认行为,或保持状态等待 keyup
|
||||||
|
// isAltPressed.value = false;
|
||||||
|
// altShortcutKey.value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 按下无效键 (非字母数字),取消 Alt 状态
|
||||||
isAltPressed.value = false;
|
isAltPressed.value = false;
|
||||||
// console.log('[App] Alt sequence cancelled by other key press.');
|
altShortcutKey.value = null;
|
||||||
|
shortcutTriggeredInKeyDown.value = false;
|
||||||
|
console.log('[App] KeyDown: Alt sequence cancelled by non-alphanumeric key press.');
|
||||||
|
}
|
||||||
|
} else if (isAltPressed.value && ['Control', 'Shift', 'Meta'].includes(event.key)) {
|
||||||
|
// 按下其他修饰键,取消 Alt 状态
|
||||||
|
isAltPressed.value = false;
|
||||||
|
altShortcutKey.value = null;
|
||||||
|
shortcutTriggeredInKeyDown.value = false;
|
||||||
|
console.log('[App] KeyDown: Alt sequence cancelled by other modifier key press.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// +++ 修改:全局键盘事件处理函数,现在监听 keyup +++
|
// +++ 修改:全局键盘事件处理函数,监听 keyup,优先处理快捷键 +++
|
||||||
const handleGlobalKeyUp = async (event: KeyboardEvent) => {
|
const handleGlobalKeyUp = async (event: KeyboardEvent) => {
|
||||||
// 仅当 Alt 键松开,并且之前是被独立按下的状态时触发
|
if (event.key === 'Alt') {
|
||||||
if (event.key === 'Alt' && isAltPressed.value) {
|
const altWasPressed = isAltPressed.value; // 记录松开前的状态
|
||||||
isAltPressed.value = false; // 重置状态
|
const shortcutHandled = shortcutTriggeredInKeyDown.value; // 记录是否在 keydown 处理
|
||||||
event.preventDefault(); // 阻止 Alt 键松开时的默认行为 (如果有的话)
|
|
||||||
console.log('[App] Alt key released, attempting focus switch.');
|
|
||||||
|
|
||||||
// +++ Log: 打印当前的配置序列 +++
|
// 总是重置状态
|
||||||
console.log('[App] Current configured sequence in store:', JSON.stringify(focusSwitcherStore.configuredSequence));
|
isAltPressed.value = false;
|
||||||
|
altShortcutKey.value = null;
|
||||||
|
shortcutTriggeredInKeyDown.value = false;
|
||||||
|
|
||||||
// --- 确定当前焦点位置 ---
|
if (altWasPressed && !shortcutHandled) {
|
||||||
// 优先使用上次切换器聚焦的 ID
|
// 如果 Alt 之前是按下的,并且没有在 keydown 中处理快捷键,则执行顺序切换
|
||||||
|
console.log('[App] KeyUp: Alt released without a handled shortcut. Attempting sequential focus switch.');
|
||||||
|
event.preventDefault(); // 仅在执行顺序切换时阻止默认行为
|
||||||
|
|
||||||
|
// --- 顺序切换逻辑 (保持不变) ---
|
||||||
let currentFocusId: string | null = lastFocusedIdBySwitcher.value;
|
let currentFocusId: string | null = lastFocusedIdBySwitcher.value;
|
||||||
console.log(`[App] Alt released. Last focused by switcher: ${currentFocusId}`);
|
console.log(`[App] Sequential switch. Last focused by switcher: ${currentFocusId}`);
|
||||||
|
|
||||||
// 如果上次切换器聚焦的 ID 不存在,尝试从 document.activeElement 获取
|
|
||||||
if (!currentFocusId) {
|
if (!currentFocusId) {
|
||||||
const activeElement = document.activeElement as HTMLElement;
|
const activeElement = document.activeElement as HTMLElement;
|
||||||
if (activeElement && activeElement.hasAttribute('data-focus-id')) {
|
if (activeElement && activeElement.hasAttribute('data-focus-id')) {
|
||||||
currentFocusId = activeElement.getAttribute('data-focus-id');
|
currentFocusId = activeElement.getAttribute('data-focus-id');
|
||||||
console.log(`[App] Found focus ID from activeElement: ${currentFocusId}`);
|
console.log(`[App] Sequential switch. Found focus ID from activeElement: ${currentFocusId}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`[App] Could not determine current focus ID from activeElement either.`);
|
console.log(`[App] Sequential switch. Could not determine current focus ID.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 重构后的查找和聚焦逻辑 (保持不变) ---
|
const configuredItems = focusSwitcherStore.configuredItems;
|
||||||
const sequence = focusSwitcherStore.configuredSequence;
|
if (configuredItems.length === 0) {
|
||||||
if (sequence.length === 0) {
|
|
||||||
console.log('[App] No focus sequence configured.');
|
console.log('[App] No focus sequence configured.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试聚焦下一个目标,循环最多一次
|
|
||||||
let focused = false;
|
let focused = false;
|
||||||
for (let i = 0; i < sequence.length; i++) {
|
for (let i = 0; i < configuredItems.length; i++) {
|
||||||
const nextFocusId = focusSwitcherStore.getNextFocusTargetId(currentFocusId);
|
const nextFocusId = focusSwitcherStore.getNextFocusTargetId(currentFocusId);
|
||||||
if (!nextFocusId) {
|
if (!nextFocusId) {
|
||||||
console.warn('[App] Could not determine next focus target ID.');
|
console.warn('[App] Could not determine next focus target ID in sequence.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[App] Trying to focus target ID: ${nextFocusId}`);
|
console.log(`[App] Sequential switch. Trying to focus target ID: ${nextFocusId}`);
|
||||||
const success = await focusSwitcherStore.focusTarget(nextFocusId);
|
const success = await focusSwitcherStore.focusTarget(nextFocusId);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log(`[App] Successfully focused ${nextFocusId}.`);
|
console.log(`[App] Successfully focused ${nextFocusId} sequentially.`);
|
||||||
lastFocusedIdBySwitcher.value = nextFocusId;
|
lastFocusedIdBySwitcher.value = nextFocusId;
|
||||||
focused = true;
|
focused = true;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
console.log(`[App] Failed to focus ${nextFocusId}. Trying next in sequence...`);
|
console.log(`[App] Failed to focus ${nextFocusId} sequentially. Trying next...`);
|
||||||
currentFocusId = nextFocusId;
|
currentFocusId = nextFocusId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,9 +210,15 @@ const handleGlobalKeyUp = async (event: KeyboardEvent) => {
|
|||||||
console.log('[App] Cycled through sequence, no target could be focused.');
|
console.log('[App] Cycled through sequence, no target could be focused.');
|
||||||
lastFocusedIdBySwitcher.value = null;
|
lastFocusedIdBySwitcher.value = null;
|
||||||
}
|
}
|
||||||
} else if (event.key === 'Alt') {
|
// --- 顺序切换逻辑结束 ---
|
||||||
// 如果 Alt 松开,但 isAltPressed 是 false (例如被其他键取消了),确保状态被重置
|
|
||||||
isAltPressed.value = false;
|
} else if (shortcutHandled) {
|
||||||
|
console.log('[App] KeyUp: Alt released, but shortcut was handled in keydown. No further action.');
|
||||||
|
// 如果需要在 keyup 时也阻止默认行为,可以在这里添加 event.preventDefault()
|
||||||
|
} else {
|
||||||
|
// Alt 松开,但 isAltPressed 已经是 false (例如被其他键取消了)
|
||||||
|
console.log('[App] KeyUp: Alt released, but sequence was already cancelled or not active.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, reactive, type Ref } from 'vue'; // 添加 Ref
|
import { ref, computed, watch, reactive, type Ref } from 'vue'; // 添加 Ref
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import draggable from 'vuedraggable'; // 导入 draggable
|
import draggable from 'vuedraggable';
|
||||||
import { useFocusSwitcherStore, type FocusableInput } from '../stores/focusSwitcher.store'; // 导入 Store 和类型
|
import { useFocusSwitcherStore, type FocusableInput, type ConfiguredFocusableInput } from '../stores/focusSwitcher.store'; // +++ 导入新接口 +++
|
||||||
import { storeToRefs } from 'pinia'; // 导入 storeToRefs
|
import { storeToRefs } from 'pinia';
|
||||||
|
// --- 移除本地类型定义 ---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- Props ---
|
// --- Props ---
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -33,23 +37,26 @@ const dialogStyle = reactive({
|
|||||||
});
|
});
|
||||||
const hasChanges = ref(false);
|
const hasChanges = ref(false);
|
||||||
// 本地副本,用于在弹窗内编辑而不直接修改 store
|
// 本地副本,用于在弹窗内编辑而不直接修改 store
|
||||||
const localSequence: Ref<FocusableInput[]> = ref([]);
|
const localSequence: Ref<ConfiguredFocusableInput[]> = ref([]); // +++ 使用导入的接口 +++
|
||||||
// +++ 存储原始序列 ID,用于比较 +++
|
// +++ 存储原始序列(包含 ID 和快捷键),用于比较 +++
|
||||||
const originalSequenceIds: Ref<string[]> = ref([]);
|
const originalSequence: Ref<ConfiguredFocusableInput[]> = ref([]); // +++ 使用导入的接口 +++
|
||||||
|
|
||||||
// --- Watchers ---
|
// --- Watchers ---
|
||||||
watch(() => props.isVisible, (newValue) => {
|
watch(() => props.isVisible, (newValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
// 从 Store 加载当前配置到本地副本
|
// 从 Store 加载当前配置到本地副本
|
||||||
// 使用深拷贝确保 localSequence 是独立的
|
// 从 Store 加载当前配置到本地副本
|
||||||
const loadedSequence = focusSwitcherStore.getConfiguredInputs; // 直接获取 getter 的值
|
// !!! 注意:Store 现在也需要支持快捷键,这里暂时假设它返回的数据包含 shortcut !!!
|
||||||
console.log('[FocusSwitcherConfigurator] Loading sequence from store getter...'); // +++ Log: Start loading +++
|
// 假设 getConfiguredInputs 返回的是 LocalFocusableInput[] 或能转换的类型
|
||||||
localSequence.value = JSON.parse(JSON.stringify(loadedSequence));
|
const loadedSequenceFromStore = focusSwitcherStore.getConfiguredInputs; // 这个 getter 可能需要修改
|
||||||
// +++ 存储原始 ID 序列 +++
|
console.log('[FocusSwitcherConfigurator] Loading sequence from store getter...');
|
||||||
originalSequenceIds.value = loadedSequence.map(item => item.id);
|
// 深拷贝,并确保每个项目都有 shortcut 属性(可能为 undefined)
|
||||||
|
// Store getter 现在返回正确的类型,可以直接深拷贝
|
||||||
|
localSequence.value = JSON.parse(JSON.stringify(loadedSequenceFromStore));
|
||||||
|
originalSequence.value = JSON.parse(JSON.stringify(loadedSequenceFromStore)); // 同样直接拷贝
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
console.log('[FocusSwitcherConfigurator] Dialog opened. Loaded sequence to local copy:', localSequence.value); // +++ Log: Loaded local +++
|
console.log('[FocusSwitcherConfigurator] Dialog opened. Loaded sequence to local copy:', localSequence.value);
|
||||||
console.log('[FocusSwitcherConfigurator] Original sequence IDs stored:', originalSequenceIds.value); // +++ Log: Stored original +++
|
console.log('[FocusSwitcherConfigurator] Original sequence stored:', originalSequence.value);
|
||||||
// 重置/计算初始位置和大小
|
// 重置/计算初始位置和大小
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (dialogRef.value) {
|
if (dialogRef.value) {
|
||||||
@@ -68,14 +75,10 @@ watch(() => props.isVisible, (newValue) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听本地序列变化,标记未保存更改
|
// 监听本地序列(包括快捷键)变化,标记未保存更改
|
||||||
watch(localSequence, (currentLocalSequence) => {
|
watch(localSequence, (currentLocalSequence) => {
|
||||||
// 直接比较当前本地序列的 ID 和原始 ID 序列
|
// 比较当前本地序列和原始序列的 JSON 字符串
|
||||||
const currentIds = currentLocalSequence.map(item => item.id);
|
const hasChanged = JSON.stringify(currentLocalSequence) !== JSON.stringify(originalSequence.value);
|
||||||
const originalIds = originalSequenceIds.value;
|
|
||||||
|
|
||||||
// 比较 JSON 字符串看是否有变化
|
|
||||||
const hasChanged = JSON.stringify(currentIds) !== JSON.stringify(originalIds); // +++ Calculate change status +++
|
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
// console.log('[FocusSwitcherConfigurator] Local sequence changed.'); // +++ Log: Changed +++
|
// console.log('[FocusSwitcherConfigurator] Local sequence changed.'); // +++ Log: Changed +++
|
||||||
hasChanges.value = true;
|
hasChanges.value = true;
|
||||||
@@ -99,11 +102,15 @@ const closeDialog = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const saveConfiguration = () => {
|
const saveConfiguration = () => {
|
||||||
// 从本地副本提取 ID 序列
|
// 提取仅包含 id 和 shortcut 的配置项数组
|
||||||
const newSequenceIds = localSequence.value.map(item => item.id);
|
const configToSave = localSequence.value.map(item => ({
|
||||||
console.log('[FocusSwitcherConfigurator] Saving configuration. Sequence IDs to save:', newSequenceIds); // +++ Log: Saving IDs +++
|
id: item.id,
|
||||||
focusSwitcherStore.updateSequence(newSequenceIds); // 更新 Store 中的序列 (这会触发保存到后端)
|
shortcut: item.shortcut || undefined, // 空字符串视为未设置
|
||||||
console.log('[FocusSwitcherConfigurator] Configuration save process triggered via updateSequence.'); // +++ Log: Save triggered +++
|
}));
|
||||||
|
console.log('[FocusSwitcherConfigurator] Saving configuration. Config to save:', configToSave);
|
||||||
|
// 调用 Store 中正确的更新函数
|
||||||
|
focusSwitcherStore.updateConfiguration(configToSave);
|
||||||
|
console.log('[FocusSwitcherConfigurator] Configuration save process triggered via updateConfiguration.');
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
emit('close'); // 保存后关闭
|
emit('close'); // 保存后关闭
|
||||||
};
|
};
|
||||||
@@ -163,16 +170,26 @@ const localAvailableInputs = computed(() => {
|
|||||||
tag="ul"
|
tag="ul"
|
||||||
class="draggable-list configured-list"
|
class="draggable-list configured-list"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
:group="{ name: 'focus-inputs', put: true }" <!-- 明确允许放入 -->
|
:group="{ name: 'focus-inputs', put: true }"
|
||||||
handle=".drag-handle"
|
handle=".drag-handle"
|
||||||
>
|
>
|
||||||
<template #item="{ element, index }: { element: FocusableInput, index: number }">
|
<template #item="{ element, index }: { element: ConfiguredFocusableInput, index: number }">
|
||||||
|
<div> <!-- Wrap the content in a single div -->
|
||||||
<li class="draggable-item">
|
<li class="draggable-item">
|
||||||
<i class="fas fa-grip-vertical drag-handle"></i>
|
<i class="fas fa-grip-vertical drag-handle"></i>
|
||||||
<span class="item-label">{{ element.label }}</span>
|
<span class="item-label">{{ element.label }}</span>
|
||||||
|
<!-- +++ 添加快捷键输入框 +++ -->
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="element.shortcut"
|
||||||
|
class="shortcut-input"
|
||||||
|
:placeholder="t('focusSwitcher.shortcutPlaceholder')"
|
||||||
|
@keydown.prevent="captureShortcut($event, element)"
|
||||||
|
/>
|
||||||
<!-- 添加移除按钮 -->
|
<!-- 添加移除按钮 -->
|
||||||
<button @click="localSequence.splice(index, 1)" class="remove-button" :title="t('common.remove', '移除')">×</button>
|
<button @click="localSequence.splice(index, 1)" class="remove-button" :title="t('common.remove', '移除')">×</button>
|
||||||
</li>
|
</li>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<li v-if="localSequence.length === 0" class="no-items-placeholder">
|
<li v-if="localSequence.length === 0" class="no-items-placeholder">
|
||||||
@@ -193,6 +210,42 @@ const localAvailableInputs = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
// +++ 在 <script setup> 之外定义辅助函数(如果需要更复杂的逻辑或重用)+++
|
||||||
|
// 或者直接在 setup 内部定义 captureShortcut
|
||||||
|
|
||||||
|
// 内部定义 captureShortcut
|
||||||
|
const captureShortcut = (event: KeyboardEvent, element: ConfiguredFocusableInput) => { // +++ 使用导入的接口 +++
|
||||||
|
if (event.key === 'Alt' || event.key === 'Control' || event.key === 'Shift' || event.key === 'Meta') {
|
||||||
|
// 忽略单独的修饰键按下
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
|
||||||
|
// 必须是 Alt + 非修饰键
|
||||||
|
let key = event.key;
|
||||||
|
if (key.length === 1) { // 将小写字母转为大写
|
||||||
|
key = key.toUpperCase();
|
||||||
|
}
|
||||||
|
// 可以添加更多验证,例如只允许字母、数字等
|
||||||
|
if (/^[a-zA-Z0-9]$/.test(key)) { // 简化:只允许单个字母或数字
|
||||||
|
element.shortcut = `Alt+${key}`;
|
||||||
|
} else if (key === 'Backspace' || key === 'Delete') {
|
||||||
|
element.shortcut = ''; // 允许使用 Backspace 或 Delete 清空
|
||||||
|
} else {
|
||||||
|
// 可选:提示不支持的键
|
||||||
|
console.warn(`[FocusSwitcherConfigurator] Unsupported key for shortcut: ${key}`);
|
||||||
|
}
|
||||||
|
} else if (event.key === 'Backspace' || event.key === 'Delete') {
|
||||||
|
// 允许单独按 Backspace 或 Delete 清空 (即使没有 Alt)
|
||||||
|
element.shortcut = '';
|
||||||
|
} else {
|
||||||
|
// 可选:如果按下非 Alt 组合键,可以清空或提示
|
||||||
|
// console.log('[FocusSwitcherConfigurator] Invalid shortcut combination.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 样式很大程度上复用 LayoutConfigurator,但使用不同的类名以避免冲突 */
|
/* 样式很大程度上复用 LayoutConfigurator,但使用不同的类名以避免冲突 */
|
||||||
.focus-switcher-overlay {
|
.focus-switcher-overlay {
|
||||||
@@ -277,13 +330,32 @@ h3 {
|
|||||||
flex-grow: 1; /* 占据剩余空间 */
|
flex-grow: 1; /* 占据剩余空间 */
|
||||||
overflow: hidden; /* 隐藏溢出文本 */
|
overflow: hidden; /* 隐藏溢出文本 */
|
||||||
text-overflow: ellipsis; /* 显示省略号 */
|
text-overflow: ellipsis; /* 显示省略号 */
|
||||||
white-space: nowrap; /* 防止换行 */
|
white-space: nowrap;
|
||||||
|
margin-right: 0.5rem; /* 与快捷键输入框保持间距 */
|
||||||
|
}
|
||||||
|
/* +++ 新增快捷键输入框样式 +++ */
|
||||||
|
.shortcut-input {
|
||||||
|
width: 100px; /* 固定宽度 */
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--input-bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-align: center;
|
||||||
|
margin-left: auto; /* 将其推到标签和移除按钮之间 */
|
||||||
margin-right: 0.5rem; /* 与移除按钮保持间距 */
|
margin-right: 0.5rem; /* 与移除按钮保持间距 */
|
||||||
|
flex-shrink: 0; /* 防止被压缩 */
|
||||||
|
}
|
||||||
|
.shortcut-input::placeholder {
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
.remove-button {
|
.remove-button {
|
||||||
background: none; border: none; color: var(--text-color-secondary);
|
background: none; border: none; color: var(--text-color-secondary);
|
||||||
font-size: 1.2rem; cursor: pointer; padding: 0 0.3rem; line-height: 1;
|
font-size: 1.2rem; cursor: pointer; padding: 0 0.3rem; line-height: 1;
|
||||||
margin-left: auto; /* 推到最右边 */
|
flex-shrink: 0; /* 防止被压缩 */
|
||||||
|
/* margin-left: auto; 现在由 shortcut-input 推 */
|
||||||
}
|
}
|
||||||
.remove-button:hover { color: var(--danger-color, red); }
|
.remove-button:hover { color: var(--danger-color, red); }
|
||||||
.no-items-placeholder {
|
.no-items-placeholder {
|
||||||
|
|||||||
@@ -828,16 +828,20 @@
|
|||||||
"focusSwitcher": {
|
"focusSwitcher": {
|
||||||
"configTitle": "Configure Focus Switcher",
|
"configTitle": "Configure Focus Switcher",
|
||||||
"availableInputs": "Available Inputs",
|
"availableInputs": "Available Inputs",
|
||||||
"configuredSequence": "Configured Sequence",
|
"configuredSequence": "Configured Sequence (Drag to Sort)",
|
||||||
"dragHere": "Drag here to add",
|
"dragHere": "Drag inputs from the left here",
|
||||||
"allInputsConfigured": "All available inputs are configured",
|
"allInputsConfigured": "All available inputs are configured",
|
||||||
"input": {
|
"input": {
|
||||||
"commandHistorySearch": "Search command history...",
|
"commandHistorySearch": "Command History Search",
|
||||||
"quickCommandsSearch": "Search quick commands...",
|
"quickCommandsSearch": "Quick Commands Search",
|
||||||
"fileManagerSearch": "Search files...",
|
"fileManagerSearch": "File Manager Search",
|
||||||
"commandInput": "Enter command...",
|
"commandInput": "Command Input",
|
||||||
"terminalSearch": "Search terminal..."
|
"terminalSearch": "Terminal Search",
|
||||||
}
|
"connectionListSearch": "Connection List Search",
|
||||||
|
"fileEditorActive": "File Editor"
|
||||||
|
},
|
||||||
|
"confirmClose": "You have unsaved changes. Are you sure you want to close?",
|
||||||
|
"shortcutPlaceholder": "e.g., Alt+K"
|
||||||
},
|
},
|
||||||
"dockerManager": {
|
"dockerManager": {
|
||||||
"loading": "Loading Docker Containers...",
|
"loading": "Loading Docker Containers...",
|
||||||
|
|||||||
@@ -833,16 +833,20 @@
|
|||||||
"focusSwitcher": {
|
"focusSwitcher": {
|
||||||
"configTitle": "配置焦点切换器",
|
"configTitle": "配置焦点切换器",
|
||||||
"availableInputs": "可用输入源",
|
"availableInputs": "可用输入源",
|
||||||
"configuredSequence": "已配置序列",
|
"configuredSequence": "已配置序列 (拖拽排序)",
|
||||||
"dragHere": "拖拽到此处添加",
|
"dragHere": "从左侧拖拽输入框到此处",
|
||||||
"allInputsConfigured": "所有可用输入源都已配置",
|
"allInputsConfigured": "所有可用输入源都已配置",
|
||||||
"input": {
|
"input": {
|
||||||
"commandHistorySearch": "搜索命令历史...",
|
"commandHistorySearch": "命令历史搜索",
|
||||||
"quickCommandsSearch": "搜索快捷指令...",
|
"quickCommandsSearch": "快捷指令搜索",
|
||||||
"fileManagerSearch": "搜索文件...",
|
"fileManagerSearch": "文件管理器搜索",
|
||||||
"commandInput": "输入命令...",
|
"commandInput": "命令输入",
|
||||||
"terminalSearch": "搜索终端..."
|
"terminalSearch": "终端内搜索",
|
||||||
}
|
"connectionListSearch": "连接列表搜索",
|
||||||
|
"fileEditorActive": "文件编辑器"
|
||||||
|
},
|
||||||
|
"confirmClose": "有未保存的更改,确定要关闭吗?",
|
||||||
|
"shortcutPlaceholder": "例如 Alt+K"
|
||||||
},
|
},
|
||||||
"dockerManager": {
|
"dockerManager": {
|
||||||
"loading": "正在加载 Docker 容器...",
|
"loading": "正在加载 Docker 容器...",
|
||||||
|
|||||||
@@ -9,13 +9,26 @@ export interface FocusableInput {
|
|||||||
id: string; // 唯一标识符
|
id: string; // 唯一标识符
|
||||||
label: string; // 用户友好的名称
|
label: string; // 用户友好的名称
|
||||||
// componentPath 和 selector 不再需要,聚焦完全依赖 focusAction
|
// componentPath 和 selector 不再需要,聚焦完全依赖 focusAction
|
||||||
focusAction: () => boolean | Promise<boolean>; // 改为必需
|
focusAction: () => boolean | Promise<boolean>;
|
||||||
|
// shortcut?: string; // --- 从基础接口移除快捷键,因为它只在配置中相关 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
// +++ 新增:定义包含快捷键的完整配置项接口 (用于 Store 内部和配置器) +++
|
||||||
|
export interface ConfiguredFocusableInput extends FocusableInput {
|
||||||
|
shortcut?: string; // 快捷键是可选的
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义存储在后端的配置项接口 (仅 ID 和 shortcut)
|
||||||
|
export interface ConfigurableFocusableItem {
|
||||||
|
id: string;
|
||||||
|
shortcut?: string; // 快捷键是可选的
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义 Store 的 State 接口 (可选但推荐)
|
// 定义 Store 的 State 接口 (可选但推荐)
|
||||||
interface FocusSwitcherState {
|
interface FocusSwitcherState {
|
||||||
availableInputs: FocusableInput[];
|
availableInputs: FocusableInput[]; // 保持不变,定义所有可用项及其基础信息
|
||||||
configuredSequence: string[]; // 只存储 ID 序列
|
// configuredSequence: string[]; // --- 移除旧的状态 ---
|
||||||
|
configuredItems: ConfigurableFocusableItem[]; // +++ 新增:存储已配置项的 ID 和快捷键 +++
|
||||||
isConfiguratorVisible: boolean;
|
isConfiguratorVisible: boolean;
|
||||||
activateFileManagerSearchTrigger: number;
|
activateFileManagerSearchTrigger: number;
|
||||||
activateTerminalSearchTrigger: number;
|
activateTerminalSearchTrigger: number;
|
||||||
@@ -38,7 +51,8 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
|
|||||||
{ id: 'connectionListSearch', label: t('focusSwitcher.input.connectionListSearch', '连接列表搜索'), focusAction: () => false },
|
{ id: 'connectionListSearch', label: t('focusSwitcher.input.connectionListSearch', '连接列表搜索'), focusAction: () => false },
|
||||||
{ id: 'fileEditorActive', label: t('focusSwitcher.input.fileEditorActive', '文件编辑器'), focusAction: () => false },
|
{ id: 'fileEditorActive', label: t('focusSwitcher.input.fileEditorActive', '文件编辑器'), focusAction: () => false },
|
||||||
]);
|
]);
|
||||||
const configuredSequence = ref<string[]>([]);
|
// const configuredSequence = ref<string[]>([]); // --- 移除旧的状态 ref ---
|
||||||
|
const configuredItems = ref<ConfigurableFocusableItem[]>([]); // +++ 新增:存储配置项的状态 ref +++
|
||||||
const isConfiguratorVisible = ref(false);
|
const isConfiguratorVisible = ref(false);
|
||||||
const activateFileManagerSearchTrigger = ref(0);
|
const activateFileManagerSearchTrigger = ref(0);
|
||||||
const activateTerminalSearchTrigger = ref(0);
|
const activateTerminalSearchTrigger = ref(0);
|
||||||
@@ -48,83 +62,95 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
|
|||||||
|
|
||||||
// --- Actions ---
|
// --- Actions ---
|
||||||
|
|
||||||
// +++ 新增:从后端加载配置 +++
|
// +++ 修改:从后端加载配置(包括快捷键) +++
|
||||||
async function loadSequenceFromBackend() {
|
async function loadConfigurationFromBackend() { // 重命名以反映加载的是完整配置
|
||||||
const apiUrl = '/api/v1/settings/focus-switcher-sequence'; // +++ 定义 API URL +++
|
const apiUrl = '/api/v1/settings/focus-switcher-sequence'; // 假设 API 端点不变
|
||||||
console.log(`[FocusSwitcherStore] Attempting to load sequence from backend via: ${apiUrl}`); // +++ 更新日志 +++
|
console.log(`[FocusSwitcherStore] Attempting to load configuration (sequence & shortcuts) from backend via: ${apiUrl}`);
|
||||||
try {
|
try {
|
||||||
// 注意:需要根据实际项目配置调整 API 路径和认证处理
|
const response = await fetch(apiUrl);
|
||||||
const response = await fetch(apiUrl); // +++ 使用变量 +++
|
console.log(`[FocusSwitcherStore] Received response from ${apiUrl}. Status: ${response.status}`);
|
||||||
console.log(`[FocusSwitcherStore] Received response from ${apiUrl}. Status: ${response.status}`); // +++ 添加日志:响应状态 +++
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
// +++ 添加日志:记录非 OK 状态 +++
|
|
||||||
console.error(`[FocusSwitcherStore] HTTP error from ${apiUrl}. Status: ${response.status}`);
|
console.error(`[FocusSwitcherStore] HTTP error from ${apiUrl}. Status: ${response.status}`);
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sequence = await response.json();
|
// *** 假设后端返回 ConfigurableFocusableItem[] 结构 ***
|
||||||
// +++ 添加日志:记录原始 JSON 数据 +++
|
const loadedConfig = await response.json();
|
||||||
console.log(`[FocusSwitcherStore] Raw JSON received from backend for sequence:`, JSON.stringify(sequence));
|
console.log(`[FocusSwitcherStore] Raw JSON received from backend:`, JSON.stringify(loadedConfig));
|
||||||
|
|
||||||
if (Array.isArray(sequence) && sequence.every(item => typeof item === 'string')) {
|
// --- 验证和过滤 ---
|
||||||
console.log('[FocusSwitcherStore] Sequence format is valid (string array). Filtering against available inputs...'); // +++ 添加日志 +++
|
if (Array.isArray(loadedConfig) && loadedConfig.every(item => typeof item?.id === 'string')) {
|
||||||
// 验证加载的 ID 是否仍然存在于 availableInputs 中
|
console.log('[FocusSwitcherStore] Configuration format seems valid (array of objects with id). Filtering against available inputs...');
|
||||||
const availableIds = new Set(availableInputs.value.map(input => input.id));
|
const availableIds = new Set(availableInputs.value.map(input => input.id));
|
||||||
const filteredSequence = sequence.filter((id: string) => {
|
const filteredConfig: ConfigurableFocusableItem[] = loadedConfig
|
||||||
const isValid = availableIds.has(id);
|
.filter((item: any) => {
|
||||||
|
const isValid = availableIds.has(item.id);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
console.warn(`[FocusSwitcherStore] Filtered out invalid ID during load from backend: ${id}`);
|
console.warn(`[FocusSwitcherStore] Filtered out invalid ID during load: ${item.id}`);
|
||||||
}
|
}
|
||||||
return isValid;
|
return isValid;
|
||||||
});
|
})
|
||||||
configuredSequence.value = filteredSequence;
|
.map((item: any) => ({ // 确保只保留 id 和 shortcut
|
||||||
// +++ 更新日志:显示最终设置的值 +++
|
id: item.id,
|
||||||
console.log('[FocusSwitcherStore] Successfully loaded and set configuredSequence:', JSON.stringify(configuredSequence.value));
|
shortcut: typeof item.shortcut === 'string' && item.shortcut.startsWith('Alt+') ? item.shortcut : undefined // 验证快捷键格式
|
||||||
|
}));
|
||||||
|
|
||||||
|
configuredItems.value = filteredConfig;
|
||||||
|
console.log('[FocusSwitcherStore] Successfully loaded and set configuredItems:', JSON.stringify(configuredItems.value));
|
||||||
} else {
|
} else {
|
||||||
console.error('[FocusSwitcherStore] Invalid sequence format received from backend:', sequence);
|
// --- 处理旧格式 (仅 ID 数组) 或无效格式 ---
|
||||||
configuredSequence.value = []; // 使用空数组作为回退
|
if (Array.isArray(loadedConfig) && loadedConfig.every(item => typeof item === 'string')) {
|
||||||
console.log('[FocusSwitcherStore] Set configuredSequence to empty array due to invalid format.'); // +++ 添加日志 +++
|
console.warn('[FocusSwitcherStore] Received old format (string array) from backend. Converting to new structure without shortcuts.');
|
||||||
|
const availableIds = new Set(availableInputs.value.map(input => input.id));
|
||||||
|
const filteredSequence = loadedConfig.filter((id: string) => availableIds.has(id));
|
||||||
|
configuredItems.value = filteredSequence.map(id => ({ id })); // 转换为新结构,无快捷键
|
||||||
|
console.log('[FocusSwitcherStore] Converted old format to configuredItems:', JSON.stringify(configuredItems.value));
|
||||||
|
// 可以考虑触发一次保存,将转换后的新格式存回后端
|
||||||
|
// saveSequenceToBackend();
|
||||||
|
} else {
|
||||||
|
console.error('[FocusSwitcherStore] Invalid configuration format received from backend:', loadedConfig);
|
||||||
|
configuredItems.value = []; // 使用空数组作为回退
|
||||||
|
console.log('[FocusSwitcherStore] Set configuredItems to empty array due to invalid format.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// +++ 添加日志:记录 fetch 或 JSON 解析错误 +++
|
console.error(`[FocusSwitcherStore] Failed to load or parse configuration from backend (${apiUrl}):`, error);
|
||||||
console.error(`[FocusSwitcherStore] Failed to load or parse sequence from backend (${apiUrl}):`, error);
|
configuredItems.value = [];
|
||||||
// 加载失败时可以考虑使用默认值或保持为空
|
console.log('[FocusSwitcherStore] Set configuredItems to empty array due to loading error.');
|
||||||
configuredSequence.value = [];
|
|
||||||
console.log('[FocusSwitcherStore] Set configuredSequence to empty array due to loading error.'); // +++ 添加日志 +++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// +++ 新增:保存配置到后端 +++
|
// +++ 修改:保存配置到后端(包括快捷键) +++
|
||||||
async function saveSequenceToBackend() {
|
async function saveConfigurationToBackend() { // 重命名以反映保存的是完整配置
|
||||||
const apiUrl = '/api/v1/settings/focus-switcher-sequence'; // +++ 定义 API URL +++
|
const apiUrl = '/api/v1/settings/focus-switcher-sequence'; // 假设 API 端点不变
|
||||||
console.log(`[FocusSwitcherStore] Attempting to save sequence to backend via PUT: ${apiUrl}`); // +++ 更新日志 +++
|
console.log(`[FocusSwitcherStore] Attempting to save configuration (sequence & shortcuts) to backend via PUT: ${apiUrl}`);
|
||||||
try {
|
try {
|
||||||
const sequenceToSave = configuredSequence.value;
|
// *** 构造后端期望的请求体:{ sequence: string[] } ***
|
||||||
console.log('[FocusSwitcherStore] Sequence data to save:', JSON.stringify(sequenceToSave)); // +++ 添加日志 +++
|
const sequenceIds = configuredItems.value.map(item => item.id);
|
||||||
// 注意:需要根据实际项目配置调整 API 路径和认证处理
|
const requestBody = { sequence: sequenceIds };
|
||||||
const response = await fetch(apiUrl, { // +++ 使用变量 +++
|
console.log('[FocusSwitcherStore] Configuration data to save (backend format):', JSON.stringify(requestBody));
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
// 如果需要认证,添加 Authorization header
|
// Auth headers if needed
|
||||||
// 'Authorization': `Bearer ${your_token}`
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ sequence: sequenceToSave }),
|
body: JSON.stringify(requestBody), // *** 发送符合后端格式的请求体 ***
|
||||||
});
|
});
|
||||||
console.log(`[FocusSwitcherStore] Received response from PUT ${apiUrl}. Status: ${response.status}`); // +++ 添加日志 +++
|
console.log(`[FocusSwitcherStore] Received response from PUT ${apiUrl}. Status: ${response.status}`);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({})); // 尝试解析错误体
|
const errorData = await response.json().catch(() => ({}));
|
||||||
console.error(`[FocusSwitcherStore] Save failed. Status: ${response.status}, Error data:`, errorData); // +++ 添加日志 +++
|
console.error(`[FocusSwitcherStore] Save failed. Status: ${response.status}, Error data:`, errorData);
|
||||||
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.message || 'Unknown error'}`);
|
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.message || 'Unknown error'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
console.log('[FocusSwitcherStore] Configuration successfully saved to backend. Response message:', result.message); // +++ 更新日志 +++
|
console.log('[FocusSwitcherStore] Configuration successfully saved to backend. Response message:', result.message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[FocusSwitcherStore] Failed to save sequence to backend (${apiUrl}):`, error); // +++ 更新日志 +++
|
console.error(`[FocusSwitcherStore] Failed to save configuration to backend (${apiUrl}):`, error);
|
||||||
// 可以在这里触发 UI 通知用户保存失败
|
// Notify user of failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,15 +184,23 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 更新切换顺序 (现在也触发保存到后端)
|
// +++ 修改:更新配置(包括顺序和快捷键) +++
|
||||||
function updateSequence(newSequence: string[]) {
|
function updateConfiguration(newConfig: ConfigurableFocusableItem[]) { // 重命名并修改参数类型
|
||||||
console.log('[FocusSwitcherStore] updateSequence called with:', JSON.stringify(newSequence)); // +++ 更新日志 +++
|
console.log('[FocusSwitcherStore] updateConfiguration called with new configuration:', JSON.stringify(newConfig));
|
||||||
const availableIds = new Set(availableInputs.value.map(input => input.id));
|
const availableIds = new Set(availableInputs.value.map(input => input.id));
|
||||||
const filteredSequence = newSequence.filter(id => availableIds.has(id));
|
|
||||||
configuredSequence.value = filteredSequence;
|
// 过滤掉无效的 ID,并验证/清理快捷键
|
||||||
console.log('[FocusSwitcherStore] configuredSequence updated locally to:', JSON.stringify(configuredSequence.value)); // +++ 更新日志 +++
|
const filteredConfig = newConfig
|
||||||
|
.filter(item => availableIds.has(item.id))
|
||||||
|
.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
shortcut: typeof item.shortcut === 'string' && item.shortcut.startsWith('Alt+') ? item.shortcut : undefined
|
||||||
|
}));
|
||||||
|
|
||||||
|
configuredItems.value = filteredConfig;
|
||||||
|
console.log('[FocusSwitcherStore] configuredItems updated locally to:', JSON.stringify(configuredItems.value));
|
||||||
// 更新后立即保存到后端
|
// 更新后立即保存到后端
|
||||||
saveSequenceToBackend();
|
saveConfigurationToBackend(); // 调用重命名后的保存函数
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册聚焦动作 (现在更新 availableInputs 中的 focusAction)
|
// 注册聚焦动作 (现在更新 availableInputs 中的 focusAction)
|
||||||
@@ -220,46 +254,68 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Getters ---
|
// --- 修改 Getters ---
|
||||||
const getConfiguredInputs = computed((): FocusableInput[] => {
|
// +++ 修改:获取完整的已配置输入框信息(合并快捷键)+++
|
||||||
return configuredSequence.value
|
// 返回类型现在包含 shortcut,所以需要调整或确认 FocusableInput 定义
|
||||||
.map(id => availableInputs.value.find(input => input.id === id))
|
const getConfiguredInputs = computed((): ConfiguredFocusableInput[] => { // +++ 更新返回类型 +++
|
||||||
.filter((input): input is FocusableInput => input !== undefined);
|
const inputsMap = new Map(availableInputs.value.map(input => [input.id, input]));
|
||||||
|
return configuredItems.value
|
||||||
|
.map(item => {
|
||||||
|
const baseInput = inputsMap.get(item.id);
|
||||||
|
if (!baseInput) return undefined;
|
||||||
|
// 创建一个新对象,类型为 ConfiguredFocusableInput
|
||||||
|
const configuredInput: ConfiguredFocusableInput = { // +++ 使用新类型 +++
|
||||||
|
...baseInput,
|
||||||
|
shortcut: item.shortcut,
|
||||||
|
};
|
||||||
|
return configuredInput;
|
||||||
|
})
|
||||||
|
.filter((input): input is ConfiguredFocusableInput => input !== undefined); // +++ 更新类型断言 +++
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// +++ 修改:获取配置器中“可用”的输入框列表 +++
|
||||||
const getAvailableInputsForConfigurator = computed((): FocusableInput[] => {
|
const getAvailableInputsForConfigurator = computed((): FocusableInput[] => {
|
||||||
const configuredIds = new Set(configuredSequence.value);
|
const configuredIds = new Set(configuredItems.value.map(item => item.id));
|
||||||
|
// 返回尚未配置的基础输入框信息(不需要快捷键)
|
||||||
return availableInputs.value.filter(input => !configuredIds.has(input.id));
|
return availableInputs.value.filter(input => !configuredIds.has(input.id));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// +++ 修改:获取序列中的下一个聚焦目标 ID +++
|
||||||
function getNextFocusTargetId(currentFocusedId: string | null): string | null {
|
function getNextFocusTargetId(currentFocusedId: string | null): string | null {
|
||||||
if (configuredSequence.value.length === 0) {
|
if (configuredItems.value.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (currentFocusedId === null) {
|
if (currentFocusedId === null) {
|
||||||
return configuredSequence.value[0];
|
return configuredItems.value[0].id; // 返回第一个配置项的 ID
|
||||||
}
|
}
|
||||||
const currentIndex = configuredSequence.value.indexOf(currentFocusedId);
|
const currentIndex = configuredItems.value.findIndex(item => item.id === currentFocusedId);
|
||||||
if (currentIndex === -1) {
|
if (currentIndex === -1) {
|
||||||
return configuredSequence.value[0];
|
return configuredItems.value[0].id; // 如果当前 ID 不在配置中,返回第一个
|
||||||
}
|
}
|
||||||
const nextIndex = (currentIndex + 1) % configuredSequence.value.length;
|
const nextIndex = (currentIndex + 1) % configuredItems.value.length;
|
||||||
return configuredSequence.value[nextIndex];
|
return configuredItems.value[nextIndex].id; // 返回下一个配置项的 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// +++ 新增:根据快捷键获取目标 ID +++
|
||||||
|
function getFocusTargetIdByShortcut(shortcut: string): string | null {
|
||||||
|
const foundItem = configuredItems.value.find(item => item.shortcut === shortcut);
|
||||||
|
return foundItem?.id ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Initialization ---
|
// --- Initialization ---
|
||||||
// Store 创建时自动从后端加载配置
|
// Store 创建时自动从后端加载配置
|
||||||
console.log('[FocusSwitcherStore] Initializing store and scheduling loadSequenceFromBackend...'); // +++ 添加日志 +++
|
console.log('[FocusSwitcherStore] Initializing store and scheduling loadConfigurationFromBackend...'); // 使用新名称
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
console.log('[FocusSwitcherStore] nextTick triggered, calling loadSequenceFromBackend.'); // +++ 添加日志 +++
|
console.log('[FocusSwitcherStore] nextTick triggered, calling loadConfigurationFromBackend.'); // 使用新名称
|
||||||
loadSequenceFromBackend(); // +++ 调用新的加载函数 +++
|
loadConfigurationFromBackend(); // 调用重命名后的加载函数
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
availableInputs,
|
availableInputs,
|
||||||
configuredSequence,
|
// configuredSequence, // --- 移除 ---
|
||||||
|
configuredItems, // +++ 暴露新状态 +++
|
||||||
isConfiguratorVisible,
|
isConfiguratorVisible,
|
||||||
activateFileManagerSearchTrigger,
|
activateFileManagerSearchTrigger,
|
||||||
activateTerminalSearchTrigger,
|
activateTerminalSearchTrigger,
|
||||||
@@ -267,16 +323,16 @@ export const useFocusSwitcherStore = defineStore('focusSwitcher', () => {
|
|||||||
toggleConfigurator,
|
toggleConfigurator,
|
||||||
triggerFileManagerSearchActivation,
|
triggerFileManagerSearchActivation,
|
||||||
triggerTerminalSearchActivation,
|
triggerTerminalSearchActivation,
|
||||||
loadSequenceFromBackend, // +++ 导出新的加载函数 +++
|
loadConfigurationFromBackend, // +++ 使用新名称 +++
|
||||||
saveSequenceToBackend, // +++ 导出新的保存函数 +++
|
saveConfigurationToBackend, // +++ 使用新名称 +++
|
||||||
updateSequence,
|
updateConfiguration, // +++ 使用新名称 +++
|
||||||
// Getters
|
// Getters / Methods
|
||||||
getConfiguredInputs,
|
getConfiguredInputs, // 已修改
|
||||||
getAvailableInputsForConfigurator,
|
getAvailableInputsForConfigurator, // 已修改
|
||||||
getNextFocusTargetId,
|
getNextFocusTargetId, // 已修改
|
||||||
registerFocusAction, // 暴露注册方法
|
getFocusTargetIdByShortcut, // +++ 新增 +++
|
||||||
unregisterFocusAction, // 暴露注销方法
|
registerFocusAction,
|
||||||
// focusActions 不再需要暴露
|
unregisterFocusAction,
|
||||||
focusTarget, // 暴露新的统一聚焦方法
|
focusTarget,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user