From 5c1ce2fa5cae2acfe6ad0907996171e0eeff0e65 Mon Sep 17 00:00:00 2001 From: Yuhang Wu Date: Fri, 16 Oct 2026 11:38:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=B8=B8=E8=A7=84?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E8=A1=8C=E4=B8=BA=E4=B8=8E=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增常规设置路由和页面,实现关闭行为(每次询问/保持选择)与应用语言的持久化配置;封装卡片式单选组组件复用在关闭弹窗和设置页;修改关闭逻辑,根据关闭行为决定是否显示确认弹窗,同步更新多语言文案和类型定义。 --- docs/Frpilot开发提示词.md | 13 ++- src/components/CardRadioGroup.vue | 86 ++++++++++++++++++++ src/components/CloseConfirmModal.vue | 116 ++++++++++----------------- src/i18n/locales/en-US.json | 18 ++++- src/i18n/locales/zh-CN.json | 18 ++++- src/router/router.ts | 8 +- src/stores/window.ts | 48 ++++++++--- src/types/components.d.ts | 1 + src/views/Setting.vue | 7 +- src/views/setting/General.vue | 60 ++++++++++++++ 10 files changed, 286 insertions(+), 89 deletions(-) create mode 100644 src/components/CardRadioGroup.vue create mode 100644 src/views/setting/General.vue diff --git a/docs/Frpilot开发提示词.md b/docs/Frpilot开发提示词.md index ea253bc..4000bed 100644 --- a/docs/Frpilot开发提示词.md +++ b/docs/Frpilot开发提示词.md @@ -159,4 +159,15 @@ Layout布局content区域的磨砂玻璃卡片圆角样式我要改动一下: #### 3.8 系统托盘图标BUG修改 - 开发阶段每次修改代码热更新之后,系统托盘图标就会新增一个 -- 需要修复确保只有一个系统托盘图标 \ No newline at end of file +- 需要修复确保只有一个系统托盘图标 + +#### 3.9 新增常规设置 + +- 设置页增加一个子页面-常规,第一项设置内容是关闭行为设置 +- 目前的关闭行为弹窗分为最小化到系统托盘和直接退出两种,需要增加一个单选组,里面是每次询问和保持以上选择(默认每次询问) +- 每次询问即每次关闭都需要弹窗供用户选择,保持以上选择则是保持用户选择的关闭方式,持久生效 +- 常规设置页面需要同步关闭弹窗里面的内容,使用户可以再次修改设置 + +- 常规设置页面再增加一个语言切换设置,保持跟Header栏同步 + +开发过程中使用到的组件尽可能全部使用Antdv库里面的,另外我引入的有tailwindcss原子化组件、auto-import和components依赖可以简化引入,开发之前尽可能先阅读一下package.json,看一下有哪些依赖可以简化代码开发 \ No newline at end of file diff --git a/src/components/CardRadioGroup.vue b/src/components/CardRadioGroup.vue new file mode 100644 index 0000000..c4de3d4 --- /dev/null +++ b/src/components/CardRadioGroup.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/src/components/CloseConfirmModal.vue b/src/components/CloseConfirmModal.vue index 800d517..1c25a7c 100644 --- a/src/components/CloseConfirmModal.vue +++ b/src/components/CloseConfirmModal.vue @@ -2,29 +2,27 @@ - - - {{ t("closeDialog.tray") }} - {{ t("closeDialog.trayDesc") }} - - - {{ t("closeDialog.exit") }} - {{ t("closeDialog.exitDesc") }} - - + + + + + + +
{{ t("closeDialog.behavior") }}
+ @@ -33,68 +31,40 @@ - - diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 8245d97..6d7bee4 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -39,7 +39,12 @@ "tray": "Minimize to system tray", "trayDesc": "Keep Frpilot running in the background; reopen it from the system tray", "exit": "Quit directly", - "exitDesc": "Stop the tunnel and exit Frpilot completely" + "exitDesc": "Stop the tunnel and exit Frpilot completely", + "behavior": "Close behavior", + "ask": "Ask every time", + "askDesc": "Show this dialog on every close and let you choose", + "remember": "Keep the choice above", + "rememberDesc": "Remember the close method above and never ask again" }, "system": { "cpu": "CPU usage", @@ -73,9 +78,20 @@ "title": "Settings", "placeholder": "The settings page is under construction.", "menu": { + "general": "General", "appearance": "Appearance", "about": "About" }, + "general": { + "title": "General", + "subtitle": "Basic settings such as close behavior and app language", + "closeBehavior": "Close behavior", + "closeBehaviorTip": "Choose what happens when you click the close button. Kept in sync with the close dialog.", + "closeAction": "Close method", + "closeActionTip": "Takes effect when \"Keep the choice above\" is selected. The window closes this way directly, and it is also the default option in the close dialog.", + "language": "Language", + "languageTip": "Switch the interface language. In sync with the language switcher in the title bar." + }, "appearance": { "title": "Appearance", "subtitle": "Customize the theme mode and window background", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 4be45c3..077210a 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -39,7 +39,12 @@ "tray": "最小化到系统托盘", "trayDesc": "保持 Frpilot 在后台运行,可从系统托盘随时打开", "exit": "直接退出", - "exitDesc": "停止穿透并完全退出 Frpilot" + "exitDesc": "停止穿透并完全退出 Frpilot", + "behavior": "关闭行为", + "ask": "每次询问", + "askDesc": "每次关闭都弹出此对话框,由你选择关闭方式", + "remember": "保持以上选择", + "rememberDesc": "记住上面的关闭方式,之后关闭不再询问" }, "system": { "cpu": "CPU 占用", @@ -73,9 +78,20 @@ "title": "设置", "placeholder": "设置页面开发中,敬请期待。", "menu": { + "general": "常规", "appearance": "外观设置", "about": "关于" }, + "general": { + "title": "常规", + "subtitle": "关闭行为与应用语言等基础设置", + "closeBehavior": "关闭行为", + "closeBehaviorTip": "设置点击关闭按钮时的处理方式,与关闭弹窗中的选项保持一致", + "closeAction": "关闭方式", + "closeActionTip": "选择「保持以上选择」后生效,关闭窗口时将直接按此方式处理,同时也作为关闭弹窗的默认选项", + "language": "语言", + "languageTip": "切换界面显示语言,与顶部栏的语言切换保持同步" + }, "appearance": { "title": "外观设置", "subtitle": "自定义窗口的主题模式与背景图片", diff --git a/src/router/router.ts b/src/router/router.ts index c75ea1b..a7dc19a 100644 --- a/src/router/router.ts +++ b/src/router/router.ts @@ -36,9 +36,15 @@ const routes: RouteRecordRaw[] = [ path: "setting", name: "Setting", component: () => import("@/views/Setting.vue"), - redirect: "/setting/appearance", + redirect: "/setting/general", meta: { title: "settings.title" }, children: [ + { + path: "general", + name: "General", + component: () => import("@/views/setting/General.vue"), + meta: { title: "settings.menu.general" }, + }, { path: "appearance", name: "Appearance", diff --git a/src/stores/window.ts b/src/stores/window.ts index 11b2fc9..cb36358 100644 --- a/src/stores/window.ts +++ b/src/stores/window.ts @@ -5,26 +5,34 @@ import { useFrpcStore } from "@/stores/frpc" /** 关闭窗口时用户的选择:最小化到系统托盘 / 直接退出 */ export type CloseChoice = "tray" | "exit" +/** 关闭行为:每次询问 / 保持以上选择 */ +export type CloseBehavior = "ask" | "remember" + /** * 窗口生命周期:关闭确认、最小化到系统托盘、退出软件。 * - * - 顶部栏关闭按钮与系统原生关闭(如 Alt+F4)都会先弹出确认对话框; - * - 选择「最小化到系统托盘」时隐藏窗口,程序保持后台运行; - * - 选择「直接退出」时若穿透正在运行会先停止,再退出整个软件。 + * - 顶部栏关闭按钮与系统原生关闭(如 Alt+F4)都会先经过 requestClose; + * - 关闭行为为「每次询问」时弹出确认对话框,选择「最小化到系统托盘」会隐藏窗口、 + * 程序保持后台运行,选择「直接退出」会先停止穿透再退出整个软件; + * - 关闭行为为「保持以上选择」时不弹窗,直接按记住的关闭方式处理。 + * + * 关闭行为与关闭方式均持久化,可在「设置 - 常规」中再次修改,与关闭弹窗内容保持一致。 */ export const useWindowStore = defineStore("window", () => { const frpcStore = useFrpcStore() /** 关闭确认对话框是否打开 */ const closeConfirmOpen = ref(false) - /** 当前选中的关闭方式 */ - const closeChoice = ref("tray") + /** 记住的关闭方式(弹窗默认选中项;「保持以上选择」时直接生效,持久化) */ + const closeChoice = useStorage("frpilot:close-choice", "tray") + /** 关闭行为:每次询问 / 保持以上选择(持久化,默认每次询问) */ + const closeBehavior = useStorage("frpilot:close-behavior", "ask") /** 是否正在退出(停止穿透 + 退出软件) */ const quitting = ref(false) let initialized = false - /** 应用启动时调用一次:拦截系统原生关闭,改为弹出确认对话框 */ + /** 应用启动时调用一次:拦截系统原生关闭,改为按关闭行为处理 */ async function init() { if (initialized || !isTauri()) return initialized = true @@ -36,9 +44,16 @@ export const useWindowStore = defineStore("window", () => { }) } - /** 请求关闭窗口:弹出确认对话框(默认选中最小化到系统托盘) */ + /** + * 请求关闭窗口: + * - 「每次询问」弹出确认对话框; + * - 「保持以上选择」不弹窗,直接按记住的方式处理。 + */ function requestClose() { - closeChoice.value = "tray" + if (closeBehavior.value === "remember") { + void applyClose(closeChoice.value) + return + } closeConfirmOpen.value = true } @@ -47,10 +62,19 @@ export const useWindowStore = defineStore("window", () => { closeConfirmOpen.value = false } - /** 确认关闭:按所选方式处理 */ - async function confirmClose() { - const choice = closeChoice.value + /** 确认关闭:持久化所选行为与方式,再按所选方式处理 */ + async function confirmClose( + choice: CloseChoice = closeChoice.value, + behavior: CloseBehavior = closeBehavior.value, + ) { + closeChoice.value = choice + closeBehavior.value = behavior closeConfirmOpen.value = false + await applyClose(choice) + } + + /** 按所选关闭方式执行:最小化到托盘 / 退出软件 */ + async function applyClose(choice: CloseChoice) { if (choice === "tray") { await minimizeToTray() } else { @@ -85,11 +109,13 @@ export const useWindowStore = defineStore("window", () => { return { closeConfirmOpen, closeChoice, + closeBehavior, quitting, init, requestClose, cancelClose, confirmClose, + applyClose, minimizeToTray, showWindow, quitApp, diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 192db3f..9c5beee 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -45,6 +45,7 @@ declare module 'vue' { ATag: typeof import('ant-design-vue/es')['Tag'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] + CardRadioGroup: typeof import('./../components/CardRadioGroup.vue')['default'] CloseConfirmModal: typeof import('./../components/CloseConfirmModal.vue')['default'] FrpCard: typeof import('./../components/frp/FrpCard.vue')['default'] MacTitleBar: typeof import('./../components/MacTitleBar.vue')['default'] diff --git a/src/views/Setting.vue b/src/views/Setting.vue index ab4f098..754c0ed 100644 --- a/src/views/Setting.vue +++ b/src/views/Setting.vue @@ -20,7 +20,7 @@