GeekerAdmin ProTable组件的更新之路🥂

1,271 阅读4分钟

GeekerAdmin是什么

GeekerAdmin 是一款基于Vite3、vue3、Element Plus、pinia打造的包含动态路由、状态管理、权限控制等功能为一体的后台管理系统模板。

更新了什么

在此次ProTable组件更新中,我们:

  • 重新设计了ColumnProps的类型定义
  • 重新设计了SeachFromItem组件
  • 现支持el-table所有属性和事件,包括多级表头
  • 搜索表单现拥有默认响应式布局,也支持手动设置
  • 全部支持自定义的表头和单元格

更新解读(抢先版)

ColumnProps类型定义

原来的ColumnProps定义属性比较分散,类型也相对松散。

export interface ColumnProps {
 type: TypeProp; // index | selection | expand(特殊类型)
 prop: string; // 单元格数据(非特殊类型必填)
 label: string; // 单元格标题(非特殊类型必填)
 width: number | string; // 列宽
 minWidth: number | string; // 最小列宽
 isShow: boolean; // 是否显示在表格当中
 sortable: boolean; // 是否可排序(静态排序)
 fixed: FixedProp; // 固定列
 align: AlignProp; // 表格列对齐方式(默认为 center)
 tag: boolean; // 是否是标签展示
 search: boolean; // 是否为搜索项
 searchType: SearchType; // 搜索项类型
 searchProps: { [key: string]: any }; // 搜索项参数,根据 element 文档来,标签自带属性 > props 属性
 searchInitParam: string | number | boolean | any[]; // 搜索项初始值
 enum: EnumProps[] | (() => Promise<any>); // 枚举类型(渲染值的字典)
 renderHeader: (params: any) => any; // 自定义表头
}

在新版本中,我们通过各种引入了Element表单组件(常见的input、select、datepicker、timepicker等)的属性和El-Table-Column的属性。这意味着ColumnProps现在包含了el-table-column的大部分属性。对于原来的search*现在全部移到了search里面,并且传递不同的渲染项会有不同的类型提示。

Search 类型

export type BreakPoint = "xs" | "sm" | "md" | "lg" | "xl";
export type Responsive = {
 span?: number;
 offset?: number;
};

type BaseSearch = {
 /*搜索字段的默认值*/
 defaultValue?: any;

 /*搜索表单用的数据名称,undefined时会使用column的prop属性*/
 key?: string;
 /*搜索字段的排序*/
 order?: number;
 /*搜索字段所占用的列数 默认为1*/
 span?: number;
 /*搜索字段左侧间隔列数*/
 offset?: number;
} & Partial<Record<BreakPoint, Responsive>>;

interface Input {
 /*input输入框*/
 el: "input";
  /*input输入框的props*/
 props?: Partial<InputProps>;
}

interface Select {
 el: "select";
 props?: Partial<typeof ElSelect.__defaults>;
}

interface DatePicker {
 el: "date-picker";
 props?: Partial<any>; // 这个属性太多了,放在这里占地方,可以看源代码
}

interface TimePicker {
 el: "time-picker";
 props?: Partial<TimePickerDefaultProps>;
}

interface TimeSelect {
 el: "time-select";
 props?: Partial<TimeSelectProps>;
}

interface Switch {
 el: "switch";
 props?: Partial<SwitchProps>;
}

interface Slider {
 el: "slider";
 props?: Partial<SliderProps>;
}

interface TreeSelect {
 el: "tree-select";
 props?: Partial<TreeProps | typeof SelectProps>;
}
// 支持 input,select,detapicker,timepicker,timeselect,switch,slider,treeselect
type Search = BaseSearch & (Input | Select | DatePicker | TimePicker | TimeSelect | Switch | Slider | TreeSelect);

ColumnProps 类型

export interface ColumnProps<T = any>
 extends Partial<Omit<TableColumnCtx<T>, "order" | "children" | "renderHeader" | "renderCell">> {
 // index | selection | expand(特殊类型)
 type?: TypeProp;
 // 是否显示在表格当中
 isShow?: boolean;
 // 是否是标签展示
 tag?: boolean;
 /* 多级表头 */
 _children?: ColumnProps<T>[];
 /* 搜索菜单配置 */
 search?: Search;
 // 枚举类型(渲染值的字典)
 enum?: EnumProps[] | (() => Promise<any>);
 /*自定义字段名,针对于后端返回的enum不是 {label:string,value:string}的情况*/
 fieldNames?: { label?: string; value?: string };
 // 自定义表头
 headerRender?: (data: T) => any;
 /*自定义单元格*/
 render?: (record?: T) => any;
}

SearchFormItem 组件

原来的SearchFormItem组件中,渲染input、select等是使用v-if来渲染,支持的表单项不多,并且不够灵活:

