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

news2024/9/23 5:29:20

🔥 前言

这篇文章给大家分享一个高级自定义列表组件从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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1910851.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

帮企建站包响应式建站源码系统 带完整的安装代码包以及搭建部署教程

系统概述 帮企建站包响应式建站源码系统是一款为企业和个人提供便捷、高效建站解决方案的工具。它融合了先进的技术和设计理念&#xff0c;旨在帮助用户轻松构建具有专业水准的网站&#xff0c;无论在桌面端还是移动端都能呈现出完美的展示效果。 该系统基于响应式设计原则&a…

怎么制作gif动图,视频制作GIF动画更简单

在社交媒体和网络交流中&#xff0c;GIF动画以其生动活泼的表现形式成为了表达情感、幽默和创意的热门媒介。无论是分享日常趣事&#xff0c;还是制作专业演示&#xff0c;一个恰到好处的GIF动图总能吸引目光&#xff0c;传递信息。 但你知道吗&#xff1f;即使没有专业的设计背…

有什么好的录屏软件?分享4款好评的软件!

在数字化时代&#xff0c;录屏软件已成为我们工作、学习和娱乐中不可或缺的工具。但面对市场上琳琅满目的录屏软件&#xff0c;我们该如何选择最适合自己的那一款呢&#xff1f;今天&#xff0c;就让我们一起来揭秘那些备受好评的录屏神器&#xff0c;帮助你轻松找到录屏的首选…

Stable Diffusion——如何把雪碧变成了一个小女孩?

引言 Stable Diffusion WebUI是一个基于深度学习的图像生成工具&#xff0c;它可以将一张图片转换成另一种模样。通过使用先进的稳定扩散算法&#xff0c;这个工具能够生成高质量、高分辨率的图像&#xff0c;同时保持原始图像的核心特征。无论您是想将照片转换成艺术作品&…

7成期刊下降!今年的影响因子为何如此凄惨?

2024年6月20日科睿唯安发布了JCR 2023&#xff0c;21800本期刊最新影响因子正式更新&#xff0c;附完整版EXCEL下载。 影响因子 影响因子&#xff08;Journal Impact Factor&#xff0c;简称JIF或IF&#xff09;是由科睿唯安每年发布的期刊引证报告&#xff08;Journal Citati…

python——list

在Python中&#xff0c;list是一种非常灵活的数据结构&#xff0c;可以用来存储一系列的元素。这些元素可以是任何类型&#xff0c;包括数字、字符串、其他列表等&#xff0c;并且它们不需要是同一种类型。 列表特征&#xff1a; 以下是一些关于Python列表的基本操作&#xff…

书生大模型实战营(暑假场)-入门岛-第一关

书生大模型实战营暑假场重磅开启&#xff01;&#xff0c;这场学习路线看起来很好玩呀&#xff0c;闯关学习既能学到知识又有免费算力可得&#xff0c;太良心啦。感兴趣的小伙伴赶快一起报名学习吧&#xff01;&#xff01;&#xff01; 关卡任务 好的&#xff0c;我们废话不多…

论文总是写不好?这么向kimi提问再试试!【图文大全套】

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 你是否有这样的困惑&#xff1a;论文为什么总是达不到预期的高标准&#xff1f;写作过程中总感觉缺乏方向和灵感&#xff1f;在文献搜索和数据分析上耗费了大量时间却收获甚微&#xff…

老师怎样提高学生的听课效率?

在课堂上&#xff0c;我们常常面临一个问题&#xff1a;如何提高学生的听课效率&#xff1f;这是一个让无数教师头疼的问题。学生是否全神贯注&#xff0c;是否能够吸收和理解课堂上的知识&#xff0c;这直接关系到教学的成败。那么&#xff0c;作为教师&#xff0c;我们能做些…

敏捷的两种方式:Kanban和 Scrum

