ElementPlus Combogrid 组件

news2024/11/24 9:17:26

效果图: 

1.声明 Props类型

export type comboGridPropType = {

  modelValue: any; 

  url: string;

  keyField?: string;

  labelField?: string;

  filterOptions?: Array<ISearchOption>;

  tableColumns?: Array<TableColumns>;

  enableField?: string;

  multiple?: boolean;

  width?: number | string;

  panelWidth?: number;

  defaultPageSize?: number;

  autoSelectFirst?: boolean; //自动选择第一项

  sort?: string;

  order?: "desc" | "asc";

  queryOp: "Contains" | "BeginsWith" | "Equal";

  disabled?: boolean; //禁用

  searchable?: boolean; // 搜索

  isTreeData?: boolean; // 树结构数据

  treeProps?: { hasChildren?: string; children?: string };

};

2.后端数据结构

后端返回的数据结构:

using System;
using System.Collections.Generic;
using System.Text;

namespace WebiMes.Models.ViewModels
{
   /// <summary>
   /// 翻页返回集合
   /// </summary>
   public sealed class PagenationResult
   {
      public PagenationResult()
      {

      }

      /// <summary>
      /// 当前页
      /// </summary>
      public int pageIndex { get; set; }

      /// <summary>
      /// 每页显示
      /// </summary>
      public int pageSize { get; set; }

      /// <summary>
      /// 页码token(弃用)
      /// </summary>
      public string PageNationToken { get; set; }

      /// <summary>
      /// 数据集
      /// </summary>
      public IEnumerable<Object> List { get; set; }

      /// <summary>
      /// 行数
      /// </summary>
      public long Total { get; set; }

      /// <summary>
      /// 额外数据
      /// </summary>
      public Object? ExtenalData { get; set; }
   }
}

3.注意事项

1.如果不指定 keyfield和labelfield,默认将返回数据的第一列作为 keyfield和labelfield

2.filterOptions搜索字段类型 

id: number;
label: string;
value: string;
type: "string" | "boolean" | "date" | "number";

3.tableColumns表格列数据

{
    /** 是否隐藏 */
    hide?: boolean | CallableFunction;
    /** 自定义列的内容插槽 */
    slot?: string;
    /** 自定义表头的内容插槽 */
    headerSlot?: string;
    /** 多级表头,内部实现原理:嵌套 `el-table-column` */
    children?: Array<TableColumns>;
    /** 自定义单元格渲染器(`jsx`语法) */
    cellRenderer?: (data: TableColumnRenderer) => VNode;
    /** 自定义头部渲染器(`jsx`语法) */
    headerRenderer?: (data: TableColumnRenderer) => VNode;
    /** 显示的标题 */
    label?: string;
    /** 字段名称,对应列内容的字段名,也可以使用 `property` 属性 */
    prop?: string | ((index: number) => string);
    /** 对应列的类型,如果设置了 `selection` 则显示多选框;如果设置了 `index` 则显示该行的索引(从 `1` 开始计算);如果设置了 `expand` 则显示为一个可展开的按钮 */
    type?: TableColumnType;
    /** 如果设置了 `type=index`,可以通过传递 `index` 属性来自定义索引 */
    index?: number | ((index: number) => number);
    /** `column` 的 `key`, 如果需要使用 `filter-change` 事件,则需要此属性标识是哪个 `column` 的筛选条件 */
    columnKey?: string;
    /** 对应列的宽度 */
    width?: string | number;
    /** 对应列的最小宽度,对应列的最小宽度,与 `width` 的区别是 `width` 是固定的,`min-width` 会把剩余宽度按比例分配给设置了 `min-width` 的列 */
    minWidth?: string | number;
    /** 列是否固定在左侧或者右侧。`true` 表示固定在左侧 */
    fixed?: TableColumnFixed;
    /** 列标题 `Label` 区域渲染使用的 `Function` */
    renderHeader?: (data: RH) => VNode;
    /** 对应列是否可以排序, 如果设置为 `'custom'`,则代表用户希望远程排序,需要监听 `Table` 的 `sort-change `事件,默认值为 `false` */
    sortable?: TableColumnSortable;
    /** 指定数据按照哪个属性进行排序,仅当 `sortable` 设置为 `true` 的时候有效。应该如同 `Array.sort` 那样返回一个 `Number` */
    sortMethod?: (a: any, b: any) => number;
    /** 指定数据按照哪个属性进行排序,仅当 `sortable` 设置为 `true` 且没有设置 `sort-method` 的时候有效。如果 `sort-by` 为数组,则先按照第 `1` 个属性排序,如果第 `1` 个相等,再按照第 `2` 个排序,以此类推 */
    sortBy?: string | ((row: any, index: number) => string) | string[];
    /** 数据在排序时所使用排序策略的轮转顺序,仅当 `sortable` 为 `true` 时有效。需传入一个数组,随着用户点击表头,该列依次按照数组中元素的顺序进行排序,默认值为 `['ascending', 'descending', null]` */
    sortOrders?: Array<TableColumnSortOrders>;
    /** 对应列是否可以通过拖动改变宽度(需要在 `el-table` 上设置 `border` 属性为真),默认值为 `true`  */
    resizable?: boolean;
    /** 用来格式化内容 */
    formatter?: (row: any, column: TableColumnCtx<any>, cellValue: any, index: number) => VNode | string;
    /** 当内容过长被隐藏时显示 `tooltip`,默认值为 `false` */
    showOverflowTooltip?: boolean;
    /** 对齐方式,默认值为 `left` */
    align?: Align;
    /** 表头对齐方式,若不设置该项,则使用表格的对齐方式 */
    headerAlign?: Align;
    /** 列的 `className` */
    className?: string;
    /** 当前列标题的自定义类名 */
    labelClassName?: string;
    /** 仅对 `type=selection` 的列有效,类型为 `Function`,`Function` 的返回值用来决定这一行的 `CheckBox` 是否可以勾选 */
    selectable?: (row: any, index: number) => boolean;
    /** 仅对 `type=selection` 的列有效,请注意,需指定 `row-key` 来让这个功能生效,默认值为 `false` */
    reserveSelection?: boolean;
    /** 数据过滤的选项,数组格式,数组中的元素需要有 `text` 和 `value` 属性。数组中的每个元素都需要有 `text` 和 `value` 属性 */
    filters?: Array<{
        text: string;
        value: string;
    }>;
    /** 过滤弹出框的定位 */
    filterPlacement?: TableColumnFilterPlacement;
    /** 数据过滤的选项是否多选,默认值为 `true` */
    filterMultiple?: boolean;
    /** 数据过滤使用的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 `true` 就会显示 */
    filterMethod?: FilterMethods;
    /** 选中的数据过滤项,如果需要自定义表头过滤的渲染方式,可能会需要此属性 */
    filteredValue?: Array<any>;
}

