赞
踩
特性:
- 可以自定义拖拽过表格
- 可以点击某个表格,拖拽右下角小正方形进行任意方向选取单元格
- 支持选中某一行、列
- 支持监听@selectedGrids、@selectedDatas事件获取选中项的DOM对象和数据数组
- 支持props自定义显示label字段别名
- <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="hoverGrid.x === gridsData[i].x"
- :hoverGridY="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]"
- @click="clickGrid(i)"
- @mouseout="hoverGrid = {}"
- >
- <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) {
- newValue && (this.pageSize_ = newValue);
- },
- deep: true, //深度监听
- immediate: true, //立即执行
- },
- data: {
- handler(newValue, oldValue) {
- this.init_gridsData();
- },
- deep: true, //深度监听
- immediate: true, //立即执行
- },
- colCount: {
- handler(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) {
- let scrollContainer = this.$refs.scrollContainer;
- scrollContainer && this.scroll({ target: scrollContainer });
- },
- 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] {
- z-index: 2; //让drag-select-btn在顶端
- border: 1px solid #f56c6c;
- 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: block;
- }
- &:hover:not([dragMove]) {
- background-color: #f56c6c66;
- i,
- .position-text {
- display: flex;
- }
- }
- }
- &[selected] {
- z-index: 2; //让drag-select-btn在顶端
- 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;
- }
- &[selected-right][selected-bottom] {
- .drag-select-btn {
- display: block;
- }
- }
- &[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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。