feat: sidebar button config (#5818)
* feat: 新增 PreferenceCheckboxItem 组件 * feat(preferences): 添加侧边栏按钮配置功能 * feat: 新增按钮点击事件触发功能 * feat(SidebarPreferences): 新增侧边栏折叠按钮与固定按钮配置 * feat(ui): 新增侧边栏固定按钮及配置选项 * fix(test): 修正侧边栏配置项缺失问题pull/78/MERGE
parent
a0feeb1966
commit
18722ce434
|
@ -68,10 +68,12 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"autoActivateChild": false,
|
"autoActivateChild": false,
|
||||||
"collapsed": false,
|
"collapsed": false,
|
||||||
|
"collapsedButton": true,
|
||||||
"collapsedShowTitle": false,
|
"collapsedShowTitle": false,
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"expandOnHover": true,
|
"expandOnHover": true,
|
||||||
"extraCollapse": false,
|
"extraCollapse": false,
|
||||||
|
"fixedButton": true,
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"width": 224,
|
"width": 224,
|
||||||
},
|
},
|
||||||
|
|
|
@ -68,10 +68,12 @@ const defaultPreferences: Preferences = {
|
||||||
sidebar: {
|
sidebar: {
|
||||||
autoActivateChild: false,
|
autoActivateChild: false,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
collapsedButton: true,
|
||||||
collapsedShowTitle: false,
|
collapsedShowTitle: false,
|
||||||
enable: true,
|
enable: true,
|
||||||
expandOnHover: true,
|
expandOnHover: true,
|
||||||
extraCollapse: false,
|
extraCollapse: false,
|
||||||
|
fixedButton: true,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
width: 224,
|
width: 224,
|
||||||
},
|
},
|
||||||
|
|
|
@ -132,6 +132,8 @@ interface SidebarPreferences {
|
||||||
autoActivateChild: boolean;
|
autoActivateChild: boolean;
|
||||||
/** 侧边栏是否折叠 */
|
/** 侧边栏是否折叠 */
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
|
/** 侧边栏折叠按钮是否可见 */
|
||||||
|
collapsedButton: boolean;
|
||||||
/** 侧边栏折叠时,是否显示title */
|
/** 侧边栏折叠时,是否显示title */
|
||||||
collapsedShowTitle: boolean;
|
collapsedShowTitle: boolean;
|
||||||
/** 侧边栏是否可见 */
|
/** 侧边栏是否可见 */
|
||||||
|
@ -140,6 +142,8 @@ interface SidebarPreferences {
|
||||||
expandOnHover: boolean;
|
expandOnHover: boolean;
|
||||||
/** 侧边栏扩展区域是否折叠 */
|
/** 侧边栏扩展区域是否折叠 */
|
||||||
extraCollapse: boolean;
|
extraCollapse: boolean;
|
||||||
|
/** 侧边栏固定按钮是否可见 */
|
||||||
|
fixedButton: boolean;
|
||||||
/** 侧边栏是否隐藏 - css */
|
/** 侧边栏是否隐藏 - css */
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
/** 侧边栏宽度 */
|
/** 侧边栏宽度 */
|
||||||
|
|
|
@ -65,9 +65,14 @@ interface Props {
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
/**
|
/**
|
||||||
* 显示折叠按钮
|
* 显示折叠按钮
|
||||||
* @default false
|
* @default true
|
||||||
*/
|
*/
|
||||||
showCollapseButton?: boolean;
|
showCollapseButton?: boolean;
|
||||||
|
/**
|
||||||
|
* 显示固定按钮
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
showFixedButton?: boolean;
|
||||||
/**
|
/**
|
||||||
* 主题
|
* 主题
|
||||||
*/
|
*/
|
||||||
|
@ -95,6 +100,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
show: true,
|
show: true,
|
||||||
showCollapseButton: true,
|
showCollapseButton: true,
|
||||||
|
showFixedButton: true,
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -267,7 +273,7 @@ function handleMouseleave() {
|
||||||
@mouseleave="handleMouseleave"
|
@mouseleave="handleMouseleave"
|
||||||
>
|
>
|
||||||
<SidebarFixedButton
|
<SidebarFixedButton
|
||||||
v-if="!collapse && !isSidebarMixed"
|
v-if="!collapse && !isSidebarMixed && showFixedButton"
|
||||||
v-model:expand-on-hover="expandOnHover"
|
v-model:expand-on-hover="expandOnHover"
|
||||||
/>
|
/>
|
||||||
<div v-if="slots.logo" :style="headerStyle">
|
<div v-if="slots.logo" :style="headerStyle">
|
||||||
|
|
|
@ -106,6 +106,11 @@ interface VbenLayoutProps {
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
sidebarCollapse?: boolean;
|
sidebarCollapse?: boolean;
|
||||||
|
/**
|
||||||
|
* 侧边菜单折叠按钮
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
sidebarCollapsedButton?: boolean;
|
||||||
/**
|
/**
|
||||||
* 侧边菜单是否折叠时,是否显示title
|
* 侧边菜单是否折叠时,是否显示title
|
||||||
* @default true
|
* @default true
|
||||||
|
@ -121,6 +126,11 @@ interface VbenLayoutProps {
|
||||||
* @default 48
|
* @default 48
|
||||||
*/
|
*/
|
||||||
sidebarExtraCollapsedWidth?: number;
|
sidebarExtraCollapsedWidth?: number;
|
||||||
|
/**
|
||||||
|
* 侧边菜单折叠按钮是否固定
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
sidebarFixedButton?: boolean;
|
||||||
/**
|
/**
|
||||||
* 侧边栏是否隐藏
|
* 侧边栏是否隐藏
|
||||||
* @default false
|
* @default false
|
||||||
|
|
|
@ -49,8 +49,10 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
headerVisible: true,
|
headerVisible: true,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
layout: 'sidebar-nav',
|
layout: 'sidebar-nav',
|
||||||
|
sidebarCollapsedButton: true,
|
||||||
sidebarCollapseShowTitle: false,
|
sidebarCollapseShowTitle: false,
|
||||||
sidebarExtraCollapsedWidth: 60,
|
sidebarExtraCollapsedWidth: 60,
|
||||||
|
sidebarFixedButton: true,
|
||||||
sidebarHidden: false,
|
sidebarHidden: false,
|
||||||
sidebarMixedWidth: 80,
|
sidebarMixedWidth: 80,
|
||||||
sidebarTheme: 'dark',
|
sidebarTheme: 'dark',
|
||||||
|
@ -487,6 +489,8 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
||||||
v-model:expand-on-hovering="sidebarExpandOnHovering"
|
v-model:expand-on-hovering="sidebarExpandOnHovering"
|
||||||
v-model:extra-collapse="sidebarExtraCollapse"
|
v-model:extra-collapse="sidebarExtraCollapse"
|
||||||
v-model:extra-visible="sidebarExtraVisible"
|
v-model:extra-visible="sidebarExtraVisible"
|
||||||
|
:show-collapse-button="sidebarCollapsedButton"
|
||||||
|
:show-fixed-button="sidebarFixedButton"
|
||||||
:collapse-width="getSideCollapseWidth"
|
:collapse-width="getSideCollapseWidth"
|
||||||
:dom-visible="!isMobile"
|
:dom-visible="!isMobile"
|
||||||
:extra-width="sidebarExtraWidth"
|
:extra-width="sidebarExtraWidth"
|
||||||
|
|
|
@ -20,7 +20,7 @@ const props = withDefaults(defineProps<VbenButtonGroupProps>(), {
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
size: 'middle',
|
size: 'middle',
|
||||||
});
|
});
|
||||||
|
const emit = defineEmits(['btnClick']);
|
||||||
const btnDefaultProps = computed(() => {
|
const btnDefaultProps = computed(() => {
|
||||||
return {
|
return {
|
||||||
...objectOmit(props, ['options', 'btnClass', 'size', 'disabled']),
|
...objectOmit(props, ['options', 'btnClass', 'size', 'disabled']),
|
||||||
|
@ -90,6 +90,7 @@ async function onBtnClick(value: ValueType) {
|
||||||
innerValue.value = [value];
|
innerValue.value = [value];
|
||||||
modelValue.value = value;
|
modelValue.value = value;
|
||||||
}
|
}
|
||||||
|
emit('btnClick', value);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -192,6 +192,8 @@ const headerSlots = computed(() => {
|
||||||
:sidebar-collapse="preferences.sidebar.collapsed"
|
:sidebar-collapse="preferences.sidebar.collapsed"
|
||||||
:sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
|
:sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
|
||||||
:sidebar-enable="sidebarVisible"
|
:sidebar-enable="sidebarVisible"
|
||||||
|
:sidebar-collapsed-button="preferences.sidebar.collapsedButton"
|
||||||
|
:sidebar-fixed-button="preferences.sidebar.fixedButton"
|
||||||
:sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
|
:sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
|
||||||
:sidebar-extra-collapse="preferences.sidebar.extraCollapse"
|
:sidebar-extra-collapse="preferences.sidebar.extraCollapse"
|
||||||
:sidebar-hidden="preferences.sidebar.hidden"
|
:sidebar-hidden="preferences.sidebar.hidden"
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { SelectOption } from '@vben/types';
|
||||||
|
|
||||||
|
import { useSlots } from 'vue';
|
||||||
|
|
||||||
|
import { CircleHelp } from '@vben/icons';
|
||||||
|
|
||||||
|
import { VbenCheckButtonGroup, VbenTooltip } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'PreferenceCheckboxItem',
|
||||||
|
});
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
disabled?: boolean;
|
||||||
|
items: SelectOption[];
|
||||||
|
multiple?: boolean;
|
||||||
|
onBtnClick?: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
placeholder: '',
|
||||||
|
items: () => [],
|
||||||
|
onBtnClick: () => {},
|
||||||
|
multiple: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputValue = defineModel<string[]>();
|
||||||
|
|
||||||
|
const slots = useSlots();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'hover:bg-accent': !slots.tip,
|
||||||
|
'pointer-events-none opacity-50': disabled,
|
||||||
|
}"
|
||||||
|
class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1"
|
||||||
|
>
|
||||||
|
<span class="flex items-center text-sm">
|
||||||
|
<slot></slot>
|
||||||
|
|
||||||
|
<VbenTooltip v-if="slots.tip" side="bottom">
|
||||||
|
<template #trigger>
|
||||||
|
<CircleHelp class="ml-1 size-3 cursor-help" />
|
||||||
|
</template>
|
||||||
|
<slot name="tip"></slot>
|
||||||
|
</VbenTooltip>
|
||||||
|
</span>
|
||||||
|
<VbenCheckButtonGroup
|
||||||
|
v-model="inputValue"
|
||||||
|
class="h-8 w-[165px]"
|
||||||
|
:options="items"
|
||||||
|
:disabled="disabled"
|
||||||
|
:multiple="multiple"
|
||||||
|
@btn-click="onBtnClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,8 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { LayoutType } from '@vben/types';
|
import type { LayoutType } from '@vben/types';
|
||||||
|
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import CheckboxItem from '../checkbox-item.vue';
|
||||||
import NumberFieldItem from '../number-field-item.vue';
|
import NumberFieldItem from '../number-field-item.vue';
|
||||||
import SwitchItem from '../switch-item.vue';
|
import SwitchItem from '../switch-item.vue';
|
||||||
|
|
||||||
|
@ -18,6 +21,27 @@ const sidebarAutoActivateChild = defineModel<boolean>(
|
||||||
);
|
);
|
||||||
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
|
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
|
||||||
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
||||||
|
|
||||||
|
const sidebarButtons = defineModel<string[]>('sidebarButtons', { default: [] });
|
||||||
|
const sidebarCollapsedButton = defineModel<boolean>('sidebarCollapsedButton');
|
||||||
|
const sidebarFixedButton = defineModel<boolean>('sidebarFixedButton');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (
|
||||||
|
sidebarCollapsedButton.value &&
|
||||||
|
!sidebarButtons.value.includes('collapsed')
|
||||||
|
) {
|
||||||
|
sidebarButtons.value.push('collapsed');
|
||||||
|
}
|
||||||
|
if (sidebarFixedButton.value && !sidebarButtons.value.includes('fixed')) {
|
||||||
|
sidebarButtons.value.push('fixed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCheckboxChange = () => {
|
||||||
|
sidebarCollapsedButton.value = !!sidebarButtons.value.includes('collapsed');
|
||||||
|
sidebarFixedButton.value = !!sidebarButtons.value.includes('fixed');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -53,6 +77,17 @@ const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
||||||
>
|
>
|
||||||
{{ $t('preferences.sidebar.autoActivateChild') }}
|
{{ $t('preferences.sidebar.autoActivateChild') }}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
<CheckboxItem
|
||||||
|
:items="[
|
||||||
|
{ label: '收缩按钮', value: 'collapsed' },
|
||||||
|
{ label: '固定按钮', value: 'fixed' },
|
||||||
|
]"
|
||||||
|
multiple
|
||||||
|
v-model="sidebarButtons"
|
||||||
|
:on-btn-click="handleCheckboxChange"
|
||||||
|
>
|
||||||
|
按钮配置
|
||||||
|
</CheckboxItem>
|
||||||
<NumberFieldItem
|
<NumberFieldItem
|
||||||
v-model="sidebarWidth"
|
v-model="sidebarWidth"
|
||||||
:disabled="!sidebarEnable || disabled"
|
:disabled="!sidebarEnable || disabled"
|
||||||
|
|
|
@ -93,8 +93,9 @@ const sidebarCollapsedShowTitle = defineModel<boolean>(
|
||||||
const sidebarAutoActivateChild = defineModel<boolean>(
|
const sidebarAutoActivateChild = defineModel<boolean>(
|
||||||
'sidebarAutoActivateChild',
|
'sidebarAutoActivateChild',
|
||||||
);
|
);
|
||||||
const SidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
||||||
|
const sidebarCollapsedButton = defineModel<boolean>('sidebarCollapsedButton');
|
||||||
|
const sidebarFixedButton = defineModel<boolean>('sidebarFixedButton');
|
||||||
const headerEnable = defineModel<boolean>('headerEnable');
|
const headerEnable = defineModel<boolean>('headerEnable');
|
||||||
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
|
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
|
||||||
const headerMenuAlign =
|
const headerMenuAlign =
|
||||||
|
@ -317,8 +318,10 @@ async function handleReset() {
|
||||||
v-model:sidebar-collapsed="sidebarCollapsed"
|
v-model:sidebar-collapsed="sidebarCollapsed"
|
||||||
v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
|
v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
|
||||||
v-model:sidebar-enable="sidebarEnable"
|
v-model:sidebar-enable="sidebarEnable"
|
||||||
v-model:sidebar-expand-on-hover="SidebarExpandOnHover"
|
v-model:sidebar-expand-on-hover="sidebarExpandOnHover"
|
||||||
v-model:sidebar-width="sidebarWidth"
|
v-model:sidebar-width="sidebarWidth"
|
||||||
|
v-model:sidebar-collapsed-button="sidebarCollapsedButton"
|
||||||
|
v-model:sidebar-fixed-button="sidebarFixedButton"
|
||||||
:current-layout="appLayout"
|
:current-layout="appLayout"
|
||||||
:disabled="!isSideMode"
|
:disabled="!isSideMode"
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue