当前位置: 首页 > news >正文

vue3表格显示隐藏列全屏拖动功能

vue3表格显示隐藏列全屏拖动功能

  • 表格组件
  • 主页面使用 RightToolbar 组件

表格组件

创建 RightToolbar.vue 文件

<template><div class="top-right-btn"><el-row><el-tooltip class="item" effect="dark" content="刷新" placement="top"><el-button circle @click="refresh()"><Icon icon="ep:refresh" /></el-button></el-tooltip><el-tooltip class="item" effect="dark" content="全屏" placement="top"><el-button circle @click="toggleTableFullScreen"><Icon :icon="isTableFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'" /></el-button></el-tooltip><el-tooltipclass="item"effect="dark"content="显隐列"placement="top"v-if="columns && columns.length > 0"><el-button circle @click="showColumn()" v-if="showColumnsType === 'transfer'"><Icon icon="ep:menu" /></el-button><el-dropdown v-else trigger="click" :hide-on-click="false" style="padding-left: 8px"><el-button circle><Icon icon="ep:menu" /></el-button><template #dropdown><el-dropdown-menu><div class="sticky-header"><el-checkboxv-model="allSelected":indeterminate="isIndeterminate"@change="toggleAllSelection"style="width: 100%;padding: 4px 10px;border-bottom: 1px solid var(--el-border-color-light);">全选</el-checkbox></div><!-- 拖拽区域 --><divclass="scrollable-content"style="max-height: 400px; overflow-y: auto"@dragenter="handleContainerDragEnter"@dragleave="handleContainerDragLeave"@dragover.prevent><divv-for="(item, index) in columns":key="item.key"class="draggable-item":class="{'drag-over': dragOverIndex === index,dragging: dragStartIndex === index,'ghost-item': dragStartIndex === index}"draggable="true"@dragstart="handleDragStart($event, index)"@dragend="handleDragEnd"@dragover="handleDragOver($event, index)"@dragenter="handleDragEnter($event, index)"@dragleave="handleDragLeave($event, index)"@drop="handleDrop($event, index)"><el-dropdown-item class="dropdown-item-wrapper"><div class="column-item"><div class="drag-indicator"><Icon icon="ep:rank" class="drag-handle" /><div class="drag-line"></div></div><el-checkboxv-model="item.visible"@change="updateSelectionState"class="column-checkbox"><span class="column-label">{{ item.label }}</span></el-checkbox></div></el-dropdown-item></div><!-- 拖拽占位符 --><divv-if="showDropPlaceholder"class="drop-placeholder":class="{ 'drag-over': dragOverIndex === -1 }"><div class="placeholder-content"><Icon icon="ep:plus" class="placeholder-icon" /><span>拖拽到此位置</span></div></div></div></el-dropdown-menu></template></el-dropdown></el-tooltip></el-row><el-dialog :title="title" v-model="open" append-to-body><el-transfer :titles="['显示', '隐藏']" v-model="value" :data="columns" @change="dataChange"><template #left-footer><div class="transfer-footer"><span>拖动可排序</span></div></template><template #right-footer><div class="transfer-footer"><span>拖动可排序</span></div></template></el-transfer></el-dialog></div>
</template><script setup lang="ts">
import { ref, defineProps, defineEmits, watch, computed } from 'vue'defineOptions({ name: 'RightToolbar' })
const props = defineProps({columns: { type: Array, required: true },search: { type: Boolean, default: true },showColumnsType: { type: String, default: 'checkbox' },gutter: { type: Number, default: 10 }
})const emit = defineEmits(['update:columns', 'queryTable', 'toggleTableFullScreen'])// 状态管理
const value = ref([])
const title = ref('显示/隐藏')
const open = ref(false)
const allSelected = ref(false)
const isIndeterminate = ref(false)
const isTableFullscreen = ref(false)// 拖拽状态
const dragStartIndex = ref(-1)
const dragOverIndex = ref(-1)
const isDragging = ref(false)
const isContainerDragOver = ref(false)// 计算属性
const showDropPlaceholder = computed(() => {return isContainerDragOver.value && dragOverIndex.value === -1
})// 原生拖拽方法 - 优化版
const handleDragStart = (event: DragEvent, index: number) => {dragStartIndex.value = indexdragOverIndex.value = -1isDragging.value = trueif (event.dataTransfer) {event.dataTransfer.effectAllowed = 'move'// 设置拖拽图像const target = event.target as HTMLElementevent.dataTransfer.setDragImage(target, 20, 20)}// 添加全局拖拽类document.body.classList.add('drag-active')
}const handleDragEnd = () => {isDragging.value = falsedragStartIndex.value = -1dragOverIndex.value = -1isContainerDragOver.value = false// 移除全局拖拽类document.body.classList.remove('drag-active')
}const handleDragOver = (event: DragEvent) => {event.preventDefault()if (event.dataTransfer) {event.dataTransfer.dropEffect = 'move'}
}const handleDragEnter = (event: DragEvent, index: number) => {event.preventDefault()if (dragStartIndex.value !== index) {dragOverIndex.value = index}
}const handleDragLeave = (event: DragEvent) => {// 检查是否真正离开了当前元素const relatedTarget = event.relatedTarget as Nodeconst currentTarget = event.currentTarget as Nodeif (!currentTarget.contains(relatedTarget)) {dragOverIndex.value = -1}
}const handleContainerDragEnter = () => {isContainerDragOver.value = true
}const handleContainerDragLeave = (event: DragEvent) => {const relatedTarget = event.relatedTarget as Nodeconst currentTarget = event.currentTarget as Nodeif (!currentTarget.contains(relatedTarget)) {isContainerDragOver.value = falsedragOverIndex.value = -1}
}const handleDrop = (event: DragEvent, targetIndex: number) => {event.preventDefault()if (dragStartIndex.value === targetIndex || dragStartIndex.value === -1) {resetDragState()return}// 执行拖拽排序const newColumns = [...props.columns]const [movedItem] = newColumns.splice(dragStartIndex.value, 1)newColumns.splice(targetIndex, 0, movedItem)emit('update:columns', newColumns)resetDragState()
}const resetDragState = () => {dragStartIndex.value = -1dragOverIndex.value = -1isDragging.value = falseisContainerDragOver.value = falsedocument.body.classList.remove('drag-active')
}const toggleTableFullScreen = () => {isTableFullscreen.value = !isTableFullscreen.valueemit('toggleTableFullScreen', isTableFullscreen.value)
}const updateSelectionState = () => {const visibleCount = props.columns.filter((col: any) => col.visible).lengthconst totalCount = props.columns.lengthallSelected.value = visibleCount === totalCountisIndeterminate.value = visibleCount > 0 && visibleCount < totalCount
}const toggleAllSelection = (val: boolean) => {const newColumns = props.columns.map((col: any) => ({...col,visible: val}))emit('update:columns', newColumns)allSelected.value = valisIndeterminate.value = false
}function refresh() {emit('queryTable')
}function dataChange(data) {const newColumns = props.columns.map((item: any) => ({...item,visible: !data.includes(item.key)}))emit('update:columns', newColumns)
}function showColumn() {open.value = true
}watch(() => props.columns,() => {updateSelectionState()},{ immediate: true, deep: true }
)
</script><style scoped>
.top-right-btn {display: flex;align-items: center;justify-content: end;
}.sticky-header {position: sticky;top: 0;background: white;z-index: 2;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}.el-dropdown-menu {padding: 0;min-width: 180px;
}.transfer-footer {padding: 4px 8px;font-size: 11px;color: var(--el-text-color-secondary);border-top: 1px solid var(--el-border-color-light);
}.scrollable-content {max-height: 400px;overflow-y: auto;position: relative;
}/* 拖拽项样式 - 间距调小 */
.draggable-item {cursor: grab;user-select: none;transition: all 0.2s ease;border: 1px solid transparent;border-radius: 4px;margin: 1px 2px;
}.draggable-item:hover {background-color: var(--el-fill-color-light);
}.draggable-item:active {cursor: grabbing;
}/* 拖拽状态样式 */
.draggable-item.dragging {opacity: 0.6;transform: scale(0.98);background-color: var(--el-color-primary-light-9);border-color: var(--el-color-primary);
}.draggable-item.drag-over {background-color: var(--el-color-primary-light-8);border-color: var(--el-color-primary);transform: translateX(4px);
}.draggable-item.ghost-item {opacity: 0.4;
}/* 列项布局 - 间距调小 */
.column-item {display: flex;align-items: center;width: 100%;padding: 2px 0;
}.drag-indicator {display: flex;align-items: center;margin-right: 6px;opacity: 0.5;transition: opacity 0.2s ease;
}.draggable-item:hover .drag-indicator {opacity: 1;
}.drag-handle {cursor: inherit;color: var(--el-text-color-secondary);font-size: 12px;transition: color 0.2s ease;
}.drag-line {width: 1px;height: 12px;background: linear-gradient(to bottom,transparent 0%,var(--el-text-color-secondary) 20%,var(--el-text-color-secondary) 80%,transparent 100%);margin-left: 2px;opacity: 0.6;
}.column-checkbox {flex: 1;
}.column-label {font-size: 13px;color: var(--el-text-color-regular);
}/* 下拉菜单项调整 - 间距调小 */
.dropdown-item-wrapper {padding: 0 !important;
}.dropdown-item-wrapper :deep(.el-dropdown-menu__item) {padding: 0 8px;pointer-events: none;
}/* 拖拽占位符 - 间距调小 */
.drop-placeholder {border: 1px dashed var(--el-border-color);border-radius: 4px;margin: 4px 2px;padding: 12px;text-align: center;background-color: var(--el-fill-color-lighter);transition: all 0.3s ease;opacity: 0.7;
}.drop-placeholder.drag-over {border-color: var(--el-color-primary);background-color: var(--el-color-primary-light-9);opacity: 1;
}.placeholder-content {display: flex;flex-direction: column;align-items: center;gap: 4px;color: var(--el-text-color-secondary);font-size: 11px;
}.placeholder-icon {font-size: 14px;color: var(--el-color-primary);
}/* 全局拖拽状态 */
:global(.drag-active) {cursor: grabbing !important;
}:global(.drag-active *) {cursor: inherit !important;
}/* 滚动条优化 */
.scrollable-content::-webkit-scrollbar {width: 4px;
}.scrollable-content::-webkit-scrollbar-track {background: var(--el-fill-color-lighter);border-radius: 2px;
}.scrollable-content::-webkit-scrollbar-thumb {background: var(--el-border-color-dark);border-radius: 2px;
}.scrollable-content::-webkit-scrollbar-thumb:hover {background: var(--el-text-color-placeholder);
}/* 按钮间距调小 */
.el-row .el-tooltip.item {margin-left: 4px;
}.el-row .el-tooltip.item:first-child {margin-left: 0;
}
</style>

主页面使用 RightToolbar 组件

<template>
<div ref="tableContainer" class="table-container"><el-row class="m-2"><div style="position: absolute; right: 0"><RightToolbar@query-table="getList"v-model:columns="columns"@toggleTableFullScreen="handleTableFullscreen"/></div></el-row><el-tablev-loading="loading":data="list"bordersize="small":height="'calc(100vh - 420px)'"show-summary:summary-method="getSummaries"class="custom-table"><el-table-columnalign="center"label="单据号"prop="masterOrder.sheetCode"min-width="120"fixed/><template v-for="column in visibleColumns" :key="column.key"><!-- 仓库代码 --><el-table-columnv-if="column.key === 'storageCode'"align="center"label="仓库代码"prop="masterOrder.storage.storageCode"min-width="100"/><!-- 仓库名称 --><el-table-columnv-else-if="column.key === 'storageName'"align="center"label="仓库名称"prop="masterOrder.storage.storageName"min-width="160"show-overflow-tooltip/><!-- 单据状态 --><el-table-columnv-else-if="column.key === 'workStatus'"align="center"label="单据状态"prop="masterOrder.workStatus"><template #default="scope"><dict-tag:type="DICT_TYPE.GOODS_IS_VALID":value="scope.row.masterOrder.workStatus"min-width="120"/></template></el-table-column><!-- 类别编号 --><el-table-columnv-else-if="column.key === 'cateCode'"align="center"label="类别编号"prop="item.cateCode"/><!-- 类别名称 --><el-table-column v-else-if="column.key === 'cateName'" align="center" label="类别名称"prop="item.cateName" /><!-- 商品货号 --><el-table-columnv-else-if="column.key === 'itemCode'"align="center"label="商品货号"prop="item.code"min-width="120"/><!-- 条形码 --><el-table-columnv-else-if="column.key === 'barcode'"align="center"label="条形码"prop="item.barcode"min-width="180"/><!-- 商品名称 --><el-table-columnv-else-if="column.key === 'itemName'"align="center"label="商品名称"prop="item.itemName"min-width="140"show-overflow-tooltip/></template></el-table><!-- 分页 --><Paginationv-model:limit="queryParams.pageSize"v-model:page="queryParams.pageNo":total="total"@pagination="getList"/></div>
</template><script lang="ts" setup>const columns = ref([{ key: 'storageCode', label: '仓库代码', visible: true },{ key: 'storageName', label: '仓库名称', visible: true },{ key: 'workStatus', label: '单据状态', visible: true },{ key: 'cateCode', label: '类别编号', visible: true },{ key: 'cateName', label: '类别名称', visible: true },{ key: 'itemCode', label: '商品货号', visible: true },{ key: 'barcode', label: '条形码', visible: true },{ key: 'itemName', label: '商品名称', visible: true }
])// 计算可见的列
const visibleColumns = computed(() => {return columns.value.filter((column) => column.visible)
})const tableContainer = ref<HTMLElement | null>(null)
const isFullscreen = ref(false)// 全屏切换
const handleTableFullscreen = (state: boolean) => {isFullscreen.value = stateif (state) {// 进入全屏document.body.style.overflow = 'hidden'tableContainer.value?.classList.add('fullscreen-active')} else {// 退出全屏document.body.style.overflow = ''tableContainer.value?.classList.remove('fullscreen-active')}
}
</script><style lang="scss" scoped>
.table-container {position: relative;height: calc(100% - 150px); transition: all 0.3s;
}/* 全屏状态样式 */
.table-container.fullscreen-active {position: fixed;inset: 0;z-index: 2000;height: auto !important;padding: 20px;overflow: auto;background: #fff;
}/* 全屏时的表格样式 */
.fullscreen-active .el-table {height: calc(100vh - 150px) !important; /* 减去padding */
}
</style>

效果如图所示:
在这里插入图片描述

http://www.dtcms.com/a/520727.html

相关文章:

  • Git Commit Message 规范:写出清晰、可维护的提交记录
  • Orleans + Kubernetes + Istio 服务网格集成深度解析
  • 51网站怎么打开注册城乡规划师有什么用
  • 相向指针|盛最多水的容器|接雨水|验证回文串
  • Web3j 中使用 Transaction 类进行以太坊交互的核心方法
  • 承德微网站开发怎么弄一个自己的网站
  • web及h5录音wav下载
  • Kotlin 协程中常见的异步返回与控制方式(速览)
  • 做网站还有前景么动漫网页设计报告
  • Maven 多配置文件的使用
  • 【双机位A卷】华为OD笔试之【哈希表】双机位A-跳房子I【Py/Java/C++/C/JS/Go六种语言】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
  • SQL 拼接完全指南
  • 制作的网站wordpress还是自己写
  • 【HLS】Java实现统计HLS的m3u8清单中所有ts切片的视频持续时长
  • 免费网站建设ppt模板下载山西省建设银行网站首页
  • 增城网站建设价格郑州seo
  • 【Rust实战】从零构建高性能异步Web服务器:深入理解所有权与生命周期
  • Vlan-ACCESS接口+Trunk接口
  • 网站开发遇到的最大困难被k掉的网站怎么做才能有收录
  • SpringBoot-Web开发之文件上传
  • 5.2 类
  • 厦门协会网站建设电影网站做淘客
  • 网站建设介绍书如何注销公司流程及费用
  • 阿里国际站网站建设wordpress mysql 扩展
  • LeetCode 405 - 数字转换为十六进制数
  • 漳州做网站喊多少钱wordpress栏目更改无法显示
  • 集团公司网站欣赏如何做企业网站内链
  • 未来的 AI 操作系统(九)——灵魂架构:当智能系统拥有“自我”
  • 卡码网语言基础课(Python) | 20.排队取奶茶
  • ManySpeech —— 使用 C# 开发人工智能语音应用