Browse Source

feat: improve vbenCheckButtonGroup (#6329)

* 按钮组支持单选清除和多选限制最大选项数

* 按钮组支持icon插槽来定制图标
Netfan 3 weeks ago
parent
commit
4102cc2211

+ 11 - 0
packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts

@@ -29,14 +29,25 @@ export type ValueType = boolean | number | string;
 
 export interface VbenButtonGroupProps
   extends Pick<VbenButtonProps, 'disabled'> {
+  /** 单选模式下允许清除选中 */
+  allowClear?: boolean;
+  /** 值改变前的回调 */
   beforeChange?: (
     value: ValueType,
     isChecked: boolean,
   ) => boolean | PromiseLike<boolean | undefined> | undefined;
+  /** 按钮样式 */
   btnClass?: any;
+  /** 按钮间隔距离 */
   gap?: number;
+  /** 多选模式下限制最多选择的数量。0表示不限制 */
+  maxCount?: number;
+  /** 是否允许多选 */
   multiple?: boolean;
+  /** 选项 */
   options?: { [key: string]: any; label: CustomRenderType; value: ValueType }[];
+  /** 显示图标 */
   showIcon?: boolean;
+  /** 尺寸 */
   size?: 'large' | 'middle' | 'small';
 }

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

@@ -19,6 +19,8 @@ const props = withDefaults(defineProps<VbenButtonGroupProps>(), {
   multiple: false,
   showIcon: true,
   size: 'middle',
+  allowClear: false,
+  maxCount: 0,
 });
 const emit = defineEmits(['btnClick']);
 const btnDefaultProps = computed(() => {
@@ -82,12 +84,22 @@ async function onBtnClick(value: ValueType) {
     if (innerValue.value.includes(value)) {
       innerValue.value = innerValue.value.filter((item) => item !== value);
     } else {
+      if (props.maxCount > 0 && innerValue.value.length >= props.maxCount) {
+        innerValue.value = innerValue.value.slice(0, props.maxCount - 1);
+      }
       innerValue.value.push(value);
     }
     modelValue.value = innerValue.value;
   } else {
-    innerValue.value = [value];
-    modelValue.value = value;
+    if (props.allowClear && innerValue.value.includes(value)) {
+      innerValue.value = [];
+      modelValue.value = undefined;
+      emit('btnClick', undefined);
+      return;
+    } else {
+      innerValue.value = [value];
+      modelValue.value = value;
+    }
   }
   emit('btnClick', value);
 }
@@ -112,12 +124,18 @@ async function onBtnClick(value: ValueType) {
       @click="onBtnClick(btn.value)"
     >
       <div class="icon-wrapper" v-if="props.showIcon">
-        <LoaderCircle
-          class="animate-spin"
-          v-if="loadingValues.includes(btn.value)"
-        />
-        <CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
-        <Circle v-else />
+        <slot
+          name="icon"
+          :loading="loadingValues.includes(btn.value)"
+          :checked="innerValue.includes(btn.value)"
+        >
+          <LoaderCircle
+            class="animate-spin"
+            v-if="loadingValues.includes(btn.value)"
+          />
+          <CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
+          <Circle v-else />
+        </slot>
       </div>
       <slot name="option" :label="btn.label" :value="btn.value" :data="btn">
         <VbenRenderContent :content="btn.label" />

+ 34 - 0
playground/src/views/examples/button-group/index.vue

@@ -9,6 +9,7 @@ import {
   VbenButtonGroup,
   VbenCheckButtonGroup,
 } from '@vben/common-ui';
+import { LoaderCircle, Square, SquareCheckBig } from '@vben/icons';
 
 import { Button, Card, message } from 'ant-design-vue';
 
@@ -51,6 +52,7 @@ const compProps = reactive({
   gap: 0,
   showIcon: true,
   size: 'middle',
+  allowClear: false,
 } as Recordable<any>);
 
 const [Form] = useVbenForm({
@@ -63,6 +65,9 @@ const [Form] = useVbenForm({
       }
     });
   },
+  commonConfig: {
+    labelWidth: 150,
+  },
   schema: [
     {
       component: 'RadioGroup',
@@ -109,6 +114,20 @@ const [Form] = useVbenForm({
       fieldName: 'beforeChange',
       label: '前置回调',
     },
+    {
+      component: 'Switch',
+      defaultValue: false,
+      fieldName: 'allowClear',
+      label: '允许清除',
+      help: '单选时是否允许取消选中(值为undefined)',
+    },
+    {
+      component: 'InputNumber',
+      defaultValue: 0,
+      fieldName: 'maxCount',
+      label: '最大选中数量',
+      help: '多选时有效,0表示不限制',
+    },
   ],
   showDefaultActions: false,
   submitOnChange: true,
@@ -186,6 +205,21 @@ function onBtnClick(value: any) {
           v-bind="compProps"
         />
       </div>
+      <p class="mt-4">自定义图标{{ checkValue }}</p>
+      <div class="mt-2 flex flex-col gap-2">
+        <VbenCheckButtonGroup
+          v-model="checkValue"
+          multiple
+          :options="options"
+          v-bind="compProps"
+        >
+          <template #icon="{ loading, checked }">
+            <LoaderCircle class="animate-spin" v-if="loading" />
+            <SquareCheckBig v-else-if="checked" />
+            <Square v-else />
+          </template>
+        </VbenCheckButtonGroup>
+      </div>
     </Card>
 
     <Card title="设置" class="mt-4">