index.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <template>
  2. <div
  3. v-loading="loading"
  4. element-loading-text="拼命加载中"
  5. element-loading-spinner="el-icon-loading"
  6. element-loading-background="rgba(0, 0, 0, 0.8)"
  7. class="table-wrapper"
  8. >
  9. <el-table
  10. ref="table"
  11. v-el-table-infinite-scroll="load"
  12. :data="dealedTableData"
  13. :header-cell-class-name="headerCellClass"
  14. :row-class-name="rowClass"
  15. :cell-class-name="cellClass"
  16. :span-method="tableSpanMethod"
  17. :show-summary="showSummary"
  18. :summary-method="tableSummaryMethod"
  19. :height="height"
  20. stripe
  21. fit
  22. border
  23. @cell-click="cellClickHandler"
  24. >
  25. <el-table-column
  26. v-for="col in filteredTableCols"
  27. :key="col.columnName"
  28. :prop="col.columnName"
  29. :label="col.columnLabel"
  30. :width="col.width"
  31. :show-overflow-tooltip="showOverflowTooltip"
  32. :formatter="tableFormatter"
  33. >
  34. <template #header>
  35. <el-tooltip
  36. :content="col.columnDescribe || col.columnLabel"
  37. placement="top"
  38. >
  39. <TableHeaderCell
  40. :label="col.columnLabel"
  41. :filter-options="tableDataFilters[col.columnName]"
  42. :filter-values.sync="filterValues[col.columnName]"
  43. filter-style="arrow"
  44. :sortable="!!col.needSort"
  45. :sort-rule.sync="tableDataSortRules[col.columnName]"
  46. />
  47. </el-tooltip>
  48. </template>
  49. </el-table-column>
  50. </el-table>
  51. </div>
  52. </template>
  53. <script>
  54. import TableHeaderCell from '../TableHeaderCell'
  55. import { setTableFilters } from '@/utils/table'
  56. import { mapGetters } from 'vuex'
  57. export default {
  58. name: 'SimpleTable',
  59. components: { TableHeaderCell },
  60. props: {
  61. loading: {
  62. type: Boolean,
  63. default: false
  64. },
  65. height: {
  66. type: [String, Number],
  67. default: '100%'
  68. },
  69. tableCols: {
  70. type: Array,
  71. default: () => []
  72. },
  73. data: {
  74. type: Array,
  75. default: () => []
  76. },
  77. headerCellClassName: {
  78. type: Function
  79. },
  80. rowClassName: {
  81. type: Function
  82. },
  83. cellClassName: {
  84. type: Function
  85. },
  86. spanMethod: {
  87. type: Function
  88. },
  89. // 是否显示合计行
  90. showSummary: {
  91. type: Boolean,
  92. default: false
  93. },
  94. summaryMethod: {
  95. type: Function
  96. },
  97. // 不换行,溢出隐藏
  98. showOverflowTooltip: {
  99. type: Boolean,
  100. default: false
  101. },
  102. formatter: {
  103. type: Function
  104. }
  105. },
  106. data() {
  107. return {
  108. tableData: [],
  109. tableDataFilters: {}, // 表头-下拉数据
  110. filterValues: {}, // 表头-下拉-选中数据
  111. tableDataSortRules: {}, // 表头-排序
  112. tableGroups: [], // 表格分组规则
  113. spanArr: [] // 表格分组数据缓存
  114. }
  115. },
  116. computed: {
  117. ...mapGetters(['clickedCells']),
  118. filteredTableCols() {
  119. return this.tableCols.filter(col => col.needShow)
  120. },
  121. dealedTableData() {
  122. const filtered = this.tableData.filter(item => {
  123. let flag = true
  124. Object.entries(this.filterValues).forEach(([key, arr]) => {
  125. if (arr.length && !arr.includes(item[key])) {
  126. flag = false
  127. }
  128. })
  129. return flag
  130. })
  131. const sortRules = Object.entries(this.tableDataSortRules).reduce(
  132. (pre, [key, value]) => {
  133. if (value) {
  134. pre[0].push(key)
  135. value = value === 'ascending' ? 'asc' : 'desc'
  136. pre[1].push(value)
  137. }
  138. return pre
  139. },
  140. [[], []]
  141. )
  142. return this._.orderBy(filtered, sortRules[0], sortRules[1])
  143. }
  144. },
  145. watch: {
  146. data: {
  147. handler(arr) {
  148. this.tableData = arr
  149. },
  150. deep: true
  151. },
  152. tableData: {
  153. handler() {
  154. this.setTableFilters()
  155. },
  156. deep: true
  157. },
  158. tableCols: {
  159. handler() {
  160. this.setTableFilters()
  161. },
  162. deep: true
  163. },
  164. dealedTableData: {
  165. handler(arr) {
  166. const spanArr = []
  167. let pos = 0
  168. arr.forEach((item, index, arr) => {
  169. if (index === 0) {
  170. spanArr.push(1)
  171. } else {
  172. if (this.tableGroups.every(prop => arr[index][prop] === arr[index - 1][prop])) {
  173. spanArr[pos] += 1
  174. spanArr.push(0)
  175. } else {
  176. spanArr.push(1)
  177. pos = index
  178. }
  179. }
  180. })
  181. this.spanArr = spanArr
  182. },
  183. deep: true
  184. },
  185. headerCellClassName: {
  186. handler(func) {
  187. if (func) {
  188. this.headerCellClass = func.bind(this)
  189. }
  190. },
  191. deep: true
  192. },
  193. rowClassName: {
  194. handler(func) {
  195. if (func) {
  196. this.rowClass = func.bind(this)
  197. }
  198. },
  199. deep: true
  200. },
  201. cellClassName: {
  202. handler(func) {
  203. if (func) {
  204. this.cellClass = func.bind(this)
  205. }
  206. },
  207. deep: true
  208. },
  209. spanMethod: {
  210. handler(func) {
  211. if (func) {
  212. this.tableSpanMethod = func.bind(this)
  213. }
  214. },
  215. deep: true
  216. },
  217. summaryMethod: {
  218. handler(func) {
  219. if (func) {
  220. this.tableSummaryMethod = func.bind(this)
  221. }
  222. },
  223. deep: true
  224. },
  225. formatter: {
  226. handler(func) {
  227. if (func) {
  228. this.tableFormatter = func
  229. }
  230. }
  231. }
  232. },
  233. updated() {
  234. this.$refs['table']?.doLayout()
  235. },
  236. methods: {
  237. load() {
  238. this.$emit('load')
  239. },
  240. headerCellClass({ row, column, rowIndex, columnIndex }) {
  241. const classes = []
  242. const rule = this.tableDataSortRules[column.property]
  243. if (rule) {
  244. classes.push(rule)
  245. }
  246. return classes.join(' ')
  247. },
  248. rowClass() {
  249. return ''
  250. },
  251. cellClass() {
  252. return ''
  253. },
  254. tableSpanMethod({ row, column, rowIndex, columnIndex }) {
  255. if (this.tableGroups.includes(column.property)) {
  256. const _row = this.spanArr[rowIndex]
  257. const _col = _row > 0 ? 1 : 0
  258. return {
  259. rowspan: _row,
  260. colspan: _col
  261. }
  262. }
  263. },
  264. tableSummaryMethod(param) {
  265. const { columns, data } = param
  266. const sums = []
  267. columns.forEach((column, index) => {
  268. this.tableCols.forEach(p => {
  269. if (column.property === p.columnName && p.needCount) {
  270. const values = data.map(item => Number(item[column.property]))
  271. if (!values.every(value => isNaN(value))) {
  272. sums[index] = values.reduce((prev, curr) => {
  273. const value = Number(curr)
  274. if (!isNaN(value)) {
  275. return prev + curr
  276. } else {
  277. return prev
  278. }
  279. }, 0)
  280. sums[index] += ''
  281. }
  282. }
  283. })
  284. })
  285. return sums
  286. },
  287. cellClickHandler(...params) {
  288. this.$emit('cell-click', ...params)
  289. },
  290. tableFormatter(row, column, cellValue) {
  291. return cellValue
  292. },
  293. setTableFilters() {
  294. this.tableDataFilters = {}
  295. this.filteredTableCols.forEach(col => {
  296. this.tableDataFilters[col.columnName] = []
  297. if (col.needGroup) {
  298. this.tableGroups.push(col.columnName)
  299. }
  300. })
  301. setTableFilters(this.tableData, this.tableDataFilters)
  302. }
  303. }
  304. }
  305. </script>
  306. <style lang="scss" scoped>
  307. .table-wrapper {
  308. width: 100%;
  309. height: 100%;
  310. ::v-deep .el-table {
  311. width: 100%;
  312. .cell {
  313. padding: 0;
  314. text-align: center;
  315. font-size: 14px;
  316. font-family: Helvetica, 'Microsoft YaHei';
  317. letter-spacing: 0;
  318. }
  319. .cell-click {
  320. cursor: pointer;
  321. color: #2d7cff;
  322. &.cell-clicked {
  323. color: purple;
  324. }
  325. }
  326. .el-table__header-wrapper {
  327. .cell {
  328. font-weight: bold;
  329. color: #101116;
  330. > .el-checkbox {
  331. display: none;
  332. }
  333. }
  334. }
  335. }
  336. }
  337. </style>