4.完整代码

 combogrid.vue

<script setup lang="ts">
import { ref, onMounted, watch, toRefs } from "vue";
import iSearch from "@iconify-icons/ep/search";
import dayjs from "dayjs";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import _ from "lodash";
import { http } from "@/utils/http";
import {
  IResultTable,
  IFilterRule,
  FilterOp,
  FilterValueType
} from "@/api/baseTypes";
import { message } from "@/utils/message";
import { comboGridPropType, ISearchOption } from "./types";
// import { useUserStoreHook } from "@/store/modules/user";

defineOptions({
  name: "ComboGrid"
});
// statting in Vue 3.4
// const modelValue = defineModel<any>({ default: "" });
/** 配置默认值 */
const props = withDefaults(defineProps<comboGridPropType>(), {
  keyField: "", //主键Id
  labelField: "", //主键Id
  tableColumns: () => [],
  filterOptions: () => [],
  defaultPageSize: 10, //默认每页展示数量
  multiple: false, //多选
  width: 180, //宽度
  panelWidth: 700, //高度
  autoSelectFirst: false, //自动选中第一条
  sort: "",
  order: "asc",
  queryOp: "Contains",
  disabled: false,
  searchable: true,
  isTreeData: false,
  treeProps: () => ({ hasChildren: "hasChildren", children: "children" })
});
/** emits */
const emits = defineEmits<{
  (e: "update:modelValue", value: any): void;
  (e: "change", value: any): void;
}>();
/** 已初始化 */
const inited = ref(false);
//解构出来 ref
const {
  url, //搜索数据链接
  keyField: _keyField, //主键Id
  labelField: _labelField, //选中展示Label
  enableField,
  tableColumns,
  defaultPageSize, //默认每页展示数量
  filterOptions: _filterOptions, //搜索字段 数据集
  queryOp,
  disabled
} = toRefs(props);

const keyField = ref(""); //主键Id2
keyField.value = _keyField.value;
const labelField = ref(""); //选中展示Label
labelField.value = _labelField.value;
const filterOptions = ref<ISearchOption[]>([]); //搜索字段 数据集
filterOptions.value = _filterOptions.value;

