vue3 基于Ant DesignVue Select Table封装下拉选择表格组件(2023-08-25 TAntdSelectTable组件组件新增上下键盘高亮选择回车选中功能)

2023-08-25 TAntdSelectTable组件组件新增上下键盘高亮选择回车选中功能

vue3 基于Ant DesignVue Select Table封装下拉选择表格组件(2023-08-25 TAntdSelectTable组件组件新增上下键盘高亮选择回车选中功能)_第1张图片

一、最终效果

二、代码示例

<t-antd-select-table
    ref="tantdselecttable"
    selectWidth="40%"
    v-model="state.selectVal"
    :table="state.table"
    :columns="state.table.columns"
    :scroll="{ x: 2000, y: 400 }"
    isShowPagination
    :keywords="{ label: 'materialName', value: 'materialCode' }"
    @checked-change="checkedChange"
    :defaultSelectVal="state.defaultSelectVal"
    @change="tablePaginationChange"
    placeholder="antd下拉选择表格"
  >t-antd-select-table>

三、配置参数(Attributes)继承 a-table 及 a-select 属性

参数 说明 类型 默认值
v-model 绑定值 keywords.label Array -
table 表格数据对象 Object {}
—data 展示下拉数据源 Array []
—pagination 配合isShowPagination,其配置继承a-table Array []
columns 表格列的配置描述,具体项AntDesignVue文档 Array []
keywords 关键字配置 Object
------label 选项的标签 String ‘label’
------value 选项的值((value-key 配置) ) String ‘value’
rowSelection 列表项是否可选择–具体查看AntDesignVue文档 Object -
isShowPagination 是否开启分页 Boolean false
isKeyup 单选是否开启键盘事件(上下选择高亮,回车选中) Boolean false
mode 是否多选(配置’multiple’)默认单选 String -
defaultSelectVal 设置第一页默认选中项–keywords.value 值 Array -
selectWidth select宽度 (string设置百分比,number设置px) String/Number 200
tableWidth table 宽度 Number 550

2-1、columns 配置参数(Attributes)继承 a-table columns 属性

参数 说明 类型 默认值
align 设置列的对齐方式 left /right /center left
colSpan 表头列合并,设置为 0 时,不渲染 number -
customCell 设置单元格属性 Function(record, rowIndex, column) -
customFilterDropdown 启用 v-slot:customFilterDropdown,优先级低于 filterDropdown boolean false
customHeaderCell 设置头部单元格属性 Function(column) -
customRender 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引 Function({text, record, index, column}) {} -
dataIndex 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 string / string[] -
defaultFilteredValue 默认筛选值 string[] -
defaultSortOrder 默认排序顺序 ascend descend -
ellipsis 超过宽度将自动省略 设置为 true 或 { showTitle?: boolean } 时,表格布局将变成 tableLayout=“fixed”。 boolean / { showTitle?: boolean } false
filterDropdown 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 VNode -
filterDropdownVisible 用于控制自定义筛选菜单是否可见 boolean -
filtered 标识数据是否经过过滤,筛选图标会高亮 boolean false
filteredValue 筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组 string[] -
filterIcon 自定义 filter 图标。 VNode / ({filtered: boolean, column: Column}) => vNode false
filterMode 指定筛选菜单的用户界面 ‘menu’ /‘tree’ ‘menu’
filterMultiple 是否多选 boolean true
filters 表头的筛选菜单项 object[] -
filterSearch 筛选菜单项是否可搜索 Boolean false
fixed 列是否固定,可选 true(等效于 left) ‘left’ ‘right’ boolean/string false
key Vue 需要的 key,如果已经设置了唯一的 dataIndex,可以忽略这个属性 string -
maxWidth 拖动列最大宽度,会受到表格自动调整分配宽度影响 number -
minWidth 拖动列最小宽度,会受到表格自动调整分配宽度影响 number 50
resizable 是否可拖动调整宽度,此时 width 必须是 number 类型 boolean -
responsive 响应式 breakpoint 配置列表。未设置则始终可见。 Breakpoint[] -
showSorterTooltip 表头显示下一次排序的 tooltip 提示, 覆盖 table 中 showSorterTooltip boolean /Tooltip props true
sortDirections 支持的排序方式,取值为 ‘ascend’ ‘descend’ Array [‘ascend’, ‘descend’]
sorter 排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 true Function/boolean -
sortOrder 排序的受控属性,外界可用此控制列的排序,可设置为 ‘ascend’ ‘descend’ false boolean/string -
title 列头显示文字 string -
width 列宽度 string/number -
onFilter 本地模式下,确定筛选的运行函数, 使用 template 或 jsx 时作为filter事件使用 Function -
onFilterDropdownVisibleChange 自定义筛选菜单可见变化时调用,使用 template 或 jsx 时作为filterDropdownVisibleChange事件使用 function(visible) {} -

