diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3b66410 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/packages/frontend/src/components/StyleCustomizer.vue b/packages/frontend/src/components/StyleCustomizer.vue index 4b34e8a..f9cb47f 100644 --- a/packages/frontend/src/components/StyleCustomizer.vue +++ b/packages/frontend/src/components/StyleCustomizer.vue @@ -35,8 +35,9 @@ const editableUiThemeString = ref(''); // 用于 textarea 绑定 const themeParseError = ref(null); // 用于显示 JSON 解析错误 // 终端主题管理相关状态 -const selectedTerminalThemeId = ref(null); // 下拉框选择的 ID const isEditingTheme = ref(false); // 是否正在编辑某个主题 +const themeSearchTerm = ref(''); // 主题搜索词 +const themeSortOrder = ref<'nameAsc' | 'nameDesc'>('nameAsc'); // 主题排序方式 // 使用 reactive 确保嵌套对象 themeData 的响应性 // 修正:editingTheme 应该是一个 ref 包含 TerminalTheme 或 null const editingTheme = ref(null); // 正在编辑的主题数据副本 (完整结构) @@ -71,7 +72,6 @@ const initializeEditableState = () => { editableTerminalFontFamily.value = currentTerminalFontFamily.value; editableTerminalFontSize.value = currentTerminalFontSize.value; editableEditorFontSize.value = currentEditorFontSize.value; // <-- 新增 - selectedTerminalThemeId.value = activeTerminalThemeId.value ?? null; // 初始化下拉框 uploadError.value = null; importError.value = null; saveThemeError.value = null; @@ -342,14 +342,20 @@ const handleSaveEditorFontSize = async () => { } }; -// 更改激活的终端主题 -const handleTerminalThemeChange = async () => { +// 应用选定的终端主题 +const handleApplyTheme = async (theme: TerminalTheme) => { + // 确保 theme._id 存在且不等于当前激活的 ID + // setActiveTerminalTheme 期望 string | null,而 theme._id 是 string | undefined + // 如果 theme._id 是 undefined (理论上不应发生在列表项上),传递 null + const themeIdToApply = theme._id ?? null; + if (themeIdToApply === null || themeIdToApply === activeTerminalThemeId.value) return; + try { - await appearanceStore.setActiveTerminalTheme(selectedTerminalThemeId.value); + await appearanceStore.setActiveTerminalTheme(themeIdToApply); + // 成功后 activeTerminalThemeId 会自动更新 + alert(t('styleCustomizer.themeAppliedSuccess', { name: theme.name })); // 需要添加翻译 } catch (error: any) { - console.error("设置激活终端主题失败:", error); - // 恢复下拉框选择到之前的状态 - selectedTerminalThemeId.value = activeTerminalThemeId.value ?? null; + console.error("应用终端主题失败:", error); alert(t('styleCustomizer.setActiveThemeFailed', { message: error.message })); } }; @@ -468,15 +474,18 @@ const handleImportThemeFile = async (event: Event) => { } }; -// 处理主题导出 -const handleExportTheme = async () => { - if (selectedTerminalThemeId.value) { +// 处理主题导出 (导出当前激活的主题) +const handleExportActiveTheme = async () => { + const currentId = activeTerminalThemeId.value; // activeTerminalThemeId 是 Ref + if (currentId) { // 检查 currentId 是否为真值 (不是 undefined 或空字符串) try { - await appearanceStore.exportTerminalTheme(selectedTerminalThemeId.value); + await appearanceStore.exportTerminalTheme(currentId); } catch (error: any) { console.error("导出主题失败:", error); alert(t('styleCustomizer.exportFailed', { message: error.message })); } + } else { + alert(t('styleCustomizer.noActiveThemeToExport')); // 需要添加翻译 } }; @@ -563,6 +572,46 @@ const formatXtermLabel = (key: keyof ITheme): string => { // 简单的转换逻辑 return key.replace(/([A-Z])/g, ' $1').replace(/^./, (str) => str.toUpperCase()); }; + +// --- 新增:计算属性 --- + +// 获取当前激活主题的名称 +const activeThemeName = computed(() => { + const theme = availableTerminalThemes.value.find(t => t._id === activeTerminalThemeId.value); + // 如果找不到主题 (例如 ID 无效或列表为空),则显示提示 + return theme ? theme.name : t('styleCustomizer.noActiveThemeSelected', '无激活主题'); // 需要添加翻译 +}); + +// 过滤和排序主题列表 +const filteredAndSortedThemes = computed(() => { + const searchTerm = themeSearchTerm.value.toLowerCase().trim(); // 转小写并去除首尾空格 + let themes = [...availableTerminalThemes.value]; // 创建副本以进行排序 + + // 过滤 + if (searchTerm) { + themes = themes.filter(theme => theme.name.toLowerCase().includes(searchTerm)); + } + + // 排序 + themes.sort((a, b) => { + const nameA = a.name.toLowerCase(); + const nameB = b.name.toLowerCase(); + if (themeSortOrder.value === 'nameAsc') { + return nameA.localeCompare(nameB); + } else { // nameDesc + return nameB.localeCompare(nameA); + } + // 未来可扩展日期排序,需要后端提供 createdAt/updatedAt + }); + + return themes; +}); + +// 切换排序顺序 +const handleSortThemes = () => { + themeSortOrder.value = themeSortOrder.value === 'nameAsc' ? 'nameDesc' : 'nameAsc'; +}; +