单选/多选 组件

145 阅读3分钟

单选

单选组件代码如下

<template>
  <view>
    <view class="titleBox">
      <view class="top">
        <text class="title">{{ title }}</text>
        <text class="remark">已选:{{
          chooseRemark
        }}</text>
      </view>
      <view class="search-bar-box-body">
        <view class="search-bar-box">
          <uni-search-bar v-model="searchVal" bgColor="transparent" cancelButton="none" placeholder="搜索"></uni-search-bar>
        </view>
      </view>
    </view>
    <template v-for="item in radiolist">
      <view class="item" @click="choose(item)">
        {{ item[props.sign] }}
        <view class="check" v-if="item.isChoose"></view>
        <view class="uncheck" v-else> </view>
      </view>
    </template>
  </view>
</template>
<script setup>
import { ref, computed } from "vue";
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
const props = defineProps({
  sign: {
    //标识,需要显示的字段
    type: String,
    default: "",
  },
});
const title = ref("单选");
const chooseRemark = ref("");
const list = ref([]); //如果包含isChoose=true则为选中
//已选中的list
const chooselist = computed(() => {
  return list.value.filter((item) => item.isChoose)[0];
});
//选择
function choose(item) {
  list.value.forEach(element => {
    element.isChoose = false;
  });
  item.isChoose = true;
  chooseRemark.value = item[props.sign]
}
//筛选
const searchVal = ref();
const radiolist = computed(() => {
  if (!searchVal.value) {
    return list.value;
  }
  return list.value.filter((item) =>
    item[props.sign].includes(searchVal.value)
  );
});
defineExpose({
  title,
  chooseRemark,
  list,
  chooselist,
});
</script>

<style scoped lang="scss">
.item:last-child {
  border-bottom: 1px solid #fff;
}

.item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 30rpx 0;
  border-bottom: 1px solid rgba(217, 217, 217, 1);
  //   margin-top:20rpx;
}

.check {
  width: 40rpx;
  height: 40rpx;
  border-radius: 50%;
  background: rgba(51, 124, 250, 1);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 600;
  border: 1rpx solid rgba(51, 124, 250, 1);
}

.uncheck {
  width: 40rpx;
  height: 40rpx;
  border-radius: 50%;
  border: 1rpx solid rgba(122, 122, 122, 1);
}

.titleBox {
  width: 100%;
  position: sticky;
  top: 0px;
  background-color: #fff;
  padding-bottom: 20rpx;

  .top {
    display: flex;
    align-items: flex-end;

    .title {
      font-size: 34.35rpx;
      font-weight: 600;
    }

    .remark {
      margin-left: 30rpx;
      color: rgba(122, 122, 122, 1);
      font-size: 26.71rpx;
    }
  }

  .search-bar-box-body {
    padding: 20rpx 0 0;
    position: sticky;
    top: 0px;
    background-color: #fff;
    z-index: 9;

    :deep {
      .uni-searchbar__box {
        justify-content: start;
      }

      .uni-input-placeholder {
        color: #b5b5b5;
      }
    }

    .search-bar-box {
      display: flex;
      justify-content: space-between;
      align-items: center;
      width: 100%;
      height: 65rpx;
      background-color: #f3f5f9;
      border-radius: 60rpx;

      .uni-searchbar {
        flex: 1;
      }

      .search-btn {
        margin-right: 26rpx;
        color: $uni-primary;
        font-size: 24rpx;
        line-height: 24px;
      }
    }
  }
}</style>

使用如下,需要使用到弹窗容器包裹,具体看我以前写的,实现代码及图例如下,根据不同种单选类型二次封装组件,让逻辑部分的代码更加简洁

image.png

<template>
    <view @click="open()">打开弹窗</view>
    <popupBox ref="Popup" :onConfirmDisable="onConfirmDisable">
        <template #title> </template>
        <template #content>
            <popupBox_Content_radio ref="popupBox_Content_radio_Ref" sign="name"></popupBox_Content_radio>
        </template>
    </popupBox>
</template>
  
