优化表单交互:在 el-select 组件中嵌入表格显示选项

介绍了一种通过 el-select 插槽实现表格样式数据展示的方案,可更直观地辅助用户选择。支持列配置、行数据绑定及自定义搜索,简洁高效,适用于复杂选择场景。完整代码见GitHub 仓库

背景

在进行业务开发选择订单时,如果单纯的根据单号是无法编写是哪一条订单的,这个时候就可以通过表格的方式去展示这条订单的其他信息,辅助用户分辨出订单,不用再去查,更快速、更友好。

效果如下:

实现

环境与依赖

  • node 22
  • vue 3
  • element-plus

思路

原理很简单,利用插槽。

虽然通过 el-table 可以实现表格效果,但对这种简单需求来说过于臃肿。而直接使用原生 table 标签结构实现太过于繁琐,不便于实现遍历 el-option

html 复制代码
<el-select>
    <!-- 表头,遍历 -->
    <!-- 表体,遍历 -->
    <el-option>
    	<!-- 行数据,遍历 -->
    </el-option>
</el-select>

因此,我采用列表结构实现,结合 CSS 美化出表格效果。具体做法是遍历「列配置」生成表头,再遍历「options」生成行数据。

vue 复制代码
<el-select 
  ref="inputRef" 
  v-model="selectValue"
  clearable
  filterable
  :placeholder="placeholder" 
  :disabled="disabled" 
  :filter-method="filterMethod" 
  @change="handleChange"
>
  <!-- 表头 -->
  <ul class="select-ul">
    <li v-for="column in columnConfig" :key="column.label">{{ column.label }}</li>
  </ul>
  <el-option v-for="item in showOptions" :key="item[keyNameCom]" :value="item[valueName]">
    <ul class="select-ul select-ul-data">
      <li v-for="column in columnConfig" :key="column.label">{{ item[column.prop] }}</li>
    </ul>
  </el-option>
</el-select>

其中,el-option 的数据绑定属性名默认是 id,可通过配置修改。同时,遍历 key 属性支持自定义,若为空则默认使用 value 属性名,减少额外数据处理。

如何使用

一个简单演示。

  • options:行数据
  • columnConfig:列配置

template 结构和 js 部分如下:

vue 复制代码
<BaseTableSelect 
  v-model="selectValue" 
  :options="options" 
  :columnConfig="columnConfig" 
>
</BaseTableSelect>
JavaScript 复制代码
const options = ref([
  {id: "213", department: '古典风格号', createTime: '2024-12-23', place: '学校'},
  {id: "546", department: '都听好', createTime: '2024-12-23', place: '家里'},
  {id: "345", department: '按时到岗', createTime: '2024-12-23', place: '医院'}
])

const columnConfig = ref([
  {label: '单号', prop: 'id'},
  {label: '部门', prop: 'department'},
  {label: '时间', prop: 'createTime'},
  {label: '地点', prop: 'place'},
])

自定义全表格搜索

组件 el-select 默认的搜索功能只会根据 label 属性的值去搜索,在表格展示的场景下并不符合,因此需要用到 filter-method 自定义搜索方法属性。

初步简单实现可使用 JSON.stringify() 将选项对象转为字符串,并检测是否包含搜索关键词:

js 复制代码
const filterMethod = queryString => {
  showOptions.value = props.options.filter(item => (JSON.stringify(item).includes(queryString) ? true : false))
}

不过,这种方式会将未展示的属性也纳入搜索,导致结果不准确。因此,优化后的搜索方法仅匹配已展示的列数据:

js 复制代码
const filterMethod = queryString => {
  if (!queryString) {
    showOptions.value = props.options;
    return;
  }

  showOptions.value = props.options.filter(item => {
    // 针对每个要过滤的列进行判断
    return props.columnConfig.some(config => {
      const propValue = item[config.prop];
      // 将属性值转换为字符串并检查是否包含查询字符串
      return propValue && propValue.toString().includes(queryString);
    });
  });
}

扩展

本文提供的是基础实现。如果需要进一步功能扩展,例如控制搜索功能 (filterable) 或其他交互行为,可通过额外配置实现。但基础版本已满足我的需求,便不写太多不利于阅读代码。

组件代码

仓库:🔗 el-select-table-option

