“El-Table二次封装“这样做【高级前端必备技能之一】

🔥 前言

这篇文章给大家分享一个高级自定义列表组件从0到1的开发过程,这个列表组件的主要功能有,列表拖拽排序,右侧操作按钮统一使用Tooltip展示,操作表头增加自定列表icon,点击icon可以对列表展示数据进行是否显示、排序等操作,契合业务需求,增加表格美观以及复用性。

🔥关与自定义表格

随着系统业务复杂度的提高,列表需要展示的数据变得复杂,常见的El-table逐渐不能满足我们系统的日常使用,更为关键的是El-table 在使用的过程种比较复杂,需要书写大量的< ,并且不能满足我们的系统UI,所以我们决定对El-table进行高度的自定义二次封装,使得团队同学在使用的时候更加便捷、容易,同时也满足了我们系统统一页面风格的需求,下面就给大家介绍我们实现的详细过程。

🔥效果图展示:

在这里插入图片描述✨表格拖转排序
在这里插入图片描述
✨右侧操作按钮显示UI
在这里插入图片描述
✨自定义表头字段弹框
在这里插入图片描述

🔥基本使用

index.vue

<template>
	<!-- 列表 -->
	<div style="height: calc(100% - 155px)">
		<h-table
		    ref="selectionTableRef"
		    v-loading="loading"
		    :border="true"
		    :columns="state.table.columns"
		    :custom-list="customList"
		    :is-custom-list="true"
		    :table="state.table"
		    operatorTheme="useless"
		    stripe
		    @saveCustomList="saveCustomList"
		</h-table>
	</div>
</template>
<script setup>
const state = reactive({
    table: {
        total: 0,
        pageNo: 1,
        pageSize: 10,
        pageSizes: [10, 20, 50, 100],
        // 接口返回数据
        data: [],
        // 表头数据
        columns: [],
        // 多选
        firstColumn: { type: 'selection', fixed: 'left' },
        // 操作列样式
        operatorConfig: {
            fixed: 'right', // 固定列表右边(left则固定在左边)
            width: 100,
            label: '操作',
        },
        operator: [
            {
                text: '查看',
                fun: (val) => look([val]),
                show: [{ key: 'status', val: ['new'] }],
            },
            {
                text: '编辑',
                style: { color: '#f56c6c' },
                fun: (val) => edit([val]),
                show: [{ key: 'status', val: ['new'] }],
            },
        ],
    },
})
// 列配置
const customList = reactive({
	//列表数据
    allColumns: [
        {
            label: '姓名',
            prop: 'name',
            key: 'name',
            width: 150,
            fixed: 'left',
        },
        {
            label: '性别',
            prop: 'sex',
            key: 'sex',
            fixed: 'left',
            minWidth: 280,
        },
        {
            label: '年龄',
            prop: 'age',
            key: 'age',
            minWidth: 100,
        },
        {
            label: '时间',
            prop: 'date',
            key: 'date',
            minWidth: 160,
        }
    ],
    //自定义表头左侧数据
    allData: [
        {
            title: '全部',
            children: [
                { key: 'name', title: '姓名'},
                { key: 'sex', title: '性别'},
                { key: 'age', title: '年龄' },
                { key: 'date', title: '时间' },
            ],
        },
    ],
    //自定义表头右侧可拖拽数据
    defaultCheckData: [
        { key: 'name',title: ['姓名'] },
        { key: 'sex', title: ['性别'] },
        { key: 'age', title: ['年龄'] },
        { key: 'date', title: ['时间'] },
    ],
})

</script>

🔥 碎碎念

看了上面的组件使用是不是觉得,使用起来非常简单,而且在模板层面可以减少很多HTML内容的书写

🔥内部实现

