feat: 顶栏与用户下拉框按钮样式统一与 tooltip 修复
- 修复 VbenTooltip 的 :content prop 类型错误,改用默认插槽 - 修复 VbenIconButton 的 onClick prop 支持数组合并 - VbenFullScreen 新增 tooltip prop,支持直接设置提示 - 统一顶栏与用户下拉框所有按钮样式,使用 VbenIconButton 包装 - 移除 i18n 中多余的"启用"字样(全局搜索/语言切换) - 修复 VbenPopover PopoverTrigger 嵌套 button 导致 tooltip 不显示 Co-Authored-By: AtomCode (GLM-5.2) <noreply@atomgit.com>
This commit is contained in:
@@ -24,10 +24,16 @@ defineOptions({
|
||||
});
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ enableShortcutKey?: boolean; menus?: MenuRecordRaw[] }>(),
|
||||
defineProps<{
|
||||
enableShortcutKey?: boolean;
|
||||
menus?: MenuRecordRaw[];
|
||||
/** 是否显示触发按钮(嵌在 dropdown 等容器里时可关掉) */
|
||||
showButton?: boolean;
|
||||
}>(),
|
||||
{
|
||||
enableShortcutKey: true,
|
||||
menus: () => [],
|
||||
showButton: true,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -94,6 +100,13 @@ onMounted(() => {
|
||||
window.removeEventListener('keydown', preventDefaultBrowserSearchHotKey);
|
||||
});
|
||||
});
|
||||
|
||||
// 暴露给父组件,允许从外部触发搜索弹框(如 user-dropdown 里点击菜单项触发)
|
||||
defineExpose({
|
||||
open: () => modalApi.open(),
|
||||
close: () => modalApi.close(),
|
||||
toggle: toggleOpen,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -135,6 +148,7 @@ onMounted(() => {
|
||||
</template>
|
||||
</Modal>
|
||||
<div
|
||||
v-if="showButton"
|
||||
class="group flex h-8 cursor-pointer items-center gap-3 rounded-2xl border-none bg-none px-2 py-0.5 outline-hidden md:bg-accent"
|
||||
@click="toggleOpen()"
|
||||
>
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectOption } from '@vben/types';
|
||||
|
||||
import { computed, nextTick, onMounted, ref } from 'vue';
|
||||
|
||||
import { GripVertical } from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
interface Item {
|
||||
key: string;
|
||||
label: string;
|
||||
position: 'auto' | 'fixed' | 'header' | 'none' | 'user-dropdown';
|
||||
/** 每项可选的 position 选项;不传则用 props.positionItems */
|
||||
positionItems?: SelectOption[];
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
items: Item[];
|
||||
positionItems: SelectOption[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
updateOrder: [keys: string[]];
|
||||
updatePosition: [
|
||||
key: string,
|
||||
position: 'auto' | 'fixed' | 'header' | 'none' | 'user-dropdown',
|
||||
];
|
||||
}>();
|
||||
|
||||
const listRef = ref<HTMLElement>();
|
||||
let sortableInstance: null | Sortable = null;
|
||||
|
||||
// 划分规则:
|
||||
// - hidden:position === 'none'(不显示)
|
||||
// - sortable:其它(含 auto/fixed,偏好按钮的智能模式视觉归入顶栏组参与排序,
|
||||
// 避免 preferences 从 widget.order 丢失)
|
||||
const sortableList = computed(() =>
|
||||
props.items.filter((item) => item.position !== 'none'),
|
||||
);
|
||||
|
||||
const hiddenList = computed(() =>
|
||||
props.items.filter((item) => item.position === 'none'),
|
||||
);
|
||||
function initSortable() {
|
||||
if (!listRef.value) return;
|
||||
sortableInstance?.destroy();
|
||||
sortableInstance = Sortable.create(listRef.value, {
|
||||
animation: 200,
|
||||
handle: '.drag-handle',
|
||||
onEnd() {
|
||||
// Sortable 已经改了 DOM,但 sortableList computed 还是旧顺序。
|
||||
// 直接从 DOM 读 children 的 data-key 拿新顺序,再追加 hidden 部分。
|
||||
const newOrder = [...listRef.value!.children]
|
||||
.map((el) => (el as HTMLElement).dataset.key)
|
||||
.filter(Boolean) as string[];
|
||||
emit('updateOrder', [...newOrder, ...hiddenList.value.map((i) => i.key)]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(initSortable);
|
||||
|
||||
function setPosition(key: string, event: Event) {
|
||||
const value = (event.target as HTMLSelectElement).value as
|
||||
| 'auto'
|
||||
| 'fixed'
|
||||
| 'header'
|
||||
| 'none'
|
||||
| 'user-dropdown';
|
||||
emit('updatePosition', key, value);
|
||||
nextTick(() => {
|
||||
emit(
|
||||
'updateOrder',
|
||||
[...sortableList.value, ...hiddenList.value].map((i) => i.key),
|
||||
);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-1">
|
||||
<div ref="listRef" class="space-y-1">
|
||||
<div
|
||||
v-for="item in sortableList"
|
||||
:key="item.key"
|
||||
:data-key="item.key"
|
||||
class="bg-accent flex items-center gap-2 rounded-md px-2 py-1.5"
|
||||
>
|
||||
<GripVertical
|
||||
class="drag-handle size-4 shrink-0 cursor-grab text-muted-foreground active:cursor-grabbing"
|
||||
/>
|
||||
<span class="min-w-0 flex-1 truncate text-sm">{{ item.label }}</span>
|
||||
<select
|
||||
:value="item.position"
|
||||
class="bg-background h-7 w-28 shrink-0 rounded border px-1 text-xs"
|
||||
@change="(e) => setPosition(item.key, e)"
|
||||
>
|
||||
<option
|
||||
v-for="opt in item.positionItems ?? positionItems"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hiddenList.length > 0" class="pt-2">
|
||||
<div class="text-muted-foreground mb-1 text-xs font-medium">
|
||||
{{ $t('preferences.widget.hidden') }}
|
||||
</div>
|
||||
<div
|
||||
v-for="item in hiddenList"
|
||||
:key="item.key"
|
||||
class="flex items-center gap-2 rounded-md px-2 py-1"
|
||||
>
|
||||
<span
|
||||
class="text-muted-foreground min-w-0 flex-1 truncate text-sm line-through decoration-dotted"
|
||||
>
|
||||
{{ item.label }}
|
||||
</span>
|
||||
<select
|
||||
:value="item.position"
|
||||
class="bg-background h-7 w-28 shrink-0 rounded border px-1 text-xs"
|
||||
@change="(e) => setPosition(item.key, e)"
|
||||
>
|
||||
<option
|
||||
v-for="opt in item.positionItems ?? positionItems"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,16 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectOption } from '@vben/types';
|
||||
import type { PreferencesButtonPositionType, SelectOption } from '@vben/types';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SelectItem from '../select-item.vue';
|
||||
import DraggableList from '../draggable-list.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceInterfaceControl',
|
||||
});
|
||||
|
||||
const widgetOrder = defineModel<string[]>('widgetOrder', { required: true });
|
||||
|
||||
const widgetGlobalSearchButtonPosition = defineModel<string>(
|
||||
'widgetGlobalSearchButtonPosition',
|
||||
);
|
||||
@@ -32,17 +34,37 @@ const widgetLockScreenButtonPosition = defineModel<string>(
|
||||
const widgetLogoutButtonPosition = defineModel<string>(
|
||||
'widgetLogoutButtonPosition',
|
||||
);
|
||||
const appPreferencesButtonPosition = defineModel<string>(
|
||||
'appPreferencesButtonPosition',
|
||||
);
|
||||
const widgetRefreshButtonPosition = defineModel<string>(
|
||||
'widgetRefreshButtonPosition',
|
||||
);
|
||||
const widgetTimezoneButtonPosition = defineModel<string>(
|
||||
'widgetTimezoneButtonPosition',
|
||||
);
|
||||
const appPreferencesButtonPosition = defineModel<PreferencesButtonPositionType>(
|
||||
'appPreferencesButtonPosition',
|
||||
);
|
||||
|
||||
const positionItems = computed((): SelectOption[] => [
|
||||
const buttonPositionItems = computed((): SelectOption[] => [
|
||||
{
|
||||
label: $t('preferences.widget.header'),
|
||||
value: 'header',
|
||||
},
|
||||
{
|
||||
label: $t('preferences.widget.userDropdown'),
|
||||
value: 'user-dropdown',
|
||||
},
|
||||
{
|
||||
label: $t('common.notShow'),
|
||||
value: 'none',
|
||||
},
|
||||
]);
|
||||
|
||||
/**
|
||||
* preferences 按钮独享的位置选项:
|
||||
* 保留 auto/fixed 让 use-preferences.ts 的智能 fallback 生效
|
||||
* (移动端/全屏内容模式下自动切换 header/fixed,避免偏好入口死锁)
|
||||
*/
|
||||
const preferencesPositionItems = computed((): SelectOption[] => [
|
||||
{
|
||||
label: $t('preferences.position.auto'),
|
||||
value: 'auto',
|
||||
@@ -59,77 +81,99 @@ const positionItems = computed((): SelectOption[] => [
|
||||
label: $t('preferences.position.userDropdown'),
|
||||
value: 'user-dropdown',
|
||||
},
|
||||
]);
|
||||
|
||||
const buttonPositionItems = computed((): SelectOption[] => [
|
||||
{
|
||||
label: $t('preferences.widget.header'),
|
||||
value: 'header',
|
||||
},
|
||||
{
|
||||
label: $t('preferences.widget.userDropdown'),
|
||||
value: 'user-dropdown',
|
||||
},
|
||||
{
|
||||
label: $t('common.notShow'),
|
||||
value: 'none',
|
||||
},
|
||||
]);
|
||||
|
||||
const positionMap: Record<string, string> = {
|
||||
globalSearch: 'widgetGlobalSearchButtonPosition',
|
||||
preferences: 'appPreferencesButtonPosition',
|
||||
themeToggle: 'widgetThemeToggleButtonPosition',
|
||||
languageToggle: 'widgetLanguageToggleButtonPosition',
|
||||
timezone: 'widgetTimezoneButtonPosition',
|
||||
fullscreen: 'widgetFullscreenButtonPosition',
|
||||
notification: 'widgetNotificationButtonPosition',
|
||||
lockScreenBtn: 'widgetLockScreenButtonPosition',
|
||||
logoutBtn: 'widgetLogoutButtonPosition',
|
||||
refresh: 'widgetRefreshButtonPosition',
|
||||
};
|
||||
|
||||
const labelMap: Record<string, string> = {
|
||||
globalSearch: 'preferences.widget.globalSearch',
|
||||
preferences: 'preferences.title',
|
||||
themeToggle: 'preferences.widget.themeToggle',
|
||||
languageToggle: 'preferences.widget.languageToggle',
|
||||
timezone: 'preferences.widget.timezone',
|
||||
fullscreen: 'preferences.widget.fullscreen',
|
||||
notification: 'preferences.widget.notification',
|
||||
lockScreenBtn: 'ui.widgets.lockScreen.title',
|
||||
logoutBtn: 'common.logout',
|
||||
refresh: 'preferences.widget.refresh',
|
||||
};
|
||||
|
||||
const draggableItems = computed(() =>
|
||||
(widgetOrder.value ?? []).map((key) => ({
|
||||
key,
|
||||
label: $t(labelMap[key] ?? key),
|
||||
position: getPosition(key),
|
||||
positionItems:
|
||||
key === 'preferences' ? preferencesPositionItems.value : undefined,
|
||||
})),
|
||||
);
|
||||
|
||||
function getPosition(
|
||||
key: string,
|
||||
): 'auto' | 'fixed' | 'header' | 'none' | 'user-dropdown' {
|
||||
const modelName = positionMap[key];
|
||||
if (!modelName) return 'none';
|
||||
const modelMap: Record<string, any> = {
|
||||
widgetGlobalSearchButtonPosition,
|
||||
widgetFullscreenButtonPosition,
|
||||
widgetLanguageToggleButtonPosition,
|
||||
widgetNotificationButtonPosition,
|
||||
widgetThemeToggleButtonPosition,
|
||||
widgetLockScreenButtonPosition,
|
||||
widgetLogoutButtonPosition,
|
||||
widgetRefreshButtonPosition,
|
||||
widgetTimezoneButtonPosition,
|
||||
appPreferencesButtonPosition,
|
||||
};
|
||||
return modelMap[modelName]?.value ?? 'none';
|
||||
}
|
||||
|
||||
function handleUpdateOrder(keys: string[]) {
|
||||
widgetOrder.value = keys;
|
||||
}
|
||||
|
||||
function handleUpdatePosition(
|
||||
key: string,
|
||||
position: 'auto' | 'fixed' | 'header' | 'none' | 'user-dropdown',
|
||||
) {
|
||||
const modelName = positionMap[key];
|
||||
if (!modelName) return;
|
||||
const modelMap: Record<string, any> = {
|
||||
widgetGlobalSearchButtonPosition,
|
||||
widgetFullscreenButtonPosition,
|
||||
widgetLanguageToggleButtonPosition,
|
||||
widgetNotificationButtonPosition,
|
||||
widgetThemeToggleButtonPosition,
|
||||
widgetLockScreenButtonPosition,
|
||||
widgetLogoutButtonPosition,
|
||||
widgetRefreshButtonPosition,
|
||||
widgetTimezoneButtonPosition,
|
||||
appPreferencesButtonPosition,
|
||||
};
|
||||
modelMap[modelName].value = position;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem
|
||||
v-model="widgetGlobalSearchButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.globalSearchPosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetThemeToggleButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.themeTogglePosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetLanguageToggleButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.languageTogglePosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetFullscreenButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.fullscreenPosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetNotificationButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.notificationPosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetLockScreenButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.lockScreenPosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem v-model="widgetLogoutButtonPosition" :items="buttonPositionItems">
|
||||
{{ $t('preferences.widget.logoutButtonPosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetRefreshButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.refreshPosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-model="widgetTimezoneButtonPosition"
|
||||
:items="buttonPositionItems"
|
||||
>
|
||||
{{ $t('preferences.widget.timezonePosition') }}
|
||||
</SelectItem>
|
||||
<SelectItem v-model="appPreferencesButtonPosition" :items="positionItems">
|
||||
{{ $t('preferences.position.title') }}
|
||||
</SelectItem>
|
||||
<DraggableList
|
||||
:items="draggableItems"
|
||||
:position-items="buttonPositionItems"
|
||||
@update-order="handleUpdateOrder"
|
||||
@update-position="handleUpdatePosition"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -195,6 +195,7 @@ const widgetLockScreenButtonPosition = defineModel<string>(
|
||||
const widgetLogoutButtonPosition = defineModel<string>(
|
||||
'widgetLogoutButtonPosition',
|
||||
);
|
||||
const widgetOrder = defineModel<string[]>('widgetOrder', { required: true });
|
||||
const widgetRefreshButtonPosition = defineModel<string>(
|
||||
'widgetRefreshButtonPosition',
|
||||
);
|
||||
@@ -516,6 +517,7 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
|
||||
v-model:widget-logout-button-position="
|
||||
widgetLogoutButtonPosition
|
||||
"
|
||||
v-model:widget-order="widgetOrder"
|
||||
v-model:widget-notification-button-position="
|
||||
widgetNotificationButtonPosition
|
||||
"
|
||||
|
||||
@@ -3,23 +3,24 @@ import type { Component } from 'vue';
|
||||
|
||||
import type { AnyFunction } from '@vben/types';
|
||||
|
||||
import { computed, useTemplateRef, watch } from 'vue';
|
||||
import { computed, ref, useTemplateRef, watch } from 'vue';
|
||||
|
||||
import { useHoverToggle } from '@vben/hooks';
|
||||
import { SUPPORT_LANGUAGES } from '@vben/constants';
|
||||
import { useHoverToggle, useRefresh } from '@vben/hooks';
|
||||
import {
|
||||
Bell,
|
||||
Fullscreen,
|
||||
Languages,
|
||||
LockKeyhole,
|
||||
LogOut,
|
||||
RotateCw,
|
||||
Search,
|
||||
Settings,
|
||||
Sun,
|
||||
SunMoon,
|
||||
} from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
import { preferences, usePreferences } from '@vben/preferences';
|
||||
import { $t, loadLocaleMessages } from '@vben/locales';
|
||||
import {
|
||||
preferences,
|
||||
updatePreferences,
|
||||
usePreferences,
|
||||
} from '@vben/preferences';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { isWindowsOs } from '@vben/utils';
|
||||
|
||||
@@ -34,13 +35,19 @@ import {
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
VbenAvatar,
|
||||
VbenFullScreen,
|
||||
VbenIcon,
|
||||
VbenIconButton,
|
||||
} from '@vben-core/shadcn-ui';
|
||||
|
||||
import { useMagicKeys, whenever } from '@vueuse/core';
|
||||
|
||||
import { GlobalSearch } from '../global-search';
|
||||
import { LockScreenModal } from '../lock-screen';
|
||||
import { Notification } from '../notification';
|
||||
import { Preferences } from '../preferences';
|
||||
import { ThemeToggle } from '../theme-toggle';
|
||||
import { TimezoneButton } from '../timezone';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
@@ -91,10 +98,12 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
const emit = defineEmits<{ clearPreferencesAndLogout: []; logout: [] }>();
|
||||
|
||||
const {
|
||||
globalLockScreenShortcutKey,
|
||||
globalLogoutShortcutKey,
|
||||
globalLockScreenShortcutKey,
|
||||
globalSearchShortcutKey,
|
||||
preferencesButtonPosition,
|
||||
} = usePreferences();
|
||||
const { refresh } = useRefresh();
|
||||
const accessStore = useAccessStore();
|
||||
const [LockModal, lockModalApi] = useVbenModal({
|
||||
connectedComponent: LockScreenModal,
|
||||
@@ -108,6 +117,7 @@ const [LogoutModal, logoutModalApi] = useVbenModal({
|
||||
const refTrigger = useTemplateRef('refTrigger');
|
||||
const refContent = useTemplateRef('refContent');
|
||||
const refPreferences = useTemplateRef('refPreferences');
|
||||
const refGlobalSearch = useTemplateRef('refGlobalSearch');
|
||||
const [openPopover, hoverWatcher] = useHoverToggle(
|
||||
[refTrigger, refContent],
|
||||
() => props.hoverDelay,
|
||||
@@ -233,6 +243,32 @@ function handleOpenSettings() {
|
||||
refPreferences.value?.open();
|
||||
}
|
||||
|
||||
// 刷新当前页面
|
||||
function handleRefresh() {
|
||||
openPopover.value = false;
|
||||
refresh();
|
||||
}
|
||||
|
||||
// 搜索 - 不关闭 dropdown,直接触发 GlobalSearch 组件的弹框
|
||||
function handleGlobalSearch() {
|
||||
refGlobalSearch.value?.open();
|
||||
}
|
||||
|
||||
// 语言切换 - 阻止 Radix 默认关闭外层 dropdown,就地展开/收起 locale 列表
|
||||
const showLanguageList = ref(false);
|
||||
function handleLanguageToggleSelect(event?: Event) {
|
||||
event?.preventDefault();
|
||||
showLanguageList.value = !showLanguageList.value;
|
||||
}
|
||||
async function handleLocaleChange(event: Event, value: 'en-US' | 'zh-CN') {
|
||||
// 阻止默认关闭,让用户能继续看到选择结果;选完手动收起
|
||||
event.preventDefault();
|
||||
updatePreferences({ app: { locale: value } });
|
||||
await loadLocaleMessages(value);
|
||||
showLanguageList.value = false;
|
||||
openPopover.value = false;
|
||||
}
|
||||
|
||||
if (enableShortcutKey.value) {
|
||||
const keys = useMagicKeys();
|
||||
const logoutKey = keys['Alt+KeyQ'];
|
||||
@@ -285,6 +321,14 @@ if (enableShortcutKey.value) {
|
||||
@clear-preferences-and-logout="emit('clearPreferencesAndLogout')"
|
||||
/>
|
||||
|
||||
<GlobalSearch
|
||||
v-if="showGlobalSearchInDropdown"
|
||||
ref="refGlobalSearch"
|
||||
:enable-shortcut-key="globalSearchShortcutKey"
|
||||
:menus="accessStore.accessMenus"
|
||||
:show-button="false"
|
||||
/>
|
||||
|
||||
<DropdownMenu v-model:open="openPopover" :modal="false">
|
||||
<DropdownMenuTrigger ref="refTrigger" :disabled="props.trigger === 'hover'">
|
||||
<div class="mr-2 ml-1 cursor-pointer rounded-full p-1.5 hover:bg-accent">
|
||||
@@ -331,7 +375,9 @@ if (enableShortcutKey.value) {
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@click="menu.handler"
|
||||
>
|
||||
<VbenIcon :icon="menu.icon" class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="menu.handler">
|
||||
<VbenIcon :icon="menu.icon" class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ menu.text }}
|
||||
</DropdownMenuItem>
|
||||
<template v-if="showLockInDropdown || showLogoutInDropdown">
|
||||
@@ -341,7 +387,9 @@ if (enableShortcutKey.value) {
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@click="handleOpenLock"
|
||||
>
|
||||
<LockKeyhole class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="handleOpenLock">
|
||||
<LockKeyhole class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('ui.widgets.lockScreen.title') }}
|
||||
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
|
||||
{{ altView }} L
|
||||
@@ -353,7 +401,9 @@ if (enableShortcutKey.value) {
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@click="handleLogout"
|
||||
>
|
||||
<LogOut class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="handleLogout">
|
||||
<LogOut class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('common.logout') }}
|
||||
<DropdownMenuShortcut v-if="enableLogoutShortcutKey">
|
||||
{{ altView }} Q
|
||||
@@ -375,50 +425,75 @@ if (enableShortcutKey.value) {
|
||||
<DropdownMenuItem
|
||||
v-if="showGlobalSearchInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@select="handleGlobalSearch"
|
||||
>
|
||||
<Search class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="handleGlobalSearch">
|
||||
<Search class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('preferences.widget.globalSearch') }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="showThemeToggleInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
>
|
||||
<Sun class="mr-2 size-4" />
|
||||
{{ $t('preferences.widget.themeToggle') }}
|
||||
<ThemeToggle class="mr-2" />
|
||||
{{ $t('preferences.theme.title') }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="showLanguageToggleInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@select="handleLanguageToggleSelect"
|
||||
>
|
||||
<Languages class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="handleLanguageToggleSelect">
|
||||
<Languages class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('preferences.widget.languageToggle') }}
|
||||
</DropdownMenuItem>
|
||||
<template v-if="showLanguageList">
|
||||
<DropdownMenuItem
|
||||
v-for="lang in SUPPORT_LANGUAGES"
|
||||
:key="lang.value"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 pl-8 leading-8"
|
||||
@select="(e: Event) => handleLocaleChange(e, lang.value)"
|
||||
>
|
||||
<span
|
||||
:class="
|
||||
lang.value === preferences.app.locale ? 'bg-foreground' : ''
|
||||
"
|
||||
class="mr-2 size-1.5 rounded-full"
|
||||
></span>
|
||||
{{ lang.label }}
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
<DropdownMenuItem
|
||||
v-if="showTimezoneInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
>
|
||||
<SunMoon class="mr-2 size-4" />
|
||||
{{ $t('preferences.widget.timezone') }}
|
||||
<TimezoneButton class="mr-2" />
|
||||
{{ $t('ui.widgets.timezone.setTimezone') }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="showFullscreenInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
>
|
||||
<Fullscreen class="mr-2 size-4" />
|
||||
<VbenFullScreen class="mr-2" />
|
||||
{{ $t('preferences.widget.fullscreen') }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="showNotificationInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
>
|
||||
<Bell class="mr-2 size-4" />
|
||||
<Notification class="mr-2" />
|
||||
{{ $t('preferences.widget.notification') }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="showRefreshInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@click="handleRefresh"
|
||||
>
|
||||
<RotateCw class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="handleRefresh">
|
||||
<RotateCw class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('preferences.widget.refresh') }}
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
@@ -430,7 +505,9 @@ if (enableShortcutKey.value) {
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@click="handleOpenSettings"
|
||||
>
|
||||
<Settings class="mr-2 size-4" />
|
||||
<VbenIconButton class="mr-2" @click="handleOpenSettings">
|
||||
<Settings class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('preferences.title') }}
|
||||
</DropdownMenuItem>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user