feat: improve vbenCheckButtonGroup (#6329)
* 按钮组支持单选清除和多选限制最大选项数 * 按钮组支持icon插槽来定制图标
This commit is contained in:
parent
ea776aa710
commit
4102cc2211
@ -29,14 +29,25 @@ export type ValueType = boolean | number | string;
|
|||||||
|
|
||||||
export interface VbenButtonGroupProps
|
export interface VbenButtonGroupProps
|
||||||
extends Pick<VbenButtonProps, 'disabled'> {
|
extends Pick<VbenButtonProps, 'disabled'> {
|
||||||
|
/** 单选模式下允许清除选中 */
|
||||||
|
allowClear?: boolean;
|
||||||
|
/** 值改变前的回调 */
|
||||||
beforeChange?: (
|
beforeChange?: (
|
||||||
value: ValueType,
|
value: ValueType,
|
||||||
isChecked: boolean,
|
isChecked: boolean,
|
||||||
) => boolean | PromiseLike<boolean | undefined> | undefined;
|
) => boolean | PromiseLike<boolean | undefined> | undefined;
|
||||||
|
/** 按钮样式 */
|
||||||
btnClass?: any;
|
btnClass?: any;
|
||||||
|
/** 按钮间隔距离 */
|
||||||
gap?: number;
|
gap?: number;
|
||||||
|
/** 多选模式下限制最多选择的数量。0表示不限制 */
|
||||||
|
maxCount?: number;
|
||||||
|
/** 是否允许多选 */
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
|
/** 选项 */
|
||||||
options?: { [key: string]: any; label: CustomRenderType; value: ValueType }[];
|
options?: { [key: string]: any; label: CustomRenderType; value: ValueType }[];
|
||||||
|
/** 显示图标 */
|
||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
|
/** 尺寸 */
|
||||||
size?: 'large' | 'middle' | 'small';
|
size?: 'large' | 'middle' | 'small';
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ const props = withDefaults(defineProps<VbenButtonGroupProps>(), {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
size: 'middle',
|
size: 'middle',
|
||||||
|
allowClear: false,
|
||||||
|
maxCount: 0,
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['btnClick']);
|
const emit = defineEmits(['btnClick']);
|
||||||
const btnDefaultProps = computed(() => {
|
const btnDefaultProps = computed(() => {
|
||||||
@ -82,13 +84,23 @@ async function onBtnClick(value: ValueType) {
|
|||||||
if (innerValue.value.includes(value)) {
|
if (innerValue.value.includes(value)) {
|
||||||
innerValue.value = innerValue.value.filter((item) => item !== value);
|
innerValue.value = innerValue.value.filter((item) => item !== value);
|
||||||
} else {
|
} else {
|
||||||
|
if (props.maxCount > 0 && innerValue.value.length >= props.maxCount) {
|
||||||
|
innerValue.value = innerValue.value.slice(0, props.maxCount - 1);
|
||||||
|
}
|
||||||
innerValue.value.push(value);
|
innerValue.value.push(value);
|
||||||
}
|
}
|
||||||
modelValue.value = innerValue.value;
|
modelValue.value = innerValue.value;
|
||||||
|
} else {
|
||||||
|
if (props.allowClear && innerValue.value.includes(value)) {
|
||||||
|
innerValue.value = [];
|
||||||
|
modelValue.value = undefined;
|
||||||
|
emit('btnClick', undefined);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
innerValue.value = [value];
|
innerValue.value = [value];
|
||||||
modelValue.value = value;
|
modelValue.value = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
emit('btnClick', value);
|
emit('btnClick', value);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -112,12 +124,18 @@ async function onBtnClick(value: ValueType) {
|
|||||||
@click="onBtnClick(btn.value)"
|
@click="onBtnClick(btn.value)"
|
||||||
>
|
>
|
||||||
<div class="icon-wrapper" v-if="props.showIcon">
|
<div class="icon-wrapper" v-if="props.showIcon">
|
||||||
|
<slot
|
||||||
|
name="icon"
|
||||||
|
:loading="loadingValues.includes(btn.value)"
|
||||||
|
:checked="innerValue.includes(btn.value)"
|
||||||
|
>
|
||||||
<LoaderCircle
|
<LoaderCircle
|
||||||
class="animate-spin"
|
class="animate-spin"
|
||||||
v-if="loadingValues.includes(btn.value)"
|
v-if="loadingValues.includes(btn.value)"
|
||||||
/>
|
/>
|
||||||
<CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
|
<CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
|
||||||
<Circle v-else />
|
<Circle v-else />
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<slot name="option" :label="btn.label" :value="btn.value" :data="btn">
|
<slot name="option" :label="btn.label" :value="btn.value" :data="btn">
|
||||||
<VbenRenderContent :content="btn.label" />
|
<VbenRenderContent :content="btn.label" />
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
VbenButtonGroup,
|
VbenButtonGroup,
|
||||||
VbenCheckButtonGroup,
|
VbenCheckButtonGroup,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
|
import { LoaderCircle, Square, SquareCheckBig } from '@vben/icons';
|
||||||
|
|
||||||
import { Button, Card, message } from 'ant-design-vue';
|
import { Button, Card, message } from 'ant-design-vue';
|
||||||
|
|
||||||
@ -51,6 +52,7 @@ const compProps = reactive({
|
|||||||
gap: 0,
|
gap: 0,
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
size: 'middle',
|
size: 'middle',
|
||||||
|
allowClear: false,
|
||||||
} as Recordable<any>);
|
} as Recordable<any>);
|
||||||
|
|
||||||
const [Form] = useVbenForm({
|
const [Form] = useVbenForm({
|
||||||
@ -63,6 +65,9 @@ const [Form] = useVbenForm({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
commonConfig: {
|
||||||
|
labelWidth: 150,
|
||||||
|
},
|
||||||
schema: [
|
schema: [
|
||||||
{
|
{
|
||||||
component: 'RadioGroup',
|
component: 'RadioGroup',
|
||||||
@ -109,6 +114,20 @@ const [Form] = useVbenForm({
|
|||||||
fieldName: 'beforeChange',
|
fieldName: 'beforeChange',
|
||||||
label: '前置回调',
|
label: '前置回调',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Switch',
|
||||||
|
defaultValue: false,
|
||||||
|
fieldName: 'allowClear',
|
||||||
|
label: '允许清除',
|
||||||
|
help: '单选时是否允许取消选中(值为undefined)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'InputNumber',
|
||||||
|
defaultValue: 0,
|
||||||
|
fieldName: 'maxCount',
|
||||||
|
label: '最大选中数量',
|
||||||
|
help: '多选时有效,0表示不限制',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
submitOnChange: true,
|
submitOnChange: true,
|
||||||
@ -186,6 +205,21 @@ function onBtnClick(value: any) {
|
|||||||
v-bind="compProps"
|
v-bind="compProps"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<Card title="设置" class="mt-4">
|
<Card title="设置" class="mt-4">
|
||||||
|
Loading…
Reference in New Issue
Block a user