<template>
	<div class="h-table">
		<el-table ref="TTable" :data="state.tableData" :scrollbar-always-on="scrollbarAlwaysOn" :size="size"
			:highlight-current-row="highlightCurrentRow" :border="border || table.border || isTableBorder"
			@cell-dblclick="cellDblclick"
			@row-click="rowClick" :cell-class-name="cellClassNameFuc" :tooltip-options="tooltipOptions" v-bind="{
      ...$attrs,
      class: {
        cursor: isCopy,
        highlightCurrentRow: highlightCurrentRow,
        radioStyle: table.firstColumn && table.firstColumn.type === 'radio',
        outerBorder: !border && !(table.border || isTableBorder),
      },
      style: ''
    }" style="height: 100%;">
			<!-- 行拖拽列 -->
			<template v-if="table.lockColumn">
				<el-table-column type="lock" :width="table.lockColumn.width || 55" :fixed="table.lockColumn.fixed">
					<template #header>
						<slot :name="table.lockColumn.slotName + '_header'">
							<el-icon @click.stop="lockChange($event)">
								<Lock v-show="isLock" :style="{ 'color': table.lockColumn.lockDefaultColor || '#006ef0' }"
									class="pointCursor" />
								<Unlock v-show="!isLock" :style="{ 'color': table.lockColumn.lockActiveColor || '#3ccda0' }"
									class="pointCursor" />
							</el-icon>
						</slot>
					</template>
					<template #default="scope">
						<!-- 自定义插槽 -->
						<slot :name="table.lockColumn.slotName" :scope="scope">
							<el-icon>
								<Rank
									:style="{ 'color': isLock ? table.lockColumn.rankDefaultColor || '#8d9399' : table.lockColumn.rankActiveColor || '#006ef0' }"
									:class="{ 'pointCursor': isLock ? false : true }" />
							</el-icon>
						</slot>
					</template>
				</el-table-column>
			</template>
			<!-- 复选框/单选框/序列号 -->
			<template v-if="table.firstColumn">
				<!-- 复选框 -->
				<el-table-column v-if="table.firstColumn.type === 'selection'" :type="table.firstColumn.type"
					:width="table.firstColumn.width || 55" :reserve-selection="table.firstColumn.isPaging || false"
					:label="table.firstColumn.label" :align="table.firstColumn.align || 'center'" :fixed="table.firstColumn.fixed"
					:selectable="table.firstColumn.selectable" />
				<!-- 单选框 -->
				<el-table-column v-if="table.firstColumn.type === 'radio'" :type="table.firstColumn.type"
					:width="table.firstColumn.width || 55" :label="table.firstColumn.label" :fixed="table.firstColumn.fixed"
					:align="table.firstColumn.align || 'center'">
					<template #default="scope">
						<el-radio v-model="radioVal" :value="scope.$index + 1"
							@click.stop="radioChange($event, scope.row, scope.$index + 1)"></el-radio>
					</template>
				</el-table-column>
				<!-- 序列号 -->
				<el-table-column v-if="table.firstColumn.type === 'index'" :type="table.firstColumn.type"
					:width="table.firstColumn.width || 55" :label="table.firstColumn.label || '序号'"
					:fixed="table.firstColumn.fixed" :align="table.firstColumn.align || 'left'">
					<template #default="scope">
              			{{isShowPagination? (table.pageNo - 1) * table.pageSize + scope.$index + 1: scope.$index + 1}}
					</template>
				</el-table-column>
			</template>
			<!-- 主体内容 -->
			<template v-for="(item, index) in renderColumns">
				<template v-if="!item.children">
					<!-- 常规列 -->
					<el-table-column v-if="item.permission === falseitem.permission : true" :key="index + 'i'"
						:type="item.type" :label="item.label" :prop="item.prop" :width="item.width" :min-width="item.minWidth || 90"
						:sortable="item.sortable" :align="item.align || 'left'" :fixed="item.fixed"
						:show-overflow-tooltip="item.noShowTip === false ? item.noShowTip : true">
						<template #header v-if="item.slotHeader">
							<slot :name="item.prop + '_header'">
								{{ item.label }}
							</slot>
						</template>
						<template #default="scope">
							<!-- formatter渲染 -->
							<template v-if="item.formatter">
								{{ item.formatter({ [item.prop]: scope.row[item.prop], item: scope.row, index: scope.$index }) }}
							</template>
							<!-- render渲染 -->
							<template v-if="item.render">
								<render-col :column="item" :row="scope.row" :render="item.render" :index="scope.$index" />
							</template>
							<!-- 自定义插槽 -->
							<template v-if="item.slotName">
								<slot :name="item.slotName" :scope="scope" :index="scope.$index"></slot>
						</template>
					</el-table-column>
				</template>
			</template>
			<slot></slot>
			<!-- 操作按钮 -->
			<template>
				<el-table-column v-if="table.operator || table.operatorConfig?.onlySetting" :fixed="table.operatorConfig?.fixed"
					:width="56" :min-width="56" :align="table.operatorConfig?.align || 'left'"
					class-name="operator operator_useless">
					<template #header>
						<div class="operator-menu" :class="{ 'operator-menu-disable': !customList.allData.length }"
							v-if="isCustomList">
							<el-icon class="icons" @click="openCustomList">
								<Setting />
							</el-icon>
						</div>
					</template>
					<template #default="scope">
						<el-popover placement="left" effect="customized" popper-class="mtable_operator_useless_popover" :offset="0"
							trigger="hover" v-if="operatorList(scope).length">
							<template #reference>
								<!--@click="scope.row.popVisible = !scope.row.popVisible"-->
								<div class="useless-popover-icon">
									<el-icon>
										<Operation />
									</el-icon>
								</div>
							</template>
							<template #default>
								<div class="operator_useless_btn" v-for="(item, index) in operatorList(scope)" :key="index">
									<template v-if="!item.slot">
										<m-button @click="clickOperationBtn(item, scope.row, scope.$index)"
											:type="item.type ? item.type : 'primary'" link :style="filStyle(item)"
											:icon="item.icon ? item.icon : ''" :disabled="item.disabled"
											:size="item.size ? item.size : 'default'" :title="item.title"
											:class="{ defbtn: !item.disabled && !item.style }">
											<!-- render渲染 -->
											<template v-if="item.render">
												<render-col :column="item" :row="scope.row" :render="item.render" :index="scope.$index" />
											</template>
											<span v-if="!item.render">{{ item.text }}</span>
										</m-button>
									</template>
									<!-- 插槽 -->
									<template v-else>
										<slot :name="item.slot" :scope="{ row: scope.row }" :index="scope.$index"></slot>
									</template>
								</div>
							</template>
						</el-popover>
					</template>
				</el-table-column>
			</template>
		</el-table>
		<!-- 自定义列表弹窗 -->
		<m-custom-list ref="customlisttable" :allData="customList.allData" :append-to-body="appendToBody"
			@save="saveCustomList" />
	</div>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { get } from 'lodash-es'
