|
@@ -15,41 +15,52 @@
|
|
|
]"
|
|
|
>
|
|
|
<span class="drag-line" data-row="-1"></span>
|
|
|
- <div v-for="(row, i) in data.row_list" :key="i" class="row">
|
|
|
- <div
|
|
|
- v-for="(col, j) in row.col_list"
|
|
|
- :key="j"
|
|
|
- class="col"
|
|
|
- :style="{
|
|
|
- width: col.width,
|
|
|
- gridTemplateAreas: col.grid_template_areas,
|
|
|
- gridTemplateColumns: col.grid_template_columns,
|
|
|
- gridTemplateRows: col.grid_template_rows,
|
|
|
- }"
|
|
|
- >
|
|
|
- <template v-for="(grid, k) in col.grid_list">
|
|
|
- <span
|
|
|
- v-if="k === 0"
|
|
|
- :key="`start-${k}`"
|
|
|
- class="drag-vertical-line"
|
|
|
- :data-row="i"
|
|
|
- :data-col="j"
|
|
|
- :data-grid="k"
|
|
|
- ></span>
|
|
|
- <component
|
|
|
- :is="componentList[grid.type]"
|
|
|
- :id="grid.id"
|
|
|
- ref="component"
|
|
|
- :key="k"
|
|
|
- :style="{ gridArea: grid.grid_area }"
|
|
|
- @showSetting="showSetting"
|
|
|
- @deleteComponent="deleteComponent"
|
|
|
- />
|
|
|
- <span :key="`end-${k}`" class="drag-vertical-line" :data-row="i" :data-col="j" :data-grid="k + 1"></span>
|
|
|
+ <!-- 行 -->
|
|
|
+ <template v-for="(row, i) in data.row_list">
|
|
|
+ <div :key="i" class="row" :style="getMultipleColStyle(i)">
|
|
|
+ <!-- 列 -->
|
|
|
+ <template v-for="(col, j) in row.col_list">
|
|
|
+ <span v-if="j === 0" :key="`start-${i}-${j}`" class="drag-vertical-line" :data-row="i" :data-col="j"></span>
|
|
|
+ <div
|
|
|
+ :key="j"
|
|
|
+ :class="['col', `col-${i}-${j}`]"
|
|
|
+ :style="{
|
|
|
+ width: col.width,
|
|
|
+ gridTemplateAreas: col.grid_template_areas,
|
|
|
+ gridTemplateColumns: col.grid_template_columns,
|
|
|
+ gridTemplateRows: col.grid_template_rows,
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <!-- 网格 -->
|
|
|
+ <template v-for="(grid, k) in col.grid_list">
|
|
|
+ <span
|
|
|
+ v-if="k === 0"
|
|
|
+ :key="`start-${k}`"
|
|
|
+ class="drag-line grid-line"
|
|
|
+ :data-row="i"
|
|
|
+ :data-col="j"
|
|
|
+ :data-grid="k"
|
|
|
+ ></span>
|
|
|
+ <component
|
|
|
+ :is="componentList[grid.type]"
|
|
|
+ :id="grid.id"
|
|
|
+ ref="component"
|
|
|
+ :key="k"
|
|
|
+ :class="[grid.id]"
|
|
|
+ :style="{ gridArea: grid.grid_area, height: grid.height }"
|
|
|
+ :delete-component="deleteComponent(i, j, k)"
|
|
|
+ :component-move="componentMove(i, j, k)"
|
|
|
+ @showSetting="showSetting"
|
|
|
+ />
|
|
|
+ <span :key="`end-${k}`" class="drag-line grid-line" :data-row="i" :data-col="j" :data-grid="k + 1"></span>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <span :key="`end-${i}-${j}`" class="drag-vertical-line" :data-row="i" :data-col="j + 1"></span>
|
|
|
</template>
|
|
|
</div>
|
|
|
- <span class="drag-line" :data-row="i"></span>
|
|
|
- </div>
|
|
|
+ <span v-if="i < data.row_list.length - 1" :key="`row-${i}`" class="drag-line" :data-row="i"></span>
|
|
|
+ </template>
|
|
|
+ <span class="drag-line" :data-row="data.row_list.length - 1"></span>
|
|
|
</main>
|
|
|
</template>
|
|
|
|
|
@@ -166,9 +177,108 @@ export default {
|
|
|
showSetting(setting, type, id) {
|
|
|
this.$emit('showSetting', setting, type, id);
|
|
|
},
|
|
|
- deleteComponent() {
|
|
|
- console.log(1);
|
|
|
- this.$message.success('删除成功');
|
|
|
+ componentMove(i, j, k) {
|
|
|
+ return ({ type, offsetX, offsetY, id }) => {
|
|
|
+ const row = this.data.row_list[i];
|
|
|
+ const col = row.col_list[j];
|
|
|
+ const grid = col.grid_list[k];
|
|
|
+
|
|
|
+ if (['top', 'bottom'].includes(type)) {
|
|
|
+ if (grid.height === 'auto') {
|
|
|
+ const gridHeight = document.querySelector(`.${id}`).offsetHeight;
|
|
|
+ grid.height = `${gridHeight + offsetY}px`;
|
|
|
+ col.grid_template_rows = `0 ${col.grid_list.map(({ height }) => height).join(' 16px ')} 0`;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const height = Number(grid.height.replace('px', ''));
|
|
|
+ if (height + offsetY >= 50) {
|
|
|
+ grid.height = `${height + offsetY}px`;
|
|
|
+ col.grid_template_rows = `0 ${col.grid_list.map(({ height }) => height).join(' 16px ')} 0`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type === 'left' && j > 0) {
|
|
|
+ const prevGrid = row.width_list[j - 1];
|
|
|
+ const prevWidth = Number(prevGrid.replace('%', ''));
|
|
|
+ const width = Number(row.width_list[j].replace('%', ''));
|
|
|
+ const max = prevWidth + width - 10;
|
|
|
+ if (prevWidth + offsetX < 10 || prevWidth + offsetX > max || width - offsetX > max || width - offsetX < 10) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 计算拖动的距离与总宽度的比例
|
|
|
+ const ratio = (offsetX / document.querySelector('.row').offsetWidth) * 100;
|
|
|
+ row.width_list[j + 1] = `${prevWidth + ratio}%`;
|
|
|
+ row.width_list[j] = `${width - ratio}%`;
|
|
|
+ this.$forceUpdate();
|
|
|
+ }
|
|
|
+ if (type === 'right' && j < row.col_list.length - 1) {
|
|
|
+ let nextGrid = row.width_list[j + 1];
|
|
|
+ const nextWidth = Number(nextGrid.replace('%', ''));
|
|
|
+ const width = Number(row.width_list[j].replace('%', ''));
|
|
|
+ const max = nextWidth + width - 10;
|
|
|
+ if (nextWidth - offsetX < 10 || nextWidth - offsetX > max || width + offsetX > max || width + offsetX < 10) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const ratio = (offsetX / document.querySelector('.row').offsetWidth) * 100;
|
|
|
+ row.width_list[j + 1] = `${nextWidth - ratio}%`;
|
|
|
+ row.width_list[j] = `${width + ratio}%`;
|
|
|
+ this.$forceUpdate();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 重新计算格子宽度
|
|
|
+ * @param {number} i 行
|
|
|
+ * @param {number} j 列
|
|
|
+ */
|
|
|
+ recalculateGridWidth(i, j) {
|
|
|
+ let col = this.data.row_list[i].col_list[j];
|
|
|
+ let grid_template_columns = '0';
|
|
|
+ col.grid_list.forEach(({ width }, i) => {
|
|
|
+ const w = `calc(${width} - ${(16 * (col.grid_list.length - 1)) / col.grid_list.length}px)`;
|
|
|
+ if (i === col.grid_list.length - 1) {
|
|
|
+ grid_template_columns += ` ${w} 0`;
|
|
|
+ } else {
|
|
|
+ grid_template_columns += ` ${w} 16px`;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ col.grid_template_columns = grid_template_columns;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 删除组件
|
|
|
+ * @param {number} i 行
|
|
|
+ * @param {number} j 列
|
|
|
+ * @param {number} k 格子
|
|
|
+ */
|
|
|
+ deleteComponent(i, j, k) {
|
|
|
+ return () => {
|
|
|
+ this.data.row_list[i].col_list[j].grid_list.splice(k, 1);
|
|
|
+
|
|
|
+ const colList = this.data.row_list[i].col_list[j];
|
|
|
+ if (colList.grid_list.length === 0) {
|
|
|
+ this.data.row_list[i].col_list.splice(j, 1);
|
|
|
+ let width_list = this.data.row_list[i].width_list;
|
|
|
+ const delW = width_list[j];
|
|
|
+ width_list.splice(j, 1);
|
|
|
+ this.data.row_list[i].width_list = width_list.map((item) => {
|
|
|
+ return `${Number(item.replace('%', '')) + Number(delW.replace('%', '') / width_list.length)}%`;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.data.row_list[i].col_list.length === 0) {
|
|
|
+ this.data.row_list.splice(i, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ const gridList = this.data.row_list[i]?.col_list[j]?.grid_list;
|
|
|
+ if (gridList?.length > 0) {
|
|
|
+ colList.grid_template_columns = `100%`;
|
|
|
+
|
|
|
+ const grid_template_areas = gridList.map(({ grid_area }) => grid_area).join(' ');
|
|
|
+ colList.grid_template_areas = `'.' '${grid_template_areas}' '.'`;
|
|
|
+
|
|
|
+ colList.grid_template_rows = `0 ${gridList.map(({ height }) => height).join(' 16px ')} 0`;
|
|
|
+ }
|
|
|
+ };
|
|
|
},
|
|
|
/**
|
|
|
* 拖拽开始
|
|
@@ -256,69 +366,117 @@ export default {
|
|
|
if (this.curRow >= -1 && this.curCol <= -1) {
|
|
|
this.data.row_list.splice(this.curRow + 1, 0, this.calculateRowInsertedObject());
|
|
|
}
|
|
|
- if (this.curRow >= -1 && this.curCol > -1 && this.curGrid > -1) {
|
|
|
+
|
|
|
+ if (this.curRow >= -1 && this.curCol > -1 && this.curGrid <= -1) {
|
|
|
this.calculateColObject();
|
|
|
}
|
|
|
+
|
|
|
+ if (this.curRow >= -1 && this.curCol > -1 && this.curGrid > -1) {
|
|
|
+ this.calculateGridObject();
|
|
|
+ }
|
|
|
}
|
|
|
this.enterCanvas = false;
|
|
|
},
|
|
|
+ getMultipleColStyle(i) {
|
|
|
+ let row = this.data.row_list[i];
|
|
|
+ let col = row.col_list;
|
|
|
+ if (col.length <= 1) {
|
|
|
+ return {
|
|
|
+ gridTemplateColumns: '0 100% 0',
|
|
|
+ };
|
|
|
+ }
|
|
|
+ let str = row.width_list
|
|
|
+ .map((item) => {
|
|
|
+ return `calc(${item} - ${(16 * (row.width_list.length - 1)) / row.width_list.length}px)`;
|
|
|
+ })
|
|
|
+ .join(' 16px ');
|
|
|
+ let gridTemplateColumns = `0 ${str} 0`;
|
|
|
+
|
|
|
+ return {
|
|
|
+ gridAutoFlow: 'column',
|
|
|
+ gridTemplateColumns,
|
|
|
+ gridTemplateRows: 'auto',
|
|
|
+ };
|
|
|
+ },
|
|
|
/**
|
|
|
- * 计算列插入的对象
|
|
|
+ * 计算网格插入的对象
|
|
|
*/
|
|
|
- calculateColObject() {
|
|
|
- let num = 0; // 计算当前行之前的所有 grid_list 的数量
|
|
|
- for (let i = 0; i <= this.curRow; i++) {
|
|
|
- this.data.row_list[i]?.col_list.forEach((item) => {
|
|
|
- num += item.grid_list.length;
|
|
|
- });
|
|
|
- }
|
|
|
- const id = getRandomNumber(12);
|
|
|
- const letter = String.fromCharCode(65 + num);
|
|
|
- let col = this.data.row_list[this.curRow].col_list[this.curCol];
|
|
|
+ calculateGridObject() {
|
|
|
+ const id = `ID-${getRandomNumber(12)}`;
|
|
|
+ const letter = `L${getRandomNumber(6)}`;
|
|
|
|
|
|
- col.grid_list.splice(this.curGrid, 0, {
|
|
|
+ let row = this.data.row_list[this.curRow];
|
|
|
+ let col = row.col_list[this.curCol];
|
|
|
+ let grid = col.grid_list;
|
|
|
+
|
|
|
+ grid.splice(this.curGrid, 0, {
|
|
|
id,
|
|
|
grid_area: letter,
|
|
|
+ width: '100%',
|
|
|
+ height: 'auto',
|
|
|
type: this.curType,
|
|
|
});
|
|
|
+ col.grid_template_areas = `'.' ${grid.map(({ grid_area }) => `'${grid_area}'`).join(" '.' ")} '.'`;
|
|
|
+ col.grid_template_rows = `0 ${grid.map(({ height }) => height).join(' 16px ')} 0`;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 计算列插入的对象
|
|
|
+ */
|
|
|
+ calculateColObject() {
|
|
|
+ const id = `ID-${getRandomNumber(12)}`;
|
|
|
+ const letter = `L${getRandomNumber(6)}`;
|
|
|
|
|
|
- let grid_template_columns = '0';
|
|
|
- col.grid_list.forEach((item, i) => {
|
|
|
- if (i === col.grid_list.length - 1) grid_template_columns += ' auto 0';
|
|
|
- else grid_template_columns += ' auto 16px';
|
|
|
+ let row = this.data.row_list[this.curRow];
|
|
|
+ let col = row.col_list;
|
|
|
+
|
|
|
+ let w = 0;
|
|
|
+ row.width_list.forEach((item, i) => {
|
|
|
+ let itemW = Number(item.replace('%', ''));
|
|
|
+ let rowW = itemW / (row.width_list.length + 1);
|
|
|
+ w += rowW;
|
|
|
+ row.width_list[i] = `${itemW - rowW}%`;
|
|
|
});
|
|
|
- col.grid_template_columns = grid_template_columns;
|
|
|
+ row.width_list.splice(this.curCol, 0, `${w}%`);
|
|
|
|
|
|
- let grid_template_areas = '.';
|
|
|
- col.grid_list.forEach(({ grid_area }) => {
|
|
|
- grid_template_areas += ` ${grid_area} .`;
|
|
|
+ col.splice(this.curCol, 0, {
|
|
|
+ width: '100%',
|
|
|
+ height: 'auto',
|
|
|
+ grid_template_areas: `'.' '${letter}' '.'`,
|
|
|
+ grid_template_columns: '100%',
|
|
|
+ grid_template_rows: '0 auto 0',
|
|
|
+ grid_list: [
|
|
|
+ {
|
|
|
+ id,
|
|
|
+ grid_area: letter,
|
|
|
+ width: '100%',
|
|
|
+ height: 'auto',
|
|
|
+ type: this.curType,
|
|
|
+ },
|
|
|
+ ],
|
|
|
});
|
|
|
- col.grid_template_areas = `"${grid_template_areas}"`;
|
|
|
},
|
|
|
/**
|
|
|
* 计算行插入的对象
|
|
|
*/
|
|
|
calculateRowInsertedObject() {
|
|
|
- let num = 0; // 计算当前行之前的所有 grid_list 的数量
|
|
|
- for (let i = 0; i <= this.curRow; i++) {
|
|
|
- this.data.row_list[i]?.col_list.forEach((item) => {
|
|
|
- num += item.grid_list.length;
|
|
|
- });
|
|
|
- }
|
|
|
- const id = getRandomNumber(12);
|
|
|
- const letter = String.fromCharCode(65 + num);
|
|
|
+ const id = `ID-${getRandomNumber(12)}`;
|
|
|
+ const letter = `L${getRandomNumber(6)}`;
|
|
|
|
|
|
return {
|
|
|
+ width_list: ['100%'],
|
|
|
col_list: [
|
|
|
{
|
|
|
width: '100%',
|
|
|
- grid_template_areas: `'. ${letter} .'`,
|
|
|
- grid_template_columns: '0 auto 0',
|
|
|
- grid_template_rows: 'auto',
|
|
|
+ height: 'auto',
|
|
|
+ grid_template_areas: `'.' '${letter}' '.'`,
|
|
|
+ grid_template_columns: '100%',
|
|
|
+ grid_template_rows: '0 auto 0',
|
|
|
grid_list: [
|
|
|
{
|
|
|
id,
|
|
|
grid_area: letter,
|
|
|
+ width: '100%',
|
|
|
+ height: 'auto',
|
|
|
type: this.curType,
|
|
|
},
|
|
|
],
|
|
@@ -365,9 +523,12 @@ export default {
|
|
|
border-radius: 4px;
|
|
|
|
|
|
.row {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- row-gap: 6px;
|
|
|
+ display: grid;
|
|
|
+ row-gap: 16px;
|
|
|
+
|
|
|
+ > .drag-vertical-line:not(:first-child, :last-child) {
|
|
|
+ left: 6px;
|
|
|
+ }
|
|
|
|
|
|
.col {
|
|
|
display: grid;
|
|
@@ -393,6 +554,11 @@ export default {
|
|
|
background-color: #379fff;
|
|
|
border-radius: 4px;
|
|
|
opacity: 0;
|
|
|
+
|
|
|
+ &.grid-line:not(:first-child, :last-child) {
|
|
|
+ position: relative;
|
|
|
+ top: 6px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.drag-vertical-line {
|