@jzt-packages/components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +68 -0
- package/src/JztBackTop/index.vue +255 -0
- package/src/JztButtonList/index.vue +88 -0
- package/src/JztChart/index.vue +95 -0
- package/src/JztCharts/index.vue +317 -0
- package/src/JztClassTabs/index.vue +156 -0
- package/src/JztDateSelect/dateSelect.vue +186 -0
- package/src/JztDateSelect/dateType.vue +54 -0
- package/src/JztDateSelect/index.ts +135 -0
- package/src/JztDateSelect/interface/index.ts +13 -0
- package/src/JztDialog/index.vue +249 -0
- package/src/JztEllipsisTooltip/index.vue +61 -0
- package/src/JztEmpty/index.vue +45 -0
- package/src/JztErrorPage/403.vue +30 -0
- package/src/JztErrorPage/404.vue +19 -0
- package/src/JztErrorPage/500.vue +18 -0
- package/src/JztErrorPage/assets/401.png +0 -0
- package/src/JztErrorPage/assets/403.png +0 -0
- package/src/JztErrorPage/assets/404.png +0 -0
- package/src/JztErrorPage/assets/500.png +0 -0
- package/src/JztErrorPage/index.scss +35 -0
- package/src/JztErrorPage/index.vue +35 -0
- package/src/JztFilePreview/components/pdfViewer.vue +221 -0
- package/src/JztFilePreview/hooks/useImageMethod.ts +256 -0
- package/src/JztFilePreview/index.scss +171 -0
- package/src/JztFilePreview/index.vue +68 -0
- package/src/JztFilePreview/interface/index.ts +18 -0
- package/src/JztFilePreview/previewFile.vue +371 -0
- package/src/JztFormGrid/README.md +520 -0
- package/src/JztFormGrid/components/formItem.vue +209 -0
- package/src/JztFormGrid/components/formItemValue.vue +384 -0
- package/src/JztFormGrid/components/showDetailForm.vue +172 -0
- package/src/JztFormGrid/index.scss +60 -0
- package/src/JztFormGrid/index.vue +513 -0
- package/src/JztFormGrid/interface/index.ts +106 -0
- package/src/JztGrid/components/GridItem.vue +68 -0
- package/src/JztGrid/index.vue +179 -0
- package/src/JztGrid/interface/index.ts +6 -0
- package/src/JztImportExcel/assets/delete.png +0 -0
- package/src/JztImportExcel/index.scss +46 -0
- package/src/JztImportExcel/index.vue +430 -0
- package/src/JztImportExcel/interface/index.ts +25 -0
- package/src/JztLabelTitle/index.vue +65 -0
- package/src/JztLeftRightMode/components/CollapseButton.vue +80 -0
- package/src/JztLeftRightMode/components/LeftCard.vue +203 -0
- package/src/JztLeftRightMode/components/LeftLayout.vue +173 -0
- package/src/JztLeftRightMode/components/RightHeader.vue +186 -0
- package/src/JztLeftRightMode/components/RightLayout.vue +235 -0
- package/src/JztLeftRightMode/components/RightTableHeader.vue +43 -0
- package/src/JztLeftRightMode/hooks/useCollapse.ts +17 -0
- package/src/JztLeftRightMode/hooks/useDefaultProps.ts +19 -0
- package/src/JztLeftRightMode/hooks/useLeftLayout.ts +201 -0
- package/src/JztLeftRightMode/hooks/useMode.ts +20 -0
- package/src/JztLeftRightMode/hooks/usePrevNext.ts +60 -0
- package/src/JztLeftRightMode/hooks/useRightLayout.ts +215 -0
- package/src/JztLeftRightMode/hooks/useSlots.ts +15 -0
- package/src/JztLeftRightMode/index.ts +3 -0
- package/src/JztLeftRightMode/index.vue +494 -0
- package/src/JztLeftRightMode/types/index.ts +457 -0
- package/src/JztLoading/fullScreen.ts +45 -0
- package/src/JztLoading/index.scss +67 -0
- package/src/JztLoading/index.vue +18 -0
- package/src/JztLogin/components/LoginFooter.vue +17 -0
- package/src/JztLogin/components/LoginForm.vue +99 -0
- package/src/JztLogin/hooks/useLogin.ts +186 -0
- package/src/JztLogin/index.scss +142 -0
- package/src/JztLogin/index.vue +31 -0
- package/src/JztLogin/interface/index.ts +47 -0
- package/src/JztNumericalRange/index.vue +81 -0
- package/src/JztPageCard/comm/datePicker.vue +151 -0
- package/src/JztPageCard/comm/details.vue +60 -0
- package/src/JztPageCard/comm/export.vue +24 -0
- package/src/JztPageCard/comm/tabs.vue +94 -0
- package/src/JztPageCard/comm/tooltip.vue +31 -0
- package/src/JztPageCard/index.vue +287 -0
- package/src/JztPagination/index.vue +70 -0
- package/src/JztProductInfo/components/imagePreview.vue +275 -0
- package/src/JztProductInfo/components/qxUnique.vue +101 -0
- package/src/JztProductInfo/components/records.vue +265 -0
- package/src/JztProductInfo/hooks/useParams.ts +143 -0
- package/src/JztProductInfo/hooks/useQxUnique.tsx +466 -0
- package/src/JztProductInfo/images/defaultProduct.png +0 -0
- package/src/JztProductInfo/index.ts +116 -0
- package/src/JztProductInfo/index.vue +108 -0
- package/src/JztProductInfo/interface/index.ts +15 -0
- package/src/JztQueryDetailTable/index.scss +100 -0
- package/src/JztQueryDetailTable/index.vue +400 -0
- package/src/JztQueryDetailTable/interface/index.ts +10 -0
- package/src/JztQueryTable/QueryTable /345/212/237/350/203/275.md" +1580 -0
- package/src/JztQueryTable/README.md +567 -0
- package/src/JztQueryTable/components/ColSetting.vue +67 -0
- package/src/JztQueryTable/components/ColumnsSetting.vue +404 -0
- package/src/JztQueryTable/components/ColumnsSetting1.vue +220 -0
- package/src/JztQueryTable/components/DeployToAccountLevelSetting.vue +351 -0
- package/src/JztQueryTable/components/Pagination.vue +54 -0
- package/src/JztQueryTable/components/TableColumn.vue +109 -0
- package/src/JztQueryTable/const.ts +1 -0
- package/src/JztQueryTable/hooks/useQueryTable.ts +194 -0
- package/src/JztQueryTable/hooks/useSelection.ts +47 -0
- package/src/JztQueryTable/hooks/useTableSetting.ts +197 -0
- package/src/JztQueryTable/hooks/useTemplate.ts +127 -0
- package/src/JztQueryTable/index.scss +91 -0
- package/src/JztQueryTable/index.vue +1445 -0
- package/src/JztQueryTable/interface/index.ts +185 -0
- package/src/JztRegionSelect/index.vue +134 -0
- package/src/JztSearchForm/components/SearchFormItem.vue +473 -0
- package/src/JztSearchForm/index.vue +530 -0
- package/src/JztSearchForm/interface/index.ts +100 -0
- package/src/JztSelectFilter/index.scss +63 -0
- package/src/JztSelectFilter/index.vue +110 -0
- package/src/JztSelectTable/index.vue +257 -0
- package/src/JztTable/index.scss +72 -0
- package/src/JztTable/index.vue +353 -0
- package/src/JztTable/interface/index.ts +1 -0
- package/src/JztTime/comm/agencySelect.vue +112 -0
- package/src/JztTime/comm/collapseRow.vue +132 -0
- package/src/JztTime/comm/dateSelect.vue +292 -0
- package/src/JztTime/comm/deptSelect.vue +193 -0
- package/src/JztTime/comm/typeSelect.vue +97 -0
- package/src/JztTime/index.ts +216 -0
- package/src/JztTime/index.vue +303 -0
- package/src/JztTime/interface/index.ts +23 -0
- package/src/JztTreeFilter/index.scss +44 -0
- package/src/JztTreeFilter/index.vue +177 -0
- package/src/JztUploadFile/interface/index.ts +21 -0
- package/src/JztUploadFile/multiple.scss +215 -0
- package/src/JztUploadFile/multiple.vue +318 -0
- package/src/JztUploadFile/single.scss +226 -0
- package/src/JztUploadFile/single.vue +274 -0
- package/src/JztUploadImg/Img.vue +294 -0
- package/src/JztUploadImg/Imgs.vue +411 -0
- package/src/JztUploadImg/index.scss +138 -0
- package/src/JztUploadImg/interface/index.ts +22 -0
- package/src/SelectIcon/index.scss +39 -0
- package/src/SelectIcon/index.vue +106 -0
- package/src/SvgIcon/index.vue +22 -0
- package/src/hooks/useAuthButtons.ts +58 -0
- package/src/hooks/useFormByUserType.ts +90 -0
- package/src/hooks/useTableEvents.ts +30 -0
- package/src/hooks/useUploadFileHook.ts +262 -0
- package/src/index.ts +91 -0
- package/src/typings/global.d.ts +101 -0
- package/src/utils/index.ts +107 -0
- package/src/utils/tree.ts +57 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,1580 @@
|
|
|
1
|
+
## 前言 📖
|
|
2
|
+
|
|
3
|
+
## 一、QueryTable 功能 🚀🚀🚀
|
|
4
|
+
|
|
5
|
+
> QueryTable 组件目前使用属性透传进行重构,支持 **el-table && el-table-column** 所有属性、事件、方法的调用,不会有任何心智负担。
|
|
6
|
+
|
|
7
|
+
- 表格内容自适应屏幕宽高,溢出内容表格内部滚动(flex 布局)
|
|
8
|
+
- 表格搜索、重置、分页查询 Hooks 封装 (页面使用不会存在任何搜索、重置、分页查询逻辑)
|
|
9
|
+
- 表格数据操作 Hooks 封装 (单条数据删除、批量删除、重置密码、状态切换等操作)
|
|
10
|
+
- 表格数据多选 Hooks 封装 (支持现跨页勾选数据)
|
|
11
|
+
- 表格数据导入组件、导出 Hooks 封装
|
|
12
|
+
- 表格搜索区域使用 Grid 布局重构,支持自定义响应式配置
|
|
13
|
+
- 表格分页组件封装(Pagination)支持静态数据分页
|
|
14
|
+
- 表格数据刷新、列显隐、搜索区域显隐设置
|
|
15
|
+
- 表格配置 columns 支持动态更新
|
|
16
|
+
- 表格支持行拖拽排序、单选框设置
|
|
17
|
+
- 表格配置支持多级 prop(示例 ==> prop: user.detail.name)
|
|
18
|
+
- 单元格内容格式化、tag 标签显示(有字典 enum 会根据字典 enum 自动格式化)
|
|
19
|
+
- 支持多级表头、表头内容自定义渲染(支持作用域插槽、tsx 语法、h 函数)
|
|
20
|
+
- 支持单元格内容自定义渲染(支持作用域插槽、tsx 语法、h 函数)
|
|
21
|
+
- 配合 TreeFilter、SelectFilter 组件使用更佳(项目中有使用示例)
|
|
22
|
+
|
|
23
|
+
## 二、QueryTable 功能 🚀🚀🚀 功能需求分析 📑
|
|
24
|
+
|
|
25
|
+
### 首先我们来看效果图(总共可以分为五个模块)
|
|
26
|
+
|
|
27
|
+
- 1、表格搜索区域
|
|
28
|
+
- 2、表格数据操作按钮区域
|
|
29
|
+
- 3、表格功能按钮区域
|
|
30
|
+
- 4、表格主体内容展示区域
|
|
31
|
+
- 5、表格分页区域
|
|
32
|
+
|
|
33
|
+
### 1、表格搜索区域需求分析:
|
|
34
|
+
|
|
35
|
+
> 可以看到搜索区域的字段都是存在于表格当中的,并且每个页面的搜索、重置方法都是一样的逻辑,只是不同的查询参数而已。我们完全可以在一个表单配置项 **queryItems** 时,直接配置**search** 参数,就能把该项变为搜索项,然后使用 **el** 字段可以指定搜索框的类型,最后把表格的搜索方法都封装成 **Hooks** 钩子函数。页面上完全就不会存在任何搜索、重置逻辑了。
|
|
36
|
+
|
|
37
|
+
> 搜索组件在通过 **component :is** 动态组件 && **v-bind** 属性透传实现,将用户传递的参数全部透传到组件上,所以大家可以直接根据 **element** 官方文档在 **props** 中传递参数了。以下代码还结合了自己逻辑上的一些处理:
|
|
38
|
+
|
|
39
|
+
> **表格搜索项可使用 tsx 组件自定义渲染**
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
html代码解读复制代码
|
|
43
|
+
<script setup lang="tsx">
|
|
44
|
+
{
|
|
45
|
+
prop: "age",
|
|
46
|
+
label: "年龄",
|
|
47
|
+
search: {
|
|
48
|
+
// 自定义 search 组件
|
|
49
|
+
render: ({ searchParam }) => {
|
|
50
|
+
return (
|
|
51
|
+
<div class="flx-center">
|
|
52
|
+
<el-input vModel_trim={searchParam.minAge} placeholder="最小年龄" style={{ width: "50%" }} />
|
|
53
|
+
<span class="mr10 ml10">-</span>
|
|
54
|
+
<el-input vModel_trim={searchParam.maxAge} placeholder="最大年龄" style={{ width: "50%" }} />
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
</script>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> 表格搜索组件支持响应式配置,使用 **Grid** 方法进行整体重构 😋。
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
### 2、表格数据操作按钮区域需求分析:
|
|
68
|
+
|
|
69
|
+
> 表格数据操作按钮基本上每个页面都会不一样,所以我们直接使用 **作用域插槽** 来完成每个页面的数据操作按钮区域,**作用域插槽** 可以将表格多选数据信息从 **QueryTable** 的 **Hooks** 多选钩子函数中传到页面上使用。
|
|
70
|
+
|
|
71
|
+
> **scope** 数据中包含:**selectedList(当前选择的数据)、selectedListIds(当前选择的数据id)、isSelected(当前是否选中的数据)**
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
html代码解读复制代码
|
|
75
|
+
<!-- ProTable 中 tableHeader 插槽 -->
|
|
76
|
+
<slot name="tableHeader" :selected-list="selectedList" :selected-list-ids="selectedListIds" :is-selected="isSelected" />
|
|
77
|
+
|
|
78
|
+
<!-- 页面使用 -->
|
|
79
|
+
<template #tableHeader="scope">
|
|
80
|
+
<el-button type="primary" @click="openDrawer('新增')">新增用户</el-button>
|
|
81
|
+
</template>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3、表格功能按钮区域分析:
|
|
85
|
+
|
|
86
|
+
这块区域没什么特殊功能,只有四个按钮,其功能分别为:
|
|
87
|
+
|
|
88
|
+
> **表格数据刷新(一直会携带当前查询和分页条件)**、
|
|
89
|
+
>
|
|
90
|
+
> **表格全屏/退出全屏**、
|
|
91
|
+
>
|
|
92
|
+
> **表格列设置(列显隐、列排序)**、
|
|
93
|
+
>
|
|
94
|
+
> **表格搜索区域显隐**(方便展示更多的数据信息)**。 **
|
|
95
|
+
>
|
|
96
|
+
> 可通过 **toolButton** 属性控制这块区域的显隐。
|
|
97
|
+
|
|
98
|
+
### 4、表格主体内容展示区域分析:
|
|
99
|
+
|
|
100
|
+
> 🍉 该区域是最重要的数据展示区域,对于使用最多的功能就是表头和单元格内容可以自定义渲染,自定义表头只支持传入`renderHeader`方法,自定义单元格内容只支持`slot`插槽。
|
|
101
|
+
|
|
102
|
+
> 💥 表头支持`headerRender`方法(避免与 **el-table-column** 上的属性重名导致报错)、作用域插槽(`column.prop + 'Header'`)两种方式自定义,单元格内容支持`render`方法和作用域插槽(`column 上的 prop 属性`)两种方式自定义。
|
|
103
|
+
|
|
104
|
+
- 使用作用域插槽:
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
html代码解读复制代码
|
|
108
|
+
<!-- 使用作用域插槽自定义单元格内容 username -->
|
|
109
|
+
<template #username="scope">{{ scope.row.username }}</template>
|
|
110
|
+
|
|
111
|
+
<!-- 使用作用域插槽自定义表头内容 username -->
|
|
112
|
+
<template #usernameHeader="scope">
|
|
113
|
+
<el-button type="primary" @click="ElMessage.success('我是通过作用域插槽渲染的表头')">
|
|
114
|
+
{{ scope.column.label }}
|
|
115
|
+
</el-button>
|
|
116
|
+
</template>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- 使用 tsx 语法:
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
html代码解读复制代码
|
|
123
|
+
<script setup lang="tsx">
|
|
124
|
+
const columns = reactive<ColumnProps<User.ResUserList>[]>([
|
|
125
|
+
{
|
|
126
|
+
prop: "username",
|
|
127
|
+
label: "用户姓名",
|
|
128
|
+
// 使用 headerRender 自定义表头
|
|
129
|
+
headerRender: scope => {
|
|
130
|
+
return (
|
|
131
|
+
<el-button
|
|
132
|
+
type="primary"
|
|
133
|
+
onClick={() => {
|
|
134
|
+
ElMessage.success("我是通过 tsx 语法渲染的表头");
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
{scope.column.label}
|
|
138
|
+
</el-button>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
prop: "status",
|
|
144
|
+
label: "用户状态",
|
|
145
|
+
// 使用 render 自定义表格内容
|
|
146
|
+
render: scope => {
|
|
147
|
+
return (
|
|
148
|
+
<el-switch
|
|
149
|
+
model-value={scope.row.status}
|
|
150
|
+
active-text={scope.row.status ? "启用" : "禁用"}
|
|
151
|
+
active-value={1}
|
|
152
|
+
inactive-value={0}
|
|
153
|
+
onClick={() => changeStatus(scope.row)}
|
|
154
|
+
/>
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
</script>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 5、表格主体操作列展示分析:
|
|
164
|
+
|
|
165
|
+
> 🍉 该区域是每一列数据的操作区域,支持函数或者boolean控制按钮的展示隐藏,可以自动效验用户permission;自动插入click事件。
|
|
166
|
+
|
|
167
|
+
> 💥 操作项支持`JSON配置`、`render函数`、`自定义slot`方法,3种方式自定义;
|
|
168
|
+
|
|
169
|
+
- \*\*
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
html代码解读复制代码
|
|
173
|
+
<template>
|
|
174
|
+
<JztQueryTable>
|
|
175
|
+
<!-- 表格操作--插槽 -->
|
|
176
|
+
<template #resetPassSlot="row">{{ row.username }}</template>
|
|
177
|
+
<JztQueryTable>
|
|
178
|
+
</template>
|
|
179
|
+
|
|
180
|
+
<script setup lang="tsx" name="ProTable">
|
|
181
|
+
{
|
|
182
|
+
prop: 'operation',
|
|
183
|
+
label: '操作',
|
|
184
|
+
fixed: 'right',
|
|
185
|
+
width: 220,
|
|
186
|
+
btnLList: [
|
|
187
|
+
{
|
|
188
|
+
text: '重置密码',
|
|
189
|
+
hasPerm: 'table-edit',
|
|
190
|
+
fun: (row: any) => { resetPass(row) }
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
text: '重置',
|
|
194
|
+
hasPerm: 'table-reset',
|
|
195
|
+
// slot显示内容
|
|
196
|
+
slotName: 'resetPassSlot',
|
|
197
|
+
fun: (row: any) => { resetPass(row}
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'delete',
|
|
201
|
+
text: '删除',
|
|
202
|
+
type: 'danger',
|
|
203
|
+
hasPerm: 'table-delete',
|
|
204
|
+
// 自定义 render 显示内容
|
|
205
|
+
render: () => {
|
|
206
|
+
return <span style="color:red">删除</span>
|
|
207
|
+
},
|
|
208
|
+
fun: (row: any) => { deleteAccount(row) }
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
</script>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
> 💢💢💢 **最强大的功能:如果你想使用 `el-table` 的任何属性、事件,目前通过属性透传都能支持。**
|
|
216
|
+
>
|
|
217
|
+
> **如果你还不了解属性透传,请阅读 vue 官方文档:[cn.vuejs.org/guide/compo…](https://link.juejin.cn/?target=https%3A%2F%2Fcn.vuejs.org%2Fguide%2Fcomponents%2Fattrs.html)**
|
|
218
|
+
|
|
219
|
+
- **QueryTable 组件上的绑定的所有属性和事件都会通过 `v-bind="$attrs"` 透传到 el-table 上。**
|
|
220
|
+
- **QueryTable组件内部暴露了 el-table DOM,可通过 `QueryTable.value.element.方法名` 调用其方法。**
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
html代码解读复制代码
|
|
224
|
+
<template>
|
|
225
|
+
<el-table ref="tableRef" v-bind="$attrs"></el-table>
|
|
226
|
+
</template>
|
|
227
|
+
|
|
228
|
+
<script setup lang="ts" name="ProTable">
|
|
229
|
+
// import { ref } from 'vue'
|
|
230
|
+
import { ElTable } from 'element-plus'
|
|
231
|
+
|
|
232
|
+
const tableRef = ref<InstanceType<typeof ElTable>>()
|
|
233
|
+
|
|
234
|
+
defineExpose({ element: tableRef })
|
|
235
|
+
</script>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 6、表格分页区域分析:
|
|
239
|
+
|
|
240
|
+
> 分页区域也没有什么特殊的功能,该支持的都支持了🤣(页面上使用 QueryTable组件完全不存在分页逻辑)
|
|
241
|
+
|
|
242
|
+
## 三、QueryTable 文档 📚
|
|
243
|
+
|
|
244
|
+
### 1、QueryTable 属性(TableConfigProps):
|
|
245
|
+
|
|
246
|
+
> 使用 `v-bind="$attrs"` 通过属性透传将 **QueryTable ** 组件属性全部透传到 **el-table** 上,所以我们支持 **el-table** 的所有 **Props** 属性。在此基础上,还扩展了以下 **Props:**
|
|
247
|
+
|
|
248
|
+
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
|
|
249
|
+
| ------------ | ---------------- | -------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
250
|
+
| queryItems | QueryItemsProps | ✅ | [] | QueryTable组件会根据此字段渲染搜索表单QueryItemsProps |
|
|
251
|
+
| data | Array | ✅ | — | 静态 tableData 数据(支持分页),若存在则不会使用 requestApi 返回的 data |
|
|
252
|
+
| topList | Array | ✅ | | 表格功能按钮区域 |
|
|
253
|
+
| columns | Array | ✅ | | QueryTable组件会根据此字段渲染搜索表格列(支持动态更新),详情见 ColumnProps |
|
|
254
|
+
| requestApi | Function | ❌ | — | 获取表格数据的请求 API |
|
|
255
|
+
| requestAuto | Boolean | ❌ | true | 表格初始化时是否自动执行请求 API |
|
|
256
|
+
| requestError | Function | ❌ | — | 表格 API 请求错误监听 |
|
|
257
|
+
| dataCallback | Function | ❌ | — | 后台返回数据的回调函数,可对后台返回数据进行处理 |
|
|
258
|
+
| title | String | ❌ | — | 表格标题,目前没有用到 |
|
|
259
|
+
| pagination | Boolean | ❌ | true | 是否显示分页组件:pagination 为 false 后台返回数据应该没有分页信息 和 list 字段,data 就是 list 数据 |
|
|
260
|
+
| initParam | Object | ❌ | {} | 表格请求的初始化参数,该值变化会重新请求表格数据 |
|
|
261
|
+
| toolButton | Boolean \| Array | ❌ | true | 是否显示表格功能按钮,支持(["refresh" \| "setting" \| "search"]) |
|
|
262
|
+
| rowKey | String | ❌ | 'id' | 当表格数据多选时,所指定的 id |
|
|
263
|
+
| searchCol | Number \| Object | ❌ | { xs: 1, sm: 2, md: 3, lg: 4, xl: 5,xxl:6 } | 表格搜索项每列占比配置 |
|
|
264
|
+
|
|
265
|
+
### 2、QueryItems配置(QueryItemsProps):
|
|
266
|
+
|
|
267
|
+
> 使用 `v-bind="{ ...handleSearchProps, ...placeholder, searchParam: _searchParam, clearable }""` 通过属性透传将每一项 **column** 属性全部透传到 **el-table-column** 上,所以我们支持 **el-form 的所有 **Props\*\* 属性。
|
|
268
|
+
|
|
269
|
+
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
|
|
270
|
+
| ---------- | ----------------- | -------- | ------ | ----------------------------------------------------------------------------------------------- |
|
|
271
|
+
| label | String | ❌ | — | 对应表单的label |
|
|
272
|
+
| prop | Boolean | ❌ | false | 对应表单的prop ,查询接口所需要的字段名称 |
|
|
273
|
+
| isShow | Boolean | ❌ | true | 是否显示在表单当中 |
|
|
274
|
+
| search | SearchProps | ❌ | — | 搜索项配置,详情见 SearchProps |
|
|
275
|
+
| enum | Array \| Function | ❌ | — | 字典,可格式化单元格内容,还可以作为搜索框的下拉选项(字典可以为 API 请求函数,内部会自动执行) |
|
|
276
|
+
| fieldNames | Object | ❌ | — | 指定 label && value && children 的 key 值 |
|
|
277
|
+
| | | | | |
|
|
278
|
+
|
|
279
|
+
### 3、搜索项 配置(SearchProps):
|
|
280
|
+
|
|
281
|
+
> 使用 ` v-bind="{ ...handleSearchProps, ...placeholder, searchParam: _searchParam, clearable }"` 通过属性透传将 **search.props** 属性全部透传到每一项搜索组件上,所以我们支持:
|
|
282
|
+
>
|
|
283
|
+
> **'el-input'|'el-input-number'|'el-select'|'el-select-v2'|'el-tree-select'|'el-cascader'| 'el-date-picker'| 'el-time-picker' |'el-time-select'| 'el-switch'|'el-slider'**,大部分属性,并在其基础上还扩展了以下 **Props:**
|
|
284
|
+
|
|
285
|
+
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
|
|
286
|
+
| ------------ | -------- | -------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
287
|
+
| el | String | ❌ | — | 当前项搜索框的类型,支持:input、input-number、select、select-v2、tree-select、cascader、date-picker、time-picker、time-select、switch、slider |
|
|
288
|
+
| label | String | ❌ | — | 当搜索项 label,如果不指定默认取 column 的 label |
|
|
289
|
+
| props | Object | ❌ | — | 根据 element plus 官方文档来传递,该属性所有值会透传到组件 |
|
|
290
|
+
| key | String | ❌ | — | 当搜索项 key 不为 prop 属性时,可通过 key 指定 |
|
|
291
|
+
| tooltip | String | ❌ | — | 搜索提示 |
|
|
292
|
+
| order | Number | ❌ | — | 搜索项排序(从小到大) |
|
|
293
|
+
| span | Number | ❌ | 1 | 搜索项所占用的列数,默认为 1 列 |
|
|
294
|
+
| offset | Number | ❌ | — | 搜索字段左侧偏移列数 |
|
|
295
|
+
| defaultValue | Any | ❌ | — | 搜索项默认值(该值重置时会重置回初始值) |
|
|
296
|
+
| render | Function | ❌ | | 自定义搜索内容渲染(tsx 语法、h 语法) |
|
|
297
|
+
|
|
298
|
+
### 4、topList配置(TopButtonProps):
|
|
299
|
+
|
|
300
|
+
> \*\*
|
|
301
|
+
|
|
302
|
+
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
|
|
303
|
+
| ---------- | -------- | -------- | ------- | ----------------------------------------------------------- |
|
|
304
|
+
| id | String | ❌ | — | 唯一标识 |
|
|
305
|
+
| text | String | ❌ | — | 按钮的显示文字 |
|
|
306
|
+
| type | String | ❌ | default | ''\|primary'\|'success'\|'warning\|'danger'\|'info'\|'text' |
|
|
307
|
+
| hasPerm | String | ❌ | — | |
|
|
308
|
+
| loading | Boolean | ❌ | false | 是否loading |
|
|
309
|
+
| icon | String | ❌ | — | 按钮图标,支持element-plus内部的icon |
|
|
310
|
+
| fun | Function | ❌ | — | 点击函数 |
|
|
311
|
+
| hideFun | Function | ❌ | — | 隐藏函数 需要return Boolean |
|
|
312
|
+
| disabled | boolean | ❌ | false | 禁用 |
|
|
313
|
+
| disabledFn | Function | ❌ | — | 禁用函数 需要return Boolean |
|
|
314
|
+
| render | Function | ❌ | — | render函数 |
|
|
315
|
+
| ....... | ....... | ....... | ....... | 遵循el-button其他属性 |
|
|
316
|
+
| circle | false | ❌ | — | 是否为圆角按钮 |
|
|
317
|
+
| plain | boolean | ❌ | false | 是否为朴素按钮 |
|
|
318
|
+
| link | boolean | ❌ | false | 是否为链接按钮 |
|
|
319
|
+
| round | boolean | ❌ | false | |
|
|
320
|
+
| | | | | |
|
|
321
|
+
|
|
322
|
+
####
|
|
323
|
+
|
|
324
|
+
### 5、Column 配置(ColumnProps):
|
|
325
|
+
|
|
326
|
+
> 使用 `v-bind="column"` 通过属性透传将每一项 **column** 属性全部透传到 **el-table-column** 上,所以我们支持 **el-table-column** 的所有 **Props** 属性。在此基础上,还扩展了以下 **Props:**
|
|
327
|
+
|
|
328
|
+
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
|
|
329
|
+
| ------------ | ----------------- | -------- | ------ | ----------------------------------------------------------------------------------------------- |
|
|
330
|
+
| type | String | ❌ | — | 对应列的类型("index" \| "selection" \| "radio" \| "expand" \| "sort") |
|
|
331
|
+
| tag | Boolean | ❌ | false | 当前单元格值是否为标签展示,可通过 enum 数据中 tagType 字段指定 tag 类型 |
|
|
332
|
+
| isShow | Boolean | ❌ | true | 当前列是否显示在表格内,设置只搜索不展示在表格中(只对 prop 列生效) |
|
|
333
|
+
| isSetting | Boolean | ❌ | true | 当前列是否在 ColSetting 中可配置(只对 prop 列生效) |
|
|
334
|
+
| enum | Array \| Function | ❌ | — | 字典,可格式化单元格内容,还可以作为搜索框的下拉选项(字典可以为 API 请求函数,内部会自动执行) |
|
|
335
|
+
| isFilterEnum | Boolean | ❌ | true | 当前单元格值是否根据 enum 格式化(例如 enum 只作为搜索项数据,不参与内容格式化) |
|
|
336
|
+
| fieldNames | Object | | — | 指定 label && value && children 的 key 值 |
|
|
337
|
+
| render | Function | ❌ | — | 自定义单元格内容渲染(tsx 语法、h 语法) |
|
|
338
|
+
| \_children | ColumnProps | ❌ | — | 多级表头 |
|
|
339
|
+
| | | | | |
|
|
340
|
+
| | | | | |
|
|
341
|
+
|
|
342
|
+
#### 5.1 Column - operation 配置(TopButtonProps)
|
|
343
|
+
|
|
344
|
+
同4topList配置(TopButtonProps)
|
|
345
|
+
|
|
346
|
+
### 6、QueryTable 事件:
|
|
347
|
+
|
|
348
|
+
> 根据 **ElementPlus Table** 文档在 **QueryTable ** 组件上绑定事件即可,组件会通过 **$attrs** 透传给 **el-table**。
|
|
349
|
+
|
|
350
|
+
| 事件名 | 描述 | 回调参数 |
|
|
351
|
+
| -------- | ------------------------ | ---------------------- |
|
|
352
|
+
| dragSort | 表格拖拽排序成功后触发, | { newIndex, oldIndex } |
|
|
353
|
+
| search | 点击表格搜索按钮时会触发 | — |
|
|
354
|
+
| reset | 点击表格重置按钮时会触发 | — |
|
|
355
|
+
|
|
356
|
+
### 7、QueryTable 方法:
|
|
357
|
+
|
|
358
|
+
> **QueryTable ** 组件暴露了 **el-table** 实例和一些组件内部的参数和方法:
|
|
359
|
+
|
|
360
|
+
| 方法名 | 描述 |
|
|
361
|
+
| ------------------- | ------------------------------------------------------------------------------------- |
|
|
362
|
+
| element | `el-table` 实例,可以通过`QueryTable.value.element.***()`来调用 `el-table` 的所有方法 |
|
|
363
|
+
| tableData | 当前页面所展示的数据 |
|
|
364
|
+
| radio | 当前表格单选值(指定 type 为 "radio" 时可取到) |
|
|
365
|
+
| pageable | 当前表格的分页数据 |
|
|
366
|
+
| searchParam | 所有的搜索参数,不包含分页 |
|
|
367
|
+
| searchInitParam | 所有的搜索初始化默认的参数 |
|
|
368
|
+
| getTableList | 获取、刷新表格数据的方法(携带所有参数) |
|
|
369
|
+
| search | 表格查询方法,相当于点击搜索按钮 |
|
|
370
|
+
| reset | 重置表格查询参数,相当于点击重置按钮 |
|
|
371
|
+
| handleSizeChange | 表格每页条数改变触发的事件 |
|
|
372
|
+
| handleCurrentChange | 表格当前页改变触发的事件 |
|
|
373
|
+
| clearSelection | 清空表格所选择的数据,除此方法之外还可使用 `proTable.value.element.clearSelection()` |
|
|
374
|
+
| enumMap | 当前表格使用的所有字典数据(Map 数据结构) |
|
|
375
|
+
| isSelected | 表格是否选中数据 |
|
|
376
|
+
| selectedList | 表格选中的数据列表 |
|
|
377
|
+
| selectedListIds | 表格选中的数据列表的 id |
|
|
378
|
+
|
|
379
|
+
### 8、QueryTable 插槽:
|
|
380
|
+
|
|
381
|
+
| 插槽名 | 描述 |
|
|
382
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
383
|
+
| — | 默认插槽,支持直接在 QueryTable中写 el-table-column 标签 |
|
|
384
|
+
| tableHeader | 自定义表格头部左侧区域的插槽,一般情况该区域放操作按钮 |
|
|
385
|
+
| toolButton | 自定义表格头部左右侧侧功能区域的插槽 |
|
|
386
|
+
| append | 插入至表格最后一行之后的内容, 如果需要对表格的内容进行无限滚动操作,可能需要用到这个 slot。 若表格有合计行,该 slot 会位于合计行之上。 |
|
|
387
|
+
| empty | 当表格数据为空时自定义的内容 |
|
|
388
|
+
| pagination | 分页组件插槽 |
|
|
389
|
+
| `column.prop` | 单元格的作用域插槽 |
|
|
390
|
+
| `column.prop` + "Header" | 表头的作用域插槽 |
|
|
391
|
+
|
|
392
|
+
## 四、代码实现 & 基础使用 💪(代码较多,详情请去项目里查看)
|
|
393
|
+
|
|
394
|
+
> 🤔 **前提:在封装 ProTable 组件的时候,在不影响 el-table 原有的属性、事件、方法的前提下,然后在其基础上做二次封装。**
|
|
395
|
+
|
|
396
|
+
> 🧐 **思路:把一个表格页面所有重复的功能 (表格多选、查询、重置、刷新、分页、数据操作二次确认、文件下载、文件上传) 都封装成 Hooks 函数钩子或组件,然后在 QueryTable 组件中使用这些函数钩子或组件。在页面中使用的时,只需传给 QueryTable 当前表格数据的请求 API、表格配置项 columns 就行了,数据传输都使用 作用域插槽 或 tsx 语法从 ProTable 传递给父组件就能在页面上获取到了。**
|
|
397
|
+
|
|
398
|
+
### 1、常用 Hooks 函数
|
|
399
|
+
|
|
400
|
+
- **useTable:**
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
ts代码解读复制代码import { Table } from "./interface";
|
|
404
|
+
import { reactive, computed, toRefs } from "vue";
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @description table 页面操作方法封装
|
|
408
|
+
* @param {Function} api 获取表格数据 api 方法 (必传)
|
|
409
|
+
* @param {Object} initParam 获取数据初始化参数 (非必传,默认为{})
|
|
410
|
+
* @param {Boolean} isPageable 是否有分页 (非必传,默认为true)
|
|
411
|
+
* @param {Function} dataCallBack 对后台返回的数据进行处理的方法 (非必传)
|
|
412
|
+
* */
|
|
413
|
+
export const useTable = (
|
|
414
|
+
validateSearchForm?: () => Promise<any>,
|
|
415
|
+
api?: (params: any) => Promise<any>,
|
|
416
|
+
initParam: object = {},
|
|
417
|
+
isPageable: boolean = true,
|
|
418
|
+
dataCallBack?: (data: any) => any,
|
|
419
|
+
requestError?: (error: any) => void
|
|
420
|
+
) => {
|
|
421
|
+
const state = reactive<Table.StateProps>({
|
|
422
|
+
// 表格数据
|
|
423
|
+
tableData: [],
|
|
424
|
+
// 分页数据
|
|
425
|
+
pageable: {
|
|
426
|
+
// 当前页数
|
|
427
|
+
pageIndex: 1,
|
|
428
|
+
// 每页显示条数
|
|
429
|
+
pageSize: 10,
|
|
430
|
+
// 总条数
|
|
431
|
+
total: 0
|
|
432
|
+
},
|
|
433
|
+
// 查询参数(只包括查询)
|
|
434
|
+
searchParam: {},
|
|
435
|
+
// 初始化默认的查询参数
|
|
436
|
+
searchInitParam: {},
|
|
437
|
+
// 总参数(包含分页和查询参数)
|
|
438
|
+
totalParam: {}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* @description 分页查询参数(只包括分页和表格字段排序,其他排序方式可自行配置)
|
|
443
|
+
* */
|
|
444
|
+
const pageParam = computed({
|
|
445
|
+
get: () => {
|
|
446
|
+
return {
|
|
447
|
+
pageIndex: state.pageable.pageIndex,
|
|
448
|
+
pageSize: state.pageable.pageSize
|
|
449
|
+
};
|
|
450
|
+
},
|
|
451
|
+
set: (newVal: any) => {
|
|
452
|
+
console.log("我是分页更新之后的值", newVal);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @description 获取表格数据
|
|
458
|
+
* @return void
|
|
459
|
+
* */
|
|
460
|
+
const getTableList = async () => {
|
|
461
|
+
if (!api) return;
|
|
462
|
+
// 效验查询部分的必填项
|
|
463
|
+
if (validateSearchForm) {
|
|
464
|
+
const valid = await validateSearchForm()
|
|
465
|
+
if (!valid) return
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
// 先把初始化参数和分页参数放到总参数里面
|
|
469
|
+
Object.assign(state.totalParam, initParam, isPageable ? pageParam.value : {});
|
|
470
|
+
let { data } = await api({ ...state.searchInitParam, ...state.totalParam });
|
|
471
|
+
dataCallBack && (data = dataCallBack(data));
|
|
472
|
+
state.tableData = isPageable ? data.list : data;
|
|
473
|
+
// 解构后台返回的分页数据 (如果有分页更新分页信息)
|
|
474
|
+
if (isPageable) {
|
|
475
|
+
state.pageable.total = data.total;
|
|
476
|
+
}
|
|
477
|
+
} catch (error) {
|
|
478
|
+
requestError && requestError(error);
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* @description 更新查询参数
|
|
484
|
+
* @return void
|
|
485
|
+
* */
|
|
486
|
+
const updatedTotalParam = () => {
|
|
487
|
+
state.totalParam = {};
|
|
488
|
+
// 处理查询参数,可以给查询参数加自定义前缀操作
|
|
489
|
+
let nowSearchParam: Table.StateProps["searchParam"] = {};
|
|
490
|
+
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
|
|
491
|
+
for (let key in state.searchParam) {
|
|
492
|
+
// 某些情况下参数为 false/0 也应该携带参数
|
|
493
|
+
if (state.searchParam[key] || state.searchParam[key] === false || state.searchParam[key] === 0) {
|
|
494
|
+
nowSearchParam[key] = state.searchParam[key];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
Object.assign(state.totalParam, nowSearchParam, isPageable ? pageParam.value : {});
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* @description 表格数据查询
|
|
502
|
+
* @return void
|
|
503
|
+
* */
|
|
504
|
+
const search = () => {
|
|
505
|
+
state.pageable.pageIndex = 1;
|
|
506
|
+
updatedTotalParam();
|
|
507
|
+
getTableList();
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* @description 表格数据重置
|
|
512
|
+
* @return void
|
|
513
|
+
* */
|
|
514
|
+
const reset = () => {
|
|
515
|
+
state.pageable.pageIndex = 1;
|
|
516
|
+
// 重置搜索表单的时,如果有默认搜索参数,则重置默认的搜索参数
|
|
517
|
+
state.searchParam = { ...state.searchInitParam };
|
|
518
|
+
updatedTotalParam();
|
|
519
|
+
getTableList();
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* @description 每页条数改变
|
|
524
|
+
* @param {Number} val 当前条数
|
|
525
|
+
* @return void
|
|
526
|
+
* */
|
|
527
|
+
const handleSizeChange = (val: number) => {
|
|
528
|
+
state.pageable.pageIndex = 1;
|
|
529
|
+
state.pageable.pageSize = val;
|
|
530
|
+
getTableList();
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* @description 当前页改变
|
|
535
|
+
* @param {Number} val 当前页
|
|
536
|
+
* @return void
|
|
537
|
+
* */
|
|
538
|
+
const handleCurrentChange = (val: number) => {
|
|
539
|
+
state.pageable.pageIndex = val;
|
|
540
|
+
getTableList();
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
...toRefs(state),
|
|
545
|
+
getTableList,
|
|
546
|
+
search,
|
|
547
|
+
reset,
|
|
548
|
+
handleSizeChange,
|
|
549
|
+
handleCurrentChange,
|
|
550
|
+
updatedTotalParam
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
- **useSelection:**
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
ts代码解读复制代码import { ref, computed } from "vue";
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* @description 表格多选数据操作
|
|
562
|
+
* @param {String} rowKey 当表格可以多选时,所指定的 id
|
|
563
|
+
* */
|
|
564
|
+
export const useSelection = (rowKey: string = "id") => {
|
|
565
|
+
const isSelected = ref<boolean>(false);
|
|
566
|
+
const selectedList = ref<{ [key: string]: any }[]>([]);
|
|
567
|
+
|
|
568
|
+
// 当前选中的所有 ids 数组
|
|
569
|
+
const selectedListIds = computed((): string[] => {
|
|
570
|
+
let ids: string[] = [];
|
|
571
|
+
selectedList.value.forEach(item => ids.push(item[rowKey]));
|
|
572
|
+
return ids;
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* @description 多选操作
|
|
577
|
+
* @param {Array} rowArr 当前选择的所有数据
|
|
578
|
+
* @return void
|
|
579
|
+
*/
|
|
580
|
+
const selectionChange = (rowArr: { [key: string]: any }[]) => {
|
|
581
|
+
rowArr.length ? (isSelected.value = true) : (isSelected.value = false);
|
|
582
|
+
selectedList.value = rowArr;
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
isSelected,
|
|
587
|
+
selectedList,
|
|
588
|
+
selectedListIds,
|
|
589
|
+
selectionChange
|
|
590
|
+
};
|
|
591
|
+
};
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
- **useDownload:**
|
|
595
|
+
|
|
596
|
+
```ts
|
|
597
|
+
ts代码解读复制代码import { ElNotification } from "element-plus";
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* @description 接收数据流生成 blob,创建链接,下载文件
|
|
601
|
+
* @param {Function} api 导出表格的api方法 (必传)
|
|
602
|
+
* @param {String} tempName 导出的文件名 (必传)
|
|
603
|
+
* @param {Object} params 导出的参数 (默认{})
|
|
604
|
+
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
|
|
605
|
+
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
|
|
606
|
+
* */
|
|
607
|
+
export const useDownload = async (
|
|
608
|
+
api: (param: any) => Promise<any>,
|
|
609
|
+
tempName: string,
|
|
610
|
+
params: any = {},
|
|
611
|
+
isNotify: boolean = true,
|
|
612
|
+
fileType: string = ".xlsx"
|
|
613
|
+
) => {
|
|
614
|
+
if (isNotify) {
|
|
615
|
+
ElNotification({
|
|
616
|
+
title: "温馨提示",
|
|
617
|
+
message: "如果数据庞大会导致下载缓慢哦,请您耐心等待!",
|
|
618
|
+
type: "info",
|
|
619
|
+
duration: 3000
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
const res = await api(params);
|
|
624
|
+
const blob = new Blob([res]);
|
|
625
|
+
// 兼容 edge 不支持 createObjectURL 方法
|
|
626
|
+
if ("msSaveOrOpenBlob" in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType);
|
|
627
|
+
const blobUrl = window.URL.createObjectURL(blob);
|
|
628
|
+
const exportFile = document.createElement("a");
|
|
629
|
+
exportFile.style.display = "none";
|
|
630
|
+
exportFile.download = `${tempName}${fileType}`;
|
|
631
|
+
exportFile.href = blobUrl;
|
|
632
|
+
document.body.appendChild(exportFile);
|
|
633
|
+
exportFile.click();
|
|
634
|
+
// 去除下载对 url 的影响
|
|
635
|
+
document.body.removeChild(exportFile);
|
|
636
|
+
window.URL.revokeObjectURL(blobUrl);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
console.log(error);
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
- **useHandleData:**
|
|
644
|
+
|
|
645
|
+
```ts
|
|
646
|
+
ts代码解读复制代码import { ElMessageBox, ElMessage } from "element-plus";
|
|
647
|
+
import { HandleData } from "./interface";
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* @description 操作单条数据信息 (二次确认【删除、禁用、启用、重置密码】)
|
|
651
|
+
* @param {Function} api 操作数据接口的api方法 (必传)
|
|
652
|
+
* @param {Object} params 携带的操作数据参数 {id,params} (必传)
|
|
653
|
+
* @param {String} message 提示信息 (必传)
|
|
654
|
+
* @param {String} confirmType icon类型 (不必传,默认为 warning)
|
|
655
|
+
* @returns {Promise}
|
|
656
|
+
*/
|
|
657
|
+
export const useHandleData = (
|
|
658
|
+
api: (params: any) => Promise<any>,
|
|
659
|
+
params: any = {},
|
|
660
|
+
message: string,
|
|
661
|
+
confirmType: HandleData.MessageType = "warning"
|
|
662
|
+
) => {
|
|
663
|
+
return new Promise((resolve, reject) => {
|
|
664
|
+
ElMessageBox.confirm(`是否${message}?`, "温馨提示", {
|
|
665
|
+
confirmButtonText: "确定",
|
|
666
|
+
cancelButtonText: "取消",
|
|
667
|
+
type: confirmType,
|
|
668
|
+
draggable: true
|
|
669
|
+
}).then(async () => {
|
|
670
|
+
const res = await api(params);
|
|
671
|
+
if (!res) return reject(false);
|
|
672
|
+
ElMessage({
|
|
673
|
+
type: "success",
|
|
674
|
+
message: `${message}成功!`
|
|
675
|
+
});
|
|
676
|
+
resolve(true);
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
};
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### 2、页面使用 QueryTable 组件:
|
|
683
|
+
|
|
684
|
+
#### 2.1 基础页面使用
|
|
685
|
+
|
|
686
|
+
```html
|
|
687
|
+
<template>
|
|
688
|
+
<div class="table-box">
|
|
689
|
+
<query-table
|
|
690
|
+
ref="proTable"
|
|
691
|
+
:request-auto="false"
|
|
692
|
+
:column-config="columnConfig"
|
|
693
|
+
:table-config="tableConfigOptions"
|
|
694
|
+
:request-api="getTableList"
|
|
695
|
+
:init-param="initParam"
|
|
696
|
+
:data-callback="dataCallback"
|
|
697
|
+
@drag-sort="sortTable"
|
|
698
|
+
>
|
|
699
|
+
<!-- 表格 header 按钮 -->
|
|
700
|
+
<template #tableHeader="scope">
|
|
701
|
+
<el-button @click="removeData(scope)">自定义按钮</el-button>
|
|
702
|
+
</template>
|
|
703
|
+
<!-- Expand -->
|
|
704
|
+
<template #expand="scope">{{ scope.row }}</template>
|
|
705
|
+
<template #usernameHeader="scope">
|
|
706
|
+
<el-button type="primary" @click="ElMessage.success('我是通过作用域插槽渲染的表头')">
|
|
707
|
+
{{ scope.column.label }}
|
|
708
|
+
</el-button>
|
|
709
|
+
</template>
|
|
710
|
+
<!-- createTime -->
|
|
711
|
+
<template #createTime="scope">
|
|
712
|
+
<el-button type="primary" link @click="ElMessage.success('我是通过作用域插槽渲染的内容')">
|
|
713
|
+
{{ scope.row.createTime }}
|
|
714
|
+
</el-button>
|
|
715
|
+
</template>
|
|
716
|
+
</query-table>
|
|
717
|
+
<dialogForm ref="dialogFormRef" />
|
|
718
|
+
</div>
|
|
719
|
+
</template>
|
|
720
|
+
|
|
721
|
+
<script setup lang="tsx" name="UseQueryTable">
|
|
722
|
+
// import { ref, reactive } from 'vue'
|
|
723
|
+
import { ElMessage } from 'element-plus'
|
|
724
|
+
import { ProTableInstance, TableConfigProps } from '@/components/QueryTable/interface'
|
|
725
|
+
import { getUserList, getUserGender, getUserStatus } from '@/api/modules/user'
|
|
726
|
+
import QueryTable from '@/components/QueryTable/index.vue'
|
|
727
|
+
import { genderType } from '@/utils/dict'
|
|
728
|
+
import dialogForm from './dialogForm.vue'
|
|
729
|
+
|
|
730
|
+
const dialogFormRef = ref()
|
|
731
|
+
|
|
732
|
+
// ProTable 实例
|
|
733
|
+
const proTable = ref<ProTableInstance>()
|
|
734
|
+
|
|
735
|
+
// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
|
736
|
+
const initParam = reactive({ type: 1 })
|
|
737
|
+
// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
|
|
738
|
+
// 或者直接去 hooks/useQueryTable.ts 文件中把字段改为你后端对应的就行
|
|
739
|
+
const dataCallback = (data: any) => {
|
|
740
|
+
return {
|
|
741
|
+
list: data.list,
|
|
742
|
+
total: data.total
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// 模拟远程加载性别搜索框数据
|
|
747
|
+
const loading = ref(false)
|
|
748
|
+
const filterGenderEnum = ref<typeof genderType>([])
|
|
749
|
+
const remoteMethod = (query: string) => {
|
|
750
|
+
filterGenderEnum.value = []
|
|
751
|
+
if (!query) return
|
|
752
|
+
loading.value = true
|
|
753
|
+
setTimeout(() => {
|
|
754
|
+
loading.value = false
|
|
755
|
+
filterGenderEnum.value = genderType.filter(item => item.label.includes(query))
|
|
756
|
+
}, 500)
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const getTableList = (params: any) => {
|
|
760
|
+
console.log(params)
|
|
761
|
+
return getUserList(params)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const columnConfig = {
|
|
765
|
+
layout: 'selection,sort',
|
|
766
|
+
selectionProps: {
|
|
767
|
+
fixed: 'left',
|
|
768
|
+
width: 70
|
|
769
|
+
},
|
|
770
|
+
sortProps: {
|
|
771
|
+
label: 'Sort'
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
const tableConfigOptions = reactive<TableConfigProps>({
|
|
775
|
+
// 查询配置项
|
|
776
|
+
queryItems: [
|
|
777
|
+
{
|
|
778
|
+
prop: 'username',
|
|
779
|
+
label: '用户姓名',
|
|
780
|
+
search: { el: 'el-input', tooltip: '我是搜索提示' }
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
prop: 'remoteGender',
|
|
784
|
+
label: '远程性别',
|
|
785
|
+
enum: filterGenderEnum,
|
|
786
|
+
search: {
|
|
787
|
+
el: 'el-select',
|
|
788
|
+
props: {
|
|
789
|
+
placeholder: '请输入性别查询',
|
|
790
|
+
filterable: true,
|
|
791
|
+
remote: true,
|
|
792
|
+
reserveKeyword: true,
|
|
793
|
+
loading,
|
|
794
|
+
remoteMethod
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
render: scope => <>{scope.row.gender === 1 ? '男' : '女'}</>
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
prop: 'gender',
|
|
801
|
+
label: '性别',
|
|
802
|
+
// 字典数据(本地数据)
|
|
803
|
+
// enum: genderType,
|
|
804
|
+
// 字典请求不带参数
|
|
805
|
+
enum: getUserGender,
|
|
806
|
+
// 字典请求携带参数
|
|
807
|
+
// enum: () => getUserGender({ id: 1 }),
|
|
808
|
+
search: { el: 'el-select', props: { filterable: true } },
|
|
809
|
+
fieldNames: { label: 'genderLabel', value: 'genderValue' },
|
|
810
|
+
rules: [{ required: true, message: '性别不能为空', trigger: 'blur' }]
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
// 多级 prop
|
|
814
|
+
prop: 'age',
|
|
815
|
+
label: '年龄',
|
|
816
|
+
search: {
|
|
817
|
+
// 自定义 search 显示内容
|
|
818
|
+
render: ({ searchParam }) => {
|
|
819
|
+
return (
|
|
820
|
+
<div class="flx-center">
|
|
821
|
+
<el-input vModel_trim={searchParam.minAge} placeholder="最小年龄" />
|
|
822
|
+
<span class="mr10 ml10">-</span>
|
|
823
|
+
<el-input vModel_trim={searchParam.maxAge} placeholder="最大年龄" />
|
|
824
|
+
</div>
|
|
825
|
+
)
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
{ prop: 'idCard', label: '身份证号', search: { el: 'el-input' } },
|
|
830
|
+
{
|
|
831
|
+
prop: 'status',
|
|
832
|
+
label: '用户状态',
|
|
833
|
+
enum: getUserStatus,
|
|
834
|
+
search: { el: 'el-tree-select', props: { filterable: true } },
|
|
835
|
+
fieldNames: { label: 'userLabel', value: 'userStatus' }
|
|
836
|
+
},
|
|
837
|
+
|
|
838
|
+
{
|
|
839
|
+
prop: 'createTime',
|
|
840
|
+
label: '创建时间',
|
|
841
|
+
search: {
|
|
842
|
+
el: 'el-date-picker',
|
|
843
|
+
span: 2,
|
|
844
|
+
props: { type: 'datetimerange', valueFormat: 'YYYY-MM-DD HH:mm:ss' },
|
|
845
|
+
defaultValue: ['2022-11-12 11:35:00', '2022-12-12 11:35:00']
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
],
|
|
849
|
+
// 表格头部按钮配置项
|
|
850
|
+
topList: [
|
|
851
|
+
{
|
|
852
|
+
text: '打开弹框',
|
|
853
|
+
hasPerm: 'add',
|
|
854
|
+
type: 'primary',
|
|
855
|
+
fun: () => {
|
|
856
|
+
dialogFormRef.value.openDialog()
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
text: '导出',
|
|
861
|
+
hasPerm: 'export',
|
|
862
|
+
plain: true,
|
|
863
|
+
fun: () => {
|
|
864
|
+
console.log('新增')
|
|
865
|
+
// addDialog({
|
|
866
|
+
// title: "用户详情",
|
|
867
|
+
// fullscreen: false,
|
|
868
|
+
// hideFooter: false,
|
|
869
|
+
// showConfirm: false,
|
|
870
|
+
// showCancel: false,
|
|
871
|
+
// // confirmText: '1232',
|
|
872
|
+
// // cancelText: '333',
|
|
873
|
+
// contentRenderer: () => <Detail />, //<div>12222</div>,
|
|
874
|
+
// props: {
|
|
875
|
+
// formInline: {
|
|
876
|
+
// user: 123,
|
|
877
|
+
// name: "admin"
|
|
878
|
+
// }
|
|
879
|
+
// },
|
|
880
|
+
// width: "50%",
|
|
881
|
+
// draggable: true,
|
|
882
|
+
// fullscreenIcon: true,
|
|
883
|
+
// closeOnClickModal: false,
|
|
884
|
+
// beforeSure: (done, { options }) => {
|
|
885
|
+
// function chores() {
|
|
886
|
+
// message(`您修改了数据`, {
|
|
887
|
+
// type: "success"
|
|
888
|
+
// });
|
|
889
|
+
// done(); // 关闭弹框
|
|
890
|
+
// }
|
|
891
|
+
// chores();
|
|
892
|
+
// }
|
|
893
|
+
// });
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
text: '批量删除',
|
|
898
|
+
hasPerm: 'batch-add',
|
|
899
|
+
type: 'danger',
|
|
900
|
+
plain: true,
|
|
901
|
+
fun: scope => {
|
|
902
|
+
console.log('新建字典', scope)
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
],
|
|
906
|
+
// 表格配置项
|
|
907
|
+
columns: [
|
|
908
|
+
{
|
|
909
|
+
prop: 'id',
|
|
910
|
+
label: '用户编号',
|
|
911
|
+
isShow: false
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
prop: 'username',
|
|
915
|
+
label: '用户名称',
|
|
916
|
+
width: 150,
|
|
917
|
+
align: 'left'
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
prop: 'avatar',
|
|
921
|
+
label: '用户头像',
|
|
922
|
+
isSetting: true,
|
|
923
|
+
render: scope => {
|
|
924
|
+
return (
|
|
925
|
+
<>
|
|
926
|
+
<img src={scope.row.avatar} alt="" width="50px" height="50px" />
|
|
927
|
+
</>
|
|
928
|
+
)
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
prop: 'nickname',
|
|
933
|
+
label: '用户昵称'
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
prop: 'phone',
|
|
937
|
+
label: '部门'
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
prop: 'sex',
|
|
941
|
+
label: '性别',
|
|
942
|
+
render: scope => {
|
|
943
|
+
return (
|
|
944
|
+
<>
|
|
945
|
+
<el-tag type={!!scope.row.sex ? 'success' : 'danger'}>{!!scope.row.sex ? '男' : '女'}</el-tag>
|
|
946
|
+
</>
|
|
947
|
+
)
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
prop: 'status',
|
|
952
|
+
label: '用户状态',
|
|
953
|
+
render: scope => {
|
|
954
|
+
return (
|
|
955
|
+
<>
|
|
956
|
+
<el-tag type={scope.row.status ? 'success' : 'danger'}>{scope.row.status ? '启用' : '禁用'}</el-tag>
|
|
957
|
+
</>
|
|
958
|
+
)
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
prop: 'createTime',
|
|
963
|
+
label: '创建时间',
|
|
964
|
+
width: 180
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
prop: 'operation',
|
|
968
|
+
label: '操作',
|
|
969
|
+
fixed: 'right',
|
|
970
|
+
width: 170,
|
|
971
|
+
btnLList: [
|
|
972
|
+
{
|
|
973
|
+
text: '编辑',
|
|
974
|
+
hasPerm: 'table-edit',
|
|
975
|
+
fun: (row: any) => {
|
|
976
|
+
console.log(row)
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
id: 'delete',
|
|
981
|
+
text: '删除',
|
|
982
|
+
type: 'danger',
|
|
983
|
+
fun: (row: any) => {
|
|
984
|
+
console.log(row)
|
|
985
|
+
// deleteByIdFn(row.setId);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
]
|
|
989
|
+
}
|
|
990
|
+
]
|
|
991
|
+
})
|
|
992
|
+
// 表格拖拽排序
|
|
993
|
+
const sortTable = ({ newIndex, oldIndex }: { newIndex?: number; oldIndex?: number }) => {
|
|
994
|
+
console.log(newIndex, oldIndex)
|
|
995
|
+
console.log(proTable.value?.tableData)
|
|
996
|
+
ElMessage.success('修改列表排序成功')
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
const removeData = data => {
|
|
1000
|
+
console.log(data)
|
|
1001
|
+
}
|
|
1002
|
+
</script>
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
```html
|
|
1006
|
+
html代码解读复制代码
|
|
1007
|
+
<template>
|
|
1008
|
+
<div class="table-box">
|
|
1009
|
+
<ProTable
|
|
1010
|
+
ref="proTable"
|
|
1011
|
+
:columns="columns"
|
|
1012
|
+
:request-api="getTableList"
|
|
1013
|
+
:init-param="initParam"
|
|
1014
|
+
:data-callback="dataCallback"
|
|
1015
|
+
@drag-sort="sortTable"
|
|
1016
|
+
>
|
|
1017
|
+
<!-- 表格 header 按钮 -->
|
|
1018
|
+
<template #tableHeader="scope">
|
|
1019
|
+
<el-button v-auth="'add'" type="primary" :icon="CirclePlus" @click="openDrawer('新增')">新增用户</el-button>
|
|
1020
|
+
<el-button v-auth="'batchAdd'" type="primary" :icon="Upload" plain @click="batchAdd">批量添加用户</el-button>
|
|
1021
|
+
<el-button v-auth="'export'" type="primary" :icon="Download" plain @click="downloadFile">
|
|
1022
|
+
导出用户数据
|
|
1023
|
+
</el-button>
|
|
1024
|
+
<el-button type="primary" plain @click="toDetail">To 子集详情页面</el-button>
|
|
1025
|
+
<el-button
|
|
1026
|
+
type="danger"
|
|
1027
|
+
:icon="Delete"
|
|
1028
|
+
plain
|
|
1029
|
+
:disabled="!scope.isSelected"
|
|
1030
|
+
@click="batchDelete(scope.selectedListIds)"
|
|
1031
|
+
>
|
|
1032
|
+
批量删除用户
|
|
1033
|
+
</el-button>
|
|
1034
|
+
</template>
|
|
1035
|
+
<!-- Expand -->
|
|
1036
|
+
<template #expand="scope">{{ scope.row }}</template>
|
|
1037
|
+
<!-- usernameHeader -->
|
|
1038
|
+
<template #usernameHeader="scope">
|
|
1039
|
+
<el-button type="primary" @click="ElMessage.success('我是通过作用域插槽渲染的表头')">
|
|
1040
|
+
{{ scope.column.label }}
|
|
1041
|
+
</el-button>
|
|
1042
|
+
</template>
|
|
1043
|
+
<!-- createTime -->
|
|
1044
|
+
<template #createTime="scope">
|
|
1045
|
+
<el-button type="primary" link @click="ElMessage.success('我是通过作用域插槽渲染的内容')">
|
|
1046
|
+
{{ scope.row.createTime }}
|
|
1047
|
+
</el-button>
|
|
1048
|
+
</template>
|
|
1049
|
+
<!-- 表格操作 -->
|
|
1050
|
+
<template #operation="scope">
|
|
1051
|
+
<el-button type="primary" link :icon="View" @click="openDrawer('查看', scope.row)">查看</el-button>
|
|
1052
|
+
<el-button type="primary" link :icon="EditPen" @click="openDrawer('编辑', scope.row)">编辑</el-button>
|
|
1053
|
+
<el-button type="primary" link :icon="Refresh" @click="resetPass(scope.row)">重置密码</el-button>
|
|
1054
|
+
<el-button type="primary" link :icon="Delete" @click="deleteAccount(scope.row)">删除</el-button>
|
|
1055
|
+
</template>
|
|
1056
|
+
</ProTable>
|
|
1057
|
+
<UserDrawer ref="drawerRef" />
|
|
1058
|
+
<ImportExcel ref="dialogRef" />
|
|
1059
|
+
</div>
|
|
1060
|
+
</template>
|
|
1061
|
+
|
|
1062
|
+
<script setup lang="tsx" name="useProTable">
|
|
1063
|
+
import { ref, reactive } from 'vue'
|
|
1064
|
+
import { useRouter } from 'vue-router'
|
|
1065
|
+
import { User } from '@/api/interface'
|
|
1066
|
+
import { useHandleData } from '@/hooks/useHandleData'
|
|
1067
|
+
import { useDownload } from '@/hooks/useDownload'
|
|
1068
|
+
import { useAuthButtons } from '@/hooks/useAuthButtons'
|
|
1069
|
+
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
1070
|
+
import ProTable from '@/components/ProTable/index.vue'
|
|
1071
|
+
import ImportExcel from '@/components/ImportExcel/index.vue'
|
|
1072
|
+
import UserDrawer from '@/views/proTable/components/UserDrawer.vue'
|
|
1073
|
+
import { ProTableInstance, ColumnProps, HeaderRenderScope } from '@/components/ProTable/interface'
|
|
1074
|
+
import { CirclePlus, Delete, EditPen, Download, Upload, View, Refresh } from '@element-plus/icons-vue'
|
|
1075
|
+
import {
|
|
1076
|
+
getUserList,
|
|
1077
|
+
deleteUser,
|
|
1078
|
+
editUser,
|
|
1079
|
+
addUser,
|
|
1080
|
+
changeUserStatus,
|
|
1081
|
+
resetUserPassWord,
|
|
1082
|
+
exportUserInfo,
|
|
1083
|
+
BatchAddUser,
|
|
1084
|
+
getUserStatus,
|
|
1085
|
+
getUserGender
|
|
1086
|
+
} from '@/api/modules/user'
|
|
1087
|
+
|
|
1088
|
+
const router = useRouter()
|
|
1089
|
+
|
|
1090
|
+
// 跳转详情页
|
|
1091
|
+
const toDetail = () => {
|
|
1092
|
+
router.push(`/proTable/useProTable/detail/${Math.random().toFixed(3)}?params=detail-page`)
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// ProTable 实例
|
|
1096
|
+
const proTable = ref<ProTableInstance>()
|
|
1097
|
+
|
|
1098
|
+
// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
|
1099
|
+
const initParam = reactive({ type: 1 })
|
|
1100
|
+
|
|
1101
|
+
// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
|
|
1102
|
+
// 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
|
|
1103
|
+
const dataCallback = (data: any) => {
|
|
1104
|
+
return {
|
|
1105
|
+
list: data.list,
|
|
1106
|
+
total: data.total
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// 如果你想在请求之前对当前请求参数做一些操作,可以自定义如下函数:params 为当前所有的请求参数(包括分页),最后返回请求列表接口
|
|
1111
|
+
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
|
|
1112
|
+
const getTableList = (params: any) => {
|
|
1113
|
+
let newParams = JSON.parse(JSON.stringify(params))
|
|
1114
|
+
newParams.createTime && (newParams.startTime = newParams.createTime[0])
|
|
1115
|
+
newParams.createTime && (newParams.endTime = newParams.createTime[1])
|
|
1116
|
+
delete newParams.createTime
|
|
1117
|
+
return getUserList(newParams)
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// 页面按钮权限(按钮权限既可以使用 hooks,也可以直接使用 v-auth 指令,指令适合直接绑定在按钮上,hooks 适合根据按钮权限显示不同的内容)
|
|
1121
|
+
const { BUTTONS } = useAuthButtons()
|
|
1122
|
+
|
|
1123
|
+
// 自定义渲染表头(使用tsx语法)
|
|
1124
|
+
const headerRender = (scope: HeaderRenderScope<User.ResUserList>) => {
|
|
1125
|
+
return (
|
|
1126
|
+
<el-button type="primary" onClick={() => ElMessage.success('我是通过 tsx 语法渲染的表头')}>
|
|
1127
|
+
{scope.column.label}
|
|
1128
|
+
</el-button>
|
|
1129
|
+
)
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// 表格配置项
|
|
1133
|
+
const columns = reactive<ColumnProps<User.ResUserList>[]>([
|
|
1134
|
+
{ type: 'selection', fixed: 'left', width: 70 },
|
|
1135
|
+
{ type: 'sort', label: 'Sort', width: 80 },
|
|
1136
|
+
{ type: 'expand', label: 'Expand', width: 85 },
|
|
1137
|
+
{
|
|
1138
|
+
prop: 'username',
|
|
1139
|
+
label: '用户姓名',
|
|
1140
|
+
search: { el: 'input', tooltip: '我是搜索提示' },
|
|
1141
|
+
render: scope => {
|
|
1142
|
+
return (
|
|
1143
|
+
<el-button type="primary" link onClick={() => ElMessage.success('我是通过 tsx 语法渲染的内容')}>
|
|
1144
|
+
{scope.row.username}
|
|
1145
|
+
</el-button>
|
|
1146
|
+
)
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
prop: 'gender',
|
|
1151
|
+
label: '性别',
|
|
1152
|
+
// 字典数据(本地数据)
|
|
1153
|
+
// enum: genderType,
|
|
1154
|
+
// 字典请求不带参数
|
|
1155
|
+
enum: getUserGender,
|
|
1156
|
+
// 字典请求携带参数
|
|
1157
|
+
// enum: () => getUserGender({ id: 1 }),
|
|
1158
|
+
search: { el: 'select', props: { filterable: true } },
|
|
1159
|
+
fieldNames: { label: 'genderLabel', value: 'genderValue' }
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
// 多级 prop
|
|
1163
|
+
prop: 'user.detail.age',
|
|
1164
|
+
label: '年龄',
|
|
1165
|
+
search: {
|
|
1166
|
+
// 自定义 search 显示内容
|
|
1167
|
+
render: ({ searchParam }) => {
|
|
1168
|
+
return (
|
|
1169
|
+
<div class="flx-center">
|
|
1170
|
+
<el-input vModel_trim={searchParam.minAge} placeholder="最小年龄" />
|
|
1171
|
+
<span class="mr10 ml10">-</span>
|
|
1172
|
+
<el-input vModel_trim={searchParam.maxAge} placeholder="最大年龄" />
|
|
1173
|
+
</div>
|
|
1174
|
+
)
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
},
|
|
1178
|
+
{ prop: 'idCard', label: '身份证号', search: { el: 'input' } },
|
|
1179
|
+
{ prop: 'email', label: '邮箱' },
|
|
1180
|
+
{ prop: 'address', label: '居住地址' },
|
|
1181
|
+
{
|
|
1182
|
+
prop: 'status',
|
|
1183
|
+
label: '用户状态',
|
|
1184
|
+
enum: getUserStatus,
|
|
1185
|
+
search: { el: 'tree-select', props: { filterable: true } },
|
|
1186
|
+
fieldNames: { label: 'userLabel', value: 'userStatus' },
|
|
1187
|
+
render: scope => {
|
|
1188
|
+
return (
|
|
1189
|
+
<>
|
|
1190
|
+
{BUTTONS.value.status ? (
|
|
1191
|
+
<el-switch
|
|
1192
|
+
model-value={scope.row.status}
|
|
1193
|
+
active-text={scope.row.status ? '启用' : '禁用'}
|
|
1194
|
+
active-value={1}
|
|
1195
|
+
inactive-value={0}
|
|
1196
|
+
onClick={() => changeStatus(scope.row)}
|
|
1197
|
+
/>
|
|
1198
|
+
) : (
|
|
1199
|
+
<el-tag type={scope.row.status ? 'success' : 'danger'}>{scope.row.status ? '启用' : '禁用'}</el-tag>
|
|
1200
|
+
)}
|
|
1201
|
+
</>
|
|
1202
|
+
)
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
prop: 'createTime',
|
|
1207
|
+
label: '创建时间',
|
|
1208
|
+
headerRender,
|
|
1209
|
+
width: 180,
|
|
1210
|
+
search: {
|
|
1211
|
+
el: 'date-picker',
|
|
1212
|
+
span: 2,
|
|
1213
|
+
props: { type: 'datetimerange', valueFormat: 'YYYY-MM-DD HH:mm:ss' },
|
|
1214
|
+
defaultValue: ['2022-11-12 11:35:00', '2022-12-12 11:35:00']
|
|
1215
|
+
}
|
|
1216
|
+
},
|
|
1217
|
+
{ prop: 'operation', label: '操作', fixed: 'right', width: 330 }
|
|
1218
|
+
])
|
|
1219
|
+
|
|
1220
|
+
// 表格拖拽排序
|
|
1221
|
+
const sortTable = ({ newIndex, oldIndex }: { newIndex?: number; oldIndex?: number }) => {
|
|
1222
|
+
console.log(newIndex, oldIndex)
|
|
1223
|
+
console.log(proTable.value?.tableData)
|
|
1224
|
+
ElMessage.success('修改列表排序成功')
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// 删除用户信息
|
|
1228
|
+
const deleteAccount = async (params: User.ResUserList) => {
|
|
1229
|
+
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`)
|
|
1230
|
+
proTable.value?.getTableList()
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// 批量删除用户信息
|
|
1234
|
+
const batchDelete = async (id: string[]) => {
|
|
1235
|
+
await useHandleData(deleteUser, { id }, '删除所选用户信息')
|
|
1236
|
+
proTable.value?.clearSelection()
|
|
1237
|
+
proTable.value?.getTableList()
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// 重置用户密码
|
|
1241
|
+
const resetPass = async (params: User.ResUserList) => {
|
|
1242
|
+
await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`)
|
|
1243
|
+
proTable.value?.getTableList()
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// 切换用户状态
|
|
1247
|
+
const changeStatus = async (row: User.ResUserList) => {
|
|
1248
|
+
await useHandleData(
|
|
1249
|
+
changeUserStatus,
|
|
1250
|
+
{ id: row.id, status: row.status == 1 ? 0 : 1 },
|
|
1251
|
+
`切换【${row.username}】用户状态`
|
|
1252
|
+
)
|
|
1253
|
+
proTable.value?.getTableList()
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// 导出用户列表
|
|
1257
|
+
const downloadFile = async () => {
|
|
1258
|
+
ElMessageBox.confirm('确认导出用户数据?', '温馨提示', { type: 'warning' }).then(() =>
|
|
1259
|
+
useDownload(exportUserInfo, '用户列表', proTable.value?.searchParam)
|
|
1260
|
+
)
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// 批量添加用户
|
|
1264
|
+
const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
|
|
1265
|
+
const batchAdd = () => {
|
|
1266
|
+
const params = {
|
|
1267
|
+
title: '用户',
|
|
1268
|
+
tempApi: exportUserInfo,
|
|
1269
|
+
importApi: BatchAddUser,
|
|
1270
|
+
getTableList: proTable.value?.getTableList
|
|
1271
|
+
}
|
|
1272
|
+
dialogRef.value?.acceptParams(params)
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// 打开 drawer(新增、查看、编辑)
|
|
1276
|
+
const drawerRef = ref<InstanceType<typeof UserDrawer> | null>(null)
|
|
1277
|
+
const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
|
|
1278
|
+
const params = {
|
|
1279
|
+
title,
|
|
1280
|
+
isView: title === '查看',
|
|
1281
|
+
row: { ...row },
|
|
1282
|
+
api: title === '新增' ? addUser : title === '编辑' ? editUser : undefined,
|
|
1283
|
+
getTableList: proTable.value?.getTableList
|
|
1284
|
+
}
|
|
1285
|
+
drawerRef.value?.acceptParams(params)
|
|
1286
|
+
}
|
|
1287
|
+
</script>
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
#### 2.2 页面使用
|
|
1291
|
+
|
|
1292
|
+
```html
|
|
1293
|
+
<template>
|
|
1294
|
+
<div class="table-box">
|
|
1295
|
+
<query-table
|
|
1296
|
+
ref="proTable"
|
|
1297
|
+
title="用户列表"
|
|
1298
|
+
highlight-current-row
|
|
1299
|
+
:column-config="columnConfig"
|
|
1300
|
+
:table-config="tableConfigOptions"
|
|
1301
|
+
:request-api="getUserList"
|
|
1302
|
+
:row-class-name="tableRowClassName"
|
|
1303
|
+
:span-method="objectSpanMethod"
|
|
1304
|
+
:show-summary="true"
|
|
1305
|
+
:summary-method="getSummaries"
|
|
1306
|
+
@row-click="rowClick"
|
|
1307
|
+
>
|
|
1308
|
+
<!-- 表格 header 按钮 -->
|
|
1309
|
+
<template #tableHeader="scope">
|
|
1310
|
+
<el-button type="primary" @click="proTable?.element?.toggleAllSelection">全选 / 全不选</el-button>
|
|
1311
|
+
<el-button type="primary" plain @click="setCurrent">选中第五行</el-button>
|
|
1312
|
+
<el-button type="danger" plain :disabled="!scope.isSelected" @click="batchDelete(scope.selectedListIds)">
|
|
1313
|
+
批量删除用户
|
|
1314
|
+
</el-button>
|
|
1315
|
+
</template>
|
|
1316
|
+
<!-- Expand -->
|
|
1317
|
+
<template #expand="scope">{{ scope.row }}</template>
|
|
1318
|
+
<!-- 表格操作 -->
|
|
1319
|
+
<template #operation="scope">
|
|
1320
|
+
<el-button type="primary" link :icon="Refresh" @click="resetPass(scope.row)">重置密码</el-button>
|
|
1321
|
+
<el-button type="primary" link :icon="Delete" @click="deleteAccount(scope.row)">删除</el-button>
|
|
1322
|
+
</template>
|
|
1323
|
+
<template #append>
|
|
1324
|
+
<span style="color: var(--el-color-primary)">
|
|
1325
|
+
<div style="height: 30px; line-height: 30px; background: #f3d2db">
|
|
1326
|
+
我是插入在表格最后的内容。若表格有合计行,该内容会位于合计行之上。
|
|
1327
|
+
</div>
|
|
1328
|
+
</span>
|
|
1329
|
+
</template>
|
|
1330
|
+
</query-table>
|
|
1331
|
+
</div>
|
|
1332
|
+
</template>
|
|
1333
|
+
|
|
1334
|
+
<script setup lang="tsx" name="complexProTable">
|
|
1335
|
+
// import { reactive, ref } from 'vue'
|
|
1336
|
+
import { ElMessage } from 'element-plus'
|
|
1337
|
+
import { User } from '@/api/interface'
|
|
1338
|
+
import { useHandleData } from '@/hooks/useHandleData'
|
|
1339
|
+
import QueryTable from '@/components/QueryTable/index.vue'
|
|
1340
|
+
import { CirclePlus, Pointer, Delete, Refresh } from '@element-plus/icons-vue'
|
|
1341
|
+
import type { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
|
|
1342
|
+
import { ProTableInstance, HeaderRenderScope, TableConfigProps } from '@/components/QueryTable/interface'
|
|
1343
|
+
import { getUserList, deleteUser, resetUserPassWord, getUserStatus, getUserGender } from '@/api/modules/user'
|
|
1344
|
+
|
|
1345
|
+
// ProTable 实例
|
|
1346
|
+
const proTable = ref<ProTableInstance>()
|
|
1347
|
+
|
|
1348
|
+
// 自定义渲染表头(使用tsx语法)
|
|
1349
|
+
const headerRender = (scope: HeaderRenderScope<User.ResUserList>) => {
|
|
1350
|
+
return (
|
|
1351
|
+
<el-button type="primary" onClick={() => ElMessage.success('我是通过 tsx 语法渲染的表头')}>
|
|
1352
|
+
{scope.column.label}
|
|
1353
|
+
</el-button>
|
|
1354
|
+
)
|
|
1355
|
+
}
|
|
1356
|
+
const columnConfig = {
|
|
1357
|
+
layout: 'selection,expand,radio,index,sort',
|
|
1358
|
+
selectionProps: {},
|
|
1359
|
+
radioProps: {
|
|
1360
|
+
label: 'Radio'
|
|
1361
|
+
},
|
|
1362
|
+
indexProps: {
|
|
1363
|
+
label: '#'
|
|
1364
|
+
},
|
|
1365
|
+
expandProps: {
|
|
1366
|
+
label: 'Expand'
|
|
1367
|
+
},
|
|
1368
|
+
sortProps: {
|
|
1369
|
+
label: 'Sort'
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
const tableConfigOptions = reactive<TableConfigProps>({
|
|
1373
|
+
queryItems: [
|
|
1374
|
+
{
|
|
1375
|
+
prop: 'username',
|
|
1376
|
+
label: '用户姓名',
|
|
1377
|
+
search: { el: 'el-input', tooltip: '我是搜索提示' }
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
prop: 'sliders',
|
|
1381
|
+
label: '开关',
|
|
1382
|
+
search: { el: 'el-slider', defaultValue: 20 }
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
prop: 'open',
|
|
1386
|
+
label: '开关',
|
|
1387
|
+
search: { el: 'el-switch', defaultValue: false }
|
|
1388
|
+
}
|
|
1389
|
+
],
|
|
1390
|
+
// 表格头部按钮配置项
|
|
1391
|
+
topList: [
|
|
1392
|
+
{
|
|
1393
|
+
text: '新增',
|
|
1394
|
+
hasPerm: 'add',
|
|
1395
|
+
type: 'primary',
|
|
1396
|
+
fun: () => {
|
|
1397
|
+
console.log('新建字典')
|
|
1398
|
+
}
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
text: '导出',
|
|
1402
|
+
hasPerm: 'export',
|
|
1403
|
+
plain: true,
|
|
1404
|
+
fun: () => {
|
|
1405
|
+
console.log('新增')
|
|
1406
|
+
}
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
text: '批量删除',
|
|
1410
|
+
hasPerm: 'batch-add',
|
|
1411
|
+
// icon: 'delete',
|
|
1412
|
+
type: 'danger',
|
|
1413
|
+
plain: true,
|
|
1414
|
+
fun: scope => {
|
|
1415
|
+
console.log('新建字典', scope)
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
],
|
|
1419
|
+
columns: [
|
|
1420
|
+
{
|
|
1421
|
+
prop: 'base',
|
|
1422
|
+
label: '基本信息',
|
|
1423
|
+
// isSetting: true,
|
|
1424
|
+
headerRender,
|
|
1425
|
+
_children: [
|
|
1426
|
+
{ prop: 'username', label: '用户姓名用户姓名用户姓名', width: 110 },
|
|
1427
|
+
{ prop: 'user.detail.age', label: '年龄', width: 100 },
|
|
1428
|
+
{
|
|
1429
|
+
prop: 'gender',
|
|
1430
|
+
label: '性别',
|
|
1431
|
+
width: 100,
|
|
1432
|
+
enum: getUserGender,
|
|
1433
|
+
fieldNames: { label: 'genderLabel', value: 'genderValue' }
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
prop: 'details',
|
|
1437
|
+
label: '详细资料',
|
|
1438
|
+
_children: [
|
|
1439
|
+
{ prop: 'idCard', label: '身份证号' },
|
|
1440
|
+
{ prop: 'email', label: '邮箱' },
|
|
1441
|
+
{ prop: 'address', label: '居住地址' }
|
|
1442
|
+
]
|
|
1443
|
+
}
|
|
1444
|
+
]
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
prop: 'status',
|
|
1448
|
+
label: '用户状态',
|
|
1449
|
+
// isSetting: true,
|
|
1450
|
+
tag: true,
|
|
1451
|
+
enum: getUserStatus,
|
|
1452
|
+
fieldNames: { label: 'userLabel', value: 'userStatus' }
|
|
1453
|
+
},
|
|
1454
|
+
{ prop: 'createTime', label: '创建时间', width: 200 },
|
|
1455
|
+
{ prop: 'operation', label: '操作', fixed: 'right', width: 230 }
|
|
1456
|
+
]
|
|
1457
|
+
})
|
|
1458
|
+
// 表格配置项
|
|
1459
|
+
/* const columns = reactive<ColumnProps<User.ResUserList>[]>([
|
|
1460
|
+
{ type: 'selection', width: 80 },
|
|
1461
|
+
{ type: 'index', label: '#', width: 80 },
|
|
1462
|
+
{ type: 'expand', label: 'Expand', width: 100 },
|
|
1463
|
+
{
|
|
1464
|
+
prop: 'base',
|
|
1465
|
+
label: '基本信息',
|
|
1466
|
+
headerRender,
|
|
1467
|
+
_children: [
|
|
1468
|
+
{ prop: 'username', label: '用户姓名', width: 110 },
|
|
1469
|
+
{ prop: 'user.detail.age', label: '年龄', width: 100 },
|
|
1470
|
+
{
|
|
1471
|
+
prop: 'gender',
|
|
1472
|
+
label: '性别',
|
|
1473
|
+
width: 100,
|
|
1474
|
+
enum: getUserGender,
|
|
1475
|
+
fieldNames: { label: 'genderLabel', value: 'genderValue' }
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
prop: 'details',
|
|
1479
|
+
label: '详细资料',
|
|
1480
|
+
_children: [
|
|
1481
|
+
{ prop: 'idCard', label: '身份证号' },
|
|
1482
|
+
{ prop: 'email', label: '邮箱' },
|
|
1483
|
+
{ prop: 'address', label: '居住地址' }
|
|
1484
|
+
]
|
|
1485
|
+
}
|
|
1486
|
+
]
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
prop: 'status',
|
|
1490
|
+
label: '用户状态',
|
|
1491
|
+
tag: true,
|
|
1492
|
+
enum: getUserStatus,
|
|
1493
|
+
fieldNames: { label: 'userLabel', value: 'userStatus' }
|
|
1494
|
+
},
|
|
1495
|
+
{ prop: 'createTime', label: '创建时间', width: 200 },
|
|
1496
|
+
{ prop: 'operation', label: '操作', fixed: 'right', width: 230 }
|
|
1497
|
+
]) */
|
|
1498
|
+
|
|
1499
|
+
// 选择行
|
|
1500
|
+
const setCurrent = () => {
|
|
1501
|
+
proTable.value?.element?.setCurrentRow(proTable.value?.tableData[4])
|
|
1502
|
+
proTable.value?.element?.toggleRowSelection(proTable.value?.tableData[4], true)
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// 表尾合计行(自行根据条件计算)
|
|
1506
|
+
interface SummaryMethodProps<T = User.ResUserList> {
|
|
1507
|
+
columns: TableColumnCtx<T>[]
|
|
1508
|
+
data: T[]
|
|
1509
|
+
}
|
|
1510
|
+
const getSummaries = (param: SummaryMethodProps) => {
|
|
1511
|
+
const { columns } = param
|
|
1512
|
+
const sums: string[] = []
|
|
1513
|
+
columns.forEach((column, index) => {
|
|
1514
|
+
if (index === 0) return (sums[index] = '合计')
|
|
1515
|
+
else sums[index] = 'N/A'
|
|
1516
|
+
})
|
|
1517
|
+
return sums
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// 列合并
|
|
1521
|
+
interface SpanMethodProps {
|
|
1522
|
+
row: User.ResUserList
|
|
1523
|
+
column: TableColumnCtx<User.ResUserList>
|
|
1524
|
+
rowIndex: number
|
|
1525
|
+
columnIndex: number
|
|
1526
|
+
}
|
|
1527
|
+
const objectSpanMethod = ({ rowIndex, columnIndex }: SpanMethodProps) => {
|
|
1528
|
+
if (columnIndex === 3) {
|
|
1529
|
+
if (rowIndex % 2 === 0) return { rowspan: 2, colspan: 1 }
|
|
1530
|
+
else return { rowspan: 0, colspan: 0 }
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// 设置列样式
|
|
1535
|
+
const tableRowClassName = ({ rowIndex }: { row: User.ResUserList; rowIndex: number }) => {
|
|
1536
|
+
if (rowIndex === 2) return 'warning-row'
|
|
1537
|
+
if (rowIndex === 6) return 'success-row'
|
|
1538
|
+
return ''
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// 单击行
|
|
1542
|
+
const rowClick = (row: User.ResUserList, column: TableColumnCtx<User.ResUserList>) => {
|
|
1543
|
+
if (column.property == 'radio' || column.property == 'operation') return
|
|
1544
|
+
console.log(row)
|
|
1545
|
+
ElMessage.success('当前行被点击了!')
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// 删除用户信息
|
|
1549
|
+
const deleteAccount = async (params: User.ResUserList) => {
|
|
1550
|
+
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`)
|
|
1551
|
+
proTable.value?.getTableList()
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// 批量删除用户信息
|
|
1555
|
+
const batchDelete = async (id: string[]) => {
|
|
1556
|
+
await useHandleData(deleteUser, { id }, '删除所选用户信息')
|
|
1557
|
+
proTable.value?.clearSelection()
|
|
1558
|
+
proTable.value?.getTableList()
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// 重置用户密码
|
|
1562
|
+
const resetPass = async (params: User.ResUserList) => {
|
|
1563
|
+
await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`)
|
|
1564
|
+
proTable.value?.getTableList()
|
|
1565
|
+
}
|
|
1566
|
+
</script>
|
|
1567
|
+
|
|
1568
|
+
<style lang="scss" scoped>
|
|
1569
|
+
.el-table .warning-row,
|
|
1570
|
+
.el-table .warning-row .el-table-fixed-column--right,
|
|
1571
|
+
.el-table .warning-row .el-table-fixed-column--left {
|
|
1572
|
+
background-color: var(--el-color-warning-light-9);
|
|
1573
|
+
}
|
|
1574
|
+
.el-table .success-row,
|
|
1575
|
+
.el-table .success-row .el-table-fixed-column--right,
|
|
1576
|
+
.el-table .success-row .el-table-fixed-column--left {
|
|
1577
|
+
background-color: var(--el-color-success-light-9);
|
|
1578
|
+
}
|
|
1579
|
+
</style>
|
|
1580
|
+
```
|