import { Setting, CaretBottom, Operation, MoreFilled, Unlock, Rank, Lock } from '@element-plus/icons-vue'
import RowDrag from 'sortablejs'
defineOptions({
	name: 'HTable',
})

const props = defineProps({
	// table所需数据
	table: {
		type: Object,
		default: () => {
		},
		required: true,
	},
	// 表头数据
	columns: {
		type: Array,
		default: () => [],
		// required: true
	},
	// 表格标题
	title: {
		type: String,
	},
	// 是否复制单元格
	isCopy: {
		type: Boolean,
		default: false,
	},
	// 是否开启点击整行选中单选框
	rowClickRadio: {
		type: Boolean,
		default: true,
	},
	// 是否开启编辑保存按钮
	isShowFooterBtn: {
		type: Boolean,
		default: false,
	},
	// 是否高亮选中行
	highlightCurrentRow: {
		type: Boolean,
		default: false,
	},
	// 是否开启合计行隐藏复选框/单选框/序列
	isTableColumnHidden: {
		type: Boolean,
		default: false,
	},
	border: {
		type: Boolean,
		default: false,
	},
	// 尺寸风格
	size: {
		type: String,
		default: 'default',
	},
	// tooltip风格配置
	tooltipOptions: {
		type: Object,
		default: () => {
			return {
				effect: 'light',
				offset: 0,
			}
		},
	},
	// 是否需要自定义列表操作
	isCustomList: {
		type: Boolean,
		default: false,
	},
	// 自定义列表配置
	customList: {
		type: Object,
		default: () => {
			return {
				allData: [],
			}
		},
	},
	// 默认选中的数据
	defRadioObj: {
		type: Object,
		default: () => {
		},
	},
	// 按钮权限数组
	btnPremList: {
		type: Array,
		default: () => [],
	},
	// 是否长显滚动条
	scrollbarAlwaysOn: {
		type: Boolean,
		default: true,
	},
	// 自定义列表弹窗是否放到body下
	appendToBody: {
		type: Boolean,
		default: true,
	},
	// 操作栏样式主题 default-默认、useless
	operatorTheme: {
		type: String,
		default: 'default',
	},
})
// 初始化数据
let state = reactive({
	tableData: props.table?.data || [],
	columnSet: [],
})

