一尘子、道法自然、博客园、前端

vue3封装筛选项

背景

项目开发中遇到筛选项,并且几个页面都有使用,依次写,太过于繁琐

筛选项解构如下

封装全局组件fjj-content

<template>
  <div class="fjj-content">
    <ul class="flex-col fjj-list">
      <li v-for="(item, index) in list" :key="index">
        <!-- custom 单选、多选 item.isMutiple 是否支持多选 -->
        <div v-if="item.type === 'custom'">
          <!-- item.isMutiple true 多选 -->
          <div class="flex-row">
            <span class="label-box">{{ item.title }}:</span>
            <ul class="flex-row tags-list items-center">
              <!-- 选中默认项 -->
              <li
                :class="{ activeLi: item.defaultSelect }"
                class="justify-center items-center"
                @click="selectDefault(item.detailList, item.isMutiple, item)"
              >
                <span> {{ item.defaultLabel }}</span>
              </li>
              <!-- 多选项、单选项公用一个 -->
              <li
                v-for="(ele, eIndex) in item.detailList"
                :key="eIndex"
                :class="{ activeLi: ele.isSelected }"
                class="justify-center items-center"
                @click="selectMutiple(eIndex, item.key, item.isMutiple, item)"
              >
                <span> {{ ele.title }}</span>
              </li>
              <!-- 单选项,且有日期选择框的那种 -->
              <div v-if="!item.isMutiple && item.childrenType == 'rangetime'">
                <el-date-picker
                  v-model="item.dateTime"
                  type="datetimerange"
                  range-separator="至"
                  start-placeholder="开始日期"
                  end-placeholder="结束日期"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  @change="changeDate($event, item.key, item)"
                />
              </div>
            </ul>
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>

<script setup>
/***
 * 筛选组件,当前支持多选、单选
 * item.type (custom 单选、多选、rangetime 时间范围带时分秒、range 时间范围不带时分秒、rangenumber 数字范围)
 * item.isMutiple 是否支持多选
 * 筛选后返回格式{"listName1":[value,value](多选),"listName2":"value"(单选),...}
 * rangenumber形式-可能为["",1]或[1,""]表示只有一个最大值或最小值
 ***/

// Props
defineProps({
  list: {
    type: Array,
    default() {
      return []
    }
  }
})

// Emit
const emit = defineEmits('resultConditon')

// Result object
const result = {}

// 选中默认项
const selectDefault = (list, isMutiple, item) => {
  if (isMutiple) {
    // 多选情况下,选中所有项
    for (let i = 0; i < list.length; i++) {
      list[i].isSelected = true
    }
  } else {
    // 单选情况下,取消选中所有项并清空日期选择框
    for (let i = 0; i < list.length; i++) {
      list[i].isSelected = false
    }
    item.dateTime = null
  }
  item.defaultSelect = true
  result[item.key] = ''
  emit('resultConditon', result)
}

// 多选项点击事件
const selectMutiple = (index, key, isMutiple, item) => {
  if (isMutiple) {
    // 多选情况下,切换选中状态并更新结果对象
    item.detailList[index].isSelected = !item.detailList[index].isSelected
    if (!result[key]) {
      result[key] = []
    }
    if (item.detailList[index].isSelected) {
      result[key].push(item.detailList[index].value)
    } else {
      item.detailList[index].isSelected = false
      let idx = result[key].indexOf(item.detailList[index].value)
      result[key].splice(idx, 1)
    }
    let length = item.detailList.filter(item => item.isSelected).length
    if (length === item.detailList.length || length == 0) {
      item.defaultSelect = true
    } else {
      item.defaultSelect = false
    }
    console.log(result)
  } else {
    // 单选情况下,更新选中项并更新结果对象
    item.defaultSelect = false
    item.dateTime = null
    result[key] = item.detailList[index].value
    for (let i = 0; i < item.detailList.length; i++) {
      if (index == i) {
        item.detailList[i].isSelected = true
      } else {
        item.detailList[i].isSelected = false
      }
    }
  }
  emit('resultConditon', result)
}

// 选中日期框
const changeDate = (value, key, item) => {
  const createTime = value ? [...value].join(',') : ''
  result[key] = createTime
  item.defaultSelect = false
  for (let i = 0; i < item.detailList.length; i++) {
    item.detailList[i].isSelected = false
  }
  if (!createTime) {
    item.defaultSelect = true
  }
  emit('resultConditon', result)
}
</script>
<style lang="scss" scoped>
/* @use ''; 引入css类 */
.fjj-content {
  .fjj-list {
    row-gap: 10px;
    & > li {
      .label-box {
        margin-right: 8px;
        font-size: 14px;
        font-family: Microsoft YaHei;
        font-weight: 400;
        color: #818181;
        padding: 8px 0;
      }
      .tags-list {
        li {
          padding: 8px 14px;
          margin-right: 2px;
          cursor: pointer;
          span {
            line-height: 14px;
            font-size: 14px;
            font-family: Microsoft YaHei;
            font-weight: 400;
            color: #333333;
          }
          &.activeLi {
            background: #ebf5ff;
            span {
              color: #2794ff;
            }
          }
        }
      }
    }
  }
}
</style>

以下是引入代码模块

<template>
  <div class="newsInfo">
    <!-- 筛选项 -->
    <fjj-condition :list="menuList" @resultConditon="resultConditon" />
  </div>
</template>

<script setup>
/**
 * 筛选项数据
 */
const menuList = ref([
  {
    title: '信源类型',
    type: 'custom',
    key: 'siteTypes',
    isMutiple: true, //多选
    defaultLabel: '全选',
    defaultSelect: true,
    detailList: [
      {
        value: '6FD6F9F43DE8420',
        title: '智库网站'
      },
      {
        value: '5F4990CACF12410',
        title: '新闻网站'
      },
      {
        value: '5F4990CACF12666',
        title: '社交媒体'
      }
    ]
  },
  {
    title: '采集时间',
    type: 'custom',
    key: 'createTime',
    isMutiple: false, //单选
    defaultLabel: '全部',
    defaultSelect: true,
    childrenType: 'rangetime',
    detailList: [
      {
        title: '24h',
        value: 1
      },
      {
        title: '近三日',
        value: 2
      },
      {
        title: '最近一周',
        value: 3
      },
      {
        title: '最近一个月',
        value: 4
      }
    ]
  }
])
// 获取筛选项数据
const resultConditon = obj => {
  console.log(obj)
}
</script>
<style lang="scss" scoped>
/* @use ''; 引入css类 */
.newsInfo {
  padding: 24px 39px 23px 40px;
}
</style>
posted @ 2023-08-31 18:48  一尘子!  阅读(164)  评论(0编辑  收藏  举报
Live2D
返回顶端