Просмотр исходного кода

feat: sidebar button config (#5818)

* feat: 新增 PreferenceCheckboxItem 组件

* feat(preferences): 添加侧边栏按钮配置功能

* feat: 新增按钮点击事件触发功能

* feat(SidebarPreferences): 新增侧边栏折叠按钮与固定按钮配置

* feat(ui): 新增侧边栏固定按钮及配置选项

* fix(test): 修正侧边栏配置项缺失问题
Jin Mao 5 месяцев назад
Родитель
Сommit
18722ce434

+ 2 - 0
packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap

@@ -68,10 +68,12 @@ exports[`defaultPreferences immutability test > should not modify the config obj
   "sidebar": {
     "autoActivateChild": false,
     "collapsed": false,
+    "collapsedButton": true,
     "collapsedShowTitle": false,
     "enable": true,
     "expandOnHover": true,
     "extraCollapse": false,
+    "fixedButton": true,
     "hidden": false,
     "width": 224,
   },

+ 2 - 0
packages/@core/preferences/src/config.ts

@@ -68,10 +68,12 @@ const defaultPreferences: Preferences = {
   sidebar: {
     autoActivateChild: false,
     collapsed: false,
+    collapsedButton: true,
     collapsedShowTitle: false,
     enable: true,
     expandOnHover: true,
     extraCollapse: false,
+    fixedButton: true,
     hidden: false,
     width: 224,
   },

+ 4 - 0
packages/@core/preferences/src/types.ts

@@ -132,6 +132,8 @@ interface SidebarPreferences {
   autoActivateChild: boolean;
   /** 侧边栏是否折叠 */
   collapsed: boolean;
+  /** 侧边栏折叠按钮是否可见 */
+  collapsedButton: boolean;
   /** 侧边栏折叠时,是否显示title */
   collapsedShowTitle: boolean;
   /** 侧边栏是否可见 */
@@ -140,6 +142,8 @@ interface SidebarPreferences {
   expandOnHover: boolean;
   /** 侧边栏扩展区域是否折叠 */
   extraCollapse: boolean;
+  /** 侧边栏固定按钮是否可见 */
+  fixedButton: boolean;
   /** 侧边栏是否隐藏 - css */
   hidden: boolean;
   /** 侧边栏宽度 */

+ 8 - 2
packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue

@@ -65,9 +65,14 @@ interface Props {
   show?: boolean;
   /**
    * 显示折叠按钮
-   * @default false
+   * @default true
    */
   showCollapseButton?: boolean;
+  /**
+   * 显示固定按钮
+   * @default true
+   */
+  showFixedButton?: boolean;
   /**
    * 主题
    */
@@ -95,6 +100,7 @@ const props = withDefaults(defineProps<Props>(), {
   paddingTop: 0,
   show: true,
   showCollapseButton: true,
+  showFixedButton: true,
   zIndex: 0,
 });
 
@@ -267,7 +273,7 @@ function handleMouseleave() {
     @mouseleave="handleMouseleave"
   >
     <SidebarFixedButton
-      v-if="!collapse && !isSidebarMixed"
+      v-if="!collapse && !isSidebarMixed && showFixedButton"
       v-model:expand-on-hover="expandOnHover"
     />
     <div v-if="slots.logo" :style="headerStyle">

+ 10 - 0
packages/@core/ui-kit/layout-ui/src/vben-layout.ts

@@ -106,6 +106,11 @@ interface VbenLayoutProps {
    * @default false
    */
   sidebarCollapse?: boolean;
+  /**
+   * 侧边菜单折叠按钮
+   * @default true
+   */
+  sidebarCollapsedButton?: boolean;
   /**
    * 侧边菜单是否折叠时,是否显示title
    * @default true
@@ -121,6 +126,11 @@ interface VbenLayoutProps {
    * @default 48
    */
   sidebarExtraCollapsedWidth?: number;
+  /**
+   * 侧边菜单折叠按钮是否固定
+   * @default true
+   */
+  sidebarFixedButton?: boolean;
   /**
    * 侧边栏是否隐藏
    * @default false

+ 4 - 0
packages/@core/ui-kit/layout-ui/src/vben-layout.vue

@@ -49,8 +49,10 @@ const props = withDefaults(defineProps<Props>(), {
   headerVisible: true,
   isMobile: false,
   layout: 'sidebar-nav',
+  sidebarCollapsedButton: true,
   sidebarCollapseShowTitle: false,
   sidebarExtraCollapsedWidth: 60,
+  sidebarFixedButton: true,
   sidebarHidden: false,
   sidebarMixedWidth: 80,
   sidebarTheme: 'dark',
@@ -487,6 +489,8 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
       v-model:expand-on-hovering="sidebarExpandOnHovering"
       v-model:extra-collapse="sidebarExtraCollapse"
       v-model:extra-visible="sidebarExtraVisible"
+      :show-collapse-button="sidebarCollapsedButton"
+      :show-fixed-button="sidebarFixedButton"
       :collapse-width="getSideCollapseWidth"
       :dom-visible="!isMobile"
       :extra-width="sidebarExtraWidth"

+ 2 - 1
packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue

@@ -20,7 +20,7 @@ const props = withDefaults(defineProps<VbenButtonGroupProps>(), {
   showIcon: true,
   size: 'middle',
 });
-
+const emit = defineEmits(['btnClick']);
 const btnDefaultProps = computed(() => {
   return {
     ...objectOmit(props, ['options', 'btnClass', 'size', 'disabled']),
@@ -90,6 +90,7 @@ async function onBtnClick(value: ValueType) {
     innerValue.value = [value];
     modelValue.value = value;
   }
+  emit('btnClick', value);
 }
 </script>
 <template>

+ 2 - 0
packages/effects/layouts/src/basic/layout.vue

@@ -192,6 +192,8 @@ const headerSlots = computed(() => {
     :sidebar-collapse="preferences.sidebar.collapsed"
     :sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
     :sidebar-enable="sidebarVisible"
+    :sidebar-collapsed-button="preferences.sidebar.collapsedButton"
+    :sidebar-fixed-button="preferences.sidebar.fixedButton"
     :sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
     :sidebar-extra-collapse="preferences.sidebar.extraCollapse"
     :sidebar-hidden="preferences.sidebar.hidden"

+ 63 - 0
packages/effects/layouts/src/widgets/preferences/blocks/checkbox-item.vue

@@ -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>

+ 35 - 0
packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue

@@ -1,8 +1,11 @@
 <script setup lang="ts">
 import type { LayoutType } from '@vben/types';
 
+import { onMounted } from 'vue';
+
 import { $t } from '@vben/locales';
 
+import CheckboxItem from '../checkbox-item.vue';
 import NumberFieldItem from '../number-field-item.vue';
 import SwitchItem from '../switch-item.vue';
 
@@ -18,6 +21,27 @@ const sidebarAutoActivateChild = defineModel<boolean>(
 );
 const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
 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>
 
 <template>
@@ -53,6 +77,17 @@ const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
   >
     {{ $t('preferences.sidebar.autoActivateChild') }}
   </SwitchItem>
+  <CheckboxItem
+    :items="[
+      { label: '收缩按钮', value: 'collapsed' },
+      { label: '固定按钮', value: 'fixed' },
+    ]"
+    multiple
+    v-model="sidebarButtons"
+    :on-btn-click="handleCheckboxChange"
+  >
+    按钮配置
+  </CheckboxItem>
   <NumberFieldItem
     v-model="sidebarWidth"
     :disabled="!sidebarEnable || disabled"

+ 6 - 3
packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue

@@ -93,8 +93,9 @@ const sidebarCollapsedShowTitle = defineModel<boolean>(
 const sidebarAutoActivateChild = defineModel<boolean>(
   '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 headerMode = defineModel<LayoutHeaderModeType>('headerMode');
 const headerMenuAlign =
@@ -317,8 +318,10 @@ async function handleReset() {
                 v-model:sidebar-collapsed="sidebarCollapsed"
                 v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
                 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-collapsed-button="sidebarCollapsedButton"
+                v-model:sidebar-fixed-button="sidebarFixedButton"
                 :current-layout="appLayout"
                 :disabled="!isSideMode"
               />