CusTable.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <template>
  2. <div class="cus-table" :class="{'cus-table-normal': !otherStyle, 'cus-table-full': full !== false}">
  3. <div class="cus-table-main" ref="ref_tableMain" :class="{'cus-table-main-no-foot': noFoot !== false}">
  4. <el-table
  5. v-bind="$attrs"
  6. class="ct-table"
  7. ref="ref_table"
  8. :data="tableData"
  9. :border="false"
  10. @sort-change="handleSort"
  11. @filter-change="handleFilter"
  12. @selection-change="v => $emit('update:selected', v)"
  13. >
  14. <el-table-column v-if="selected" type="selection" :width="selectWidth" align="center" :selectable="selectable"/>
  15. <el-table-column v-if="singled" :width="singledWidth" align="center" class-name="single-column">
  16. <template #default="scope">
  17. <div class="single-circle" :class="{active: scope.row[singledKey] === singled[singledKey]}" @click.stop="$emit('update:singled', scope.row)">
  18. <div class="single-circle-in"></div>
  19. </div>
  20. </template>
  21. </el-table-column>
  22. <el-table-column v-if="showIndex" type="index" label="序号" align="center" fixed="left" width="60"/>
  23. <template v-for="(item, index) in tempTableHead" :key="index">
  24. <template v-if="item.children && item.children.length > 0">
  25. <el-table-column :label="item.label" v-if="item.show" align="center">
  26. <template v-for="(item2, index2) in item.children">
  27. <template v-if="item2.children && item.children.length > 0">
  28. <el-table-column :label="item2.label" v-if="item2.show" align="center">
  29. <template v-for="(item3, index3) in item2.children">
  30. <CusTableColumn :item="item3">
  31. <template #[`${item3.value}-column`]="{column}">
  32. <slot :name="`${item3.value}-column`" :column="column">
  33. </slot>
  34. </template>
  35. <template #[`${item3.value}-column-value`]="{scope}">
  36. <slot :name="`${item3.value}-column-value`" :scope="scope">
  37. </slot>
  38. </template>
  39. </CusTableColumn>
  40. </template>
  41. </el-table-column>
  42. </template>
  43. <template v-else>
  44. <CusTableColumn :item="item2">
  45. <template #[`${item2.value}-column`]="{column}">
  46. <slot :name="`${item2.value}-column`" :column="column">
  47. </slot>
  48. </template>
  49. <template #[`${item2.value}-column-value`]="{scope}">
  50. <slot :name="`${item2.value}-column-value`" :scope="scope">
  51. </slot>
  52. </template>
  53. </CusTableColumn>
  54. </template>
  55. </template>
  56. </el-table-column>
  57. </template>
  58. <template v-else>
  59. <CusTableColumn :item="item">
  60. <template #[`${item.value}-column`]="{column}">
  61. <slot :name="`${item.value}-column`" :column="column">
  62. </slot>
  63. </template>
  64. <template #[`${item.value}-column-value`]="{scope}">
  65. <slot :name="`${item.value}-column-value`" :scope="scope">
  66. </slot>
  67. </template>
  68. </CusTableColumn>
  69. </template>
  70. </template>
  71. </el-table>
  72. </div>
  73. <div class="ct-foot" v-if="noFoot === false">
  74. <div class="total">
  75. <template v-if="$util.isValue(selectedNum)">
  76. 已选中 {{ selectedNum }} 条 /
  77. </template>
  78. <template v-else-if="$util.isValue(selected?.length)">
  79. 已选中 {{ selected.length }} 条 /
  80. </template>
  81. 共 {{ total }} 条
  82. </div>
  83. <el-pagination
  84. v-if="noPage === false"
  85. ref="ref_tablePage"
  86. class="__cus-pagination"
  87. :currentPage="page"
  88. :page-size="pageSize"
  89. background
  90. :page-sizes="pageSizes"
  91. :layout="pageLayoutCpt"
  92. :total="Number(total)"
  93. @size-change="handleSizeChange"
  94. @current-change="handleCurrentChange"
  95. />
  96. </div>
  97. </div>
  98. </template>
  99. <script lang="ts">
  100. import {
  101. defineComponent,
  102. computed,
  103. onMounted,
  104. ref,
  105. reactive,
  106. watch,
  107. getCurrentInstance,
  108. nextTick,
  109. ComponentInternalInstance, toRefs
  110. } from 'vue'
  111. import {useStore} from 'vuex'
  112. import {useRouter} from 'vue-router'
  113. export default defineComponent({
  114. name: 'CusTable',
  115. props: {
  116. tableData: {
  117. required: true
  118. },
  119. tableHead: {
  120. default: () => [],
  121. required: true
  122. },
  123. total: {
  124. required: true
  125. },
  126. page: {},
  127. pageSize: {},
  128. pageSizes: {
  129. default: () => [10, 20, 30, 50, 100]
  130. },
  131. selected: {
  132. default: null
  133. },
  134. selectedNum: {
  135. default: null
  136. },
  137. selectWidth: {
  138. default: 50
  139. },
  140. singledWidth: {
  141. default: 50
  142. },
  143. singled: {
  144. default: null
  145. },
  146. singledKey: {
  147. default: 'id'
  148. },
  149. noPage: {
  150. default: false
  151. },
  152. showIndex: {
  153. default: true
  154. },
  155. otherStyle: {
  156. default: false
  157. },
  158. noLayout: {
  159. default: () => []
  160. },
  161. full: {
  162. default: false
  163. },
  164. selectable: {},
  165. noFoot: {
  166. default: false
  167. }
  168. },
  169. setup(props, { emit }) {
  170. const store = useStore();
  171. const router = useRouter();
  172. const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties
  173. const ref_table = ref();
  174. const ref_tableMain = ref();
  175. const ref_tablePage = ref();
  176. const state = reactive({
  177. tempTableHead: <any>[],
  178. pageLayout: ['sizes', 'prev', 'pager', 'next', 'jumper']
  179. });
  180. watch(() => props.tableHead, (nVal: any) => {
  181. formatTableHead(nVal)
  182. }, {deep: true})
  183. // watch(() => props.tableData, (nVal) => {
  184. // ref_table.value?.doLayout()
  185. // }, {deep: true})
  186. const pageLayoutCpt = computed(() => {
  187. return state.pageLayout.filter((v: string) => props.noLayout.every((s: string) => v !== s)).join(',')
  188. })
  189. const formatTableHead = (tableHead: Array<any>) => {
  190. const result = tableHead
  191. const deep = (arr: any[]) => {
  192. let flag = 0
  193. arr.forEach(v => {
  194. if (v.children && v.children.length > 0) {
  195. v.show = deep(v.children)
  196. }
  197. if (v.show) {
  198. flag++
  199. }
  200. })
  201. return flag > 0
  202. }
  203. deep(result)
  204. state.tempTableHead = result
  205. }
  206. const handleSizeChange = (val: Number) => {
  207. emit('handlePage', {
  208. page: 1,
  209. pageSize: val
  210. })
  211. }
  212. const handleCurrentChange = (val: Number) => {
  213. emit('handlePage', {
  214. page: val,
  215. pageSize: props.pageSize
  216. })
  217. }
  218. const handleSort = ({ column, prop, order }: any) => {
  219. emit('handleSort', {
  220. key: prop,
  221. value: order
  222. })
  223. }
  224. const handleFilter = (val: any) => {
  225. const key = Object.keys(val)[0]
  226. const head = props.tableHead.filter(v => v.value === key)[0]
  227. let value = null
  228. if (val[key].length > 0) {
  229. if (head.filterMultiple) {
  230. value = val[key]
  231. } else {
  232. value = val[key][0]
  233. }
  234. }
  235. emit('handleFilter', {key, value})
  236. }
  237. const resetFilter = (key: any = null) => {
  238. key ? ref_table.value.clearFilter([key]) : ref_table.value.clearFilter()
  239. }
  240. const reset = (key: any = null) => {
  241. resetFilter(key)
  242. ref_table.value.clearSort()
  243. }
  244. watch(() => props.selected, (n: any) => {
  245. if (n.length === 0) {
  246. ref_table.value.clearSelection()
  247. } else {
  248. setTimeout(() => {
  249. n.forEach(v => {
  250. ref_table.value.toggleRowSelection(v, true)
  251. })
  252. }, 100)
  253. }
  254. })
  255. onMounted(() => {
  256. formatTableHead(props.tableHead)
  257. })
  258. return {
  259. handleSizeChange,
  260. handleCurrentChange,
  261. handleSort,
  262. resetFilter,
  263. handleFilter,
  264. ref_table,
  265. ref_tableMain,
  266. ref_tablePage,
  267. ...toRefs(state),
  268. pageLayoutCpt,
  269. reset
  270. }
  271. },
  272. })
  273. </script>
  274. <style scoped lang="scss">
  275. .cus-table {
  276. width: 100%;
  277. height: 100%;
  278. max-height: 100%;
  279. display: flex;
  280. flex-direction: column;
  281. $cus-page-height: 32px;
  282. $cus-page-mt: 25px;
  283. position: relative;
  284. :deep(.el-popper) {
  285. max-width: 60% !important;
  286. }
  287. .cus-table-main {
  288. width: 100%;
  289. height: calc(100% - #{$cus-page-height} - #{$cus-page-mt}) !important;
  290. position: absolute;
  291. &.cus-table-main-no-foot {
  292. height: 100% !important;
  293. }
  294. :deep(.ct-table) {
  295. height: 100%;
  296. }
  297. }
  298. :deep(.ct-foot) {
  299. height: $cus-page-height;
  300. font-size: 14px;
  301. font-family: Microsoft YaHei;
  302. font-weight: 400;
  303. color: #999999;
  304. display: flex;
  305. justify-content: space-between;
  306. .total {
  307. display: flex;
  308. align-items: center;
  309. }
  310. }
  311. &.cus-table-normal {
  312. .cus-table-main {
  313. :deep(.ct-table) {
  314. width: 100%;
  315. //height: calc(100% - #{$cus-page-height} - 20px);
  316. .el-checkbox__inner {
  317. &:after {
  318. }
  319. }
  320. $borderColor: #FFFFFF;
  321. $borderWidth: 2px;
  322. // 表格左边框
  323. &::before, .el-table__border-left-patch {
  324. display: none;
  325. }
  326. // 表格上边框
  327. .el-table__inner-wrapper::after {
  328. display: none;
  329. }
  330. // 表格右边框
  331. &.el-table--border::after {
  332. display: none;
  333. }
  334. // 表格下边框
  335. .el-table__inner-wrapper::before {
  336. display: none;
  337. }
  338. .el-table__header-wrapper, .el-table__fixed-right, .el-table__fixed {
  339. .el-table__header {
  340. tr {
  341. >th {
  342. border-left: $borderWidth solid $borderColor;
  343. border-top: $borderWidth solid $borderColor;
  344. border-right: none;
  345. border-bottom: none;
  346. background-color: #F5F5F5;
  347. height: 52px;
  348. .cell {
  349. font-size: 14px;
  350. font-family: 微软雅黑;
  351. font-weight: 400;
  352. color: #666666;
  353. padding: 0 4px;
  354. &.highlight {
  355. color: #409eff;
  356. }
  357. }
  358. }
  359. &:first-child {
  360. >th {
  361. border-top: none;
  362. &:first-child {
  363. border-left: none;
  364. }
  365. }
  366. }
  367. }
  368. }
  369. }
  370. .el-table__body-wrapper, .el-table__fixed-body-wrapper {
  371. .el-table__body {
  372. .el-table__row {
  373. >td {
  374. border-right: none;
  375. border-bottom-color: #F4F4F4;
  376. .cell {
  377. font-size: 14px;
  378. font-family: 微软雅黑;
  379. font-weight: 400;
  380. color: #666666;
  381. white-space: nowrap;
  382. overflow: hidden;
  383. text-overflow: ellipsis;
  384. word-break: break-all;
  385. padding: 0 6px;
  386. .el-link +.el-link {
  387. margin-left: 10px;
  388. }
  389. }
  390. }
  391. }
  392. }
  393. }
  394. }
  395. }
  396. :deep(.ct-foot) {
  397. margin-top: auto;
  398. }
  399. }
  400. &.cus-table-full {
  401. .cus-table-main {
  402. position: unset;
  403. height: 100% !important;
  404. }
  405. :deep(.ct-foot) {
  406. margin-top: $cus-page-mt;
  407. }
  408. }
  409. }
  410. :deep(.single-column) {
  411. .cell {
  412. display: flex;
  413. justify-content: center;
  414. align-items: center;
  415. .single-circle {
  416. width: 16px;
  417. height: 16px;
  418. border: 1px solid rgba(96, 98, 102, 0.6);
  419. border-radius: 50%;
  420. cursor: pointer;
  421. display: flex;
  422. justify-content: center;
  423. align-items: center;
  424. &.active {
  425. background-color: #22a5fe;
  426. border-color: #22a5fe;
  427. }
  428. .single-circle-in {
  429. width: 6px;
  430. height: 6px;
  431. border-radius: 50%;
  432. background-color: #FFFFFF;
  433. }
  434. }
  435. }
  436. }
  437. </style>