feat: 新增开机自启功能
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-autostart": "^2.7.0",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-process": "^2.4.0",
|
||||
"@tauri-apps/plugin-updater": "^2.13.3",
|
||||
|
||||
Generated
+10
@@ -14,6 +14,9 @@ importers:
|
||||
'@tauri-apps/api':
|
||||
specifier: ^2
|
||||
version: 2.12.3
|
||||
'@tauri-apps/plugin-autostart':
|
||||
specifier: ^2.7.0
|
||||
version: 2.7.0
|
||||
'@tauri-apps/plugin-opener':
|
||||
specifier: ^2
|
||||
version: 2.7.1
|
||||
@@ -535,6 +538,9 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@tauri-apps/plugin-autostart@2.7.0':
|
||||
resolution: {integrity: sha512-4uRq+rqKSAh6MWAxgk6FnUPigc8VMG8eDVYTLk3A7OfKs9sLVwfeM8kbUvzjpabRQvyl4zd9tfzXBTrrtuprsA==}
|
||||
|
||||
'@tauri-apps/plugin-opener@2.7.1':
|
||||
resolution: {integrity: sha512-XkXdt30rontti9TII/gBBxFRXWfj8DiprFW7RgBFRBj1OhdfjBNVcFqSEWkkZ4hXafHnuEnB7F1sFsmu6uyywQ==}
|
||||
|
||||
@@ -1578,6 +1584,10 @@ snapshots:
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.12.2
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.12.2
|
||||
|
||||
'@tauri-apps/plugin-autostart@2.7.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.12.3
|
||||
|
||||
'@tauri-apps/plugin-opener@2.7.1':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.12.3
|
||||
|
||||
Generated
+112
-5
@@ -231,6 +231,20 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "auto-launch"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17918dba7ecf78b9a14507ec9f984e7977d13fad87dc86d61038980a45d128cf"
|
||||
dependencies = [
|
||||
"dirs 6.0.0",
|
||||
"os_info",
|
||||
"smappservice-rs",
|
||||
"thiserror 2.0.21",
|
||||
"windows-registry",
|
||||
"windows-result",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
@@ -493,6 +507,12 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.2"
|
||||
@@ -889,6 +909,15 @@ dependencies = [
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "7.0.0"
|
||||
@@ -1280,6 +1309,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-helper",
|
||||
"tauri-plugin-autostart",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-prevent-default",
|
||||
@@ -2571,6 +2601,18 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.3"
|
||||
@@ -2802,6 +2844,30 @@ dependencies = [
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-security"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-service-management"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b213642d6959cc6023ceb1217aa595eaaf09b8094ce95127c103cab611fe65e8"
|
||||
dependencies = [
|
||||
"block2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
"objc2-security",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.2"
|
||||
@@ -2923,6 +2989,21 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"log",
|
||||
"nix",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-ui-kit",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "osakit"
|
||||
version = "0.3.1"
|
||||
@@ -3988,6 +4069,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smappservice-rs"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52703b97a53101cf5d4580e0737aaa634ce1fdcfc123c16b2a9658e358013488"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-service-management",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.5"
|
||||
@@ -4494,7 +4587,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"dirs",
|
||||
"dirs 7.0.0",
|
||||
"dunce",
|
||||
"embed_plist",
|
||||
"getrandom 0.3.4",
|
||||
@@ -4544,7 +4637,7 @@ checksum = "2eea0b6302f82c574b21af6fc6ae6e86183b9280853c22621d0abf0936911ffc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
"dirs",
|
||||
"dirs 7.0.0",
|
||||
"glob",
|
||||
"heck 0.5.0",
|
||||
"json-patch",
|
||||
@@ -4629,6 +4722,20 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-autostart"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bce56ceb0a99b8ab5eebc977ba9ffc62391b862648cb9ab76fc8bc7231b8f7e2"
|
||||
dependencies = [
|
||||
"auto-launch",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.8.2"
|
||||
@@ -4740,7 +4847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "855a336eb389d2c1502244fd48fa2d8cb487a430f691bbaed025195f99a04489"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
"dirs 7.0.0",
|
||||
"flate2",
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -5277,7 +5384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b9c52859a94554803ccd4a24b98f74148ebc73b90676d783f3490b1bff9d72"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dirs 7.0.0",
|
||||
"libappindicator",
|
||||
"muda",
|
||||
"objc2",
|
||||
@@ -6137,7 +6244,7 @@ dependencies = [
|
||||
"block2",
|
||||
"cookie",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dirs 7.0.0",
|
||||
"dom_query",
|
||||
"dpi",
|
||||
"dunce",
|
||||
|
||||
@@ -39,6 +39,7 @@ sysinfo = "0.39"
|
||||
tauri-plugin-dialog = "2.7.2"
|
||||
tauri-plugin-updater = "2.10.1"
|
||||
tauri-plugin-process = "2.3.1"
|
||||
tauri-plugin-autostart = "2"
|
||||
tauri-plugin-prevent-default = "6.0.1"
|
||||
reqwest = { version = "0.12", features = ["json", "stream"] }
|
||||
futures-util = "0.3"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"opener:default",
|
||||
"updater:default",
|
||||
"process:default",
|
||||
"autostart:default",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:window:allow-minimize",
|
||||
"core:window:allow-unminimize",
|
||||
|
||||
@@ -28,6 +28,11 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_dialog::init()) // 对话框插件
|
||||
.plugin(tauri_plugin_updater::Builder::new().build()) // 更新插件
|
||||
.plugin(tauri_plugin_process::init()) // 进程插件(更新后重启)
|
||||
// 开机自启插件:读写系统启动项(Windows 注册表 Run 键 / macOS LaunchAgent / Linux XDG autostart)
|
||||
.plugin(tauri_plugin_autostart::init(
|
||||
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
|
||||
None,
|
||||
))
|
||||
.plugin(tauri_plugin_prevent_default::Builder::new().build())
|
||||
.setup(|app| {
|
||||
let pool = tauri::async_runtime::block_on(async {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { isTauri } from "@/utils/window"
|
||||
|
||||
/**
|
||||
* 开机自启封装(Tauri 官方 autostart 插件)。
|
||||
*
|
||||
* 自启状态由系统启动项决定(Windows 注册表 Run 键 / macOS LaunchAgent /
|
||||
* Linux XDG autostart),这里只做读写,不在前端另行持久化,避免两端不一致。
|
||||
* 非 Tauri 环境(纯浏览器调试)下调用会被安全忽略。
|
||||
*/
|
||||
|
||||
/** 读取系统启动项:本应用是否已设为开机自启 */
|
||||
export async function isAutostartEnabled(): Promise<boolean> {
|
||||
if (!isTauri()) return false
|
||||
const { isEnabled } = await import("@tauri-apps/plugin-autostart")
|
||||
return isEnabled()
|
||||
}
|
||||
|
||||
/** 开启 / 关闭开机自启(直接写入系统启动项) */
|
||||
export async function setAutostartEnabled(enabled: boolean): Promise<void> {
|
||||
if (!isTauri()) return
|
||||
const { enable, disable } = await import("@tauri-apps/plugin-autostart")
|
||||
await (enabled ? enable() : disable())
|
||||
}
|
||||
@@ -29,7 +29,7 @@ const model = defineModel<T>({ required: true })
|
||||
.card-radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ const model = defineModel<T>({ required: true })
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
margin-inline-end: 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.1);
|
||||
transition: border-color 0.2s ease, background-color 0.2s ease;
|
||||
|
||||
@@ -54,13 +54,13 @@ const model = defineModel<T>({ required: true })
|
||||
}
|
||||
|
||||
:deep(.ant-radio) {
|
||||
margin-top: 2px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
:deep(> span:last-child) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
gap: 0;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
}
|
||||
@@ -75,12 +75,14 @@ const model = defineModel<T>({ required: true })
|
||||
}
|
||||
|
||||
.card-radio-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.card-radio-desc {
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
font-size: 11.5px;
|
||||
line-height: 1.35;
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,15 +7,16 @@
|
||||
:mask-closable="false"
|
||||
:closable="!store.quitting"
|
||||
:keyboard="!store.quitting"
|
||||
:body-style="{ paddingTop: '10px', paddingBottom: '4px' }"
|
||||
@cancel="store.cancelClose()"
|
||||
>
|
||||
<!-- 关闭方式:最小化到系统托盘 / 直接退出 -->
|
||||
<CardRadioGroup v-model="draftChoice" :options="choiceOptions" :disabled="store.quitting" />
|
||||
|
||||
<a-divider class="!my-3" />
|
||||
<a-divider class="close-divider" />
|
||||
|
||||
<!-- 关闭行为:每次询问 / 保持以上选择 -->
|
||||
<div class="mb-2 text-xs opacity-60">{{ t("closeDialog.behavior") }}</div>
|
||||
<div class="mb-1.5 text-xs opacity-60">{{ t("closeDialog.behavior") }}</div>
|
||||
<CardRadioGroup v-model="draftBehavior" :options="behaviorOptions" :disabled="store.quitting" />
|
||||
|
||||
<template #footer>
|
||||
@@ -68,3 +69,10 @@ function onConfirm() {
|
||||
void store.confirmClose(draftChoice.value, draftBehavior.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 弹窗内两组选项之间只留窄窄一段分隔,整体更紧凑 */
|
||||
.close-divider {
|
||||
margin: 8px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -84,7 +84,13 @@
|
||||
},
|
||||
"general": {
|
||||
"title": "General",
|
||||
"subtitle": "Basic settings such as close behavior and app language",
|
||||
"subtitle": "Basic settings such as launch at startup, close behavior and app language",
|
||||
"autostart": "Launch at startup",
|
||||
"autostartTip": "Start FrpcPilot automatically after you sign in. The switch writes straight to the system startup entry.",
|
||||
"autostartUnsupported": "System startup entries are not accessible here, so this setting is unavailable.",
|
||||
"autostartEnabled": "Launch at startup enabled",
|
||||
"autostartDisabled": "Launch at startup disabled",
|
||||
"autostartFailed": "Failed to update launch at startup: {message}",
|
||||
"closeBehavior": "Close behavior",
|
||||
"closeBehaviorTip": "Choose what happens when you click the close button. Kept in sync with the close dialog.",
|
||||
"closeAction": "Close method",
|
||||
|
||||
@@ -84,7 +84,13 @@
|
||||
},
|
||||
"general": {
|
||||
"title": "常规",
|
||||
"subtitle": "关闭行为与应用语言等基础设置",
|
||||
"subtitle": "开机自启、关闭行为与应用语言等基础设置",
|
||||
"autostart": "开机自启",
|
||||
"autostartTip": "登录系统后自动启动 FrpcPilot,开关状态直接写入系统启动项",
|
||||
"autostartUnsupported": "当前环境无法读写系统启动项,暂不支持该设置",
|
||||
"autostartEnabled": "已开启开机自启",
|
||||
"autostartDisabled": "已关闭开机自启",
|
||||
"autostartFailed": "设置开机自启失败:{message}",
|
||||
"closeBehavior": "关闭行为",
|
||||
"closeBehaviorTip": "设置点击关闭按钮时的处理方式,与关闭弹窗中的选项保持一致",
|
||||
"closeAction": "关闭方式",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { defineStore } from "pinia"
|
||||
import { message } from "ant-design-vue"
|
||||
import { isTauri } from "@/utils/window"
|
||||
import { isAutostartEnabled, setAutostartEnabled } from "@/api/autostart"
|
||||
import { translate } from "@/i18n"
|
||||
|
||||
/**
|
||||
* 开机自启:直接读写系统启动项,状态以系统为准,不做本地持久化。
|
||||
*
|
||||
* - 进入设置页时读一次系统启动项,保证开关展示的就是系统真实状态;
|
||||
* - 切换后重新读取一次,写入失败则回滚开关并提示原因,避免界面与系统不一致;
|
||||
* - 无法访问系统启动项(浏览器调试 / 插件不可用)时 supported 为 false,开关置灰。
|
||||
*/
|
||||
export const useAutostartStore = defineStore("autostart", () => {
|
||||
/** 系统启动项是否已登记本应用 */
|
||||
const enabled = ref(false)
|
||||
/** 读取 / 写入系统启动项期间显示加载态,同时防止重复操作 */
|
||||
const loading = ref(isTauri())
|
||||
/** 当前环境能否读写系统启动项 */
|
||||
const supported = ref(isTauri())
|
||||
|
||||
function errMsg(e: unknown) {
|
||||
return e instanceof Error ? e.message : String(e)
|
||||
}
|
||||
|
||||
/** 读取系统启动项的真实状态(进入设置页时调用) */
|
||||
async function refresh() {
|
||||
if (!isTauri()) return
|
||||
loading.value = true
|
||||
try {
|
||||
enabled.value = await isAutostartEnabled()
|
||||
supported.value = true
|
||||
} catch {
|
||||
// 读取失败(如插件未注册、权限缺失):禁用开关,不展示可能错误的状态
|
||||
supported.value = false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 切换开机自启:写入系统启动项,成功 / 失败都以系统状态为准并给出提示 */
|
||||
async function toggle(value: boolean) {
|
||||
if (!supported.value || loading.value || value === enabled.value) return
|
||||
|
||||
const previous = enabled.value
|
||||
loading.value = true
|
||||
try {
|
||||
await setAutostartEnabled(value)
|
||||
// 以系统启动项为准;读回失败时先按目标值展示,下次进入设置页会再次校准
|
||||
enabled.value = await isAutostartEnabled().catch(() => value)
|
||||
message.success(
|
||||
translate(
|
||||
value ? "settings.general.autostartEnabled" : "settings.general.autostartDisabled",
|
||||
),
|
||||
)
|
||||
} catch (e) {
|
||||
enabled.value = previous
|
||||
message.error(translate("settings.general.autostartFailed", { message: errMsg(e) }))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return { enabled, loading, supported, refresh, toggle }
|
||||
})
|
||||
@@ -1,26 +1,45 @@
|
||||
<template>
|
||||
<div class="max-w-2xl">
|
||||
<h2 class="mb-1 text-lg font-semibold">{{ t("settings.general.title") }}</h2>
|
||||
<p class="mb-6 text-xs opacity-60">{{ t("settings.general.subtitle") }}</p>
|
||||
<p class="mb-5 text-xs opacity-60">{{ t("settings.general.subtitle") }}</p>
|
||||
|
||||
<!-- 开机自启:开关直接写入系统启动项,状态由系统决定 -->
|
||||
<section>
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium">{{ t("settings.general.autostart") }}</div>
|
||||
<p class="mt-1 text-xs opacity-60">{{ autostartTip }}</p>
|
||||
</div>
|
||||
<a-switch
|
||||
class="shrink-0"
|
||||
:checked="autostartStore.enabled"
|
||||
:loading="autostartStore.loading"
|
||||
:disabled="!autostartStore.supported"
|
||||
@change="onAutostartChange"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a-divider class="general-divider" />
|
||||
|
||||
<!-- 关闭行为:与关闭弹窗内容保持一致,可在此再次修改 -->
|
||||
<section class="mb-6">
|
||||
<section>
|
||||
<div class="mb-1 text-sm font-medium">{{ t("settings.general.closeBehavior") }}</div>
|
||||
<p class="mb-3 text-xs opacity-60">{{ t("settings.general.closeBehaviorTip") }}</p>
|
||||
<p class="mb-2 text-xs opacity-60">{{ t("settings.general.closeBehaviorTip") }}</p>
|
||||
<CardRadioGroup v-model="windowStore.closeBehavior" :options="behaviorOptions" />
|
||||
|
||||
<!-- 关闭方式:与关闭弹窗一致,可随时修改;选择「保持以上选择」后关闭时直接生效 -->
|
||||
<div class="mt-4 mb-1 text-sm font-medium">{{ t("settings.general.closeAction") }}</div>
|
||||
<p class="mb-3 text-xs opacity-60">{{ t("settings.general.closeActionTip") }}</p>
|
||||
<div class="mt-3 mb-1 text-sm font-medium">{{ t("settings.general.closeAction") }}</div>
|
||||
<p class="mb-2 text-xs opacity-60">{{ t("settings.general.closeActionTip") }}</p>
|
||||
<CardRadioGroup v-model="windowStore.closeChoice" :options="choiceOptions" />
|
||||
</section>
|
||||
|
||||
<a-divider />
|
||||
<a-divider class="general-divider" />
|
||||
|
||||
<!-- 语言:与顶部栏语言切换共用同一状态 -->
|
||||
<section>
|
||||
<div class="mb-1 text-sm font-medium">{{ t("settings.general.language") }}</div>
|
||||
<p class="mb-3 text-xs opacity-60">{{ t("settings.general.languageTip") }}</p>
|
||||
<p class="mb-2 text-xs opacity-60">{{ t("settings.general.languageTip") }}</p>
|
||||
<a-radio-group
|
||||
v-model:value="appStore.language"
|
||||
:options="languageRadios"
|
||||
@@ -35,10 +54,12 @@
|
||||
import { useI18n } from "vue-i18n"
|
||||
import { languageOptions } from "@/i18n"
|
||||
import { useAppStore } from "@/stores/app"
|
||||
import { useAutostartStore } from "@/stores/autostart"
|
||||
import { useWindowStore } from "@/stores/window"
|
||||
|
||||
const { t } = useI18n()
|
||||
const appStore = useAppStore()
|
||||
const autostartStore = useAutostartStore()
|
||||
const windowStore = useWindowStore()
|
||||
|
||||
const behaviorOptions = computed(() => [
|
||||
@@ -57,4 +78,28 @@ const choiceOptions = computed(() => [
|
||||
|
||||
/** 语言选项与顶部栏共用 languageOptions,保证两端一致 */
|
||||
const languageRadios = languageOptions.map((item) => ({ label: item.label, value: item.key }))
|
||||
|
||||
/** 无法访问系统启动项时(非桌面环境)给出说明,避免开关无故置灰 */
|
||||
const autostartTip = computed(() =>
|
||||
autostartStore.supported
|
||||
? t("settings.general.autostartTip")
|
||||
: t("settings.general.autostartUnsupported"),
|
||||
)
|
||||
|
||||
/** 切换开机自启:失败时由 store 回滚开关状态并提示原因 */
|
||||
async function onAutostartChange(checked: boolean | string | number) {
|
||||
await autostartStore.toggle(Boolean(checked))
|
||||
}
|
||||
|
||||
// 每次进入设置页都读一次系统启动项,用户可能在系统设置里改过
|
||||
onMounted(() => {
|
||||
void autostartStore.refresh()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 各组设置之间留白收紧,避免分隔线两侧过于松散 */
|
||||
.general-divider {
|
||||
margin: 16px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user