feat: 为快捷指令添加紧凑模式功能

This commit is contained in:
Baobhan Sith
2025-05-20 14:56:00 +08:00
parent d8cec9f21d
commit a267cbd3ee
2 changed files with 121 additions and 26 deletions
+53 -2
View File
@@ -64,6 +64,7 @@ interface SettingsState {
terminalEnableRightClickPaste?: string; // 'true' or 'false' - 终端右键粘贴
showStatusMonitorIpAddress?: string; // 'true' or 'false' - 状态监视器显示IP地址
quickCommandRowSizeMultiplier?: string; // +++ 快捷命令列表行大小乘数 (e.g., '1.0') +++
quickCommandsCompactMode?: string; // +++ 快捷指令视图紧凑模式 (e.g., 'false') +++
[key: string]: string | undefined;
}
@@ -315,7 +316,31 @@ export const useSettingsStore = defineStore('settings', () => {
settings.value.quickCommandRowSizeMultiplier = '1.0';
console.log(`[SettingsStore] quickCommandRowSizeMultiplier not found, set to default: ${settings.value.quickCommandRowSizeMultiplier}`);
}
// +++ 快捷指令视图紧凑模式默认值 +++
if (settings.value.quickCommandsCompactMode === undefined) {
settings.value.quickCommandsCompactMode = 'false';
console.log(`[SettingsStore] quickCommandsCompactMode not found, set to default: ${settings.value.quickCommandsCompactMode}`);
}
// --- 从 localStorage 加载 QuickCommands 特有设置 ---
const localQcRowSizeMultiplier = localStorage.getItem('nexus_quickCommandRowSizeMultiplier');
if (localQcRowSizeMultiplier) {
const parsedLocalMultiplier = parseFloat(localQcRowSizeMultiplier);
if (!isNaN(parsedLocalMultiplier) && parsedLocalMultiplier > 0) {
settings.value.quickCommandRowSizeMultiplier = localQcRowSizeMultiplier;
console.log(`[SettingsStore] Loaded quickCommandRowSizeMultiplier from localStorage: ${localQcRowSizeMultiplier}`);
} else {
console.warn(`[SettingsStore] Invalid quickCommandRowSizeMultiplier in localStorage: ${localQcRowSizeMultiplier}. Using server/default.`);
}
}
const localQcCompactMode = localStorage.getItem('nexus_quickCommandsCompactMode');
if (localQcCompactMode === 'true' || localQcCompactMode === 'false') {
settings.value.quickCommandsCompactMode = localQcCompactMode;
console.log(`[SettingsStore] Loaded quickCommandsCompactMode from localStorage: ${localQcCompactMode}`);
} else if (localQcCompactMode !== null) {
console.warn(`[SettingsStore] Invalid quickCommandsCompactMode in localStorage: ${localQcCompactMode}. Using server/default.`);
}
// --- 语言设置 ---
const langFromSettings = settings.value.language;
@@ -409,7 +434,8 @@ export const useSettingsStore = defineStore('settings', () => {
'fileManagerShowDeleteConfirmation',
'terminalEnableRightClickPaste',
'showStatusMonitorIpAddress',
'quickCommandRowSizeMultiplier'
'quickCommandRowSizeMultiplier',
'quickCommandsCompactMode'
];
if (!allowedKeys.includes(key)) {
console.error(`[SettingsStore] 尝试更新不允许的设置键: ${key}`);
@@ -451,6 +477,17 @@ export const useSettingsStore = defineStore('settings', () => {
// Update store state *after* successful API call
settings.value = { ...settings.value, [key]: String(value) }; // Store as string internally
// --- 保存到 localStorage ---
if (key === 'quickCommandsCompactMode' && (String(value) === 'true' || String(value) === 'false')) {
try {
localStorage.setItem('nexus_quickCommandsCompactMode', String(value));
console.log(`[SettingsStore] Saved quickCommandsCompactMode to localStorage: ${String(value)}`);
} catch (e) {
console.error('[SettingsStore] Failed to save quickCommandsCompactMode to localStorage:', e);
}
}
// quickCommandRowSizeMultiplier 由其专用 action 处理 localStorage 保存
// If updating language, check if it's valid and update i18n
if (key === 'language' && typeof value === 'string' && availableLocales.includes(value)) {
console.log(`[SettingsStore] updateSetting: Language updated to ${value}. Calling setLocale...`);
@@ -506,7 +543,8 @@ export const useSettingsStore = defineStore('settings', () => {
'fileManagerShowDeleteConfirmation',
'terminalEnableRightClickPaste',
'showStatusMonitorIpAddress',
'quickCommandRowSizeMultiplier'
'quickCommandRowSizeMultiplier',
'quickCommandsCompactMode'
];
const filteredUpdates: Partial<SettingsState> = {};
let languageUpdate: string | undefined = undefined;
@@ -604,6 +642,13 @@ export const useSettingsStore = defineStore('settings', () => {
try {
await updateSetting('quickCommandRowSizeMultiplier', multiplierString);
// 本地状态 settings.value 会在 updateSetting 成功后更新
// --- 保存到 localStorage ---
try {
localStorage.setItem('nexus_quickCommandRowSizeMultiplier', multiplierString);
console.log(`[SettingsStore] Saved quickCommandRowSizeMultiplier to localStorage: ${multiplierString}`);
} catch (e) {
console.error('[SettingsStore] Failed to save quickCommandRowSizeMultiplier to localStorage:', e);
}
console.log(`[SettingsStore] Quick Command row size multiplier updated to: ${multiplierString}`);
} catch (error) {
console.error('[SettingsStore] Failed to save Quick Command row size multiplier:', error);
@@ -838,6 +883,11 @@ export const useSettingsStore = defineStore('settings', () => {
const val = parseFloat(valStr);
return isNaN(val) || val <= 0 ? 1.0 : val; // Fallback to 1.0 if invalid
});
// +++ Getter for Quick Command compact mode, returning boolean +++
const quickCommandsCompactModeBoolean = computed(() => {
return settings.value.quickCommandsCompactMode === 'true';
});
return {
settings, // 只包含通用设置
@@ -874,6 +924,7 @@ export const useSettingsStore = defineStore('settings', () => {
commandInputSyncTarget, // +++ 暴露命令输入同步目标 getter +++
timezone,
quickCommandRowSizeMultiplierNumber, // +++ 暴露快捷命令大小 getter +++
quickCommandsCompactModeBoolean, // +++ 暴露快捷指令紧凑模式 getter +++
dashboardSortBy,
dashboardSortOrder,
saveDashboardSortPreference,
@@ -16,11 +16,17 @@
class="flex-grow min-w-0 px-4 py-1.5 border border-border/50 rounded-lg bg-input text-foreground text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition duration-150 ease-in-out"
/>
<!-- Sort Button -->
<button @click="toggleSortBy" class="w-8 h-8 border border-border/50 rounded-lg text-text-secondary hover:bg-border hover:text-foreground transition-colors duration-150 flex-shrink-0 flex items-center justify-center" :title="sortButtonTitle"> <!-- Use w-8 h-8 -->
<button @click="toggleSortBy" class="w-8 h-8 border border-border/50 rounded-lg text-text-secondary hover:bg-border hover:text-foreground transition-colors duration-150 flex-shrink-0 flex items-center justify-center" :title="sortButtonTitle">
<i :class="[sortButtonIcon, 'text-base']"></i>
</button>
<!-- Compact Mode Toggle Button -->
<button @click="toggleCompactMode"
class="w-8 h-8 border border-border/50 rounded-lg text-text-secondary hover:bg-border hover:text-foreground transition-colors duration-150 flex-shrink-0 flex items-center justify-center"
:class="{ 'bg-primary/20 text-primary': isCompactMode }">
<i :class="['fas', isCompactMode ? 'fa-compress-alt' : 'fa-expand-alt', 'text-base']"></i>
</button>
<!-- Add Button -->
<button @click="openAddForm" class="w-8 h-8 bg-primary text-white border-none rounded-lg text-sm font-semibold cursor-pointer shadow-md transition-colors duration-200 ease-in-out hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary flex-shrink-0 flex items-center justify-center" :title="$t('quickCommands.add', '添加快捷指令')"> <!-- Use w-8 h-8 -->
<button @click="openAddForm" class="w-8 h-8 bg-primary text-white border-none rounded-lg text-sm font-semibold cursor-pointer shadow-md transition-colors duration-200 ease-in-out hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary flex-shrink-0 flex items-center justify-center" :title="$t('quickCommands.add', '添加快捷指令')">
<i class="fas fa-plus text-base"></i>
</button>
</div>
@@ -61,7 +67,7 @@
<!-- Group Header - Modified for inline editing -->
<div
class="group font-semibold flex items-center text-foreground rounded-md hover:bg-header/80 transition-colors duration-150"
:style="{ padding: `calc(0.5rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` }"
:style="{ padding: isCompactMode ? `calc(0.25rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` : `calc(0.5rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` }"
:class="{ 'cursor-pointer': editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) }"
@click="editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) ? toggleGroup(groupData.groupName) : null"
>
@@ -106,26 +112,39 @@
:key="cmd.id"
:data-command-id="cmd.id"
class="group flex justify-between items-center mb-1 cursor-pointer rounded-md hover:bg-primary/10 transition-colors duration-150"
:style="{ padding: `calc(0.625rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` }"
:style="{ padding: isCompactMode ? `calc(0.1rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` : `calc(0.625rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` }"
:class="{ 'bg-primary/20 font-medium': isCommandSelected(cmd.id) }"
@click="executeCommand(cmd)"
@contextmenu.prevent="showQuickCommandContextMenu($event, cmd)"
>
<!-- Command Info -->
<div class="flex flex-col overflow-hidden mr-2 flex-grow">
<span v-if="cmd.name" class="font-medium truncate mb-0.5 text-foreground" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.name }}</span>
<span class="truncate font-mono" :class="{ 'text-sm': !cmd.name, 'text-text-secondary': true }" :style="{ fontSize: `calc(0.75em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.command }}</span>
<span v-if="cmd.name"
class="font-medium truncate text-foreground"
:class="{'mb-0.5': !isCompactMode, 'leading-tight': isCompactMode}"
:style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.name }}</span>
<span v-if="!isCompactMode && cmd.command"
class="truncate font-mono"
:class="{ 'text-sm': !cmd.name, 'text-text-secondary': true }"
:style="{ fontSize: `calc(0.75em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.command }}</span>
<span v-else-if="isCompactMode && !cmd.name && cmd.command"
class="truncate font-mono text-xs text-text-secondary/70 leading-tight"
:style="{ fontSize: `calc(0.65em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` }">{{ cmd.command }}</span>
</div>
<!-- Actions -->
<div class="flex items-center flex-shrink-0 focus-within:opacity-100 transition-opacity duration-150">
<button @click.stop="copyCommand(cmd.command)" class="p-1.5 rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('commandHistory.copy', '复制')">
<i class="fas fa-copy" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
<div class="flex items-center flex-shrink-0 transition-opacity duration-150"
:class="{
'opacity-0 group-hover:opacity-100 focus-within:opacity-100': isCompactMode,
'opacity-100': !isCompactMode
}">
<button @click.stop="copyCommand(cmd.command)" :class="isCompactMode ? 'p-1' : 'p-1.5'" class="rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('commandHistory.copy', '复制')">
<i class="fas fa-copy" :style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
</button>
<button @click.stop="openEditForm(cmd)" class="p-1.5 rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('common.edit', '编辑')">
<i class="fas fa-edit" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
<button @click.stop="openEditForm(cmd)" :class="isCompactMode ? 'p-1' : 'p-1.5'" class="rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('common.edit', '编辑')">
<i class="fas fa-edit" :style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
</button>
<button @click.stop="confirmDelete(cmd)" class="p-1.5 rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-error" :title="$t('common.delete', '删除')">
<i class="fas fa-times" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
<button @click.stop="confirmDelete(cmd)" :class="isCompactMode ? 'p-1' : 'p-1.5'" class="rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-error" :title="$t('common.delete', '删除')">
<i class="fas fa-times" :style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
</button>
</div>
</li>
@@ -139,26 +158,39 @@
:key="cmd.id"
:data-command-id="cmd.id"
class="group flex justify-between items-center mb-1 cursor-pointer rounded-md hover:bg-primary/10 transition-colors duration-150"
:style="{ padding: `calc(0.625rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` }"
:style="{ padding: isCompactMode ? `calc(0.1rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` : `calc(0.625rem * var(--qc-row-size-multiplier)) calc(0.75rem * var(--qc-row-size-multiplier))` }"
:class="{ 'bg-primary/20 font-medium': isCommandSelected(cmd.id) }"
@click="executeCommand(cmd)"
@contextmenu.prevent="showQuickCommandContextMenu($event, cmd)"
>
<!-- Command Info -->
<div class="flex flex-col overflow-hidden mr-2 flex-grow">
<span v-if="cmd.name" class="font-medium truncate mb-0.5 text-foreground" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.name }}</span>
<span class="truncate font-mono" :class="{ 'text-sm': !cmd.name, 'text-text-secondary': true }" :style="{ fontSize: `calc(0.75em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.command }}</span>
<span v-if="cmd.name"
class="font-medium truncate text-foreground"
:class="{'mb-0.5': !isCompactMode, 'leading-tight': isCompactMode}"
:style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.name }}</span>
<span v-if="!isCompactMode && cmd.command"
class="truncate font-mono"
:class="{ 'text-sm': !cmd.name, 'text-text-secondary': true }"
:style="{ fontSize: `calc(0.75em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }">{{ cmd.command }}</span>
<span v-else-if="isCompactMode && !cmd.name && cmd.command"
class="truncate font-mono text-xs text-text-secondary/70 leading-tight"
:style="{ fontSize: `calc(0.65em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` }">{{ cmd.command }}</span>
</div>
<!-- Actions -->
<div class="flex items-center flex-shrink-0 focus-within:opacity-100 transition-opacity duration-150">
<button @click.stop="copyCommand(cmd.command)" class="p-1.5 rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('commandHistory.copy', '复制')">
<i class="fas fa-copy" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
<div class="flex items-center flex-shrink-0 transition-opacity duration-150"
:class="{
'opacity-0 group-hover:opacity-100 focus-within:opacity-100': isCompactMode,
'opacity-100': !isCompactMode
}">
<button @click.stop="copyCommand(cmd.command)" :class="isCompactMode ? 'p-1' : 'p-1.5'" class="rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('commandHistory.copy', '复制')">
<i class="fas fa-copy" :style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
</button>
<button @click.stop="openEditForm(cmd)" class="p-1.5 rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('common.edit', '编辑')">
<i class="fas fa-edit" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
<button @click.stop="openEditForm(cmd)" :class="isCompactMode ? 'p-1' : 'p-1.5'" class="rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-primary" :title="$t('common.edit', '编辑')">
<i class="fas fa-edit" :style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
</button>
<button @click.stop="confirmDelete(cmd)" class="p-1.5 rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-error" :title="$t('common.delete', '删除')">
<i class="fas fa-times" :style="{ fontSize: `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
<button @click.stop="confirmDelete(cmd)" :class="isCompactMode ? 'p-1' : 'p-1.5'" class="rounded hover:bg-black/10 transition-colors duration-150 text-text-secondary hover:text-error" :title="$t('common.delete', '删除')">
<i class="fas fa-times" :style="{ fontSize: isCompactMode ? `calc(0.8em * max(0.8, var(--qc-row-size-multiplier) * 0.5 + 0.5))` : `calc(0.875em * max(0.85, var(--qc-row-size-multiplier) * 0.6 + 0.4))` }"></i>
</button>
</div>
</li>
@@ -245,7 +277,11 @@ const isLoading = computed(() => quickCommandsStore.isLoading);
const { selectedIndex: storeSelectedIndex, flatVisibleCommands, expandedGroups } = storeToRefs(quickCommandsStore);
const { showQuickCommandTagsBoolean, quickCommandRowSizeMultiplierNumber: qcRowSizeMultiplierFromStore } = storeToRefs(settingsStore);
const {
showQuickCommandTagsBoolean,
quickCommandRowSizeMultiplierNumber: qcRowSizeMultiplierFromStore,
quickCommandsCompactModeBoolean, // +++ 引入紧凑模式状态 +++
} = storeToRefs(settingsStore);
const quickCommandRowSizeMultiplier = ref(1.0);
@@ -285,6 +321,14 @@ const flatFilteredCommands = computed(() => {
return quickCommandsStore.flatVisibleCommands;
});
// --- Compact Mode ---
const isCompactMode = computed(() => quickCommandsCompactModeBoolean.value);
const toggleCompactMode = () => {
const currentMode = quickCommandsCompactModeBoolean.value;
settingsStore.updateSetting('quickCommandsCompactMode', String(!currentMode));
};
// --- Helper function for selection check ---
const isCommandSelected = (commandId: number): boolean => {
// 使用 store 的 flatVisibleCommands 和 storeSelectedIndex