<script setup>
import popupBox from "./components/popupBox";
import popupBox_Content_radio from "./components/popupBox_Content_radio";
import { ref, computed, onMounted, watch, defineEmits } from "vue";
import { onLoad } from "@dcloudio/uni-app";
// 弹窗容器
const Popup = ref(null);
// 单选组件
const popupBox_Content_radio_Ref = ref(null);
// 单选列表
const SelectList = [{ name: "选项1", id: 1, isChoose: true },
{ name: "选项2", id: 1 },
{ name: "选项3", id: 1 },
{ name: "选项4", id: 1 }]
// 已选项
const isSelect = ref(SelectList[0]);
// 是否禁用确认按钮
const onConfirmDisable = computed(() => {
    if (!popupBox_Content_radio_Ref.value?.chooselist) {
        return true
    }
    return false
})
function open() {
    //打开弹窗
    Popup.value.open({
        cancelText: "取消",
        confirmText: "确认",
        onOpen: function () {
            //单选标题
            popupBox_Content_radio_Ref.value.title = "单选组件标题";
            //单选已选
            popupBox_Content_radio_Ref.value.chooseRemark = isSelect.value.name;
            //单选列表
            popupBox_Content_radio_Ref.value.list =JSON.parse(JSON.stringify(SelectList))  
        },
        onConfirm: function () {
            //已选项赋值
            isSelect.value = popupBox_Content_radio_Ref.value.chooselist
            Popup.value.close()//关闭弹窗
        },
        onClose: function () {
            console.log("触发了弹窗《关闭》事件");
        },
    });
}
</script>
<style lang="scss" scoped></style>

多选

多选组件代码如下

<template>
  <view>
    <view class="titleBox">
      <view class="top">
        <text class="title">{{ title }}</text>
        <text class="remark"
          >已选:{{
            chooseRemark || `${chooselist.length}/${list.length}`
          }}</text
        >
      </view>
      <view class="search-bar-box-body">
        <view class="search-bar-box">
          <uni-search-bar
            v-model="searchVal"
            bgColor="transparent"
            cancelButton="none"
            placeholder="搜索"
          ></uni-search-bar>
        </view>
      </view>
    </view>
    <template v-for="item in multiplelist">
      <view class="item" @click="item.isChoose = !item.isChoose">
        {{ item[props.sign] }}
        <view class="check" v-if="item.isChoose"></view>
        <view class="uncheck" v-else> </view>
      </view>
    </template>
  </view>
</template>
<script setup>
import { ref, computed } from "vue";
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";

const props = defineProps({
  sign: {
    //标识,需要显示的字段
    type: String,
    default: "",
  },
});
const title = ref("多选");
const chooseRemark = ref("");
const sign = ref("");
const list = ref([]); //如果包含isChoose=true则为选中
//已选中的list
const chooselist = computed(() => {
  return list.value.filter((item) => item.isChoose);
});
//选项列表
const searchVal = ref();
const multiplelist = computed(() => {
  if (!searchVal.value) {
    return list.value;
  }
  return list.value.filter((item) =>
    item[props.sign].includes(searchVal.value)
  );
});
defineExpose({
  title,
  chooseRemark,
  list,
  chooselist,
});
</script>

