Skip to content

Commit

Permalink
feat: 🚀 add proTable instance type
Browse files Browse the repository at this point in the history
  • Loading branch information
HalseySpicy committed May 18, 2023
1 parent f58291f commit 8262f04
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 71 deletions.
14 changes: 7 additions & 7 deletions src/components/ProTable/components/TableColumn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<script setup lang="tsx" name="TableColumn">
import { inject, ref, useSlots } from "vue";
import { ColumnProps } from "@/components/ProTable/interface";
import { ColumnProps, RenderScope, HeaderRenderScope } from "@/components/ProTable/interface";
import { filterEnum, formatValue, handleProp, handleRowAccordingToProp } from "@/utils";
defineProps<{ column: ColumnProps }>();
Expand All @@ -14,14 +14,14 @@ const slots = useSlots();
const enumMap = inject("enumMap", ref(new Map()));
// 渲染表格数据
const renderCellData = (item: ColumnProps, scope: { [key: string]: any }) => {
const renderCellData = (item: ColumnProps, scope: RenderScope<any>) => {
return enumMap.value.get(item.prop) && item.isFilterEnum
? filterEnum(handleRowAccordingToProp(scope.row, item.prop!), enumMap.value.get(item.prop)!, item.fieldNames)
: formatValue(handleRowAccordingToProp(scope.row, item.prop!));
};
// 获取 tag 类型
const getTagType = (item: ColumnProps, scope: { [key: string]: any }) => {
const getTagType = (item: ColumnProps, scope: RenderScope<any>) => {
return filterEnum(handleRowAccordingToProp(scope.row, item.prop!), enumMap.value.get(item.prop), item.fieldNames, "tag");
};
Expand All @@ -35,16 +35,16 @@ const RenderTableColumn = (item: ColumnProps) => {
showOverflowTooltip={item.showOverflowTooltip ?? item.prop !== "operation"}
>
{{
default: (scope: any) => {
default: (scope: RenderScope<any>) => {
if (item._children) return item._children.map(child => RenderTableColumn(child));
if (item.render) return item.render(scope);
if (slots[handleProp(item.prop!)]) return slots[handleProp(item.prop!)]!(scope);
if (item.tag) return <el-tag type={getTagType(item, scope)}>{renderCellData(item, scope)}</el-tag>;
return renderCellData(item, scope);
},
header: () => {
if (item.headerRender) return item.headerRender(item);
if (slots[`${handleProp(item.prop!)}Header`]) return slots[`${handleProp(item.prop!)}Header`]!({ row: item });
header: (scope: HeaderRenderScope<any>) => {
if (item.headerRender) return item.headerRender(scope);
if (slots[`${handleProp(item.prop!)}Header`]) return slots[`${handleProp(item.prop!)}Header`]!(scope);
return item.label;
}
}}
Expand Down
6 changes: 3 additions & 3 deletions src/components/ProTable/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ import ColSetting from "./components/ColSetting.vue";
import TableColumn from "./components/TableColumn.vue";
import printJS from "print-js";

interface ProTableProps {
export interface ProTableProps {
columns: ColumnProps[]; // 列配置项 ==> 必传
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传
Expand All @@ -119,8 +119,8 @@ interface ProTableProps {

// 接受父组件参数,配置默认值
const props = withDefaults(defineProps<ProTableProps>(), {
requestAuto: true,
columns: () => [],
requestAuto: true,
pagination: true,
initParam: {},
border: true,
Expand Down Expand Up @@ -207,7 +207,7 @@ const colSetting = tableColumns.value!.filter(
);
const openColSetting = () => colRef.value.openColSetting();

// 🙅‍♀️ 不需要打印可以把以下方法删除,打印功能目前存在很多 bug(目前数据处理比较复杂 210-248 行)
// 🙅‍♀️ 不需要打印可以把以下方法删除,打印功能目前存在很多 bug
// 处理打印数据(把后台返回的值根据 enum 做转换)
const printData = computed(() => {
const handleData = props.data ?? tableData.value;
Expand Down
21 changes: 16 additions & 5 deletions src/components/ProTable/interface/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
import { VNode, ComponentPublicInstance } from "vue";
import { BreakPoint, Responsive } from "@/components/Grid/interface";
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
import { ProTableProps } from "@/components/ProTable/index.vue";
import ProTable from "@/components/ProTable/index.vue";

export interface EnumProps {
label: string; // 选项框显示的文字
Expand Down Expand Up @@ -41,7 +44,7 @@ export type SearchProps = {
span?: number; // 搜索项所占用的列数,默认为1列
offset?: number; // 搜索字段左侧偏移列数
defaultValue?: string | number | boolean | any[]; // 搜索项默认值
render?: (scope: SearchRenderScope) => any; // 自定义搜索内容渲染(tsx语法)
render?: (scope: SearchRenderScope) => VNode; // 自定义搜索内容渲染(tsx语法)
} & Partial<Record<BreakPoint, Responsive>>;

export type FieldNamesProps = {
Expand All @@ -50,21 +53,29 @@ export type FieldNamesProps = {
children?: string;
};

export type TableColumnRenderScope<T> = {
export type RenderScope<T> = {
row: T;
$index: number;
column: TableColumnCtx<T>;
[key: string]: any;
};

export type HeaderRenderScope<T> = {
$index: number;
column: TableColumnCtx<T>;
[key: string]: any;
};

export interface ColumnProps<T = any> extends Partial<Omit<TableColumnCtx<T>, "children" | "renderCell" | "renderHeader">> {
tag?: boolean; // 是否是标签展示
isShow?: boolean; // 是否显示在表格当中
search?: SearchProps | undefined; // 搜索项配置
enum?: EnumProps[] | ((params?: any) => Promise<any>); // 枚举类型(字典)
isFilterEnum?: boolean; // 当前单元格值是否根据 enum 格式化(示例:enum 只作为搜索项数据)
fieldNames?: FieldNamesProps; // 指定 label && value && children 的 key 值
headerRender?: (row: ColumnProps) => any; // 自定义表头内容渲染(tsx语法)
render?: (scope: TableColumnRenderScope<T>) => any; // 自定义单元格内容渲染(tsx语法)
headerRender?: (scope: HeaderRenderScope<T>) => VNode; // 自定义表头内容渲染(tsx语法)
render?: (scope: RenderScope<T>) => VNode | string; // 自定义单元格内容渲染(tsx语法)
_children?: ColumnProps<T>[]; // 多级表头
}

export type ProTableInstance = Omit<InstanceType<typeof ProTable>, keyof ComponentPublicInstance | keyof ProTableProps>;
29 changes: 11 additions & 18 deletions src/views/proTable/complexProTable/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@
>
<!-- 表格 header 按钮 -->
<template #tableHeader="scope">
<el-button type="primary" :icon="CirclePlus" @click="proTable.element.toggleAllSelection()">全选 / 全不选</el-button>
<el-button type="primary" :icon="CirclePlus" @click="proTable?.element?.toggleAllSelection">全选 / 全不选</el-button>
<el-button type="primary" :icon="Pointer" plain @click="setCurrent">选中第五行</el-button>
<el-button type="danger" :icon="Delete" plain @click="batchDelete(scope.selectedListIds)" :disabled="!scope.isSelected">
批量删除用户
</el-button>
</template>
<!-- 单选 -->
<template #radio="scope">
<el-radio :label="scope.row.id" v-model="radio"><i></i></el-radio>
</template>
<!-- Expand -->
<template #expand="scope">
{{ scope.row }}
Expand All @@ -44,31 +40,28 @@
import { ref } from "vue";
import { ElMessage } from "element-plus";
import { User } from "@/api/interface";
import { ColumnProps } from "@/components/ProTable/interface";
import { useHandleData } from "@/hooks/useHandleData";
import ProTable from "@/components/ProTable/index.vue";
import type { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
import { ProTableInstance, ColumnProps, HeaderRenderScope } from "@/components/ProTable/interface";
import { CirclePlus, Pointer, Delete, Refresh } from "@element-plus/icons-vue";
import { getUserList, deleteUser, resetUserPassWord, getUserStatus, getUserGender } from "@/api/modules/user";
// 获取 ProTable DOM
const proTable = ref();
// 单选
const radio = ref();
const proTable = ref<ProTableInstance>();
// 自定义渲染表头(使用tsx语法)
const headerRender = (row: ColumnProps) => {
const headerRender = (scope: HeaderRenderScope<User.ResUserList>) => {
return (
<el-button type="primary" onClick={() => ElMessage.success("我是通过 tsx 语法渲染的表头")}>
{row.label}
{scope.column.label}
</el-button>
);
};
// 表格配置项
const columns: ColumnProps<User.ResUserList>[] = [
{ prop: "radio", label: "单选", width: 80 },
{ type: "selection", width: 80 },
{ type: "index", label: "#", width: 80 },
{ type: "expand", label: "Expand", width: 100 },
{
Expand Down Expand Up @@ -109,7 +102,7 @@ const columns: ColumnProps<User.ResUserList>[] = [
// 选择行
const setCurrent = () => {
proTable.value.element.setCurrentRow(proTable.value.tableData[4]);
proTable.value?.element?.setCurrentRow(proTable.value?.tableData[4]);
};
// 表尾合计行(自行根据条件计算)
Expand Down Expand Up @@ -158,20 +151,20 @@ const rowClick = (row: User.ResUserList, column: TableColumnCtx<User.ResUserList
// 删除用户信息
const deleteAccount = async (params: User.ResUserList) => {
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`);
proTable.value.getTableList();
proTable.value?.getTableList();
};
// 批量删除用户信息
const batchDelete = async (id: string[]) => {
await useHandleData(deleteUser, { id }, "删除所选用户信息");
proTable.value.clearSelection();
proTable.value.getTableList();
proTable.value?.clearSelection();
proTable.value?.getTableList();
};
// 重置用户密码
const resetPass = async (params: User.ResUserList) => {
await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`);
proTable.value.getTableList();
proTable.value?.getTableList();
};
</script>

Expand Down
14 changes: 7 additions & 7 deletions src/views/proTable/treeProTable/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@

<script setup lang="tsx" name="treeProTable">
import { onMounted, reactive, ref } from "vue";
import { ElMessage, ElNotification } from "element-plus";
import { User } from "@/api/interface";
import { ColumnProps } from "@/components/ProTable/interface";
import { useHandleData } from "@/hooks/useHandleData";
import { genderType } from "@/utils/serviceDict";
import { useHandleData } from "@/hooks/useHandleData";
import { ElMessage, ElNotification } from "element-plus";
import ProTable from "@/components/ProTable/index.vue";
import TreeFilter from "@/components/TreeFilter/index.vue";
import ImportExcel from "@/components/ImportExcel/index.vue";
import UserDrawer from "@/views/proTable/components/UserDrawer.vue";
import { CirclePlus, Delete, EditPen, View } from "@element-plus/icons-vue";
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
import { getUserTreeList, deleteUser, editUser, addUser, getUserStatus, getUserDepartment } from "@/api/modules/user";
onMounted(() => {
Expand All @@ -69,7 +69,7 @@ onMounted(() => {
});
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTable = ref();
const proTable = ref<ProTableInstance>();
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
const initParam = reactive({ departmentId: "" });
Expand All @@ -86,7 +86,7 @@ const getTreeFilter = async () => {
// 树形筛选切换
const changeTreeFilter = (val: string) => {
ElMessage.success("请注意查看请求参数变化 🤔");
proTable.value.pageable.pageNum = 1;
proTable.value!.pageable.pageNum = 1;
initParam.departmentId = val;
};
Expand Down Expand Up @@ -138,7 +138,7 @@ const columns: ColumnProps<User.ResUserList>[] = [
// 删除用户信息
const deleteAccount = async (params: User.ResUserList) => {
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`);
proTable.value.getTableList();
proTable.value?.getTableList();
};
// 打开 drawer(新增、查看、编辑)
Expand All @@ -149,7 +149,7 @@ const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
row: { ...row },
isView: title === "查看",
api: title === "新增" ? addUser : title === "编辑" ? editUser : undefined,
getTableList: proTable.value.getTableList
getTableList: proTable.value?.getTableList
};
drawerRef.value?.acceptParams(params);
};
Expand Down
26 changes: 13 additions & 13 deletions src/views/proTable/useProTable/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<!-- usernameHeader -->
<template #usernameHeader="scope">
<el-button type="primary" @click="ElMessage.success('我是通过作用域插槽渲染的表头')">
{{ scope.row.label }}
{{ scope.column.label }}
</el-button>
</template>
<!-- createTime -->
Expand All @@ -51,14 +51,14 @@
import { ref, reactive } from "vue";
import { useRouter } from "vue-router";
import { User } from "@/api/interface";
import { ColumnProps } from "@/components/ProTable/interface";
import { useHandleData } from "@/hooks/useHandleData";
import { useDownload } from "@/hooks/useDownload";
import { useAuthButtons } from "@/hooks/useAuthButtons";
import { ElMessage, ElMessageBox } from "element-plus";
import ProTable from "@/components/ProTable/index.vue";
import ImportExcel from "@/components/ImportExcel/index.vue";
import UserDrawer from "@/views/proTable/components/UserDrawer.vue";
import { ProTableInstance, ColumnProps, HeaderRenderScope } from "@/components/ProTable/interface";
import { CirclePlus, Delete, EditPen, Download, Upload, View, Refresh } from "@element-plus/icons-vue";
import {
getUserList,
Expand All @@ -81,7 +81,7 @@ const toDetail = () => {
};
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTable = ref();
const proTable = ref<ProTableInstance>();
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
const initParam = reactive({ type: 1 });
Expand Down Expand Up @@ -111,10 +111,10 @@ const getTableList = (params: any) => {
const { BUTTONS } = useAuthButtons();
// 自定义渲染表头(使用tsx语法)
const headerRender = (row: ColumnProps) => {
const headerRender = (scope: HeaderRenderScope<User.ResUserList>) => {
return (
<el-button type="primary" onClick={() => ElMessage.success("我是通过 tsx 语法渲染的表头")}>
{row.label}
{scope.column.label}
</el-button>
);
};
Expand Down Expand Up @@ -210,32 +210,32 @@ const columns: ColumnProps<User.ResUserList>[] = [
// 删除用户信息
const deleteAccount = async (params: User.ResUserList) => {
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`);
proTable.value.getTableList();
proTable.value?.getTableList();
};
// 批量删除用户信息
const batchDelete = async (id: string[]) => {
await useHandleData(deleteUser, { id }, "删除所选用户信息");
proTable.value.clearSelection();
proTable.value.getTableList();
proTable.value?.clearSelection();
proTable.value?.getTableList();
};
// 重置用户密码
const resetPass = async (params: User.ResUserList) => {
await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`);
proTable.value.getTableList();
proTable.value?.getTableList();
};
// 切换用户状态
const changeStatus = async (row: User.ResUserList) => {
await useHandleData(changeUserStatus, { id: row.id, status: row.status == 1 ? 0 : 1 }, `切换【${row.username}】用户状态`);
proTable.value.getTableList();
proTable.value?.getTableList();
};
// 导出用户列表
const downloadFile = async () => {
ElMessageBox.confirm("确认导出用户数据?", "温馨提示", { type: "warning" }).then(() =>
useDownload(exportUserInfo, "用户列表", proTable.value.searchParam)
useDownload(exportUserInfo, "用户列表", proTable.value?.searchParam)
);
};
Expand All @@ -246,7 +246,7 @@ const batchAdd = () => {
title: "用户",
tempApi: exportUserInfo,
importApi: BatchAddUser,
getTableList: proTable.value.getTableList
getTableList: proTable.value?.getTableList
};
dialogRef.value?.acceptParams(params);
};
Expand All @@ -259,7 +259,7 @@ const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
isView: title === "查看",
row: { ...row },
api: title === "新增" ? addUser : title === "编辑" ? editUser : undefined,
getTableList: proTable.value.getTableList
getTableList: proTable.value?.getTableList
};
drawerRef.value?.acceptParams(params);
};
Expand Down
Loading

1 comment on commit 8262f04

@vercel
Copy link

@vercel vercel bot commented on 8262f04 May 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.