Skip to content

Commit 8262f04

Browse files
author
HalseySpicy
committed
feat: 🚀 add proTable instance type
1 parent f58291f commit 8262f04

File tree

8 files changed

+91
-71
lines changed

8 files changed

+91
-71
lines changed

src/components/ProTable/components/TableColumn.vue

+7-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<script setup lang="tsx" name="TableColumn">
66
import { inject, ref, useSlots } from "vue";
7-
import { ColumnProps } from "@/components/ProTable/interface";
7+
import { ColumnProps, RenderScope, HeaderRenderScope } from "@/components/ProTable/interface";
88
import { filterEnum, formatValue, handleProp, handleRowAccordingToProp } from "@/utils";
99
1010
defineProps<{ column: ColumnProps }>();
@@ -14,14 +14,14 @@ const slots = useSlots();
1414
const enumMap = inject("enumMap", ref(new Map()));
1515
1616
// 渲染表格数据
17-
const renderCellData = (item: ColumnProps, scope: { [key: string]: any }) => {
17+
const renderCellData = (item: ColumnProps, scope: RenderScope<any>) => {
1818
return enumMap.value.get(item.prop) && item.isFilterEnum
1919
? filterEnum(handleRowAccordingToProp(scope.row, item.prop!), enumMap.value.get(item.prop)!, item.fieldNames)
2020
: formatValue(handleRowAccordingToProp(scope.row, item.prop!));
2121
};
2222
2323
// 获取 tag 类型
24-
const getTagType = (item: ColumnProps, scope: { [key: string]: any }) => {
24+
const getTagType = (item: ColumnProps, scope: RenderScope<any>) => {
2525
return filterEnum(handleRowAccordingToProp(scope.row, item.prop!), enumMap.value.get(item.prop), item.fieldNames, "tag");
2626
};
2727
@@ -35,16 +35,16 @@ const RenderTableColumn = (item: ColumnProps) => {
3535
showOverflowTooltip={item.showOverflowTooltip ?? item.prop !== "operation"}
3636
>
3737
{{
38-
default: (scope: any) => {
38+
default: (scope: RenderScope<any>) => {
3939
if (item._children) return item._children.map(child => RenderTableColumn(child));
4040
if (item.render) return item.render(scope);
4141
if (slots[handleProp(item.prop!)]) return slots[handleProp(item.prop!)]!(scope);
4242
if (item.tag) return <el-tag type={getTagType(item, scope)}>{renderCellData(item, scope)}</el-tag>;
4343
return renderCellData(item, scope);
4444
},
45-
header: () => {
46-
if (item.headerRender) return item.headerRender(item);
47-
if (slots[`${handleProp(item.prop!)}Header`]) return slots[`${handleProp(item.prop!)}Header`]!({ row: item });
45+
header: (scope: HeaderRenderScope<any>) => {
46+
if (item.headerRender) return item.headerRender(scope);
47+
if (slots[`${handleProp(item.prop!)}Header`]) return slots[`${handleProp(item.prop!)}Header`]!(scope);
4848
return item.label;
4949
}
5050
}}

src/components/ProTable/index.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ import ColSetting from "./components/ColSetting.vue";
101101
import TableColumn from "./components/TableColumn.vue";
102102
import printJS from "print-js";
103103

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

120120
// 接受父组件参数,配置默认值
121121
const props = withDefaults(defineProps<ProTableProps>(), {
122-
requestAuto: true,
123122
columns: () => [],
123+
requestAuto: true,
124124
pagination: true,
125125
initParam: {},
126126
border: true,
@@ -207,7 +207,7 @@ const colSetting = tableColumns.value!.filter(
207207
);
208208
const openColSetting = () => colRef.value.openColSetting();
209209

210-
// 🙅‍♀️ 不需要打印可以把以下方法删除,打印功能目前存在很多 bug(目前数据处理比较复杂 210-248 行)
210+
// 🙅‍♀️ 不需要打印可以把以下方法删除,打印功能目前存在很多 bug
211211
// 处理打印数据(把后台返回的值根据 enum 做转换)
212212
const printData = computed(() => {
213213
const handleData = props.data ?? tableData.value;

src/components/ProTable/interface/index.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
1+
import { VNode, ComponentPublicInstance } from "vue";
22
import { BreakPoint, Responsive } from "@/components/Grid/interface";
3+
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
4+
import { ProTableProps } from "@/components/ProTable/index.vue";
5+
import ProTable from "@/components/ProTable/index.vue";
36

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

4750
export type FieldNamesProps = {
@@ -50,21 +53,29 @@ export type FieldNamesProps = {
5053
children?: string;
5154
};
5255

53-
export type TableColumnRenderScope<T> = {
56+
export type RenderScope<T> = {
5457
row: T;
5558
$index: number;
5659
column: TableColumnCtx<T>;
5760
[key: string]: any;
5861
};
5962

63+
export type HeaderRenderScope<T> = {
64+
$index: number;
65+
column: TableColumnCtx<T>;
66+
[key: string]: any;
67+
};
68+
6069
export interface ColumnProps<T = any> extends Partial<Omit<TableColumnCtx<T>, "children" | "renderCell" | "renderHeader">> {
6170
tag?: boolean; // 是否是标签展示
6271
isShow?: boolean; // 是否显示在表格当中
6372
search?: SearchProps | undefined; // 搜索项配置
6473
enum?: EnumProps[] | ((params?: any) => Promise<any>); // 枚举类型(字典)
6574
isFilterEnum?: boolean; // 当前单元格值是否根据 enum 格式化(示例:enum 只作为搜索项数据)
6675
fieldNames?: FieldNamesProps; // 指定 label && value && children 的 key 值
67-
headerRender?: (row: ColumnProps) => any; // 自定义表头内容渲染(tsx语法)
68-
render?: (scope: TableColumnRenderScope<T>) => any; // 自定义单元格内容渲染(tsx语法)
76+
headerRender?: (scope: HeaderRenderScope<T>) => VNode; // 自定义表头内容渲染(tsx语法)
77+
render?: (scope: RenderScope<T>) => VNode | string; // 自定义单元格内容渲染(tsx语法)
6978
_children?: ColumnProps<T>[]; // 多级表头
7079
}
80+
81+
export type ProTableInstance = Omit<InstanceType<typeof ProTable>, keyof ComponentPublicInstance | keyof ProTableProps>;

src/views/proTable/complexProTable/index.vue

+11-18
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,12 @@
1414
>
1515
<!-- 表格 header 按钮 -->
1616
<template #tableHeader="scope">
17-
<el-button type="primary" :icon="CirclePlus" @click="proTable.element.toggleAllSelection()">全选 / 全不选</el-button>
17+
<el-button type="primary" :icon="CirclePlus" @click="proTable?.element?.toggleAllSelection">全选 / 全不选</el-button>
1818
<el-button type="primary" :icon="Pointer" plain @click="setCurrent">选中第五行</el-button>
1919
<el-button type="danger" :icon="Delete" plain @click="batchDelete(scope.selectedListIds)" :disabled="!scope.isSelected">
2020
批量删除用户
2121
</el-button>
2222
</template>
23-
<!-- 单选 -->
24-
<template #radio="scope">
25-
<el-radio :label="scope.row.id" v-model="radio"><i></i></el-radio>
26-
</template>
2723
<!-- Expand -->
2824
<template #expand="scope">
2925
{{ scope.row }}
@@ -44,31 +40,28 @@
4440
import { ref } from "vue";
4541
import { ElMessage } from "element-plus";
4642
import { User } from "@/api/interface";
47-
import { ColumnProps } from "@/components/ProTable/interface";
4843
import { useHandleData } from "@/hooks/useHandleData";
4944
import ProTable from "@/components/ProTable/index.vue";
5045
import type { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
46+
import { ProTableInstance, ColumnProps, HeaderRenderScope } from "@/components/ProTable/interface";
5147
import { CirclePlus, Pointer, Delete, Refresh } from "@element-plus/icons-vue";
5248
import { getUserList, deleteUser, resetUserPassWord, getUserStatus, getUserGender } from "@/api/modules/user";
5349
5450
// 获取 ProTable DOM
55-
const proTable = ref();
56-
57-
// 单选
58-
const radio = ref();
51+
const proTable = ref<ProTableInstance>();
5952
6053
// 自定义渲染表头(使用tsx语法)
61-
const headerRender = (row: ColumnProps) => {
54+
const headerRender = (scope: HeaderRenderScope<User.ResUserList>) => {
6255
return (
6356
<el-button type="primary" onClick={() => ElMessage.success("我是通过 tsx 语法渲染的表头")}>
64-
{row.label}
57+
{scope.column.label}
6558
</el-button>
6659
);
6760
};
6861
6962
// 表格配置项
7063
const columns: ColumnProps<User.ResUserList>[] = [
71-
{ prop: "radio", label: "单选", width: 80 },
64+
{ type: "selection", width: 80 },
7265
{ type: "index", label: "#", width: 80 },
7366
{ type: "expand", label: "Expand", width: 100 },
7467
{
@@ -109,7 +102,7 @@ const columns: ColumnProps<User.ResUserList>[] = [
109102
110103
// 选择行
111104
const setCurrent = () => {
112-
proTable.value.element.setCurrentRow(proTable.value.tableData[4]);
105+
proTable.value?.element?.setCurrentRow(proTable.value?.tableData[4]);
113106
};
114107
115108
// 表尾合计行(自行根据条件计算)
@@ -158,20 +151,20 @@ const rowClick = (row: User.ResUserList, column: TableColumnCtx<User.ResUserList
158151
// 删除用户信息
159152
const deleteAccount = async (params: User.ResUserList) => {
160153
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`);
161-
proTable.value.getTableList();
154+
proTable.value?.getTableList();
162155
};
163156
164157
// 批量删除用户信息
165158
const batchDelete = async (id: string[]) => {
166159
await useHandleData(deleteUser, { id }, "删除所选用户信息");
167-
proTable.value.clearSelection();
168-
proTable.value.getTableList();
160+
proTable.value?.clearSelection();
161+
proTable.value?.getTableList();
169162
};
170163
171164
// 重置用户密码
172165
const resetPass = async (params: User.ResUserList) => {
173166
await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`);
174-
proTable.value.getTableList();
167+
proTable.value?.getTableList();
175168
};
176169
</script>
177170

src/views/proTable/treeProTable/index.vue

+7-7
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@
3838

3939
<script setup lang="tsx" name="treeProTable">
4040
import { onMounted, reactive, ref } from "vue";
41-
import { ElMessage, ElNotification } from "element-plus";
4241
import { User } from "@/api/interface";
43-
import { ColumnProps } from "@/components/ProTable/interface";
44-
import { useHandleData } from "@/hooks/useHandleData";
4542
import { genderType } from "@/utils/serviceDict";
43+
import { useHandleData } from "@/hooks/useHandleData";
44+
import { ElMessage, ElNotification } from "element-plus";
4645
import ProTable from "@/components/ProTable/index.vue";
4746
import TreeFilter from "@/components/TreeFilter/index.vue";
4847
import ImportExcel from "@/components/ImportExcel/index.vue";
4948
import UserDrawer from "@/views/proTable/components/UserDrawer.vue";
5049
import { CirclePlus, Delete, EditPen, View } from "@element-plus/icons-vue";
50+
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
5151
import { getUserTreeList, deleteUser, editUser, addUser, getUserStatus, getUserDepartment } from "@/api/modules/user";
5252
5353
onMounted(() => {
@@ -69,7 +69,7 @@ onMounted(() => {
6969
});
7070
7171
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
72-
const proTable = ref();
72+
const proTable = ref<ProTableInstance>();
7373
7474
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
7575
const initParam = reactive({ departmentId: "" });
@@ -86,7 +86,7 @@ const getTreeFilter = async () => {
8686
// 树形筛选切换
8787
const changeTreeFilter = (val: string) => {
8888
ElMessage.success("请注意查看请求参数变化 🤔");
89-
proTable.value.pageable.pageNum = 1;
89+
proTable.value!.pageable.pageNum = 1;
9090
initParam.departmentId = val;
9191
};
9292
@@ -138,7 +138,7 @@ const columns: ColumnProps<User.ResUserList>[] = [
138138
// 删除用户信息
139139
const deleteAccount = async (params: User.ResUserList) => {
140140
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`);
141-
proTable.value.getTableList();
141+
proTable.value?.getTableList();
142142
};
143143
144144
// 打开 drawer(新增、查看、编辑)
@@ -149,7 +149,7 @@ const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
149149
row: { ...row },
150150
isView: title === "查看",
151151
api: title === "新增" ? addUser : title === "编辑" ? editUser : undefined,
152-
getTableList: proTable.value.getTableList
152+
getTableList: proTable.value?.getTableList
153153
};
154154
drawerRef.value?.acceptParams(params);
155155
};

src/views/proTable/useProTable/index.vue

+13-13
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<!-- usernameHeader -->
2626
<template #usernameHeader="scope">
2727
<el-button type="primary" @click="ElMessage.success('我是通过作用域插槽渲染的表头')">
28-
{{ scope.row.label }}
28+
{{ scope.column.label }}
2929
</el-button>
3030
</template>
3131
<!-- createTime -->
@@ -51,14 +51,14 @@
5151
import { ref, reactive } from "vue";
5252
import { useRouter } from "vue-router";
5353
import { User } from "@/api/interface";
54-
import { ColumnProps } from "@/components/ProTable/interface";
5554
import { useHandleData } from "@/hooks/useHandleData";
5655
import { useDownload } from "@/hooks/useDownload";
5756
import { useAuthButtons } from "@/hooks/useAuthButtons";
5857
import { ElMessage, ElMessageBox } from "element-plus";
5958
import ProTable from "@/components/ProTable/index.vue";
6059
import ImportExcel from "@/components/ImportExcel/index.vue";
6160
import UserDrawer from "@/views/proTable/components/UserDrawer.vue";
61+
import { ProTableInstance, ColumnProps, HeaderRenderScope } from "@/components/ProTable/interface";
6262
import { CirclePlus, Delete, EditPen, Download, Upload, View, Refresh } from "@element-plus/icons-vue";
6363
import {
6464
getUserList,
@@ -81,7 +81,7 @@ const toDetail = () => {
8181
};
8282
8383
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
84-
const proTable = ref();
84+
const proTable = ref<ProTableInstance>();
8585
8686
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
8787
const initParam = reactive({ type: 1 });
@@ -111,10 +111,10 @@ const getTableList = (params: any) => {
111111
const { BUTTONS } = useAuthButtons();
112112
113113
// 自定义渲染表头(使用tsx语法)
114-
const headerRender = (row: ColumnProps) => {
114+
const headerRender = (scope: HeaderRenderScope<User.ResUserList>) => {
115115
return (
116116
<el-button type="primary" onClick={() => ElMessage.success("我是通过 tsx 语法渲染的表头")}>
117-
{row.label}
117+
{scope.column.label}
118118
</el-button>
119119
);
120120
};
@@ -210,32 +210,32 @@ const columns: ColumnProps<User.ResUserList>[] = [
210210
// 删除用户信息
211211
const deleteAccount = async (params: User.ResUserList) => {
212212
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`);
213-
proTable.value.getTableList();
213+
proTable.value?.getTableList();
214214
};
215215
216216
// 批量删除用户信息
217217
const batchDelete = async (id: string[]) => {
218218
await useHandleData(deleteUser, { id }, "删除所选用户信息");
219-
proTable.value.clearSelection();
220-
proTable.value.getTableList();
219+
proTable.value?.clearSelection();
220+
proTable.value?.getTableList();
221221
};
222222
223223
// 重置用户密码
224224
const resetPass = async (params: User.ResUserList) => {
225225
await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`);
226-
proTable.value.getTableList();
226+
proTable.value?.getTableList();
227227
};
228228
229229
// 切换用户状态
230230
const changeStatus = async (row: User.ResUserList) => {
231231
await useHandleData(changeUserStatus, { id: row.id, status: row.status == 1 ? 0 : 1 }, `切换【${row.username}】用户状态`);
232-
proTable.value.getTableList();
232+
proTable.value?.getTableList();
233233
};
234234
235235
// 导出用户列表
236236
const downloadFile = async () => {
237237
ElMessageBox.confirm("确认导出用户数据?", "温馨提示", { type: "warning" }).then(() =>
238-
useDownload(exportUserInfo, "用户列表", proTable.value.searchParam)
238+
useDownload(exportUserInfo, "用户列表", proTable.value?.searchParam)
239239
);
240240
};
241241
@@ -246,7 +246,7 @@ const batchAdd = () => {
246246
title: "用户",
247247
tempApi: exportUserInfo,
248248
importApi: BatchAddUser,
249-
getTableList: proTable.value.getTableList
249+
getTableList: proTable.value?.getTableList
250250
};
251251
dialogRef.value?.acceptParams(params);
252252
};
@@ -259,7 +259,7 @@ const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
259259
isView: title === "查看",
260260
row: { ...row },
261261
api: title === "新增" ? addUser : title === "编辑" ? editUser : undefined,
262-
getTableList: proTable.value.getTableList
262+
getTableList: proTable.value?.getTableList
263263
};
264264
drawerRef.value?.acceptParams(params);
265265
};

0 commit comments

Comments
 (0)