feat: add shortcut keys
parent
666371ed67
commit
977d108ca0
|
@ -7,6 +7,8 @@ export const IcRoundKeyboardArrowDown = createIcon(
|
|||
);
|
||||
|
||||
export const IcRoundChevronRight = createIcon('ic:round-chevron-right');
|
||||
|
||||
export const IcRoundKeyboard = createIcon('ic:round-keyboard');
|
||||
// export const IcRoundMenuOpen = createIcon('ic:round-menu-open');
|
||||
|
||||
export const IcRoundMenu = createIcon('ic:round-menu');
|
||||
|
|
|
@ -91,6 +91,8 @@ interface Preference {
|
|||
pageTransitionEnable: boolean;
|
||||
/** 是否开启半深色菜单(只在theme='light'时生效) */
|
||||
semiDarkMenu: boolean;
|
||||
/** 是否启用快捷键 */
|
||||
shortcutKeys: boolean;
|
||||
/** 是否显示偏好设置 */
|
||||
showPreference: boolean;
|
||||
/** 侧边栏是否折叠 */
|
||||
|
|
|
@ -21,7 +21,7 @@ import { isWindowsOs } from '@vben-core/toolkit';
|
|||
|
||||
import { $t } from '@vben/locales';
|
||||
import { useMagicKeys, useToggle, whenever } from '@vueuse/core';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import SearchPanel from './search-panel.vue';
|
||||
|
||||
|
@ -29,9 +29,13 @@ defineOptions({
|
|||
name: 'GlobalSearch',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<{ menus: MenuRecordRaw[] }>(), {
|
||||
menus: () => [],
|
||||
});
|
||||
const props = withDefaults(
|
||||
defineProps<{ enableShortcutKey?: boolean; menus: MenuRecordRaw[] }>(),
|
||||
{
|
||||
enableShortcutKey: true,
|
||||
menus: () => [],
|
||||
},
|
||||
);
|
||||
|
||||
const [open, toggleOpen] = useToggle();
|
||||
const keyword = ref('');
|
||||
|
@ -41,13 +45,15 @@ function handleClose() {
|
|||
keyword.value = '';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.enableShortcutKey) {
|
||||
const keys = useMagicKeys();
|
||||
const cmd = isWindowsOs() ? keys['ctrl+k'] : keys['cmd+k'];
|
||||
whenever(cmd, () => {
|
||||
open.value = true;
|
||||
if (props.enableShortcutKey) {
|
||||
open.value = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -67,11 +73,13 @@ onMounted(() => {
|
|||
{{ $t('search.search') }}
|
||||
</span>
|
||||
<span
|
||||
v-if="enableShortcutKey"
|
||||
class="bg-background border-foreground/50 text-muted-foreground group-hover:text-foreground relative hidden rounded-sm rounded-r-xl px-1.5 py-1 text-xs leading-none group-hover:opacity-100 md:block"
|
||||
>
|
||||
{{ isWindowsOs() ? 'Ctrl' : '⌘' }}
|
||||
<kbd>K</kbd>
|
||||
</span>
|
||||
<span v-else></span>
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<DialogContent
|
||||
|
|
|
@ -13,6 +13,7 @@ defineOptions({
|
|||
|
||||
const locale = defineModel<string>('locale');
|
||||
const dynamicTitle = defineModel<boolean>('dynamicTitle');
|
||||
const shortcutKeys = defineModel<boolean>('shortcutKeys');
|
||||
|
||||
const localeItems: SelectListItem[] = staticPreference.supportLanguages.map(
|
||||
(item) => ({
|
||||
|
@ -29,4 +30,7 @@ const localeItems: SelectListItem[] = staticPreference.supportLanguages.map(
|
|||
<SwitchItem v-model="dynamicTitle">
|
||||
{{ $t('preference.dynamic-title') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="shortcutKeys">
|
||||
{{ $t('preference.shortcut-key') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
|
|
|
@ -54,9 +54,11 @@ function updateLocale(value: string) {
|
|||
:locale="preference.locale"
|
||||
:navigation-accordion="preference.navigationAccordion"
|
||||
:navigation-style="preference.navigationStyle"
|
||||
:shortcut-keys="preference.shortcutKeys"
|
||||
:navigation-split="preference.navigationSplit"
|
||||
:side-collapse-show-title="preference.sideCollapseShowTitle"
|
||||
:page-transition-enable="preference.pageTransitionEnable"
|
||||
@update:shortcut-keys="(value) => handleUpdate('shortcutKeys', value)"
|
||||
@update:navigation-style="(value) => handleUpdate('navigationStyle', value)"
|
||||
@update:navigation-accordion="
|
||||
(value) => handleUpdate('navigationAccordion', value)
|
||||
|
|
|
@ -62,6 +62,7 @@ const pageTransitionEnable = defineModel<boolean>('pageTransitionEnable');
|
|||
const layout = defineModel<LayoutType>('layout');
|
||||
const contentCompact = defineModel<string>('contentCompact');
|
||||
const sideVisible = defineModel<boolean>('sideVisible');
|
||||
const shortcutKeys = defineModel<boolean>('shortcutKeys');
|
||||
const tabsVisible = defineModel<boolean>('tabsVisible');
|
||||
const tabsIcon = defineModel<boolean>('tabsIcon');
|
||||
// const logoVisible = defineModel<boolean>('logoVisible');
|
||||
|
@ -95,6 +96,10 @@ const tabs = computed((): SegmentedItem[] => {
|
|||
label: $t('preference.general'),
|
||||
value: 'general',
|
||||
},
|
||||
// {
|
||||
// label: $t('preference.shortcut-key'),
|
||||
// value: 'shortcutKey',
|
||||
// },
|
||||
];
|
||||
});
|
||||
|
||||
|
@ -233,6 +238,24 @@ function handleReset() {
|
|||
<General
|
||||
v-model:locale="locale"
|
||||
v-model:dynamic-title="dynamicTitle"
|
||||
v-model:shortcut-keys="shortcutKeys"
|
||||
/>
|
||||
</Block>
|
||||
|
||||
<Block :title="$t('preference.animation')">
|
||||
<Animation
|
||||
v-model:page-progress="pageProgress"
|
||||
v-model:page-transition="pageTransition"
|
||||
v-model:page-transition-enable="pageTransitionEnable"
|
||||
/>
|
||||
</Block>
|
||||
</template>
|
||||
<template #shortcutKey>
|
||||
<Block :title="$t('preference.general')">
|
||||
<General
|
||||
v-model:locale="locale"
|
||||
v-model:dynamic-title="dynamicTitle"
|
||||
v-model:shortcut-keys="shortcutKeys"
|
||||
/>
|
||||
</Block>
|
||||
|
||||
|
|
|
@ -9,17 +9,20 @@ import {
|
|||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
VbenAlertDialog,
|
||||
VbenAvatar,
|
||||
VbenIcon,
|
||||
} from '@vben-core/shadcn-ui';
|
||||
import { isWindowsOs } from '@vben-core/toolkit';
|
||||
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
import { preference } from '@vben/preference';
|
||||
import { ref } from 'vue';
|
||||
import { useMagicKeys, whenever } from '@vueuse/core';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useOpenPreference } from '../preference/use-open-preference';
|
||||
|
||||
|
@ -32,15 +35,19 @@ interface Props {
|
|||
* @zh_CN 描述
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* 是否启用快捷键
|
||||
*/
|
||||
enableShortcutKey?: boolean;
|
||||
/**
|
||||
* 菜单数组
|
||||
*/
|
||||
menus?: Array<{ handler: AnyFunction; icon?: Component; text: string }>;
|
||||
|
||||
/**
|
||||
* 标签文本
|
||||
*/
|
||||
tagText?: string;
|
||||
|
||||
/**
|
||||
* 文本
|
||||
*/
|
||||
|
@ -51,10 +58,12 @@ defineOptions({
|
|||
name: 'UserDropdown',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
avatar: '',
|
||||
description: '',
|
||||
enableShortcutKey: true,
|
||||
menus: () => [],
|
||||
showShortcutKey: true,
|
||||
tagText: '',
|
||||
text: '',
|
||||
});
|
||||
|
@ -65,6 +74,12 @@ const openDialog = ref(false);
|
|||
|
||||
const { handleOpenPreference } = useOpenPreference();
|
||||
|
||||
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||
|
||||
const shortcutKeys = computed(() => {
|
||||
return props.enableShortcutKey && preference.shortcutKeys;
|
||||
});
|
||||
|
||||
function handleLogout() {
|
||||
// emit
|
||||
openDialog.value = true;
|
||||
|
@ -75,6 +90,21 @@ function handleSubmitLogout() {
|
|||
emit('logout');
|
||||
openDialog.value = false;
|
||||
}
|
||||
|
||||
if (shortcutKeys.value) {
|
||||
const keys = useMagicKeys();
|
||||
whenever(keys['Alt+KeyQ'], () => {
|
||||
if (shortcutKeys.value) {
|
||||
handleLogout();
|
||||
}
|
||||
});
|
||||
|
||||
whenever(keys['Alt+Comma'], () => {
|
||||
if (shortcutKeys.value) {
|
||||
handleOpenPreference();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -137,6 +167,9 @@ function handleSubmitLogout() {
|
|||
>
|
||||
<IcRoundSettingsSuggest class="mr-2 size-5" />
|
||||
{{ $t('preference.preferences') }}
|
||||
<DropdownMenuShortcut v-if="shortcutKeys">
|
||||
{{ altView }} ,
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
|
@ -145,6 +178,9 @@ function handleSubmitLogout() {
|
|||
>
|
||||
<IcRoundLogout class="mr-2 size-5" />
|
||||
{{ $t('common.logout') }}
|
||||
<DropdownMenuShortcut v-if="shortcutKeys">
|
||||
{{ altView }} Q
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { VbenFullScreen } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { GlobalSearch, LanguageToggle, ThemeToggle } from '@vben/common-ui';
|
||||
import { preference } from '@vben/preference';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
interface Props {
|
||||
|
@ -30,7 +31,11 @@ const accessStore = useAccessStore();
|
|||
<slot name="menu"></slot>
|
||||
</div>
|
||||
<div class="flex h-full min-w-0 flex-shrink-0 items-center">
|
||||
<GlobalSearch class="mr-4" :menus="accessStore.getAccessMenus" />
|
||||
<GlobalSearch
|
||||
class="mr-4"
|
||||
:enable-shortcut-key="preference.shortcutKeys"
|
||||
:menus="accessStore.getAccessMenus"
|
||||
/>
|
||||
<ThemeToggle class="mr-2" />
|
||||
<LanguageToggle class="mr-2" />
|
||||
<VbenFullScreen class="mr-2" />
|
||||
|
|
|
@ -42,6 +42,7 @@ preference:
|
|||
preferences: Preferences
|
||||
preferences-subtitle: Customize Preferences & Preview in Real Time
|
||||
theme: Theme
|
||||
shortcut-key: Shortcut Key
|
||||
appearance: Appearance
|
||||
theme-color: Theme Color
|
||||
layout: Layout
|
||||
|
|
|
@ -40,6 +40,7 @@ search:
|
|||
preference:
|
||||
preferences: 偏好设置
|
||||
preferences-subtitle: 自定义偏好设置 & 实时预览
|
||||
shortcut-key: 快捷键
|
||||
theme: 主题
|
||||
appearance: 外观
|
||||
theme-color: 主题色
|
||||
|
|
|
@ -34,6 +34,7 @@ const defaultPreference: Preference = {
|
|||
pageTransition: 'fade-slide',
|
||||
pageTransitionEnable: true,
|
||||
semiDarkMenu: true,
|
||||
shortcutKeys: true,
|
||||
showPreference: true,
|
||||
sideCollapse: false,
|
||||
sideCollapseShowTitle: true,
|
||||
|
|
|
@ -609,6 +609,9 @@ importers:
|
|||
'@vben-core/toolkit':
|
||||
specifier: workspace:*
|
||||
version: link:../../@vben-core/shared/toolkit
|
||||
'@vben/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../../constants
|
||||
'@vben/locales':
|
||||
specifier: workspace:*
|
||||
version: link:../../locales
|
||||
|
@ -681,7 +684,11 @@ importers:
|
|||
specifier: workspace:*
|
||||
version: link:../../@vben-core/shared/typings
|
||||
|
||||
packages/constants: {}
|
||||
packages/constants:
|
||||
dependencies:
|
||||
'@vben-core/toolkit':
|
||||
specifier: workspace:*
|
||||
version: link:../@vben-core/shared/toolkit
|
||||
|
||||
packages/hooks:
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in New Issue