当前位置:   article > 正文

【通用表格组件】vue3 + element-ui + tsx 实现通用表格组件_tsx elementui

tsx elementui

介绍: 这是基于 vue3 + el-table 封装的通用表格组件 的 tsx写法,想要参考模板写法的可以到我另一篇博客喔~

【通用表格组件】vue3 + element-ui + template 实现通用表格组件

话不多说,本组件分为以下四部分,并且在同一层级:

1、CommonTable.module.scss 文件为组件样式文件

  1. :global {
  2. .common-table {
  3. .el-table__header,
  4. .el-table__body {
  5. margin: 0;
  6. }
  7. .el-table::before {
  8. height: 0;
  9. }
  10. .el-button {
  11. padding: 0;
  12. border: none;
  13. margin: 0 4px;
  14. padding: 0 4px 0 8px;
  15. border-left: 1px solid #e2e2e2;
  16. font-size: 14px;
  17. min-height: 14px;
  18. &:first-child {
  19. border-left: none;
  20. }
  21. }
  22. .el-button+.el-button {
  23. margin-left: 0;
  24. }
  25. .btn-right div {
  26. margin-right: 5px;
  27. }
  28. .btn-right div:empty {
  29. margin-right: 0px;
  30. }
  31. //斑马纹表格背景色
  32. .el-table .even-row {
  33. --el-table-tr-background-color: #f5fafb;
  34. }
  35. .el-table .odd-row {
  36. --el-table-tr-background-color: #ffffff;
  37. }
  38. .el-table--border::after,
  39. .el-table--group::after {
  40. width: 0;
  41. }
  42. .el-table__fixed-right::before,
  43. .el-table__fixed::before {
  44. background-color: transparent;
  45. }
  46. .custom-table-header {
  47. th {
  48. background-color: #fff4d9 !important;
  49. }
  50. }
  51. .progress-line {
  52. .el-progress-bar__outer {
  53. height: 16px !important;
  54. }
  55. .el-progress-bar__outer,
  56. .el-progress-bar__inner {
  57. border-radius: 0 !important;
  58. }
  59. }
  60. .text-no-wrap {
  61. cursor: pointer;
  62. display: inline;
  63. }
  64. .el-table {
  65. td.el-table__cell div,
  66. th.el-table__cell>.cell {
  67. font-size: 14px;
  68. }
  69. th.el-table__cell>.cell {
  70. font-weight: normal;
  71. }
  72. .cell {
  73. padding: 0 10px;
  74. line-height: 39px;
  75. }
  76. .el-table__header-wrapper .checkBoxRadio .el-checkbox {
  77. display: none;
  78. }
  79. .el-checkbox {
  80. display: flex;
  81. align-items: center;
  82. justify-content: center;
  83. }
  84. .table-img {
  85. width: 60px;
  86. height: 60px;
  87. object-fit: cover;
  88. padding: 6px 0;
  89. display: flex;
  90. align-items: center;
  91. margin: 0 auto;
  92. justify-content: center;
  93. }
  94. }
  95. .el-table--small .el-table__cell {
  96. padding: 0;
  97. }
  98. .el-dropdown-menu__item {
  99. padding: 5px 10px !important;
  100. .el-button {
  101. width: 100%;
  102. text-align: center;
  103. padding: 0 8px;
  104. margin: 0;
  105. }
  106. }
  107. .flex-box {
  108. display: flex;
  109. flex-flow: row nowrap;
  110. justify-content: flex-start;
  111. .item {
  112. margin: 0 10px;
  113. }
  114. }
  115. }
  116. .el-table{
  117. .el-table__expand-icon{
  118. .el-icon-arrow-right{
  119. display: none;
  120. }
  121. }
  122. }
  123. }