<style scoped lang="scss">
.item:last-child {
  border-bottom: 1px solid #fff;
}
.item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 30rpx 0;
  border-bottom: 1px solid rgba(217, 217, 217, 1);
  //   margin-top:20rpx;
}
.check {
  width: 40rpx;
  height: 40rpx;
  border-radius: 8rpx;
  background: rgba(51, 124, 250, 1);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 600;
  border: 1rpx solid rgba(51, 124, 250, 1);
}
.uncheck {
  width: 40rpx;
  height: 40rpx;
  border-radius: 8rpx;
  border: 1rpx solid rgba(122, 122, 122, 1);
}
.titleBox {
  width: 100%;
  position: sticky;
  top: 0px;
  background-color: #fff;
  padding-bottom: 20rpx;
  .top {
    display: flex;
    align-items: flex-end;
    .title {
      font-size: 34.35rpx;
      font-weight: 600;
    }
    .remark {
      margin-left: 30rpx;
      color: rgba(122, 122, 122, 1);
      font-size: 26.71rpx;
    }
  }

  .search-bar-box-body {
    padding: 20rpx 0 0;
    position: sticky;
    top: 0px;
    background-color: #fff;
    z-index: 9;
    :deep {
      .uni-searchbar__box {
        justify-content: start;
      }
      .uni-input-placeholder {
        color: #b5b5b5;
      }
    }
    .search-bar-box {
      display: flex;
      justify-content: space-between;
      align-items: center;
      width: 100%;
      height: 65rpx;
      background-color: #f3f5f9;
      border-radius: 60rpx;
      .uni-searchbar {
        flex: 1;
      }
      .search-btn {
        margin-right: 26rpx;
        color: rgba(51, 124, 250, 1);
        font-size: 24rpx;
        line-height: 24px;
      }
    }
  }
}
</style>

使用如下,需要使用到弹窗容器包裹,具体看我以前写的,实现代码及图例如下,根据不同种多选类型二次封装组件,让逻辑部分的代码更加简洁

image.png

<template>
  <view @click="open()">打开弹窗</view>
  <popupBox ref="Popup" :onConfirmDisable="onConfirmDisable">
      <template #title> </template>
      <template #content>
          <popupBox_Content_multipleChoice ref="popupBox_Content_multipleChoice_Ref" sign="name"></popupBox_Content_multipleChoice>
      </template>
  </popupBox>
</template>

<script setup>
import popupBox from "./components/popupBox";
import popupBox_Content_multipleChoice from "./components/popupBox_Content_multipleChoice";
import { ref, computed, onMounted, watch, defineEmits } from "vue";
import { onLoad } from "@dcloudio/uni-app";
// 弹窗容器
const Popup = ref(null);
// 多选组件
const popupBox_Content_multipleChoice_Ref = ref(null);
// 多选列表
const SelectList = [{ name: "选项1", id: 1, isChoose: true },
{ name: "选项2", id: 1 },
{ name: "选项3", id: 1 },
{ name: "选项4", id: 1 }]
// 已选项
const isSelect = ref([SelectList.values[0]]);
// 是否禁用确认按钮
const onConfirmDisable = computed(() => {
  if (!popupBox_Content_multipleChoice_Ref.value?.chooselist.length) {
      return true
  }
  return false
})
function open() {
  //打开弹窗
  Popup.value.open({
      cancelText: "取消",
      confirmText: "确认",
      onOpen: function () {
          //多选标题
          popupBox_Content_multipleChoice_Ref.value.title = "多选组件标题";
          //多选列表
          popupBox_Content_multipleChoice_Ref.value.list = JSON.parse(JSON.stringify(SelectList))
             
      },
      onConfirm: function () {
          //已选项赋值
          isSelect.value = popupBox_Content_multipleChoice_Ref.value.chooselist
          Popup.value.close()//关闭弹窗
      },
      onClose: function () {
          console.log("触发了弹窗《关闭》事件");
      },
  });
}
</script>
<style lang="scss" scoped></style>

✓符号 替换

这个✓符号在实际项目运行不好看,可以替换为svg

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15" height="15" viewBox="0 0 32 32">
  <path
    d="M1.952 18.080q-0.32-0.352-0.416-0.88t0.128-0.976l0.16-0.352q0.224-0.416 0.64-0.528t0.8 0.176l6.496 4.704q0.384 0.288 0.912 0.272t0.88-0.336l17.312-14.272q0.352-0.288 0.848-0.256t0.848 0.352l-0.416-0.416q0.32 0.352 0.32 0.816t-0.32 0.816l-18.656 18.912q-0.32 0.352-0.8 0.352t-0.8-0.32l-7.936-8.064z"
    fill="rgba(51, 124, 250, 1)"
  ></path>
</svg>

在uniapp使用,具体样式和svg颜色自己改

<image :src="isChoosesvg" mode="widthFix"></image>
import isChoosesvg from "./xxxx.svg";