/**初始化字段 */
const isInitColumn = ref(false);
isInitColumn.value = tableColumns.value?.length <= 0;
if (!tableColumns.value?.some(x => x.type === "index")) {
  tableColumns.value.splice(0, 0, {
    type: "index",
    label: "#",
    align: "center",
    index: (index: number) => {
      return (currentPage.value - 1) * defaultPageSize.value + index + 1;
    }
  });
}

const loadingList = ref(false); //加载数据列表
const tableData = ref([]); //表格数据
const selectRef = ref(); //选择框Ref
const tabRef = ref(); //数据展示Ref
const fieldSelectRef = ref(); //筛选字段Ref
//搜索字段
const filterField = ref<ISearchOption>();
/**初始化搜索字段 */
const isInitfilterOptions = ref(false);
//搜索字段 数组
if (_.isArray(filterOptions.value) && filterOptions.value.length > 0) {
  filterField.value = filterOptions.value[0];
} else isInitfilterOptions.value = true;
// 当前账户
// const useUserStore = useUserStoreHook();
// 已选数据的Label
const selectLabel = ref<string[]>([]);
// 搜索值
const searchQuery = ref();
/** 搜索 */
function onSearch() {
  let searchKey;
  let op: FilterOp = FilterOp.BeginsWith;
  if (queryOp.value === "Contains" || queryOp.value === "Equal") {
    const FilterOpStr: keyof typeof FilterOp = queryOp.value;
    op = FilterOp[FilterOpStr];
  }
  let valType: FilterValueType = FilterValueType.String;
  if (filterField.value?.type === "boolean") {
    const lowerVal = searchQuery.value.toLowerCase();
    if (lowerVal === "true") searchKey = true;
    if (lowerVal === "false") searchKey = false;
    if (_.isNumber(lowerVal)) searchKey = Number(lowerVal) > 0;
    if (_.isEmpty(searchKey)) {
      message("搜索数据类型不匹配" + filterField.value.type, {
        type: "warning",
        showClose: true,
        duration: 3000
      });
      return false;
    }
    op = FilterOp.Equal;
    valType = FilterValueType.Boolean;
  } else if (filterField.value?.type === "date") {
    const dateVal = new Date(searchQuery.value);
    if (!_.isDate(dateVal)) {
      message("搜索数据类型不匹配" + filterField.value.type, {
        type: "warning",
        showClose: true,
        duration: 3000
      });
      return false;
    } else {
      searchKey = dateVal;
      op = FilterOp.GreaterThanOrEqual;
      valType = FilterValueType.Datetime;
    }
  } else {
    searchKey = searchQuery.value;
  }
  if (_searhFilters.length > 1)
    _searhFilters.splice(1, _searhFilters.length - 1);

  if (_.isEmpty(searchKey) || _.isEmpty(filterField.value)) {
    // message(`搜索 类型或值,为空,将搜索所有数据`, {
    //   type: "warning",
    //   showClose: true,
    //   duration: 3000
    // });
    const sfilter = _searhFilters.find(
      x => x.field == filterField.value?.value
    );
    if (_.isEmpty(sfilter)) {
      _searhFilters.push({
        field: filterField.value?.value, // 字段
        op: op, // 比较符号
        value: searchKey, // 值
        valType: valType // 值类型
      });
    } else {
      _searhFilters[0].op = op;
      _searhFilters[0].valType = valType;
      _searhFilters[0].value = "";
    }
  } else {
    const sfilter = _searhFilters.find(x => x.field == filterField.value.value);
    if (_.isEmpty(sfilter)) {
      _searhFilters.push({
        field: filterField.value.value, // 字段
        op: op, // 比较符号
        value: searchKey, // 值
        valType: valType // 值类型
      });
    } else {
      _searhFilters[0].op = op;
      _searhFilters[0].valType = valType;
      _searhFilters[0].value = searchKey;
    }
  }
  currentPage.value = 1;
  getTabelData();
}
/** 选中的 行 */
const selectedRows = ref([]);
/** 更新v-Model */
function updateVModel() {
  if (!props.multiple) {
    if (!_.isEmpty(selectedRows.value)) {
      const newValue = selectedRows.value[0][keyField.value];
      emits("update:modelValue", newValue);
      emits("change", selectedRows.value[0]);
    } else {
      emits("update:modelValue", null);
      emits("change", null);
    }
  } else {
    const newValues = selectedRows.value.map(x => x[keyField.value]);
    emits("update:modelValue", newValues);
    emits("change", selectedRows.value);
  }
}
/** 行选中 */
function handleRowClick(currentRow, _column, _event) {
  if (!_.isEmpty(currentRow)) {
    if (
      !_.isEmpty(enableField.value) &&
      !_.isEmpty(currentRow.value) &&
      _.isBoolean(currentRow[enableField.value]) &&
      !currentRow[enableField.value]
    ) {
      message("没有权限选择本条数据", { type: "warning" });
      return false;
    }
    let removeIndex = -1;
    let some = false;
    selectedRows.value.forEach((x, index) => {
      if (x[keyField.value] === currentRow[keyField.value]) {
        some = true;
        removeIndex = index;
        return false;
      }
    });
    const _label = currentRow[labelField.value];
    if (!props.multiple && selectedRows.value.length > 0) {
      //清空
      selectedRows.value.splice(0, selectedRows.value.length);
      selectLabel.value.splice(0, selectLabel.value.length);
    }
    if (!some) {
      selectedRows.value.push(currentRow);
    } else {
      if (removeIndex >= 0) selectedRows.value.splice(removeIndex, 1);
    }
    selectLabel.value.forEach((x, index) => {
      if (x === _label) {
        some = true;
        removeIndex = index;
        return false;
      }
    });
    if (!some) {
      selectLabel.value.push(_label);
    } else {
      if (removeIndex >= 0) selectLabel.value.splice(removeIndex, 1);
    }
    /** 更新v-Model */
    updateVModel();
    //单选自动关闭
    if (!props.multiple) selectRef.value.blur();
    set_ElSelectTagsText();
  }
}
/** 暂存搜索数据 */
const _searhFilters: IFilterRule[] = [];
if (!_.isEmpty(filterField.value?.value)) {
  _searhFilters.push({
    field: filterField.value?.value, // 字段
    op: FilterOp.Equal, // 比较符号
    value: "", // 值
    valType: FilterValueType.String // 值类型
  });
}
/**翻页配置 */
const currentPage = ref(1);
// const defaultPageSize = ref(10);
const totalRecord = ref(0);
/** 翻页 */
async function handlePageChange(_currentPage) {
  currentPage.value = _currentPage;
  await getTabelData();
}
/** 获取数据信息 */
async function getTabelData(init = false) {
  try {
    loadingList.value = true;
    // // 数据为空时,强制设定头
    // if (!init) init = !_.isEmpty(tableData.value);
    let res: IResultTable;
    if (!_.isEmpty(url.value)) {
      res = await http.request<IResultTable>("get", url.value, {
        params: {
          searhFilters: JSON.stringify(
            _searhFilters.filter(x => !_.isEmpty(x))
          ),
          page: currentPage.value,
          limit: defaultPageSize.value,
          sort: props.sort,
          order: props.order
        }
      });
    }
    if (res && res.isSuccess) {
      tableData.value = res.data.list;
      totalRecord.value = res.data.total;
      if (init || !inited.value) {
        if (!_.isEmpty(tableData.value)) {
          const top = tableData.value[0];
          const arrEntry = Object.entries(top);
          let i = 0;
          // tableColumns没有配置的字段,新增进去
          for (const [key, val] of arrEntry) {
            i++;
            if (i == 1) {
              // 配置值字段,和显示字段
              if (_.isEmpty(keyField.value)) {
                keyField.value = _.cloneDeep(key);
              }
              if (_.isEmpty(labelField.value)) {
                labelField.value = keyField.value;
              }
            }
            if (isInitColumn.value) {
              if (!tableColumns.value.some(x => x.prop == key))
                tableColumns.value.push({ label: key, prop: key });
            }
            if (isInitfilterOptions.value) {
              if (!filterOptions.value?.some(x => x.label == key)) {
                let _type: "date" | "boolean" | "string" = "string";
                const dateObj = dayjs(val?.toString());
                if (dateObj.isValid() && dateObj > dayjs("2020-01-01")) {
                  _type = "date";
                }
                if (_.isBoolean(val)) {
                  _type = "boolean";
                }
                const filterOption: ISearchOption = {
                  id: i,
                  label: key,
                  value: key,
                  type: _type
                };
                filterOptions.value.push(filterOption);
              }
            }
          }
          //选中第一行
          if (props.autoSelectFirst && _.isEmpty(props.modelValue)) {
            const firstRow = tableData.value[0];
            selectLabel.value.push(firstRow[labelField.value]);
            selectedRows.value.push(firstRow);
          } else {
            if (!_.isEmpty(props.modelValue)) {
              // 首次加载选中项
              if (!props.multiple) {
                // if (!_.isArray(props.modelValue)) {
                //   const seltRows = tableData.value.filter(
                //     x => x[keyField.value] == props.modelValue
                //   );
                //   if (!_.isEmpty(seltRows)) selectedRows.value = [seltRows[0]];
                // }
              } else {
                selectedRows.value = tableData.value.filter(x =>
                  props.modelValue.some(n => n == x[keyField.value])
                );
              }
              selectedRows.value.forEach(item => {
                selectLabel.value.push(item[labelField.value]);
              });
            }
          }
          // 已初始化
          inited.value = true;
        }
      } else {
        //清空
        _searhFilters.splice(0, _searhFilters.length);
      }
    } else {
      if (!_.isEmpty(url.value))
        message(`获取数据出错:${res.errMessage}`, { type: "error" });
    }
  } catch (error) {
    message(`获取数据出错:${error.message}`, { type: "error" });
  } finally {
    loadingList.value = false;
  }
}
/**行class */
const tableRowClassName = ({
  row,
  _rowIndex
}: {
  row: any;
  _rowIndex: number;
}) => {
  if (selectedRows.value.some(x => x[keyField.value] === row[keyField.value])) {
    return "success-row";
  }
  if (_.isBoolean(row?._Enable) && !row._Enable) return "warning-row";
  return "";
};
/** 设置el-select_Tags-text Title */
function set_ElSelectTagsText() {
  if (!_.isEmpty(selectedRows.value)) {
    const maxTimes = 100;
    let timeNUm = 0;
    const _$el = selectRef.value.$el;
    const len = selectedRows.value.length;
    const interval = setInterval(() => {
      timeNUm++;
      if (timeNUm > maxTimes) {
        clearInterval(interval);
      }
      const elems = _$el.querySelectorAll("span.el-select__tags-text");
      if (elems.length === len) {
        console.log(
          "selectRef",
          selectRef.value,
          _$el.querySelectorAll("span.el-select__tags-text")
        );
        elems.forEach(elem => {
          if (elem.innerText.indexOf("+ ") < 0) {
            elem.setAttribute("title", elem.innerText);
          }
        });
        clearInterval(interval);
      }
    }, 300);
  }
}
/**设置主Select-panel不关闭 */
function visibleChange(_visible) {
  if (!_visible) {
    selectRef.value.visible = true;
    selectRef.value.expanded = true;
    // selectRef.value.focus();
    // setTimeout(() => {
    // }, 100);
    console.log("_visible, selectRef.value");
  }
  console.log(_visible, selectRef.value);
}
/**设置主Select-panel不关闭 */
function mainVisibleChange(_visible) {
  selectRef.value.visible = !_visible;
  console.log(selectRef.value);
}
/** 监听 选中数据的修改 */
watch(selectLabel, (newval, oldval) => {
  if (newval.length > 0) {
    //清除未选择的数据
    oldval.forEach((x, removeIndex) => {
      if (!newval.some(n => n === x)) {
        selectedRows.value.splice(removeIndex, 1);
      }
    });
  } else {
    //清空选择的数据
    selectedRows.value.splice(0, selectedRows.value.length);
  }
  /** 更新v-Model */
  updateVModel();
  // if (!props.multiple) {
  //   if (!_.isEmpty(selectedRows.value)) {
  //     const newValue = selectedRows.value[0][keyField.value];
  //     emits("update:modelValue", newValue);
  //     emits("change", selectedRows.value[0]);
  //   } else {
  //     emits("update:modelValue", null);
  //     emits("change", null);
  //   }
  // } else {
  //   const newValues = selectedRows.value.map(x => x[keyField.value]);
  //   emits("update:modelValue", newValues);
  //   emits("change", selectedRows.value);
  // }
});
watch(
  () => props.modelValue,
  newval => {
    console.log("watch-props", newval, typeof newval);
    /** _.isEmpty 如果是int boolean,将返回true */
    if (
      (_.isNumber(newval) && (_.isNaN(newval) || newval <= 0)) ||
      (!_.isNumber(newval) && _.isEmpty(newval))
    ) {
      selectLabel.value = [];
    }
  }
);
watch(url, (newval, oldval) => {
  //清空
  _searhFilters.splice(0, _searhFilters.length);
  if (_.isEmpty(newval)) {
    tableData.value = [];
    emits("update:modelValue", null);
    // modelValue.value = null;
    emits("change", null);
  } else if (newval != oldval) {
    tableData.value = [];
    emits("update:modelValue", null);
    // modelValue.value = null;
    emits("change", null);
    //链接修改重新初始化
    getTabelData(true).then(() => {
      set_ElSelectTagsText();
    });
  }
});
onMounted(async () => {
  await getTabelData(true);
  if (!_.isEmpty(props.modelValue)) {
    let filter = _searhFilters.find(x => x.field == keyField.value);
    let isNew = false;
    if (_.isEmpty(filter)) {
      isNew = true;
      filter = {
        field: keyField.value, // 字段
        op: props.multiple ? FilterOp.In : FilterOp.BeginsWith, // 比较符号
        value: "", // 值
        valType: FilterValueType.String // 值类型
      };
    }
    let filterValStr = props.modelValue;
    if (_.isArray(props.modelValue)) filterValStr = props.modelValue.join(",");
    Object.assign(filter, {
      field: keyField.value, // 字段
      op: props.multiple ? FilterOp.In : FilterOp.BeginsWith, // 比较符号
      value: filterValStr, // 值
      valType: FilterValueType.String // 值类型
    });
    if (isNew) _searhFilters.push(filter);
    // 没有filter 无需重新搜索
    if (!_.isEmpty(_searhFilters)) {
      // getTabelData().then(() => {
      //   if (_.isEmpty(selectedRows.value)) {
      //     const seletData = tableData.value.find(
      //       x => x[keyField.value] == filterValStr
      //     );
      //     if (!_.isEmpty(seletData)) {
      //       selectedRows.value.push(seletData);
      //       selectLabel.value.push(seletData[labelField.value]);
      //     }
      //   }
      //   set_ElSelectTagsText();
      // });
      await getTabelData();
      if (_.isEmpty(selectedRows.value)) {
        const seletData = tableData.value.find(
          x => x[keyField.value] == filterValStr
        );
        if (!_.isEmpty(seletData)) {
          selectedRows.value.push(seletData);
          selectLabel.value.push(seletData[labelField.value]);
          emits("change", selectedRows.value[0]);
        }
      }
    }
  }
  console.log("ComboGrid - onMounted", keyField, labelField);
  set_ElSelectTagsText();
});
</script>
<template>
  <el-select
    ref="selectRef"
    clearable
    collapse-tags
    collapse-tags-tooltip
    placeholder="Please select Data"
    value-key="_Id"
    v-model="selectLabel"
    :multiple="true"
    :loading="loadingList"
    :style="{
      width: _.isNumber(props.width || 180)
        ? parseInt(props.width || 180) > 1000
          ? '100%'
          : `${props.width || 180}px`
        : props.width
    }"
    @visible-change="mainVisibleChange"
    :disabled="disabled"
    class="combo-grid"
  >
    <template #empty>
      <div :class="`w-[${props.panelWidth}px] m-4`">
        <el-card height="500px">
          <template #header v-if="props.searchable">
            <el-row :gutter="1">
              <el-col :span="9" :sm="7" :md="8" :lg="9" :xl="10">
                <el-select
                  ref="fieldSelectRef"
                  filterable
                  v-model="filterField"
                  placeholder="Select"
                  style="width: 100%"
                  value-key="id"
                  @visibleChange="visibleChange"
                >
                  <el-option
                    v-for="item in filterOptions"
                    :key="item.id"
                    :label="item.label"
                    :value="item"
                  />
                </el-select>
              </el-col>
              <el-col :span="15" :sm="17" :md="16" :lg="15" :xl="14">
                <el-input
                  v-model="searchQuery"
                  clearable
                  @keyup.enter="onSearch"
                >
                  <template #append>
                    <el-button
                      type="primary"
                      :icon="useRenderIcon(iSearch)"
                      :loading="loadingList"
                      v-auth="'view'"
                      @click="onSearch"
                    />
                  </template>
                </el-input>
              </el-col>
            </el-row>
          </template>
          <el-row>
            <el-col :span="24">
              <pure-table
                ref="tabRef"
                style="width: 100%"
                size="small"
                border
                fit
                show-overflow-tooltip
                :max-height="350"
                :data="tableData"
                :row-key="keyField"
                :columns="tableColumns"
                :loading="loadingList"
                :row-class-name="tableRowClassName"
                @row-click="handleRowClick"
                :tree-props="(props.isTreeData ? props.treeProps : {}) as any"
                :default-expand-all="props.isTreeData"
              />
              <el-pagination
                v-if="!props.isTreeData"
                size="small"
                layout="prev, pager, next, jumper"
                :default-page-size="defaultPageSize"
                :total="totalRecord"
                @current-change="handlePageChange"
              />
            </el-col>
          </el-row>
        </el-card>
      </div>
    </template>
  </el-select>
