赞
踩
介绍: 这是基于 vue3 + el-table 封装的通用表格组件 的 tsx写法,想要参考模板写法的可以到我另一篇博客喔~
【通用表格组件】vue3 + element-ui + template 实现通用表格组件
话不多说,本组件分为以下四部分,并且在同一层级:
- :global {
- .common-table {
-
- .el-table__header,
- .el-table__body {
- margin: 0;
- }
-
- .el-table::before {
- height: 0;
- }
-
- .el-button {
- padding: 0;
- border: none;
- margin: 0 4px;
- padding: 0 4px 0 8px;
- border-left: 1px solid #e2e2e2;
- font-size: 14px;
- min-height: 14px;
-
- &:first-child {
- border-left: none;
- }
- }
-
- .el-button+.el-button {
- margin-left: 0;
- }
-
- .btn-right div {
- margin-right: 5px;
- }
-
- .btn-right div:empty {
- margin-right: 0px;
- }
-
- //斑马纹表格背景色
- .el-table .even-row {
- --el-table-tr-background-color: #f5fafb;
- }
-
- .el-table .odd-row {
- --el-table-tr-background-color: #ffffff;
- }
-
- .el-table--border::after,
- .el-table--group::after {
- width: 0;
- }
-
- .el-table__fixed-right::before,
- .el-table__fixed::before {
- background-color: transparent;
- }
-
- .custom-table-header {
- th {
- background-color: #fff4d9 !important;
- }
- }
-
- .progress-line {
- .el-progress-bar__outer {
- height: 16px !important;
- }
-
- .el-progress-bar__outer,
- .el-progress-bar__inner {
- border-radius: 0 !important;
- }
- }
-
- .text-no-wrap {
- cursor: pointer;
- display: inline;
- }
-
- .el-table {
-
- td.el-table__cell div,
- th.el-table__cell>.cell {
- font-size: 14px;
- }
-
- th.el-table__cell>.cell {
- font-weight: normal;
- }
-
- .cell {
- padding: 0 10px;
- line-height: 39px;
- }
-
- .el-table__header-wrapper .checkBoxRadio .el-checkbox {
- display: none;
- }
-
- .el-checkbox {
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .table-img {
- width: 60px;
- height: 60px;
- object-fit: cover;
- padding: 6px 0;
- display: flex;
- align-items: center;
- margin: 0 auto;
- justify-content: center;
- }
- }
-
- .el-table--small .el-table__cell {
- padding: 0;
- }
-
- .el-dropdown-menu__item {
- padding: 5px 10px !important;
-
- .el-button {
- width: 100%;
- text-align: center;
- padding: 0 8px;
- margin: 0;
- }
- }
-
- .flex-box {
- display: flex;
- flex-flow: row nowrap;
- justify-content: flex-start;
-
- .item {
- margin: 0 10px;
- }
- }
- }
- .el-table{
- .el-table__expand-icon{
- .el-icon-arrow-right{
- display: none;
- }
- }
- }
- }
- import {
- ref,
- watch,
- nextTick
- } from 'vue';
-
- export default function(props, emit, commonTable) {
- const curPageCheck = ref([])
- const selectId = ref()
-
- watch(() => props.selectRadioId, () => {
- selectId.value = props.selectRadioId
- }, { immediate: true })
-
- watch(() => props.data, () => {
- if (props.showCheckBox || props.turnRadio) {
- nextTick(() => {
- commonTable.value.clearSelection()
- curPageCheck.value = []
- if (props.showCheckBox && props.turnRadio) {
- props.data.filter((item) => {
- if (item.id === props.selectedIdArr[0]) {
- commonTable.value.toggleRowSelection(item, true)
- }
- })
- } else if (props.showCheckBox) {
- props.data.filter((item) => {
- if (props.selectedIdArr.includes(item.id)) {
- commonTable.value.toggleRowSelection(item, true)
- curPageCheck.value.push(item.id)
- }
- })
- }
- })
- }
- }, {
- immediate: true
- })
- watch(() => props.selectedIdArr, (val) => {
- if (props.showCheckBox || props.turnRadio) {
- nextTick(() => {
- commonTable.value.clearSelection()
- curPageCheck.value = []
- if (props.showCheckBox && props.turnRadio) {
- props.data.filter((item) => {
- if (item.id === val[0]) {
- commonTable.value.toggleRowSelection(item, true)
- }
- })
- } else if (props.showCheckBox) {
- props.data.filter((item) => {
- if (val.includes(item.id)) {
- commonTable.value.toggleRowSelection(item, true)
- curPageCheck.value.push(item.id)
- }
- })
- }
- })
- }
- }, {
- immediate: true
- })
- const methods = {
- /**
- * prop 单值 或者 数组过滤(此处为针对时间组,不作为通用处理)
- */
- propFilter(prop, row) {
- const res = prop.reduce((total, cur) => {
- if (row[cur]) {
- return (total += row[cur] + '~')
- } else {
- return ''
- }
- }, '')
- return res ? res.replace(/~$/, '') : ''
- },
- handleTableButton(row, type) {
- emit('operation', row, type);
- },
- /**
- * 后续扩展位
- * @param {*} methods
- * @param {*} row
- */
- handleClickon(methods, row) {
- emit(methods, { methods, row })
- },
- handleSelectionChange(val) {
- if (props.showCheckBox && props.turnRadio) {
- // 选择项大于1时
- if (val.length > 1) {
- const del_row = val.shift()
- commonTable.value.toggleRowSelection(del_row, false)
- }
- }
- // 全选
- if (props.showCheckBox && props.selectedIdArr) {
- if (props.turnRadio) {
- emit('handle-selection-change', val)
- } else {
- // 一般复选框都是走到这一步
- emit('handle-selection-change', val)
- }
- } else {
- emit('handle-selection-change', val)
- }
- },
- getRowKeys(row) {
- return row.id
- },
- selectAll(val) {
- if (props.showCheckBox && props.turnRadio) {
- // 选择项大于1时
- if (val.length > 1) {
- val.length = 1
- }
- }
- emit('handle-selection-change', val)
- },
- // 斑马纹表格背景色
- tabRowClassName({ rowIndex }) {
- const index = rowIndex + 1
- if (index % 2 === 0) {
- return 'even-row'
- } else {
- return 'odd-row'
- }
- },
- cellClassName({ row, columnIndex }) {
- if (row.confirmTag === 2 && columnIndex < props.tableLabel.length) {
- return 'height_light_cell'
- } else {
- return ''
- }
- },
- buttonDisabled(item, row) {
- if (typeof item.disabled === 'function') return item.disabled(row) || false
- if (!item.disabled) return item.disabled
- },
- /**
- * 单选框选中事件
- */
- rowClick(row) {
- if (row) {
- selectId.value = row.id;
- emit('rowClick', row)
- }
- }
- };
- return {
- selectId,
- methods
- }
- }
- import {
- defineComponent,
- ref,
- PropType
- } from 'vue';
- import {
- OptionLabel,
- TableLabel
- } from './types';
- import useModule from './commonTable.module';
- import './commonTable.module.scss';
-
- export default defineComponent({
- name: 'CommonTable',
- props: {
- /**
- * 表格最高高度
- */
- maxHeight: {
- type: [String, Number],
- default: ''
- },
- /**
- * 表格自定义属性展示
- */
- tableLabel: {
- type: Array as PropType<TableLabel[]>,
- default: () => []
- },
- /**
- * 表格数据源
- */
- data: {
- type: Array,
- default: () => []
- },
- /**
- * 配置需要显示的操作菜单
- */
- option: {
- type: Object as PropType<OptionLabel>,
- default: () => {}
- },
- showCheckBox: {
- // 配置是否显示全选(复选框)
- type: Boolean,
- default: false
- },
- /** 是否复选框禁用 */
- isEnable: {
- type: Boolean,
- default: false
- },
- /** 是否复选框禁用的方法 */
- enableCheckBox: {
- type: Function as PropType<(row: any) => boolean>,
- default: null
- },
- /**
- * 是否显示索引
- */
- showIndex: {
- type: Boolean,
- default: false
- },
- turnRadio: {
- type: Boolean,
- default: false
- },
- selectedIdArr: {
- type: Array as PropType<number[] | string[]>,
- default: () => []
- },
- /**
- * 是否 隐藏文字过长
- */
- overflowText: {
- type: Boolean,
- default: false
- },
- /**
- * 加载提示
- */
- loading: {
- type: Boolean,
- default: false
- },
- /**
- * 是否保持之前复选框的数据
- */
- keep: {
- type: Boolean,
- default: false
- },
- /**
- * 动态绑定 key 值
- */
- keyId: {
- type: String,
- default: 'id'
- },
- /**
- * 行内自定义样式配置
- */
- rowStyle: {
- type: Object,
- default: () => {
- return {
- height: '40px'
- }
- }
- },
- /**
- * 是否展示展开按钮
- */
- showExpand: {
- type: Boolean,
- default: false
- },
- /**
- * 单选模式
- */
- showRadio: {
- type: Boolean,
- default: false
- },
- selectRadioId: {
- type: String,
- default: ''
- }
- },
- setup(props, { emit, slots }) {
- const commonTable = ref(null)
- const {
- selectId,
- methods
- } = useModule(props, emit, commonTable)
- return () => (
- <div v-loading={props.loading}>
- <el-table
- class='common-table'
- ref={commonTable}
- data={props.data}
- border
- highlight-current-row={props.showRadio}
- max-height={props.maxHeight}
- row-class-name={methods.tabRowClassName}
- row-style={props.rowStyle}
- cell-class-name={methods.cellClassName}
- header-row-class-name='custom-table-header'
- row-key={props.keyId}
- on-select={methods.handleSelectionChange}
- on-select-all={methods.handleSelectionChange}
- on-current-change={methods.rowClick}
- >
- {
- props.showRadio && <el-table-column
- label='选择'
- align='center'
- width='55'
- scopedSlots={{
- default: scope => (
- <el-radio
- label={scope.row.url}
- vModel={selectId.value}
- onChange={() => methods.rowClick(scope.row)}
- >
-
- </el-radio>
- )
- }}
- />
- }
- { props.isEnable ? (props.showCheckBox && <el-table-column
- key='showCheckBox'
- width='55'
- type='selection'
- reserve-selection={props.keep}
- selectable={props.enableCheckBox}
- class-name={props.turnRadio ? 'checkBoxRadio' : ''}
- align='center'
- />) : (props.showCheckBox && <el-table-column
- key='showCheckBox'
- width='55'
- type='selection'
- reserve-selection={props.keep}
- class-name={props.turnRadio ? 'checkBoxRadio' : ''}
- align='center'
- />)
- }
- {
- props.showExpand && <el-table-column
- key='showExpand'
- type='expand'
- scopedSlots={{
- default: scope => {
- return <fragment row={scope.row}>
- {
- slots.expand?.()
- }
- </fragment>
- }
- }}
- >
- </el-table-column>
- }
- {
- props.showIndex && <el-table-column
- align='center'
- label='序号'
- width='50'
- scopedSlots={{
- default: scope => {
- return scope.$index + 1
- }
- }}
- >
- </el-table-column>
- }
- {
- props.tableLabel.map((item: TableLabel) => {
- return <el-table-column
- key={item[props.keyId]}
- width={item.width ?? ''}
- align={item.align ?? 'center'}
- label={item.label}
- show-overflow-tooltip={props.overflowText}
- fixed={item.fixed}
- prop={item.prop}
- scopedSlots={{
- default: (scope) => {
- if (item.render) {
- return <div
- style='cursor: pointer'
- onClick={() => item.methods && methods.handleClickon(item.methods, scope.row)}
- domPropsInnerHTML={item.render(scope.row)}
- >
- </div>
- } else {
- return <div
- class='text-no-wrap'
- onClick={() => item.methods && methods.handleClickon(item.methods, scope.row)}>
- {
- Object.prototype.toString.call(item.prop) === '[object Array]' ? methods.propFilter(item.prop, scope.row) : (scope.row[item.prop] ?? '--')
- }
- </div>
- }
- }
- }}
- >
- </el-table-column>
- })
- }
- {
- props.option && <el-table-column
- width={props.option.width}
- label={props.option.label}
- fixed={props.option.fixed}
- align={props.option.align ?? 'center'}
- scopedSlots={{
- default: scope => {
- return props.option.children.length && <div
- class='flex-box'
- >
- {
- props.option.children.map(item => {
- return (typeof item.hidden === 'function' ? !item.hidden(scope.row) : !item.hidden) && (
- <el-tooltip
- class='item'
- effect='light'
- popper-class='common-tooltip-primary'
- content={typeof item.label === 'function' ? item.label(scope.row) : item.label}
- placement='top'
- >
- <i
- class={['common-tooltip-icon', typeof item.icon === 'function' ? item.icon(scope.row) : item.icon]}
- plain='true'
- v-permission={item.permission ?? ''}
- onClick={() => methods.handleTableButton(scope.row, item.methods)}
- />
- </el-tooltip>
- )
- })
- }
- </div>
- }
- }}
- >
- </el-table-column>
- }
- </el-table>
- </div>
- )
- }
- })
- /** 表格基础类型配置 */
- interface BasicLabel {
- label: string; // 标题
- width?: number | string; // 宽度
- fixed?: string; // 固定位置
- align?: string; // 行排列
- }
-
- /** 表格顶部类型配置 */
- export interface TableLabel extends BasicLabel {
- prop: string;
- methods?: string;
- render?: render;
- }
-
- /** 表格顶部自定义函数类型 */
- interface render {
- (row: any): string | any
- }
-
- /** 表格操作栏类型 */
- export interface OptionLabel extends BasicLabel {
- children: OptionChild[]
- }
-
- /** 表格操作栏子选项类型 */
- export interface OptionChild {
- label: string | ((row: any) => string); // 标题
- icon: string | ((row: any) => string); // icon图标
- methods: string; // 执行方法
- permission?: string | string[] | object; // 权限
- hidden?: boolean | ((row: any) => boolean);
- }
组件调用:
- <CommonTable
- show-index
- show-check-box={true}
- loading={loading.value}
- max-height={550}
- table-label={tableHeaderData}
- data={tableData.value}
- option={tableOptionsData}
- on-operation={methods.operationHandler}
- on-handle-selection-change={methods.handleSelectionChange}
- />
属性及方法使用说明:
注意:如果你在使用Sortable插件想要拖动排序表格时,tableOptionsData 下的fixed参数请不要写,不然会导致无法拖动!
- /** 表格头部配置 */
- const tableHeaderData = [
- {
- label: '文件大小',
- prop: 'size',
- width: '100',
- methods: 'fileEntry'
- render(row: TableDataItem) {
- return `<span>${row.size}</span>`
- }
- }
- ]
-
- /** 表格操作栏配置 */
- const tableOptionsData = {
- label: '操作',
- width: '150',
- fixed: 'right',
- children: [
- {
- hidden: false || (row: TableDataItem) { // 可为boolean 或者 函数
- return !row.status
- },
- label: '操作记录',
- icon: 'xxx',
- methods: 'record',
- permission: ''
- }
- ]
- }
-
- const methods = {
- /**
- * 操作栏分发逻辑
- * @param row 当行数据
- * @param type 分发函数名
- */
- operationHandler(row: TableDataItem, type: string) {
- if (type === 'record') { // 操作记录
- }
- },
- /**
- * 复选框处理回调
- * @param val 复选框选中的数据
- */
- handleSelectionChange(val: TableDataItem[]) {
- multipleSelection.value = val
- }
- }
---觉得好用且实用的,那就点赞收藏吧---
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。