This commit is contained in:
Baobhan Sith
2025-04-17 09:24:51 +08:00
parent 3b4bbf134c
commit 76b740a752
6 changed files with 233 additions and 114 deletions
@@ -6,7 +6,9 @@ export const settingsService = {
* @returns 返回包含所有设置项的数组
*/
async getAllSettings(): Promise<Record<string, string>> {
console.log('[Service] Calling repository.getAllSettings...'); // 添加日志
const settingsArray = await settingsRepository.getAllSettings();
console.log('[Service] Got settings array from repository:', JSON.stringify(settingsArray)); // 添加日志
const settingsRecord: Record<string, string> = {};
settingsArray.forEach(setting => {
settingsRecord[setting.key] = setting.value;
@@ -37,7 +39,9 @@ export const settingsService = {
* @param settings 包含多个设置项键值对的对象
*/
async setMultipleSettings(settings: Record<string, string>): Promise<void> {
console.log('[Service] Calling repository.setMultipleSettings with:', JSON.stringify(settings)); // 添加日志
await settingsRepository.setMultipleSettings(settings);
console.log('[Service] Finished repository.setMultipleSettings.'); // 添加日志
},
/**
@@ -1,38 +1,41 @@
<script setup lang="ts">
import { computed, ref, onMounted, onBeforeUnmount, watch } from 'vue'; // 导入 ref, watch 等
import { computed, ref, onMounted, onBeforeUnmount, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import MonacoEditor from './MonacoEditor.vue'; // 导入 Monaco Editor 组件
import FileEditorTabs from './FileEditorTabs.vue'; // 导入标签栏组件
import { useFileEditorStore } from '../stores/fileEditor.store'; // 导入新的 Store
// 导入设置 store 以检查弹窗设置 (虽然 App.vue 做了顶层控制,但这里可以加一层保险)
import MonacoEditor from './MonacoEditor.vue';
// import FileEditorTabs from './FileEditorTabs.vue'; // 不再需要标签栏
import { useFileEditorStore } from '../stores/fileEditor.store';
import { useSettingsStore } from '../stores/settings.store';
import { useSessionStore } from '../stores/session.store'; // 导入 Session Store
import type { EditorFileContent, SaveStatus } from '../types/sftp.types'; // 导入类型
import { getLanguageFromFilename, getFilenameFromPath } from '../stores/fileEditor.store'; // 导入辅助函数
const { t } = useI18n();
const fileEditorStore = useFileEditorStore();
const settingsStore = useSettingsStore();
const sessionStore = useSessionStore(); // 导入 Session Store
// const { t } = useI18n(); // 移除重复声明
// --- 本地状态控制弹窗显示 ---
const isVisible = ref(false);
// --- 从 Store 获取状态 ---
const {
// editorVisibleState, // 不再使用
activeTab, // 当前激活的标签页对象 (computed)
activeEditorContent,// 用于 v-model 绑定 (computed)
orderedTabs, // 标签页数组 (computed)
popupTrigger, // 监听这个值的变化来显示弹窗
} = storeToRefs(fileEditorStore);
const { showPopupFileEditorBoolean } = storeToRefs(settingsStore); // 获取弹窗设置
const { popupTrigger, popupFileInfo } = storeToRefs(fileEditorStore); // 监听触发器和文件信息
const { showPopupFileEditorBoolean } = storeToRefs(settingsStore);
// --- 从 Store 获取方法 ---
const {
saveFile, // 现在保存当前激活的标签页
closeTab, // 关闭指定标签页 (由 FileEditorTabs 调用)
setActiveTab, // 设置激活标签页 (由 FileEditorTabs 调用)
// setEditorVisibility, // 不再使用
// closeAllTabs, // 不在此组件中关闭所有
} = fileEditorStore;
// --- 本地状态管理弹窗内的文件 ---
const popupFilePath = ref<string | null>(null);
const popupSessionId = ref<string | null>(null);
const popupFilename = ref<string>('');
const popupContent = ref<string>('');
const popupOriginalContent = ref<string>('');
const popupLanguage = ref<string>('plaintext');
const popupIsLoading = ref<boolean>(false);
const popupLoadingError = ref<string | null>(null);
const popupIsSaving = ref<boolean>(false);
const popupSaveStatus = ref<SaveStatus>('idle');
const popupSaveError = ref<string | null>(null);
const popupIsModified = computed(() => popupContent.value !== popupOriginalContent.value);
// --- 弹窗尺寸和拖拽状态 ---
const popupWidthPx = ref(window.innerWidth * 0.75); // 初始宽度 75vw (像素)
@@ -50,24 +53,73 @@ const popupStyle = computed(() => ({
width: `${popupWidthPx.value}px`,
height: `${popupHeightPx.value}px`,
}));
const currentTabIsLoading = computed(() => activeTab.value?.isLoading ?? false);
const currentTabLoadingError = computed(() => activeTab.value?.loadingError ?? null);
const currentTabIsSaving = computed(() => activeTab.value?.isSaving ?? false);
const currentTabSaveStatus = computed(() => activeTab.value?.saveStatus ?? 'idle');
const currentTabSaveError = computed(() => activeTab.value?.saveError ?? null);
const currentTabLanguage = computed(() => activeTab.value?.language ?? 'plaintext');
const currentTabFilePath = computed(() => activeTab.value?.filePath ?? '');
const currentTabIsModified = computed(() => activeTab.value?.isModified ?? false); // 用于显示修改状态
// 不再需要基于 activeTab 的计算属性
// --- 事件处理 ---
const handleSaveRequest = () => {
// saveFile() 默认保存当前激活的标签页
saveFile();
// 保存弹窗中的文件
const handlePopupSave = async () => {
if (!popupFilePath.value || !popupSessionId.value || popupIsSaving.value || popupIsLoading.value) {
console.warn('[FileEditorOverlay] 保存条件不满足,无法保存。');
return;
}
const session = sessionStore.sessions.get(popupSessionId.value);
if (!session || !session.wsManager.isConnected.value || !session.wsManager.isSftpReady.value) {
console.error(`[FileEditorOverlay] 保存失败:会话 ${popupSessionId.value} 无效或未连接/SFTP 未就绪。`);
popupSaveStatus.value = 'error';
popupSaveError.value = t('fileManager.errors.sessionInvalidOrNotReady');
setTimeout(() => { popupSaveStatus.value = 'idle'; popupSaveError.value = null; }, 5000);
return;
}
const sftpManager = session.sftpManager;
const contentToSave = popupContent.value;
console.log(`[FileEditorOverlay] 开始保存文件: ${popupFilePath.value}`);
popupIsSaving.value = true;
popupSaveStatus.value = 'saving';
popupSaveError.value = null;
try {
await sftpManager.writeFile(popupFilePath.value, contentToSave);
console.log(`[FileEditorOverlay] 文件 ${popupFilePath.value} 保存成功。`);
popupIsSaving.value = false;
popupSaveStatus.value = 'success';
popupSaveError.value = null;
popupOriginalContent.value = contentToSave; // 更新原始内容
// popupIsModified 会自动变为 false
setTimeout(() => { if (popupSaveStatus.value === 'success') popupSaveStatus.value = 'idle'; }, 2000);
} catch (err: any) {
console.error(`[FileEditorOverlay] 保存文件 ${popupFilePath.value} 失败:`, err);
popupIsSaving.value = false;
popupSaveStatus.value = 'error';
popupSaveError.value = `${t('fileManager.errors.saveFailed')}: ${err.message || err}`;
setTimeout(() => { if (popupSaveStatus.value === 'error') popupSaveStatus.value = 'idle'; popupSaveError.value = null; }, 5000);
}
};
// 关闭弹窗
// 关闭弹窗并重置状态
const handleCloseContainer = () => {
isVisible.value = false; // 只隐藏弹窗
if (popupIsModified.value) {
if (!confirm(`文件 ${popupFilename.value} 已修改但未保存。确定要关闭吗?`)) {
return; // 用户取消关闭
}
}
isVisible.value = false;
// 重置本地状态
popupFilePath.value = null;
popupSessionId.value = null;
popupFilename.value = '';
popupContent.value = '';
popupOriginalContent.value = '';
popupLanguage.value = 'plaintext';
popupIsLoading.value = false;
popupLoadingError.value = null;
popupIsSaving.value = false;
popupSaveStatus.value = 'idle';
popupSaveError.value = null;
};
// 最小化编辑器容器 (如果需要实现)
@@ -106,13 +158,72 @@ const stopResize = () => {
}
};
// 监听 popupTrigger 的变化来显示弹窗 (如果设置允许)
watch(popupTrigger, () => {
if (showPopupFileEditorBoolean.value) {
console.log('[FileEditorOverlay] Popup trigger changed, showing overlay.');
isVisible.value = true;
} else {
console.log('[FileEditorOverlay] Popup trigger changed, but overlay is disabled in settings.');
// 监听 popupTrigger 的变化来加载文件并显示弹窗
watch(popupTrigger, async () => {
if (!showPopupFileEditorBoolean.value || !popupFileInfo.value) {
console.log('[FileEditorOverlay] Popup trigger changed, but overlay is disabled or file info is missing.');
isVisible.value = false; // 确保在不应显示时隐藏
return;
}
const { filePath, sessionId } = popupFileInfo.value;
console.log(`[FileEditorOverlay] Triggered for file: ${filePath}, session: ${sessionId}`);
// 设置状态并显示弹窗
popupFilePath.value = filePath;
popupSessionId.value = sessionId;
popupFilename.value = getFilenameFromPath(filePath);
popupLanguage.value = getLanguageFromFilename(filePath);
popupIsLoading.value = true;
popupLoadingError.value = null;
popupContent.value = ''; // 清空旧内容
popupOriginalContent.value = '';
popupIsSaving.value = false;
popupSaveStatus.value = 'idle';
popupSaveError.value = null;
isVisible.value = true; // 显示弹窗
// 获取 SFTP 管理器并加载文件
const session = sessionStore.sessions.get(sessionId);
if (!session || !session.sftpManager) {
console.error(`[FileEditorOverlay] Cannot find SFTP manager for session ${sessionId}`);
popupLoadingError.value = t('fileManager.errors.sftpManagerNotFound');
popupIsLoading.value = false;
return;
}
const sftpManager = session.sftpManager;
try {
const fileData = await sftpManager.readFile(filePath);
console.log(`[FileEditorOverlay] File ${filePath} read successfully. Encoding: ${fileData.encoding}`);
let decodedContent = '';
if (fileData.encoding === 'base64') {
try {
const binaryString = atob(fileData.content);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); }
const decoder = new TextDecoder('utf-8');
decodedContent = decoder.decode(bytes);
} catch (decodeError) {
console.error(`[FileEditorOverlay] Base64/UTF-8 decode error for ${filePath}:`, decodeError);
popupLoadingError.value = t('fileManager.errors.fileDecodeError');
decodedContent = `// ${popupLoadingError.value}\n// Original Base64:\n${fileData.content}`;
}
} else {
decodedContent = fileData.content;
if (decodedContent.includes('\uFFFD')) {
console.warn(`[FileEditorOverlay] File ${filePath} might not be UTF-8.`);
}
}
popupContent.value = decodedContent;
popupOriginalContent.value = decodedContent;
} catch (err: any) {
console.error(`[FileEditorOverlay] Failed to read file ${filePath}:`, err);
popupLoadingError.value = `${t('fileManager.errors.readFileFailed')}: ${err.message || err}`;
popupContent.value = `// ${popupLoadingError.value}`;
} finally {
popupIsLoading.value = false;
}
});
@@ -130,61 +241,38 @@ onBeforeUnmount(() => {
<!-- 编辑器弹窗/容器应用动态样式 -->
<div class="editor-popup" :style="popupStyle">
<!-- 1. 标签栏 -->
<!-- 1. 标签栏 -->
<FileEditorTabs
:tabs="orderedTabs"
:active-tab-id="activeTab?.id ?? null"
@activate-tab="setActiveTab"
@close-tab="closeTab"
/>
<!-- 移除标签栏 -->
<!-- 2. 编辑器头部 (显示当前激活标签信息) -->
<div v-if="activeTab" class="editor-header">
<!-- 显示当前激活标签的文件路径和修改状态 -->
<!-- 编辑器头部 -->
<div class="editor-header">
<span>
{{ t('fileManager.editingFile') }}: {{ currentTabFilePath }}
<span v-if="currentTabIsModified" class="modified-indicator">*</span>
{{ t('fileManager.editingFile') }}: {{ popupFilename }}
<span v-if="popupIsModified" class="modified-indicator">*</span>
</span>
<div class="editor-actions">
<!-- 显示当前激活标签的保存状态 -->
<span v-if="currentTabSaveStatus === 'saving'" class="save-status saving">{{ t('fileManager.saving') }}...</span>
<span v-if="currentTabSaveStatus === 'success'" class="save-status success"> {{ t('fileManager.saveSuccess') }}</span>
<span v-if="currentTabSaveStatus === 'error'" class="save-status error"> {{ t('fileManager.saveError') }}: {{ currentTabSaveError }}</span>
<!-- 保存按钮状态基于当前激活标签 -->
<button @click="handleSaveRequest" :disabled="currentTabIsSaving || currentTabIsLoading || !!currentTabLoadingError || !activeTab" class="save-btn">
{{ currentTabIsSaving ? t('fileManager.saving') : t('fileManager.actions.save') }}
<span v-if="popupSaveStatus === 'saving'" class="save-status saving">{{ t('fileManager.saving') }}...</span>
<span v-if="popupSaveStatus === 'success'" class="save-status success"> {{ t('fileManager.saveSuccess') }}</span>
<span v-if="popupSaveStatus === 'error'" class="save-status error"> {{ t('fileManager.saveError') }}: {{ popupSaveError }}</span>
<button @click="handlePopupSave" :disabled="popupIsSaving || popupIsLoading || !!popupLoadingError || !popupFilePath" class="save-btn">
{{ popupIsSaving ? t('fileManager.saving') : t('fileManager.actions.save') }}
</button>
<!-- 关闭整个容器按钮 -->
<button @click="handleCloseContainer" class="close-editor-btn" :title="t('fileManager.actions.closeEditor')"></button>
<!-- 可以添加最小化按钮 -->
<!-- <button @click="handleMinimizeContainer" class="minimize-editor-btn" title="Minimize">_</button> -->
</div>
</div>
<!-- 如果没有活动标签页显示提示 -->
<div v-else class="editor-header editor-header-placeholder">
<span>{{ t('fileManager.noOpenFile') }}</span>
<button @click="handleCloseContainer" class="close-editor-btn" :title="t('fileManager.actions.closeEditor')"></button>
</div>
<!-- 3. 编辑器内容区域 -->
<!-- 编辑器内容区域 -->
<div class="editor-content-area">
<!-- 显示当前激活标签的加载状态 -->
<div v-if="currentTabIsLoading" class="editor-loading">{{ t('fileManager.loadingFile') }}</div>
<!-- 显示当前激活标签的加载错误 -->
<div v-else-if="currentTabLoadingError" class="editor-error">{{ currentTabLoadingError }}</div>
<!-- Monaco 编辑器实例 (仅当有活动标签且未加载/错误时显示) -->
<div v-if="popupIsLoading" class="editor-loading">{{ t('fileManager.loadingFile') }}</div>
<div v-else-if="popupLoadingError" class="editor-error">{{ popupLoadingError }}</div>
<MonacoEditor
v-else-if="activeTab"
:key="activeTab.id"
v-model="activeEditorContent"
:language="currentTabLanguage"
v-else-if="popupFilePath"
:key="popupFilePath"
v-model="popupContent"
:language="popupLanguage"
theme="vs-dark"
class="editor-instance"
@request-save="handleSaveRequest"
@request-save="handlePopupSave"
/>
<!-- 如果容器可见但没有活动标签页 -->
<div v-else class="editor-placeholder">{{ t('fileManager.selectFileToEdit') }}</div>
</div>
<!-- 添加拖拽手柄 -->
@@ -309,13 +309,20 @@ const handleItemClick = (event: MouseEvent, item: FileListItem) => { // item 已
const filePath = joinPath(currentPath.value, item.filename); // Use joinPath from props
const fileInfo: FileInfo = { name: item.filename, fullPath: filePath };
// 检查是否需要触发弹窗 (无论共享模式如何)
if (settingsStore.showPopupFileEditorBoolean) {
console.log(`[FileManager ${props.sessionId}] Triggering popup for: ${filePath}`);
fileEditorStore.triggerPopup(filePath, props.sessionId); // <-- 传递参数
}
// 根据共享模式决定如何打开/加载文件
if (shareFileEditorTabsBoolean.value) {
// 共享模式:调用全局 fileEditorStore
console.log(`[FileManager ${props.sessionId}] Opening file in shared mode: ${filePath}`);
// 共享模式:调用全局 fileEditorStore (它会处理标签页和加载)
console.log(`[FileManager ${props.sessionId}] Opening file in shared mode (store handles loading): ${filePath}`);
fileEditorStore.openFile(filePath, props.sessionId);
} else {
// 独立模式:调用 sessionStore
console.log(`[FileManager ${props.sessionId}] Opening file in independent mode: ${filePath}`);
// 独立模式:调用 sessionStore (它会处理标签页和加载)
console.log(`[FileManager ${props.sessionId}] Opening file in independent mode (store handles loading): ${filePath}`);
sessionStore.openFileInSession(props.sessionId, fileInfo);
}
}
@@ -32,9 +32,8 @@ export interface FileTab {
// sessionId: string; // 暂时不加,因为 session.store 已处理独立模式
}
// 辅助函数:根据文件名获取语言 (保持不变)
const getLanguageFromFilename = (filename: string): string => {
// --- 辅助函数 (移到外部并导出) ---
export const getLanguageFromFilename = (filename: string): string => {
const extension = filename.split('.').pop()?.toLowerCase();
switch (extension) {
case 'js': return 'javascript';
@@ -65,11 +64,10 @@ const getLanguageFromFilename = (filename: string): string => {
}
};
// 辅助函数:从路径获取文件名
const getFilenameFromPath = (filePath: string): string => {
export const getFilenameFromPath = (filePath: string): string => {
return filePath.split('/').pop() || filePath;
};
// --- End Helper Functions ---
export const useFileEditorStore = defineStore('fileEditor', () => {
const { t } = useI18n();
@@ -80,6 +78,7 @@ export const useFileEditorStore = defineStore('fileEditor', () => {
const activeTabId = ref<string | null>(null); // 当前激活的标签页 ID
// const editorVisibleState = ref<'visible' | 'minimized' | 'closed'>('closed'); // 移除,面板可见性由布局控制
const popupTrigger = ref(0); // 新增:用于触发弹窗显示的信号
const popupFileInfo = ref<{ filePath: string; sessionId: string } | null>(null); // 新增:存储弹窗文件信息
// --- 计算属性 ---
const orderedTabs = computed(() => Array.from(tabs.value.values())); // 获取标签页数组,用于渲染
@@ -100,6 +99,13 @@ export const useFileEditorStore = defineStore('fileEditor', () => {
// --- 核心方法 ---
// 修改:triggerPopup 接收文件信息并存储
const triggerPopup = (filePath: string, sessionId: string) => {
console.log(`[文件编辑器 Store] Triggering popup for ${filePath} in session ${sessionId}.`);
popupFileInfo.value = { filePath, sessionId };
popupTrigger.value++; // 增加触发器值以通知监听者
};
// 获取指定会话的 SFTP 管理器 (保持不变)
const getSftpManager = (sessionId: string | null) => {
if (!sessionId) return null;
@@ -151,8 +157,8 @@ export const useFileEditorStore = defineStore('fileEditor', () => {
};
tabs.value.set(tabId, newTab);
setActiveTab(tabId); // 激活新标签页
// 触发弹窗 (如果设置允许)
popupTrigger.value++;
// 不再在这里触发弹窗
// popupTrigger.value++;
// 获取 SFTP 管理器
const sftpManager = getSftpManager(sessionId);
@@ -419,6 +425,7 @@ export const useFileEditorStore = defineStore('fileEditor', () => {
activeTabId: readonly(activeTabId),
// editorVisibleState: readonly(editorVisibleState), // 移除
popupTrigger: readonly(popupTrigger), // 暴露触发器 (只读)
popupFileInfo: readonly(popupFileInfo), // 暴露弹窗文件信息 (只读)
// 计算属性
orderedTabs,
@@ -432,6 +439,7 @@ export const useFileEditorStore = defineStore('fileEditor', () => {
closeAllTabs,
setActiveTab,
updateFileContent, // 暴露新的更新方法
triggerPopup, // 暴露新的触发方法
// setEditorVisibility, // 移除
};
});
+15 -11
View File
@@ -29,6 +29,7 @@ export const useSettingsStore = defineStore('settings', () => {
* Should be called early in the application lifecycle (e.g., main.ts).
*/
async function loadInitialSettings() {
console.log('[SettingsStore] Entering loadInitialSettings function...'); // <-- 添加更早的日志
isLoading.value = true;
error.value = null;
let fetchedLang: 'en' | 'zh' | undefined;
@@ -37,12 +38,13 @@ export const useSettingsStore = defineStore('settings', () => {
console.log('[SettingsStore] Starting loadInitialSettings...'); // 添加日志
const response = await axios.get<Record<string, string>>('/api/v1/settings');
settings.value = response.data; // Store all fetched settings
console.log('[SettingsStore] Fetched settings:', JSON.stringify(settings.value)); // 添加日志
console.log('[SettingsStore] Fetched settings raw:', JSON.stringify(response.data)); // 打印原始响应
console.log('[SettingsStore] Raw showPopupFileEditor from backend:', response.data.showPopupFileEditor); // <--- 添加日志:打印原始值
// --- 设置默认值 (如果后端未返回) ---
// 弹窗编辑器设置
if (settings.value.showPopupFileEditor === undefined) {
console.log('[SettingsStore] Setting default for showPopupFileEditor: true');
console.log('[SettingsStore] showPopupFileEditor is undefined, setting default: true'); // 修改日志
settings.value.showPopupFileEditor = 'true'; // 默认为 true
}
// 共享编辑器标签页设置
@@ -92,18 +94,20 @@ export const useSettingsStore = defineStore('settings', () => {
* @param value The new value for the setting.
*/
async function updateSetting(key: keyof SettingsState, value: string) {
const previousValue = settings.value[key];
settings.value = { ...settings.value, [key]: value }; // Optimistic update
// const previousValue = settings.value[key]; // No longer needed for optimistic revert
// settings.value = { ...settings.value, [key]: value }; // Remove optimistic update
try {
await axios.put('/api/v1/settings', { [key]: value });
// Update store state *after* successful API call
settings.value = { ...settings.value, [key]: value };
// If updating language, also update i18n
if (key === 'language' && (value === 'en' || value === 'zh')) {
setLocale(value);
}
} catch (err: any) {
console.error(`Failed to update setting '${key}':`, err);
settings.value = { ...settings.value, [key]: previousValue }; // Revert on error
// settings.value = { ...settings.value, [key]: previousValue }; // Remove revert logic
throw new Error(err.response?.data?.message || err.message || `Failed to update setting '${key}'`);
}
}
@@ -113,18 +117,20 @@ export const useSettingsStore = defineStore('settings', () => {
* @param updates An object containing key-value pairs of settings to update.
*/
async function updateMultipleSettings(updates: Partial<SettingsState>) {
const previousSettings = { ...settings.value };
settings.value = { ...settings.value, ...updates }; // Optimistic update
// const previousSettings = { ...settings.value }; // No longer needed for optimistic revert
// settings.value = { ...settings.value, ...updates }; // Remove optimistic update
try {
await axios.put('/api/v1/settings', updates);
// Update store state *after* successful API call
settings.value = { ...settings.value, ...updates };
// If language is updated, apply it
if (updates.language && (updates.language === 'en' || updates.language === 'zh')) {
setLocale(updates.language);
}
} catch (err: any) {
console.error('Failed to update multiple settings:', err);
settings.value = previousSettings; // Revert on error
// settings.value = previousSettings; // Remove revert logic
throw new Error(err.response?.data?.message || err.message || 'Failed to update settings');
}
}
@@ -137,9 +143,7 @@ export const useSettingsStore = defineStore('settings', () => {
// Getter for the popup editor setting, returning boolean
const showPopupFileEditorBoolean = computed(() => {
// 默认为 true,除非明确设置为 'false'
// 默认为 true (共享),除非明确设置为 'false'
// 默认为 true (共享),除非明确设置为 'false'
return settings.value.shareFileEditorTabs !== 'false';
return settings.value.showPopupFileEditor !== 'false'; // <-- 修正:检查正确的键 showPopupFileEditor
});
// Getter for sharing setting, returning boolean
+17 -9
View File
@@ -253,15 +253,23 @@ const shareTabsMessage = ref('');
const shareTabsSuccess = ref(false);
// --- Watcher to sync local form state with store state ---
watch(settings, (newSettings) => {
watch(settings, (newSettings, oldSettings) => {
// Initialize only if settings were previously null or undefined
const isInitialLoad = !oldSettings;
ipWhitelistInput.value = newSettings.ipWhitelist || '';
selectedLanguage.value = newSettings.language || 'en';
blacklistSettingsForm.maxLoginAttempts = newSettings.maxLoginAttempts || '5';
blacklistSettingsForm.loginBanDuration = newSettings.loginBanDuration || '300';
//
popupEditorEnabled.value = showPopupFileEditorBoolean.value;
//
shareTabsEnabled.value = shareFileEditorTabsBoolean.value;
// Initialize local state only on initial load or if store changes externally
if (isInitialLoad || newSettings.showPopupFileEditor !== oldSettings?.showPopupFileEditor) {
popupEditorEnabled.value = showPopupFileEditorBoolean.value;
}
if (isInitialLoad || newSettings.shareFileEditorTabs !== oldSettings?.shareFileEditorTabs) {
shareTabsEnabled.value = shareFileEditorTabsBoolean.value;
}
}, { deep: true, immediate: true }); // immediate: true to run on initial load
// --- Popup Editor setting method ---
@@ -279,8 +287,8 @@ const handleUpdatePopupEditorSetting = async () => {
console.error('更新弹窗编辑器设置失败:', error);
popupEditorMessage.value = error.message || t('settings.popupEditor.error.saveFailed'); //
popupEditorSuccess.value = false;
// Store
popupEditorEnabled.value = showPopupFileEditorBoolean.value;
// v-model
// popupEditorEnabled.value = showPopupFileEditorBoolean.value; // <--
} finally {
popupEditorLoading.value = false;
}
@@ -300,8 +308,8 @@ const handleUpdateShareTabsSetting = async () => {
console.error('更新共享编辑器标签页设置失败:', error);
shareTabsMessage.value = error.message || t('settings.shareEditorTabs.error.saveFailed'); //
shareTabsSuccess.value = false;
// Store
shareTabsEnabled.value = shareFileEditorTabsBoolean.value;
//
// shareTabsEnabled.value = shareFileEditorTabsBoolean.value; // <--
} finally {
shareTabsLoading.value = false;
}