From f813245827b61b77cdd9fa49afebbc5cd8b82278 Mon Sep 17 00:00:00 2001 From: PanFu Date: Sat, 23 May 2026 09:50:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=81=8F=E5=A5=BD=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E7=9A=84=E5=BF=AB=E6=8D=B7=E9=94=AE=E5=88=97=E8=A1=A8=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0ESC=E5=BF=AB=E6=8D=B7=E9=94=AE=E7=9A=84=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=EF=BC=88=E5=85=B3=E9=97=AD=E5=BD=93=E5=89=8D=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=EF=BC=89=20(#7947)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 快捷键追加ESC控制,关闭当前窗口 * feat: 偏好设置中,页面切换动画的颜色看不清的问题(使用当前主题色) * feat: 三种弹出框支持快捷键ESC动作 * feat: 代码自动格式化(3个框架改动) * feat: 代码自动格式化(3个框架改动) * fix: 修正locale数据获取方式 * 单元测试问题修改 * 单元测试问题修改 * fix: 解决代码评论的问题 * fix: 解决代码评论的问题 * fix: 解决代码评论的问题 * fix: 解决代码评论的问题 * 单元测试问题修改 * fix: 解决评论问题 * fix: 解决代码格式导致pnpm run lint报错的问题 --------- Co-authored-by: PanFu --- .../__snapshots__/config.test.ts.snap | 1 + packages/@core/preferences/src/config.ts | 1 + packages/@core/preferences/src/preferences.ts | 2 +- packages/@core/preferences/src/types.ts | 2 ++ .../@core/preferences/src/use-preferences.ts | 12 ++++++- packages/@core/ui-kit/popup-ui/package.json | 1 + .../@core/ui-kit/popup-ui/src/alert/alert.ts | 2 ++ .../@core/ui-kit/popup-ui/src/alert/alert.vue | 13 +++++-- .../ui-kit/popup-ui/src/drawer/drawer.vue | 4 +-- .../ui-kit/popup-ui/src/drawer/use-drawer.ts | 14 ++++++-- .../ui-kit/popup-ui/src/modal/use-modal.ts | 13 +++++-- .../preferences/blocks/general/animation.vue | 5 ++- .../blocks/shortcut-keys/global.vue | 5 +++ .../preferences/preferences-drawer.vue | 4 +++ .../locales/src/langs/en-US/preferences.json | 1 + .../locales/src/langs/zh-CN/preferences.json | 1 + pnpm-lock.yaml | 35 +++++++++++++++---- 17 files changed, 98 insertions(+), 18 deletions(-) diff --git a/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap b/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap index 041dfab27..2d0fca58c 100644 --- a/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap +++ b/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap @@ -75,6 +75,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj }, "shortcutKeys": { "enable": true, + "globalEscape": false, "globalLockScreen": true, "globalLogout": true, "globalPreferences": true, diff --git a/packages/@core/preferences/src/config.ts b/packages/@core/preferences/src/config.ts index 88ba861fb..d776282d6 100644 --- a/packages/@core/preferences/src/config.ts +++ b/packages/@core/preferences/src/config.ts @@ -76,6 +76,7 @@ const defaultPreferences: Preferences = { }, shortcutKeys: { enable: true, + globalEscape: false, globalLockScreen: true, globalLogout: true, globalPreferences: true, diff --git a/packages/@core/preferences/src/preferences.ts b/packages/@core/preferences/src/preferences.ts index dddadd76a..c7f5caa34 100644 --- a/packages/@core/preferences/src/preferences.ts +++ b/packages/@core/preferences/src/preferences.ts @@ -135,7 +135,7 @@ class PreferenceManager { const cachedPreferences = (await this.loadFromCache()) || {}; const mergedPreference = merge( {}, - cachedPreferences, // 用户缓存的设置优先 + cachedPreferences, // 用户缓存的设置优先 this.initialPreferences, // 初始设置仅补齐缺失字段 ); diff --git a/packages/@core/preferences/src/types.ts b/packages/@core/preferences/src/types.ts index bf86935bf..4710ab75a 100644 --- a/packages/@core/preferences/src/types.ts +++ b/packages/@core/preferences/src/types.ts @@ -277,6 +277,8 @@ interface SidebarPreferences { interface ShortcutKeyPreferences { /** 是否启用快捷键-全局 */ enable: boolean; + /** 是否启用全局关闭窗口快捷键 */ + globalEscape: boolean; /** 是否启用全局锁屏快捷键 */ globalLockScreen: boolean; /** 是否启用全局注销快捷键 */ diff --git a/packages/@core/preferences/src/use-preferences.ts b/packages/@core/preferences/src/use-preferences.ts index a27f8d733..2a075f645 100644 --- a/packages/@core/preferences/src/use-preferences.ts +++ b/packages/@core/preferences/src/use-preferences.ts @@ -39,7 +39,7 @@ function usePreferences() { }); const locale = computed(() => { - return preferences.app.locale; + return appPreferences.value.locale; }); const isMobile = computed(() => { @@ -185,6 +185,14 @@ function usePreferences() { return enable && globalLogout; }); + /** + * @zh_CN 是否启用全局注销快捷键 + */ + const globalEscapeShortcutKey = computed(() => { + const { enable, globalEscape } = shortcutKeysPreferences.value; + return enable && globalEscape; + }); + const globalLockScreenShortcutKey = computed(() => { const { enable, globalLockScreen } = shortcutKeysPreferences.value; return enable && globalLockScreen; @@ -247,6 +255,7 @@ function usePreferences() { diffCustomPreference, globalLockScreenShortcutKey, globalLogoutShortcutKey, + globalEscapeShortcutKey, globalSearchShortcutKey, isDark, isFullContent, @@ -265,6 +274,7 @@ function usePreferences() { preferencesButtonPosition, sidebarCollapsed, theme, + app: appPreferences.value, }; } diff --git a/packages/@core/ui-kit/popup-ui/package.json b/packages/@core/ui-kit/popup-ui/package.json index 865a83c07..0114a01be 100644 --- a/packages/@core/ui-kit/popup-ui/package.json +++ b/packages/@core/ui-kit/popup-ui/package.json @@ -42,6 +42,7 @@ "dependencies": { "@vben-core/composables": "workspace:*", "@vben-core/icons": "workspace:*", + "@vben-core/preferences": "workspace:*", "@vben-core/shadcn-ui": "workspace:*", "@vben-core/shared": "workspace:*", "@vben-core/typings": "workspace:*", diff --git a/packages/@core/ui-kit/popup-ui/src/alert/alert.ts b/packages/@core/ui-kit/popup-ui/src/alert/alert.ts index 5a214fa2d..ff19830e1 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/alert.ts +++ b/packages/@core/ui-kit/popup-ui/src/alert/alert.ts @@ -36,6 +36,8 @@ export type AlertProps = { contentClass?: string; /** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/ contentMasking?: boolean; + /** 按下Esc时是否关闭弹窗 */ + escapeKeyClose?: boolean; /** 弹窗底部内容(与按钮在同一个容器中) */ footer?: Component | string; /** 弹窗的图标(在标题的前面) */ diff --git a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue index 57af6ac37..d49e0cc8b 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue +++ b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue @@ -14,6 +14,7 @@ import { Info, X, } from '@vben-core/icons'; +import { usePreferences } from '@vben-core/preferences'; import { AlertDialog, AlertDialogAction, @@ -34,8 +35,10 @@ const props = withDefaults(defineProps(), { bordered: true, buttonAlign: 'end', centered: true, + escapeKeyClose: true, }); const emits = defineEmits(['closed', 'confirm', 'opened']); +const { globalEscapeShortcutKey } = usePreferences(); const open = defineModel('open', { default: false }); const { $t } = useSimpleLocale(); const components = globalShareState.getComponents(); @@ -46,8 +49,14 @@ function onAlertClosed() { isConfirm.value = false; } -function onEscapeKeyDown() { +function onEscapeKeyDown(e: KeyboardEvent) { + // 先标记是按 Esc 触发的(用于后续 isConfirm 判断等) isConfirm.value = false; + + // 只有当组件参数和全局配置都为false时才阻止关闭,其任意一个为true都需要让esc生效 + if (!props.escapeKeyClose && !globalEscapeShortcutKey.value) { + e.preventDefault(); + } } const getIconRender = computed(() => { @@ -143,7 +152,7 @@ async function handleOpenChange(val: boolean) { :overlay-blur="overlayBlur" @opened="emits('opened')" @closed="onAlertClosed" - @escape-key-down="onEscapeKeyDown" + @escape-key-down="onEscapeKeyDown($event)" :class=" cn( containerClass, diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue index b0f454405..0a7fd35f0 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -54,7 +54,7 @@ const components = globalShareState.getComponents(); const id = useId(); provide('DISMISSABLE_DRAWER_ID', id); -const wrapperRef = ref(); +// const wrapperRef = ref(); const { $t } = useSimpleLocale(); const { isMobile } = useIsMobile(); @@ -285,8 +285,8 @@ const getForceMount = computed(() => { +
-
+
diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue b/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue index f71a1f6c5..baa18a900 100644 --- a/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue +++ b/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue @@ -17,6 +17,7 @@ const shortcutKeysGlobalSearch = defineModel( const shortcutKeysLogout = defineModel('shortcutKeysLogout'); // const shortcutKeysPreferences = defineModel('shortcutKeysPreferences'); const shortcutKeysLockScreen = defineModel('shortcutKeysLockScreen'); +const shortcutKeysEscape = defineModel('shortcutKeysEscape'); const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥')); @@ -47,4 +48,8 @@ const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥')); {{ $t('ui.widgets.lockScreen.title') }} + + {{ $t('preferences.shortcutKeys.escape') }} + + diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue index 963e9da4b..66e09a715 100644 --- a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue +++ b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -166,6 +166,9 @@ const shortcutKeysGlobalSearch = defineModel( const shortcutKeysGlobalLogout = defineModel( 'shortcutKeysGlobalLogout', ); +const shortcutKeysGlobalEscape = defineModel( + 'shortcutKeysGlobalEscape', +); const shortcutKeysGlobalLockScreen = defineModel( 'shortcutKeysGlobalLockScreen', @@ -520,6 +523,7 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) { v-model:shortcut-keys-global-search="shortcutKeysGlobalSearch" v-model:shortcut-keys-lock-screen="shortcutKeysGlobalLockScreen" v-model:shortcut-keys-logout="shortcutKeysGlobalLogout" + v-model:shortcut-keys-escape="shortcutKeysGlobalEscape" /> diff --git a/packages/locales/src/langs/en-US/preferences.json b/packages/locales/src/langs/en-US/preferences.json index 209eacfe6..d027227e9 100644 --- a/packages/locales/src/langs/en-US/preferences.json +++ b/packages/locales/src/langs/en-US/preferences.json @@ -187,6 +187,7 @@ "global": "Global", "search": "Global Search", "logout": "Logout", + "escape": "Close Current Window", "preferences": "Preferences" }, "widget": { diff --git a/packages/locales/src/langs/zh-CN/preferences.json b/packages/locales/src/langs/zh-CN/preferences.json index 2d4a3d44f..8cbc6075b 100644 --- a/packages/locales/src/langs/zh-CN/preferences.json +++ b/packages/locales/src/langs/zh-CN/preferences.json @@ -187,6 +187,7 @@ "global": "全局", "search": "全局搜索", "logout": "退出登录", + "escape": "关闭当前窗口", "preferences": "偏好设置" }, "widget": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a97ccbcf..9b38c9afe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1006,14 +1006,14 @@ importers: version: 2.9.7(vue@3.5.34(typescript@6.0.3)) vitepress-plugin-group-icons: specifier: 'catalog:' - version: 1.7.5(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)) + version: 1.7.5(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)) devDependencies: '@nolebase/vitepress-plugin-git-changelog': specifier: 'catalog:' version: 2.18.2(vitepress@2.0.0-alpha.17(@types/node@25.9.1)(async-validator@4.2.5)(axios@1.16.1)(change-case@5.4.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(nprogress@0.2.0)(postcss@8.5.15)(qrcode@1.5.4)(sass-embedded@1.100.0)(sass@1.100.0)(sortablejs@1.15.7)(terser@5.48.0)(typescript@6.0.3)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)) '@vben/tailwind-config': specifier: workspace:* version: link:../internal/tailwind-config @@ -1022,7 +1022,7 @@ importers: version: link:../internal/vite-config '@vite-pwa/vitepress': specifier: 'catalog:' - version: 1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1)) + version: 1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1)) vitepress: specifier: 'catalog:' version: 2.0.0-alpha.17(@types/node@25.9.1)(async-validator@4.2.5)(axios@1.16.1)(change-case@5.4.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(nprogress@0.2.0)(postcss@8.5.15)(qrcode@1.5.4)(sass-embedded@1.100.0)(sass@1.100.0)(sortablejs@1.15.7)(terser@5.48.0)(typescript@6.0.3)(yaml@2.9.0) @@ -1522,6 +1522,9 @@ importers: '@vben-core/icons': specifier: workspace:* version: link:../../base/icons + '@vben-core/preferences': + specifier: workspace:* + version: link:../../preferences '@vben-core/shadcn-ui': specifier: workspace:* version: link:../shadcn-ui @@ -14279,6 +14282,13 @@ snapshots: tailwindcss: 4.3.0 vite: 8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0) + '@tailwindcss/vite@4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + tailwindcss: 4.3.0 + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0) + '@tanstack/match-sorter-utils@8.19.4': dependencies: remove-accents: 0.5.0 @@ -15015,9 +15025,9 @@ snapshots: - rollup - supports-color - '@vite-pwa/vitepress@1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1))': + '@vite-pwa/vitepress@1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1))': dependencies: - vite-plugin-pwa: 1.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1) + vite-plugin-pwa: 1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1) '@vitejs/plugin-vue-jsx@5.1.5(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3))': dependencies: @@ -20397,6 +20407,17 @@ snapshots: transitivePeerDependencies: - supports-color + vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.16 + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0) + workbox-build: 7.4.1 + workbox-window: 7.4.1 + transitivePeerDependencies: + - supports-color + vite-plugin-vue-devtools@8.1.2(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3)): dependencies: '@vue/devtools-core': 8.1.2(vue@3.5.34(typescript@6.0.3)) @@ -20481,13 +20502,13 @@ snapshots: terser: 5.48.0 yaml: 2.9.0 - vitepress-plugin-group-icons@1.7.5(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)): + vitepress-plugin-group-icons@1.7.5(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@iconify-json/logos': 1.2.11 '@iconify-json/vscode-icons': 1.2.50 '@iconify/utils': 3.1.3 optionalDependencies: - vite: 8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0) vitepress@2.0.0-alpha.17(@types/node@25.9.1)(async-validator@4.2.5)(axios@1.16.1)(change-case@5.4.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(nprogress@0.2.0)(postcss@8.5.15)(qrcode@1.5.4)(sass-embedded@1.100.0)(sass@1.100.0)(sortablejs@1.15.7)(terser@5.48.0)(typescript@6.0.3)(yaml@2.9.0): dependencies: