feat: 添加"弹出文件管理器窗口"设置项

This commit is contained in:
Baobhan Sith
2025-05-13 08:47:42 +08:00
parent 83e3d8e043
commit e886d13ed3
10 changed files with 133 additions and 5 deletions
@@ -41,6 +41,7 @@ export const settingsController = {
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
'showPopupFileManager', // +++ 添加弹窗文件管理器设置键 +++
'sidebarPaneWidths', // +++ 添加侧边栏宽度对象键 +++
'fileManagerRowSizeMultiplier', // +++ 添加文件管理器行大小键 +++
'fileManagerColWidths', // +++ 添加文件管理器列宽键 +++
@@ -25,7 +25,7 @@ const commandHistoryStore = useCommandHistoryStore();
const sessionStore = useSessionStore(); // +++ 初始化 Session Store +++
// Get reactive setting from store
const { commandInputSyncTarget } = storeToRefs(settingsStore);
const { commandInputSyncTarget, showPopupFileManagerBoolean } = storeToRefs(settingsStore); // +++ Import showPopupFileManagerBoolean +++
// Get reactive state and actions from quick commands store
const { selectedIndex: quickCommandsSelectedIndex, flatVisibleCommands: quickCommandsFiltered } = storeToRefs(quickCommandsStore);
const { resetSelection: resetQuickCommandsSelection } = quickCommandsStore;
@@ -298,6 +298,17 @@ const closeSuspendedSshSessionsModal = () => {
showSuspendedSshSessionsModal.value = false;
};
// +++ Function to request opening the file manager modal via event bus +++
const openFileManagerModal = () => {
if (activeSessionId.value) {
console.log(`[CommandInputBar] Emitting fileManager:openModalRequest for session: ${activeSessionId.value}`);
emitWorkspaceEvent('fileManager:openModalRequest', { sessionId: activeSessionId.value });
} else {
console.warn('[CommandInputBar] Cannot open file manager modal: No active session ID.');
// Optionally, show a notification to the user
}
};
// +++ Handler for command execution from the modal +++
const handleQuickCommandExecute = (command: string) => {
console.log(`[CommandInputBar] Executing quick command: ${command}`);
@@ -400,6 +411,15 @@ const handleQuickCommandExecute = (command: string) => {
<i v-if="!isSearching" class="fas fa-search text-base"></i>
<i v-else class="fas fa-times text-base"></i>
</button>
<!-- File Manager Button -->
<button
v-if="showPopupFileManagerBoolean"
@click="openFileManagerModal"
class="flex-shrink-0 flex items-center justify-center w-8 h-8 border border-border/50 rounded-lg text-text-secondary transition-colors duration-200 hover:bg-border hover:text-foreground"
:title="t('fileManager.title', '文件管理器')"
>
<i class="fas fa-folder text-base"></i>
</button>
<!-- Search navigation buttons (Hide on mobile when searching) -->
<template v-if="isSearching && !props.isMobile"> <!-- +++ Add !props.isMobile condition +++ -->
@@ -434,6 +454,7 @@ const handleQuickCommandExecute = (command: string) => {
:is-visible="showSuspendedSshSessionsModal"
@close="closeSuspendedSshSessionsModal"
/>
<!-- File Manager Modal is now handled by a listener for 'fileManager:openModalRequest' event -->
</template>
<style scoped>
@@ -21,6 +21,26 @@
</form>
</div>
<hr class="border-border/50">
<!-- Popup File Manager -->
<div class="settings-section-content">
<h3 class="text-base font-semibold text-foreground mb-3">{{ t('settings.popupFileManager.title') }}</h3>
<form @submit.prevent="handleUpdateShowPopupFileManager" class="space-y-4">
<div class="flex items-center">
<input type="checkbox" id="showPopupFileManager" v-model="showPopupFileManagerLocal"
class="h-4 w-4 rounded border-border text-primary focus:ring-primary mr-2 cursor-pointer">
<label for="showPopupFileManager" class="text-sm text-foreground cursor-pointer select-none">{{ t('settings.popupFileManager.enableLabel') }}</label>
</div>
<small class="block mt-1 text-xs text-text-secondary">{{ t('settings.popupFileManager.description') }}</small>
<div class="flex items-center justify-between">
<button type="submit"
class="px-4 py-2 bg-button text-button-text rounded-md shadow-sm hover:bg-button-hover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary transition duration-150 ease-in-out text-sm font-medium">
{{ t('common.save') }}
</button>
<p v-if="showPopupFileManagerMessage" :class="['text-sm', showPopupFileManagerSuccess ? 'text-success' : 'text-error']">{{ showPopupFileManagerMessage }}</p>
</div>
</form>
</div>
<hr class="border-border/50">
<!-- Share Tabs -->
<div class="settings-section-content">
<h3 class="text-base font-semibold text-foreground mb-3">{{ $t('settings.shareEditorTabs.title') }}</h3>
@@ -268,6 +288,12 @@ const {
terminalEnableRightClickPasteMessage, // NEW
terminalEnableRightClickPasteSuccess, // NEW
handleUpdateTerminalRightClickPasteSetting, // NEW
// Popup File Manager
showPopupFileManagerLocal,
// showPopupFileManagerLoading, // Not used
showPopupFileManagerMessage,
showPopupFileManagerSuccess,
handleUpdateShowPopupFileManager,
} = useWorkspaceSettings();
</script>
@@ -163,7 +163,6 @@ export function useSystemSettings() {
selectedLanguage.value = newVal;
}, { immediate: true });
return {
// Language
selectedLanguage,
@@ -18,6 +18,7 @@ export function useWorkspaceSettings() {
terminalScrollbackLimitNumber,
fileManagerShowDeleteConfirmationBoolean,
terminalEnableRightClickPasteBoolean, // NEW
showPopupFileManagerBoolean, // +++ Import the new getter +++
} = storeToRefs(settingsStore);
// --- Popup Editor ---
@@ -261,6 +262,30 @@ export function useWorkspaceSettings() {
}
};
// --- Popup File Manager ---
const showPopupFileManagerLocal = ref(true);
const showPopupFileManagerLoading = ref(false);
const showPopupFileManagerMessage = ref('');
const showPopupFileManagerSuccess = ref(false);
const handleUpdateShowPopupFileManager = async () => {
showPopupFileManagerLoading.value = true;
showPopupFileManagerMessage.value = '';
showPopupFileManagerSuccess.value = false;
try {
const valueToSave = showPopupFileManagerLocal.value ? 'true' : 'false';
await settingsStore.updateSetting('showPopupFileManager', valueToSave);
showPopupFileManagerMessage.value = t('settings.popupFileManager.success.saved');
showPopupFileManagerSuccess.value = true;
} catch (error: any) {
console.error('更新弹窗文件管理器设置失败:', error);
showPopupFileManagerMessage.value = error.message || t('settings.popupFileManager.error.saveFailed');
showPopupFileManagerSuccess.value = false;
} finally {
showPopupFileManagerLoading.value = false;
}
};
// Watchers to sync local state with store state
watch(showPopupFileEditorBoolean, (newValue) => { popupEditorEnabled.value = newValue; }, { immediate: true });
watch(shareFileEditorTabsBoolean, (newValue) => { shareTabsEnabled.value = newValue; }, { immediate: true });
@@ -272,6 +297,7 @@ export function useWorkspaceSettings() {
watch(terminalScrollbackLimitNumber, (newValue) => { terminalScrollbackLimitLocal.value = newValue; }, { immediate: true });
watch(fileManagerShowDeleteConfirmationBoolean, (newValue) => { fileManagerShowDeleteConfirmationLocal.value = newValue; }, { immediate: true });
watch(terminalEnableRightClickPasteBoolean, (newValue) => { terminalEnableRightClickPasteLocal.value = newValue; }, { immediate: true }); // NEW
watch(showPopupFileManagerBoolean, (newValue) => { showPopupFileManagerLocal.value = newValue; }, { immediate: true }); // +++ Watch for popup file manager +++
return {
@@ -334,5 +360,12 @@ export function useWorkspaceSettings() {
terminalEnableRightClickPasteMessage, // NEW
terminalEnableRightClickPasteSuccess, // NEW
handleUpdateTerminalRightClickPasteSetting, // NEW
// Popup File Manager
showPopupFileManagerLocal,
showPopupFileManagerLoading,
showPopupFileManagerMessage,
showPopupFileManagerSuccess,
handleUpdateShowPopupFileManager,
};
}
@@ -45,6 +45,7 @@ export type WorkspaceEventPayloads = {
// UI Interaction Events
'ui:openLayoutConfigurator': void;
// 'ui:toggleVirtualKeyboard': void; // 如果决定迁移 CommandInputBar 的这个事件
'fileManager:openModalRequest': { sessionId: string }; // 请求打开文件管理器模态框
// Suspended SSH Session Events
'suspendedSession:actionCompleted': void; // Emitted when a resume/remove action is completed
+12
View File
@@ -1,4 +1,5 @@
{
"appName": "Nexus Terminal",
"projectName": "Nexus Terminal",
"slogan":"Stir the stars, command the terminal.",
@@ -503,6 +504,17 @@
"deleteFailed": "Failed to delete tag \"{name}\": {error}"
},
"settings": {
"popupFileManager": {
"title": "Popup File Manager",
"enableLabel": "Enable Popup File Manager",
"description": "When enabled, the file manager button will be displayed in the command input bar, allowing you to open the popup file manager.",
"success": {
"saved": "Popup File Manager settings saved successfully."
},
"error": {
"saveFailed": "Failed to save Popup File Manager settings."
}
},
"title": "Settings",
"category": {
"security": "Security Settings",
+12
View File
@@ -1,4 +1,5 @@
{
"appName": "星枢ターミナル",
"auditLog": {
"actions": {
@@ -690,6 +691,17 @@
"title": "VNCセッション"
},
"settings": {
"popupFileManager": {
"title": "ポップアップファイルマネージャー",
"enableLabel": "ポップアップファイルマネージャーを有効にする",
"description": "有効にすると、コマンド入力バーにファイルマネージャーボタンが表示され、ポップアップファイルマネージャーを開くことができます。",
"success": {
"saved": "ポップアップファイルマネージャーの設定が保存されました。"
},
"error": {
"saveFailed": "ポップアップファイルマネージャーの設定の保存に失敗しました。"
}
},
"appearance": {
"customizeButton": "外観をカスタマイズ",
"description": "アプリケーションのビジュアルテーマと背景をカスタマイズします。",
+12
View File
@@ -1,4 +1,5 @@
{
"appName": "星枢终端",
"projectName": "星枢终端",
"slogan": "星垂平野阔,枢动万端通",
@@ -502,6 +503,17 @@
"deleteFailed": "标签 \"{name}\" 删除失败: {error}"
},
"settings": {
"popupFileManager": {
"title": "弹窗文件管理器",
"enableLabel": "启用弹窗文件管理器",
"description": "启用后,命令输入栏将显示文件管理器按钮,点击可打开弹窗式文件管理器。",
"success": {
"saved": "弹窗文件管理器设置已保存。"
},
"error": {
"saveFailed": "保存弹窗文件管理器设置失败。"
}
},
"title": "设置",
"category": {
"security": "安全设置",
+14 -3
View File
@@ -36,6 +36,7 @@ interface SettingsState {
maxLoginAttempts?: string;
loginBanDuration?: string;
showPopupFileEditor?: string; // 'true' or 'false'
showPopupFileManager?: string; // 'true' or 'false' - NEW: 弹窗文件管理器
shareFileEditorTabs?: string; // 'true' or 'false'
ipWhitelistEnabled?: string; // 添加 IP 白名单启用状态 'true' or 'false'
autoCopyOnSelect?: string; // 'true' or 'false' - 终端选中自动复制
@@ -114,6 +115,10 @@ export const useSettingsStore = defineStore('settings', () => {
if (settings.value.showPopupFileEditor === undefined) {
settings.value.showPopupFileEditor = 'true';
}
// +++ 添加 showPopupFileManager 默认值 (改为 false) +++
if (settings.value.showPopupFileManager === undefined) {
settings.value.showPopupFileManager = 'false'; // 默认禁用弹窗文件管理器
}
if (settings.value.shareFileEditorTabs === undefined) {
settings.value.shareFileEditorTabs = 'true';
}
@@ -371,7 +376,7 @@ export const useSettingsStore = defineStore('settings', () => {
// 移除外观相关的键检查
const allowedKeys: Array<keyof SettingsState> = [
'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration',
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
'showPopupFileEditor', 'showPopupFileManager', 'shareFileEditorTabs', 'ipWhitelistEnabled', // +++ 添加 showPopupFileManager +++
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
@@ -466,7 +471,7 @@ export const useSettingsStore = defineStore('settings', () => {
// 移除外观相关的键检查
const allowedKeys: Array<keyof SettingsState> = [
'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration',
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
'showPopupFileEditor', 'showPopupFileManager', 'shareFileEditorTabs', 'ipWhitelistEnabled', // +++ 添加 showPopupFileManager +++
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
@@ -663,7 +668,12 @@ export const useSettingsStore = defineStore('settings', () => {
const showPopupFileEditorBoolean = computed(() => {
return settings.value.showPopupFileEditor !== 'false';
});
// +++ Getter for popup file manager setting, returning boolean +++
const showPopupFileManagerBoolean = computed(() => {
return settings.value.showPopupFileManager !== 'false'; // Default to true
});
// Getter for sharing setting, returning boolean
const shareFileEditorTabsBoolean = computed(() => {
return settings.value.shareFileEditorTabs !== 'false';
@@ -791,6 +801,7 @@ export const useSettingsStore = defineStore('settings', () => {
error,
language,
showPopupFileEditorBoolean,
showPopupFileManagerBoolean, // +++ 暴露弹窗文件管理器 getter +++
shareFileEditorTabsBoolean,
ipWhitelistEnabled, // 暴露 IP 白名单启用状态
ipBlacklistEnabledBoolean, // <-- NEW: 暴露 IP 黑名单启用状态 getter