软件编程
位置:首页>> 软件编程>> Android编程>> uniapp下单选框的实现方法详解

uniapp下单选框的实现方法详解

作者:lsjweiyi  发布时间:2024-10-17 16:42:08 

标签:uniapp,单选框

uniapp官方虽然提供了uni-data-checkbox,含括了单选和多选框功能。但是它功能实在不能满足需求:

  • 单选框不支持再次点击取消

  • 无法与父组件的数据源进行联动,无法实现如多规格选择的那种联动

  • 源码每次点击都是对数据源进行拷贝,然后再进行json解析等操作,看着就很不靠谱,数据量大必然有性能问题。

其实我放弃uni-data-checkbox,选择自己实现也是因为商品规格展示是比较复杂的,不自己实现的话无法达到目的:

uniapp下单选框的实现方法详解

看图中,三组规格选项是要相互联动的,选择了其中一个后,就得判断其余的是否可选。然后我认为也可以将已选中的取消。所以得自己实现,好根据业务定制。

代码如下:

<template>
   <!-- uniapp内置的单选组件,见https://uniapp.dcloud.io/component/radio.html -->
   <radio-group class="checklist-group" @change="change">
       <label
           class="checklist-box is--tag"
           v-for="item in radioData.option"
           :class="[radioData.selected === item.id ? 'is-checked' : '', item.disable ? 'is-disable' : '']">
           <radio
               class="hidden"
               :disabled="item.disable"
               :value="String(item.id)"
               :checked="radioData.selected === item.id" />
           <view class="checklist-content">
               <text class="checklist-text">{{ item.text }}</text>
           </view>
       </label>
   </radio-group>
</template>
<script setup lang="ts">
   const props = defineProps({
       // 该id设计的目的是为了应对数组,记录数组的下标,这样父类就不需要遍历查找了。当然也可以根据业务用于其他方面,不需要就不用即可。
       id: {
           type: [Number, String],
       },
       /*数据源,它的数据结构应该:{selected:,option:[{id:,disable:,text:,}...]}
       其中selected 的值应取自option的id。
       */
       radioData: {
           type: Object,
           required: true,
       },
   });
   // 点击后回调父类的change方法
   const emit = defineEmits(["change"]);
// 点击后触发
   function change(e: any) {
       // 参数:tag的id;props.id
       emit("change", e.detail.value, props.id);
   }
</script>
<style lang="scss">
   $checked-color: #2979ff;
   $border-color: #dcdfe6;
   $disable: 0.4;
   @mixin flex {
       /* #ifndef APP-NVUE */
       display: flex;
       /* #endif */
   }
   .checklist-group {
       @include flex;
       flex-direction: row;
       flex-wrap: wrap;
.checklist-box {
           @include flex;
           flex-direction: row;
           align-items: center;
           position: relative;
           margin: 5px 0;
           margin-right: 25px;
.hidden {
               position: absolute;
               opacity: 0;
           }
// 文字样式
           .checklist-content {
               @include flex;
               flex: 1;
               flex-direction: row;
               align-items: center;
               justify-content: space-between;
               .checklist-text {
                   font-size: 14px;
                   color: #666;
                   margin-left: 5px;
                   line-height: 14px;
               }
           }
// 单选样式
           .radio__inner {
               @include flex;
               /* #ifndef APP-NVUE */
               flex-shrink: 0;
               box-sizing: border-box;
               /* #endif */
               justify-content: center;
               align-items: center;
               position: relative;
               width: 16px;
               height: 16px;
               border: 1px solid $border-color;
               border-radius: 16px;
               background-color: #fff;
               z-index: 1;
.radio__inner-icon {
                   width: 8px;
                   height: 8px;
                   border-radius: 10px;
                   opacity: 0;
               }
           }
// 标签样式
           &.is--tag {
               margin-right: 10px;
               padding: 5px 10px;
               border: 1px $border-color solid;
               border-radius: 3px;
               background-color: #f5f5f5;
.checklist-text {
                   margin: 0;
                   color: #666;
               }
// 禁用
               &.is-disable {
                   /* #ifdef H5 */
                   cursor: not-allowed;
                   /* #endif */
                   opacity: $disable;
               }
&.is-checked {
                   background-color: $checked-color;
                   border-color: $checked-color;
.checklist-text {
                       color: #fff;
                   }
               }
           }
       }
   }
</style>

其实代码本身内容很少,是样式的代码多,我样式是直接照抄uni-data-checkbox的。

传入的数据结构应该是:

interface radio{
   selected: number;
   option: {
       id: number;
       text: string;
       disable: boolean;
   }[];
}

selectedid可以是别的类型,但selected是取值于id

这里得注意,数据源必须是响应式的,考虑这里肯定是个对象,那么就是要用reactive去包围数据,使其具有响应性,否则页面不会更新,例如下面:

const radioData= reactive(radio);

PS1:由于vue3规范建议:子组件不修改父组件的数据源,否则会导致数据的变化难以理解。所以change方法中没有做任何修改数据的动作。比如将selected直接修改也是完全可以的,但是我这里还是交由父组件去决定如何修改。

PS2:咋一看change方法仅传递了当前选择的选项,并没有告知之前的选项是什么,如果要对比前后的时候不是没有办法?其实selected就是存储的之前的选项,在修改它之前用它作比较即可。

PS3:由于radio本身是不支持选中之后再取消的,我们这里采用将selected赋值为一个不存在的id,这样就会取消选择了。但是会报错:

uni-shared.es.js:470 Uncaught TypeError: Cannot destructure property 'id' of 'el' as it is null.
   at normalizeTarget (uni-shared.es.js:470:13)
   at createNativeEvent (uni-h5.es.js:1260:13)
   at $nne (uni-h5.es.js:1234:15)
   at HTMLElement.invoker (vue.runtime.esm.js:9397:19)

但是不影响功能哈。

PS4:目前仅在H5下测试功能时正常的。

来源:https://blog.csdn.net/lsjweiyi/article/details/124080763

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com