优化表单交互:在 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

相关推荐
web150850966413 小时前
【React&前端】大屏适配解决方案&从框架结构到实现(超详细)(附代码)
前端·react.js·前端框架
理想不理想v3 小时前
前端项目性能优化(详细)
前端·性能优化
CodeToGym3 小时前
使用 Vite 和 Redux Toolkit 创建 React 项目
前端·javascript·react.js·redux
Cachel wood4 小时前
Vue.js前端框架教程8:Vue消息提示ElMessage和ElMessageBox
linux·前端·javascript·vue.js·前端框架·ecmascript
桃园码工6 小时前
4_使用 HTML5 Canvas API (3) --[HTML5 API 学习之旅]
前端·html5·canvas
桃园码工6 小时前
9_HTML5 SVG (5) --[HTML5 API 学习之旅]
前端·html5·svg
人才程序员6 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
m0_548514776 小时前
前端三大主流框架:React、Vue、Angular
前端·vue.js·react.js
m0_748232397 小时前
单页面应用 (SPA):现代 Web 开发的全新视角
前端
开心工作室_kaic7 小时前
springboot461学生成绩分析和弱项辅助系统设计(论文+源码)_kaic
开发语言·数据库·vue.js·php·apache