2、CommonTable.module.ts为组件逻辑文件

  1. import {
  2. ref,
  3. watch,
  4. nextTick
  5. } from 'vue';
  6. export default function(props, emit, commonTable) {
  7. const curPageCheck = ref([])
  8. const selectId = ref()
  9. watch(() => props.selectRadioId, () => {
  10. selectId.value = props.selectRadioId
  11. }, { immediate: true })
  12. watch(() => props.data, () => {
  13. if (props.showCheckBox || props.turnRadio) {
  14. nextTick(() => {
  15. commonTable.value.clearSelection()
  16. curPageCheck.value = []
  17. if (props.showCheckBox && props.turnRadio) {
  18. props.data.filter((item) => {
  19. if (item.id === props.selectedIdArr[0]) {
  20. commonTable.value.toggleRowSelection(item, true)
  21. }
  22. })
  23. } else if (props.showCheckBox) {
  24. props.data.filter((item) => {
  25. if (props.selectedIdArr.includes(item.id)) {
  26. commonTable.value.toggleRowSelection(item, true)
  27. curPageCheck.value.push(item.id)
  28. }
  29. })
  30. }
  31. })
  32. }
  33. }, {
  34. immediate: true
  35. })
  36. watch(() => props.selectedIdArr, (val) => {
  37. if (props.showCheckBox || props.turnRadio) {
  38. nextTick(() => {
  39. commonTable.value.clearSelection()
  40. curPageCheck.value = []
  41. if (props.showCheckBox && props.turnRadio) {
  42. props.data.filter((item) => {
  43. if (item.id === val[0]) {
  44. commonTable.value.toggleRowSelection(item, true)
  45. }
  46. })
  47. } else if (props.showCheckBox) {
  48. props.data.filter((item) => {
  49. if (val.includes(item.id)) {
  50. commonTable.value.toggleRowSelection(item, true)
  51. curPageCheck.value.push(item.id)
  52. }
  53. })
  54. }
  55. })
  56. }
  57. }, {
  58. immediate: true
  59. })
  60. const methods = {
  61. /**
  62. * prop 单值 或者 数组过滤(此处为针对时间组,不作为通用处理)
  63. */
  64. propFilter(prop, row) {
  65. const res = prop.reduce((total, cur) => {
  66. if (row[cur]) {
  67. return (total += row[cur] + '~')
  68. } else {
  69. return ''
  70. }
  71. }, '')
  72. return res ? res.replace(/~$/, '') : ''
  73. },
  74. handleTableButton(row, type) {
  75. emit('operation', row, type);
  76. },
  77. /**
  78. * 后续扩展位
  79. * @param {*} methods
  80. * @param {*} row
  81. */
  82. handleClickon(methods, row) {
  83. emit(methods, { methods, row })
  84. },
  85. handleSelectionChange(val) {
  86. if (props.showCheckBox && props.turnRadio) {
  87. // 选择项大于1时
  88. if (val.length > 1) {
  89. const del_row = val.shift()
  90. commonTable.value.toggleRowSelection(del_row, false)
  91. }
  92. }
  93. // 全选
  94. if (props.showCheckBox && props.selectedIdArr) {
  95. if (props.turnRadio) {
  96. emit('handle-selection-change', val)
  97. } else {
  98. // 一般复选框都是走到这一步
  99. emit('handle-selection-change', val)
  100. }
  101. } else {
  102. emit('handle-selection-change', val)
  103. }
  104. },
  105. getRowKeys(row) {
  106. return row.id
  107. },
  108. selectAll(val) {
  109. if (props.showCheckBox && props.turnRadio) {
  110. // 选择项大于1时
  111. if (val.length > 1) {
  112. val.length = 1
  113. }
  114. }
  115. emit('handle-selection-change', val)
  116. },
  117. // 斑马纹表格背景色
  118. tabRowClassName({ rowIndex }) {
  119. const index = rowIndex + 1
  120. if (index % 2 === 0) {
  121. return 'even-row'
  122. } else {
  123. return 'odd-row'
  124. }
  125. },
  126. cellClassName({ row, columnIndex }) {
  127. if (row.confirmTag === 2 && columnIndex < props.tableLabel.length) {
  128. return 'height_light_cell'
  129. } else {
  130. return ''
  131. }
  132. },
  133. buttonDisabled(item, row) {
  134. if (typeof item.disabled === 'function') return item.disabled(row) || false
  135. if (!item.disabled) return item.disabled
  136. },
  137. /**
  138. * 单选框选中事件
  139. */
  140. rowClick(row) {
  141. if (row) {
  142. selectId.value = row.id;
  143. emit('rowClick', row)
  144. }
  145. }
  146. };
  147. return {
  148. selectId,
  149. methods
  150. }
  151. }

