APP.vue
<div id="app">
<!-- label是radio的值value -->
<!--🌸 希望两个单选框是一组的话,控制一个值的话,就得有v-model -->
<hm-radio label="0" v-model="gender">男</hm-radio>
<hm-radio label="1" v-model="gender">女</hm-radio>
<hm-radio label="3" v-model="gender"></hm-radio>
<hm-radio label="4" v-model="gender"></hm-radio>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
gender: 1,//radio.vue
}
}
}
</script>
<style lang="less">
#app {
.hm-input {
width: 180px;
}
}
</style>
src/components/radio
<!-- 🌸当前radio的label值 = 父组件传过来的value值 -->
<label class="hm-radio" :class="{ 'is-checked': label == value }">
<span class="hm-radio_input">
<span class="hm-radio_inner"></span>
<input
type="radio"
class="hm-radio_original"
:value="label"
:name="name"
v-model="model"
/>
<!--🌸 这里的v-model应该和value绑定,但是value是传进来的值,所以不能直接写 -->
</span>
<span class="hm-radio_label">
<slot></slot>
<template v-if="!$slots.default">{{ label }}</template>
<!-- 🌸如果radio不传内容进来,就把label渲染进页面 -->
</span>
</label>
</template>
<script>
export default {
name: 'HmRadio',
props: {
label: {
type: [String, Number, Boolean],
default: ''
},
value: null,
name: {
type: String,
default: '',
}
},
//🌸双向绑定计算属性的话,必须提供他的get和set值
computed: {
model: {
get () {//获取父组件的value
return this.value
},
set (value) {
// 🌸触发父组件给当前组件注册的input事件,因为父组件有v-model,内部有value
this.$emit('input', value)
},
}
}
}
</script>
<style lang="less" scoped>
.hm-radio {
color: #606266;
font-weight: 500;
line-height: 1;
position: relative;
cursor: pointer;
display: inline-block;
white-space: nowrap;
outline: none;
font-size: 14px;
margin-right: 30px;
// -moz-user-select: none;
// -webkit-user-select: none;
// -ms-user-select: none;
.hm-radio_input {
white-space: nowrap;
cursor: pointer;
outline: none;
display: inline-block;
line-height: 1;
position: relative;
vertical-align: middle;
.hm-radio_inner {
border: 1px solid #dcdfe6;
border-radius: 100%;
width: 14px;
height: 14px;
background-color: #fff;
position: relative;
cursor: pointer;
display: inline-block;
box-sizing: border-box;
&:after {
width: 4px;
height: 4px;
border-radius: 100%;
background-color: #fff;
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) scale(0);
transition: transform 0.15s ease-in;
}
}
.hm-radio_original {
opacity: 0;
outline: none;
position: absolute;
z-index: 1;
top: 0;
left: 0px;
right: 0;
bottom: 0;
margin: 0;
}
}
.hm-radio_label {
font-size: 14px;
padding-left: 10px;
}
}
.hm-radio.is-checked {
.hm-radio_input {
.hm-radio_inner {
border-color: #409eff;
background: #409eff;
&:after {
transform: translate(-50%, -50%) scale(1);
}
}
}
}
.hm-radio_label {
color: #409eff;
}
</style>