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:
@@ -8,7 +8,11 @@ import { preferences, usePreferences } from '@vben/preferences';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { useVbenModal } from '@vben-core/popup-ui';
|
||||
import { VbenFullScreen, VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
import {
|
||||
VbenFullScreen,
|
||||
VbenIconButton,
|
||||
VbenTooltip,
|
||||
} from '@vben-core/shadcn-ui';
|
||||
|
||||
import { useMagicKeys, whenever } from '@vueuse/core';
|
||||
|
||||
@@ -16,6 +20,7 @@ import {
|
||||
GlobalSearch,
|
||||
LanguageToggle,
|
||||
LockScreenModal,
|
||||
Notification,
|
||||
PreferencesButton,
|
||||
ThemeToggle,
|
||||
TimezoneButton,
|
||||
@@ -121,94 +126,77 @@ type SlotItem = { index: number; name: string };
|
||||
|
||||
const rightSlots = computed(() => {
|
||||
const list: Array<SlotItem> = [];
|
||||
// 全局搜索
|
||||
if (
|
||||
preferences.widget.globalSearch &&
|
||||
preferences.widget.globalSearchButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE,
|
||||
name: 'global-search',
|
||||
});
|
||||
}
|
||||
// 偏好设置快捷功能
|
||||
if (preferencesButtonPosition.value.header) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 10,
|
||||
name: 'preferences',
|
||||
});
|
||||
}
|
||||
// 主题、语言、时区等子功能由各自的 widget 开关独立控制,
|
||||
// 不应跟随偏好设置按钮的显示与否(如 enablePreferences 为 false 或按钮位于其他位置时)
|
||||
if (
|
||||
preferences.widget.themeToggle &&
|
||||
preferences.widget.themeToggleButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 20,
|
||||
name: 'theme-toggle',
|
||||
});
|
||||
}
|
||||
if (
|
||||
preferences.widget.languageToggle &&
|
||||
preferences.widget.languageToggleButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 30,
|
||||
name: 'language-toggle',
|
||||
});
|
||||
}
|
||||
if (
|
||||
preferences.widget.timezone &&
|
||||
preferences.widget.timezoneButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 40,
|
||||
name: 'timezone',
|
||||
});
|
||||
}
|
||||
// 全屏
|
||||
if (
|
||||
preferences.widget.fullscreen &&
|
||||
preferences.widget.fullscreenButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 50,
|
||||
name: 'fullscreen',
|
||||
});
|
||||
}
|
||||
// 消息通知
|
||||
if (
|
||||
preferences.widget.notification &&
|
||||
preferences.widget.notificationButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 60,
|
||||
name: 'notification',
|
||||
});
|
||||
}
|
||||
// 锁定屏幕(仅在 header 位置显示)
|
||||
if (
|
||||
preferences.widget.lockScreen &&
|
||||
preferences.widget.lockScreenButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 70,
|
||||
name: 'lock-screen-btn',
|
||||
});
|
||||
}
|
||||
// 退出登录(仅在 header 位置显示)
|
||||
if (preferences.widget.logoutButtonPosition === 'header') {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE + 80,
|
||||
name: 'logout-btn',
|
||||
});
|
||||
|
||||
// 按 widget.order 顺序遍历,检查每个部件是否应在 header 中显示
|
||||
const widgetChecks: Record<string, { slotName: string; visible: boolean }> = {
|
||||
globalSearch: {
|
||||
visible:
|
||||
preferences.widget.globalSearch &&
|
||||
preferences.widget.globalSearchButtonPosition === 'header',
|
||||
slotName: 'global-search',
|
||||
},
|
||||
preferences: {
|
||||
visible: preferencesButtonPosition.value.header,
|
||||
slotName: 'preferences',
|
||||
},
|
||||
themeToggle: {
|
||||
visible:
|
||||
preferences.widget.themeToggle &&
|
||||
preferences.widget.themeToggleButtonPosition === 'header',
|
||||
slotName: 'theme-toggle',
|
||||
},
|
||||
languageToggle: {
|
||||
visible:
|
||||
preferences.widget.languageToggle &&
|
||||
preferences.widget.languageToggleButtonPosition === 'header',
|
||||
slotName: 'language-toggle',
|
||||
},
|
||||
timezone: {
|
||||
visible:
|
||||
preferences.widget.timezone &&
|
||||
preferences.widget.timezoneButtonPosition === 'header',
|
||||
slotName: 'timezone',
|
||||
},
|
||||
fullscreen: {
|
||||
visible:
|
||||
preferences.widget.fullscreen &&
|
||||
preferences.widget.fullscreenButtonPosition === 'header',
|
||||
slotName: 'fullscreen',
|
||||
},
|
||||
notification: {
|
||||
visible:
|
||||
preferences.widget.notification &&
|
||||
preferences.widget.notificationButtonPosition === 'header',
|
||||
slotName: 'notification',
|
||||
},
|
||||
lockScreenBtn: {
|
||||
visible:
|
||||
preferences.widget.lockScreen &&
|
||||
preferences.widget.lockScreenButtonPosition === 'header',
|
||||
slotName: 'lock-screen-btn',
|
||||
},
|
||||
logoutBtn: {
|
||||
visible: preferences.widget.logoutButtonPosition === 'header',
|
||||
slotName: 'logout-btn',
|
||||
},
|
||||
refresh: {
|
||||
visible:
|
||||
preferences.widget.refresh &&
|
||||
preferences.widget.refreshButtonPosition === 'header',
|
||||
slotName: 'refresh',
|
||||
},
|
||||
};
|
||||
|
||||
for (const key of preferences.widget.order) {
|
||||
const check = widgetChecks[key];
|
||||
if (check?.visible) {
|
||||
list.push({ index: REFERENCE_VALUE + list.length, name: check.slotName });
|
||||
}
|
||||
}
|
||||
|
||||
// 用户插槽(header-right-N)追加在后面
|
||||
Object.keys(slots).forEach((key) => {
|
||||
// 适配插槽名称,例如第一个插槽名:header-right-1
|
||||
if (key.startsWith('header-right')) {
|
||||
// 取第三个占位的数字,若是第三个占位不是数字,则自动分配排序索引
|
||||
const slotIndex = Number(key.split('-')[2]);
|
||||
const index = Number.isNaN(slotIndex) ? nextIndex(list) : slotIndex;
|
||||
list.push({ index, name: key });
|
||||
@@ -223,16 +211,6 @@ const rightSlots = computed(() => {
|
||||
|
||||
const leftSlots = computed(() => {
|
||||
const list: Array<SlotItem> = [];
|
||||
// 刷新
|
||||
if (
|
||||
preferences.widget.refresh &&
|
||||
preferences.widget.refreshButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: 0,
|
||||
name: 'refresh',
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(slots).forEach((key) => {
|
||||
// 适配插槽名称,例如第一个插槽名:header-left-1
|
||||
@@ -322,19 +300,37 @@ function clearPreferencesAndLogout() {
|
||||
</template>
|
||||
|
||||
<template v-else-if="slot.name === 'preferences'">
|
||||
<PreferencesButton
|
||||
class="mr-1"
|
||||
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
||||
/>
|
||||
<VbenTooltip side="bottom">
|
||||
<template #trigger>
|
||||
<PreferencesButton
|
||||
class="mr-1"
|
||||
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
||||
/>
|
||||
</template>
|
||||
{{ $t('preferences.title') }}
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'theme-toggle'">
|
||||
<ThemeToggle class="mt-0.5 mr-1" />
|
||||
<VbenTooltip side="bottom">
|
||||
<template #trigger>
|
||||
<ThemeToggle class="mt-0.5 mr-1" />
|
||||
</template>
|
||||
{{ $t('preferences.theme.title') }}
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'language-toggle'">
|
||||
<LanguageToggle class="mr-1" />
|
||||
<VbenTooltip side="bottom">
|
||||
<template #trigger>
|
||||
<LanguageToggle class="mr-1" />
|
||||
</template>
|
||||
{{ $t('preferences.widget.languageToggle') }}
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'fullscreen'">
|
||||
<VbenFullScreen class="mr-1" />
|
||||
<VbenFullScreen
|
||||
class="mr-1"
|
||||
:tooltip="$t('preferences.widget.fullscreen')"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'timezone'">
|
||||
<TimezoneButton class="mt-0.5 mr-1" />
|
||||
@@ -357,6 +353,23 @@ function clearPreferencesAndLogout() {
|
||||
<LogOut class="size-4" />
|
||||
</VbenIconButton>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'notification'">
|
||||
<VbenTooltip side="bottom">
|
||||
<template #trigger>
|
||||
<Notification class="mr-1" />
|
||||
</template>
|
||||
{{ $t('preferences.widget.notification') }}
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'refresh'">
|
||||
<VbenIconButton
|
||||
class="my-0 mr-1 rounded-md"
|
||||
:tooltip="$t('preferences.widget.refresh')"
|
||||
@click="refresh"
|
||||
>
|
||||
<RotateCw class="size-4" />
|
||||
</VbenIconButton>
|
||||
</template>
|
||||
</slot>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user