vue2-components-plus 1.0.28 → 1.0.31
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/dist/ComponentDemo/DialogDemo.md +323 -0
- package/dist/ComponentDemo/{DialogSetupDemo.vue → DialogDemo.vue} +119 -14
- package/dist/ComponentDemo/DirectivesDemo.md +297 -0
- package/dist/ComponentDemo/DirectivesDemo.vue +0 -2
- package/dist/ComponentDemo/FormDemo.md +432 -0
- package/dist/ComponentDemo/{FormSetupDemo.vue → FormDemo.vue} +75 -1
- package/dist/ComponentDemo/TableDemo.md +573 -0
- package/dist/ComponentDemo/TableDemo.vue +980 -0
- package/dist/vue2-components-plus.css +1 -1
- package/dist/vue2-components-plus.es5.js +27 -5
- package/dist/vue2-components-plus.js +15 -5
- package/dist/vue2-components-plus.umd.cjs +1 -1
- package/package.json +9 -8
- package/dist/ComponentDemo/NsTableDemo/SetupDemo.vue +0 -546
- /package/dist/ComponentDemo/{NsTableDemo/mockData.js → mockData.js} +0 -0
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="table-demo">
|
|
3
|
+
<el-card shadow="never" class="table-demo__feature-card">
|
|
4
|
+
<template v-slot:header>属性能力开关(便于外部项目逐项对照)</template>
|
|
5
|
+
<div class="table-demo__feature-grid">
|
|
6
|
+
<div class="table-demo__feature-item">
|
|
7
|
+
<span>showSearch</span>
|
|
8
|
+
<el-switch v-model="featureState.showSearch" />
|
|
9
|
+
</div>
|
|
10
|
+
<div class="table-demo__feature-item">
|
|
11
|
+
<span>searchProps.showCollapse</span>
|
|
12
|
+
<el-switch v-model="featureState.showSearchCollapse" />
|
|
13
|
+
</div>
|
|
14
|
+
<div class="table-demo__feature-item">
|
|
15
|
+
<span>showHeaderToolbar</span>
|
|
16
|
+
<el-switch v-model="featureState.showHeaderToolbar" />
|
|
17
|
+
</div>
|
|
18
|
+
<div class="table-demo__feature-item">
|
|
19
|
+
<span>showAddButton</span>
|
|
20
|
+
<el-switch v-model="featureState.showAddButton" />
|
|
21
|
+
</div>
|
|
22
|
+
<div class="table-demo__feature-item">
|
|
23
|
+
<span>useHeaderActionsSlot</span>
|
|
24
|
+
<el-switch v-model="featureState.useHeaderActionsSlot" />
|
|
25
|
+
</div>
|
|
26
|
+
<div class="table-demo__feature-item">
|
|
27
|
+
<span>afterResetSlot</span>
|
|
28
|
+
<el-switch v-model="featureState.useAfterResetActionsSlot" />
|
|
29
|
+
</div>
|
|
30
|
+
<div class="table-demo__feature-item">
|
|
31
|
+
<span>showSelection</span>
|
|
32
|
+
<el-switch v-model="featureState.showSelection" />
|
|
33
|
+
</div>
|
|
34
|
+
<div class="table-demo__feature-item">
|
|
35
|
+
<span>showIndex</span>
|
|
36
|
+
<el-switch v-model="featureState.showIndex" />
|
|
37
|
+
</div>
|
|
38
|
+
<div class="table-demo__feature-item">
|
|
39
|
+
<span>showPagination</span>
|
|
40
|
+
<el-switch v-model="featureState.showPagination" />
|
|
41
|
+
</div>
|
|
42
|
+
<div class="table-demo__feature-item">
|
|
43
|
+
<span>border</span>
|
|
44
|
+
<el-switch v-model="featureState.border" />
|
|
45
|
+
</div>
|
|
46
|
+
<div class="table-demo__feature-item">
|
|
47
|
+
<span>stripe</span>
|
|
48
|
+
<el-switch v-model="featureState.stripe" />
|
|
49
|
+
</div>
|
|
50
|
+
<div class="table-demo__feature-item">
|
|
51
|
+
<span>highlightCurrentRow</span>
|
|
52
|
+
<el-switch v-model="featureState.highlightCurrentRow" />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</el-card>
|
|
56
|
+
|
|
57
|
+
<NsTableContainer
|
|
58
|
+
ref="containerRef"
|
|
59
|
+
class="table-demo__container"
|
|
60
|
+
page-number-key="currentPage1"
|
|
61
|
+
page-size-key="pageSize1"
|
|
62
|
+
page-total-key="total1"
|
|
63
|
+
:show-search="featureState.showSearch"
|
|
64
|
+
:search-items="searchItems"
|
|
65
|
+
:external-search-params="externalSearchParams"
|
|
66
|
+
:search-props="mergedSearchProps"
|
|
67
|
+
:table-data="tableData"
|
|
68
|
+
:columns="columns"
|
|
69
|
+
:action-buttons="actionButtons"
|
|
70
|
+
:total="total"
|
|
71
|
+
:current-page.sync="paginationState.currentPage"
|
|
72
|
+
:page-size.sync="paginationState.pageSize"
|
|
73
|
+
:table-props="mergedTableProps"
|
|
74
|
+
:load-data="loadData"
|
|
75
|
+
@search="handleSearch"
|
|
76
|
+
@reset="handleReset"
|
|
77
|
+
@add="handleAdd"
|
|
78
|
+
@selection-change="handleSelectionChange"
|
|
79
|
+
@sort-change="handleSortChange"
|
|
80
|
+
@row-click="handleRowClick"
|
|
81
|
+
@size-change="handleSizeChange"
|
|
82
|
+
@current-change="handleCurrentChange"
|
|
83
|
+
@page-change="handlePageChange"
|
|
84
|
+
@link-click="handleLinkClick"
|
|
85
|
+
>
|
|
86
|
+
<!-- 搜索项插槽示例:slot=item.slot -->
|
|
87
|
+
<template v-slot:emailKeywordSlot="{ formData }">
|
|
88
|
+
<el-input
|
|
89
|
+
v-model="formData.emailKeyword"
|
|
90
|
+
clearable
|
|
91
|
+
placeholder="请输入邮箱关键字(自定义插槽)"
|
|
92
|
+
@keyup.enter.native="triggerSearchByEnter"
|
|
93
|
+
/>
|
|
94
|
+
</template>
|
|
95
|
+
<template
|
|
96
|
+
v-if="featureState.useAfterResetActionsSlot"
|
|
97
|
+
v-slot:actions-after-reset="{ formData, handleSearch: doSearch }"
|
|
98
|
+
>
|
|
99
|
+
<el-button type="text" @click="applyEnabledQuickFilter(formData, doSearch)">仅看启用</el-button>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<!-- 表格顶部工具栏插槽:header-left / header-actions -->
|
|
103
|
+
<template v-slot:header-left>
|
|
104
|
+
<div class="table-demo__toolbar-left">
|
|
105
|
+
<span>当前共 {{ total }} 条</span>
|
|
106
|
+
<span>已选 {{ selectedKeys.length }} 条</span>
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
<template v-if="featureState.useHeaderActionsSlot" v-slot:header-actions>
|
|
110
|
+
<el-button size="small" @click="reloadWithMessage">刷新</el-button>
|
|
111
|
+
<el-button size="small" @click="clearSortState">重置排序</el-button>
|
|
112
|
+
</template>
|
|
113
|
+
|
|
114
|
+
<!-- 表头插槽示例:column.headerSlot -->
|
|
115
|
+
<template v-slot:usernameHeader>
|
|
116
|
+
<span>
|
|
117
|
+
<i class="el-icon-link" />
|
|
118
|
+
用户名(link)
|
|
119
|
+
</span>
|
|
120
|
+
</template>
|
|
121
|
+
|
|
122
|
+
<!-- 单元格插槽示例:column.slot -->
|
|
123
|
+
<template v-slot:status="{ row }">
|
|
124
|
+
<el-tag size="small" :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag>
|
|
125
|
+
</template>
|
|
126
|
+
<template v-slot:gender="{ row }">
|
|
127
|
+
<el-tag size="small" :type="row.gender === 1 ? 'primary' : 'danger'">
|
|
128
|
+
{{ row.gender === 1 ? '男' : '女' }}
|
|
129
|
+
</el-tag>
|
|
130
|
+
</template>
|
|
131
|
+
<template v-slot:department="{ row }">
|
|
132
|
+
<el-tag size="small" effect="plain">{{ getDepartmentText(row.department) }}</el-tag>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
135
|
+
<!-- action 按钮插槽示例:button.slot -->
|
|
136
|
+
<template v-slot:deleteAction="{ row }">
|
|
137
|
+
<el-button type="text" style="color: #f56c6c" :disabled="row.status === 0" @click="handleDelete(row)">
|
|
138
|
+
删除
|
|
139
|
+
</el-button>
|
|
140
|
+
</template>
|
|
141
|
+
|
|
142
|
+
<!-- 空状态插槽示例:empty -->
|
|
143
|
+
<template v-slot:empty>
|
|
144
|
+
<div class="table-demo__empty-slot">
|
|
145
|
+
<i class="el-icon-files" />
|
|
146
|
+
<span>暂无匹配数据,请调整筛选条件</span>
|
|
147
|
+
</div>
|
|
148
|
+
</template>
|
|
149
|
+
</NsTableContainer>
|
|
150
|
+
|
|
151
|
+
<el-card shadow="never" class="table-demo__actions">
|
|
152
|
+
<template v-slot:header>实例方法能力(通过 ref 调用)</template>
|
|
153
|
+
<div class="table-demo__action-list">
|
|
154
|
+
<el-button @click="getSelectedRows">获取选中行</el-button>
|
|
155
|
+
<el-button @click="getSelectedKeys">获取选中 ID</el-button>
|
|
156
|
+
<el-button @click="selectRows([3, 7])">选中 ID 3 / 7</el-button>
|
|
157
|
+
<el-button @click="clearSelection">清空选择</el-button>
|
|
158
|
+
<el-button @click="selectAll">全选当前页</el-button>
|
|
159
|
+
<el-button @click="checkSelection">检查选择状态</el-button>
|
|
160
|
+
<el-button @click="setSearchForm">回填搜索条件</el-button>
|
|
161
|
+
<el-button @click="resetSearchForm">重置搜索表单</el-button>
|
|
162
|
+
</div>
|
|
163
|
+
</el-card>
|
|
164
|
+
|
|
165
|
+
<!-- <el-card shadow="never" class="table-demo__events">
|
|
166
|
+
<template v-slot:header>事件触发日志(search / reset / add / sort-change ...)</template>
|
|
167
|
+
<div v-if="eventLogs.length" class="table-demo__event-list">
|
|
168
|
+
<div v-for="(item, index) in eventLogs" :key="index" class="table-demo__event-item">
|
|
169
|
+
<span class="table-demo__event-name">{{ item.name }}</span>
|
|
170
|
+
<span class="table-demo__event-time">{{ item.time }}</span>
|
|
171
|
+
<span class="table-demo__event-payload">{{ item.payload }}</span>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<el-empty v-else :image-size="60" description="触发一次操作后将在此展示事件参数" />
|
|
175
|
+
</el-card> -->
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
<script setup>
|
|
180
|
+
import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue'
|
|
181
|
+
import { departmentOptions, fetchDepartmentOptions, fetchStatusOptions, filterUsers, mockUsers } from './mockData'
|
|
182
|
+
|
|
183
|
+
function createSearchItems() {
|
|
184
|
+
return [
|
|
185
|
+
{
|
|
186
|
+
prop: 'month',
|
|
187
|
+
label: '归属月',
|
|
188
|
+
span: 6,
|
|
189
|
+
component: 'ElSelect',
|
|
190
|
+
attrs: {
|
|
191
|
+
placeholder: '请选择归属月',
|
|
192
|
+
clearable: true,
|
|
193
|
+
},
|
|
194
|
+
children: Array.from({ length: 12 }, function (_, index) {
|
|
195
|
+
return {
|
|
196
|
+
label: index + 1 + '月',
|
|
197
|
+
value: String(index + 1),
|
|
198
|
+
}
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
prop: 'username',
|
|
203
|
+
label: '用户名',
|
|
204
|
+
span: 6,
|
|
205
|
+
component: 'ElInput',
|
|
206
|
+
attrs: {
|
|
207
|
+
placeholder: '请输入用户名',
|
|
208
|
+
clearable: true,
|
|
209
|
+
},
|
|
210
|
+
events: {},
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
prop: 'realName',
|
|
214
|
+
label: '真实姓名',
|
|
215
|
+
span: 6,
|
|
216
|
+
component: 'ElInput',
|
|
217
|
+
attrs: {
|
|
218
|
+
placeholder: '请输入真实姓名',
|
|
219
|
+
clearable: true,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
prop: 'emailKeyword',
|
|
224
|
+
label: '邮箱关键字',
|
|
225
|
+
span: 6,
|
|
226
|
+
type: 'slot',
|
|
227
|
+
slot: 'emailKeywordSlot',
|
|
228
|
+
formItemAttrs: {
|
|
229
|
+
required: false,
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
prop: 'status',
|
|
234
|
+
label: '状态',
|
|
235
|
+
span: 6,
|
|
236
|
+
component: 'ElSelect',
|
|
237
|
+
attrs: {
|
|
238
|
+
placeholder: '请选择状态',
|
|
239
|
+
clearable: true,
|
|
240
|
+
},
|
|
241
|
+
children: [],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
prop: 'department',
|
|
245
|
+
label: '部门',
|
|
246
|
+
span: 6,
|
|
247
|
+
component: 'ElSelect',
|
|
248
|
+
attrs: {
|
|
249
|
+
placeholder: '请选择部门',
|
|
250
|
+
clearable: true,
|
|
251
|
+
filterable: true,
|
|
252
|
+
},
|
|
253
|
+
children: [],
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
prop: 'gender',
|
|
257
|
+
label: '性别',
|
|
258
|
+
span: 6,
|
|
259
|
+
component: 'ElSelect',
|
|
260
|
+
attrs: {
|
|
261
|
+
placeholder: '请选择性别',
|
|
262
|
+
clearable: true,
|
|
263
|
+
},
|
|
264
|
+
children: [
|
|
265
|
+
{ label: '全部', value: '' },
|
|
266
|
+
{ label: '男', value: 1 },
|
|
267
|
+
{ label: '女', value: 2 },
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
prop: 'createTime',
|
|
272
|
+
label: '创建时间',
|
|
273
|
+
span: 6,
|
|
274
|
+
component: 'ElDatePicker',
|
|
275
|
+
attrs: {
|
|
276
|
+
type: 'daterange',
|
|
277
|
+
clearable: true,
|
|
278
|
+
rangeSeparator: '至',
|
|
279
|
+
startPlaceholder: '开始日期',
|
|
280
|
+
endPlaceholder: '结束日期',
|
|
281
|
+
valueFormat: 'yyyy-MM-dd',
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
prop: 'phone',
|
|
286
|
+
label: '手机号',
|
|
287
|
+
span: 6,
|
|
288
|
+
component: 'ElInput',
|
|
289
|
+
attrs: {
|
|
290
|
+
placeholder: '请输入手机号',
|
|
291
|
+
clearable: true,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
prop: 'active',
|
|
296
|
+
label: '是否激活',
|
|
297
|
+
span: 6,
|
|
298
|
+
component: 'ElSwitch',
|
|
299
|
+
attrs: {
|
|
300
|
+
activeText: '是',
|
|
301
|
+
inactiveText: '否',
|
|
302
|
+
},
|
|
303
|
+
defaultValue: true,
|
|
304
|
+
},
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function createColumns(context) {
|
|
309
|
+
return [
|
|
310
|
+
{
|
|
311
|
+
prop: 'id',
|
|
312
|
+
label: 'ID',
|
|
313
|
+
width: 70,
|
|
314
|
+
sortable: true,
|
|
315
|
+
fixed: 'left',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
prop: 'avatar',
|
|
319
|
+
label: '头像(image)',
|
|
320
|
+
width: 110,
|
|
321
|
+
type: 'image',
|
|
322
|
+
imageWidth: '34px',
|
|
323
|
+
imageHeight: '34px',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
label: '基本信息',
|
|
327
|
+
children: [
|
|
328
|
+
{
|
|
329
|
+
prop: 'username',
|
|
330
|
+
label: '用户名',
|
|
331
|
+
minWidth: 150,
|
|
332
|
+
type: 'link',
|
|
333
|
+
headerSlot: 'usernameHeader',
|
|
334
|
+
linkText: '查看用户',
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
prop: 'realName',
|
|
338
|
+
label: '真实姓名',
|
|
339
|
+
width: 120,
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
prop: 'gender',
|
|
343
|
+
label: '性别',
|
|
344
|
+
width: 90,
|
|
345
|
+
slot: 'gender',
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
prop: 'level',
|
|
349
|
+
label: '级别(enum)',
|
|
350
|
+
width: 120,
|
|
351
|
+
enum: [
|
|
352
|
+
{ label: '初级', value: 1 },
|
|
353
|
+
{ label: '中级', value: 2 },
|
|
354
|
+
{ label: '高级', value: 3 },
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
label: '组织信息',
|
|
361
|
+
children: [
|
|
362
|
+
{
|
|
363
|
+
prop: 'department',
|
|
364
|
+
label: '部门',
|
|
365
|
+
width: 130,
|
|
366
|
+
slot: 'department',
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
prop: 'status',
|
|
370
|
+
label: '状态(slot)',
|
|
371
|
+
width: 110,
|
|
372
|
+
slot: 'status',
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
label: '联系方式',
|
|
378
|
+
children: [
|
|
379
|
+
{
|
|
380
|
+
prop: 'phone',
|
|
381
|
+
label: '手机号',
|
|
382
|
+
width: 140,
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
prop: 'email',
|
|
386
|
+
label: '邮箱',
|
|
387
|
+
minWidth: 220,
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
prop: 'createTime',
|
|
393
|
+
label: '创建时间',
|
|
394
|
+
width: 180,
|
|
395
|
+
sortable: true,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
type: 'action',
|
|
399
|
+
label: '操作(action)',
|
|
400
|
+
width: 300,
|
|
401
|
+
fixed: 'right',
|
|
402
|
+
buttons: [
|
|
403
|
+
{
|
|
404
|
+
label: '查看',
|
|
405
|
+
type: 'text',
|
|
406
|
+
icon: 'el-icon-view',
|
|
407
|
+
handler: function (row) {
|
|
408
|
+
context.handleView(row)
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
label: '编辑',
|
|
413
|
+
type: 'text',
|
|
414
|
+
icon: 'el-icon-edit',
|
|
415
|
+
handler: function (row) {
|
|
416
|
+
context.handleEdit(row)
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
label: '禁用',
|
|
421
|
+
type: 'text',
|
|
422
|
+
show: function (row) {
|
|
423
|
+
return row.status === 1
|
|
424
|
+
},
|
|
425
|
+
disabled: function (row) {
|
|
426
|
+
return row.id % 2 === 0
|
|
427
|
+
},
|
|
428
|
+
handler: function (row) {
|
|
429
|
+
context.handleDisable(row)
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
label: '删除',
|
|
434
|
+
type: 'text',
|
|
435
|
+
icon: 'el-icon-delete',
|
|
436
|
+
slot: 'deleteAction',
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
},
|
|
440
|
+
]
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const containerRef = ref()
|
|
444
|
+
const loading = ref(false)
|
|
445
|
+
const total = ref(0)
|
|
446
|
+
const tableData = ref([])
|
|
447
|
+
const searchParams = ref({})
|
|
448
|
+
const sortState = ref({
|
|
449
|
+
prop: '',
|
|
450
|
+
order: '',
|
|
451
|
+
})
|
|
452
|
+
const selectedKeys = ref([])
|
|
453
|
+
const eventLogs = ref([])
|
|
454
|
+
const paginationMode = ref('backend')
|
|
455
|
+
const mockUserCount = mockUsers.length
|
|
456
|
+
const externalSearchParams = {
|
|
457
|
+
source: 'vue2-demo',
|
|
458
|
+
active: true,
|
|
459
|
+
}
|
|
460
|
+
const actionButtons = [{ label: '导出', key: 'export' }]
|
|
461
|
+
const searchItems = ref(createSearchItems())
|
|
462
|
+
const columns = ref([])
|
|
463
|
+
const paginationState = ref({
|
|
464
|
+
currentPage: 1,
|
|
465
|
+
pageSize: 10,
|
|
466
|
+
})
|
|
467
|
+
const featureState = ref({
|
|
468
|
+
showSearch: true,
|
|
469
|
+
showSearchCollapse: true,
|
|
470
|
+
showHeaderToolbar: true,
|
|
471
|
+
showAddButton: true,
|
|
472
|
+
useHeaderActionsSlot: false,
|
|
473
|
+
useAfterResetActionsSlot: false,
|
|
474
|
+
showSelection: true,
|
|
475
|
+
showIndex: true,
|
|
476
|
+
showPagination: true,
|
|
477
|
+
border: true,
|
|
478
|
+
stripe: true,
|
|
479
|
+
highlightCurrentRow: true,
|
|
480
|
+
})
|
|
481
|
+
const { proxy } = getCurrentInstance() || {}
|
|
482
|
+
|
|
483
|
+
const mergedSearchProps = computed(function () {
|
|
484
|
+
return {
|
|
485
|
+
labelWidth: '90px',
|
|
486
|
+
size: 'small',
|
|
487
|
+
defaultSpan: 6,
|
|
488
|
+
showCollapse: featureState.value.showSearchCollapse,
|
|
489
|
+
collapseLimit: 4,
|
|
490
|
+
}
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
const mergedTableProps = computed(function () {
|
|
494
|
+
return {
|
|
495
|
+
showHeaderToolbar: featureState.value.showHeaderToolbar,
|
|
496
|
+
showAddButton: featureState.value.showAddButton,
|
|
497
|
+
addButtonText: '新增用户',
|
|
498
|
+
showSelection: featureState.value.showSelection,
|
|
499
|
+
showIndex: featureState.value.showIndex,
|
|
500
|
+
loading: loading.value,
|
|
501
|
+
rowKey: 'id',
|
|
502
|
+
showPagination: featureState.value.showPagination,
|
|
503
|
+
pageSizes: [5, 10, 20],
|
|
504
|
+
paginationLayout: 'total, sizes, prev, pager, next, jumper',
|
|
505
|
+
stripe: featureState.value.stripe,
|
|
506
|
+
border: featureState.value.border,
|
|
507
|
+
autoHeight: true,
|
|
508
|
+
maxHeight: undefined,
|
|
509
|
+
defaultExpandAll: false,
|
|
510
|
+
highlightCurrentRow: featureState.value.highlightCurrentRow,
|
|
511
|
+
tooltipEffect: 'dark',
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
function createLogPayload(payload) {
|
|
516
|
+
try {
|
|
517
|
+
return JSON.stringify(payload)
|
|
518
|
+
} catch (error) {
|
|
519
|
+
return String(payload)
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function logEvent(name, payload) {
|
|
524
|
+
const now = new Date()
|
|
525
|
+
const time = [now.getHours(), now.getMinutes(), now.getSeconds()].map(function (value) {
|
|
526
|
+
return String(value).padStart(2, '0')
|
|
527
|
+
}).join(':')
|
|
528
|
+
eventLogs.value.unshift({
|
|
529
|
+
name,
|
|
530
|
+
time,
|
|
531
|
+
payload: createLogPayload(payload),
|
|
532
|
+
})
|
|
533
|
+
eventLogs.value = eventLogs.value.slice(0, 20)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function enrichRows(list) {
|
|
537
|
+
return (list || []).map(function (item) {
|
|
538
|
+
return {
|
|
539
|
+
...item,
|
|
540
|
+
level: (item.id % 3) + 1,
|
|
541
|
+
}
|
|
542
|
+
})
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function normalizeSearchParams(params) {
|
|
546
|
+
const next = Object.assign({}, params)
|
|
547
|
+
if (typeof next.emailKeyword === 'string') {
|
|
548
|
+
next.emailKeyword = next.emailKeyword.trim()
|
|
549
|
+
}
|
|
550
|
+
if (typeof next.username === 'string') {
|
|
551
|
+
next.username = next.username.trim()
|
|
552
|
+
}
|
|
553
|
+
return next
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function applyExtraFilters(list) {
|
|
557
|
+
if (!searchParams.value.emailKeyword) return list
|
|
558
|
+
return list.filter(function (item) {
|
|
559
|
+
return String(item.email || '').toLowerCase().indexOf(String(searchParams.value.emailKeyword).toLowerCase()) > -1
|
|
560
|
+
})
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function compareValue(a, b) {
|
|
564
|
+
if (a === b) return 0
|
|
565
|
+
if (a === undefined || a === null) return -1
|
|
566
|
+
if (b === undefined || b === null) return 1
|
|
567
|
+
const maybeDateA = new Date(a).getTime()
|
|
568
|
+
const maybeDateB = new Date(b).getTime()
|
|
569
|
+
if (!Number.isNaN(maybeDateA) && !Number.isNaN(maybeDateB)) {
|
|
570
|
+
return maybeDateA - maybeDateB
|
|
571
|
+
}
|
|
572
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
573
|
+
return a - b
|
|
574
|
+
}
|
|
575
|
+
return String(a).localeCompare(String(b))
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function applySort(list) {
|
|
579
|
+
if (!sortState.value.prop || !sortState.value.order) {
|
|
580
|
+
return list
|
|
581
|
+
}
|
|
582
|
+
const direction = sortState.value.order === 'ascending' ? 1 : -1
|
|
583
|
+
return (list || []).slice().sort(function (left, right) {
|
|
584
|
+
return compareValue(left[sortState.value.prop], right[sortState.value.prop]) * direction
|
|
585
|
+
})
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function triggerSearchByEnter() {
|
|
589
|
+
handleSearch(containerRef.value ? containerRef.value.getSearchFormData() : {})
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function applyEnabledQuickFilter(formData, doSearch) {
|
|
593
|
+
if (!formData) return
|
|
594
|
+
formData.active = true
|
|
595
|
+
if (typeof doSearch === 'function') {
|
|
596
|
+
doSearch()
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function getStatusType(status) {
|
|
601
|
+
return status === 1 ? 'success' : 'danger'
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function getStatusText(status) {
|
|
605
|
+
return status === 1 ? '启用' : '禁用'
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function getDepartmentText(value) {
|
|
609
|
+
const matched = departmentOptions.find(function (item) {
|
|
610
|
+
return item.value === value
|
|
611
|
+
})
|
|
612
|
+
return matched ? matched.label : value
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function getCurrentPagination() {
|
|
616
|
+
return containerRef.value && containerRef.value.getPagination
|
|
617
|
+
? containerRef.value.getPagination()
|
|
618
|
+
: { currentPage1: 1, pageSize1: paginationState.value.pageSize }
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function paginateList(list, pagination) {
|
|
622
|
+
const currentPage = Number(pagination.currentPage1 || 1)
|
|
623
|
+
const pageSize = Number(pagination.pageSize1 || 10)
|
|
624
|
+
const start = (currentPage - 1) * pageSize
|
|
625
|
+
return (list || []).slice(start, start + pageSize)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function resetContainerPage() {
|
|
629
|
+
paginationState.value.currentPage = 1
|
|
630
|
+
if (containerRef.value && containerRef.value.internalPagination) {
|
|
631
|
+
containerRef.value.internalPagination.currentPage = 1
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function handlePaginationModeChange() {
|
|
636
|
+
if (containerRef.value) {
|
|
637
|
+
containerRef.value.clearAllSelection()
|
|
638
|
+
}
|
|
639
|
+
selectedKeys.value = []
|
|
640
|
+
resetContainerPage()
|
|
641
|
+
logEvent('pagination-mode-change', paginationMode.value)
|
|
642
|
+
loadData()
|
|
643
|
+
proxy.$message.success('已切换为' + (paginationMode.value === 'frontend' ? '前端分页' : '后端分页'))
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
async function loadData() {
|
|
647
|
+
loading.value = true
|
|
648
|
+
try {
|
|
649
|
+
await new Promise(function (resolve) {
|
|
650
|
+
setTimeout(resolve, 250)
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
const pageConfig = {
|
|
654
|
+
pageNumberKey: 'currentPage1',
|
|
655
|
+
pageSizeKey: 'pageSize1',
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const filtered = filterUsers(
|
|
659
|
+
mockUsers,
|
|
660
|
+
searchParams.value,
|
|
661
|
+
{ currentPage1: 1, pageSize1: mockUserCount || 10 },
|
|
662
|
+
pageConfig,
|
|
663
|
+
)
|
|
664
|
+
const enrichedList = enrichRows(filtered.list)
|
|
665
|
+
const extraFilteredList = applyExtraFilters(enrichedList)
|
|
666
|
+
const sortedList = applySort(extraFilteredList)
|
|
667
|
+
|
|
668
|
+
if (paginationMode.value === 'frontend') {
|
|
669
|
+
const pagination = getCurrentPagination()
|
|
670
|
+
tableData.value = paginateList(sortedList, pagination)
|
|
671
|
+
total.value = sortedList.length
|
|
672
|
+
return
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const pagination = getCurrentPagination()
|
|
676
|
+
tableData.value = paginateList(sortedList, pagination)
|
|
677
|
+
total.value = sortedList.length
|
|
678
|
+
} catch (error) {
|
|
679
|
+
proxy.$message.error('加载表格数据失败')
|
|
680
|
+
} finally {
|
|
681
|
+
loading.value = false
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function handleSearch(params) {
|
|
686
|
+
const normalized = normalizeSearchParams(params)
|
|
687
|
+
searchParams.value = normalized
|
|
688
|
+
logEvent('search', normalized)
|
|
689
|
+
loadData()
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function handleReset() {
|
|
693
|
+
logEvent('reset', searchParams.value)
|
|
694
|
+
proxy.$message.info('搜索条件已重置')
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function handleSelectionChange(selection) {
|
|
698
|
+
const keys = (selection || []).map(function (item) {
|
|
699
|
+
return item.id
|
|
700
|
+
})
|
|
701
|
+
selectedKeys.value = keys
|
|
702
|
+
logEvent('selection-change', keys)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function handleSortChange(sort) {
|
|
706
|
+
sortState.value = sort || { prop: '', order: '' }
|
|
707
|
+
logEvent('sort-change', sortState.value)
|
|
708
|
+
loadData()
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function handleRowClick(row, column) {
|
|
712
|
+
logEvent('row-click', {
|
|
713
|
+
id: row.id,
|
|
714
|
+
column: column && column.property,
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function handleSizeChange(size) {
|
|
719
|
+
logEvent('size-change', size)
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function handleCurrentChange(page) {
|
|
723
|
+
logEvent('current-change', page)
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function handlePageChange(payload) {
|
|
727
|
+
logEvent('page-change', payload)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function handleLinkClick(row, column) {
|
|
731
|
+
logEvent('link-click', {
|
|
732
|
+
id: row.id,
|
|
733
|
+
prop: column && column.prop,
|
|
734
|
+
})
|
|
735
|
+
proxy.$message.info('点击了链接列:' + row.username)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function getSelectedRows() {
|
|
739
|
+
const rows = containerRef.value ? containerRef.value.getSelectionRows() : []
|
|
740
|
+
proxy.$alert(JSON.stringify(rows, null, 2), '当前选中行', {
|
|
741
|
+
confirmButtonText: '知道了',
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function getSelectedKeys() {
|
|
746
|
+
const keys = containerRef.value ? containerRef.value.getSelectionKeys() : []
|
|
747
|
+
proxy.$message.success('当前选中 ID:' + (keys.length ? keys.join(', ') : '无'))
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function selectRows(ids) {
|
|
751
|
+
if (!containerRef.value) return
|
|
752
|
+
containerRef.value.setSelectionKeys(ids)
|
|
753
|
+
proxy.$message.success('已尝试选中 ID:' + ids.join(', '))
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function clearSelection() {
|
|
757
|
+
if (!containerRef.value) return
|
|
758
|
+
containerRef.value.clearAllSelection()
|
|
759
|
+
selectedKeys.value = []
|
|
760
|
+
proxy.$message.success('已清空选中状态')
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function selectAll() {
|
|
764
|
+
if (!containerRef.value) return
|
|
765
|
+
containerRef.value.selectAll()
|
|
766
|
+
proxy.$message.success('已全选当前页')
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function checkSelection() {
|
|
770
|
+
if (!containerRef.value || !tableData.value.length) return
|
|
771
|
+
const firstSelected = containerRef.value.isRowSelected(tableData.value[0])
|
|
772
|
+
const keySelected = containerRef.value.isKeySelected(3)
|
|
773
|
+
proxy.$message.info('第一行选中:' + (firstSelected ? '是' : '否') + ';ID=3 选中:' + (keySelected ? '是' : '否'))
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function setSearchForm() {
|
|
777
|
+
if (!containerRef.value) return
|
|
778
|
+
containerRef.value.setSearchFormData({
|
|
779
|
+
username: 'zhang',
|
|
780
|
+
status: 1,
|
|
781
|
+
emailKeyword: 'example',
|
|
782
|
+
})
|
|
783
|
+
proxy.$message.success('已回填搜索条件,可点击“查询”查看效果')
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function resetSearchForm() {
|
|
787
|
+
if (!containerRef.value) return
|
|
788
|
+
containerRef.value.resetSearchForm()
|
|
789
|
+
proxy.$message.success('已重置搜索表单')
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function reloadWithMessage() {
|
|
793
|
+
loadData()
|
|
794
|
+
proxy.$message.success('已刷新数据')
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function clearSortState() {
|
|
798
|
+
sortState.value = {
|
|
799
|
+
prop: '',
|
|
800
|
+
order: '',
|
|
801
|
+
}
|
|
802
|
+
loadData()
|
|
803
|
+
proxy.$message.success('已重置排序')
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function handleAdd() {
|
|
807
|
+
logEvent('add', { source: 'toolbar-add-button' })
|
|
808
|
+
proxy.$message.success('点击了新增按钮')
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function handleView(row) {
|
|
812
|
+
proxy.$message.info('查看:' + row.username)
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function handleEdit(row) {
|
|
816
|
+
proxy.$message.success('编辑:' + row.username)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function handleDisable(row) {
|
|
820
|
+
proxy.$message.warning('已模拟禁用:' + row.username)
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function handleDelete(row) {
|
|
824
|
+
if (row.status === 0) {
|
|
825
|
+
proxy.$message.warning('禁用状态用户不可删除')
|
|
826
|
+
return
|
|
827
|
+
}
|
|
828
|
+
proxy.$confirm('确认删除用户“' + row.username + '”吗?', '提示', {
|
|
829
|
+
type: 'warning',
|
|
830
|
+
})
|
|
831
|
+
.then(() => {
|
|
832
|
+
proxy.$message.success('已模拟删除:' + row.username)
|
|
833
|
+
loadData()
|
|
834
|
+
})
|
|
835
|
+
.catch(function () {})
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
watch(
|
|
839
|
+
() => featureState.value.showSelection,
|
|
840
|
+
function (value) {
|
|
841
|
+
if (!value) {
|
|
842
|
+
clearSelection()
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
columns.value = createColumns({
|
|
848
|
+
handleView,
|
|
849
|
+
handleEdit,
|
|
850
|
+
handleDisable,
|
|
851
|
+
handleDelete,
|
|
852
|
+
})
|
|
853
|
+
searchItems.value[1].events.keyup = function (event) {
|
|
854
|
+
if (event && event.key === 'Enter') {
|
|
855
|
+
triggerSearchByEnter()
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
onMounted(async () => {
|
|
860
|
+
const statusOptions = await fetchStatusOptions()
|
|
861
|
+
const departmentList = await fetchDepartmentOptions()
|
|
862
|
+
searchItems.value[4].children = statusOptions
|
|
863
|
+
searchItems.value[5].children = departmentList
|
|
864
|
+
await nextTick()
|
|
865
|
+
if (containerRef.value) {
|
|
866
|
+
containerRef.value.initSearchAndLoad()
|
|
867
|
+
}
|
|
868
|
+
})
|
|
869
|
+
</script>
|
|
870
|
+
|
|
871
|
+
<style scoped>
|
|
872
|
+
.table-demo {
|
|
873
|
+
display: flex;
|
|
874
|
+
flex-direction: column;
|
|
875
|
+
gap: 16px;
|
|
876
|
+
height: 100%;
|
|
877
|
+
min-height: 0;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.table-demo__container {
|
|
881
|
+
flex: 1;
|
|
882
|
+
min-height: 420px;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
.table-demo__mode-card,
|
|
886
|
+
.table-demo__feature-card,
|
|
887
|
+
.table-demo__actions,
|
|
888
|
+
.table-demo__events {
|
|
889
|
+
border-radius: 12px;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.table-demo__mode-header {
|
|
893
|
+
display: flex;
|
|
894
|
+
align-items: center;
|
|
895
|
+
justify-content: space-between;
|
|
896
|
+
gap: 16px;
|
|
897
|
+
flex-wrap: wrap;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.table-demo__mode-title {
|
|
901
|
+
font-size: 16px;
|
|
902
|
+
font-weight: 600;
|
|
903
|
+
color: #303133;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.table-demo__mode-desc {
|
|
907
|
+
margin-top: 4px;
|
|
908
|
+
font-size: 13px;
|
|
909
|
+
color: #909399;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.table-demo__feature-grid {
|
|
913
|
+
display: grid;
|
|
914
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
915
|
+
gap: 10px 18px;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
.table-demo__feature-item {
|
|
919
|
+
display: flex;
|
|
920
|
+
align-items: center;
|
|
921
|
+
justify-content: space-between;
|
|
922
|
+
gap: 12px;
|
|
923
|
+
padding: 8px 10px;
|
|
924
|
+
background: #fafafa;
|
|
925
|
+
border-radius: 8px;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.table-demo__toolbar-left {
|
|
929
|
+
display: flex;
|
|
930
|
+
gap: 16px;
|
|
931
|
+
color: #606266;
|
|
932
|
+
font-size: 13px;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
.table-demo__empty-slot {
|
|
936
|
+
display: flex;
|
|
937
|
+
align-items: center;
|
|
938
|
+
justify-content: center;
|
|
939
|
+
gap: 8px;
|
|
940
|
+
color: #909399;
|
|
941
|
+
padding: 18px 0;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.table-demo__action-list {
|
|
945
|
+
display: flex;
|
|
946
|
+
flex-wrap: wrap;
|
|
947
|
+
gap: 12px;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.table-demo__event-list {
|
|
951
|
+
display: flex;
|
|
952
|
+
flex-direction: column;
|
|
953
|
+
gap: 8px;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.table-demo__event-item {
|
|
957
|
+
display: grid;
|
|
958
|
+
grid-template-columns: 170px 90px 1fr;
|
|
959
|
+
gap: 12px;
|
|
960
|
+
align-items: start;
|
|
961
|
+
padding: 8px 10px;
|
|
962
|
+
border-radius: 6px;
|
|
963
|
+
background: #fafafa;
|
|
964
|
+
font-size: 12px;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.table-demo__event-name {
|
|
968
|
+
color: #409eff;
|
|
969
|
+
font-weight: 600;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
.table-demo__event-time {
|
|
973
|
+
color: #909399;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.table-demo__event-payload {
|
|
977
|
+
color: #606266;
|
|
978
|
+
word-break: break-all;
|
|
979
|
+
}
|
|
980
|
+
</style>
|