// 单选框
const radioVal = ref(null)
// 判断单选选中及取消选中
const forbidden = ref(true)
// 获取ref
const TTable = ref(null)
// 抛出事件
const emits = defineEmits([
	'save',
	'size-change',
	'page-change',
	'handleEvent',
	'radioChange',
	'saveCustomList',
	'lockChange',
	'dropRow',
])
// 获取所有插槽
const slots = useSlots()
watch(
	() => [props.table?.data, props.defRadioObj],
	(val) => {
		state.tableData = val[0]
		radioVal.value = null // 重置选中下标
		if (val[0]?.length && val[1] && Object.keys(val[1]).length) {
			const obj = deepClone(val[1])
			const _key = obj.key
			const _value = obj.value
			if (_value === undefined || _value === null) {
				radioVal.value = null
			} else {
				val[0].forEach((it, idx) => {
					if (it[_key] === _value) {
						radioVal.value = idx + 1
					}
				})
			}
		} else {
			radioVal.value = null
		}
	},
	{ immediate: true, deep: true },
)
// 处理操作按钮,判断权限且整合是否展示更多
const operatorList = computed(() => {
	return function(scope) {
		// console.log('scope: ', scope);
		const _op = props.table.operator
		// 过滤掉没有不展示的数据
		const _nop = []
		_op.forEach((_opit, _opidx) => {
			if (checkIsShow(scope, _opit)) {
				_nop.push({ ..._opit, popVisible: false })
			}
		})
		let moreList = []
		if (props.operatorTheme === 'default') {
			// 处理到“更多”
			if (_nop.length <= 2) return _nop
			let newArray = {
				more: true,
				children: [..._nop.slice(2)],
			}
			moreList = [_nop[0], _nop[1], newArray]
		} else {
			moreList = _nop
		}
		return moreList
	}
})
// 点击操作按钮的回调
const clickOperationBtn = (item, scoprow, scopindex) => {
	// console.log(item, scoprow, scopindex)
	// scoprow.popVisible = false
	return item.fun && item.fun(scoprow, scopindex, state.tableData)
}
// 更多按钮展开/收起操作-选中效果
const isOpenMorebtn = ref(false)
const openMoreIndex = ref(null)
const changeMorebtn = (e, index) => {
	isOpenMorebtn.value = e
	openMoreIndex.value = index
}
// 处理按钮颜色
const filStyle = computed(() => {
	return function(item) {
		const _color = props.operatorTheme === 'default' ? 'color: #006ef0' : 'color: #fff'
		return !item.disabled ? item.style ? item.style : _color : ''
	}
})
// 判断如果有表头合并就自动开启单元格缩放
const isTableBorder = computed(() => {
	return props.columns.some((item) => item.children)
})
// 处理回显数据
const fileValue = computed(() => {
	return function(row, prop, unit) {
		const _data = get(row, prop)
		return _data || _data === 0 ? `${ _data }${ unit || '' }` : '/'
	}
})
// 合并行隐藏复选框/单选框
const cellClassNameFuc = ({ row }) => {
	if (!props.isTableColumnHidden) {
		return false
	}
	if (
		state.tableData.length -
		(state.tableData.length - props.table.pageSize < 0
			? 1
			: state.tableData.length - props.table.pageSize) <=
		row.rowIndex
	) {
		return 'table_column_hidden'
	}
}
// forbidden取值(选择单选或取消单选)
const isForbidden = () => {
	forbidden.value = false
	setTimeout(() => {
		forbidden.value = true
	}, 0)
}
// 单选抛出事件radioChange
const radioClick = (row, index) => {
	forbidden.value = !!forbidden.value
	if (radioVal.value) {
		if (radioVal.value === index) {
			radioVal.value = null
			isForbidden()
			// 取消勾选就把回传数据清除
			emits('radioChange', null, radioVal.value)
		} else {
			isForbidden()
			radioVal.value = index
			emits('radioChange', row, radioVal.value)
		}
	} else {
		isForbidden()
		radioVal.value = index
		emits('radioChange', row, radioVal.value)
	}
}
// 判断是否使用漏了某个插槽
const isShow = (name) => {
	return Object.keys(slots).includes(name)
}
// 整行编辑返回数据
const save = () => {
	emits('save', state.tableData)
	return state.tableData
}
const onMouseOver = (event, item) => {
	const { offsetWidth, offsetLeft } = event.target
	const pOffsetWidth = event.fromElement.offsetWidth
	const width = item.minWidth ? item.minWidth : pOffsetWidth
	// console.log(event)
	width < offsetWidth + offsetLeft * 2 ? (item.showSelfTip = true) : (item.showSelfTip = false)
}
/**
 * 公共方法
 */
