特性:
- 可以自定义拖拽过表格
- 可以点击某个表格,拖拽右下角小正方形进行任意方向选取单元格
- 支持选中某一行、列
- 支持监听@selectedGrids、@selectedDatas事件获取选中项的DOM对象和数据数组
- 支持props自定义显示label字段别名
sgExcelGrid源码
<template>
<div :class="$options.name">
<div class="ruler-corner"></div>
<div class="horizontal-ruler" :style="{ left: `${-rulerPosition.x}px` }">
<div
class="tick"
:hoverGrid="hoverGrid.x === A_Z[i]"
@click="(hoverGrid = { x: A_Z[i] }), (mousedownGrid = {})"
v-for="(a, i) in A_Z.slice(0, colCount_)"
:key="i"
>
{{ a }}
</div>
</div>
<div class="vertical-ruler" :style="{ top: `${-rulerPosition.y}px` }">
<div
class="tick"
:hoverGrid="hoverGrid.y === i"
@click="(hoverGrid = { y: i }), (mousedownGrid = {})"
v-for="(a, i) in Math.ceil(pageSize / colCount_)"
:key="i"
>
{{ i + 1 }}
</div>
</div>
<div class="grids-scroll" ref="scrollContainer">
<div class="grids" ref="dragContainer" :selectedGrids="selectedGrids.length > 0">
<div
class="grid"
:hoverGridX="selectedGrids.length == 0 && hoverGrid.x === gridsData[i].x"
:hoverGridY="selectedGrids.length == 0 && hoverGrid.y === gridsData[i].y"
:mousedownGrid="
mousedownGrid.x === gridsData[i].x && mousedownGrid.y === gridsData[i].y
"
:dragMove="isMouseDragMove"
:type="a.strong ? 'primary' : ''"
v-for="(a, i) in data"
:key="i"
@mouseover="hoverGrid = gridsData[i]"
@mouseout="hoverGrid = {}"
@click="clickGrid(i)"
>
<span :title="a[label]">{{ a[label] }}</span
><i class="el-icon-close" @click="del(a)" />
<div
class="position-text"
:title="`点击复制`"
@click="$g.copy($g.stripHTML(getPositionText(gridsData[i])), true)"
v-html="getPositionText(gridsData[i])"
></div>
<div class="drag-select-btn" @mousedown.stop="clickResizeHandle"></div>
</div>
</div>
</div>
<!-- 拖拽 -->
<sgDragMoveTile :data="dragMoveTileData" @scroll="scroll" />
</div>
</template>
<script>
import sgDragMoveTile from "@/vue/components/admin/sgDragMoveTile";
export default {
name: "sgExcelGrid",
components: {
sgDragMoveTile,
},
data() {
return {
A_Z: [...Array(26)].map((v, i) => String.fromCharCode(i + 65)),
hoverGrid: {}, //移入的宫格标记
mousedownGrid: {}, //点击的宫格标记
gridsData: [], //记录网格宫格状态
selectedGrids: [], //被选中的网格宫格DOM
selectedDatas: [], //被选中的网格宫格数据
rulerPosition: { x: 0, y: 0 },
dragMoveTileData: {},
gridWidth: 200,
gridHeight: 100,
colCount_: 8,
pageSize_: 100,
isMouseDragMove: false, //鼠标拖拽选中移动
label: `label`, //显示文本字段名
};
},
props: [
"value",
"props",
"data",
"pageSize", //每页显示多少个宫格
"colCount", //列数
],
computed: {},
watch: {
props: {
handler(newValue, oldValue) {
if (newValue && Object.keys(newValue).length) {
newValue.label && (this.label = newValue.label);
}
},
deep: true, //深度监听
immediate: true, //立即执行
},
pageSize: {
handler(newValue, oldValue) {
//console.log('深度监听:', newValue, oldValue);
newValue && (this.pageSize_ = newValue);
},
deep: true, //深度监听
immediate: true, //立即执行
},
data: {
handler(newValue, oldValue) {
this.init_gridsData();
},
deep: true, //深度监听
immediate: true, //立即执行
},
colCount: {
handler(newValue, oldValue) {
//console.log('深度监听:', newValue, oldValue);
newValue && (this.colCount_ = newValue);
this.$nextTick(() => {
this.$el.style.setProperty("--gridWidth", `${this.gridWidth}px`); //js往css传递局部参数
this.$el.style.setProperty("--gridHeight", `${this.gridHeight}px`); //js往css传递局部参数
this.$el.style.setProperty(
"--gridsWidth",
`${this.colCount_ * this.gridWidth}px`
); //js往css传递局部参数
});
},
deep: true, //深度监听
immediate: true, //立即执行
},
selectedGrids: {
handler(newValue, oldValue) {
this.$emit(`selectedGrids`, newValue || []);
},
deep: true, //深度监听
// immediate: true, //立即执行
},
selectedDatas: {
handler(newValue, oldValue) {
this.$emit(`selectedDatas`, newValue || []);
},
deep: true, //深度监听
// immediate: true, //立即执行
},
},
created() {},
mounted() {
this.init_grid_view();
this.addEvents();
},
destroyed() {
this.removeEvents();
},
methods: {
clickGrid(i) {
(this.mousedownGrid = this.gridsData[i]),
(this.hoverGrid = {}),
this.resetSelectGrid();
this.selectedGrids = [this.gridsData[i]];
this.selectedDatas = [this.data[i]];
},
clickResizeHandle(e) {
this.originRect = e.target.parentNode.getBoundingClientRect();
this.originRect.bottomRightX = this.originRect.x + this.originRect.width; //右下角坐标.x
this.originRect.bottomRightY = this.originRect.y + this.originRect.height; //右下角坐标.y
this.__addWindowEvents();
},
__addWindowEvents() {
this.__removeWindowEvents();
addEventListener("mousemove", this.mousemove_window);
addEventListener("mouseup", this.mouseup_window);
},
__removeWindowEvents() {
removeEventListener("mousemove", this.mousemove_window);
removeEventListener("mouseup", this.mouseup_window);
},
mousemove_window(e) {
this.isMouseDragMove = true;
let { x, y } = e;
let minWidth = 0,
minHeight = 0,
maxWidth = innerWidth,
maxHeight = innerHeight;
x < 0 && (x = 0),
y < 0 && (y = 0),
x > maxWidth && (x = maxWidth),
y > maxHeight && (y = maxHeight);
let style = {};
style.x = this.originRect.x;
style.y = this.originRect.y;
style.width = x - this.originRect.x;
style.width <= minWidth &&
((style.width = Math.abs(style.width)),
((style.x = this.originRect.x - style.width),
(style.width = style.width + this.originRect.width)));
style.height = y - this.originRect.y;
style.height <= minHeight &&
((style.height = Math.abs(style.height)),
((style.y = this.originRect.y - style.height),
(style.height = style.height + this.originRect.height)));
style.width > maxWidth && (style.width = maxWidth);
style.height > maxHeight && (style.height = maxHeight);
this.calcRectGrid(style);
},
mouseup_window(e) {
this.isMouseDragMove = false;
this.__removeWindowEvents();
},
resetAllGridStatus() {
this.resetSelectGrid();
this.mousedownGrid = {};
},
resetSelectGrid(d) {
this.selectedGrids = [];
this.selectedDatas = [];
let grids = this.$refs.dragContainer.querySelectorAll(`.grid`);
grids.forEach((v) => {
v.removeAttribute("selected-left");
v.removeAttribute("selected-top");
v.removeAttribute("selected-right");
v.removeAttribute("selected-bottom");
v.removeAttribute("selected");
});
},
// 计算是否选中格子
calcRectGrid(rect) {
this.resetSelectGrid();
this.selectedGrids = this.getSelectedDoms({
targetDoms: this.$refs.dragContainer.querySelectorAll(`.grid`),
rect,
});
this.selectedGrids.forEach((grid) => {
let grid_rect = grid.getBoundingClientRect();
let gridRectScreenWidth = grid_rect.x + grid_rect.width;
let gridRectScreenHeight = grid_rect.y + grid_rect.height;
grid_rect.x <= rect.x &&
rect.x < gridRectScreenWidth &&
grid.setAttribute("selected-left", true);
grid_rect.y <= rect.y &&
rect.y < gridRectScreenHeight &&
grid.setAttribute("selected-top", true);
let rectScreenWidth = rect.x + rect.width;
let rectScreenHeight = rect.y + rect.height;
grid_rect.x < rectScreenWidth &&
rectScreenWidth <= grid_rect.x + grid_rect.width &&
grid.setAttribute("selected-right", true);
grid_rect.y < rectScreenHeight &&
rectScreenHeight <= grid_rect.y + grid_rect.height &&
grid.setAttribute("selected-bottom", true);
grid.setAttribute("selected", true);
});
},
// 获取被选中的DOM
getSelectedDoms({ targetDoms, rect } = {}) {
this.selectedDatas = [];
return [...targetDoms].filter((targetDom, i) => {
if (this.$g.isCrash(targetDom, rect)) {
this.selectedDatas.push(this.data[i]);
return targetDom;
}
}); // 获取被圈选的内容
},
// ----------------------------------------
del(d) {
this.$emit(`del`, d);
},
addEvents(d) {
this.removeEvents();
this.__removeWindowEvents();
addEventListener("resize", this.resize);
},
removeEvents(d) {
removeEventListener("resize", this.resize);
},
getPositionText(gridData) {
return `<span>第${gridData.y + 1}行</span> <span>第${gridData.x}列</span>`;
},
init_grid_view() {
this.resize();
this.$nextTick(() => {
this.init_sgDragMoveTile();
});
},
init_gridsData(d) {
this.gridsData = [...Array(this.pageSize_)].map((v, i) => ({
x: this.A_Z[i % this.colCount_],
y: Math.floor(i / this.colCount_),
}));
this.$nextTick(() => {
this.resetAllGridStatus();
});
},
init_sgDragMoveTile() {
this.dragMoveTileData = {
scrollContainer: this.$refs.scrollContainer,
dragContainer: this.$refs.dragContainer,
};
},
resize(d) {
this.rulerPosition = {
x: 0,
y: 0,
};
},
scroll(e) {
this.rulerPosition = {
x: e.target.scrollLeft,
y: e.target.scrollTop,
};
},
},
};
</script>
<style lang="scss" scoped>
.sgExcelGrid {
/*禁止选中文本*/
user-select: none;
overflow: hidden;
$tickDis: 40px;
$gridWidth: var(--gridWidth);
$gridHeight: var(--gridHeight);
$gridsWidth: var(--gridsWidth);
$scrollbarWidth: 14px;
width: 100%;
height: 100%;
position: relative;
.ruler-corner {
position: absolute;
z-index: 2;
left: 0;
top: 0;
width: $tickDis;
height: $tickDis;
box-sizing: border-box;
border: 1px solid #ebeef5;
border-right: none;
border-bottom: none;
background-color: #eff2f755;
/*遮罩模糊*/
backdrop-filter: blur(5px);
}
.horizontal-ruler {
position: absolute;
z-index: 1;
left: 0;
top: 0;
margin-left: $tickDis;
display: flex;
flex-wrap: nowrap;
border-top: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
/*遮罩模糊*/
backdrop-filter: blur(5px);
// box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.tick {
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
width: $gridWidth;
height: $tickDis;
box-sizing: border-box;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-bottom: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
font-family: DIN-Black;
background-color: #eff2f755;
&[hoverGrid] {
border-left: 1px solid #409eff;
border-right: 1px solid #409eff;
background-color: #b3d8ff99;
color: #409eff;
}
}
}
.vertical-ruler {
position: absolute;
z-index: 1;
left: 0;
top: 0;
margin-top: $tickDis;
display: flex;
flex-wrap: wrap;
flex-direction: column;
border-top: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
/*遮罩模糊*/
backdrop-filter: blur(5px);
// box-shadow: 2px 0 12px 0 rgba(0, 0, 0, 0.1);
.tick {
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
width: $tickDis;
height: $gridHeight;
box-sizing: border-box;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-bottom: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
font-family: DIN-Black;
background-color: #eff2f7;
background-color: #eff2f755;
&[hoverGrid] {
border-top: 1px solid #409eff;
border-bottom: 1px solid #409eff;
background-color: #b3d8ff99;
color: #409eff;
}
}
}
.grids-scroll {
width: calc(100% - #{$tickDis});
height: calc(100vh - 310px);
box-sizing: border-box;
overflow: auto;
position: relative;
margin: $tickDis 0 0 $tickDis;
.grids {
width: calc(#{$gridsWidth} + #{$scrollbarWidth});
min-height: calc(#{$gridHeight} + #{$scrollbarWidth});
overflow: auto;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
box-sizing: border-box;
border-top: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
.grid {
display: flex;
justify-content: center;
align-items: center;
width: $gridWidth;
height: $gridHeight;
padding: 20px;
box-sizing: border-box;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-bottom: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
word-wrap: break-word;
word-break: break-all;
white-space: break-spaces;
position: relative;
span {
/*多行省略号*/
overflow: hidden;
word-break: break-all;
white-space: break-spaces;
display: -webkit-box;
-webkit-box-orient: vertical;
max-height: min-content;
-webkit-line-clamp: 3;
line-height: 1.2;
}
// 坐标文本
.position-text {
position: absolute;
height: 22px;
z-index: 1;
left: 0px;
top: 0px;
display: none;
flex-wrap: nowrap;
white-space: nowrap;
align-items: center;
color: white;
background-color: #00000055;
box-sizing: border-box;
padding: 0 5px;
border-radius: 0 0 8px 0;
>>> span {
font-size: 12px !important;
}
cursor: cell;
&:hover {
background-color: #409eff;
color: white;
}
}
// 删除
i.el-icon-close {
z-index: 1;
display: none;
position: absolute;
right: 0;
top: 0;
font-size: 12px !important;
justify-content: center;
align-items: center;
color: white;
background-color: #409eff;
box-sizing: border-box;
padding: 5px;
border-radius: 0 0 0 8px;
cursor: pointer;
&:hover {
background-color: #f56c6c;
}
}
// 拖拽选区
.drag-select-btn {
position: absolute;
height: 9px;
width: 9px;
z-index: 1;
right: -4.5px;
bottom: -4.5px;
display: none;
box-sizing: border-box;
border: 2px solid white;
background-color: #f56c6c;
cursor: crosshair;
}
&:nth-of-type(2n) {
background-color: #eff2f755;
}
&[hoverGridX] {
border-left: 1px solid #409eff;
border-right: 1px solid #409eff;
background-color: #f2f8fe;
}
&[hoverGridY] {
border-top: 1px solid #409eff;
border-bottom: 1px solid #409eff;
background-color: #f2f8fe;
}
&[mousedownGrid] {
border: 1px solid #f56c6c;
background-color: #f56c6c22;
.drag-select-btn {
display: block;
}
}
&[selected] {
// border: 1px solid #f56c6c;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid #f56c6c22;
border-bottom: 1px solid #f56c6c22;
background-color: #f56c6c22;
.position-text {
background-color: #f56c6c66;
color: white;
&:hover {
background-color: #f56c6c;
}
}
i.el-icon-close {
background-color: #f56c6c66;
&:hover {
background-color: #f56c6c;
}
}
.drag-select-btn {
display: none;
}
&:hover:not([dragMove]) {
border: 1px solid #f56c6c;
background-color: #f56c6c66 !important;
}
}
&[selected-left] {
border-left: 1px solid #f56c6c;
}
&[selected-top] {
border-top: 1px solid #f56c6c;
}
&[selected-right] {
border-right: 1px solid #f56c6c;
}
&[selected-bottom] {
border-bottom: 1px solid #f56c6c;
}
&[mousedownGridX] {
border-left: 1px solid #f56c6c;
border-right: 1px solid #f56c6c;
background-color: #f56c6c22;
}
&[mousedownGridY] {
border-top: 1px solid #f56c6c;
border-bottom: 1px solid #f56c6c;
background-color: #f56c6c22;
}
&[dragMove] {
}
&:hover:not([mousedownGrid]):not([dragMove]) {
background-color: #b3d8ff99;
i,
.position-text {
display: flex;
}
}
}
&[selectedGrids] {
.grid {
&:hover:not([mousedownGrid]):not([dragMove]):not([selected]) {
border: 1px solid #409eff;
}
}
}
}
}
}
</style>
应用
<template>
<sgExcelGrid
:props="{ label: `MC` }"
:data="gridDatas"
:pageSize="pageSize"
@del="delGrid"
@selectedDatas="selectedDatas"
/>
</template>
<script>
import sgExcelGrid from "@/vue/components/admin/sgExcelGrid";
export default {
components: {
sgExcelGrid,
},
data() {
return {
gridDatas: [
{ ID: 1, value: 1, MC: "显示文本1" },
{ ID: 2, value: 2, MC: "显示文本2" },
{ ID: 3, value: 3, MC: "显示文本3" },
{ ID: 4, value: 4, MC: "显示文本4" },
{ ID: 5, value: 5, MC: "显示文本5" },
],
pageSize: 100, //每页显示多少个单元格
rectSelectIDS: [], //选中的ID数组
};
},
props: ["value"],
computed: {},
watch: {},
created() {},
mounted() {},
destroyed() {},
methods: {
selectedDatas(d) {
this.rectSelectIDS = d.map((v) => v.ID); //获取选中项ID数组
},
delGrid(d) {
//删除单元格
},
},
};
</script>
基于【sgDragMoveTile】自定义组件:拖拽瓦片图、地图、大图,滚动条对应同步滚动_用鼠标拖拽(drag)内容div”,滚动条对应同步滚动 vue-CSDN博客文章浏览阅读140次。【代码】【sgDragMoveTile】自定义组件:拖拽瓦片图、地图、大图,滚动条对应同步滚动。_用鼠标拖拽(drag)内容div”,滚动条对应同步滚动 vuehttps://blog.csdn.net/qq_37860634/article/details/133292981