3、CommonTable.tsx为组件渲染文件

  1. import {
  2. defineComponent,
  3. ref,
  4. PropType
  5. } from 'vue';
  6. import {
  7. OptionLabel,
  8. TableLabel
  9. } from './types';
  10. import useModule from './commonTable.module';
  11. import './commonTable.module.scss';
  12. export default defineComponent({
  13. name: 'CommonTable',
  14. props: {
  15. /**
  16. * 表格最高高度
  17. */
  18. maxHeight: {
  19. type: [String, Number],
  20. default: ''
  21. },
  22. /**
  23. * 表格自定义属性展示
  24. */
  25. tableLabel: {
  26. type: Array as PropType<TableLabel[]>,
  27. default: () => []
  28. },
  29. /**
  30. * 表格数据源
  31. */
  32. data: {
  33. type: Array,
  34. default: () => []
  35. },
  36. /**
  37. * 配置需要显示的操作菜单
  38. */
  39. option: {
  40. type: Object as PropType<OptionLabel>,
  41. default: () => {}
  42. },
  43. showCheckBox: {
  44. // 配置是否显示全选(复选框)
  45. type: Boolean,
  46. default: false
  47. },
  48. /** 是否复选框禁用 */
  49. isEnable: {
  50. type: Boolean,
  51. default: false
  52. },
  53. /** 是否复选框禁用的方法 */
  54. enableCheckBox: {
  55. type: Function as PropType<(row: any) => boolean>,
  56. default: null
  57. },
  58. /**
  59. * 是否显示索引
  60. */
  61. showIndex: {
  62. type: Boolean,
  63. default: false
  64. },
  65. turnRadio: {
  66. type: Boolean,
  67. default: false
  68. },
  69. selectedIdArr: {
  70. type: Array as PropType<number[] | string[]>,
  71. default: () => []
  72. },
  73. /**
  74. * 是否 隐藏文字过长
  75. */
  76. overflowText: {
  77. type: Boolean,
  78. default: false
  79. },
  80. /**
  81. * 加载提示
  82. */
  83. loading: {
  84. type: Boolean,
  85. default: false
  86. },
  87. /**
  88. * 是否保持之前复选框的数据
  89. */
  90. keep: {
  91. type: Boolean,
  92. default: false
  93. },
  94. /**
  95. * 动态绑定 key 值
  96. */
  97. keyId: {
  98. type: String,
  99. default: 'id'
  100. },
  101. /**
  102. * 行内自定义样式配置
  103. */
  104. rowStyle: {
  105. type: Object,
  106. default: () => {
  107. return {
  108. height: '40px'
  109. }
  110. }
  111. },
  112. /**
  113. * 是否展示展开按钮
  114. */
  115. showExpand: {
  116. type: Boolean,
  117. default: false
  118. },
  119. /**
  120. * 单选模式
  121. */
  122. showRadio: {
  123. type: Boolean,
  124. default: false
  125. },
  126. selectRadioId: {
  127. type: String,
  128. default: ''
  129. }
  130. },
  131. setup(props, { emit, slots }) {
  132. const commonTable = ref(null)
  133. const {
  134. selectId,
  135. methods
  136. } = useModule(props, emit, commonTable)
  137. return () => (
  138. <div v-loading={props.loading}>
  139. <el-table
  140. class='common-table'
  141. ref={commonTable}
  142. data={props.data}
  143. border
  144. highlight-current-row={props.showRadio}
  145. max-height={props.maxHeight}
  146. row-class-name={methods.tabRowClassName}
  147. row-style={props.rowStyle}
  148. cell-class-name={methods.cellClassName}
  149. header-row-class-name='custom-table-header'
  150. row-key={props.keyId}
  151. on-select={methods.handleSelectionChange}
  152. on-select-all={methods.handleSelectionChange}
  153. on-current-change={methods.rowClick}
  154. >
  155. {
  156. props.showRadio && <el-table-column
  157. label='选择'
  158. align='center'
  159. width='55'
  160. scopedSlots={{
  161. default: scope => (
  162. <el-radio
  163. label={scope.row.url}
  164. vModel={selectId.value}
  165. onChange={() => methods.rowClick(scope.row)}
  166. >
  167. &nbsp;
  168. </el-radio>
  169. )
  170. }}
  171. />
  172. }
  173. { props.isEnable ? (props.showCheckBox && <el-table-column
  174. key='showCheckBox'
  175. width='55'
  176. type='selection'
  177. reserve-selection={props.keep}
  178. selectable={props.enableCheckBox}
  179. class-name={props.turnRadio ? 'checkBoxRadio' : ''}
  180. align='center'
  181. />) : (props.showCheckBox && <el-table-column
  182. key='showCheckBox'
  183. width='55'
  184. type='selection'
  185. reserve-selection={props.keep}
  186. class-name={props.turnRadio ? 'checkBoxRadio' : ''}
  187. align='center'
  188. />)
  189. }
  190. {
  191. props.showExpand && <el-table-column
  192. key='showExpand'
  193. type='expand'
  194. scopedSlots={{
  195. default: scope => {
  196. return <fragment row={scope.row}>
  197. {
  198. slots.expand?.()
  199. }
  200. </fragment>
  201. }
  202. }}
  203. >
  204. </el-table-column>
  205. }
  206. {
  207. props.showIndex && <el-table-column
  208. align='center'
  209. label='序号'
  210. width='50'
  211. scopedSlots={{
  212. default: scope => {
  213. return scope.$index + 1
  214. }
  215. }}
  216. >
  217. </el-table-column>
  218. }
  219. {
  220. props.tableLabel.map((item: TableLabel) => {
  221. return <el-table-column
  222. key={item[props.keyId]}
  223. width={item.width ?? ''}
  224. align={item.align ?? 'center'}
  225. label={item.label}
  226. show-overflow-tooltip={props.overflowText}
  227. fixed={item.fixed}
  228. prop={item.prop}
  229. scopedSlots={{
  230. default: (scope) => {
  231. if (item.render) {
  232. return <div
  233. style='cursor: pointer'
  234. onClick={() => item.methods && methods.handleClickon(item.methods, scope.row)}
  235. domPropsInnerHTML={item.render(scope.row)}
  236. >
  237. </div>
  238. } else {
  239. return <div
  240. class='text-no-wrap'
  241. onClick={() => item.methods && methods.handleClickon(item.methods, scope.row)}>
  242. {
  243. Object.prototype.toString.call(item.prop) === '[object Array]' ? methods.propFilter(item.prop, scope.row) : (scope.row[item.prop] ?? '--')
  244. }
  245. </div>
  246. }
  247. }
  248. }}
  249. >
  250. </el-table-column>
  251. })
  252. }
  253. {
  254. props.option && <el-table-column
  255. width={props.option.width}
  256. label={props.option.label}
  257. fixed={props.option.fixed}
  258. align={props.option.align ?? 'center'}
  259. scopedSlots={{
  260. default: scope => {
  261. return props.option.children.length && <div
  262. class='flex-box'
  263. >
  264. {
  265. props.option.children.map(item => {
  266. return (typeof item.hidden === 'function' ? !item.hidden(scope.row) : !item.hidden) && (
  267. <el-tooltip
  268. class='item'
  269. effect='light'
  270. popper-class='common-tooltip-primary'
  271. content={typeof item.label === 'function' ? item.label(scope.row) : item.label}
  272. placement='top'
  273. >
  274. <i
  275. class={['common-tooltip-icon', typeof item.icon === 'function' ? item.icon(scope.row) : item.icon]}
  276. plain='true'
  277. v-permission={item.permission ?? ''}
  278. onClick={() => methods.handleTableButton(scope.row, item.methods)}
  279. />
  280. </el-tooltip>
  281. )
  282. })
  283. }
  284. </div>
  285. }
  286. }}
  287. >
  288. </el-table-column>
  289. }
  290. </el-table>
  291. </div>
  292. )
  293. }
  294. })

