从零搭建Vue3.0组件库之checkbox组件
一.设计组件属性
export interface ICheckboxProps {
name?: string, // input中name属性
label?: string | boolean | number, // v-model为array时使用
modelValue: string | boolean | number, // 绑定的值
indeterminate?: boolean // 是否半选
disabled?: boolean // 禁用
checked?: boolean // 是否选中
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
二.编写checkbox组件
<template>
<label class="z-checkbox">
<span class="z-checkbox__input">
<input
type="checkbox"
:value="label"
:disabled="disabled"
/>
</span>
<!-- 没有默认 有label -->
<span v-if="$slots.default || label" class="z-checkbox__label">
<slot></slot>
<template v-if="!$slots.default">{{ label }}</template>
</span>
</label>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useCheckbox } from "./useCheckbox";
export default defineComponent({
name: "ZCheckbox",
props: {
name: {
// checkbox name属性
type: String,
},
modelValue: {
// input绑定的值
type: [Boolean, Number, String],
},
label: {
// 选中状态的值
type: [Boolean, Number, String],
},
indeterminate: Boolean, // 半选
disabled: Boolean, // 禁用
checked: Boolean, // 是否选中
}
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
1.实现checkbox双向绑定
<input
type="checkbox"
:value="label"
:disabled="disabled"
v-model="model" // 双向绑定的字段
:checked="isChecked" // input的状态
@change="handleChange" // 用于将checked字段传递给用户
/>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
const useModel = (props: ICheckboxProps) => {
const { emit } = getCurrentInstance();
const model = computed({
get() {
return props.modelValue
},
set(val: unknown) {
emit('update:modelValue', val);
}
});
return model;
}
const useCheckboxStatus = (props: ICheckboxProps, model) => {
const isChecked = computed(() => {
const value = model.value;
return value;
});
return isChecked
}
const useEvent = () => {
const { emit } = getCurrentInstance()
// checkbox修改事件
function handleChange(e: InputEvent) {
const target = e.target as HTMLInputElement;
const value = target.checked ? true : false; // 获取checked属性,触发修改逻辑
emit('change', value)
}
return handleChange
}
export function useCheckbox(props: ICheckboxProps) {
// 1.实现用于双向绑定的model属性
const model = useModel(props);
const isChecked = useCheckboxStatus(props, model);
const handleChange = useEvent();
return {
model,
isChecked,
handleChange
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2.实现半选功能
const inputRef = ref<HTMLInputElement>(null)
function indeterminate(val) {
inputRef.value.indeterminate = val;
}
watch(() => props.indeterminate, indeterminate)
onMounted(()=>{ // 默认加载完毕后
indeterminate(props.indeterminate)
});
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
三.编写Checkbox-Group组件
checkbox-group组件的功能主要是将数据同步给checkbox组件中
<template>
<div class="z-checkbox-group">
<slot></slot>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, provide, toRefs } from "vue";
export default defineComponent({
name: "ZCheckboxGroup",
props: {
modelValue: {
type: Array,
},
disabled: Boolean,
},
emits:['change','update:modelValue'],
setup(props, ctx) {
const modelValue = computed(()=>props.modelValue);
const changeEvent = (val) => { // 此事件用于子同步数据到checkbox-group中
ctx.emit("update:modelValue", val);
ctx.emit('change',val)
};
provide("CheckBoxGroup", { // 注入数据
name: "ZCheckBoxGroup",
modelValue,
changeEvent,
});
},
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
注入实例时编写实例类型
export interface ICheckboxGroupInstance {
modelValue?: ComputedRef, // 绑定的值
name?: string
changeEvent?: (...args: any[]) => any // 修改事件
}
1
2
3
4
5
2
3
4
5
const useCheckboxGroup = () => {
const checkboxGroup = inject<ICheckboxGroupInstance>('CheckBoxGroup', {});
const isGroup = computed(() => checkboxGroup.name == 'ZCheckBoxGroup')
return {
isGroup,
checkboxGroup
}
}
const useModel = (props: ICheckboxProps) => { // 1.针对checkbox-group处理数据
const { emit } = getCurrentInstance();
const { isGroup, checkboxGroup } = useCheckboxGroup(); // 针对checkbox组特殊处理
const store = computed(() => checkboxGroup ? checkboxGroup.modelValue?.value : props.modelValue); // 将父组件v-model数据获取到
const model = computed({
get() {
return isGroup.value ? store.value : props.modelValue
},
set(val: unknown) {
if (isGroup.value) { // 如果是checkbox 组让父级处理
checkboxGroup.changeEvent(val)
} else {
emit('update:modelValue', val);
}
}
});
return model;
}
const useCheckboxStatus = (props: ICheckboxProps, model) => { // 2.针对label来处理选中逻辑
const isChecked = computed(() => {
const value = model.value;
if(typeof value == 'boolean'){
return value;
}else if(Array.isArray(value)){ // 如果是数组 看是否包含这一项,来确定checked的值
return value.includes(props.label)
}
return value;
});
return isChecked
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38