当前位置: 首页 > article >正文

鸿蒙OSUniApp制作多选框与单选框组件#三方框架 #Uniapp

使用UniApp制作多选框与单选框组件

前言

在移动端应用开发中,表单元素是用户交互的重要组成部分。尤其是多选框(Checkbox)和单选框(Radio),它们几乎存在于每一个需要用户做出选择的场景中。虽然UniApp提供了基础的表单组件,但在实际项目中,我们往往需要根据UI设计稿来定制这些组件的样式和交互效果。

本文将分享如何使用UniApp框架自定义多选框和单选框组件,让它们不仅功能完善,还能适应各种设计风格。通过这篇文章,你将学习到组件封装的思路和技巧,这对提升你的UniApp开发能力会有很大帮助。

为什么要自定义表单组件?

你可能会问,UniApp不是已经提供了<checkbox><radio>组件吗?为什么还要自定义呢?原因主要有以下几点:

  1. 样式限制:原生组件的样式修改有限,难以满足设计师的"奇思妙想"
  2. 跨端一致性:原生组件在不同平台的表现可能不一致
  3. 交互体验:自定义组件可以加入更丰富的交互效果
  4. 功能扩展:可以根据业务需求添加更多功能

多选框组件实现

基本思路

多选框本质上是一个可切换状态的组件,我们可以用一个布尔值来表示选中状态,然后根据状态显示不同的样式。具体实现步骤如下:

  1. 定义组件的props和事件
  2. 设计选中和未选中的样式
  3. 处理点击事件和状态切换
  4. 处理禁用状态

代码实现

首先,创建components/my-checkbox/my-checkbox.vue文件:

<template><view class="my-checkbox" :class="[disabled ? 'my-checkbox-disabled' : '', modelValue ? 'my-checkbox-checked' : '']"@click="handleClick"><view class="checkbox-box"><view v-if="modelValue" class="checkbox-icon"><text class="iconfont icon-check"></text></view></view><text v-if="label" class="checkbox-label">{{ label }}</text></view>
</template><script>
export default {name: 'MyCheckbox',props: {modelValue: {type: Boolean,default: false},label: {type: String,default: ''},disabled: {type: Boolean,default: false}},emits: ['update:modelValue', 'change'],methods: {handleClick() {if (this.disabled) return;const newValue = !this.modelValue;this.$emit('update:modelValue', newValue);this.$emit('change', newValue);}}
}
</script><style scoped>
.my-checkbox {display: flex;align-items: center;padding: 6rpx 0;
}.checkbox-box {width: 40rpx;height: 40rpx;border: 2rpx solid #dcdfe6;border-radius: 4rpx;display: flex;justify-content: center;align-items: center;box-sizing: border-box;transition: border-color 0.3s;
}.my-checkbox-checked .checkbox-box {background-color: #2979ff;border-color: #2979ff;
}.checkbox-icon {color: #fff;font-size: 28rpx;line-height: 1;
}.checkbox-label {margin-left: 10rpx;font-size: 28rpx;color: #333;
}.my-checkbox-disabled {opacity: 0.5;cursor: not-allowed;
}/* 引入字体图标(需要自行添加) */
/* @font-face {font-family: 'iconfont';src: url('~@/static/iconfont.ttf');
} */.icon-check:before {content: '\e645';
}
</style>

注意,这里使用了字体图标作为选中状态的标识。你需要在项目中引入相应的字体文件,或者使用其他图标方案。

如何使用

在页面中使用该组件:

<template><view class="container"><my-checkbox v-model="checked1" label="选项1"></my-checkbox><my-checkbox v-model="checked2" label="选项2"></my-checkbox><my-checkbox v-model="checked3" label="禁用选项" disabled></my-checkbox></view>
</template><script>
import MyCheckbox from '@/components/my-checkbox/my-checkbox';export default {components: {MyCheckbox},data() {return {checked1: false,checked2: true,checked3: false}}
}
</script>

多选框组(CheckboxGroup)实现

在实际应用中,多选框通常是成组出现的。下面我们来实现一个多选框组组件,用于管理多个选项:

