index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. import T from 'ant-design-vue/es/table/Table'
  2. import get from 'lodash.get'
  3. import BaseTool from '@/utils/tool'
  4. export default {
  5. data () {
  6. return {
  7. needTotalList: [],
  8. selectedRows: [],
  9. selectedRowKeys: [],
  10. checkCustomMap: {},
  11. localLoading: false,
  12. localDataSource: [],
  13. // localPagination: Object.assign({}, this.pagination)
  14. localPagination: Object.assign({}, {
  15. ...this.pagination,
  16. showTotal: total => `共 ${total} 条数据`,
  17. showSizeChanger: true,
  18. pageSizeOptions: ['10', '20', '50', '100', '150']
  19. })
  20. }
  21. },
  22. props: Object.assign({}, T.props, {
  23. widthSpace: {
  24. type: Boolean,
  25. default: false
  26. },
  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: 50
  42. },
  43. showSizeChanger: {
  44. type: Boolean,
  45. default: true
  46. },
  47. size: {
  48. type: String,
  49. default: 'default'
  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. }),
  88. watch: {
  89. columns (val, oldVal) { // 深度监听,可监听到对象、数组的变化
  90. let isChecked = 0
  91. val.forEach((item, index) => {
  92. if (item['checked'] == null) {
  93. item['checked'] = false
  94. }
  95. if (item['checked']) {
  96. isChecked++
  97. }
  98. this.checkCustomMap[item.title] = item
  99. })
  100. if (isChecked === 0) {
  101. val.forEach((item, index) => {
  102. if (index < 8) {
  103. item['checked'] = true
  104. this.checkCustomMap[item.title] = item
  105. }
  106. if (item.title === '操作') {
  107. item['checked'] = true
  108. this.checkCustomMap[item.title] = item
  109. }
  110. })
  111. }
  112. },
  113. widthSpace (val) {
  114. Object.assign(this.localPagination, {
  115. widthSpace: val
  116. })
  117. },
  118. 'localPagination.current' (val) {
  119. this.pageURI && this.$router.push({
  120. ...this.$route,
  121. name: this.$route.name,
  122. params: Object.assign({}, this.$route.params, {
  123. pageNum: val
  124. })
  125. })
  126. },
  127. pageNum (val) {
  128. Object.assign(this.localPagination, {
  129. current: val
  130. })
  131. },
  132. pageSize (val) {
  133. Object.assign(this.localPagination, {
  134. pageSize: val
  135. })
  136. },
  137. showSizeChanger (val) {
  138. Object.assign(this.localPagination, {
  139. showSizeChanger: val
  140. })
  141. }
  142. },
  143. created () {
  144. const { pageNum } = this.$route.params
  145. const localPageNum = this.pageURI && (pageNum && parseInt(pageNum)) || this.pageNum
  146. this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, {
  147. current: localPageNum,
  148. pageSize: this.pageSize,
  149. showSizeChanger: this.showSizeChanger
  150. }) || false
  151. this.needTotalList = this.initTotalList(this.columns)
  152. let isChecked = 0
  153. this.columns.forEach((item, index) => {
  154. if (item['checked'] == null) {
  155. item['checked'] = false
  156. }
  157. if (item['checked']) {
  158. isChecked++
  159. }
  160. this.checkCustomMap[item.title] = item
  161. })
  162. if (isChecked === 0) {
  163. this.columns.forEach((item, index) => {
  164. if (index < 8) {
  165. item['checked'] = true
  166. this.checkCustomMap[item.title] = item
  167. }
  168. if (item.title === '操作') {
  169. item['checked'] = true
  170. this.checkCustomMap[item.title] = item
  171. }
  172. })
  173. }
  174. this.loadData()
  175. },
  176. methods: {
  177. /**
  178. * 表格重新加载方法
  179. * 如果参数为 true, 则强制刷新到第一页
  180. * @param Boolean bool
  181. */
  182. refresh (bool = false) {
  183. bool && (this.localPagination = Object.assign({}, {
  184. current: 1, pageSize: this.pageSize
  185. }))
  186. this.loadData()
  187. },
  188. /**
  189. * 加载数据方法
  190. * @param {Object} pagination 分页选项器
  191. * @param {Object} filters 过滤条件
  192. * @param {Object} sorter 排序条件
  193. */
  194. loadData (pagination, filters, sorter) {
  195. this.localLoading = true
  196. const dataScope = {
  197. }
  198. sorter && sorter.field && (dataScope.sortName = BaseTool.String.underlineToHump(sorter.field))
  199. sorter && sorter.order && (dataScope.sortBy = sorter.order.slice(0, -3))
  200. const parameter = Object.assign({
  201. pageNum: (pagination && pagination.current) ||
  202. this.showPagination && this.localPagination.current || this.pageNum,
  203. pageSize: (pagination && pagination.pageSize) ||
  204. this.showPagination && this.localPagination.pageSize || this.pageSize
  205. },
  206. {
  207. dataScope: dataScope
  208. },
  209. {
  210. ...filters
  211. }
  212. )
  213. const result = this.data(parameter)
  214. // 对接自己的通用数据接口需要修改下方代码中的 r.pageNo, r.totalCount, r.data
  215. // eslint-disable-next-line
  216. if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
  217. result.then(r => {
  218. this.localPagination = this.showPagination && Object.assign({}, this.localPagination, {
  219. current: r.pageNum, // 返回结果中的当前分页数
  220. total: r.total, // 返回结果中的总记录数
  221. showSizeChanger: this.showSizeChanger,
  222. pageSize: (pagination && pagination.pageSize) ||
  223. this.localPagination.pageSize
  224. }) || false
  225. // 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
  226. if (r.rows.length === 0 && this.showPagination && this.localPagination.current > 1) {
  227. this.localPagination.current--
  228. this.loadData()
  229. return
  230. }
  231. // 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
  232. // 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
  233. try {
  234. if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNum * this.localPagination.pageSize))) {
  235. this.localPagination.hideOnSinglePage = true
  236. }
  237. } catch (e) {
  238. this.localPagination = false
  239. }
  240. this.localDataSource = r.rows // 返回结果中的数组数据
  241. this.localLoading = false
  242. })
  243. }
  244. },
  245. initTotalList (columns) {
  246. const totalList = []
  247. columns && columns instanceof Array && columns.forEach(column => {
  248. if (column.needTotal) {
  249. totalList.push({
  250. ...column,
  251. total: 0
  252. })
  253. }
  254. })
  255. return totalList
  256. },
  257. /**
  258. * 用于更新已选中的列表数据 total 统计
  259. * @param selectedRowKeys
  260. * @param selectedRows
  261. */
  262. updateSelect (selectedRowKeys, selectedRows) {
  263. this.selectedRows = selectedRows
  264. this.selectedRowKeys = selectedRowKeys
  265. const list = this.needTotalList
  266. this.needTotalList = list.map(item => {
  267. return {
  268. ...item,
  269. total: selectedRows.reduce((sum, val) => {
  270. const total = sum + parseInt(get(val, item.dataIndex))
  271. return isNaN(total) ? 0 : total
  272. }, 0)
  273. }
  274. })
  275. },
  276. /**
  277. * 清空 table 已选中项
  278. */
  279. clearSelected () {
  280. if (this.rowSelection) {
  281. this.rowSelection.onChange([], [])
  282. this.updateSelect([], [])
  283. }
  284. },
  285. /**
  286. * 处理交给 table 使用者去处理 clear 事件时,内部选中统计同时调用
  287. * @param callback
  288. * @returns {*}
  289. */
  290. renderClear (callback) {
  291. if (this.selectedRowKeys.length <= 0) return null
  292. return (
  293. <a style="margin-left: 24px" onClick={() => {
  294. callback()
  295. this.clearSelected()
  296. }}>清空</a>
  297. )
  298. },
  299. /** myColumns
  300. * 处理交给 table 使用者去处理 clear 事件时,内部选中统计同时调用
  301. * @returns {*}
  302. */
  303. renderColumns () {
  304. // this.columns
  305. // if (this.selectedRowKeys.length <= 0) { return null }
  306. const checkedKeys = []
  307. const treeData = []
  308. Object.keys(this.checkCustomMap).forEach((key) => {
  309. const item = this.checkCustomMap[key]
  310. if (item.checked) {
  311. checkedKeys.push(item.title)
  312. }
  313. treeData.push({ title: item.title,
  314. key: item.title })
  315. })
  316. // const choose=this.$t('m.common.choose_show_column')
  317. return (
  318. <a style="margin-left: 24px" onClick={() => {
  319. }}>
  320. <a-popover placement="bottom">
  321. <template slot="content">
  322. <a-tree
  323. checkable={true}
  324. onCheck={(checkedKeys) => {
  325. Object.keys(this.checkCustomMap).forEach((key) => {
  326. const item = this.checkCustomMap[key]
  327. item.checked = false
  328. })
  329. checkedKeys.forEach((item) => {
  330. this.checkCustomMap[item].checked = true
  331. })
  332. // 重新渲染render ()函数
  333. this.$forceUpdate()
  334. }}
  335. onSelect={(selectedKeys, info) => {
  336. }}
  337. checkedKeys={checkedKeys}
  338. treeData={treeData} />
  339. </template>
  340. <template slot="title">
  341. <span>选择显示列</span>
  342. </template>
  343. <a-icon type="appstore" />
  344. </a-popover>
  345. </a>
  346. )
  347. },
  348. renderAlert () {
  349. // 绘制统计列数据
  350. const needTotalItems = this.needTotalList.map((item) => {
  351. return (<span style="margin-right: 12px">
  352. {item.title}总计 <a style="font-weight: 600">{!item.customRender ? item.total : item.customRender(item.total)}</a>
  353. </span>)
  354. })
  355. // 绘制 清空 按钮
  356. const clearItem = (typeof this.alert.clear === 'boolean' && this.alert.clear) ? (
  357. this.renderClear(this.clearSelected)
  358. ) : (this.alert !== null && typeof this.alert.clear === 'function') ? (
  359. this.renderClear(this.alert.clear)
  360. ) : null
  361. const renderColumns = this.renderColumns()
  362. // const ss = this.$t('m.common.choose_total')
  363. // 绘制 alert 组件
  364. return (
  365. <a-alert showIcon={true} style="margin-bottom: 16px">
  366. <template slot="message">
  367. <span style="margin-right: 12px">已选择:<a style="font-weight: 600">{this.selectedRows.length}</a></span>
  368. {needTotalItems}
  369. {clearItem}
  370. {renderColumns}
  371. </template>
  372. </a-alert>
  373. )
  374. }
  375. },
  376. render () {
  377. const props = {}
  378. const localKeys = Object.keys(this.$data)
  379. const showAlert = (typeof this.alert === 'object' && this.alert !== null && this.alert.show) && typeof this.rowSelection.selectedRowKeys !== 'undefined' || this.alert
  380. Object.keys(T.props).forEach(k => {
  381. const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
  382. if (localKeys.includes(localKey)) {
  383. props[k] = this[localKey]
  384. return props[k]
  385. }
  386. if (k === 'rowSelection') {
  387. if (showAlert && this.rowSelection) {
  388. // 如果需要使用alert,则重新绑定 rowSelection 事件
  389. props[k] = {
  390. ...this.rowSelection,
  391. selectedRows: this.selectedRows,
  392. selectedRowKeys: this.selectedRowKeys,
  393. onChange: (selectedRowKeys, selectedRows) => {
  394. this.updateSelect(selectedRowKeys, selectedRows)
  395. typeof this[k].onChange !== 'undefined' && this[k].onChange(selectedRowKeys, selectedRows)
  396. }
  397. }
  398. return props[k]
  399. } else if (!this.rowSelection) {
  400. // 如果没打算开启 rowSelection 则清空默认的选择项
  401. props[k] = null
  402. return props[k]
  403. }
  404. }
  405. this[k] && (props[k] = this[k])
  406. return props[k]
  407. })
  408. const myColumns = []
  409. let totalWidth = 0
  410. Object.keys(this.checkCustomMap).forEach((key) => {
  411. const item = this.checkCustomMap[key]
  412. if (item.checked) {
  413. myColumns.push(item)
  414. const width = /^\d+/.exec(item.width)
  415. if (width && width.length > 0) {
  416. totalWidth += parseInt(width[0])
  417. }
  418. }
  419. })
  420. const x = props.scroll ? props.scroll.x : 0
  421. if (x && x > 0) {
  422. props.scroll.x = totalWidth
  423. }
  424. props.columns = myColumns
  425. const myClass = this.widthSpace ? 'future-table-width-space' : ''
  426. const table = (
  427. <a-table class={myClass} {...{ props, scopedSlots: { ...this.$scopedSlots } }} onChange={this.loadData}>
  428. { Object.keys(this.$slots).map(name => (<template slot={name}>{this.$slots[name]}</template>)) }
  429. </a-table>
  430. )
  431. return (
  432. <div class="table-wrapper">
  433. { showAlert ? this.renderAlert() : null }
  434. { table }
  435. </div>
  436. )
  437. }
  438. }