emis-frontend/src/components/XdTable/index3.vue
2025-07-07 10:09:48 +08:00

712 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="xd-table full-block flex-layout" :id="tableId" >
<div class="relative overflow-x-auto">
<el-table
class="xd-tb-wrapper"
:data="data"
tabindex="0"
stripe
border
:key="refreshKeyId"
ref="tableRef"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
@cell-click="handleCellClick">
<el-table-column prop="date" label="日期" width="180"></el-table-column>
<el-table-column prop="name" label="姓名" width="180"></el-table-column>
<el-table-column prop="age" label="年龄" width="100"></el-table-column>
<el-table-column prop="address" label="地址"></el-table-column>
<el-table-column prop="email" label="邮箱"></el-table-column>
</el-table>
<!-- 选择范围指示器 -->
<div id="selectionRange" class="selection-range" style="display: none;"></div>
</div>
<div class="mt-6 p-4 border border-gray-200 rounded-lg">
<h3 class="font-medium text-gray-700 mb-2">选中内容预览:</h3>
<pre id="selectionPreview" class="p-3 bg-gray-50 rounded overflow-x-auto text-sm min-h-16">
框选单元格后,内容将显示在这里...
</pre>
</div>
</div>
</template>
<script>
import { listSysCustomizeTpl, delSysCustomizeTpl, addSysCustomizeTpl, updateSysCustomizeTpl } from "@/api/system/sysCustomizeTpl";
import { genJsSeqNo } from "@/utils/custom";
import clip from '@/utils/clipboard'
import Sortable from 'sortablejs'
import draggable from 'vuedraggable'
import origTable from './table.jsx'
import VirtualScroll from "el-table-virtual-scroll";
import { VirtualColumn } from "el-table-virtual-scroll";
const components = { origTable, draggable,VirtualScroll }
export default {
name: 'XdTable',
components,
props: {
id: {
type: String,
},
showSearch: {
type: Boolean,
default: true,
},
showStatInfo: {
type: Boolean,
default: false,
},
canHideColumns: {
type: Boolean,
default: true,
},
storageName: {
type: String,
required: false
},
queryParams:{
type: Object,
default: null,
},
total:{
type: Number,
default: 0,
},
showRightButton:{
type: Boolean,
default: true,
},
showPagination:{
type: Boolean,
default: true,
},
showOperateButton:{
type: Boolean,
default: true,
},
showCustomizeTpl:{
type: Boolean,
default: true,
},
data:{
type: Array
},
},
data() {
return {
refreshKeyId:Math.random().toString(36).substring(3,10),
tableId:this.id?this.id:"XdTable"+Math.random().toString(36).substring(3,10),
isSelecting: false,
startCell: null,
endCell: null,
selectedCells: [],
ctrlKeyPressed: false,
shiftKeyPressed: false,
// 表格个性化记忆配置
isNeedDbSaveTpl:false,
fullUrl:null,
pageId:null,
saveStorageName:this.storageName,
storeTplList:[],
storageList: [],
openTbShowTplDlg:false,
curentTbShowTpl:null,
form:{
id: null,
tplCode: null,
tplName: null,
tplType: null,
tagId: null,
blDefault: null,
tplData: null,
},
rules: {
tplName: [
{ required: true, message: "模版名称必填", trigger: "blur" }
],
}
}
},
watch: {
},
mounted() {
// 初始化表格单元格选择功能
this.initCellSelection();
// 监听键盘状态
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp);
},
beforeDestroy() {
// 清理事件监听
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('keyup', this.handleKeyUp);
const tableEl = this.$refs.tableRef.$el;
tableEl.removeEventListener('mousedown', this.handleTableMouseDown);
document.removeEventListener('mousemove', this.handleDocumentMouseMove);
document.removeEventListener('mouseup', this.handleDocumentMouseUp);
},
methods: {
// 初始化单元格选择功能
initCellSelection() {
const tableEl = this.$refs.tableRef.$el;
const selectionRange = document.getElementById('selectionRange');
// 鼠标按下事件
tableEl.addEventListener('mousedown', this.handleTableMouseDown);
// 鼠标移动事件
document.addEventListener('mousemove', this.handleDocumentMouseMove);
// 鼠标释放事件
document.addEventListener('mouseup', this.handleDocumentMouseUp);
},
// 处理表格鼠标按下
handleTableMouseDown(event) {
// 只处理左键点击
if (event.button !== 0) return;
// 获取鼠标按下的单元格
const cell = event.target.closest('td');
if (!cell || cell.classList.contains('el-table__expanded-cell')) return;
// 获取单元格位置信息
const cellInfo = this.getCellInfo(cell);
if (!cellInfo) return;
// 阻止默认行为,防止文本选中
event.preventDefault();
// 记录起始单元格
this.startCell = cellInfo;
// 如果按下Ctrl键,启用多选模式
if (this.ctrlKeyPressed) {
this.toggleCellSelection(cellInfo);
} else if (!this.shiftKeyPressed) {
// 清除现有选择(除非按下Shift键)
this.clearSelection();
this.selectCell(cellInfo);
}
// 开始选择
this.isSelecting = true;
},
// 处理文档鼠标移动
handleDocumentMouseMove(event) {
if (!this.isSelecting) return;
// 获取鼠标当前所在的单元格
const cell = event.target.closest('td');
if (!cell || cell.classList.contains('el-table__expanded-cell')) return;
// 获取单元格位置信息
const cellInfo = this.getCellInfo(cell);
if (!cellInfo) return;
// 记录结束单元格
this.endCell = cellInfo;
// 更新选择范围
this.updateSelectionRange();
},
// 处理文档鼠标释放
handleDocumentMouseUp() {
if (!this.isSelecting) return;
// 结束选择
this.isSelecting = false;
// 如果有结束单元格,更新选择范围
if (this.endCell) {
this.updateSelectionRange(true);
}
// 隐藏选择范围指示器
const selectionRange = document.getElementById('selectionRange');
selectionRange.style.display = 'none';
// 更新预览
this.updateSelectionPreview();
},
// 获取单元格信息
getCellInfo(cell) {
if (!cell) return null;
// 获取行索引(排除表头)
const row = Array.from(cell.closest('tbody').children).indexOf(cell.parentElement);
if (row === -1) return null;
// 获取列索引
const column = Array.from(cell.parentElement.children).indexOf(cell);
if (column === -1) return null;
// 获取单元格内容
const content = cell.textContent.trim();
// 获取对应的属性名
const table = this.$refs.tableRef;
const columnProperty = table.columns[column].property;
return { row, column, content, columnProperty, element: cell };
},
// 更新选择范围
updateSelectionRange(finalize = false) {
if (!this.startCell || !this.endCell) return;
// 计算选择范围
const startRow = Math.min(this.startCell.row, this.endCell.row);
const endRow = Math.max(this.startCell.row, this.endCell.row);
const startColumn = Math.min(this.startCell.column, this.endCell.column);
const endColumn = Math.max(this.startCell.column, this.endCell.column);
if (finalize) {
// 最终确定选择范围
this.clearSelection();
// 选择范围内的所有单元格
for (let row = startRow; row <= endRow; row++) {
for (let col = startColumn; col <= endColumn; col++) {
const cell = this.getCellByPosition(row, col);
if (cell) {
const cellInfo = this.getCellInfo(cell);
this.selectCell(cellInfo);
}
}
}
} else {
// 实时显示选择范围指示器
const startCell = this.getCellByPosition(startRow, startColumn);
const endCell = this.getCellByPosition(endRow, endColumn);
if (startCell && endCell) {
const selectionRange = document.getElementById('selectionRange');
const startRect = startCell.getBoundingClientRect();
const endRect = endCell.getBoundingClientRect();
const tableRect = this.$refs.tableRef.$el.getBoundingClientRect();
selectionRange.style.left = `${startRect.left - tableRect.left}px`;
selectionRange.style.top = `${startRect.top - tableRect.top}px`;
selectionRange.style.width = `${endRect.right - startRect.left}px`;
selectionRange.style.height = `${endRect.bottom - startRect.top}px`;
selectionRange.style.display = 'block';
}
}
},
// 根据行列位置获取单元格元素
getCellByPosition(rowIndex, columnIndex) {
const table = this.$refs.tableRef;
const rows = table.$el.querySelectorAll('tbody tr');
if (rowIndex >= 0 && rowIndex < rows.length) {
const row = rows[rowIndex];
const cells = row.querySelectorAll('td');
if (columnIndex >= 0 && columnIndex < cells.length) {
return cells[columnIndex];
}
}
return null;
},
// 选择单元格
selectCell(cellInfo) {
if (!cellInfo || this.isCellSelected(cellInfo)) return;
// 添加到选中列表
this.selectedCells.push(cellInfo);
// 添加选中样式
if (cellInfo.element) {
cellInfo.element.classList.add('selected-cell');
}
},
// 取消选择单元格
deselectCell(cellInfo) {
if (!cellInfo) return;
// 从选中列表中移除
this.selectedCells = this.selectedCells.filter(cell =>
cell.row !== cellInfo.row || cell.column !== cellInfo.column
);
// 移除选中样式
if (cellInfo.element) {
cellInfo.element.classList.remove('selected-cell');
}
},
// 切换单元格选择状态
toggleCellSelection(cellInfo) {
if (this.isCellSelected(cellInfo)) {
this.deselectCell(cellInfo);
} else {
this.selectCell(cellInfo);
}
},
// 检查单元格是否已选中
isCellSelected(cellInfo) {
if (!cellInfo) return false;
return this.selectedCells.some(cell =>
cell.row === cellInfo.row && cell.column === cellInfo.column
);
},
// 清除所有选择
clearSelection() {
// 移除所有选中样式
this.selectedCells.forEach(cell => {
if (cell.element) {
cell.element.classList.remove('selected-cell');
}
});
// 清空选中列表
this.selectedCells = [];
// 重置起始和结束单元格
this.startCell = null;
this.endCell = null;
// 更新预览
this.updateSelectionPreview();
},
// 全选所有单元格
selectAll() {
this.clearSelection();
const table = this.$refs.tableRef;
const rows = table.$el.querySelectorAll('tbody tr');
rows.forEach((row, rowIndex) => {
const cells = row.querySelectorAll('td');
cells.forEach((cell, columnIndex) => {
const cellInfo = this.getCellInfo(cell);
if (cellInfo) {
this.selectCell(cellInfo);
}
});
});
this.updateSelectionPreview();
},
// 复制选中内容到剪贴板
copySelection() {
if (this.selectedCells.length === 0) {
this.$message.warning('没有选中的单元格');
return;
}
// 按行和列排序选中的单元格
const sortedCells = [...this.selectedCells].sort((a, b) => {
if (a.row !== b.row) {
return a.row - b.row;
}
return a.column - b.column;
});
// 生成表格数据
let copyText = '';
let currentRow = sortedCells[0].row;
sortedCells.forEach(cell => {
if (cell.row !== currentRow) {
copyText += '\n';
currentRow = cell.row;
} else if (cell.column > 0) {
copyText += '\t';
}
copyText += cell.content;
});
// 复制到剪贴板
navigator.clipboard.writeText(copyText).then(() => {
this.$message.success('已复制到剪贴板');
}).catch(err => {
this.$message.error('复制失败,请手动复制');
console.error('复制失败:', err);
});
},
// 更新选择内容预览
updateSelectionPreview() {
const previewElement = document.getElementById('selectionPreview');
if (this.selectedCells.length === 0) {
previewElement.textContent = '框选单元格后,内容将显示在这里...';
return;
}
// 按行和列排序选中的单元格
const sortedCells = [...this.selectedCells].sort((a, b) => {
if (a.row !== b.row) {
return a.row - b.row;
}
return a.column - b.column;
});
// 生成预览文本
let previewText = '';
let currentRow = sortedCells[0].row;
sortedCells.forEach(cell => {
if (cell.row !== currentRow) {
previewText += '\n';
currentRow = cell.row;
} else if (cell.column > 0) {
previewText += ' | ';
}
previewText += cell.content;
});
previewElement.textContent = previewText;
},
// 处理键盘按下
handleKeyDown(event) {
if (event.key === 'Control' || event.key === 'Meta') {
this.ctrlKeyPressed = true;
}
if (event.key === 'Shift') {
this.shiftKeyPressed = true;
}
},
// 处理键盘释放
handleKeyUp(event) {
if (event.key === 'Control' || event.key === 'Meta') {
this.ctrlKeyPressed = false;
}
if (event.key === 'Shift') {
this.shiftKeyPressed = false;
}
},
// 处理单元格鼠标进入
handleCellMouseEnter(row, column, cell, event) {
cell.classList.add('bg-gray-50');
},
// 处理单元格鼠标离开
handleCellMouseLeave(row, column, cell, event) {
if (!this.isCellSelected(this.getCellInfo(cell))) {
cell.classList.remove('bg-gray-50');
}
},
// 处理单元格点击
handleCellClick(row, column, cell, event) {
// 如果不是左键点击或正在选择,不处理
if (event.button !== 0 || this.isSelecting) return;
const cellInfo = this.getCellInfo(cell);
if (!cellInfo) return;
// 处理点击选择
if (this.ctrlKeyPressed) {
// Ctrl+点击:切换选择状态
this.toggleCellSelection(cellInfo);
} else if (this.shiftKeyPressed && this.startCell) {
// Shift+点击:从起点扩展选择
this.endCell = cellInfo;
this.updateSelectionRange(true);
} else {
// 普通点击:清除现有选择,选择当前单元格
this.clearSelection();
this.selectCell(cellInfo);
}
this.updateSelectionPreview();
}
}
}
</script>
<style lang="scss" scoped>
.table-cloumn-setting-popper{
.setting-row-content{
max-height: 600px;
overflow-y: auto;
//overflow-y: hidden;
.setting-row{
height: 30px;
line-height: 30px;
.el-icon-s-operation{
cursor: move;
font-size: 16px;
margin-right: 8px;
}
.label{
margin-right: 8px;
}
.btn{
padding: 4px!important;
}
}
}
}
.xd-table{
width:100%;
height:100%;
position: relative;
.el-icon-setting{
position: absolute;
right: 7px;
top: 5px;
cursor: pointer;
}
.table-operate-btn-content{
// width: calc(100% - 40px);
width: 100%;
// min-height: 28px; // 给一个最低的高度,防止没有插槽,右侧icon被吞
min-height: 40px; // 给一个最低的高度,防止没有插槽,右侧icon被吞
.operate-btn-content {
display: flex;
justify-content: space-between;
align-items: center;
min-height: 28px;
padding-bottom: 6px;
}
}
.parent:has(.child){
background-color: #eee;
}
}
.pagination-container{
padding: 0px 20px !important;
}
.menuDiv {
position: absolute;
background-color: #fff;
width: 100px;
/*height: 106px;*/
}
:deep .seled{
background-color: #51dd397d !important;
color: black;
font-weight: 600;
}
:deep .rowSeled{
background-color: #ffa5a55e !important;
color: black;
font-weight: 600;
}
.xd-tb-wrapper :deep .el-table__body tr:hover > td {
background-color: #ffa5a55e;
}
.xd-tb-wrapper{
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
}
.drag-ghost { background-color: #e7e7e77d; }
.drag-chosen { background-color: #e7e7e77d; }
// 拖拽
.dragClass {
background: rgba($color: #41c21a, $alpha: 0.5) !important;
}
// 停靠
.ghostClass {
background: rgba($color: #6cacf5, $alpha: 0.5) !important;
}
// 选择
.chosenClass:hover > td {
background: rgba($color: #f56c6c, $alpha: 0.5) !important;
}
.el-table__body tr.current-row > td {
background-color: #78f709 !important;
}
.el-table--enable-row-hover .el-table__body tr:hover > td {
background-color: #f6d604 !important;
cursor:pointer;
}
.full-block{
height:100%;
width:100%;
overflow:hidden;
}
.flex-layout{
display:flex;
flex-direction:column;
}
.flex-auto{
flex:1 1 auto
}
.flex-none{
flex:none
}
.el-table {
width: 100%;
display: flex;
flex-direction: column;
.el-table__body-wrapper {
flex: 1;
}
}
.content-auto {
content-visibility: auto;
}
.selected-cell {
background-color: rgba(64, 158, 255, 0.2) !important;
border: 1px solid #409EFF !important;
}
.selection-range {
position: absolute;
background-color: rgba(64, 158, 255, 0.1);
border: 1px dashed #409EFF;
pointer-events: none;
z-index: 10;
}
</style>