手搓一个下拉框多组单选组件,
实现采用框架 arco-disign vue + vue3
组件采用:a-popover、a-input-tag、a-radio-group、a-radio
实现效果:
在这里插入图片描述

调用组件

<SelectGroupRadio
 v-model="searchModel.indicatorScreening"
  :options="dict.indicatorScreening"
></SelectGroupRadio>
const searchModel = ref({
    indicatorScreening: ['01', '04', '05'],
  });
  const dict = reactive({
    indicatorScreening: [
      // 动态sql 拼接
      [
        { value: '01', label: '超标' },
        { value: '02', label: '非超标' },
      ],
      [
        {
          value: '03',
          label: '已维护',
        },
        {
          value: '04',
          label: '未维护',
        },
      ],
      [
        {
          value: '05',
          label: '持续',
        },
        {
          value: '06',
          label: '非持续',
        },
      ],
    ],
<!-- 
  实现下拉列表:
    多组单选
    modelValue:可由此设置默认值,以及获取所选值
    options:下拉显示的内容,需要一个二维数组:
              例如: [
                        [
                          { value: '01', label: '超标' },
                          { value: '02', label: '非超标' },
                        ],
                        [
                          {
                            value: '03',
                            label: '已豁免',
                          },
                          {
                            value: '04',
                            label: '未豁免',
                          },
                        ],
                      ]
-->
<template>
  <a-popover position="bl" trigger="click" :arrow-style="{ display: 'none' }">
    <a-input-tag
      v-model="inputTag"
      allow-clear
      :max-tag-count="2"
      @remove="remove"
      @clear="clear"
      @input-value-change="null"
      @press-enter="null"
    />
    <template #content>
      <div v-for="(group, index) in props.options" :key="index">
        <a-radio-group
          :key="index"
          v-model="redioModel['model' + index]"
          direction="vertical"
          style="width: 290px"
          @change="change"
        >
          <a-radio
            v-for="(item, iindex) in group"
            :key="item.value + iindex"
            :value="item.value"
            >{{ item.label }}</a-radio
          >
        </a-radio-group>
        <a-divider v-if="index < options.length - 1" />
      </div>
    </template>
  </a-popover>
</template>

<script lang="ts" setup>
  import { ref, reactive, onMounted, computed, watch } from 'vue';
  import XEUtils from 'xe-utils';

  const props: any = defineProps({
    modelValue: {
      type: Array,
      required: true,
      default: () => {
        return [];
      },
    },
    options: {
      type: Array,
      required: true,
      default: () => {
        return [];
      },
    },
  });

  //  初始化每个组的绑定变量
  const redioModel = ref<any>({});
  props.options.map((item, index) => {
    redioModel.value[`model${index}`] = '';
    return item;
  });

  const inputTag = ref([]);
  // 回填值
  const emits = defineEmits(['update:modelValue', 'resultValue']);
  const resultValue = () => {
    emits(
      'update:modelValue',
      XEUtils.flatten(props.options)
        .filter((item) => inputTag.value.indexOf(item.label) !== -1)
        .map((item) => item.value)
    );
  };

  const change = (value) => {
    // 过滤出二维数组中匹配的一组
    const groupItem = props.options.filter((gItem, index) => {
      if (
        gItem.filter((item) => {
          return item.value === value;
        }).length > 0
      ) {
        redioModel.value[`model${index}`] = value; // 赋值新绑定的值
        return true;
      }
      return false;
    })[0];
    if (inputTag.value.length > 0) {
      // 从tag 中 移除掉该组中所有的选项
      groupItem.map((item: any) => {
        const index = inputTag.value.indexOf(item.label);
        if (index !== -1) {
          inputTag.value.splice(index, 1);
        }
        return item;
      });
    }

    // 向Tag框中赋值当前选择的项
    const yesItem = groupItem.filter((item: any) => item.value === value);
    yesItem.map((item: any) => {
      inputTag.value.push(item.label);
      return item;
    });
    resultValue();
  };
  // 默认值回填
  watch(
    () => props.modelValue,
    (newValue, oldValue) => {
      if (newValue.length > 0) {
        XEUtils.flatten(props.options)
          .filter((item) => newValue.indexOf(item.value) !== -1)
          .map((item) => change(item.value));
      }
    },
    { deep: true, immediate: true }
  );

  const clear = () => {
    // 清除下拉选项框中绑定的值
    props.options.map((item, index) => {
      redioModel.value[`model${index}`] = '';
      return item;
    });
    // 清除父组件v-model 绑定的值
    inputTag.value = [];
  };

  const remove = (label) => {
    // 1.过滤出二维数组中匹配的一组下标, 根据下标删除
    props.options.filter((gItem, index) => {
      if (
        gItem.filter((item) => {
          return item.label === label;
        }).length > 0
      ) {
        redioModel.value[`model${index}`] = ''; // 将绑定的值置空
        return true;
      }
      return false;
    });
    // 移除tag 输入框中对应的元素
    const index = inputTag.value.indexOf(label);
    if (index !== -1) {
      inputTag.value.splice(index, 1);
    }
    // 重置父组件绑定的值
    resultValue();
  };

  onMounted(() => {});
  defineExpose({});
</script>

<style></style>

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