2-2、a-pagination 配置参数(Attributes)继承 a-pagination属性(分页配置)

参数 说明 类型 默认值
current(v-model) 当前页数 number -
defaultPageSize 默认的每页条数 number 10
disabled 禁用分页 boolean -
hideOnSinglePage 只有一页时是否隐藏分页器 boolean false
itemRender 用于自定义页码的结构,可用于优化 SEO ({page, type: ‘page’ / ‘prev’ /‘next’, originalElement}) => vNode / v-slot -
pageSize(v-model) 每页条数 number -
pageSizeOptions 指定每页可以显示多少条 string[] [‘10’, ‘20’, ‘30’, ‘40’]
responsive 当 size 未指定时,根据屏幕宽度自动调整尺寸 boolean -
showLessItems 是否显示较少页面内容 boolean false
showQuickJumper 是否可以快速跳转至某页 boolean false
showSizeChanger 是否展示 pageSize 切换器,当 total 大于 50 时默认为 true boolean -
showTotal 用于显示数据总量和当前数据顺序 Function(total, range) -
simple 当添加该属性时,显示为简单分页 boolean -
size 当为「small」时,是小尺寸分页 string “”
total 数据总数 number 0

2-3、rowSelection 配置参数(Attributes)继承 a-table rowSelection 属性

参数 说明 类型 默认值
checkStrictly checkable 状态下节点选择完全受控(父子数据选中状态不再关联) boolean true
columnTitle 自定义列表选择框标题 string/VNode -
columnWidth 自定义列表选择框宽度 string/number -
fixed 把选择框列固定在左边 boolean -
getCheckboxProps 选择框的默认属性配置 Function(record) -
hideDefaultSelections 去掉『全选』『反选』两个默认选项 boolean false
hideSelectAll 隐藏全选勾选框与自定义选择项 boolean false
preserveSelectedRowKeys 当数据被删除时仍然保留选项的 key boolean -
selectedRowKeys 指定选中项的 key 数组,需要和 onChange 进行配合 string[] []
selections 自定义选择项 配置项, 设为 true 时使用默认选择项 object[] / boolean true
type 多选/单选,checkbox or radio string checkbox
onChange 选中项发生变化时的回调 Function(selectedRowKeys, selectedRows) -
onSelect 用户手动选择/取消选择某列的回调 Function(record, selected, selectedRows, nativeEvent) -
onSelectAll 用户手动选择/取消选择所有列的回调 Function(selected, selectedRows, changeRows) -
onSelectInvert 用户手动选择反选的回调 Function(selectedRows) -
onSelectNone 用户清空选择的回调 function() -

四、事件(events)继承 a-table 及 a-select 事件

事件名 说明 回调参数
checkedChange 选中项事件 返回选中项的 keywords.value 集合与选中的项集合

五、方法(a-select—Methods)

方法名 说明 回调参数
focus 使 input 获取焦点 -
blur 使 input 失去焦点,并隐藏下拉框 -
openSelectDropdown 显示下拉框 -

六、 源码