4、types.ts为类型定义文件

  1. /** 表格基础类型配置 */
  2. interface BasicLabel {
  3. label: string; // 标题
  4. width?: number | string; // 宽度
  5. fixed?: string; // 固定位置
  6. align?: string; // 行排列
  7. }
  8. /** 表格顶部类型配置 */
  9. export interface TableLabel extends BasicLabel {
  10. prop: string;
  11. methods?: string;
  12. render?: render;
  13. }
  14. /** 表格顶部自定义函数类型 */
  15. interface render {
  16. (row: any): string | any
  17. }
  18. /** 表格操作栏类型 */
  19. export interface OptionLabel extends BasicLabel {
  20. children: OptionChild[]
  21. }
  22. /** 表格操作栏子选项类型 */
  23. export interface OptionChild {
  24. label: string | ((row: any) => string); // 标题
  25. icon: string | ((row: any) => string); // icon图标
  26. methods: string; // 执行方法
  27. permission?: string | string[] | object; // 权限
  28. hidden?: boolean | ((row: any) => boolean);
  29. }

5、组件调用方式 

组件调用:

  1. <CommonTable
  2. show-index
  3. show-check-box={true}
  4. loading={loading.value}
  5. max-height={550}
  6. table-label={tableHeaderData}
  7. data={tableData.value}
  8. option={tableOptionsData}
  9. on-operation={methods.operationHandler}
  10. on-handle-selection-change={methods.handleSelectionChange}
  11. />

 属性及方法使用说明:

