Vue 3.0 + Element Plus 下拉框表格组件

前言

由于开发中频繁使用到下拉表格组件,每次重新写太浪费时间了,所以将其封装为一个通用组件便于日后重复利用。
组件实时详情可查看gitee

用法

属性(带有[Required]为必填属性)说明
data[Required]双向绑定数据
fields[Required]表格列对象
tableData[Required]表格数据对象
value双向绑定数据的值(为空时data属性为表格当前行数据对象)
label选择框显示的文本内容(为空时分别查看value和objKey是否为空,若value和objKey都有值,则value优先级大于objKey)
objKey对象key(绑定值的唯一标识,绑定值为对象时必填)
placeholder选择框占位文本
size组件大小
border表格是否带有边

示例代码及效果展示

<template>
  <div>
    <SelectTable
      v-model:data="data"
      :fields="fields"
      :tableData="tableData"
      :label="label"
      :objKey="objKey"
      :border="true"
    ></SelectTable>
    <el-button @click="test">测试</el-button>
  </div>
</template>

<script setup lang="ts">
import SelectTable from "./components/SelectTable.vue";
import { ref } from "vue";

const data = ref({
  id: "1002",
  name: "李四",
  address: "No. 189, Grove St, Los Angeles",
});
const objKey = ref("id");
const label = ref("name");

const test = () => {
  console.log(data.value);
};

const fields = [
  {
    prop: "id",
    label: "ID",
    width: 180,
  },
  {
    prop: "name",
    label: "姓名",
    width: 180,
  },
  {
    prop: "address",
    label: "地址",
    width: 200,
    showTooltip: true,
  },
];

const tableData = [
  {
    id: "1001",
    name: "张三",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    id: "1002",
    name: "李四",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    id: "1003",
    name: "王五",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    id: "1004",
    name: "赵六",
    address: "No. 189, Grove St, Los Angeles",
  },
];
</script>

<style scoped></style>

效果

QQ录屏20231115092710

源代码

<!--
 * SelectTable 下拉表格组件
 * @author: Goal
 * @since: 2023-11-13
 * SelectTable.vue
 *
 * 属性(带有[Required]为必填属性)
 * data[Required]: 双向绑定数据
 * fields[Required]: 表格列对象
 * tableData[Required]: 表格数据对象
 * value: 双向绑定数据的值(为空时data属性为表格当前行数据对象)
 * label: 选择框显示的文本内容(为空时分别查看value和objKey是否为空,若value和objKey都有值,则value优先级大于objKey)
 * objKey: 对象key(绑定值的唯一标识,绑定值为对象时必填)
 * placeholder: 选择框占位文本
 * size: 组件大小
 * border: 表格是否带有边框
-->
<template>
  <div>
    <el-select
      ref="selectTable"
      v-model="state.selectShowValue"
      :placeholder="props.placeholder"
      :size="props.size"
    >
      <template #empty>
        <el-table
          :data="props.tableData"
          :highlight-current-row="props.isHighlight"
          style="width: 100%"
          :size="props.size"
          :border="props.border"
          @current-change="handleCurrentChange"
        >
          <el-table-column
            v-for="field in props.fields"
            :prop="field.prop"
            :label="field.label"
            :width="field.width"
            :show-overflow-tooltip="field.showTooltip"
          />
        </el-table>
      </template>
    </el-select>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";

interface Field {
  prop: string;
  label: string;
  width: number;
  showTooltip?: boolean;
}

interface Props {
  data: any;
  fields: Field[];
  tableData: object[];
  label?: string;
  value?: string;
  objKey?: string;
  isHighlight?: boolean;
  size?: string;
  placeholder?: string;
  border?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
  placeholder: "请选择",
  size: "default",
  isHighlight: true,
  value: undefined,
  label: undefined,
  border: false,
});

interface Emits {
  (e: "update:data", val: any): void;
}

const emits = defineEmits<Emits>();

onMounted(() => {
  //处理数据回显
  // debugger;
  if (props.data !== null) {
    // 配置label则显示label
    if (typeof props.label !== "undefined") {
      // 配置了value属性,则找到与value属性值一样的表格行对象,将配置的label回显到下拉框
      if (typeof props.value !== "undefined") {
        let row = props.tableData.find(
          (item) => item[props.value] === props.data
        );
        state.selectShowValue =
          typeof row === "undefined" ? props.data : row[props.label];
        // 配置了objKey属性,则找到与objKey属性值一样的表格行对象,将配置的label回显到下拉框
      } else if (typeof props.objKey !== "undefined") {
        let row = props.tableData.find(
          (item) => item[props.objKey] === props.data[props.objKey]
        );
        state.selectShowValue =
          typeof row === "undefined"
            ? props.data[props.objKey]
            : row[props.label];
      }
      //没有配置label属性则回显value或者objKey
    } else {
      // 配置了value属性,则找到与value属性值一样的表格行对象,将配置的label回显到下拉框
      if (typeof props.value !== "undefined") {
        let row = props.tableData.find(
          (item) => item[props.value] === props.data
        );
        state.selectShowValue =
          typeof row === "undefined" ? props.data : row[props.value];
        // 配置了objKey属性,则找到与objKey属性值一样的表格行对象,将配置的label回显到下拉框
      } else if (typeof props.objKey !== "undefined") {
        let row = props.tableData.find(
          (item) => item[props.objKey] === props.data
        );
        state.selectShowValue =
          typeof row === "undefined" ? props.data : row[props.objKey];
      }
    }
  }
});

const selectTable = ref();

const state = reactive({
  selectValue: null,
  selectShowValue: null,
});

/**
 * 点击当前行事件
 * @param val
 */
const handleCurrentChange = (val: any) => {
  // 如果没有配置label则显示value或者objKey
  setLabel(val);
  //  设置双向绑定值
  if (typeof props.value !== "undefined") {
    emits("update:data", val[props.value]);
  } else {
    emits("update:data", val);
  }
  selectTable.value.blur();
};

const setLabel = (val: any) => {
  // 如果没有配置label则显示value或者objKey
  if (typeof props.label === "undefined") {
    // 如果配置了value,则显示value
    if (typeof props.value !== "undefined") {
      state.selectShowValue = val[props.value];
    } else if (typeof props.objKey !== "undefined") {
      //没有配置value但配置了objKey,则显示objKey
      state.selectShowValue = val[props.objKey];
    }
  } else {
    state.selectShowValue = val[props.label];
  }
};
</script>

<style scoped></style>

总结

这是第一个做前端的通用组件封装,肯定有很多不到位的地方。后续可能还会加上多选的功能,希望各位前端大佬能在评论区指出我问题所在并且提出宝贵的意见🙏🙏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值