样式变更

This commit is contained in:
2026-08-25 10:43:24 +08:00
parent 54fa1c6be2
commit 7bfc19c4f4
5 changed files with 113 additions and 72 deletions
+10 -2
View File
@@ -114,13 +114,21 @@
}
}
// 表格横向拖拽滚动:拖拽过程中光标切换为“抓手”
// 表格横向拖拽滚动:拖拽过程中光标保持默认箭头,不切换为“抓手”
.el-table__body-wrapper.is-dragging,
.el-table__fixed-body-wrapper.is-dragging {
cursor: grabbing;
cursor: default;
}
}
/* 表格横拖期间锁定整页光标与文本选中:
避免光标划过按钮/链接时闪变成手型、以及拖拽过程误选中文字 */
body.el-table-dragging,
body.el-table-dragging * {
user-select: none !important;
cursor: default !important;
}
/* 原生表格同样禁止选中文本,避免横向滑动/滚动时误触发复制前的文本选中 */
table {
-webkit-user-select: none;
+73 -35
View File
@@ -3,15 +3,18 @@
* 隐藏表格的横向滚动条,按住鼠标左键横向拖动即可查看后面的字段。
* 通过 document 事件委托全局生效,所有 el-table 自动支持,无需逐页绑定。
*
* 手感优化:
* - 文本选中已在全局样式中禁用(user-select: none),拖拽过程不会再误选文字
* - 位移以横向为主且超过阈值才触发滚动,轻微手抖、普通点击、纵向拖拽(如行排序)不受影响
* - 光标仅在真正拖动时变为抓手,平时保持默认
* - 拖拽结束(确实发生了滚动)后屏蔽本次 click,避免误触发行点击、复选框等
* 实现要点(全面优化):
* - 采用 Pointer Events + setPointerCapture:按下瞬间把指针捕获到滚动容器上,
* 之后无论光标移动到固定列、表格下方还是窗口外,移动事件都不会中断,真正"跟手"。
* - requestAnimationFrame 合并每帧滚动,避免高频事件造成卡顿/闪烁。
* - 横向位移超过阈值即触发,允许轻微斜向/弧线;仅当纵向明显占优时才不触发,
* 保留普通点击与纵向拖拽(如行排序)的区分。
* - 拖拽期间整页锁定光标与文本选中,光标保持默认箭头,不闪变为抓手/手型。
* - 拖拽结束(确实发生了滚动)后屏蔽本次 click,避免误触发行点击、复选框等。
*/
const DRAG_THRESHOLD = 4 // 横向位移超过该像素且以横向为主,才判定为拖动
const DRAG_THRESHOLD = 2 // 横向位移超过该像素即判定为拖动,越小越跟手
// 在这些元素上按下鼠标不触发拖拽,保证按钮、复选框等交互不受影响
// 在这些元素上按下不触发拖拽,保证按钮、复选框等交互不受影响
const INTERACTIVE_SELECTOR = [
'a', 'button', 'input', 'textarea', 'select', 'label',
'.el-button', '.el-checkbox', '.el-radio', '.el-switch', '.el-select',
@@ -20,77 +23,112 @@ const INTERACTIVE_SELECTOR = [
].join(',')
let wrapper = null
let pointerId = null
let startX = 0
let startY = 0
let startScrollLeft = 0
let dragging = false
let rafId = null
let pendingScrollLeft = 0
function isHorizontallyScrollable(el) {
return el.scrollWidth > el.clientWidth + 1
}
function handleMouseDown(e) {
if (e.button !== 0) return
// 每帧只应用一次滚动位置,提升平滑度
function applyScroll() {
if (!wrapper) return
wrapper.scrollLeft = pendingScrollLeft
rafId = null
}
function handlePointerDown(e) {
if (e.button !== 0 && e.pointerType === 'mouse') return
if (e.target.closest(INTERACTIVE_SELECTOR)) return
const wrap = e.target.closest('.el-table__body-wrapper')
// 固定列(如 fixed="right" 的操作列)渲染在 .el-table__fixed-body-wrapper 中,悬浮覆盖在
// 表体右侧,需一并识别;最终统一落到真正负责横向滚动的 .el-table__body-wrapper 上
const anyWrapper = e.target.closest('.el-table__body-wrapper, .el-table__fixed-body-wrapper')
if (!anyWrapper) return
const table = anyWrapper.closest('.el-table')
const wrap = table && table.querySelector('.el-table__body-wrapper')
if (!wrap || !isHorizontallyScrollable(wrap)) return
wrapper = wrap
pointerId = e.pointerId
startX = e.clientX
startY = e.clientY
startScrollLeft = wrap.scrollLeft
pendingScrollLeft = startScrollLeft
dragging = false
// 关键:捕获指针,之后所有 pointermove/pointerup 都持续派发到容器,
// 即使光标移出表体/窗口也不中断
if (wrap.setPointerCapture && e.pointerId != null) {
try { wrap.setPointerCapture(e.pointerId) } catch (err) { /* 忽略捕获失败 */ }
}
// 阻止默认行为,抑制原生图片/文本拖拽与选中
e.preventDefault()
}
function handleMouseMove(e) {
if (!wrapper) return
function handlePointerMove(e) {
if (!wrapper || e.pointerId !== pointerId) return
e.preventDefault()
const dx = e.clientX - startX
const dy = e.clientY - startY
if (!dragging) {
// 轻微位移视为点击,不触发滚动;位移必须以横向为主,避免与纵向拖拽(如行排序)冲突
if (Math.abs(dx) <= DRAG_THRESHOLD || Math.abs(dx) <= Math.abs(dy)) return
// 开始拖动:以当前位置为基准,避免起始跳变
// 仅当纵向明显占优(超过横向 3 倍且超过 12px)才判定为纵向操作、不触发横向拖动,
// 其余情况(含轻微斜向/弧线)都正常触发,避免"拖不动"
if (Math.abs(dx) <= DRAG_THRESHOLD || (Math.abs(dy) > Math.abs(dx) * 3 && Math.abs(dy) > 12)) return
dragging = true
startX = e.clientX
startY = e.clientY
startScrollLeft = wrapper.scrollLeft
wrapper.classList.add('is-dragging')
// 整页锁定光标与文本选中,保证拖拽手感一致(光标不闪变、文字不误选)
document.body.classList.add('el-table-dragging')
}
e.preventDefault()
wrapper.scrollLeft = startScrollLeft - (e.clientX - startX)
// 记录目标位置,交由 rAF 统一应用
pendingScrollLeft = startScrollLeft - (e.clientX - startX)
if (rafId == null) rafId = requestAnimationFrame(applyScroll)
}
function handleMouseUp() {
function endDrag(blockClick) {
if (!wrapper) return
if (rafId != null) {
cancelAnimationFrame(rafId)
rafId = null
}
wrapper.classList.remove('is-dragging')
if (dragging) {
document.body.classList.remove('el-table-dragging')
if (blockClick && dragging) {
// 拖拽确实发生了滚动,屏蔽本次 click,避免误触发行点击、复选框等
const blockClick = (ev) => {
const suppress = (ev) => {
ev.preventDefault()
ev.stopPropagation()
document.removeEventListener('click', blockClick, true)
document.removeEventListener('click', suppress, true)
}
document.addEventListener('click', blockClick, true)
document.addEventListener('click', suppress, true)
}
wrapper = null
pointerId = null
dragging = false
}
// 鼠标拖出窗口外时清理拖拽状态,防止状态残留
function handleMouseLeave() {
if (!wrapper) return
wrapper.classList.remove('is-dragging')
wrapper = null
dragging = false
function handlePointerUp(e) {
if (e.pointerId !== pointerId) return
endDrag(true)
}
function handlePointerCancel() {
endDrag(false)
}
const install = function() {
if (install.installed) return
install.installed = true
document.addEventListener('mousedown', handleMouseDown, false)
document.addEventListener('mousemove', handleMouseMove, false)
document.addEventListener('mouseup', handleMouseUp, false)
document.addEventListener('mouseleave', handleMouseLeave, false)
document.addEventListener('pointerdown', handlePointerDown, true)
document.addEventListener('pointermove', handlePointerMove, true)
document.addEventListener('pointerup', handlePointerUp, true)
document.addEventListener('pointercancel', handlePointerCancel, true)
}
export default install
@@ -6,22 +6,22 @@
<el-row :gutter="24">
<el-col :xs="24" :md="8">
<el-form-item label="专业名称">
<el-input v-model="searchForm.zymc" placeholder="请输入专业名称(模糊)" clearable />
<el-input v-model="searchForm.zymc" placeholder="请输入专业名称" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
<el-form-item label="专业代码">
<el-input v-model="searchForm.zydm" placeholder="请输入专业代码(模糊)" clearable />
<el-input v-model="searchForm.zydm" placeholder="请输入专业代码" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
<el-form-item label="培训类型">
<el-input v-model="searchForm.pxlx" placeholder="请输入培训类型(精确)" clearable />
<el-input v-model="searchForm.pxlx" placeholder="请输入培训类型" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
<el-form-item label="培训层次">
<el-input v-model="searchForm.pxcc" placeholder="请输入培训层次(精确)" clearable />
<el-input v-model="searchForm.pxcc" placeholder="请输入培训层次" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
@@ -65,17 +65,17 @@
</div>
<el-table v-loading="loading" :data="tableData" border stripe highlight-current-row>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="zydh" label="专业代号" min-width="120" show-overflow-tooltip header-align="center" />
<el-table-column prop="zymc" label="专业名称" min-width="140" show-overflow-tooltip header-align="center" />
<el-table-column prop="zyfx" label="专业方向" min-width="120" show-overflow-tooltip header-align="center" />
<el-table-column prop="zydm" label="专业代码" min-width="110" show-overflow-tooltip header-align="center" />
<el-table-column prop="pxlx" label="培训类型" min-width="110" show-overflow-tooltip header-align="center" />
<el-table-column prop="pxcc" label="培训层次" min-width="120" show-overflow-tooltip header-align="center" />
<el-table-column prop="pxlx2" label="培训类型2" min-width="110" show-overflow-tooltip header-align="center" />
<el-table-column prop="xylb" label="学员类别" min-width="100" show-overflow-tooltip header-align="center" />
<el-table-column prop="xnz" label="学年制" min-width="80" show-overflow-tooltip header-align="center" />
<el-table-column prop="zydh" label="专业代号" width="120" show-overflow-tooltip header-align="center" />
<el-table-column prop="zymc" label="专业名称" width="140" show-overflow-tooltip header-align="center" />
<el-table-column prop="zyfx" label="专业方向" width="120" show-overflow-tooltip header-align="center" />
<el-table-column prop="zydm" label="专业代码" width="110" show-overflow-tooltip header-align="center" />
<el-table-column prop="pxlx" label="培训类型" width="110" show-overflow-tooltip header-align="center" />
<el-table-column prop="pxcc" label="培训层次" width="120" show-overflow-tooltip header-align="center" />
<el-table-column prop="pxlx2" label="培训类型2" width="110" show-overflow-tooltip header-align="center" />
<el-table-column prop="xylb" label="学员类别" width="100" show-overflow-tooltip header-align="center" />
<el-table-column prop="xnz" label="学年制" width="80" show-overflow-tooltip header-align="center" />
<el-table-column prop="xqs" label="学期数" width="80" align="center" />
<el-table-column prop="zybb" label="专业版本" min-width="100" show-overflow-tooltip header-align="center" />
<el-table-column prop="zybb" label="专业版本" width="100" show-overflow-tooltip header-align="center" />
<el-table-column label="主干专业" width="90" align="center">
<template slot-scope="scope">{{ fmtYesNo(scope.row.zgzy) }}</template>
</el-table-column>
@@ -92,7 +92,7 @@
<el-table-column label="停用时间" width="150" align="center">
<template slot-scope="scope">{{ fmtDateTime(scope.row.tysj) }}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" fixed="right">
<el-table-column label="操作" width="240" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="mini" icon="el-icon-view" @click="handleDetail(scope.row)">详情</el-button>
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
@@ -122,7 +122,7 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="专业代号" prop="zydh">
<el-input v-model="dialog.form.zydh" placeholder="请输入专业代号(主键)" :disabled="dialog.isEdit" />
<el-input v-model="dialog.form.zydh" placeholder="请输入专业代号" :disabled="dialog.isEdit" />
</el-form-item>
</el-col>
<el-col :span="12">
@@ -495,7 +495,8 @@ export default {
return this.isTrue(val) ? '是' : '否'
},
fmtDateTime(val) {
return (val || '').substring(0, 19) || '-'
const s = (val || '').substring(0, 10)
return s || '-'
},
fmtVal(val) {
return val === '' || val === null || val === undefined ? '-' : val
@@ -100,11 +100,6 @@
:close-on-click-modal="false">
<el-form ref="classroomForm" :model="dialog.form" :rules="rules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="主键编号" prop="id">
<el-input v-model="dialog.form.id" placeholder="请输入主键编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="教室编号" prop="jsbh">
<el-input v-model="dialog.form.jsbh" placeholder="请输入教室编号" />
@@ -230,7 +225,6 @@ export default {
form: this.createEmptyForm()
},
rules: {
id: [{ required: true, message: '请输入主键编号', trigger: 'blur' }],
jsbh: [{ required: true, message: '请输入教室编号', trigger: 'blur' }],
jsmc: [{ required: true, message: '请输入教室名称', trigger: 'blur' }]
}
@@ -50,26 +50,26 @@
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="jybh" label="教员工号" min-width="110" align="center" />
<el-table-column prop="jyxm" label="教员姓名" min-width="90" align="center" />
<el-table-column label="性别" min-width="60" align="center">
<el-table-column prop="jybh" label="教员工号" width="110" align="center" />
<el-table-column prop="jyxm" label="教员姓名" width="90" align="center" />
<el-table-column label="性别" width="60" align="center">
<template slot-scope="{ row }">{{ fmtGender(row.xb) }}</template>
</el-table-column>
<el-table-column prop="jysmc" label="教研室名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="zc" label="职称" min-width="90" align="center" />
<el-table-column prop="xl" label="学历" min-width="90" align="center" />
<el-table-column prop="xw" label="学位" min-width="90" align="center" />
<el-table-column label="出生时间" min-width="110" align="center">
<el-table-column prop="jysmc" label="教研室名称" width="200" show-overflow-tooltip align="center"/>
<el-table-column prop="zc" label="职称" width="120" align="center" />
<el-table-column prop="xl" label="学历" width="120" align="center" />
<el-table-column prop="xw" label="学位" width="120" align="center" />
<el-table-column label="出生时间" width="110" align="center">
<template slot-scope="{ row }">{{ fmtDateTime(row.cssj) || '-' }}</template>
</el-table-column>
<el-table-column label="入伍工作时间" min-width="120" align="center">
<el-table-column label="入伍工作时间" width="120" align="center">
<template slot-scope="{ row }">{{ fmtDateTime(row.rwgzsj) || '-' }}</template>
</el-table-column>
<el-table-column prop="sjh" label="联系方式" min-width="110" align="center" />
<el-table-column label="虚实类型" min-width="80" align="center">
<el-table-column prop="sjh" label="联系方式" width="110" align="center" />
<el-table-column label="虚实类型" width="80" align="center">
<template slot-scope="{ row }">{{ fmtXslx(row.xslx) }}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" fixed="right">
<el-table-column label="操作" align="center" fixed="right">
<template slot-scope="{ row }">
<el-button type="text" size="small" icon="el-icon-view" @click="handleViewAttribute(row)">查看属性</el-button>
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>