// 清空排序条件
const clearSort = () => {
	return TTable.value.clearSort()
}
// 取消某一项选中项
const toggleRowSelection = (row, selected = false) => {
	return TTable.value.toggleRowSelection(row, selected)
}
// 清空复选框
const clearSelection = () => {
	return TTable.value.clearSelection()
}
const customlisttable = ref(null)
// 打开自定义列表
const openCustomList = () => {
	if (props.customList.allData.length) {
		customlisttable.value.open(props.customList.defaultCheckData)
	}
}
// 提交自定义列表的保存的数据
const saveCustomList = (val) => {
	emits('saveCustomList', val)
}
// 重新布局表格
const doLayout = () => {
	TTable.value.doLayout()
}
// 解锁或者锁定行拖拽
const isLock = ref(props.table?.lockColumn?.isLock || true)
const lockChange = (val) => {
	isLock.value = !isLock.value
	if (isLock.value) {
		destroyDrop()
	} else {
		rowDrop()
	}
	emits('lockChange', isLock.value)
}
const tbodyObj = ref(null)
const tbody = ref(null)
// 拖拽传参
const rowDrop = () => {
	if (isLock.value) return
	tbody.value = document.querySelector(".el-table__body-wrapper tbody")
	if (tbody.value) {
		tbodyObj.value = RowDrag.create(tbody.value, {
			animation: 300,
			onEnd: ({ newIndex, oldIndex }) => {
				emits('dropRow', {
					oldIndex: oldIndex,
					newIndex: newIndex,
					data: TTable.value,
				})
			},
		})
	}
}
// 销毁RowDrag
const destroyDrop = () => {
	if (tbodyObj.value instanceof RowDrag) {
		tbodyObj.value.destroy()
	}
}
onMounted(() => {
	rowDrop()
})
// 暴露方法出去
defineExpose({ clearSelection, toggleRowSelection, clearSort, doLayout })
</script>

🔥Style 样式

