Browse Source

feat: add support for custom slot header-index, sorted by index (#4039)

* feat: header右侧支持n个自定义位置插槽,插槽命名方式header-index,根据index大小排序。
框架默认插槽user-dropdown的index:20,notification的index:10 (#4034)

* chore: 将默认组件加入排序

* chore: 更改slot命名方式支持header左侧自定义插槽,命名方式:header-right-n,header-left-n,具体位置看README

---------

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>
pull/4059/head
jinmao88 1 month ago
committed by GitHub
parent
commit
84c8fb0ecc
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      packages/effects/layouts/src/basic/README.md
  2. 98
      packages/effects/layouts/src/basic/header/header.vue
  3. 15
      packages/effects/layouts/src/basic/layout.vue

7
packages/effects/layouts/src/basic/README.md

@ -0,0 +1,7 @@
## layout
### header
- 支持N个自定义插槽,命名方式:header-right-n,header-left-n
- header-left-n ,排序方式:1-5 ,breadcrumb,6-x
- header-right-n ,排序方式:1-4,global-search,6-9,theme-toggle,11-14,language-toggle,16-19,fullscreen,21-24,notification,26-29,user-dropdown,30-x

98
packages/effects/layouts/src/basic/header/header.vue

@ -1,4 +1,6 @@
<script lang="ts" setup>
import { computed, useSlots } from 'vue';
import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { VbenFullScreen } from '@vben-core/shadcn-ui';
@ -22,26 +24,100 @@ withDefaults(defineProps<Props>(), {
const accessStore = useAccessStore();
const { globalSearchShortcutKey } = usePreferences();
const slots = useSlots();
const rightSlots = computed(() => {
const list = [{ index: 30, name: 'user-dropdown' }];
if (preferences.widget.globalSearch) {
list.push({
index: 5,
name: 'global-search',
});
}
if (preferences.widget.themeToggle) {
list.push({
index: 10,
name: 'theme-toggle',
});
}
if (preferences.widget.languageToggle) {
list.push({
index: 15,
name: 'language-toggle',
});
}
if (preferences.widget.fullscreen) {
list.push({
index: 20,
name: 'fullscreen',
});
}
if (preferences.widget.notification) {
list.push({
index: 25,
name: 'notification',
});
}
Object.keys(slots).forEach((key) => {
const name = key.split('-');
if (key.startsWith('header-right')) {
list.push({ index: Number(name[2]), name: key });
}
});
return list.sort((a, b) => a.index - b.index);
});
const leftSlots = computed(() => {
const list: any[] = [];
Object.keys(slots).forEach((key) => {
const name = key.split('-');
if (key.startsWith('header-left')) {
list.push({ index: Number(name[2]), name: key });
}
});
return list.sort((a, b) => a.index - b.index);
});
</script>
<template>
<template
v-for="slot in leftSlots.filter((item) => item.index < 5)"
:key="slot.name"
>
<slot :name="slot.name"></slot>
</template>
<div class="flex-center hidden lg:block">
<slot name="breadcrumb"></slot>
</div>
<template
v-for="slot in leftSlots.filter((item) => item.index > 5)"
:key="slot.name"
>
<slot :name="slot.name"></slot>
</template>
<div class="flex h-full min-w-0 flex-1 items-center">
<slot name="menu"></slot>
</div>
<div class="flex h-full min-w-0 flex-shrink-0 items-center">
<GlobalSearch
v-if="preferences.widget.globalSearch"
:enable-shortcut-key="globalSearchShortcutKey"
:menus="accessStore.accessMenus"
class="mr-4"
/>
<ThemeToggle v-if="preferences.widget.themeToggle" class="mr-2" />
<LanguageToggle v-if="preferences.widget.languageToggle" class="mr-2" />
<VbenFullScreen v-if="preferences.widget.fullscreen" class="mr-2" />
<slot v-if="preferences.widget.notification" name="notification"></slot>
<slot name="user-dropdown"></slot>
<template v-for="slot in rightSlots" :key="slot.name">
<slot :name="slot.name">
<template v-if="slot.name === 'global-search'">
<GlobalSearch
:enable-shortcut-key="globalSearchShortcutKey"
:menus="accessStore.accessMenus"
class="mr-4"
/>
</template>
<template v-else-if="slot.name === 'theme-toggle'">
<ThemeToggle class="mr-2" />
</template>
<template v-else-if="slot.name === 'language-toggle'">
<LanguageToggle class="mr-2" />
</template>
<template v-else-if="slot.name === 'fullscreen'">
<VbenFullScreen class="mr-2" />
</template>
</slot>
</template>
</div>
</template>

15
packages/effects/layouts/src/basic/layout.vue

@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { MenuRecordRaw } from '@vben/types';
import { computed, watch } from 'vue';
import { computed, useSlots, watch } from 'vue';
import { useWatermark } from '@vben/hooks';
import { $t } from '@vben/locales';
@ -136,11 +136,8 @@ watch(
() => preferences.app.watermark,
async (val) => {
if (val) {
// await nextTick();
updateWatermark({
await updateWatermark({
content: `${preferences.app.name} 用户名: ${userStore.userInfo?.username}`,
// parent: contentRef.value,
});
}
},
@ -148,6 +145,11 @@ watch(
immediate: true,
},
);
const slots = useSlots();
const headerSlots = computed(() => {
return Object.keys(slots).filter((key) => key.startsWith('header-'));
});
</script>
<template>
@ -240,6 +242,9 @@ watch(
<template #notification>
<slot name="notification"></slot>
</template>
<template v-for="item in headerSlots" #[item]>
<slot :name="item"></slot>
</template>
</LayoutHeader>
</template>
<!-- 侧边菜单区域 -->

Loading…
Cancel
Save