feat(settings): 添加安装设置模态框及配置管理功能

实现安装设置功能,包括更新检测通知和自动创建桌面启动器的开关配置
重构原有的安装设置逻辑,使用模态框替代直接调用脚本
新增 IPC 接口用于获取和保存设置配置
This commit is contained in:
2026-04-16 00:35:37 +08:00
parent a2d4192592
commit 120233cf56
3 changed files with 315 additions and 14 deletions
+85 -5
View File
@@ -214,21 +214,101 @@ ipcMain.on("set-theme-source", (event, theme: "system" | "light" | "dark") => {
nativeTheme.themeSource = theme;
});
// 启动安装设置脚本(可能需要提升权限)
ipcMain.handle("open-install-settings", async () => {
// 配置文件路径
const SPARK_CONFIG_DIR = path.join(
os.homedir(),
".config/spark-union/spark-store",
);
const UPDATE_CHECK_CONFIG = "ssshell-config-do-not-show-upgrade-notify";
const CREATE_DESKTOP_CONFIG = "ssshell-config-do-not-create-desktop";
// 获取安装设置
ipcMain.handle("get-install-settings", async () => {
try {
const result: Record<string, boolean> = {};
// 检查更新检测配置
result[UPDATE_CHECK_CONFIG] = fs.existsSync(
path.join(SPARK_CONFIG_DIR, UPDATE_CHECK_CONFIG),
);
// 检查自动创建桌面启动器配置
result[CREATE_DESKTOP_CONFIG] = fs.existsSync(
path.join(SPARK_CONFIG_DIR, CREATE_DESKTOP_CONFIG),
);
return { success: true, data: result };
} catch (err) {
logger.error({ err }, "Failed to get install settings");
return { success: false, message: (err as Error)?.message || String(err) };
}
});
// 设置安装设置
ipcMain.handle(
"set-install-settings",
async (
_event,
settings: {
[UPDATE_CHECK_CONFIG]?: boolean;
[CREATE_DESKTOP_CONFIG]?: boolean;
},
) => {
try {
// 确保配置目录存在
if (!fs.existsSync(SPARK_CONFIG_DIR)) {
fs.mkdirSync(SPARK_CONFIG_DIR, { recursive: true });
}
// 更新检测配置
const updateCheckPath = path.join(SPARK_CONFIG_DIR, UPDATE_CHECK_CONFIG);
if (settings[UPDATE_CHECK_CONFIG]) {
fs.writeFileSync(updateCheckPath, "");
} else {
if (fs.existsSync(updateCheckPath)) {
fs.unlinkSync(updateCheckPath);
}
}
// 自动创建桌面启动器配置
const createDesktopPath = path.join(
SPARK_CONFIG_DIR,
CREATE_DESKTOP_CONFIG,
);
if (settings[CREATE_DESKTOP_CONFIG]) {
fs.writeFileSync(createDesktopPath, "");
} else {
if (fs.existsSync(createDesktopPath)) {
fs.unlinkSync(createDesktopPath);
}
}
return { success: true };
} catch (err) {
logger.error({ err }, "Failed to set install settings");
return {
success: false,
message: (err as Error)?.message || String(err),
};
}
},
);
// 检查更新
ipcMain.handle("check-for-updates", async () => {
try {
const { spawn } = await import("node:child_process");
const scriptPath =
"/opt/durapps/spark-store/bin/update-upgrade/ss-update-controler.sh";
"/opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade.sh";
const child = spawn("systemd-run", ["--user", scriptPath], {
detached: true,
stdio: "ignore",
});
child.unref();
logger.info(`Launched ${scriptPath}`);
logger.info(`Launched update check script: ${scriptPath}`);
return { success: true };
} catch (err) {
logger.error({ err }, "Failed to launch install settings script");
logger.error({ err }, "Failed to launch update check script");
return { success: false, message: (err as Error)?.message || String(err) };
}
});
+10 -9
View File
@@ -151,6 +151,8 @@
/>
<AboutModal :show="showAboutModal" @close="closeAboutModal" />
<SettingsModal :show="showSettingsModal" @close="closeSettingsModal" />
</div>
</template>
@@ -170,6 +172,7 @@ import InstalledAppsModal from "./components/InstalledAppsModal.vue";
import UpdateCenterModal from "./components/UpdateCenterModal.vue";
import UninstallConfirmModal from "./components/UninstallConfirmModal.vue";
import AboutModal from "./components/AboutModal.vue";
import SettingsModal from "./components/SettingsModal.vue";
import {
APM_STORE_BASE_URL,
currentApp,
@@ -260,6 +263,7 @@ const updateCenterStore = createUpdateCenterStore();
const showUninstallModal = ref(false);
const uninstallTargetApp: Ref<App | null> = ref(null);
const showAboutModal = ref(false);
const showSettingsModal = ref(false);
const apmAvailable = ref(false);
/** 启动参数 --no-apm => 仅 Spark--no-spark => 仅 APM;由主进程 IPC 提供 */
@@ -747,15 +751,8 @@ const handleUpdate = async () => {
await openUpdateModal();
};
const handleOpenInstallSettings = async () => {
try {
const result = await window.ipcRenderer.invoke("open-install-settings");
if (!result || !result.success) {
logger.warn(`启动安装设置失败: ${result?.message || "未知错误"}`);
}
} catch (error) {
logger.error(`调用安装设置时出错: ${error}`);
}
const handleOpenInstallSettings = () => {
showSettingsModal.value = true;
};
const handleList = () => {
@@ -937,6 +934,10 @@ const closeAboutModal = () => {
showAboutModal.value = false;
};
const closeSettingsModal = () => {
showSettingsModal.value = false;
};
// TODO: 目前 APM 商店不能暂停下载
const pauseDownload = (id: DownloadItem) => {
const download = downloads.value.find((d) => d.id === id.id);
+220
View File
@@ -0,0 +1,220 @@
<template>
<Transition
enter-active-class="duration-200 ease-out"
enter-from-class="opacity-0 scale-95"
enter-to-class="opacity-100 scale-100"
leave-active-class="duration-150 ease-in"
leave-from-class="opacity-100 scale-100"
leave-to-class="opacity-0 scale-95"
>
<div
v-if="show"
class="fixed inset-0 z-[70] flex items-center justify-center overflow-hidden bg-slate-900/70 p-4"
@click.self="closeModal"
>
<div
class="relative w-full max-w-md overflow-hidden rounded-3xl border border-white/10 bg-white/95 shadow-2xl dark:border-slate-800 dark:bg-slate-900"
>
<!-- 标题栏 -->
<div
class="flex items-center justify-between border-b border-slate-200/60 px-6 py-4 dark:border-slate-800/60"
>
<h2
class="text-lg font-bold text-slate-900 dark:text-white flex items-center gap-2"
>
<i class="fas fa-cog text-brand"></i>
安装设置
</h2>
<button
type="button"
class="inline-flex h-8 w-8 items-center justify-center rounded-full text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 dark:hover:bg-slate-800 dark:hover:text-slate-300"
@click="closeModal"
aria-label="关闭"
>
<i class="fas fa-times"></i>
</button>
</div>
<!-- 设置内容 -->
<div class="p-6 space-y-4">
<!-- 更新检测开关 -->
<div
class="flex items-center justify-between rounded-2xl border border-slate-200/60 bg-slate-50/50 px-4 py-4 dark:border-slate-800/60 dark:bg-slate-800/50"
>
<div class="flex items-center gap-3">
<div
class="flex h-10 w-10 items-center justify-center rounded-xl bg-orange-100 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400"
>
<i class="fas fa-bell"></i>
</div>
<div>
<p
class="text-sm font-medium text-slate-800 dark:text-slate-200"
>
更新检测通知
</p>
<p class="text-xs text-slate-500 dark:text-slate-400">
系统启动后自动检测更新并通知
</p>
</div>
</div>
<button
type="button"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2"
:class="
settings.enableUpdateCheck
? 'bg-brand'
: 'bg-slate-300 dark:bg-slate-600'
"
@click="toggleUpdateCheck"
>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settings.enableUpdateCheck ? 'translate-x-6' : 'translate-x-1'
"
/>
</button>
</div>
<!-- 自动创建桌面启动器开关 -->
<div
class="flex items-center justify-between rounded-2xl border border-slate-200/60 bg-slate-50/50 px-4 py-4 dark:border-slate-800/60 dark:bg-slate-800/50"
>
<div class="flex items-center gap-3">
<div
class="flex h-10 w-10 items-center justify-center rounded-xl bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400"
>
<i class="fas fa-desktop"></i>
</div>
<div>
<p
class="text-sm font-medium text-slate-800 dark:text-slate-200"
>
自动创建桌面启动器
</p>
<p class="text-xs text-slate-500 dark:text-slate-400">
安装应用时自动创建桌面图标
</p>
</div>
</div>
<button
type="button"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2"
:class="
settings.enableCreateDesktop
? 'bg-brand'
: 'bg-slate-300 dark:bg-slate-600'
"
@click="toggleCreateDesktop"
>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settings.enableCreateDesktop
? 'translate-x-6'
: 'translate-x-1'
"
/>
</button>
</div>
</div>
<!-- 底部提示 -->
<div
class="border-t border-slate-200/60 bg-slate-50/50 px-6 py-3 dark:border-slate-800/60 dark:bg-slate-800/30"
>
<p class="text-xs text-slate-500 dark:text-slate-400 text-center">
设置会自动保存
</p>
</div>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
const props = defineProps<{
show: boolean;
}>();
const emit = defineEmits<{
(e: "close"): void;
}>();
interface Settings {
enableUpdateCheck: boolean;
enableCreateDesktop: boolean;
}
const settings = ref<Settings>({
enableUpdateCheck: true,
enableCreateDesktop: true,
});
// 配置文件路径
const UPDATE_CHECK_CONFIG = "ssshell-config-do-not-show-upgrade-notify";
const CREATE_DESKTOP_CONFIG = "ssshell-config-do-not-create-desktop";
// 加载设置
const loadSettings = async () => {
try {
const result = await window.ipcRenderer.invoke("get-install-settings");
if (result.success) {
settings.value = {
enableUpdateCheck: !result.data[UPDATE_CHECK_CONFIG],
enableCreateDesktop: !result.data[CREATE_DESKTOP_CONFIG],
};
}
} catch (error) {
console.error("加载设置失败:", error);
}
};
// 保存设置
const saveSettings = async () => {
try {
await window.ipcRenderer.invoke("set-install-settings", {
[UPDATE_CHECK_CONFIG]: !settings.value.enableUpdateCheck,
[CREATE_DESKTOP_CONFIG]: !settings.value.enableCreateDesktop,
});
} catch (error) {
console.error("保存设置失败:", error);
}
};
// 切换更新检测
const toggleUpdateCheck = () => {
settings.value.enableUpdateCheck = !settings.value.enableUpdateCheck;
saveSettings();
};
// 切换自动创建桌面启动器
const toggleCreateDesktop = () => {
settings.value.enableCreateDesktop = !settings.value.enableCreateDesktop;
saveSettings();
};
// 关闭模态框
const closeModal = () => {
emit("close");
};
// 监听显示状态
watch(
() => props.show,
(newVal) => {
if (newVal) {
loadSettings();
}
},
);
onMounted(() => {
if (props.show) {
loadSettings();
}
});
</script>