<style lang="scss" scoped>
$table-border-color: #f0f3f5;
.h-table {
	height: 100%;
	z-index: 0;

	:deep(.el-table::before) {
		background: none;
	}

	:deep(.el-table__body-wrapper) {
		background: #f7fbfe;

		.el-table__body {
			margin: 0;

			// 操作按钮部分
			.operator {
				.cell {
					height: 100%;
					display: flex;
					align-items: center;
					padding: 0 !important;
				}

				&_btn {
					height: 100%;
					display: flex;
					align-items: center;
					padding: 0 10px;

					.el-button {
						// width: 82px;
						height: 16px;
						margin: 0;
						padding: 0 10px;
						border-right: 1px solid #dadee6;

						&:first-child {
							padding-left: 0;
						}

						&:last-child {
							border: none;
							padding-right: 0;

							&:hover {
								border: none;
							}
						}

						&:hover {
							border-right: 1px solid #dadee6;
						}
					}

					.oper_mor_btn {
						width: 40px;
						height: 100%;
						padding-left: 0;

						& > span {
							width: 100%;
							height: 100%;
						}

						.morebtn {
							width: 100%;
							height: 100%;
							display: flex;
							justify-content: center;
							align-items: center;

							&-title {
								width: 100%;
								height: 100%;
								display: inline-flex;
								justify-content: center;
								align-items: center;

								& > i {
									color: #8d9399;
									transform: rotate(90deg);
									margin-left: -2px;
								}

								&:focus-visible {
									outline: none;
								}
							}
						}
					}

					.oper_mor_btn_active {

						// background: #61a3f2;
						.morebtn {
							&-title {
								& > i {
									// color: #fff;
									color: #61a3f2;
								}
							}
						}
					}
				}
			}

			// useless模式时的操作
			.operator_useless {
				.cell {
					.useless-popover-icon {
						width: 100%;
						height: 100%;
						display: flex;
						align-items: center;
						justify-content: center;
						cursor: pointer;

						& > .el-icon {
							font-size: 16px;
						}

						&:hover {
							& > .el-icon {
								color: #61a3f2;
							}
						}
					}
				}
			}
		}
	}

	:deep(.mpagination) {
		margin-top: 12px;
		background-color: transparent;
	}

	:deep(.el-popper) {
		max-width: 600px;
	}

	// 某行隐藏复选框/单选框
	:deep(.el-table) {
		.el-popper {
			font-size: 14px;
		}
		.el-table__row {
			.table_column_hidden {
				.cell {

					.el-radio__input,
					.el-checkbox__input {
						display: none;
					}

					& > span {
						display: none;
					}
				}
			}
		}
	}

	.el-table th,
	.el-table td {
		padding: 8px 0;
	}

	.el-table--border th:first-child .cell,
	.el-table--border td:first-child .cell {
		padding-left: 5px;
	}

	.el-table--scrollable-y .el-table__fixed-right {
		right: 8px !important;
	}

	.header_wrap {
		display: flex;
		align-items: center;

		.toolbar_top {
			flex: 0 70%;
			display: flex;
			align-items: center;
			justify-content: flex-end;

			.toolbar {
				display: flex;
				justify-content: flex-end;
				width: 100%;
			}

			.el-button--small {
				height: 32px;
			}

			.el-button--success {
				background-color: #355db4;
				border: 1px solid #355db4;
			}
		}

		.header_title {
			display: flex;
			align-items: center;
			flex: 0 30%;
			font-size: 16px;
			font-weight: bold;
			line-height: 35px;
			margin-left: 10px;
		}
	}

	.marginBttom {
		margin-bottom: -8px;
	}

	// 表格外边框
	.outerBorder {
		border: 1px solid $table-border-color;
	}

	// 单选样式
	.radioStyle {
		:deep(.el-table__header) {
			.el-table__cell:first-child {
				border-right: 0;

				.cell {
					display: none;
					border-right: 0 !important;
				}
			}
		}

		:deep(.el-radio) {
			&:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner {
				box-shadow: none;
			}
		}

		:deep(tbody) {
			.el-table__row {
				cursor: pointer;
			}
		}
	}

	// 复制功能样式
	.cursor {
		:deep(tbody) {
			.el-table__row {
				cursor: pointer;
			}
		}
	}

	// 表格样式调整
	:deep(.el-table) {
		.el-table__header {
			margin: 0;

			.el-table__cell {
				height: 50px;
				font-size: 16px;
				background: #e5f0fd;
				border-right: 1px solid #ecf4fe;

				.cell {
					height: 26px;
					line-height: 26px;
					color: #666;
					font-weight: normal;
				}

				&:last-child {
					border-right: 0;
				}
			}

			.el-table-column--selection {
				.cell {
					border-right: 0 !important;
				}
			}
		}

		.el-table__body-wrapper {
			.el-scrollbar__view {
				height: 100%;
			}

			.el-table__body {
				.el-table__row {
					.el-table__cell {
						height: 40px;
						padding: 0;
						border-bottom: 1px solid #e7edf9;
						border-right: 0 !important;

						// 取消展开图标的旋转
						.el-table__expand-icon {
							.el-icon {
								display: none;
							}

							transform: rotate(0deg);

							&::before {
								content: '';
								display: block;
								width: 16px;
								height: 16px;
								margin-top: -1px;
								background: url('../../../images/open.png') no-repeat center top;
								background-size: 100% 100%;
							}
						}

						// 展开节点
						.el-table__expand-icon--expanded {
							&::before {
								content: '';
								width: 16px;
								height: 16px;
								margin-top: -1px;
								background: url('../../../images/up.png') no-repeat center top;
								background-size: 100% 100%;
							}
						}
					}
				}
			}

			&.el-table--default .cell {
				padding: 0 16px;
			}

			.el-table__row--striped {
				.el-table__cell {
					background: #f5f9fe;
				}
			}
		}

	}

	:deep(.el-table.el-table--border) {
		.el-table__header {
			.el-table__cell {
				.cell {
					border-right: 1px solid #bcd0f2;
					color: #282d32;
					font-weight: 500;
				}
			}

			.is-group {
				.el-table__cell {
					.cell {
						border-right: 0;
					}
				}
			}
		}
	}

	// 操作头部
	.operator {
		.cell {
			padding: 0 !important;

			// 操作样式
			.operator-title {
				display: flex;
				// justify-content: center;
				align-items: center;
			}

			.operator-menu {
				width: 20px;
				height: 20px;
				position: absolute;
				right: 16px;
				top: 15px;
				z-index: 2;
				cursor: pointer;

				.icons {
					color: #505363;
					font-size: 20px;

					&:hover {
						color: #409eff;
					}
				}
			}

			.operator-menu-disable {
				cursor: default;


				.icons {

					&:hover {
						color: #505363;
					}
				}
			}
		}
	}

	.operator_useless {
		.cell {
			.operator-menu {
				right: 18px;
			}
		}
	}

	// 页面缓存时,表格内操作栏每行高度撑满
	:deep(.el-table__fixed-right) {
		height: 100% !important;
	}

	// 选中行样式
	.highlightCurrentRow {
		tbody {
			:deep(.el-table__row) {
				cursor: pointer;
			}

			.current-row td {
				cursor: pointer;
				color: #fff;
				background-color: #355db4 !important;
			}
		}
	}

	.el-table--scrollable-y .el-table__body-wrapper {
		overflow-x: auto;
	}

	.handle_wrap {
		position: sticky;
		z-index: 10;
		right: 0;
		bottom: -8px;
		margin: 0 -8px -8px;
		padding: 12px 16px;
		background-color: #fff;
		border-top: 1px solid #ebeef5;
		text-align: right;

		.el-btn {
			margin-left: 8px;
		}
	}

	.pointCursor {
		cursor: pointer;
	}
}
</style>
<style lang="scss">
.morebtn-popper {
	.el-dropdown-menu {
		padding: 4px !important;

		.el-dropdown-menu__item {
			height: 32px;

			&:hover {
				background: #eaf3fc;
			}

			.el-button {
				width: 100%;
				height: 100%;
			}
		}
	}
}

