inleft
2022-02-21 c5e66af68d7eded8bcc55e0fe26d034d30735c16
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   }