commit | author | age
|
9bcb19
|
1 |
import T from 'ant-design-vue/es/table/Table' |
I |
2 |
import get from 'lodash.get' |
|
3 |
import draggable from 'vuedraggable' |
|
4 |
import columnSetting from './columnSetting' |
|
5 |
import './index.less' |
|
6 |
|
|
7 |
export default { |
|
8 |
components: { |
|
9 |
draggable, columnSetting |
|
10 |
}, |
|
11 |
data () { |
|
12 |
return { |
|
13 |
needTotalList: [], |
|
14 |
|
|
15 |
selectedRows: [], |
|
16 |
selectedRowKeys: [], |
|
17 |
|
|
18 |
localLoading: false, |
|
19 |
localDataSource: [], |
|
20 |
localPagination: Object.assign({}, this.pagination), |
|
21 |
isFullscreen: false, |
|
22 |
customSize: this.size, |
|
23 |
columnsSetting: [] |
|
24 |
} |
|
25 |
}, |
|
26 |
props: Object.assign({}, T.props, { |
|
27 |
rowKey: { |
|
28 |
type: [String, Function], |
|
29 |
default: 'key' |
|
30 |
}, |
|
31 |
data: { |
|
32 |
type: Function, |
|
33 |
required: true |
|
34 |
}, |
|
35 |
pageNum: { |
|
36 |
type: Number, |
|
37 |
default: 1 |
|
38 |
}, |
|
39 |
pageSize: { |
|
40 |
type: Number, |
|
41 |
default: 10 |
|
42 |
}, |
|
43 |
showSizeChanger: { |
|
44 |
type: Boolean, |
|
45 |
default: true |
|
46 |
}, |
|
47 |
size: { |
|
48 |
type: String, |
|
49 |
default: 'middle' |
|
50 |
}, |
|
51 |
/** |
|
52 |
* alert: { |
|
53 |
* show: true, |
|
54 |
* clear: Function |
|
55 |
* } |
|
56 |
*/ |
|
57 |
alert: { |
|
58 |
type: [Object, Boolean], |
|
59 |
default: null |
|
60 |
}, |
|
61 |
rowSelection: { |
|
62 |
type: Object, |
|
63 |
default: null |
|
64 |
}, |
|
65 |
/** @Deprecated */ |
|
66 |
showAlertInfo: { |
|
67 |
type: Boolean, |
|
68 |
default: false |
|
69 |
}, |
|
70 |
showPagination: { |
|
71 |
type: String | Boolean, |
|
72 |
default: 'auto' |
|
73 |
}, |
|
74 |
/** |
|
75 |
* enable page URI mode |
|
76 |
* |
|
77 |
* e.g: |
|
78 |
* /users/1 |
|
79 |
* /users/2 |
|
80 |
* /users/3?queryParam=test |
|
81 |
* ... |
|
82 |
*/ |
|
83 |
pageURI: { |
|
84 |
type: Boolean, |
|
85 |
default: false |
|
86 |
}, |
|
87 |
extraTool: { |
|
88 |
type: Array, |
|
89 |
default: () => ([]) |
|
90 |
} |
|
91 |
}), |
|
92 |
watch: { |
|
93 |
'localPagination.current' (val) { |
|
94 |
this.pageURI && this.$router.push({ |
|
95 |
...this.$route, |
|
96 |
name: this.$route.name, |
|
97 |
params: Object.assign({}, this.$route.params, { |
|
98 |
pageNo: val |
|
99 |
}) |
|
100 |
}) |
|
101 |
}, |
|
102 |
pageNum (val) { |
|
103 |
Object.assign(this.localPagination, { |
|
104 |
current: val |
|
105 |
}) |
|
106 |
}, |
|
107 |
pageSize (val) { |
|
108 |
Object.assign(this.localPagination, { |
|
109 |
pageSize: val |
|
110 |
}) |
|
111 |
}, |
|
112 |
showSizeChanger (val) { |
|
113 |
Object.assign(this.localPagination, { |
|
114 |
showSizeChanger: val |
|
115 |
}) |
|
116 |
}, |
|
117 |
columns(v) { |
|
118 |
this.columnsSetting = v |
|
119 |
} |
|
120 |
}, |
|
121 |
created () { |
|
122 |
const { pageNo } = this.$route.params |
|
123 |
const localPageNum = this.pageURI && (pageNo && parseInt(pageNo)) || this.pageNum |
|
124 |
this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, { |
|
125 |
current: localPageNum, |
|
126 |
pageSize: this.pageSize, |
|
127 |
showSizeChanger: this.showSizeChanger, |
|
128 |
showTotal: (total, range) => { |
|
129 |
return range[0] + '-' + range[1] + '共' + total + '条' |
|
130 |
} |
|
131 |
}) || false |
|
132 |
this.needTotalList = this.initTotalList(this.columns) |
|
133 |
this.loadData() |
|
134 |
this.columnsSetting = this.columns |
|
135 |
}, |
|
136 |
methods: { |
|
137 |
/** |
|
138 |
* 表格重新加载方法 |
|
139 |
* 如果参数为 true, 则强制刷新到第一页 |
|
140 |
* @param Boolean bool |
|
141 |
*/ |
|
142 |
refresh (bool = false) { |
|
143 |
bool && (this.localPagination = Object.assign({}, { |
|
144 |
current: 1, pageSize: this.pageSize |
|
145 |
})) |
|
146 |
this.loadData() |
|
147 |
}, |
|
148 |
/** |
|
149 |
* 加载数据方法 |
|
150 |
* @param {Object} pagination 分页选项器 |
|
151 |
* @param {Object} filters 过滤条件 |
|
152 |
* @param {Object} sorter 排序条件 |
|
153 |
*/ |
|
154 |
loadData (pagination, filters, sorter) { |
|
155 |
this.localLoading = true |
|
156 |
const parameter = Object.assign({ |
|
157 |
pageNo: (pagination && pagination.current) || |
|
158 |
this.showPagination && this.localPagination.current || this.pageNum, |
|
159 |
pageSize: (pagination && pagination.pageSize) || |
|
160 |
this.showPagination && this.localPagination.pageSize || this.pageSize |
|
161 |
}, |
|
162 |
(sorter && sorter.field && { |
|
163 |
sortField: sorter.field |
|
164 |
}) || {}, |
|
165 |
(sorter && sorter.order && { |
|
166 |
sortOrder: sorter.order |
|
167 |
}) || {}, { |
|
168 |
...filters |
|
169 |
} |
|
170 |
) |
|
171 |
const result = this.data(parameter) |
|
172 |
// 对接自己的通用数据接口需要修改下方代码中的 r.pageNo, r.totalCount, r.data |
|
173 |
// eslint-disable-next-line |
|
174 |
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') { |
|
175 |
result.then(r => { |
|
176 |
if (r == null) { |
|
177 |
this.localLoading = false |
|
178 |
return |
|
179 |
} |
|
180 |
this.localPagination = this.showPagination && Object.assign({}, this.localPagination, { |
|
181 |
current: r.pageNo, // pageNo, // 返回结果中的当前分页数 |
|
182 |
total: r.totalRows, // totalCount, // 返回结果中的总记录数 |
|
183 |
showSizeChanger: this.showSizeChanger, |
|
184 |
pageSize: (pagination && pagination.pageSize) || |
|
185 |
this.localPagination.pageSize |
|
186 |
}) || false |
|
187 |
// 后端数据rows为null保存修复 |
|
188 |
if (r.rows == null) { |
|
189 |
r.rows = [] |
|
190 |
} |
|
191 |
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页 |
|
192 |
if (r.rows.length === 0 && this.showPagination && this.localPagination.current > 1) { |
|
193 |
this.localPagination.current-- |
|
194 |
this.loadData() |
|
195 |
return |
|
196 |
} |
|
197 |
|
|
198 |
// 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小 |
|
199 |
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能 |
|
200 |
try { |
|
201 |
if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.totalPage * this.localPagination.pageSize))) { |
|
202 |
this.localPagination.hideOnSinglePage = true |
|
203 |
} |
|
204 |
} catch (e) { |
|
205 |
this.localPagination = false |
|
206 |
} |
|
207 |
// 返回结果中的数组数据 |
|
208 |
if (this.showPagination === false) { |
|
209 |
// 因为按住小诺的套路,不分页的直接是在data中,我们在界面中直接就是返回了data |
|
210 |
this.localDataSource = r |
|
211 |
} else { |
|
212 |
this.localDataSource = r.rows |
|
213 |
} |
|
214 |
this.localLoading = false |
|
215 |
}) |
|
216 |
} |
|
217 |
}, |
|
218 |
initTotalList (columns) { |
|
219 |
const totalList = [] |
|
220 |
columns && columns instanceof Array && columns.forEach(column => { |
|
221 |
if (column.needTotal) { |
|
222 |
totalList.push({ |
|
223 |
...column, |
|
224 |
total: 0 |
|
225 |
}) |
|
226 |
} |
|
227 |
}) |
|
228 |
return totalList |
|
229 |
}, |
|
230 |
/** |
|
231 |
* 用于更新已选中的列表数据 total 统计 |
|
232 |
* @param selectedRowKeys |
|
233 |
* @param selectedRows |
|
234 |
*/ |
|
235 |
updateSelect (selectedRowKeys, selectedRows) { |
|
236 |
this.selectedRows = selectedRows |
|
237 |
this.selectedRowKeys = selectedRowKeys |
|
238 |
const list = this.needTotalList |
|
239 |
this.needTotalList = list.map(item => { |
|
240 |
return { |
|
241 |
...item, |
|
242 |
total: selectedRows.reduce((sum, val) => { |
|
243 |
const total = sum + parseInt(get(val, item.dataIndex)) |
|
244 |
return isNaN(total) ? 0 : total |
|
245 |
}, 0) |
|
246 |
} |
|
247 |
}) |
|
248 |
}, |
|
249 |
/** |
|
250 |
* 清空 table 已选中项 |
|
251 |
*/ |
|
252 |
clearSelected () { |
|
253 |
if (this.rowSelection) { |
|
254 |
this.rowSelection.onChange([], []) |
|
255 |
this.updateSelect([], []) |
|
256 |
} |
|
257 |
}, |
|
258 |
/** |
|
259 |
* 刷新并清空已选 |
|
260 |
*/ |
|
261 |
clearRefreshSelected (bool = false) { |
|
262 |
this.refresh(bool) |
|
263 |
this.clearSelected() |
|
264 |
}, |
|
265 |
/** |
|
266 |
* 处理交给 table 使用者去处理 clear 事件时,内部选中统计同时调用 |
|
267 |
* @param callback |
|
268 |
* @returns {*} |
|
269 |
*/ |
|
270 |
renderClear (callback) { |
|
271 |
if (this.selectedRowKeys.length <= 0) return null |
|
272 |
return ( |
|
273 |
<a style="margin-left: 24px" onClick={() => { |
|
274 |
callback() |
|
275 |
this.clearSelected() |
|
276 |
}}>清空</a> |
|
277 |
) |
|
278 |
}, |
|
279 |
renderAlert () { |
|
280 |
// 绘制统计列数据 |
|
281 |
// eslint-disable-next-line no-unused-vars |
|
282 |
const needTotalItems = this.needTotalList.map((item) => { |
|
283 |
return (<span style="margin-right: 12px"> |
|
284 |
{item.title}总计 <a style="font-weight: 600">{!item.customRender ? item.total : item.customRender(item.total)}</a> |
|
285 |
</span>) |
|
286 |
}) |
|
287 |
|
|
288 |
// 绘制 清空 按钮 |
|
289 |
// eslint-disable-next-line no-unused-vars |
|
290 |
const clearItem = (typeof this.alert.clear === 'boolean' && this.alert.clear) ? ( |
|
291 |
this.renderClear(this.clearSelected) |
|
292 |
) : (this.alert !== null && typeof this.alert.clear === 'function') ? ( |
|
293 |
this.renderClear(this.alert.clear) |
|
294 |
) : null |
|
295 |
|
|
296 |
// 绘制 alert 组件 |
|
297 |
// 统一先去除alert组件 |
|
298 |
return ( |
|
299 |
<a-alert showIcon={true} style="margin-bottom: 16px"> |
|
300 |
<template slot="message"> |
|
301 |
<span style="margin-right: 12px">已选择: <a style="font-weight: 600">{this.selectedRows.length}</a></span> |
|
302 |
{needTotalItems} |
|
303 |
{clearItem} |
|
304 |
</template> |
|
305 |
</a-alert> |
|
306 |
) |
|
307 |
}, |
|
308 |
columnChange(val) { |
|
309 |
this.columnsSetting = val |
|
310 |
}, |
|
311 |
renderHeader () { |
|
312 |
let tools = [ |
|
313 |
{ |
|
314 |
icon: 'reload', |
|
315 |
title: '刷新', |
|
316 |
onClick: () => { |
|
317 |
this.refresh() |
|
318 |
} |
|
319 |
}, |
|
320 |
{ |
|
321 |
icon: 'column-height', |
|
322 |
title: '密度', |
|
323 |
isDropdown: true, |
|
324 |
menu: () => { |
|
325 |
const onClick = ({ key }) => { |
|
326 |
this.customSize = key |
|
327 |
} |
|
328 |
return ( |
|
329 |
<a-menu slot="overlay" onClick={onClick} selectable defaultSelectedKeys={[this.customSize]}> |
|
330 |
<a-menu-item key="default">默认</a-menu-item> |
|
331 |
<a-menu-item key="middle">中等</a-menu-item> |
|
332 |
<a-menu-item key="small">紧凑</a-menu-item> |
|
333 |
</a-menu> |
|
334 |
) |
|
335 |
}, |
|
336 |
onClick: () => { |
|
337 |
} |
|
338 |
}, |
|
339 |
{ |
|
340 |
icon: 'setting', |
|
341 |
title: '列设置', |
|
342 |
isDropdown: true, |
|
343 |
menu: () => { |
|
344 |
return <columnSetting slot="overlay" columns={this.columns} onColumnChange={this.columnChange} /> |
|
345 |
}, |
|
346 |
onClick: () => { |
|
347 |
} |
|
348 |
} |
|
349 |
] |
|
350 |
if (this.extraTool.length) { |
|
351 |
tools = tools.concat(this.extraTool) |
|
352 |
} |
|
353 |
|
|
354 |
return ( |
|
355 |
<div class="s-table-tool"> |
|
356 |
<div class="s-table-tool-left"> |
|
357 |
{this.$scopedSlots.operator && this.$scopedSlots.operator()} |
|
358 |
</div> |
|
359 |
<div class="s-table-tool-right"> |
|
360 |
{ |
|
361 |
tools.map(tool => { |
|
362 |
if (tool.isDropdown) { |
|
363 |
return ( |
|
364 |
<a-dropdown trigger={['click']}> |
|
365 |
<a-tooltip title={tool.title} class="s-tool-item" onClick={tool.onClick}> |
|
366 |
<a-icon type={tool.icon}/> |
|
367 |
</a-tooltip> |
|
368 |
{ tool.menu() } |
|
369 |
</a-dropdown> |
|
370 |
) |
|
371 |
} |
|
372 |
return ( |
|
373 |
<a-tooltip title={tool.title} class="s-tool-item" onClick={tool.onClick}> |
|
374 |
<a-icon type={tool.icon} /> |
|
375 |
</a-tooltip> |
|
376 |
) |
|
377 |
}) |
|
378 |
} |
|
379 |
</div> |
|
380 |
</div> |
|
381 |
) |
|
382 |
/* |
|
383 |
return ( |
|
384 |
<a-alert showIcon={true} style="margin-bottom: 16px"> |
|
385 |
<template slot="message"> |
|
386 |
<span style="margin-right: 12px">已选择: <a style="font-weight: 600">{this.selectedRows.length}</a></span> |
|
387 |
{needTotalItems} |
|
388 |
{clearItem} |
|
389 |
</template> |
|
390 |
</a-alert> |
|
391 |
) |
|
392 |
*/ |
|
393 |
} |
|
394 |
}, |
|
395 |
|
|
396 |
render () { |
|
397 |
let props = {} |
|
398 |
const localKeys = Object.keys(this.$data) |
|
399 |
const showAlert = (typeof this.alert === 'object' && this.alert !== null && this.alert.show) && typeof this.rowSelection.selectedRowKeys !== 'undefined' || this.alert |
|
400 |
|
|
401 |
Object.keys(T.props).forEach(k => { |
|
402 |
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}` |
|
403 |
if (localKeys.includes(localKey)) { |
|
404 |
props[k] = this[localKey] |
|
405 |
return props[k] |
|
406 |
} |
|
407 |
if (k === 'rowSelection') { |
|
408 |
if (showAlert && this.rowSelection) { |
|
409 |
// 如果需要使用alert,则重新绑定 rowSelection 事件 |
|
410 |
props[k] = { |
|
411 |
...this.rowSelection, |
|
412 |
selectedRows: this.selectedRows, |
|
413 |
selectedRowKeys: this.selectedRowKeys, |
|
414 |
onChange: (selectedRowKeys, selectedRows) => { |
|
415 |
this.updateSelect(selectedRowKeys, selectedRows) |
|
416 |
typeof this[k].onChange !== 'undefined' && this[k].onChange(selectedRowKeys, selectedRows) |
|
417 |
} |
|
418 |
} |
|
419 |
return props[k] |
|
420 |
} else if (!this.rowSelection) { |
|
421 |
// 如果没打算开启 rowSelection 则清空默认的选择项 |
|
422 |
props[k] = null |
|
423 |
return props[k] |
|
424 |
} |
|
425 |
} |
|
426 |
this[k] && (props[k] = this[k]) |
|
427 |
// 此处配置表格大小与要显示的列 |
|
428 |
props = { |
|
429 |
...props, |
|
430 |
size: this.customSize, |
|
431 |
columns: this.columnsSetting.filter(value => value.checked === undefined || value.checked) |
|
432 |
} |
|
433 |
return props[k] |
|
434 |
}) |
|
435 |
const table = ( |
|
436 |
<a-table {...{ props, scopedSlots: { ...this.$scopedSlots } }} onChange={this.loadData} onExpand={ (expanded, record) => { this.$emit('expand', expanded, record) } }> |
|
437 |
{ Object.keys(this.$slots).map(name => (<template slot={name}>{this.$slots[name]}</template>)) } |
|
438 |
</a-table> |
|
439 |
) |
|
440 |
|
|
441 |
return ( |
|
442 |
<div class="table-wrapper"> |
|
443 |
{ this.renderHeader() } |
|
444 |
{ showAlert ? this.renderAlert() : null } |
|
445 |
{ table } |
|
446 |
</div> |
|
447 |
) |
|
448 |
} |
|
449 |
} |