</template>
<style lang="scss" scoped>
:deep(.el-card) {
  --el-card-padding: 6px;
}
:deep(.el-table__row) {
  cursor: pointer;
}
</style>
<style>
.el-table .warning-row {
  --el-table-tr-bg-color: var(--el-color-warning-light-9);
}
.el-table .success-row {
  --el-table-tr-bg-color: var(--el-color-success-light-9);
}
</style>

types.ts

import { TableColumns } from "@pureadmin/table";

export type comboGridPropType = {
  modelValue: any;
  url: string;
  keyField?: string;
  labelField?: string;
  filterOptions?: Array<ISearchOption>;
  tableColumns?: Array<TableColumns>;
  enableField?: string;
  multiple?: boolean;
  width?: number | string;
  panelWidth?: number;
  defaultPageSize?: number;
  autoSelectFirst?: boolean; //自动选择第一项
  sort?: string;
  order?: "desc" | "asc";
  queryOp: "Contains" | "BeginsWith" | "Equal";
  disabled?: boolean; //禁用
  searchable?: boolean; // 搜索
  isTreeData?: boolean; // 树结构数据
  treeProps?: { hasChildren?: string; children?: string };
};

export interface ISearchOption {
  id: number;
  label: string;
  value: string;
  type: "string" | "boolean" | "date" | "number";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1870322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】深度概率模型(DPM)原理和文本分类实践

1.引言 1.1.DPM模型简介 深度概率模型&#xff08;Deep Probabilistic Models&#xff09; 是结合了深度学习和概率论的一类模型。这类模型通过使用深度学习架构&#xff08;如神经网络&#xff09;来构建复杂的概率分布&#xff0c;从而能够处理不确定性并进行预测。深度概率…

1.1章节print输出函数语法八种 使用和示例

1.打印变量和字符串 2-4.三种使用字符串格式化 5.输出ASCLL码的值和中文字符 6.打印到文件或其他对象&#xff08;而不是控制台&#xff09; 7.自定义分隔符、和换行符和结束符 8.连接符加号连接字符串 在Python中&#xff0c;print() 函数用于在控制台上输出信息。这是一个非常…

【Proteus仿真】基于stm32的数码管时钟

【Proteus仿真】基于stm32的数码管时钟 Proteus仿真&#xff01;基于stm32的数码管时钟~_哔哩哔哩_bilibili ‍ 01原理图 ​​ 02功能描述 1.通过按键修改时间 2.数码管显示实时时间&#xff0c;时-分-秒-毫秒格式 03获取方式 https://docs.qq.com/sheet/DTExIc2dPUUJ…

Rust 跨平台-Android 和鸿蒙 OS

1. 安装 rustup rustup 是 Rust 的安装和版本管理工具 $ curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh 该命令会安装 rusup 和最新的稳定版本的 Rust&#xff1b;包括&#xff1a; rustc Rust 编译器&#xff0c;用于将 Rust 代码编译成可执行文件或库。 ca…

深入理解SSH:网络安全的守护者

在当今数字化时代&#xff0c;网络安全已成为全球关注的焦点。随着网络攻击手段的不断升级&#xff0c;保护数据传输的安全性变得尤为重要。SSH&#xff08;Secure Shell&#xff09;作为一种安全的网络协议&#xff0c;为远程登录和网络服务提供了强大的安全保障&#xff0c;成…

【数据结构】(C语言):动态数组

动态数组&#xff1a; 内存区域连续&#xff0c;即每个元素的内存地址连续。可用索引查看元素&#xff0c;数组[索引号]。指定位置删除元素&#xff0c;该位置之后的元素全部往前移动一位。指定位置添加元素&#xff0c;从最后到该位置的元素全部往后移动一位。物理大小&#…

自费5K,测评安德迈、小米、希喂三款宠物空气净化器谁才是高性价比之王

最近&#xff0c;家里的猫咪掉毛严重&#xff0c;简直成了一个活生生的蒲公英&#xff0c;家中、空气中各处都弥漫着猫浮毛甚至所有衣物都覆盖着一层厚厚的猫毛。令人难以置信的是&#xff0c;有时我甚至在抠出的眼屎中都能发现夹杂着几根猫毛。真的超级困扰了。但其实最空气中…

句法分析概述

第1关&#xff1a;句法分析概述 任务描述 本关任务&#xff1a;通过对句法分析基本概念的学习&#xff0c;完成相应的选择题。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 句法分析的基础概念&#xff1b; 句法分析的数据集和评测方法。 句法分析简介…

ubuntu如何切换到root用户

1、主要指令&#xff1a; sudo -i su root 2、示例 3、其他说明 在Ubuntu&#xff08;以及大多数其他基于Linux的操作系统中&#xff09;&#xff0c;切换到root用户通常意味着获得了对系统的完全访问权限。这种权限允许执行以下操作&#xff08;但不限于这些&#xff09;…

深度学习论文: Surge Phenomenon in Optimal Learning Rate and Batch Size Scaling

深度学习论文: Surge Phenomenon in Optimal Learning Rate and Batch Size Scaling Surge Phenomenon in Optimal Learning Rate and Batch Size Scaling PDF:https://arxiv.org/pdf/2405.14578 PyTorch: https://github.com/shanglianlm0525/PyTorch-Networks 1 概述 本文研…

使用Python进行Socket接口测试

大家好&#xff0c;在现代软件开发中&#xff0c;网络通信是不可或缺的一部分。无论是传输数据、获取信息还是实现实时通讯&#xff0c;都离不开可靠的网络连接和有效的数据交换机制。而在网络编程的基础中&#xff0c;Socket&#xff08;套接字&#xff09;技术扮演了重要角色…

2024 6.17~6.23 周报

一、上周工作 吴恩达的机器学习、实验-回顾之前密集连接部分 二、本周计划 继续机器学习&#xff0c;同时思考实验如何修改&#xff0c;开始整理代码 三、完成情况 3.1 多类特征、多元线性回归的梯度下降、特征缩放、逻辑回归 多类特征&#xff1a; 多元线性回归的梯度下…

基于PHP的长城景区信息管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的长城景区信息管理系统 一 介绍 此长城景区信息管理系统基于原生PHP开发&#xff0c;数据库mysql。系统角色分为用户和管理员。 技术栈&#xff1a;phpmysqlphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 浏览长城景区信息(旅…

出手便是王炸,曙光存储将高端存储推向新高度

二十年磨一剑&#xff0c;今朝试锋芒。 近日&#xff0c;曙光存储重磅发布全球首个亿级IOPS集中式全闪存储FlashNexus&#xff0c;正式宣告进入高端存储市场。 作为存储产业皇冠上的明珠&#xff0c;高端存储一向以技术难度大、市场准入门槛高和竞争格局稳定著称&#xff0c;…

React的Props、生命周期

Props 的只读性 “Props” 是 React 中用于传递数据给组件的一种机制&#xff0c;通常作为组件的参数进行传递。在 React 中&#xff0c;props 是只读的&#xff0c;意味着一旦将数据传递给组件的 props&#xff0c;组件就不能直接修改这些 props 的值。所以组件无论是使用函数…

Studying-代码随想录训练营day22| 回溯理论基础、77.组合、216.组合总和II、17.电话号码的字母组合

第22天&#xff0c;回溯章节开始&#xff01;一大算法难点&#xff0c;加油加油&#xff01; 回溯理论基础组合问题的剪枝操作 文档讲解&#xff1a;代码随想录回溯理论基础 视频讲解&#xff1a;回溯理论基础 回溯法也叫回溯搜索法&#xff0c;它是一种搜索&#xff0c;遍历的…

数值稳定性、模型初始化和激活函数

一、数值稳定性&#xff1a;神经网络很深的时候数据非常容易不稳定 1、神经网络梯度 h^(t-1)是t-1层的输出&#xff0c;也就是t层的输入&#xff0c;y是需要优化的目标函数&#xff0c;向量关于向量的倒数是一个矩阵。 2、问题&#xff1a;梯度爆炸、梯度消失 &#xff08;1&…

leetcode-19-回溯

引自代码随想录 [77]组合 给定两个整数 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 个数的组合。 示例: 输入: n 4, k 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4]] 1、大致逻辑 k为树的深度&#xff0c;到叶子节点的路径即为一个结果 开始索引保证不重复…

当了面试官才知道:做好这3点,面试成功率至少提高50%

关于辉哥&#xff1a; 资深IT从业者&#xff0c; 曾就职于阿里、腾讯、美团、中信科等互联网公司和央企&#xff1b; 两岁小男孩的父亲。 不定期分享职场 | 婚姻 | 育儿 | 个人成长心得体会 关注我&#xff0c;一起学习和成长。 最近作为公司社招面…

一文入门CMake

我们前几篇文章已经入门了gcc和Makefile&#xff0c;现在可以来玩玩CMake了。 CMake和Makefile是差不多的&#xff0c;基本上是可以相互替换使用的。CMAke可以生成Makefile&#xff0c;所以本质上我们还是用的Makefile&#xff0c;只不过用了CMake就不用再写Makefile了&#x…