本文最后更新于 8 天前,如有失效请评论区留言。
更优雅的el-radio-button实现方式
小鹿最近想要做一个类似于el-radio-button的一个组件,但是我发现官方的组件并不支持我自定义其中的内容,官方文档只给了一个label属性让我填入我所需要展示的label,所以我在这里自己写了一个公共组件,支持高度自定义,参数都可以传入。
效果展示
代码实现
<template>
<div class="binary-selector">
<div v-if="label" class="selector-label">
<span v-if="required" class="required">*</span>
{{ label }}:
</div>
<el-radio-group
v-model="selectedValue"
@change="handleChange"
class="radio-group"
:style="{ width: width, height: height }"
>
<el-radio-button
v-for="option in options"
:key="option.value"
:label="option.value"
class="custom-radio-button"
>
<div class="radio-content">
{{ option.label }}
<div v-if="selectedValue === option.value" class="selected-corner">
<img class="corner-triangle" src="../assets/icons/corner-triangle.svg" alt="" />
<img class="check-mark" src="../assets/icons/check-mark.svg" alt="" />
</div>
</div>
</el-radio-button>
</el-radio-group>
</div>
</template>
<script lang="ts" setup>
import { ref, defineProps, defineEmits, onMounted } from 'vue';
interface Option {
label: string;
value: string | number | boolean;
}
interface Props {
label?: string;
required?: boolean;
modelValue?: Option['value'];
options: Option[];
width?: string;
height?: string;
}
const props = withDefaults(defineProps<Props>(), {
label: '',
required: false,
modelValue: '',
options: () => [],
width: '360px',
height: '32px',
});
const emit = defineEmits(['update:modelValue', 'change']);
const selectedValue = ref(props.modelValue);
// 组件挂载时,如果没有初始值,则选中第一项
onMounted(() => {
if (!props.modelValue && props.options.length > 0) {
const firstValue = props.options[0].value;
selectedValue.value = firstValue;
emit('update:modelValue', firstValue);
}
});
const handleChange = (value: Option['value']) => {
emit('update:modelValue', value);
emit('change', value);
};
</script>
<style scoped>
.binary-selector {
padding: 10px;
}
.selector-label {
margin-right: 6px;
line-height: 32px;
}
.required {
color: #f56c6c;
margin-right: 4px;
}
.radio-group {
flex: 1;
display: flex;
}
:deep(.el-radio-button) {
flex: 1;
}
.radio-content {
position: relative;
padding: 0 8px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
box-sizing: border-box;
}
.selected-corner {
position: absolute;
right: 0;
bottom: 0;
width: 24px;
height: 23px;
}
.corner-triangle {
position: absolute;
right: 0;
bottom: 0;
width: 24px;
height: 23px;
}
.check-mark {
position: absolute;
right: 3px;
bottom: 3px;
width: 10px;
height: 8.33px;
z-index: 1;
}
/* 基础样式:所有按钮默认无圆角 */
:deep(.el-radio-button__inner) {
width: 100%;
min-width: 80px;
text-align: center;
border-width: 1px 1px 1px 0px !important;
border-style: solid !important;
border-color: #8e8e8e !important;
padding: 0;
height: 32px !important;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
border-radius: 0 !important;
transition: none !important;
}
/* 第一个按钮:左侧圆角 */
:deep(.el-radio-button:first-child .el-radio-button__inner) {
border-left-width: 1px !important;
border-radius: 4px 0 0 4px !important;
}
/* 最后一个按钮:右侧圆角 */
:deep(.el-radio-button:last-child .el-radio-button__inner) {
border-radius: 0 4px 4px 0 !important;
}
/* 选中状态的按钮样式 */
:deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
background-color: #eff6ff !important;
color: #1c69d4 !important;
border: 2px solid #1c69d4 !important;
position: relative;
z-index: 1;
height: 32px !important;
box-sizing: border-box;
margin: 0;
transition: none !important;
}
/* 根据位置设置选中状态的圆角 */
/* 第一个按钮选中时 */
:deep(
.el-radio-button:first-child .el-radio-button__original-radio:checked + .el-radio-button__inner
) {
border-radius: 4px 0 0 4px !important;
}
/* 最后一个按钮选中时 */
:deep(
.el-radio-button:last-child .el-radio-button__original-radio:checked + .el-radio-button__inner
) {
border-radius: 0 4px 4px 0 !important;
}
/* 中间按钮选中时 */
:deep(
.el-radio-button:not(:first-child):not(:last-child)
.el-radio-button__original-radio:checked
+ .el-radio-button__inner
) {
border-radius: 0 !important;
}
:deep(.el-radio-button__inner:hover) {
color: inherit;
position: static;
z-index: auto;
}
:deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
color: #1c69d4 !important;
}
/* 确保所有状态变化都没有过渡效果 */
:deep(.el-radio-button__inner),
:deep(.el-radio-button__inner:hover),
:deep(.el-radio-button__inner:active),
:deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
transition: none !important;
}
</style>
调用demo
<BinarySelector
label="是否数据安全评估"
:options="[
{ label: '否', value: 1 },
{ label: '是', value: 2 },
{ label: '未知', value: 3 },
]"
required
/>