鸿蒙OSUniApp制作多选框与单选框组件#三方框架 #Uniapp
使用UniApp制作多选框与单选框组件
前言
在移动端应用开发中,表单元素是用户交互的重要组成部分。尤其是多选框(Checkbox)和单选框(Radio),它们几乎存在于每一个需要用户做出选择的场景中。虽然UniApp提供了基础的表单组件,但在实际项目中,我们往往需要根据UI设计稿来定制这些组件的样式和交互效果。
本文将分享如何使用UniApp框架自定义多选框和单选框组件,让它们不仅功能完善,还能适应各种设计风格。通过这篇文章,你将学习到组件封装的思路和技巧,这对提升你的UniApp开发能力会有很大帮助。
为什么要自定义表单组件?
你可能会问,UniApp不是已经提供了<checkbox>
和<radio>
组件吗?为什么还要自定义呢?原因主要有以下几点:
- 样式限制:原生组件的样式修改有限,难以满足设计师的"奇思妙想"
- 跨端一致性:原生组件在不同平台的表现可能不一致
- 交互体验:自定义组件可以加入更丰富的交互效果
- 功能扩展:可以根据业务需求添加更多功能
多选框组件实现
基本思路
多选框本质上是一个可切换状态的组件,我们可以用一个布尔值来表示选中状态,然后根据状态显示不同的样式。具体实现步骤如下:
- 定义组件的props和事件
- 设计选中和未选中的样式
- 处理点击事件和状态切换
- 处理禁用状态
代码实现
首先,创建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组件的开发中。
在实现过程中,有几点值得注意:
- 组件通信:使用
v-model
结合update:modelValue
事件实现双向绑定,这是Vue3推荐的做法 - 样式隔离:使用scoped样式避免样式污染,对于需要修改子组件样式的情况,可以使用
:deep()
- 状态管理:清晰地定义组件状态,并通过props传递给子组件
- 交互优化:添加过渡效果提升用户体验
希望这篇文章对你在UniApp中自定义表单组件有所帮助。记住,组件开发的核心是复用和抽象,好的组件设计可以大大提高开发效率和代码质量。
进阶提示
如果你想进一步完善这些组件,可以考虑:
- 添加表单验证功能
- 实现不同风格的主题
- 支持更多的配置选项,如自定义图标
- 添加无障碍访问支持
- 优化移动端的触摸体验
最后,别忘了测试你的组件在不同平台的表现,确保它们在各种环境下都能正常工作。
相关文章:
鸿蒙OSUniApp制作多选框与单选框组件#三方框架 #Uniapp
使用UniApp制作多选框与单选框组件 前言 在移动端应用开发中,表单元素是用户交互的重要组成部分。尤其是多选框(Checkbox)和单选框(Radio),它们几乎存在于每一个需要用户做出选择的场景中。虽然UniApp提供…...

康谋分享 | 自动驾驶仿真进入“标准时代”:aiSim全面对接ASAM OpenX
目录 一、OpenDRIVE:兼容多版本地图标准 (1)Atlas 工作流 (2)UE Plugin 工作流 二、OpenSCENARIO:标准化动态行为建模 三、OpenCRG:还原毫米级路面细节 四、OpenMATERIAL:更真…...
VMware中快速安装与优化Ubuntu全攻略
准备工作 在开始安装之前,确保已经下载了VMware Workstation或VMware Player,并准备好Ubuntu的ISO镜像文件。VMware Workstation是一款功能强大的虚拟机软件,支持在Windows或Linux主机上运行多个操作系统。 创建虚拟机 打开VMware Worksta…...

GPUGeek云平台实战:DeepSeek-R1-70B大语言模型一站式部署
随着人工智能技术的迅猛发展,特别是在自然语言处理领域,大型语言模型如DeepSeek-R1-70B的出现,推动了各行各业的变革。为了应对这些庞大模型的计算需求,云计算平台的普及成为了关键,特别是基于GPU加速的云平台…...
无人机动力系统全解析:核心组件、工作原理与实用指南
无人机想要实现稳定飞行与灵活操控,离不开一套高效协同的动力系统。该系统以电机、电子调速器(电调)、电池和螺旋桨四大核心组件为基础,各部分精密配合,共同驱动无人机翱翔蓝天。接下来,本文将从基础原理入…...

【C语言】初阶数据结构相关习题(二)
🎆个人主页:夜晚中的人海 今日语录:知识是从刻苦劳动中得来的,任何成就都是刻苦劳动的结果。——宋庆龄 文章目录 🎄一、链表内指定区间翻转🎉二、从链表中删去总和值为零的节点🚀三、链表求和&…...

