【vue3组件封装】checkbox复选框与复选框组,radioGroup单选框

源码:https://gitee.com/chenxiangzhi/components.git
历史文章:

  1. 封装含校验的Input

Checkbox

单个复选框

01 使用

<s-checkbox
    :checked="state.remember"
    name="remember"
    @update="updateForm"
    label="记住密码"
/>

<script lang="ts" setup>
import { SCheckbox } from "@/components"
// ...

// 返回一个对象,包括name和value,value为布尔值
const updateForm = (param:BaseForm) => {
  state[param.name] = param.value
}
</script>

02 Types

type CheckboxProp={
	checked:boolean,  // 值
	name:string,      // 名称
	label:string			  
}

type BaseForm = {
   name: string;
   value: string | number | boolean;
};

03 封装

<template>
    <div class="s-checkbox">
        <input type="checkbox" id="checkbox" :checked="props.checked" @input="update">
        <label for="checkbox">{{ props.label }}</label>
    </div>
</template>

<script lang="ts" setup>
const emit = defineEmits(["update"])

const props = defineProps({
  checked: {
    type: Boolean,
    default: false
  },
  name: String,
  label: {
    type: String,
    default: ""
  }
})

const update = (e:Event) => {
  const targetChecked = (e.target as HTMLInputElement).checked
  emit("update", { name: props.name, value: targetChecked })
}
</script>

Checkbox-Group

复选框组

01 使用

<template>
	<s-checkbox-group
	   name="selectedKeys"
	   :value="state.selectedKeys"
	   :options="[{label:'1',value:1},{label:'2',value:2}]"
	   @update="changeOptions"
	/>
</template>


<script lang="ts" setup>
import { SCheckboxGroup } from "@/components"
import { reactive } from "vue"

const state = reactive({
  selectedKeys: []
})

// 返回选中的value数组和选项数组
const changeOptions = (keys, options) => {
  console.log(keys, options)
}

</script>

02 Types

// 单个选项
type CheckboxOption={
    label: string,	// 显示的label
    value: string|number, // 对应的值
    key?:string, 
    checked?:boolean // 是否选中
}

// 组件属性
type CheckboxGroupProp={
    value:Array<string|number>, // 选中的值数组
    name:string, // 名称
    layout:"row" | "column", // 排列方向
    options:CheckboxOption[] // 可选列表
}

03 封装

<template>
   <div :class="['s-checkbox-group',{column:props.layout==='column'},{row:props.layout==='row'}]">
        <div class="s-checkbox-group__option" v-for="item in state.options" :key="item.key">
            <input type="checkbox" :id="item.key" :checked="item.checked" @input="(e)=>{update(e,item)}">
            <label :for="item.key">{{ item.label }}</label>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { computed, PropType, reactive } from "vue"
import { CheckboxOption} from "@types"
const emit = defineEmits(["update"])

// 组件属性
const props = defineProps({
  value: {
    type: Array,
    default: () => []
  },
  name: String,
  options: {
    type: Array as PropType<CheckboxOption[]>,
    default: () => []
  },
  layout: {
    type: String,
    default: "row"
  }
})

// 初始化选项:加入key属性和checked属性
function initOptions () {
  return props.options.length
    ? props.options.map(op => {
      if (!op.key) {
        op.key = op.value.toString()
      }
      if (props.value.length) {
        op.checked = !!props.value.includes(op.value)
      } else {
        op.checked = false
      }
      return op
    })
    : []
}

// 响应式状态
const state = reactive({
  options: initOptions()
})

// 选中的选项
const selectedOptions = computed(() => state.options.filter(op => op.checked))

// 更新与传递
const update = (e:Event, item:CheckboxOption) => {
  const targetChecked = (e.target as HTMLInputElement).checked
  const target = state.options.find(op => op.key === item.key)
  if (target) {
    target.checked = targetChecked
    emit("update", selectedOptions.value.map(op => op.value), selectedOptions)
  }
}
</script>

<style lang="less">
.s-checkbox-group{
  &.row .s-checkbox-group__option{
    display: inline-block;
    margin-right: @itemSpace
  }
  &.column .s-checkbox-group__option{
    height: @listHeight;
    line-height: @listHeight;
  }
}

</style>

RadioGroup

单选框

01 使用

<template>
 <s-radio-group
     name="status"
     :options="statusOptions"
     layout="column"
     :value="state.status"
     @update="updateForm"
 />
</template>

<script lang="ts" setup>
import { SRadioGroup } from "@/components"
// ...
const statusOptions = [
    { label: "启用", value: 1 },
    { label: "禁用", value: 0 }
]
const updateForm = (params: BaseForm) => {
    state[params.name]: Number(params.value)
}
</script>

02 Types

// 单个选项
type Radio = {
   label: string;
   value: string | number;
   key?: string;
};

// 选项数组
type RadioOptions = Array<Radio>;

// 属性
type RadioGroupProp={
	options:RadioOptions, // 选项数组
	value:string,      		// 选项值
	name:string,			// 名称
	layout?:'row' | 'column' // 排列方式
}

03 封装

<template>
    <div :class="['s-radio-group',{column:props.layout==='column'},{row:props.layout==='row'}]">
        <div class="s-radio" v-for="item in props.options" :key="item.value" @click="onChange">
            <input
                type="radio"
                :id="item.value"
                :value="item.value"
                :checked="props.value === item.value"
            />
            <label :for="item.value" :data-value="item.value">{{ item.label }}</label>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { PropType } from "vue"
import { RadioOptions } from "@types"
const props = defineProps({
  options: Array as PropType<RadioOptions>,
  value: [String, Number],
  name: String,
  layout: {
    type: String,
    default: "row"
  }
})

const emit = defineEmits(["update"])

// 点击单选框或者label都可以触发
const onChange = (e: Event) => {
  const target = (e.target as HTMLInputElement)
  if (target.tagName === "LABEL") {
    emit("update", { name: props.name, value: target.dataset.value })
  } else if (target.tagName === "INPUT") {
    emit("update", { name: props.name, value: target.value })
  }
}
</script>
<style lang="less" scoped>
    .column{
        display: flex;
        flex-direction: column;
        margin-bottom: 10px;
    }
    .row{
      display: flex;
    }
    .s-radio-group{
        .s-radio {
            input,label{
                cursor: pointer;
            }
            line-height: @listHeight;
            height: @listHeight;
            margin-right: 10px;
            white-space: nowrap;
        }
    }

</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值