<template>
  <a-select
    ref="selectRef"
    v-model:value="childSelectedValue"
    class="t-antd-select-table"
    dropdownClassName="t_antd_select_dropdown"
    :style="`width:${typeof selectWidth === 'number' ? `${selectWidth}px` : `${selectWidth}`}`"
    :mode="mode"
    :open="open"
    v-bind="selectAttr"
    :value-key="keywords.value"
    :filterOption="false"
    @search="filterMethodHandle"
    @dropdown-visible-change="visibleChange"
    @deselect="removeTag"
    @clear="clear"
  >
    <template #notFoundContent>
      <div class="t-table-select__table" :style="{ width: `${tableWidth}px` }">
        <a-table
          ref="selectTable"
          :data-source="state.tableData"
          :columns="columns"
          :class="{
            radioStyle: !(mode === 'multiple')
          }"
          bordered
          :row-key="getRowKey"
          :pagination="isShowPagination && table.pagination"
          :row-selection="
            rowSelection || {
              selectedRowKeys: state.selectedRowKeys,
              onChange: onSelectChange,
              onSelectNone: onSelectNone,
              type: mode === 'multiple' ? 'checkbox' : 'radio'
            }
          "
          :customRow="rowClick"
          v-bind="$attrs"
        >
          <template #[item]="scope" v-for="item in renderArr">
            <slot :name="item" :scope="scope" v-bind="scope || {}">slot>
          template>
          <slot>slot>
        a-table>
      div>
    template>
  a-select>
template>
<script lang="ts">
export default {
  name: "TAntdSelectTable"
};
script>
<script setup lang="ts">
// import { Table, Select } from "ant-design-vue";
import { computed, useAttrs, ref, watch, reactive, onMounted, useSlots } from "vue";
const props = defineProps({
  modelValue: {
    type: [String, Number, Array]
  },
  // table所需数据
  table: {
    type: Object,
    default: () => {
      return {};
    }
  },
  // 表头数据
  columns: {
    type: Array as unknown as any[],
    default: () => []
  },
  // 单选框--是否开启点击整行选中
  rowClickRadio: {
    type: Boolean,
    default: true
  },
  // 列表项是否可选择
  rowSelection: {
    type: Object
  },
  // 是否显示分页
  isShowPagination: {
    type: Boolean,
    default: false
  },
  // 下拉数据指向的label/value
  keywords: {
    type: Object,
    default: () => {
      return {
        label: "label",
        value: "value"
      };
    }
  },
  // 多选 'multiple'
  mode: {
    type: String
  },
  // select宽度
  selectWidth: {
    type: [String, Number],
    default: 200
  },
  // table宽度
  tableWidth: {
    type: Number,
    default: 550
  },
  // 设置默认选中项--keywords.value值(单选是String, Number类型;多选时是数组)
  defaultSelectVal: {
    type: Array as unknown as any[]
  }
});

const selectAttr = computed(() => {
  return {
    allowClear: true,
    showSearch: true,
    dropdownMatchSelectWidth: false,
    ...useAttrs()
  };
});
const slots = useSlots();
const renderArr = Object.keys(slots);