vue 复制代码
<template>
  <el-select 
    ref="inputRef" 
    v-model="selectValue"
    clearable
    filterable
    :placeholder="placeholder" 
    :disabled="disabled" 
    :filter-method="filterMethod" 
    @change="handleChange"
  >
    <!-- 表头 -->
    <ul class="select-ul">
      <li v-for="column in columnConfig" :key="column.label">{{ column.label }}</li>
    </ul>
    <el-option v-for="item in showOptions" :key="item[keyNameCom]" :value="item[valueName]">
      <ul class="select-ul select-ul-data">
        <li v-for="column in columnConfig" :key="column.label">{{ item[column.prop] }}</li>
      </ul>
    </el-option>
  </el-select>
</template>

<script setup>
import { ref, reactive, onMounted, computed, watch, nextTick } from 'vue'

defineOptions({
  name: 'BaseTableSelect'
})

const emit = defineEmits(['update:modelValue', 'keyup-enter', 'focus', 'change'])

const props = defineProps({
  modelValue: null,
  disabled: {
    type: Boolean,
    default: false
  },
  placeholder: {
    type: String,
    default: '请选择'
  },
  /* 列配置 */
  columnConfig: {
    type: Array,
    default(rawProps) {
      return []
    }
  },
  options: {
    type: Array,
    default(rawProps) {
      return []
    }
  },
  valueName: {
    type: String,
    default: 'id'
  },
  keyName: {
    type: String,
    default: ''
  }
})

const selectValue = computed({
  get: () => props.modelValue,
  set: val => {
    emit('update:modelValue', val)
  }
})

// 实际 keyName
// 优先使用 keyName,keyName 为空时,使用 valueName
const keyNameCom = computed(() => {
  return props.keyName !== '' ? props.keyName : props.valueName
})

const handleChange = val => {
  emit('change', val)
}


// ref
const inputRef = ref(null)

// 渲染用的options
const showOptions = ref(props.options)
watch(
  () => props.options,
  newValue => {
    showOptions.value = newValue
    selectValue.value = ''
  },
)
// 筛选
const filterMethod = queryString => {
  if (!queryString) {
    showOptions.value = props.options;
    return;
  }

  showOptions.value = props.options.filter(item => {
    // 针对每个要过滤的列进行判断
    return props.columnConfig.some(config => {
      const propValue = item[config.prop];
      // 将属性值转换为字符串并检查是否包含查询字符串
      return propValue && propValue.toString().includes(queryString);
    });
  });
}

// 得到焦点
const getFocus = () => {
  nextTick(() => {
    inputRef.value.focus()
  })
}

defineExpose({
  getFocus
})
</script>

<style lang="scss" scoped></style>
<style scoped lang="scss">
.select-ul {
  padding-right: 0;
  list-style: none;
  display: flex;
  justify-content: space-between;
  text-align: center;
  padding-inline-start: 0;
  padding: 0 20px;
  > li {
    display: inline-block;
    width: 100px;
    // margin: 6px;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}
.el-select-dropdown__item {
  padding: 0;
}
</style>

属性

参数 说明 类型 默认值
disabled 是否禁用 boolean false
placeholder 请选择 string '请选择'
columnConfig 列配置,{label: '', prop: ''} array \[\]
options 选项 array \[\]
valueName 选项值的属性名 string 'id'
keyName 遍历选项时的 key string ''

参考

首发地址:https://blog.xchive.top/2024/optimizing-form-interactions-embedding-table-display-options-in-el-select-components.html

相关推荐
都在酒里几秒前
Linux字符设备驱动开发(九):内核定时器——实现LED周期性闪烁与轮询驱动原理
linux·运维·驱动开发·交互
都在酒里1 小时前
Linux字符设备驱动开发(十):综合实例——I2C传感器 + LED智能控制与进阶指南
linux·运维·服务器·驱动开发·交互
Csvn9 小时前
OpenSpec 详细使用教程
前端
之歆9 小时前
Day19_LESS 完全指南——从入门到工程实践
前端·css·less
云水一下10 小时前
HTML5 从入门到精通:实战收官——从零搭建完整静态网站,综合运用所有知识
前端·html5
不总是10 小时前
Windows 系统 Node.js 免安装版(zip)安装与配置教程(2026 最新)
前端·windows·node.js
冬奇Lab10 小时前
每日一个开源项目(第105篇):Twenty - 跳出 Salesforce 的圈套,定义现代开源 CRM
前端·后端·开源
zhangyao94033011 小时前
开发pc端时,表格的高度怎么设置才能铺满页面
前端·javascript·elementui
kjs--12 小时前
浏览器书签执行脚本
前端
之歆12 小时前
Day16_JavaScript 轮播图与事件工程实战(下篇)
服务器·开发语言·前端·javascript·网络·性能优化