vue3+ts实现checkbox-group组件

1、checkbox.vue

<template>
  <div class="z-checkbox">
    <span class="z-checkbox__input">
      <input
        type="checkbox"
        v-model="model"
        :checked="isChecked"
        @change="handleChange"
        :name="name"
        :disabled="disabled"
        :indeterminate="indeterminate"
        :value="label"
      />
      <!-- vue的特点 如果对于checkbox而言 绑定的数据是数组,那么value在v-model中的数据中则被选中 -->
    </span>
    <span class="z-checkbox-label">
      <slot>{{label}}</slot>
    </span>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { useCheckbox } from "./useCheckbox";

export default defineComponent({
  name: "ZCheckbox",
  props: {
    // 属性校验 vue2 里面也是一样
    name: String,
    indeterminate: Boolean,
    checked: Boolean,
    disabled: Boolean,
    label: [String, Number, Boolean],
    modelValue: [String, Number, Boolean],
  },
  emits: ["update:modelValue", "change"], // ts没提示 而且方法会被绑定到根上
  setup(props, { emit, attrs }) {
    return useCheckbox(props);
  },
});
</script>

2、checkbox.types

import { ComputedRef } from "vue";

export interface ICheckboxProps {
    indeterminate?: boolean, // 是否半选
    checked?: boolean, // 是否选中
    name?: string, // 原生的name
    disabled?: boolean, // 是否禁用
    label?: string | number | boolean, // 目前没有 group中使用
    modelValue?: string |number | boolean // 绑定checkbox的值
}
// ts中接口中的可有可无


export interface ICheckboxGroupProvide{
    modelValue?:ComputedRef,
    changeEvent?: (val:unknown) => void,
    name?:string
}

2、useCheckbox.ts

import { computed, getCurrentInstance, inject, WritableComputedRef } from "vue";
import { ICheckboxGroupProvide, ICheckboxProps } from "./ckeckbox.types";
const useCheckboxGroup = () => {
    const checkboxGroup = inject<ICheckboxGroupProvide>('ZCheckboxGroup', {})
    const isGroup = checkboxGroup.name == 'ZCheckboxGroup'; // 判断有没有父亲叫ZCheckboxGroup
    return {
        isGroup,
        checkboxGroup
    }
}


const useModel = (props: ICheckboxProps) => {
    // getCurrentInstance是vue3中用于获取组件的上下文
    const { emit } = getCurrentInstance(); // 以前只有checkbox的时候 用户会传递modelValue
    const { isGroup, checkboxGroup } = useCheckboxGroup();
    const store = computed(() => checkboxGroup ? checkboxGroup.modelValue?.value : props.modelValue); // 从爸爸的modelValue取出来 传递给自己, type="checkbox" 可以绑定数组
    const model = computed({
        get() {
            return isGroup ? store.value : props.modelValue
        },
        set(val) {
            if(isGroup){ // 只要是组 就需要触发组的更新方法,不触发自己的
                return checkboxGroup.changeEvent(val)
            }
            emit('update:modelValue', val);
        }
    });
    return model
}
const useCheckboxStatus = (props: ICheckboxProps, model: WritableComputedRef<unknown>) => {
    const isChecked = computed(() => {
        const value = model.value; // 当前是不是选中的  [上海,深圳]
        // todo...
        if(Array.isArray(value)){ // 针对父组件传递过来的数组
            return value.includes(props.label)
        }else{ // 针对true false
            return value
        }
    });
    return isChecked
}
const useEvent = () => {
    const { emit } = getCurrentInstance()
    const handleChange = (e: InputEvent) => {
        const target = e.target as HTMLInputElement;
        emit('change', target.checked);
    }
    return handleChange
}
export const useCheckbox = (props: ICheckboxProps) => {
    // 1.设计一个属性 这个属性采用的就是modelValue, 还能更改,更改的时候要触发一个事件,更新数据
    let model = useModel(props);

    // 2.需要给checkbox 设置一个checked的状态,等一会我们更改checkbox选中或者取消选中需要获取到checked状态
    const isChecked = useCheckboxStatus(props, model);

    // 3.创造一个change事件 可以触发绑定到自己身上的change
    const handleChange = useEvent();

    // 每次状态发生变化 都需要 调用changeEvent来触发更新

    return {
        model,
        isChecked,
        handleChange
    }
}

3、checkbox-group.vue

<template>
  <div class="z-checkbox-group">
    <slot></slot>
  </div>
</template>
<script lang="ts">
import { computed, defineComponent, provide } from "vue";

export default defineComponent({
  name: "ZCheckboxGroup",
  props: {
    modelValue: Array, // 还是需要 父组件将modelValue这个数组传递给儿子
  },
  emits: ["change", "update:modelValue"],
  setup(props, { emit }) {
    // vue3 provide inject computed watch getInstance
    // 将props属性创造出一个新的来交给儿子
    const modelValue = computed(() => props.modelValue);
    const changeEvent = (val) => { // 儿子等会调用这个方法去通知更新
      emit("change", val);  // change
      emit("update:modelValue", val); // v-model
    };
    provide('ZCheckboxGroup',{
        name:'ZCheckboxGroup',
        modelValue,
        changeEvent
    })
  },
});
</script>

4.use.vue

<template> 
  <!-- 组件内部 会去 emit('update:modelValue',[])   checkVal = []-->
  {{ checkVal }}
  <z-checkbox-group v-model="checkVal" @change="checkboxChange">
    <z-checkbox v-for="c in checks" :key="c" :label="c"></z-checkbox>
  </z-checkbox-group>
</template>

5、useCheckbox.ts

import { ref } from "vue";

export function useCheckbox() {
    // 定义数据尽量使用ref,方便视图更新 
    const checkVal = ref(['上海','深圳']);
    const checks = ref(['上海','北京','天津','深圳']);
    const checkboxChange = (val) =>{
      console.log(val)
    }
    return {
      checkVal,
      checkboxChange,
      checks
    }
  }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值