嵌入式学习--江科大51单片机day7
我们在听课的过程中,可能对老师讲的有疑问,或者有些自己的理解,我们可以去问豆包,包括在写博客的时候我也是,不断去问豆包保证思考的正确性。(有人感觉豆包很low啊,其实这些基础性的东西豆包一般…...
基于大模型预测围术期麻醉苏醒时间的技术方案
目录 一、数据收集与处理(一)数据来源(二)数据预处理二、大模型构建与训练(一)模型选择(二)模型训练三、围术期麻醉苏醒时间预测(一)术前预测(二)术中动态预测四、并发症风险预测(一)风险因素分析(二)风险预测模型五、基于预测制定手术方案(一)个性化手术规划…...

Element Plus 取消el-form-item点击触发组件,改为原生表单控件
文章目录 问题:方法一:使用全局样式覆盖(推荐)方法二:自定义指令(更灵活)方法三:封装高阶组件方法四:运行时DOM修改(不推荐) 问题: 描…...
javascript —— ! 和 !! 的区别与作用
javascript —— ! 和 !! 的区别与作用 在 JavaScript 里,! 和 !! 是两种不同的逻辑运算符,它们的功能和使用场景有明显区别。 1、 !(逻辑非运算符) 它的主要作用是 对操作数进行布尔值取反。具体来说,就是 先把操作…...
鸿蒙 ArkUI - ArkTS 组件 官方 UI组件 合集
ArkUI 组件速查表 鸿蒙应用开发页面上需要实现的 UI 功能组件如果在这 100 多个组件里都找不到,那就需要组合造轮子了 使用技巧:先判断需要实现的组件大方向,比如“选择”、“文本”、“信息”等,或者是某种形状比如“块”、“图…...
LLM笔记(三)位置编码(1)
位置编码理论与应用 1. 位置编码如何解决置换不变性及其数学表现 在Transformer模型中,自注意力机制(Self-Attention)具有置换不变性(permutation invariance),这意味着对输入序列的词元(toke…...
麒麟v10 部署 MySQL 5.6.10 完整步骤
需要包的私信我 一、安装依赖(Perl环境) # 在线安装依赖 yum -y install perl perl-devel# 离线安装(需提前下载好rpm包) mkdir /data/ybn/soft/pre yum install --downloadonly --downloaddir/data/ybn/soft/pre perl perl-dev…...

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

专项智能练习(定义判断)
1. 单选题 热传导是介质内无宏观运动时的传热现象,其在固体、液体和气体中均可发生。但严格而言,只有在固体中才是纯粹的热传导,在流体(泛指液体和气体)中又是另外一种情况,流体即使处于静止状态ÿ…...
失控的产品
大部分程序员很难有机会做一个新的产品,绝大多时候去一家新公司也都是在旧产品上修修补补。 笔者还是很幸运得到了开发新品的机会,从2023年开始做,中间经历了许多磕磕碰碰。 有的小伙伴从中离开,偶尔又加入1~2个人,但…...

【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
Dopamine越狱 iPhone X iOS 16.6 (20G75) Dopamine兼容设备 参考:https://www.bilibili.com/opus/977469285985157129 A9 - A11(iPhone6s-X):iOS15.0-16.6.1 A12-A14(iPhoneXR-12PM…...
无线定位之 二 SX1302 网关源码 thread_down 线程详解
前言 笔者计划通过无线定位系列文章、系统的描述 TDOA 无线定位和混合定位相关技术知识点, 并以实践来验证此定位系统精度。 笔者从实践出发、本篇直接走读无线定位系统关键节点、网关 SX1302 源码框架,并在源码走读过程 中、着重分析与无线定位相关的PPS时间的来龙去脉、并在…...

对心理幸福感含义的探索 | 幸福就是一切吗?
注:机翻,未校。 Happiness Is Everything, or Is It? Explorations on the Meaning of Psychological Well-Being 幸福就是一切吗?对心理幸福感含义的探索 Journal of Personality and Social Psychology 1989, Vol. 57, No. 6,1069-1081 …...
多平台图标设计与管理的终极解决方案
IconWorkshop Pro 是一款由Axialis团队开发的专业图标设计与制作软件,专注于为设计师、开发者及企业用户提供高效且灵活的图标创作解决方案。该软件凭借其强大的功能与跨平台适配性,成为Windows、macOS、iOS、Android等多系统图标设计的首选工具之一。 …...
ngx_http_keyval_module动态键值管理
一、模块安装与验证 检查模块是否可用 nginx -V 2>&1 | grep --color -o ngx_http_keyval_module如果看到 ngx_http_keyval_module,说明模块已编译进 NGINX。 若未找到,请联系你的 NGINX 供应商,获取商业版或重新编译并启用该模块&am…...

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

讯联云库项目开发日志(二)AOP参数拦截
目录 利用AOP实现参数拦截: 一、HTTP请求进入Controller(发送邮件验证码) 二、AOP切面触发 1. 切面拦截(GlobalOperactionAspect.class) method.getAnnotation() null interceptor 判断 2.参数校验注解 3. 参…...

龙虎榜——20250515
上证指数缩量收阴线,个股跌多涨少,上涨波段4月9日以来已有24个交易日,时间周期上处于上涨末端,注意风险。 深证指数缩量收阴线,日线上涨结束的概率在增大,注意风险。 2025年5月15日龙虎榜行业方向分析 一…...
知识图谱重构电商搜索:下一代AI搜索引擎的底层逻辑
1. 搜索引擎的进化论 从雅虎目录式搜索到Google的PageRank算法,搜索引擎经历了三次技术跃迁。而AI搜索引擎正在掀起第四次革命:在电商场景中,传统的「关键词匹配」已无法满足个性化购物需求,MOE搜索等新一代架构开始融合知识图谱…...
python-修改图片背景色
在Python中,可以使用图像处理库(如OpenCV或Pillow)来修改图片的背景色。通常,修改背景色的流程包括以下步骤: 1、对图片进行分割,识别前景和背景。 2、对背景区域进行颜色替换。 下面是两种实现方法&#x…...

卡洛诗,将高端西餐的冗余价值转化为普惠体验
西餐市场正经历一场结构性变革,一二线城市的高端西餐陷入内卷,而下沉市场却因品质与价格断层陷入选择困境——消费者既不愿为高价西餐的面子溢价买单,又难以忍受快餐式西餐的粗糙体验。这一矛盾催生了万亿级的市场真空地带,萨莉亚…...
【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在键盘的上方加一个完成按钮
有些情况下,输入框在输入键盘弹出后, 需要在键盘的上方显示一个toolbar , 然后 toolbar 上面一个完成按钮,点完成按钮把键盘关闭。 如图: 直接上代码,这样写的好处是,把 TextField 给封装了&…...

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