.mtable_operator_useless_popover {
	width: inherit !important;
	display: flex;
	margin-bottom: -4px;
	padding: 11px 4px !important;
	min-width: inherit !important;

	&.is-customized {
		background: rgba(99, 108, 128, 0.9) !important;

		.el-popper__arrow::before {
			background: rgba(99, 108, 128, 0.9) !important;
		}
	}

	.operator_useless_btn {
		height: 16px;
		display: flex;
		align-items: center;
		margin: 0 12px 0;
		position: relative;

		.el-button {
			font-size: 14px;
			padding: 0;
		}

		.el-button.defbtn:hover {
			color: #52abff !important;
		}

		&:not(:first-child)::before {
			content: '';
			position: absolute;
			width: 1px;
			height: 12px;
			background: #949ba9;
			left: -12px;
			top: 50%;
			transform: translateY(-50%);

		}
	}
}
</style>
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
el-table二次封装可以通过配置项来实现自动生成el-table-column,从而省去冗余代码。这样,在下一次管理系统添加新模块时,就不需要再进行复制粘贴了。虽然复制粘贴可以快速完成需求,但是代码会变得冗余且难以维护。通过二次封装,可以提高代码的可维护性和可读性。\[3\]具体实现的方法是在父组件中定义一个columns数组,数组中的每个元素代表一个el-table-column的配置项。然后在子组件中使用v-for指令遍历columns数组,动态生成el-table-column。这样就可以实现el-table二次封装,减少冗余代码的编写。\[1\] \[2\] #### 引用[.reference_title] - *1* *3* [两种方式对el-table二次封装](https://blog.csdn.net/weixin_42375707/article/details/130042460)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Element UI 表格 el-table 二次封装](https://blog.csdn.net/DevilAngelia/article/details/122251519)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值