敏捷方法通过提供灵活、迭代的项目管理方法&#xff0c;改变了软件开发。敏捷方法中最著名的框架是 Kanban 和 Scrum。虽然这两种方法都旨在提高生产力和效率&#xff0c;但它们的运作原则和实践却截然不同。 在本文中&#xff0c;我们将深入探讨 Kanban 和 Scrum 的起源、主要…

简过网:备考一级造价师工程师,到底要不要报个培训班?

最近&#xff0c;小编刷到了有很多朋友在备考一级造价师&#xff0c;其中要不要报个培训班引想了小编的注意&#xff0c;今天&#xff0c;针对备考一级造价师工程师&#xff0c;到底要不要报个培训班这个问题&#xff0c;小编给大家一块来了解一下。 ​ 备考一级造价师工程师要…

侯捷C++面向对象高级编程(上)-10-组合与继承

1.复合-介绍 2.复合-适配 3.复合-计算 4.复合-构造和析构 5.委托 6.继承-介绍 7.继承-构造和析构

【数据结构】TreeMap及TreeSet

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持&#xff01; 一、概念 TreeMap和TreeSet都是Java中基于红黑树&#xff08;Red-Black tree&#xff09;实现的有序集合类。它们分别实现了SortedMap和SortedSet接口&#xff0c;因此提供了按照键&#xff08;对…

8.2结构体的定义和使用

代码 #include <iostream> using namespace std; #include <string>//1、创建学生数据类型&#xff1a;学生包括(姓名&#xff0c;年龄&#xff0c;分数) //自定义数据类型&#xff0c;一些类型集合组成一个类型 //语法 struct 类型名称{成员列表} struct Stduent…

ClickHouse 更新操作导致不可写,应该选择哪种表引擎?

一个工作日下午&#xff0c;某大型通信运营商业务人员报告&#xff0c;一套 ClickHouse 集群在进行插入数据操作的时候&#xff0c;系统报错 too many mutations(1036)&#xff0c;插入数据失败&#xff01; 作者&#xff1a;张宇&#xff0c;爱可生DBA&#xff0c;负责数据库运…

【问题记录】VsCode中以管理员权限运行Powershell

问题展示 今天在尝试运行nodemon命令的时候出问题&#xff0c;显示没法识别&#xff0c;经过分析发现是管理员权限的问题&#xff0c;由于是在vscode里面进行开发&#xff0c;因此特此进行配置。 方法一 直接在vscode命令行中输入如下命令&#xff1a; Start-Process powers…

MySQL--视图和用户管理

视图 这里的视图跟之前事务里面的read view没有任何关系。 视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表&#xff0c;基表的数据变化也会影响到视图。 创建视图 create view 视…

最新盘点!2024年最好用的十大仓储管理系统(WMS)

深度盘点2024年十大仓鼠管理系统管理系统&#xff1a;简道云、伙伴云、百数、吉客云、旺店通、通天晓、富勒、万里牛、鼎捷、Arpa。 WMS系统&#xff08;Warehouse Management System&#xff09;&#xff0c;指的是仓库管理系统&#xff0c;是一种用于优化仓库运营、提高存储…

智能猫砂盆不好用?三款热门智能猫砂盆推荐!

为什么现在那么多人会淘汰掉普通的猫砂盆&#xff0c;转而去购买智能猫砂盆呢&#xff1f;因为智能猫砂盆的自动铲屎功能是真的香啊&#xff0c;有智能猫砂盆在&#xff0c;就不用每天都自己去铲屎了&#xff0c;我只需要隔三四天去清理一下集便仓就好了&#xff0c;对于我们这…

优质可视化大屏模板+动态图表+科技感原件等

优质可视化大屏模板动态图表科技感原件等 软件版本&#xff1a;Axure RP 9 作品类型&#xff1a;高保真 作品内容&#xff1a; 1、大屏可视化模版&#xff08;100套&#xff09;&#xff1a;包含智慧城市、智慧社区、智慧园区、智慧农业、智慧水务、智慧警务、城市交通、电…