源码:https://gitee.com/chenxiangzhi/components.git
历史文章:
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>