|
|
@@ -1,11 +1,17 @@
|
|
|
<template>
|
|
|
<main ref="canvas" class="canvas">
|
|
|
<div v-if="isEdit" class="edit">
|
|
|
- <div v-for="item in lineList" :key="item[0]" class="group-line" :style="computedGroupLine(item)"></div>
|
|
|
+ <div
|
|
|
+ v-for="item in lineList"
|
|
|
+ :key="item[0]"
|
|
|
+ class="group-line"
|
|
|
+ :data-canvas-height="canvasHeight"
|
|
|
+ :style="computedGroupLine(item)"
|
|
|
+ ></div>
|
|
|
<span class="drag-line" data-row="-1"></span>
|
|
|
<!-- 行 -->
|
|
|
<template v-for="(row, i) in data.row_list">
|
|
|
- <div :key="row.row_id" class="row" :style="computedRowStyle(i)">
|
|
|
+ <div :key="`check-${row.row_id}`" class="row" :style="computedRowStyle(i)">
|
|
|
<el-checkbox
|
|
|
v-if="row?.row_id"
|
|
|
v-model="rowCheckList[row.row_id]"
|
|
|
@@ -192,6 +198,7 @@ export default {
|
|
|
projectResourcePopedom: [], // 当前编辑人员的项目资源权限
|
|
|
componentsDataLoaded: false, // 组件数据是否加载完成
|
|
|
visible_id: this.$route.query.visible_id, // 可见组件 id
|
|
|
+ canvasHeight: 0, // 画布高度,监听变化用于重绘分组线
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -268,13 +275,8 @@ export default {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 过滤掉已经不存在的行,并将第一行的 is_pre_same_group 设置为 false
|
|
|
- this.content_group_row_list = this.content_group_row_list.filter((group) =>
|
|
|
- val.find((row) => row.row_id === group.row_id),
|
|
|
- );
|
|
|
- if (this.content_group_row_list[0]) {
|
|
|
- this.content_group_row_list[0].is_pre_same_group = false;
|
|
|
- }
|
|
|
+ // 过滤掉已经不存在的行,并修正行删除后的分组状态
|
|
|
+ this.content_group_row_list = this.normalizeGroupRowList(this.content_group_row_list, val);
|
|
|
},
|
|
|
immediate: true,
|
|
|
},
|
|
|
@@ -411,11 +413,14 @@ export default {
|
|
|
document.addEventListener('mousemove', this.dragMove);
|
|
|
document.addEventListener('mouseup', this.dragEnd);
|
|
|
window.addEventListener('keydown', this.handleCopy);
|
|
|
+ // 监听画布高度变化,重绘分组线
|
|
|
+ this.initCanvasResizeObserver();
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
document.removeEventListener('mousemove', this.dragMove);
|
|
|
document.removeEventListener('mouseup', this.dragEnd);
|
|
|
window.removeEventListener('keydown', this.handleCopy);
|
|
|
+ this.destroyCanvasResizeObserver();
|
|
|
},
|
|
|
methods: {
|
|
|
/**
|
|
|
@@ -1574,11 +1579,41 @@ export default {
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
+ * 初始化画布 ResizeObserver,监听画布高度变化
|
|
|
+ * 高度变化时更新 canvasHeight,触发重新渲染以重绘分组线
|
|
|
+ */
|
|
|
+ initCanvasResizeObserver() {
|
|
|
+ const canvas = this.$refs.canvas;
|
|
|
+ if (!canvas || typeof ResizeObserver === 'undefined') return;
|
|
|
+
|
|
|
+ this.canvasResizeObserver = new ResizeObserver((entries) => {
|
|
|
+ entries.forEach((entry) => {
|
|
|
+ const height = entry.contentRect.height;
|
|
|
+ if (height !== this.canvasHeight) {
|
|
|
+ this.canvasHeight = height;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ this.canvasResizeObserver.observe(canvas);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 销毁画布 ResizeObserver
|
|
|
+ */
|
|
|
+ destroyCanvasResizeObserver() {
|
|
|
+ if (this.canvasResizeObserver) {
|
|
|
+ this.canvasResizeObserver.disconnect();
|
|
|
+ this.canvasResizeObserver = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
* 计算分组线样式
|
|
|
* @param {Array} rowIdList 行 ID 列表
|
|
|
* @returns {Object} 样式对象
|
|
|
*/
|
|
|
computedGroupLine(rowIdList) {
|
|
|
+ // 画布高度变化时(canvasHeight 更新)会触发组件重渲染,此处重新读取 DOM 计算分组线位置
|
|
|
+
|
|
|
// 获取画布顶部位置
|
|
|
const canvas = this.$refs.canvas;
|
|
|
const firstCheckbox = this.$el.querySelector(`.row-checkbox.${rowIdList[0]}`);
|
|
|
@@ -1682,6 +1717,46 @@ export default {
|
|
|
return data;
|
|
|
},
|
|
|
/**
|
|
|
+ * 删除行后归一化行分组列表
|
|
|
+ *
|
|
|
+ * is_pre_same_group 为 true 表示该行与其前一行同属一个分组。
|
|
|
+ * 行删除后,content_group_row_list 中剩余行的 is_pre_same_group 仍是删除前的值,
|
|
|
+ * 若某分组的首行被删除,则组内剩余首行的 is_pre_same_group 需置为 false,
|
|
|
+ * 否则分组线(.group-line)会错误地连接到前一组。
|
|
|
+ *
|
|
|
+ * @param {Array} groupList 删除前(含新增行)的行分组列表
|
|
|
+ * @param {Array} rowList 删除后的 row_list
|
|
|
+ * @returns {Array} 归一化后的行分组列表
|
|
|
+ */
|
|
|
+ normalizeGroupRowList(groupList, rowList) {
|
|
|
+ const validRowIds = new Set((rowList || []).map((row) => row.row_id));
|
|
|
+
|
|
|
+ // 按删除前的分组划分:is_pre_same_group 为 false 的行是新分组的首行
|
|
|
+ const groups = [];
|
|
|
+ let curGroup = [];
|
|
|
+ (groupList || []).forEach((item) => {
|
|
|
+ if (item.is_pre_same_group) {
|
|
|
+ curGroup.push(item);
|
|
|
+ } else {
|
|
|
+ if (curGroup.length) groups.push(curGroup);
|
|
|
+ curGroup = [item];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (curGroup.length) groups.push(curGroup);
|
|
|
+
|
|
|
+ // 过滤掉已删除的行,并将每组剩余首行的 is_pre_same_group 置为 false,其余保持 true
|
|
|
+ const result = [];
|
|
|
+ groups.forEach((group) => {
|
|
|
+ const remaining = group.filter((item) => validRowIds.has(item.row_id));
|
|
|
+ if (!remaining.length) return;
|
|
|
+ remaining.forEach((item, i) => {
|
|
|
+ item.is_pre_same_group = i > 0;
|
|
|
+ });
|
|
|
+ result.push(...remaining);
|
|
|
+ });
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ /**
|
|
|
* 加载模板数据
|
|
|
* @param {Object} param
|
|
|
* @param {Object} param.data - 模板数据
|