<template><view class="checkbox-group"><my-checkbox v-for="(item, index) in options" :key="index":model-value="isChecked(item.value)":label="item.label":disabled="item.disabled"@change="(val) => handleChange(item.value, val)"></my-checkbox></view>
</template><script>
import MyCheckbox from '../my-checkbox/my-checkbox';export default {name: 'CheckboxGroup',components: {MyCheckbox},props: {modelValue: {type: Array,default: () => []},options: {type: Array,default: () => []}},emits: ['update:modelValue', 'change'],methods: {isChecked(value) {return this.modelValue.includes(value);},handleChange(value, checked) {let newValue = [...this.modelValue];if (checked) {// 如果选中且不在数组中,则添加if (!newValue.includes(value)) {newValue.push(value);}} else {// 如果取消选中且在数组中,则移除const index = newValue.indexOf(value);if (index !== -1) {newValue.splice(index, 1);}}this.$emit('update:modelValue', newValue);this.$emit('change', newValue);}}
}
</script><style scoped>
.checkbox-group {display: flex;flex-direction: column;
}
.checkbox-group :deep(.my-checkbox) {margin-bottom: 20rpx;
}
</style>

使用多选框组:

<template><view class="container"><checkbox-group v-model="selectedFruits" :options="fruitOptions"></checkbox-group><view class="result">已选择: {{ selectedFruits.join(', ') }}</view></view>
</template><script>
import CheckboxGroup from '@/components/checkbox-group/checkbox-group';export default {components: {CheckboxGroup},data() {return {selectedFruits: ['apple'],fruitOptions: [{ label: '苹果', value: 'apple' },{ label: '香蕉', value: 'banana' },{ label: '橙子', value: 'orange' },{ label: '葡萄', value: 'grape', disabled: true }]}}
}
</script>

单选框组件实现

单选框与多选框类似,但它通常是成组出现的,并且一个组内只能选中一个选项。

<template><view class="radio-group"><view v-for="(item, index) in options" :key="index"class="my-radio":class="[item.disabled ? 'my-radio-disabled' : '', modelValue === item.value ? 'my-radio-checked' : '']"@click="handleClick(item)"><view class="radio-box"><view v-if="modelValue === item.value" class="radio-inner"></view></view><text class="radio-label">{{ item.label }}</text></view></view>
</template><script>
export default {name: 'RadioGroup',props: {modelValue: {type: [String, Number, Boolean],default: ''},options: {type: Array,default: () => []}},emits: ['update:modelValue', 'change'],methods: {handleClick(item) {if (item.disabled) return;if (this.modelValue !== item.value) {this.$emit('update:modelValue', item.value);this.$emit('change', item.value);}}}
}
</script><style scoped>
.radio-group {display: flex;flex-direction: column;
}.my-radio {display: flex;align-items: center;margin-bottom: 20rpx;
}.radio-box {width: 40rpx;height: 40rpx;border: 2rpx solid #dcdfe6;border-radius: 50%;display: flex;justify-content: center;align-items: center;box-sizing: border-box;transition: all 0.3s;
}.my-radio-checked .radio-box {border-color: #2979ff;
}.radio-inner {width: 20rpx;height: 20rpx;border-radius: 50%;background-color: #2979ff;transition: all 0.3s;
}.radio-label {margin-left: 10rpx;font-size: 28rpx;color: #333;
}.my-radio-disabled {opacity: 0.5;cursor: not-allowed;
}
</style>

使用单选框组:

<template><view class="container"><radio-group v-model="gender" :options="genderOptions"></radio-group><view class="result">性别: {{ getGenderLabel() }}</view></view>
</template><script>
import RadioGroup from '@/components/radio-group/radio-group';export default {components: {RadioGroup},data() {return {gender: 'male',genderOptions: [{ label: '男', value: 'male' },{ label: '女', value: 'female' },{ label: '保密', value: 'secret', disabled: true }]}},methods: {getGenderLabel() {const option = this.genderOptions.find(item => item.value === this.gender);return option ? option.label : '';}}
}
</script>

实际案例:问卷调查表单

下面是一个结合多选框和单选框的问卷调查表单案例:

<template><view class="survey-form"><view class="form-title">满意度调查问卷</view><view class="form-item"><view class="item-title">1. 您的年龄段:</view><radio-group v-model="survey.age" :options="ageOptions"></radio-group></view><view class="form-item"><view class="item-title">2. 您对我们的产品满意吗?</view><radio-group v-model="survey.satisfaction" :options="satisfactionOptions"></radio-group></view><view class="form-item"><view class="item-title">3. 您希望我们改进哪些方面?(可多选)</view><checkbox-group v-model="survey.improvements" :options="improvementOptions"></checkbox-group></view><button class="submit-btn" type="primary" @click="submitSurvey">提交问卷</button></view>
</template><script>
import RadioGroup from '@/components/radio-group/radio-group';
import CheckboxGroup from '@/components/checkbox-group/checkbox-group';export default {components: {RadioGroup,CheckboxGroup},data() {return {survey: {age: '',satisfaction: '',improvements: []},ageOptions: [{ label: '18岁以下', value: 'under18' },{ label: '18-25岁', value: '18-25' },{ label: '26-35岁', value: '26-35' },{ label: '36-45岁', value: '36-45' },{ label: '45岁以上', value: 'above45' }],satisfactionOptions: [{ label: '非常满意', value: 'very-satisfied' },{ label: '满意', value: 'satisfied' },{ label: '一般', value: 'neutral' },{ label: '不满意', value: 'unsatisfied' },{ label: '非常不满意', value: 'very-unsatisfied' }],improvementOptions: [{ label: '产品功能', value: 'feature' },{ label: '用户界面', value: 'ui' },{ label: '性能速度', value: 'performance' },{ label: '售后服务', value: 'service' },{ label: '价格', value: 'price' }]}},methods: {submitSurvey() {// 表单验证if (!this.survey.age) {uni.showToast({title: '请选择您的年龄段',icon: 'none'});return;}if (!this.survey.satisfaction) {uni.showToast({title: '请选择产品满意度',icon: 'none'});return;}if (this.survey.improvements.length === 0) {uni.showToast({title: '请至少选择一项需要改进的方面',icon: 'none'});return;}// 提交数据console.log('提交的问卷数据:', this.survey);uni.showLoading({title: '提交中...'});// 模拟提交setTimeout(() => {uni.hideLoading();uni.showToast({title: '提交成功',icon: 'success'});}, 1500);}}
}
</script><style scoped>
.survey-form {padding: 30rpx;
}.form-title {font-size: 40rpx;font-weight: bold;text-align: center;margin-bottom: 50rpx;
}.form-item {margin-bottom: 40rpx;
}.item-title {font-size: 32rpx;margin-bottom: 20rpx;
}.submit-btn {margin-top: 50rpx;
}
</style>

总结与思考

通过自定义多选框和单选框组件,我们不仅解决了原生组件样式定制的限制,还提升了组件的可复用性和扩展性。这种组件封装的思路,其实可以应用到各种UI组件的开发中。

在实现过程中,有几点值得注意:

  1. 组件通信:使用v-model结合update:modelValue事件实现双向绑定,这是Vue3推荐的做法
  2. 样式隔离:使用scoped样式避免样式污染,对于需要修改子组件样式的情况,可以使用:deep()
  3. 状态管理:清晰地定义组件状态,并通过props传递给子组件
  4. 交互优化:添加过渡效果提升用户体验

希望这篇文章对你在UniApp中自定义表单组件有所帮助。记住,组件开发的核心是复用抽象,好的组件设计可以大大提高开发效率和代码质量。

进阶提示

如果你想进一步完善这些组件,可以考虑:

  1. 添加表单验证功能
  2. 实现不同风格的主题
  3. 支持更多的配置选项,如自定义图标
  4. 添加无障碍访问支持
  5. 优化移动端的触摸体验

最后,别忘了测试你的组件在不同平台的表现,确保它们在各种环境下都能正常工作。

相关文章:

鸿蒙OSUniApp制作多选框与单选框组件#三方框架 #Uniapp

使用UniApp制作多选框与单选框组件 前言 在移动端应用开发中&#xff0c;表单元素是用户交互的重要组成部分。尤其是多选框&#xff08;Checkbox&#xff09;和单选框&#xff08;Radio&#xff09;&#xff0c;它们几乎存在于每一个需要用户做出选择的场景中。虽然UniApp提供…...

康谋分享 | 自动驾驶仿真进入“标准时代”:aiSim全面对接ASAM OpenX

目录 一、OpenDRIVE&#xff1a;兼容多版本地图标准 &#xff08;1&#xff09;Atlas 工作流 &#xff08;2&#xff09;UE Plugin 工作流 二、OpenSCENARIO&#xff1a;标准化动态行为建模 三、OpenCRG&#xff1a;还原毫米级路面细节 四、OpenMATERIAL&#xff1a;更真…...

VMware中快速安装与优化Ubuntu全攻略

准备工作 在开始安装之前&#xff0c;确保已经下载了VMware Workstation或VMware Player&#xff0c;并准备好Ubuntu的ISO镜像文件。VMware Workstation是一款功能强大的虚拟机软件&#xff0c;支持在Windows或Linux主机上运行多个操作系统。 创建虚拟机 打开VMware Worksta…...

GPUGeek云平台实战:DeepSeek-R1-70B大语言模型一站式部署

随着人工智能技术的迅猛发展&#xff0c;特别是在自然语言处理领域&#xff0c;大型语言模型如DeepSeek-R1-70B的出现&#xff0c;推动了各行各业的变革。为了应对这些庞大模型的计算需求&#xff0c;云计算平台的普及成为了关键&#xff0c;特别是基于GPU加速的云平台&#xf…...

无人机动力系统全解析:核心组件、工作原理与实用指南

无人机想要实现稳定飞行与灵活操控&#xff0c;离不开一套高效协同的动力系统。该系统以电机、电子调速器&#xff08;电调&#xff09;、电池和螺旋桨四大核心组件为基础&#xff0c;各部分精密配合&#xff0c;共同驱动无人机翱翔蓝天。接下来&#xff0c;本文将从基础原理入…...

【C语言】初阶数据结构相关习题(二)

&#x1f386;个人主页&#xff1a;夜晚中的人海 今日语录&#xff1a;知识是从刻苦劳动中得来的&#xff0c;任何成就都是刻苦劳动的结果。——宋庆龄 文章目录 &#x1f384;一、链表内指定区间翻转&#x1f389;二、从链表中删去总和值为零的节点&#x1f680;三、链表求和&…...

嵌入式学习--江科大51单片机day7

我们在听课的过程中&#xff0c;可能对老师讲的有疑问&#xff0c;或者有些自己的理解&#xff0c;我们可以去问豆包&#xff0c;包括在写博客的时候我也是&#xff0c;不断去问豆包保证思考的正确性。&#xff08;有人感觉豆包很low啊&#xff0c;其实这些基础性的东西豆包一般…...

基于大模型预测围术期麻醉苏醒时间的技术方案

目录 一、数据收集与处理(一)数据来源(二)数据预处理二、大模型构建与训练(一)模型选择(二)模型训练三、围术期麻醉苏醒时间预测(一)术前预测(二)术中动态预测四、并发症风险预测(一)风险因素分析(二)风险预测模型五、基于预测制定手术方案(一)个性化手术规划…...

Element Plus 取消el-form-item点击触发组件,改为原生表单控件

文章目录 问题&#xff1a;方法一&#xff1a;使用全局样式覆盖&#xff08;推荐&#xff09;方法二&#xff1a;自定义指令&#xff08;更灵活&#xff09;方法三&#xff1a;封装高阶组件方法四&#xff1a;运行时DOM修改&#xff08;不推荐&#xff09; 问题&#xff1a; 描…...

javascript —— ! 和 !! 的区别与作用

javascript —— ! 和 !! 的区别与作用 在 JavaScript 里&#xff0c;! 和 !! 是两种不同的逻辑运算符&#xff0c;它们的功能和使用场景有明显区别。 1、 !&#xff08;逻辑非运算符&#xff09; 它的主要作用是 对操作数进行布尔值取反。具体来说&#xff0c;就是 先把操作…...

鸿蒙 ArkUI - ArkTS 组件 官方 UI组件 合集

ArkUI 组件速查表 鸿蒙应用开发页面上需要实现的 UI 功能组件如果在这 100 多个组件里都找不到&#xff0c;那就需要组合造轮子了 使用技巧&#xff1a;先判断需要实现的组件大方向&#xff0c;比如“选择”、“文本”、“信息”等&#xff0c;或者是某种形状比如“块”、“图…...

LLM笔记(三)位置编码(1)

位置编码理论与应用 1. 位置编码如何解决置换不变性及其数学表现 在Transformer模型中&#xff0c;自注意力机制&#xff08;Self-Attention&#xff09;具有置换不变性&#xff08;permutation invariance&#xff09;&#xff0c;这意味着对输入序列的词元&#xff08;toke…...

麒麟v10 部署 MySQL 5.6.10 完整步骤

需要包的私信我 一、安装依赖&#xff08;Perl环境&#xff09; # 在线安装依赖 yum -y install perl perl-devel# 离线安装&#xff08;需提前下载好rpm包&#xff09; mkdir /data/ybn/soft/pre yum install --downloadonly --downloaddir/data/ybn/soft/pre perl perl-dev…...

Git-学习笔记(粗略版)

前言 很多人都听过git&#xff0c;github这些名词,但是它们是什么&#xff0c;怎么使用&#xff1f;git和github是一个东西吗&#xff1f;本文将详细解答这些问题&#xff0c;彻底弄懂git。 1.Git是啥❓ 有一天&#xff0c;我们的插画师小王接到一个绘画订单&#xff0c;但奈…...

专项智能练习(定义判断)

1. 单选题 热传导是介质内无宏观运动时的传热现象&#xff0c;其在固体、液体和气体中均可发生。但严格而言&#xff0c;只有在固体中才是纯粹的热传导&#xff0c;在流体&#xff08;泛指液体和气体&#xff09;中又是另外一种情况&#xff0c;流体即使处于静止状态&#xff…...

失控的产品

大部分程序员很难有机会做一个新的产品&#xff0c;绝大多时候去一家新公司也都是在旧产品上修修补补。 笔者还是很幸运得到了开发新品的机会&#xff0c;从2023年开始做&#xff0c;中间经历了许多磕磕碰碰。 有的小伙伴从中离开&#xff0c;偶尔又加入1~2个人&#xff0c;但…...

【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error

Dopamine越狱 iPhone X iOS 16.6 (20G75) Dopamine兼容设备 参考&#xff1a;https://www.bilibili.com/opus/977469285985157129 A9 - A11&#xff08;iPhone6s&#xff0d;X&#xff09;&#xff1a;iOS15.0-16.6.1 A12-A14&#xff08;iPhoneXR&#xff0d;12PM&#xf…...

无线定位之 二 SX1302 网关源码 thread_down 线程详解

前言 笔者计划通过无线定位系列文章、系统的描述 TDOA 无线定位和混合定位相关技术知识点, 并以实践来验证此定位系统精度。 笔者从实践出发、本篇直接走读无线定位系统关键节点、网关 SX1302 源码框架,并在源码走读过程 中、着重分析与无线定位相关的PPS时间的来龙去脉、并在…...

对心理幸福感含义的探索 | 幸福就是一切吗?

注&#xff1a;机翻&#xff0c;未校。 Happiness Is Everything, or Is It? Explorations on the Meaning of Psychological Well-Being 幸福就是一切吗&#xff1f;对心理幸福感含义的探索 Journal of Personality and Social Psychology 1989, Vol. 57, No. 6,1069-1081 …...

多平台图标设计与管理的终极解决方案

IconWorkshop Pro 是一款由Axialis团队开发的专业图标设计与制作软件&#xff0c;专注于为设计师、开发者及企业用户提供高效且灵活的图标创作解决方案。该软件凭借其强大的功能与跨平台适配性&#xff0c;成为Windows、macOS、iOS、Android等多系统图标设计的首选工具之一。 …...

ngx_http_keyval_module动态键值管理

一、模块安装与验证 检查模块是否可用 nginx -V 2>&1 | grep --color -o ngx_http_keyval_module如果看到 ngx_http_keyval_module&#xff0c;说明模块已编译进 NGINX。 若未找到&#xff0c;请联系你的 NGINX 供应商&#xff0c;获取商业版或重新编译并启用该模块&am…...

【高频面试题】LRU缓存

文章目录 1 相关前置知识&#xff08;OS&#xff09;2 面试题 16.25. LRU 缓存2.1 题面2.2 示例2.3 解法1 &#xff08;双端队列哈希表&#xff09;思路 2.4 解法2思路 3 参考 1 相关前置知识&#xff08;OS&#xff09; 为什么需要页面置换算法&#xff1a;当进程运行时&…...

讯联云库项目开发日志(二)AOP参数拦截

目录 利用AOP实现参数拦截: 一、​​HTTP请求进入Controller​&#xff08;发送邮件验证码&#xff09; 二、AOP切面触发 1. 切面拦截&#xff08;GlobalOperactionAspect.class&#xff09; method.getAnnotation()​​ null interceptor 判断​​ 2.参数校验注解 3. 参…...

龙虎榜——20250515

上证指数缩量收阴线&#xff0c;个股跌多涨少&#xff0c;上涨波段4月9日以来已有24个交易日&#xff0c;时间周期上处于上涨末端&#xff0c;注意风险。 深证指数缩量收阴线&#xff0c;日线上涨结束的概率在增大&#xff0c;注意风险。 2025年5月15日龙虎榜行业方向分析 一…...

知识图谱重构电商搜索:下一代AI搜索引擎的底层逻辑

1. 搜索引擎的进化论 从雅虎目录式搜索到Google的PageRank算法&#xff0c;搜索引擎经历了三次技术跃迁。而AI搜索引擎正在掀起第四次革命&#xff1a;在电商场景中&#xff0c;传统的「关键词匹配」已无法满足个性化购物需求&#xff0c;MOE搜索等新一代架构开始融合知识图谱…...

python-修改图片背景色

在Python中&#xff0c;可以使用图像处理库&#xff08;如OpenCV或Pillow&#xff09;来修改图片的背景色。通常&#xff0c;修改背景色的流程包括以下步骤&#xff1a; 1、对图片进行分割&#xff0c;识别前景和背景。 2、对背景区域进行颜色替换。 下面是两种实现方法&#x…...

卡洛诗,将高端西餐的冗余价值转化为普惠体验

西餐市场正经历一场结构性变革&#xff0c;一二线城市的高端西餐陷入内卷&#xff0c;而下沉市场却因品质与价格断层陷入选择困境——消费者既不愿为高价西餐的面子溢价买单&#xff0c;又难以忍受快餐式西餐的粗糙体验。这一矛盾催生了万亿级的市场真空地带&#xff0c;萨莉亚…...

【ROS2】ROS节点启动崩溃:rclcpp::exceptions::RCLInvalidArgument

1、问题描述 启动ROS节点时,直接崩溃,打印信息如下: terminate called after throwing an instance of rclcpp::exceptions::RCLInvalidArgumentwhat(): failed to create guard condition: context argument is null, at ./src/rcl/guard_condition.c:65 [ros2run]: Abo…...

Flutter在键盘的上方加一个完成按钮

有些情况下&#xff0c;输入框在输入键盘弹出后&#xff0c; 需要在键盘的上方显示一个toolbar &#xff0c; 然后 toolbar 上面一个完成按钮&#xff0c;点完成按钮把键盘关闭。 如图&#xff1a; 直接上代码&#xff0c;这样写的好处是&#xff0c;把 TextField 给封装了&…...

SQL注入---05--跨站注入

1 权限说明 select * from mysql.user; 这里的Y表示我前面的命令权限为root&#xff0c;n表示不支持root权限 导致结果&#xff1a; 如果为root的话&#xff0c;我就可操作这些命令并且可以进行跨数据库攻击&#xff0c;但是如果不是高权限root就无法执行这些操作 2 root权限…...