注意:如果你在使用Sortable插件想要拖动排序表格时,tableOptionsData 下的fixed参数请不要写,不然会导致无法拖动!

  1. /** 表格头部配置 */
  2. const tableHeaderData = [
  3. {
  4. label: '文件大小',
  5. prop: 'size',
  6. width: '100',
  7. methods: 'fileEntry'
  8. render(row: TableDataItem) {
  9. return `<span>${row.size}</span>`
  10. }
  11. }
  12. ]
  13. /** 表格操作栏配置 */
  14. const tableOptionsData = {
  15. label: '操作',
  16. width: '150',
  17. fixed: 'right',
  18. children: [
  19. {
  20. hidden: false || (row: TableDataItem) { // 可为boolean 或者 函数
  21. return !row.status
  22. },
  23. label: '操作记录',
  24. icon: 'xxx',
  25. methods: 'record',
  26. permission: ''
  27. }
  28. ]
  29. }
  30. const methods = {
  31. /**
  32. * 操作栏分发逻辑
  33. * @param row 当行数据
  34. * @param type 分发函数名
  35. */
  36. operationHandler(row: TableDataItem, type: string) {
  37. if (type === 'record') { // 操作记录
  38. }
  39. },
  40. /**
  41. * 复选框处理回调
  42. * @param val 复选框选中的数据
  43. */
  44. handleSelectionChange(val: TableDataItem[]) {
  45. multipleSelection.value = val
  46. }
  47. }

---觉得好用且实用的,那就点赞收藏吧---

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/684257
推荐阅读
相关标签
  

闽ICP备14008679号