vue封装checkBox列表组件(实现group和自定义选项,纯css实现效果)

本文介绍了如何在Vue中封装一个纯CSS实现的CheckBox组件,包括分组和自定义选项功能。通过组件参数如label、options、checked等控制组件行为,并通过v-model实现双向绑定。文章还详细解释了代码实现过程,强调了数据一致性与单向数据流的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击我查看效果

一、前言

【又在重复造轮子!!!】vue有很多现成的UI框架,博主的手机端用的是mintUI,但是mintUI样式不能整改为想要的样子,因此纯手工封装了一个checkBox组件(纯css实现效果,需要不一样的可自制样式)

先来一个效果图:

二、正文

1、说明

  • 使用checkBox绑定数据,选中的数据使用数组存储(vue分组的写法)
  • 为了保证是数组类型,需要指定组件参数使用的变量,使用$emit回传数据到父组件,checkbox实现v-model双向绑定
  • 通过参数确定,是否显示多选框,用样式控制

2、实例调用

<!-- html -->   
 <multi-check-list label="多选测试1" :options="multiCheckOptions" v-model="checkedVal1"></multi-check-list>
<multi-check-list label="多选测试2" :options="multiCheckOptions" v-model="checkedVal2" :showbox="false"></multi-check-list>
<multi-check-list label="多选测试3(全部禁
<think>我们将创建三个自定义组件:CustomInput、CustomRadio、CustomSelect,然后在父组件中组合成表单。 每个组件都使用Vue3的Composition API`<script setup>`风格,但为了清晰,我们将每个组件单独定义。 注意:在实际项目中,我们可能会将每个组件放在单独的文件中,但这里为了方便,我们写在一个文件中。 步骤: 1. 定义三个自定义组件:CustomInput(输入框)、CustomRadio(单选框)、CustomSelect(下拉菜单) 2. 在父组件中引入并使用这些组件,通过v-model进行双向绑定 3. 设置表单数据,处理表单提交重置 我们将使用`ref`来创建响应式数据,并利用Vue3的自定义组件v-model规范(使用modelValueupdate:modelValue)。 由于要求分开写组件,我们在同一个文件中用三个常量定义组件(在单文件组件中,通常我们会拆分成多个文件,但这里为了演示,放在一个文件中)。 注意:这里我们使用模板字符串来定义组件的模板,实际开发中可以使用单文件组件(.vue)的方式。 我们将为每个组件添加一些基本的样式,使表单看起来更友好。 开始编写代码:</think>下面是一个使用 Vue 3 编写的简易表单,其中输入框、单选框下拉菜单作为独立的自定义组件实现: ```vue <!-- 父组件 App.vue --> <script setup> import { ref } from 'vue'; import CustomInput from './components/CustomInput.vue'; import CustomRadio from './components/CustomRadio.vue'; import CustomSelect from './components/CustomSelect.vue'; // 表单数据 const formData = ref({ name: '', email: '', password: '', gender: '', role: '', subscribe: false }); // 选项配置 const genderOptions = [ { label: '男', value: 'male' }, { label: '女', value: 'female' } ]; const roleOptions = [ { label: '管理员', value: 'admin' }, { label: '编辑', value: 'editor' }, { label: '用户', value: 'user' } ]; // 表单提交处理 function submitForm() { alert(` 表单提交数据: 姓名: ${formData.value.name} 邮箱: ${formData.value.email} 密码: ${formData.value.password} 性别: ${formData.value.gender} 角色: ${formData.value.role} 订阅: ${formData.value.subscribe ? '是' : '否'} `); } // 表单重置 function resetForm() { formData.value = { name: '', email: '', password: '', gender: '', role: '', subscribe: false }; } </script> <template> <div class="app-container"> <h1>用户注册表单</h1> <form @submit.prevent="submitForm" class="custom-form"> <div class="form-group"> <label>姓名:</label> <CustomInput v-model="formData.name" placeholder="请输入姓名" /> </div> <div class="form-group"> <label>邮箱:</label> <CustomInput v-model="formData.email" placeholder="请输入邮箱" type="email" /> </div> <div class="form-group"> <label>密码:</label> <CustomInput v-model="formData.password" placeholder="请输入密码" type="password" /> </div> <div class="form-group"> <label>性别:</label> <CustomRadio :options="genderOptions" v-model="formData.gender" /> </div> <div class="form-group"> <label>角色:</label> <CustomSelect :options="roleOptions" v-model="formData.role" /> </div> <div class="form-group"> <label> <input type="checkbox" v-model="formData.subscribe" /> 订阅新闻邮件 </label> </div> <div class="form-actions"> <button type="submit" class="submit-btn">提交</button> <button type="button" @click="resetForm" class="reset-btn">重置</button> </div> </form> </div> </template> <style scoped> .app-container { max-width: 600px; margin: 0 auto; padding: 20px; } h1 { text-align: center; color: #2c3e50; } .custom-form { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .form-group { margin-bottom: 20px; } .form-group label { display: block; margin-bottom: 8px; font-weight: 500; color: #34495e; } .form-actions { display: flex; gap: 15px; margin-top: 25px; } .submit-btn, .reset-btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; transition: all 0.3s ease; } .submit-btn { background-color: #3498db; color: white; } .submit-btn:hover { background-color: #2980b9; } .reset-btn { background-color: #e74c3c; color: white; } .reset-btn:hover { background-color: #c0392b; } </style> ``` ```vue <!-- components/CustomInput.vue --> <script setup> defineProps({ modelValue: { type: [String, Number], default: '' }, placeholder: { type: String, default: '' }, type: { type: String, default: 'text' } }); const emit = defineEmits(['update:modelValue']); </script> <template> <input :type="type" :value="modelValue" :placeholder="placeholder" @input="emit('update:modelValue', $event.target.value)" class="custom-input" /> </template> <style scoped> .custom-input { width: 100%; padding: 12px 15px; border: 1px solid #dcdee2; border-radius: 4px; font-size: 16px; transition: border-color 0.3s; } .custom-input:focus { border-color: #3498db; outline: none; box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); } </style> ``` ```vue <!-- components/CustomRadio.vue --> <script setup> defineProps({ modelValue: { type: [String, Number], required: true }, options: { type: Array, required: true, validator: (value) => { return value.every(option => typeof option === 'object' && 'label' in option && 'value' in option ); } } }); const emit = defineEmits(['update:modelValue']); </script> <template> <div class="radio-group"> <label v-for="(option, index) in options" :key="index" class="radio-label" > <input type="radio" :value="option.value" :checked="modelValue === option.value" @change="emit('update:modelValue', option.value)" class="radio-input" /> <span class="radio-text">{{ option.label }}</span> </label> </div> </template> <style scoped> .radio-group { display: flex; gap: 20px; } .radio-label { display: flex; align-items: center; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: background-color 0.2s; } .radio-label:hover { background-color: #f5f7fa; } .radio-input { margin-right: 8px; width: 18px; height: 18px; cursor: pointer; } .radio-text { font-size: 16px; user-select: none; } </style> ``` ```vue <!-- components/CustomSelect.vue --> <script setup> defineProps({ modelValue: { type: [String, Number], required: true }, options: { type: Array, required: true, validator: (value) => { return value.every(option => typeof option === 'object' && 'label' in option && 'value' in option ); } } }); const emit = defineEmits(['update:modelValue']); </script> <template> <select :value="modelValue" @change="emit('update:modelValue', $event.target.value)" class="custom-select" > <option disabled value="">请选择</option> <option v-for="(option, index) in options" :key="index" :value="option.value" > {{ option.label }} </option> </select> </template> <style scoped> .custom-select { width: 100%; padding: 12px 15px; border: 1px solid #dcdee2; border-radius: 4px; font-size: 16px; background-color: white; appearance: none; background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 15px center; background-size: 16px; transition: border-color 0.3s; } .custom-select:focus { border-color: #3498db; outline: none; box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); } </style> ``` ### 组件分离设计说明: 1. **组件结构**: - `App.vue`:主组件,负责表单布局数据管理 - `CustomInput.vue`:输入框组件,支持多种输入类型 - `CustomRadio.vue`:单选框组件,支持选项组 - `CustomSelect.vue`:下拉菜单组件,支持选项组 2. **组件通信**: - 所有自定义组件都使用 Vue 3 的 `v-model` 实现双向绑定 - 通过 `defineProps` 接收父组件传递的数据 - 通过 `defineEmits` 定义更新事件 - 使用 `modelValue` 作为 prop 名称,符合 Vue 3 规范 3. **组件特点**: - **CustomInput**: - 支持多种输入类型(通过 `type` prop 控制) - 包含焦点样式过渡效果 - **CustomRadio**: - 使用选项验证确保数据结构正确 - 添加悬停效果增强用户体验 - **CustomSelect**: - 使用自定义下拉箭头图标 - 添加焦点样式过渡效果 - 包含禁用选项 4. **表单管理**: - 使用 `ref` 创建响应式表单数据对象 - 提供表单提交重置功能 - 集中管理所有表单字段 5. **样式设计**: - 每个组件都有独立的作用域样式 - 使用 CSS 变量过渡效果增强交互体验 - 响应式设计适配不同屏幕尺寸 - 按钮添加悬停效果 ### 组件复用优势: 1. **可维护性**:每个组件职责单一,易于维护 2. **可复用性**:组件可在不同表单中重复使用 3. **可扩展性**:可轻松添加新功能(如验证、图标等) 4. **一致性**:保持整个应用的表单元素风格统一 5. **封装性**:组件内部实现细节对外部隐藏
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值