feat: 偏好设置的快捷键列表追加ESC快捷键的控制(关闭当前窗口) (#7947)
* feat: 快捷键追加ESC控制,关闭当前窗口 * feat: 偏好设置中,页面切换动画的颜色看不清的问题(使用当前主题色) * feat: 三种弹出框支持快捷键ESC动作 * feat: 代码自动格式化(3个框架改动) * feat: 代码自动格式化(3个框架改动) * fix: 修正locale数据获取方式 * 单元测试问题修改 * 单元测试问题修改 * fix: 解决代码评论的问题 * fix: 解决代码评论的问题 * fix: 解决代码评论的问题 * fix: 解决代码评论的问题 * 单元测试问题修改 * fix: 解决评论问题 * fix: 解决代码格式导致pnpm run lint报错的问题 --------- Co-authored-by: PanFu <panfu@zhihuaai.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -76,6 +76,7 @@ const defaultPreferences: Preferences = {
|
||||
},
|
||||
shortcutKeys: {
|
||||
enable: true,
|
||||
globalEscape: false,
|
||||
globalLockScreen: true,
|
||||
globalLogout: true,
|
||||
globalPreferences: true,
|
||||
|
||||
@@ -135,7 +135,7 @@ class PreferenceManager {
|
||||
const cachedPreferences = (await this.loadFromCache()) || {};
|
||||
const mergedPreference = merge(
|
||||
{},
|
||||
cachedPreferences, // 用户缓存的设置优先
|
||||
cachedPreferences, // 用户缓存的设置优先
|
||||
this.initialPreferences, // 初始设置仅补齐缺失字段
|
||||
);
|
||||
|
||||
|
||||
@@ -277,6 +277,8 @@ interface SidebarPreferences {
|
||||
interface ShortcutKeyPreferences {
|
||||
/** 是否启用快捷键-全局 */
|
||||
enable: boolean;
|
||||
/** 是否启用全局关闭窗口快捷键 */
|
||||
globalEscape: boolean;
|
||||
/** 是否启用全局锁屏快捷键 */
|
||||
globalLockScreen: boolean;
|
||||
/** 是否启用全局注销快捷键 */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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:*",
|
||||
|
||||
@@ -36,6 +36,8 @@ export type AlertProps = {
|
||||
contentClass?: string;
|
||||
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
|
||||
contentMasking?: boolean;
|
||||
/** 按下Esc时是否关闭弹窗 */
|
||||
escapeKeyClose?: boolean;
|
||||
/** 弹窗底部内容(与按钮在同一个容器中) */
|
||||
footer?: Component | string;
|
||||
/** 弹窗的图标(在标题的前面) */
|
||||
|
||||
@@ -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<AlertProps>(), {
|
||||
bordered: true,
|
||||
buttonAlign: 'end',
|
||||
centered: true,
|
||||
escapeKeyClose: true,
|
||||
});
|
||||
const emits = defineEmits(['closed', 'confirm', 'opened']);
|
||||
const { globalEscapeShortcutKey } = usePreferences();
|
||||
const open = defineModel<boolean>('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,
|
||||
|
||||
@@ -54,7 +54,7 @@ const components = globalShareState.getComponents();
|
||||
const id = useId();
|
||||
provide('DISMISSABLE_DRAWER_ID', id);
|
||||
|
||||
const wrapperRef = ref<HTMLElement>();
|
||||
// const wrapperRef = ref<HTMLElement>();
|
||||
const { $t } = useSimpleLocale();
|
||||
const { isMobile } = useIsMobile();
|
||||
|
||||
@@ -285,8 +285,8 @@ const getForceMount = computed(() => {
|
||||
<SheetDescription />
|
||||
</VisuallyHidden>
|
||||
</template>
|
||||
<!-- 注释掉的部分 <div ref="wrapperRef" -->
|
||||
<div
|
||||
ref="wrapperRef"
|
||||
:class="
|
||||
cn('relative flex-1 overflow-y-auto p-3', contentClass, {
|
||||
'pointer-events-none': showLoading || submitting,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
ref,
|
||||
} from 'vue';
|
||||
|
||||
import { usePreferences } from '@vben-core/preferences';
|
||||
import { useStore } from '@vben-core/shared/store';
|
||||
|
||||
import { DrawerApi } from './drawer-api';
|
||||
@@ -21,6 +22,11 @@ import VbenDrawer from './drawer.vue';
|
||||
|
||||
const USER_DRAWER_INJECT_KEY = Symbol('VBEN_DRAWER_INJECT');
|
||||
|
||||
const { globalEscapeShortcutKey } = usePreferences();
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
const DEFAULT_DRAWER_PROPS: Partial<DrawerProps> = {};
|
||||
|
||||
export function setDefaultDrawerProps(props: Partial<DrawerProps>) {
|
||||
@@ -33,6 +39,10 @@ export function useVbenDrawer<
|
||||
// Drawer一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
|
||||
// 外部的Drawer通过provide/inject传递api
|
||||
|
||||
const defaultOptions = {
|
||||
closeOnPressEscape: globalEscapeShortcutKey.value, // 全局Esc快捷键配置
|
||||
...options,
|
||||
};
|
||||
const { connectedComponent } = options;
|
||||
if (connectedComponent) {
|
||||
const extendedApi = reactive({});
|
||||
@@ -45,7 +55,7 @@ export function useVbenDrawer<
|
||||
// 不能用 Object.assign,会丢失 api 的原型函数
|
||||
Object.setPrototypeOf(extendedApi, api);
|
||||
},
|
||||
options,
|
||||
defaultOptions,
|
||||
async reCreateDrawer() {
|
||||
isDrawerReady.value = false;
|
||||
await nextTick();
|
||||
@@ -79,7 +89,7 @@ export function useVbenDrawer<
|
||||
const mergedOptions = {
|
||||
...DEFAULT_DRAWER_PROPS,
|
||||
...injectData.options,
|
||||
...options,
|
||||
...defaultOptions,
|
||||
} as DrawerApiOptions;
|
||||
|
||||
mergedOptions.onOpenChange = (isOpen: boolean) => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ref,
|
||||
} from 'vue';
|
||||
|
||||
import { usePreferences } from '@vben-core/preferences';
|
||||
import { useStore } from '@vben-core/shared/store';
|
||||
|
||||
import { ModalApi } from './modal-api';
|
||||
@@ -17,6 +18,10 @@ import VbenModal from './modal.vue';
|
||||
|
||||
const USER_MODAL_INJECT_KEY = Symbol('VBEN_MODAL_INJECT');
|
||||
|
||||
const { globalEscapeShortcutKey } = usePreferences();
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
const DEFAULT_MODAL_PROPS: Partial<ModalProps> = {};
|
||||
|
||||
export function setDefaultModalProps(props: Partial<ModalProps>) {
|
||||
@@ -29,6 +34,10 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
// Modal一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
|
||||
// 外部的Modal通过provide/inject传递api
|
||||
|
||||
const defaultOptions = {
|
||||
closeOnPressEscape: globalEscapeShortcutKey.value, // 全局Esc快捷键配置
|
||||
...options,
|
||||
};
|
||||
const { connectedComponent } = options;
|
||||
if (connectedComponent) {
|
||||
const extendedApi = reactive({});
|
||||
@@ -42,7 +51,7 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
Object.setPrototypeOf(extendedApi, api);
|
||||
},
|
||||
consumed: false,
|
||||
options,
|
||||
defaultOptions,
|
||||
async reCreateModal() {
|
||||
isModalReady.value = false;
|
||||
await nextTick();
|
||||
@@ -85,7 +94,7 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
const mergedOptions = {
|
||||
...DEFAULT_MODAL_PROPS,
|
||||
...injectData.options,
|
||||
...options,
|
||||
...defaultOptions,
|
||||
} as ModalApiOptions;
|
||||
|
||||
mergedOptions.onOpenChange = (isOpen: boolean) => {
|
||||
|
||||
@@ -45,7 +45,10 @@ function handleClick(value: string) {
|
||||
class="outline-box p-2"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<div :class="`${item}-slow`" class="h-10 w-12 rounded-md bg-accent"></div>
|
||||
<div
|
||||
:class="`${item}-slow`"
|
||||
class="h-10 w-12 rounded-md bg-primary"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -17,6 +17,7 @@ const shortcutKeysGlobalSearch = defineModel<boolean>(
|
||||
const shortcutKeysLogout = defineModel<boolean>('shortcutKeysLogout');
|
||||
// const shortcutKeysPreferences = defineModel<boolean>('shortcutKeysPreferences');
|
||||
const shortcutKeysLockScreen = defineModel<boolean>('shortcutKeysLockScreen');
|
||||
const shortcutKeysEscape = defineModel<boolean>('shortcutKeysEscape');
|
||||
|
||||
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||
</script>
|
||||
@@ -47,4 +48,8 @@ const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||
{{ $t('ui.widgets.lockScreen.title') }}
|
||||
<template #shortcut> {{ altView }} L </template>
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="shortcutKeysEscape" :disabled="!shortcutKeysEnable">
|
||||
{{ $t('preferences.shortcutKeys.escape') }}
|
||||
<template #shortcut> Esc </template>
|
||||
</SwitchItem>
|
||||
</template>
|
||||
|
||||
@@ -166,6 +166,9 @@ const shortcutKeysGlobalSearch = defineModel<boolean>(
|
||||
const shortcutKeysGlobalLogout = defineModel<boolean>(
|
||||
'shortcutKeysGlobalLogout',
|
||||
);
|
||||
const shortcutKeysGlobalEscape = defineModel<boolean>(
|
||||
'shortcutKeysGlobalEscape',
|
||||
);
|
||||
|
||||
const shortcutKeysGlobalLockScreen = defineModel<boolean>(
|
||||
'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"
|
||||
/>
|
||||
</Block>
|
||||
</template>
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
"global": "Global",
|
||||
"search": "Global Search",
|
||||
"logout": "Logout",
|
||||
"escape": "Close Current Window",
|
||||
"preferences": "Preferences"
|
||||
},
|
||||
"widget": {
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
"global": "全局",
|
||||
"search": "全局搜索",
|
||||
"logout": "退出登录",
|
||||
"escape": "关闭当前窗口",
|
||||
"preferences": "偏好设置"
|
||||
},
|
||||
"widget": {
|
||||
|
||||
Reference in New Issue
Block a user