feat: 顶栏按钮支持位置选择(header/user-dropdown/none)
为顶栏所有部件按钮添加独立的位置配置,支持在顶栏、用户下拉菜单、不显示 三种选项间切换,每个按钮可独立设置。 - 新增 7 个位置偏好字段:globalSearchButtonPosition、 themeToggleButtonPosition、languageToggleButtonPosition、 fullscreenButtonPosition、notificationButtonPosition、 refreshButtonPosition、timezoneButtonPosition - 合并 lockScreenButtonPosition、logoutButtonPosition - header.vue 按位置条件过滤按钮显示 - user-dropdown.vue 新增对应菜单项,支持在用户下拉菜单中显示 - 偏好设置界面仅保留位置选择器(不再需要独立的开关) - 更新中英文国际化文案 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
This commit is contained in:
@@ -231,6 +231,7 @@ watch(
|
|||||||
description="ann.vben@gmail.com"
|
description="ann.vben@gmail.com"
|
||||||
tag-text="Pro"
|
tag-text="Pro"
|
||||||
@clear-preferences-and-logout="handleLogout"
|
@clear-preferences-and-logout="handleLogout"
|
||||||
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #notification>
|
<template #notification>
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ watch(
|
|||||||
description="ann.vben@gmail.com"
|
description="ann.vben@gmail.com"
|
||||||
tag-text="Pro"
|
tag-text="Pro"
|
||||||
@clear-preferences-and-logout="handleLogout"
|
@clear-preferences-and-logout="handleLogout"
|
||||||
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #notification>
|
<template #notification>
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ watch(
|
|||||||
description="ann.vben@gmail.com"
|
description="ann.vben@gmail.com"
|
||||||
tag-text="Pro"
|
tag-text="Pro"
|
||||||
@clear-preferences-and-logout="handleLogout"
|
@clear-preferences-and-logout="handleLogout"
|
||||||
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #notification>
|
<template #notification>
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ watch(
|
|||||||
description="ann.vben@gmail.com"
|
description="ann.vben@gmail.com"
|
||||||
tag-text="Pro"
|
tag-text="Pro"
|
||||||
@clear-preferences-and-logout="handleLogout"
|
@clear-preferences-and-logout="handleLogout"
|
||||||
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #notification>
|
<template #notification>
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ watch(
|
|||||||
description="ann.vben@gmail.com"
|
description="ann.vben@gmail.com"
|
||||||
tag-text="Pro"
|
tag-text="Pro"
|
||||||
@clear-preferences-and-logout="handleLogout"
|
@clear-preferences-and-logout="handleLogout"
|
||||||
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #notification>
|
<template #notification>
|
||||||
|
|||||||
@@ -137,14 +137,23 @@ const defaultPreferences: Preferences = {
|
|||||||
},
|
},
|
||||||
widget: {
|
widget: {
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
|
fullscreenButtonPosition: 'header',
|
||||||
globalSearch: true,
|
globalSearch: true,
|
||||||
|
globalSearchButtonPosition: 'header',
|
||||||
languageToggle: true,
|
languageToggle: true,
|
||||||
|
languageToggleButtonPosition: 'header',
|
||||||
lockScreen: true,
|
lockScreen: true,
|
||||||
|
lockScreenButtonPosition: 'header',
|
||||||
|
logoutButtonPosition: 'header',
|
||||||
notification: true,
|
notification: true,
|
||||||
|
notificationButtonPosition: 'header',
|
||||||
refresh: true,
|
refresh: true,
|
||||||
|
refreshButtonPosition: 'header',
|
||||||
sidebarToggle: true,
|
sidebarToggle: true,
|
||||||
themeToggle: true,
|
themeToggle: true,
|
||||||
|
themeToggleButtonPosition: 'header',
|
||||||
timezone: true,
|
timezone: true,
|
||||||
|
timezoneButtonPosition: 'header',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -365,22 +365,40 @@ interface TransitionPreferences {
|
|||||||
interface WidgetPreferences {
|
interface WidgetPreferences {
|
||||||
/** 是否启用全屏部件 */
|
/** 是否启用全屏部件 */
|
||||||
fullscreen: boolean;
|
fullscreen: boolean;
|
||||||
|
/** 全屏按钮位置 */
|
||||||
|
fullscreenButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 是否启用全局搜索部件 */
|
/** 是否启用全局搜索部件 */
|
||||||
globalSearch: boolean;
|
globalSearch: boolean;
|
||||||
|
/** 全局搜索按钮位置 */
|
||||||
|
globalSearchButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 是否启用语言切换部件 */
|
/** 是否启用语言切换部件 */
|
||||||
languageToggle: boolean;
|
languageToggle: boolean;
|
||||||
|
/** 语言切换按钮位置 */
|
||||||
|
languageToggleButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 是否开启锁屏功能 */
|
/** 是否开启锁屏功能 */
|
||||||
lockScreen: boolean;
|
lockScreen: boolean;
|
||||||
|
/** 锁屏按钮位置 */
|
||||||
|
lockScreenButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
|
/** 退出登录按钮位置 */
|
||||||
|
logoutButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 是否显示通知部件 */
|
/** 是否显示通知部件 */
|
||||||
notification: boolean;
|
notification: boolean;
|
||||||
|
/** 通知按钮位置 */
|
||||||
|
notificationButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 显示刷新按钮 */
|
/** 显示刷新按钮 */
|
||||||
refresh: boolean;
|
refresh: boolean;
|
||||||
|
/** 刷新按钮位置 */
|
||||||
|
refreshButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 是否显示侧边栏显示/隐藏部件 */
|
/** 是否显示侧边栏显示/隐藏部件 */
|
||||||
sidebarToggle: boolean;
|
sidebarToggle: boolean;
|
||||||
/** 是否显示主题切换部件 */
|
/** 是否显示主题切换部件 */
|
||||||
themeToggle: boolean;
|
themeToggle: boolean;
|
||||||
|
/** 主题切换按钮位置 */
|
||||||
|
themeToggleButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
/** 是否显示时区部件 */
|
/** 是否显示时区部件 */
|
||||||
timezone: boolean;
|
timezone: boolean;
|
||||||
|
/** 时区按钮位置 */
|
||||||
|
timezoneButtonPosition: 'header' | 'none' | 'user-dropdown';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Preferences {
|
interface Preferences {
|
||||||
|
|||||||
@@ -122,7 +122,10 @@ type SlotItem = { index: number; name: string };
|
|||||||
const rightSlots = computed(() => {
|
const rightSlots = computed(() => {
|
||||||
const list: Array<SlotItem> = [];
|
const list: Array<SlotItem> = [];
|
||||||
// 全局搜索
|
// 全局搜索
|
||||||
if (preferences.widget.globalSearch) {
|
if (
|
||||||
|
preferences.widget.globalSearch &&
|
||||||
|
preferences.widget.globalSearchButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE,
|
index: REFERENCE_VALUE,
|
||||||
name: 'global-search',
|
name: 'global-search',
|
||||||
@@ -137,50 +140,70 @@ const rightSlots = computed(() => {
|
|||||||
}
|
}
|
||||||
// 主题、语言、时区等子功能由各自的 widget 开关独立控制,
|
// 主题、语言、时区等子功能由各自的 widget 开关独立控制,
|
||||||
// 不应跟随偏好设置按钮的显示与否(如 enablePreferences 为 false 或按钮位于其他位置时)
|
// 不应跟随偏好设置按钮的显示与否(如 enablePreferences 为 false 或按钮位于其他位置时)
|
||||||
if (preferences.widget.themeToggle) {
|
if (
|
||||||
|
preferences.widget.themeToggle &&
|
||||||
|
preferences.widget.themeToggleButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 20,
|
index: REFERENCE_VALUE + 20,
|
||||||
name: 'theme-toggle',
|
name: 'theme-toggle',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (preferences.widget.languageToggle) {
|
if (
|
||||||
|
preferences.widget.languageToggle &&
|
||||||
|
preferences.widget.languageToggleButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 30,
|
index: REFERENCE_VALUE + 30,
|
||||||
name: 'language-toggle',
|
name: 'language-toggle',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (preferences.widget.timezone) {
|
if (
|
||||||
|
preferences.widget.timezone &&
|
||||||
|
preferences.widget.timezoneButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 40,
|
index: REFERENCE_VALUE + 40,
|
||||||
name: 'timezone',
|
name: 'timezone',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 全屏
|
// 全屏
|
||||||
if (preferences.widget.fullscreen) {
|
if (
|
||||||
|
preferences.widget.fullscreen &&
|
||||||
|
preferences.widget.fullscreenButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 50,
|
index: REFERENCE_VALUE + 50,
|
||||||
name: 'fullscreen',
|
name: 'fullscreen',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 消息通知
|
// 消息通知
|
||||||
if (preferences.widget.notification) {
|
if (
|
||||||
|
preferences.widget.notification &&
|
||||||
|
preferences.widget.notificationButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 60,
|
index: REFERENCE_VALUE + 60,
|
||||||
name: 'notification',
|
name: 'notification',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 锁定屏幕
|
// 锁定屏幕(仅在 header 位置显示)
|
||||||
if (preferences.widget.lockScreen) {
|
if (
|
||||||
|
preferences.widget.lockScreen &&
|
||||||
|
preferences.widget.lockScreenButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 70,
|
index: REFERENCE_VALUE + 70,
|
||||||
name: 'lock-screen-btn',
|
name: 'lock-screen-btn',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 退出登录
|
// 退出登录(仅在 header 位置显示)
|
||||||
list.push({
|
if (preferences.widget.logoutButtonPosition === 'header') {
|
||||||
index: REFERENCE_VALUE + 80,
|
list.push({
|
||||||
name: 'logout-btn',
|
index: REFERENCE_VALUE + 80,
|
||||||
});
|
name: 'logout-btn',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(slots).forEach((key) => {
|
Object.keys(slots).forEach((key) => {
|
||||||
// 适配插槽名称,例如第一个插槽名:header-right-1
|
// 适配插槽名称,例如第一个插槽名:header-right-1
|
||||||
@@ -201,7 +224,10 @@ const rightSlots = computed(() => {
|
|||||||
const leftSlots = computed(() => {
|
const leftSlots = computed(() => {
|
||||||
const list: Array<SlotItem> = [];
|
const list: Array<SlotItem> = [];
|
||||||
// 刷新
|
// 刷新
|
||||||
if (preferences.widget.refresh) {
|
if (
|
||||||
|
preferences.widget.refresh &&
|
||||||
|
preferences.widget.refreshButtonPosition === 'header'
|
||||||
|
) {
|
||||||
list.push({
|
list.push({
|
||||||
index: 0,
|
index: 0,
|
||||||
name: 'refresh',
|
name: 'refresh',
|
||||||
|
|||||||
@@ -6,24 +6,41 @@ import { computed } from 'vue';
|
|||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import SelectItem from '../select-item.vue';
|
import SelectItem from '../select-item.vue';
|
||||||
import SwitchItem from '../switch-item.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'PreferenceInterfaceControl',
|
name: 'PreferenceInterfaceControl',
|
||||||
});
|
});
|
||||||
|
|
||||||
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
|
const widgetGlobalSearchButtonPosition = defineModel<string>(
|
||||||
const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
|
'widgetGlobalSearchButtonPosition',
|
||||||
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle');
|
);
|
||||||
const widgetNotification = defineModel<boolean>('widgetNotification');
|
const widgetFullscreenButtonPosition = defineModel<string>(
|
||||||
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
'widgetFullscreenButtonPosition',
|
||||||
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
);
|
||||||
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
|
const widgetLanguageToggleButtonPosition = defineModel<string>(
|
||||||
|
'widgetLanguageToggleButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetNotificationButtonPosition = defineModel<string>(
|
||||||
|
'widgetNotificationButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetThemeToggleButtonPosition = defineModel<string>(
|
||||||
|
'widgetThemeToggleButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetLockScreenButtonPosition = defineModel<string>(
|
||||||
|
'widgetLockScreenButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetLogoutButtonPosition = defineModel<string>(
|
||||||
|
'widgetLogoutButtonPosition',
|
||||||
|
);
|
||||||
const appPreferencesButtonPosition = defineModel<string>(
|
const appPreferencesButtonPosition = defineModel<string>(
|
||||||
'appPreferencesButtonPosition',
|
'appPreferencesButtonPosition',
|
||||||
);
|
);
|
||||||
const widgetRefresh = defineModel<boolean>('widgetRefresh');
|
const widgetRefreshButtonPosition = defineModel<string>(
|
||||||
const widgetTimezone = defineModel<boolean>('widgetTimezone');
|
'widgetRefreshButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetTimezoneButtonPosition = defineModel<string>(
|
||||||
|
'widgetTimezoneButtonPosition',
|
||||||
|
);
|
||||||
|
|
||||||
const positionItems = computed((): SelectOption[] => [
|
const positionItems = computed((): SelectOption[] => [
|
||||||
{
|
{
|
||||||
@@ -43,36 +60,75 @@ const positionItems = computed((): SelectOption[] => [
|
|||||||
value: 'user-dropdown',
|
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',
|
||||||
|
},
|
||||||
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SwitchItem v-model="widgetGlobalSearch">
|
<SelectItem
|
||||||
{{ $t('preferences.widget.globalSearch') }}
|
v-model="widgetGlobalSearchButtonPosition"
|
||||||
</SwitchItem>
|
:items="buttonPositionItems"
|
||||||
<SwitchItem v-model="widgetThemeToggle">
|
>
|
||||||
{{ $t('preferences.widget.themeToggle') }}
|
{{ $t('preferences.widget.globalSearchPosition') }}
|
||||||
</SwitchItem>
|
</SelectItem>
|
||||||
<SwitchItem v-model="widgetLanguageToggle">
|
<SelectItem
|
||||||
{{ $t('preferences.widget.languageToggle') }}
|
v-model="widgetThemeToggleButtonPosition"
|
||||||
</SwitchItem>
|
:items="buttonPositionItems"
|
||||||
<SwitchItem v-model="widgetFullscreen">
|
>
|
||||||
{{ $t('preferences.widget.fullscreen') }}
|
{{ $t('preferences.widget.themeTogglePosition') }}
|
||||||
</SwitchItem>
|
</SelectItem>
|
||||||
<SwitchItem v-model="widgetNotification">
|
<SelectItem
|
||||||
{{ $t('preferences.widget.notification') }}
|
v-model="widgetLanguageToggleButtonPosition"
|
||||||
</SwitchItem>
|
:items="buttonPositionItems"
|
||||||
<SwitchItem v-model="widgetLockScreen">
|
>
|
||||||
{{ $t('preferences.widget.lockScreen') }}
|
{{ $t('preferences.widget.languageTogglePosition') }}
|
||||||
</SwitchItem>
|
</SelectItem>
|
||||||
<SwitchItem v-model="widgetSidebarToggle">
|
<SelectItem
|
||||||
{{ $t('preferences.widget.sidebarToggle') }}
|
v-model="widgetFullscreenButtonPosition"
|
||||||
</SwitchItem>
|
:items="buttonPositionItems"
|
||||||
<SwitchItem v-model="widgetRefresh">
|
>
|
||||||
{{ $t('preferences.widget.refresh') }}
|
{{ $t('preferences.widget.fullscreenPosition') }}
|
||||||
</SwitchItem>
|
</SelectItem>
|
||||||
<SwitchItem v-model="widgetTimezone">
|
<SelectItem
|
||||||
{{ $t('preferences.widget.timezone') }}
|
v-model="widgetNotificationButtonPosition"
|
||||||
</SwitchItem>
|
: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">
|
<SelectItem v-model="appPreferencesButtonPosition" :items="positionItems">
|
||||||
{{ $t('preferences.position.title') }}
|
{{ $t('preferences.position.title') }}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|||||||
@@ -174,15 +174,33 @@ const shortcutKeysGlobalLockScreen = defineModel<boolean>(
|
|||||||
'shortcutKeysGlobalLockScreen',
|
'shortcutKeysGlobalLockScreen',
|
||||||
);
|
);
|
||||||
|
|
||||||
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
|
const widgetGlobalSearchButtonPosition = defineModel<string>(
|
||||||
const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
|
'widgetGlobalSearchButtonPosition',
|
||||||
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle');
|
);
|
||||||
const widgetNotification = defineModel<boolean>('widgetNotification');
|
const widgetFullscreenButtonPosition = defineModel<string>(
|
||||||
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
'widgetFullscreenButtonPosition',
|
||||||
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
);
|
||||||
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
|
const widgetLanguageToggleButtonPosition = defineModel<string>(
|
||||||
const widgetRefresh = defineModel<boolean>('widgetRefresh');
|
'widgetLanguageToggleButtonPosition',
|
||||||
const widgetTimezone = defineModel<boolean>('widgetTimezone');
|
);
|
||||||
|
const widgetNotificationButtonPosition = defineModel<string>(
|
||||||
|
'widgetNotificationButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetThemeToggleButtonPosition = defineModel<string>(
|
||||||
|
'widgetThemeToggleButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetLockScreenButtonPosition = defineModel<string>(
|
||||||
|
'widgetLockScreenButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetLogoutButtonPosition = defineModel<string>(
|
||||||
|
'widgetLogoutButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetRefreshButtonPosition = defineModel<string>(
|
||||||
|
'widgetRefreshButtonPosition',
|
||||||
|
);
|
||||||
|
const widgetTimezoneButtonPosition = defineModel<string>(
|
||||||
|
'widgetTimezoneButtonPosition',
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
customPreferences,
|
customPreferences,
|
||||||
@@ -483,15 +501,33 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
|
|||||||
v-model:app-preferences-button-position="
|
v-model:app-preferences-button-position="
|
||||||
appPreferencesButtonPosition
|
appPreferencesButtonPosition
|
||||||
"
|
"
|
||||||
v-model:widget-fullscreen="widgetFullscreen"
|
v-model:widget-fullscreen-button-position="
|
||||||
v-model:widget-global-search="widgetGlobalSearch"
|
widgetFullscreenButtonPosition
|
||||||
v-model:widget-language-toggle="widgetLanguageToggle"
|
"
|
||||||
v-model:widget-lock-screen="widgetLockScreen"
|
v-model:widget-global-search-button-position="
|
||||||
v-model:widget-notification="widgetNotification"
|
widgetGlobalSearchButtonPosition
|
||||||
v-model:widget-refresh="widgetRefresh"
|
"
|
||||||
v-model:widget-sidebar-toggle="widgetSidebarToggle"
|
v-model:widget-language-toggle-button-position="
|
||||||
v-model:widget-theme-toggle="widgetThemeToggle"
|
widgetLanguageToggleButtonPosition
|
||||||
v-model:widget-timezone="widgetTimezone"
|
"
|
||||||
|
v-model:widget-lock-screen-button-position="
|
||||||
|
widgetLockScreenButtonPosition
|
||||||
|
"
|
||||||
|
v-model:widget-logout-button-position="
|
||||||
|
widgetLogoutButtonPosition
|
||||||
|
"
|
||||||
|
v-model:widget-notification-button-position="
|
||||||
|
widgetNotificationButtonPosition
|
||||||
|
"
|
||||||
|
v-model:widget-refresh-button-position="
|
||||||
|
widgetRefreshButtonPosition
|
||||||
|
"
|
||||||
|
v-model:widget-theme-toggle-button-position="
|
||||||
|
widgetThemeToggleButtonPosition
|
||||||
|
"
|
||||||
|
v-model:widget-timezone-button-position="
|
||||||
|
widgetTimezoneButtonPosition
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</Block>
|
</Block>
|
||||||
<Block :title="$t('preferences.footer.title')">
|
<Block :title="$t('preferences.footer.title')">
|
||||||
|
|||||||
@@ -3,13 +3,27 @@ import type { Component } from 'vue';
|
|||||||
|
|
||||||
import type { AnyFunction } from '@vben/types';
|
import type { AnyFunction } from '@vben/types';
|
||||||
|
|
||||||
import { useTemplateRef, watch } from 'vue';
|
import { computed, useTemplateRef, watch } from 'vue';
|
||||||
|
|
||||||
import { useHoverToggle } from '@vben/hooks';
|
import { useHoverToggle } from '@vben/hooks';
|
||||||
import { Settings } from '@vben/icons';
|
import {
|
||||||
|
Bell,
|
||||||
|
Fullscreen,
|
||||||
|
Languages,
|
||||||
|
LockKeyhole,
|
||||||
|
LogOut,
|
||||||
|
RotateCw,
|
||||||
|
Search,
|
||||||
|
Settings,
|
||||||
|
Sun,
|
||||||
|
SunMoon,
|
||||||
|
} from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
import { usePreferences } from '@vben/preferences';
|
import { preferences, usePreferences } from '@vben/preferences';
|
||||||
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
import { isWindowsOs } from '@vben/utils';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben-core/popup-ui';
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -17,11 +31,15 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
VbenAvatar,
|
VbenAvatar,
|
||||||
VbenIcon,
|
VbenIcon,
|
||||||
} from '@vben-core/shadcn-ui';
|
} from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
|
import { useMagicKeys, whenever } from '@vueuse/core';
|
||||||
|
|
||||||
|
import { LockScreenModal } from '../lock-screen';
|
||||||
import { Preferences } from '../preferences';
|
import { Preferences } from '../preferences';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -70,9 +88,22 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
hoverDelay: 500,
|
hoverDelay: 500,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
|
const emit = defineEmits<{ clearPreferencesAndLogout: []; logout: [] }>();
|
||||||
|
|
||||||
const { preferencesButtonPosition } = usePreferences();
|
const {
|
||||||
|
globalLockScreenShortcutKey,
|
||||||
|
globalLogoutShortcutKey,
|
||||||
|
preferencesButtonPosition,
|
||||||
|
} = usePreferences();
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const [LockModal, lockModalApi] = useVbenModal({
|
||||||
|
connectedComponent: LockScreenModal,
|
||||||
|
});
|
||||||
|
const [LogoutModal, logoutModalApi] = useVbenModal({
|
||||||
|
onConfirm() {
|
||||||
|
handleSubmitLogout();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const refTrigger = useTemplateRef('refTrigger');
|
const refTrigger = useTemplateRef('refTrigger');
|
||||||
const refContent = useTemplateRef('refContent');
|
const refContent = useTemplateRef('refContent');
|
||||||
@@ -96,13 +127,157 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const showLockInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.lockScreen &&
|
||||||
|
preferences.widget.lockScreenButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showLogoutInDropdown = computed(
|
||||||
|
() => preferences.widget.logoutButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showGlobalSearchInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.globalSearch &&
|
||||||
|
preferences.widget.globalSearchButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showThemeToggleInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.themeToggle &&
|
||||||
|
preferences.widget.themeToggleButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showLanguageToggleInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.languageToggle &&
|
||||||
|
preferences.widget.languageToggleButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showTimezoneInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.timezone &&
|
||||||
|
preferences.widget.timezoneButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showFullscreenInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.fullscreen &&
|
||||||
|
preferences.widget.fullscreenButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showNotificationInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.notification &&
|
||||||
|
preferences.widget.notificationButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const showRefreshInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
preferences.widget.refresh &&
|
||||||
|
preferences.widget.refreshButtonPosition === 'user-dropdown',
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasAnyInDropdown = computed(
|
||||||
|
() =>
|
||||||
|
showLockInDropdown.value ||
|
||||||
|
showLogoutInDropdown.value ||
|
||||||
|
showGlobalSearchInDropdown.value ||
|
||||||
|
showThemeToggleInDropdown.value ||
|
||||||
|
showLanguageToggleInDropdown.value ||
|
||||||
|
showTimezoneInDropdown.value ||
|
||||||
|
showFullscreenInDropdown.value ||
|
||||||
|
showNotificationInDropdown.value ||
|
||||||
|
showRefreshInDropdown.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||||
|
|
||||||
|
const enableLogoutShortcutKey = computed(() => {
|
||||||
|
return showLogoutInDropdown.value && globalLogoutShortcutKey.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const enableLockScreenShortcutKey = computed(() => {
|
||||||
|
return showLockInDropdown.value && globalLockScreenShortcutKey.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const enableShortcutKey = computed(() => {
|
||||||
|
return (
|
||||||
|
(showLockInDropdown.value || showLogoutInDropdown.value) &&
|
||||||
|
preferences.shortcutKeys.enable
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleOpenLock() {
|
||||||
|
lockModalApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmitLock(lockScreenPassword: string) {
|
||||||
|
lockModalApi.close();
|
||||||
|
accessStore.lockScreen(lockScreenPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLogout() {
|
||||||
|
logoutModalApi.open();
|
||||||
|
openPopover.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmitLogout() {
|
||||||
|
emit('logout');
|
||||||
|
logoutModalApi.close();
|
||||||
|
}
|
||||||
|
|
||||||
// 设置 - 打开偏好设置抽屉
|
// 设置 - 打开偏好设置抽屉
|
||||||
function handleOpenSettings() {
|
function handleOpenSettings() {
|
||||||
refPreferences.value?.open();
|
refPreferences.value?.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableShortcutKey.value) {
|
||||||
|
const keys = useMagicKeys();
|
||||||
|
const logoutKey = keys['Alt+KeyQ'];
|
||||||
|
const lockKey = keys['Alt+KeyL'];
|
||||||
|
|
||||||
|
if (logoutKey) {
|
||||||
|
whenever(logoutKey, () => {
|
||||||
|
if (enableLogoutShortcutKey.value) {
|
||||||
|
handleLogout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockKey) {
|
||||||
|
whenever(lockKey, () => {
|
||||||
|
if (enableLockScreenShortcutKey.value) {
|
||||||
|
handleOpenLock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<LockModal
|
||||||
|
v-if="showLockInDropdown"
|
||||||
|
:avatar="avatar"
|
||||||
|
:text="text"
|
||||||
|
@submit="handleSubmitLock"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LogoutModal
|
||||||
|
v-if="showLogoutInDropdown"
|
||||||
|
:cancel-text="$t('common.cancel')"
|
||||||
|
:confirm-text="$t('common.confirm')"
|
||||||
|
:fullscreen-button="false"
|
||||||
|
:title="$t('common.prompt')"
|
||||||
|
centered
|
||||||
|
content-class="px-8 min-h-10"
|
||||||
|
footer-class="border-none mb-3 mr-3"
|
||||||
|
header-class="border-none"
|
||||||
|
>
|
||||||
|
{{ $t('ui.widgets.logoutTip') }}
|
||||||
|
</LogoutModal>
|
||||||
|
|
||||||
<Preferences
|
<Preferences
|
||||||
v-if="preferencesButtonPosition.userDropdown"
|
v-if="preferencesButtonPosition.userDropdown"
|
||||||
ref="refPreferences"
|
ref="refPreferences"
|
||||||
@@ -159,7 +334,97 @@ function handleOpenSettings() {
|
|||||||
<VbenIcon :icon="menu.icon" class="mr-2 size-4" />
|
<VbenIcon :icon="menu.icon" class="mr-2 size-4" />
|
||||||
{{ menu.text }}
|
{{ menu.text }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator v-if="preferencesButtonPosition.userDropdown" />
|
<template v-if="showLockInDropdown || showLogoutInDropdown">
|
||||||
|
<DropdownMenuSeparator v-if="showLockInDropdown" />
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-if="showLockInDropdown"
|
||||||
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
@click="handleOpenLock"
|
||||||
|
>
|
||||||
|
<LockKeyhole class="mr-2 size-4" />
|
||||||
|
{{ $t('ui.widgets.lockScreen.title') }}
|
||||||
|
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
|
||||||
|
{{ altView }} L
|
||||||
|
</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator v-if="showLogoutInDropdown" />
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-if="showLogoutInDropdown"
|
||||||
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
@click="handleLogout"
|
||||||
|
>
|
||||||
|
<LogOut class="mr-2 size-4" />
|
||||||
|
{{ $t('common.logout') }}
|
||||||
|
<DropdownMenuShortcut v-if="enableLogoutShortcutKey">
|
||||||
|
{{ altView }} Q
|
||||||
|
</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
showGlobalSearchInDropdown ||
|
||||||
|
showThemeToggleInDropdown ||
|
||||||
|
showLanguageToggleInDropdown ||
|
||||||
|
showTimezoneInDropdown ||
|
||||||
|
showFullscreenInDropdown ||
|
||||||
|
showNotificationInDropdown ||
|
||||||
|
showRefreshInDropdown
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-if="showGlobalSearchInDropdown"
|
||||||
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
>
|
||||||
|
<Search class="mr-2 size-4" />
|
||||||
|
{{ $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') }}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-if="showLanguageToggleInDropdown"
|
||||||
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
>
|
||||||
|
<Languages class="mr-2 size-4" />
|
||||||
|
{{ $t('preferences.widget.languageToggle') }}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<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') }}
|
||||||
|
</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" />
|
||||||
|
{{ $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" />
|
||||||
|
{{ $t('preferences.widget.notification') }}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-if="showRefreshInDropdown"
|
||||||
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
>
|
||||||
|
<RotateCw class="mr-2 size-4" />
|
||||||
|
{{ $t('preferences.widget.refresh') }}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</template>
|
||||||
|
<DropdownMenuSeparator
|
||||||
|
v-if="hasAnyInDropdown || preferencesButtonPosition.userDropdown"
|
||||||
|
/>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
v-if="preferencesButtonPosition.userDropdown"
|
v-if="preferencesButtonPosition.userDropdown"
|
||||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
|||||||
@@ -21,5 +21,6 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"showSearchPanel": "Show search panel",
|
"showSearchPanel": "Show search panel",
|
||||||
"hideSearchPanel": "Hide search panel"
|
"hideSearchPanel": "Hide search panel",
|
||||||
|
"notShow": "Don't Show"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,14 +193,25 @@
|
|||||||
"widget": {
|
"widget": {
|
||||||
"title": "Widget",
|
"title": "Widget",
|
||||||
"globalSearch": "Enable Global Search",
|
"globalSearch": "Enable Global Search",
|
||||||
|
"globalSearchPosition": "Global Search Position",
|
||||||
"fullscreen": "Enable Fullscreen",
|
"fullscreen": "Enable Fullscreen",
|
||||||
|
"fullscreenPosition": "Fullscreen Position",
|
||||||
"themeToggle": "Enable Theme Toggle",
|
"themeToggle": "Enable Theme Toggle",
|
||||||
|
"themeTogglePosition": "Theme Toggle Position",
|
||||||
"languageToggle": "Enable Language Toggle",
|
"languageToggle": "Enable Language Toggle",
|
||||||
|
"languageTogglePosition": "Language Toggle Position",
|
||||||
"notification": "Enable Notification",
|
"notification": "Enable Notification",
|
||||||
|
"notificationPosition": "Notification Position",
|
||||||
"sidebarToggle": "Enable Sidebar Toggle",
|
"sidebarToggle": "Enable Sidebar Toggle",
|
||||||
"lockScreen": "Enable Lock Screen",
|
"lockScreen": "Enable Lock Screen",
|
||||||
|
"lockScreenPosition": "Lock Screen Button Position",
|
||||||
|
"logoutButtonPosition": "Logout Button Position",
|
||||||
|
"header": "Header",
|
||||||
|
"userDropdown": "User Dropdown",
|
||||||
"refresh": "Enable Refresh",
|
"refresh": "Enable Refresh",
|
||||||
"timezone": "Enable Timezone"
|
"refreshPosition": "Refresh Position",
|
||||||
|
"timezone": "Enable Timezone",
|
||||||
|
"timezonePosition": "Timezone Position"
|
||||||
},
|
},
|
||||||
"antd": {
|
"antd": {
|
||||||
"tabLabel": "Antd Extension",
|
"tabLabel": "Antd Extension",
|
||||||
|
|||||||
@@ -21,5 +21,6 @@
|
|||||||
"yes": "是",
|
"yes": "是",
|
||||||
"no": "否",
|
"no": "否",
|
||||||
"showSearchPanel": "显示搜索面板",
|
"showSearchPanel": "显示搜索面板",
|
||||||
"hideSearchPanel": "隐藏搜索面板"
|
"hideSearchPanel": "隐藏搜索面板",
|
||||||
|
"notShow": "不显示"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,14 +193,25 @@
|
|||||||
"widget": {
|
"widget": {
|
||||||
"title": "小部件",
|
"title": "小部件",
|
||||||
"globalSearch": "启用全局搜索",
|
"globalSearch": "启用全局搜索",
|
||||||
|
"globalSearchPosition": "全局搜索位置",
|
||||||
"fullscreen": "启用全屏",
|
"fullscreen": "启用全屏",
|
||||||
|
"fullscreenPosition": "全屏按钮位置",
|
||||||
"themeToggle": "启用主题切换",
|
"themeToggle": "启用主题切换",
|
||||||
|
"themeTogglePosition": "主题切换位置",
|
||||||
"languageToggle": "启用语言切换",
|
"languageToggle": "启用语言切换",
|
||||||
|
"languageTogglePosition": "语言切换位置",
|
||||||
"notification": "启用通知",
|
"notification": "启用通知",
|
||||||
|
"notificationPosition": "通知位置",
|
||||||
"sidebarToggle": "启用侧边栏切换",
|
"sidebarToggle": "启用侧边栏切换",
|
||||||
"lockScreen": "启用锁屏",
|
"lockScreen": "启用锁屏",
|
||||||
|
"lockScreenPosition": "锁屏按钮位置",
|
||||||
|
"logoutButtonPosition": "退出按钮位置",
|
||||||
|
"header": "顶栏",
|
||||||
|
"userDropdown": "用户下拉窗",
|
||||||
"refresh": "启用刷新",
|
"refresh": "启用刷新",
|
||||||
"timezone": "启用时区"
|
"refreshPosition": "刷新按钮位置",
|
||||||
|
"timezone": "启用时区",
|
||||||
|
"timezonePosition": "时区按钮位置"
|
||||||
},
|
},
|
||||||
"antd": {
|
"antd": {
|
||||||
"tabLabel": "Antd 拓展",
|
"tabLabel": "Antd 拓展",
|
||||||
|
|||||||
Reference in New Issue
Block a user