const isDefaultSelectVal = ref(true); // 是否已经重新选择了
const open = ref(false);
const state: any = reactive({
  defaultSelectValue: props.defaultSelectVal, // 默认选中
  tableData: props.table.data, // table数据
  // 选中KEY
  selectedRowKeys: [] as (string | number)[],
  // 选中行
  selectedRows: []
});
// 获取ref
const selectRef: any = ref<HTMLElement | null>(null);
const selectTable: any = ref<HTMLElement | null>(null);
// 抛出事件
const emits = defineEmits(["checkedChange", "update:modelValue"]);
watch(
  () => props.table.data,
  val => {
    state.tableData = val;
  },
  { deep: true }
);
watch(
  () => props.defaultSelectVal,
  val => {
    // console.log("defaultSelectValue--watch", val);
    state.defaultSelectValue = val;
    if (val && isDefaultSelectVal.value) {
      defaultSelect(val);
    }
  },
  { deep: true }
);
// vue3 v-model简写
let childSelectedValue = computed({
  get() {
    return props.modelValue;
  },
  set(val) {
    console.log("v-model简写", val);
    emits("update:modelValue", val);
  }
});
onMounted(() => {
  // 设置默认选中项
  if (state.defaultSelectValue && isDefaultSelectVal.value) {
    // console.log("state.defaultSelectValue--", state.defaultSelectValue);
    defaultSelect(state.defaultSelectValue);
  }
});
// 单选or 多选
const onSelectChange = (selectedRowKeys: any, selectedRows: any) => {
  setTimeout(() => {
    console.log("选择", selectedRowKeys, selectedRows);
    state.selectedRowKeys = selectedRowKeys;
    state.selectedRows = selectedRows;
    isDefaultSelectVal.value = false;
    if (state.selectedRowKeys.length > 0 && state.selectedRows.length > 0) {
      if (props.mode === "multiple") {
        childSelectedValue.value =
          state.selectedRows.length > 0 && state.selectedRows.map((item: { [x: string]: any }) => item[props.keywords.label]);
      } else {
        // console.log("单选", state.selectedRows[0]);
        childSelectedValue.value = state.selectedRows[0][props.keywords.label];
        blur();
      }
    } else {
      childSelectedValue.value = undefined;
    }
    emits("checkedChange", state.selectedRowKeys, state.selectedRows);
  }, 500);
};

const rowClick = (record: { [x: string]: any }) => {
  return {
    // 鼠标单击行
    onClick: () => {
      if (props.mode !== "multiple") {
        if (!props.rowClickRadio) return;
        // 单选
        const keys = [];
        const items = [];
        keys.push(record[props.keywords.value]);
        items.push(record);
        onSelectChange(keys, items);
      } else {
        // 多选
        const indexRow = state.selectedRowKeys.indexOf(record[props.keywords.value]);
        indexRow === -1 ? state.selectedRowKeys.push(record[props.keywords.value]) : state.selectedRowKeys.splice(indexRow, 1);
        if (indexRow === -1) {
          state.selectedRows.push(record);
        } else {
          state.selectedRows.splice(indexRow, 1);
        }
        onSelectChange(state.selectedRowKeys, state.selectedRows);
      }
    }
  };
};
// 默认选中(且只能默认选中第一页的数据)
const defaultSelect = (defaultSelectVal: any[]) => {
  if (props.mode === "multiple") {
    setTimeout(() => {
      if (defaultSelectVal.length > 0) {
        let multipleList: any = [];
        let selectedRowKeys: any = [];
        let selectedRows: any = [];
        defaultSelectVal.map((val: any) => {
          state.tableData.forEach((row: any) => {
            if (val === row[props.keywords.value]) {
              multipleList.push(row);
            }
          });
        });
        multipleList.forEach((row: { [x: string]: any }) => {
          state.tableData.forEach((item: { [x: string]: any }) => {
            if (item[props.keywords.value] === row[props.keywords.value]) {
              selectedRowKeys.push(item[props.keywords.value]);
              selectedRows.push(item);
            }
          });
        });
        state.selectedRowKeys = selectedRowKeys;
        state.selectedRows = selectedRows;
        // console.log("多选默认", selectedRowKeys, selectedRows);
        childSelectedValue.value =
          state.selectedRows.length > 0 && state.selectedRows.map((item: { [x: string]: any }) => item[props.keywords.label]);
        // console.log("childSelectedValue.value", childSelectedValue.value);
      } else {
        childSelectedValue.value = undefined;
      }
    }, 0);
  } else {
    setTimeout(() => {
      if (defaultSelectVal.length > 0) {
        state.tableData.map((val: { [x: string]: any }) => {
          if (val[props.keywords.value] === defaultSelectVal[0]) {
            state.selectedRowKeys = defaultSelectVal;
            state.selectedRows = [val];
          }
        });
        childSelectedValue.value = state.selectedRows[0] && state.selectedRows[0][props.keywords.label];
      } else {
        childSelectedValue.value = undefined;
      }
    }, 300);
  }
};
// RowKey配置
const getRowKey = (row: { [x: string]: any }) => {
  return row[props.keywords.value];
};
// 搜索过滤
const filterMethodHandle = (input: string) => {
  const tableData = JSON.parse(JSON.stringify(props.table?.data));
  if (tableData && tableData.length > 0) {
    if (!(props.mode === "multiple")) {
      if (input) {
        state.selectedRowKeys = [];
      } else {
        tableData.map((item: { [x: string]: any }) => {
          if (item[props.keywords.value] === state.selectedRows[0] && state.selectedRows[0][props.keywords.value]) {
            state.selectedRowKeys = [item[props.keywords.value]];
          }
        });
      }
    }
    state.tableData = tableData.filter((item: { [x: string]: string | any[] }) => {
      if (item[props.keywords.label].includes(input)) {
        return item;
      }
    });
  }
};
// 表格显示隐藏回调
const visibleChange = (visible: boolean) => {
  // console.log("表格显示隐藏回调", visible);
  open.value = visible;
  if (visible) {
    if (props.defaultSelectVal && isDefaultSelectVal.value) {
      defaultSelect(props.defaultSelectVal);
    }
  } else {
    // findLabel();
    filterMethodHandle("");
  }
};

