|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="create">
|
|
|
+ <div ref="create" class="create">
|
|
|
<div class="create-left">
|
|
|
<div class="back-container">
|
|
|
<el-button class="back" @click="back"><i class="el-icon-arrow-left"></i> 返回</el-button>
|
|
@@ -10,8 +10,9 @@
|
|
|
<div
|
|
|
v-for="{ value: childValue, icon, label: childLabel } in children"
|
|
|
:key="childValue"
|
|
|
+ ref="componentsItem"
|
|
|
class="components-item"
|
|
|
- @click="curType = childValue"
|
|
|
+ @mousedown="dragStart($event, childValue)"
|
|
|
>
|
|
|
<SvgIcon v-if="icon" :icon-class="icon" />
|
|
|
<span>{{ childLabel }}</span>
|
|
@@ -22,13 +23,36 @@
|
|
|
<div class="create-middle">
|
|
|
<div></div>
|
|
|
<main ref="canvas" class="canvas">
|
|
|
- <component :is="componentList[curType]" ref="components" @showSetting="showSetting" />
|
|
|
+ <div class="drag-line" data-row="-1"></div>
|
|
|
+ <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,
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <component
|
|
|
+ :is="componentList[grid.type]"
|
|
|
+ v-for="(grid, k) in col.grid_list"
|
|
|
+ :id="grid.id"
|
|
|
+ :key="k"
|
|
|
+ :style="{ gridArea: grid.grid_area }"
|
|
|
+ @showSetting="showSetting"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="drag-line" :data-row="i"></div>
|
|
|
+ </div>
|
|
|
</main>
|
|
|
</div>
|
|
|
|
|
|
<div class="create-right">
|
|
|
<div class="setting-tittle">设置</div>
|
|
|
- <component :is="componentSettingList[curType]" ref="setting" @updateSetting="updateSetting" />
|
|
|
+ <component :is="componentSettingList[curSettingType]" ref="setting" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -38,23 +62,212 @@ import { bookTypeOption, componentList, componentSettingList } from '../data/boo
|
|
|
|
|
|
export default {
|
|
|
name: 'CreatePage',
|
|
|
+ provide() {
|
|
|
+ return {
|
|
|
+ getCurSettingId: () => this.curSettingId,
|
|
|
+ };
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
+ data: {
|
|
|
+ id: '',
|
|
|
+ background_image_url: '',
|
|
|
+ background_position: {
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ width: 100,
|
|
|
+ height: 100,
|
|
|
+ },
|
|
|
+ // 组件列表
|
|
|
+ row_list: [],
|
|
|
+ },
|
|
|
+ curSettingType: '',
|
|
|
+ curSettingId: '',
|
|
|
curType: 'divider',
|
|
|
componentList,
|
|
|
componentSettingList,
|
|
|
bookTypeOption,
|
|
|
+ curRow: -2,
|
|
|
+ enterCanvas: false, // 是否进入画布
|
|
|
+ // 拖拽状态
|
|
|
+ drag: {
|
|
|
+ clientX: 0,
|
|
|
+ clientY: 0,
|
|
|
+ dragging: false,
|
|
|
+ },
|
|
|
};
|
|
|
},
|
|
|
+ watch: {
|
|
|
+ drag: {
|
|
|
+ handler(val) {
|
|
|
+ if (val.dragging) {
|
|
|
+ const dragging = document.querySelector('.dragging');
|
|
|
+ dragging.style.left = `${val.clientX}px`;
|
|
|
+ dragging.style.top = `${val.clientY}px`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ deep: true,
|
|
|
+ },
|
|
|
+ enterCanvas: {
|
|
|
+ handler(val) {
|
|
|
+ if (val) return;
|
|
|
+ const dragLineList = document.querySelectorAll('.drag-line');
|
|
|
+ dragLineList.forEach((item) => {
|
|
|
+ item.style.opacity = 0;
|
|
|
+ });
|
|
|
+ this.curRow = -2;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ document.addEventListener('mousemove', this.dragMove);
|
|
|
+ document.addEventListener('mouseup', this.dragEnd);
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ document.removeEventListener('mousemove', this.dragMove);
|
|
|
+ document.removeEventListener('mouseup', this.dragEnd);
|
|
|
+ },
|
|
|
methods: {
|
|
|
back() {
|
|
|
- this.$router.push('/');
|
|
|
+ this.$router.push('/chapter?id=1');
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 显示设置
|
|
|
+ * @param {object} setting
|
|
|
+ * @param {string} type
|
|
|
+ */
|
|
|
+ showSetting(setting, type, id) {
|
|
|
+ this.curSettingType = type;
|
|
|
+ this.curSettingId = id;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.setting.setSetting(setting);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 拖拽开始
|
|
|
+ * 用点击模拟拖拽
|
|
|
+ * @param {MouseEvent} event
|
|
|
+ * @param {string} type
|
|
|
+ */
|
|
|
+ dragStart(event, type) {
|
|
|
+ // 获取鼠标位置
|
|
|
+ const { clientX, clientY } = event;
|
|
|
+ document.body.style.userSelect = 'none'; // 禁止选中文本
|
|
|
+ this.drag.dragging = true;
|
|
|
+ this.curType = type;
|
|
|
+ // 在鼠标位置创建一个拖拽元素
|
|
|
+ const dragging = document.createElement('div');
|
|
|
+ dragging.className = 'dragging';
|
|
|
+ this.drag.clientX = clientX;
|
|
|
+ this.drag.clientY = clientY;
|
|
|
+ document.body.appendChild(dragging);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 鼠标移动
|
|
|
+ */
|
|
|
+ dragMove(event) {
|
|
|
+ if (!this.drag.dragging) return;
|
|
|
+
|
|
|
+ const { clientX, clientY } = event;
|
|
|
+ this.drag.clientX = clientX;
|
|
|
+ this.drag.clientY = clientY;
|
|
|
+
|
|
|
+ let { leftMarginDifference, topMarginDifference, isInsideCanvas } = this.getMarginDifferences();
|
|
|
+
|
|
|
+ this.enterCanvas = isInsideCanvas;
|
|
|
+ if (!isInsideCanvas) return;
|
|
|
+
|
|
|
+ const dragLineList = document.querySelectorAll('.drag-line');
|
|
|
+ let minDistance = Infinity;
|
|
|
+ let minIndex = -1;
|
|
|
+ dragLineList.forEach((item, index) => {
|
|
|
+ const rect = item.getBoundingClientRect();
|
|
|
+ const distance = Math.sqrt(
|
|
|
+ Math.pow(clientX - rect.left - rect.width / 2, 2) + Math.pow(clientY - rect.top - rect.height / 2, 2),
|
|
|
+ );
|
|
|
+ if (distance < minDistance) {
|
|
|
+ minDistance = distance;
|
|
|
+ minIndex = index;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ dragLineList.forEach((item, index) => {
|
|
|
+ if (index === minIndex) {
|
|
|
+ // 获取 item 中的 data-row
|
|
|
+ const row = item.getAttribute('data-row');
|
|
|
+ this.curRow = Number(row);
|
|
|
+ item.style.opacity = 1;
|
|
|
+ } else {
|
|
|
+ item.style.opacity = 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 鼠标松开
|
|
|
+ */
|
|
|
+ dragEnd() {
|
|
|
+ document.body.style.userSelect = 'auto';
|
|
|
+ const dragging = document.querySelector('.dragging');
|
|
|
+ if (dragging) {
|
|
|
+ document.body.removeChild(dragging);
|
|
|
+ this.drag.dragging = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.enterCanvas && this.curRow >= -1) {
|
|
|
+ this.data.row_list.splice(this.curRow + 1, 0, this.calculateInsertedObject());
|
|
|
+ }
|
|
|
+ this.enterCanvas = false;
|
|
|
},
|
|
|
- showSetting(setting) {
|
|
|
- this.$refs.setting.setSetting(setting);
|
|
|
+ /**
|
|
|
+ * 计算插入的对象
|
|
|
+ */
|
|
|
+ calculateInsertedObject() {
|
|
|
+ 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 letter = String.fromCharCode(65 + num);
|
|
|
+
|
|
|
+ return {
|
|
|
+ col_list: [
|
|
|
+ {
|
|
|
+ width: '100%',
|
|
|
+ grid_template_areas: `'${letter}'`,
|
|
|
+ grid_template_columns: 'auto',
|
|
|
+ grid_template_rows: 'auto',
|
|
|
+ grid_list: [
|
|
|
+ {
|
|
|
+ grid_area: letter,
|
|
|
+ type: this.curType,
|
|
|
+ id: letter,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
},
|
|
|
- updateSetting(setting) {
|
|
|
- this.$refs.components.updateSetting(setting);
|
|
|
+ /**
|
|
|
+ * 获取拖拽元素和画布的边距差值
|
|
|
+ * @returns {object} { leftMarginDifference, topMarginDifference, isInsideCanvas }
|
|
|
+ * leftMarginDifference: 拖拽元素和画布左边距差值
|
|
|
+ * topMarginDifference: 拖拽元素和画布上边距差值
|
|
|
+ * isInsideCanvas: 是否在画布内
|
|
|
+ */
|
|
|
+ getMarginDifferences() {
|
|
|
+ const rect1 = document.querySelector('.dragging').getBoundingClientRect();
|
|
|
+ const rect2 = this.$refs.canvas.getBoundingClientRect();
|
|
|
+
|
|
|
+ const leftMarginDifference = rect1.left - rect2.left + 128;
|
|
|
+ const topMarginDifference = rect1.top - rect2.top + 72;
|
|
|
+
|
|
|
+ let isInsideCanvas =
|
|
|
+ leftMarginDifference > 0 &&
|
|
|
+ leftMarginDifference < rect2.width &&
|
|
|
+ topMarginDifference > 0 &&
|
|
|
+ topMarginDifference < rect2.height;
|
|
|
+
|
|
|
+ return { leftMarginDifference, topMarginDifference, isInsideCanvas };
|
|
|
},
|
|
|
},
|
|
|
};
|
|
@@ -127,11 +340,33 @@ export default {
|
|
|
background-color: #ececec;
|
|
|
|
|
|
.canvas {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 6px;
|
|
|
width: 100%;
|
|
|
min-height: 100%;
|
|
|
padding: 24px;
|
|
|
- background-color: #f6f6f6;
|
|
|
+ background-color: #fff;
|
|
|
border-radius: 4px;
|
|
|
+
|
|
|
+ .row {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 6px;
|
|
|
+
|
|
|
+ .col {
|
|
|
+ display: grid;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .drag-line {
|
|
|
+ width: calc(100% - 16px);
|
|
|
+ height: 4px;
|
|
|
+ margin: 0 8px;
|
|
|
+ background-color: #379fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -148,3 +383,16 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
</style>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.dragging {
|
|
|
+ position: fixed;
|
|
|
+ z-index: 999;
|
|
|
+ width: 320px;
|
|
|
+ height: 180px;
|
|
|
+ background-color: #eaf5ff;
|
|
|
+ border: 1px solid #b5dbff;
|
|
|
+ border-radius: 4px;
|
|
|
+ transform: translate(-40%, -40%);
|
|
|
+}
|
|
|
+</style>
|