<!-- 旧的 -->
<template>
 <!-- 文本框 -->
 <template v-if="item.searchType == undefined || item.searchType == 'text'">
  <el-input
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   placeholder="请输入"
   :clearable="clearable(item)"
  ></el-input>
 </template>
 <!-- 下拉选择框 -->
 <template v-if="item.searchType == 'select' || item.searchType == 'multipleSelect'">
  <el-select
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   :multiple="item.searchType == 'multipleSelect'"
   placeholder="请选择"
   :clearable="clearable(item)"
  >
   <el-option
    v-for="itemValue in item.enum"
    :key="itemValue[item.searchProps?.value] ?? itemValue.value"
    :label="itemValue[item.searchProps?.label] ?? itemValue.label"
    :value="itemValue[item.searchProps?.value] ?? itemValue.value"
    :disabled="itemValue.disabled"
   />
  </el-select>
 </template>
 <!-- 下拉树形选择框 -->
 <template v-if="item.searchType == 'treeSelect' || item.searchType == 'multipleTreeSelect'">
  <el-tree-select
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   :multiple="item.searchType == 'multipleTreeSelect'"
   :data="item.enum"
  />
 </template>
 <!-- 日期选择 -->
 <template v-if="item.searchType == 'date'">
  <el-date-picker
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   value-format="YYYY-MM-DD"
   type="date"
   placeholder="请选择日期"
   :clearable="clearable(item)"
  />
 </template>
 <!-- 时间范围选择 -->
 <template v-if="item.searchType == 'timerange'">
  <el-time-picker
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   is-range
   value-format="HH:mm:ss"
   range-separator="至"
   start-placeholder="开始时间"
   end-placeholder="结束时间"
   :clearable="clearable(item)"
  />
 </template>
 <!-- 日期范围选择 -->
 <template v-if="item.searchType == 'daterange'">
  <el-date-picker
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   type="daterange"
   value-format="YYYY-MM-DD"
   range-separator="至"
   start-placeholder="开始时间"
   end-placeholder="结束时间"
   :clearable="clearable(item)"
  />
 </template>
 <!-- 日期时间范围选择 -->
 <template v-if="item.searchType == 'datetimerange'">
  <el-date-picker
   v-model="searchParam[item.prop!]"
   v-bind="item.searchProps"
   type="datetimerange"
   value-format="YYYY-MM-DD HH:mm:ss"
   range-separator="至"
   start-placeholder="开始时间"
   end-placeholder="结束时间"
   :clearable="clearable(item)"
  />
 </template>
</template>

现在的SearchFormItem组件全部通过vue3内置的component组件来渲染表单项,支持的搜索表单项更多也更灵活:

<!-- 新的 -->
<template>
 <component
  v-if="column.search?.el && column.prop"
  :is="`el-${column.search.el}`"
  v-model="searchParam[column.search.key ?? column.prop]"
  v-bind="column.search.props"
 >
  <template v-if="column.search.el === 'select'">
   <component
    :is="`el-option`"
    v-for="(col, index) in columnEnum"
    :key="index"
    :label="col[fieldNames().label]"
    :value="col[fieldNames().value]"
   ></component>
  </template>
 </component>
</template>

使用el-table属性和多级表头

通过vue的$attrs,可将绑定到ProTable上的属性(props和emits)直接透传至el-table元素上,具体属性可以参照Element Plus官网

在定义columns时,传入children_属性即可生成多级表头,像这样:

let columns = [
    {
     label: "基本信息",
     prop: "base",
     headerAlign: "center",
     _children: [
      {prop: "username",label: "用户姓名"},
      {prop: "gender",label: "性别"},
      {prop: "idCard",label: "身份证号"}
     ]
    }
]

image.png

自定义表头和单元格

在旧版本中,自定义表头只支持传入renderHeader方法,自定义单元格只支持slot插槽。

现在,经过我的不懈努力,表头支持headerRender(避免与El-Table-Column上的属性重名导致报错)方法和具名插槽(column.prop+'Header')两种方式自定义,单元格支持render方法和具名插槽(column上的prop属性)自定义。

多级表头也同样支持。本来只支持自定义函数,不支持slot的,但是经过我的不懈努力,两者都支持。

wangyous.webp

搜索表单响应式

在原来的搜索表单中,空间利用率不高,且排版不均匀,特别是在非全屏的情况下特别明显:

image.png

现在新的搜索表单项采用grid布局,搜索/重置操作永远放在最后,监听屏幕断点。

base.gif

你应该不需要去关心它怎么实现的,你只管用就行了

lzqz.webp

结语

感谢各位小伙伴对GeekerAdmin的支持与帮助!你对我们的支持就是我们最大的动力!

大家一直关心的文档已经在编写之中,此次更新大概率会随着文档一起发布。敬请期待!