// tags删除后回调
const removeTag = (tag: any) => {
  const row = state.tableData.find((item: { [x: string]: any }) => item[props.keywords.label] === tag);
  const indexRow = state.selectedRowKeys.indexOf(row[props.keywords.value]);
  indexRow === -1 ? state.selectedRowKeys.push(row[props.keywords.value]) : state.selectedRowKeys.splice(indexRow, 1);
  if (indexRow === -1) {
    state.selectedRows.push(row);
  } else {
    state.selectedRows.splice(indexRow, 1);
  }
  onSelectChange(state.selectedRowKeys, state.selectedRows);
};
// 多选清空
const onSelectNone = () => {
  nextTick(() => {
    state.selectedRowKeys = [];
    state.selectedRows = [];
    childSelectedValue.value = [];
  });
};
// 清空后的回调
const clear = () => {
  if (props.mode === "multiple") {
    onSelectNone();
  } else {
    state.selectedRowKeys = [];
    state.selectedRows = [];
  }
};
// 触发select隐藏
const blur = () => {
  selectRef.value.blur();
};
// 触发select显示
const focus = () => {
  selectRef.value.focus();
};
// 打开select下拉框
const openSelectDropdown = () => {
  open.value = true;
};
// 暴露方法出去
defineExpose({ focus, blur, openSelectDropdown });
script>

<style lang="scss">
.t_antd_select_dropdown {
  // 单选样式
  .radioStyle {
    .a-radio {
      .a-radio__label {
        display: none;
      }

      &:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner {
        box-shadow: none;
      }
    }
    .ant-table-row {
      cursor: pointer;
    }
  }
  .t-table-select__table {
    padding: 10px;

    .ant-table-body,
    .ant-table-header {
      margin: 0;
    }
    .ant-table-body {
      .ant-table-tbody {
        .ant-table-row {
          cursor: pointer;
        }
      }
    }
  }
  .ant-pagination {
    flex-wrap: nowrap;
  }
}
style>

七、在线预览目录

vue3 基于Ant DesignVue Select Table封装下拉选择表格组件(2023-08-25 TAntdSelectTable组件组件新增上下键盘高亮选择回车选中功能)_第2张图片

源码地址

gitHub组件地址

gitee码云组件地址

在线预览地址

相关文章

基于ElementUi或AntdUI再次封装基础组件文档


基于Element-plus再次封装基础组件文档(vue3+ts)

你可能感兴趣的:(vite+vue3项目搭建,vue.js,Vue3,typescript,anti-design-vue,Select,Table,Ant,Design,Vue)