feat(manager): 画布楼层加名称标签与悬浮操作条

- 每楼层左上角显示组件名标签
- 选中楼层右侧竖排操作条:上移/下移/复制/删除/隐藏,替代原右上角关闭图标
- 隐藏楼层变暗并显示已隐藏蒙层,hidden 随 pageData 保存
This commit is contained in:
pikachu1995@126.com
2026-10-12 15:59:58 +08:00
parent 654839b247
commit 2f097ace49
2 changed files with 104 additions and 15 deletions

View File

@@ -45,6 +45,7 @@
> >
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list"> <div class="list">
<div class="floor-name">{{ element.name || element.type }}</div>
<component <component
v-if="templates[element.type]" v-if="templates[element.type]"
class="component" class="component"
@@ -60,15 +61,24 @@
> >
未知组件「{{ element.type }}」已下线,点击删除 未知组件「{{ element.type }}」已下线,点击删除
</div> </div>
<el-icon <div v-if="element.hidden" class="floor-hidden-mask">已隐藏</div>
v-if="selected == index" <div v-if="selected == index" class="floor-tools">
@click="closeComponent(index)" <el-icon
class="close" :class="{ disabled: index === 0 }"
:size="25" @click="moveComponent(index, -1)"
color="#e1251b" ><Top
> /></el-icon>
<CircleClose /> <el-icon
</el-icon> :class="{ disabled: index === contentData.list.length - 1 }"
@click="moveComponent(index, 1)"
><Bottom
/></el-icon>
<el-icon @click="copyComponent(index)"><CopyDocument /></el-icon>
<el-icon @click="closeComponent(index)"><Delete /></el-icon>
<el-icon @click="toggleHide(index)"
><View v-if="!element.hidden" /><Hide v-else
/></el-icon>
</div>
</div> </div>
</template> </template>
</draggable> </draggable>
@@ -88,7 +98,6 @@
</template> </template>
<script> <script>
import { import {
CircleClose,
Picture, Picture,
PictureFilled, PictureFilled,
Document, Document,
@@ -110,6 +119,12 @@ import {
Crop, Crop,
Minus, Minus,
EditPen, EditPen,
Top,
Bottom,
CopyDocument,
Delete,
View,
Hide,
} from "@element-plus/icons-vue"; } from "@element-plus/icons-vue";
import templates from "./template/index"; import templates from "./template/index";
import Draggable from "vuedraggable"; import Draggable from "vuedraggable";
@@ -121,7 +136,6 @@ export default {
components: { components: {
Draggable, Draggable,
decorate, decorate,
CircleClose,
Picture, Picture,
PictureFilled, PictureFilled,
Document, Document,
@@ -143,6 +157,12 @@ export default {
Crop, Crop,
Minus, Minus,
EditPen, EditPen,
Top,
Bottom,
CopyDocument,
Delete,
View,
Hide,
}, },
data() { data() {
return { return {
@@ -268,6 +288,29 @@ export default {
console.warn(`[page-decoration] 未知组件类型: ${element.type}`); console.warn(`[page-decoration] 未知组件类型: ${element.type}`);
this.closeComponent(index); this.closeComponent(index);
}, },
// 上移/下移楼层
moveComponent(index, dir) {
const target = index + dir;
if (target < 0 || target >= this.contentData.list.length) return;
const list = this.contentData.list;
[list[index], list[target]] = [list[target], list[index]];
this.selected = target;
this.handleComponent(this.contentData.list[target], target);
},
// 复制楼层
copyComponent(index) {
const cloned = JSON.parse(JSON.stringify(this.contentData.list[index]));
const key = Date.now() + "_" + Math.ceil(Math.random() * 99999);
cloned.key = key;
cloned.model = cloned.type + "_" + key;
this.contentData.list.splice(index + 1, 0, cloned);
this.handleComponent(this.contentData.list[index + 1], index + 1);
},
// 隐藏/显示楼层(渲染端跳过 hidden 楼层)
toggleHide(index) {
const item = this.contentData.list[index];
item.hidden = !item.hidden;
},
// 右侧栏回调 // 右侧栏回调
handleDrawer(val) { handleDrawer(val) {
let newIndex = this.selected; let newIndex = this.selected;

View File

@@ -124,11 +124,57 @@
.list { .list {
position: relative; position: relative;
} }
.close { .floor-name {
position: absolute; position: absolute;
right: 5px; left: 4px;
top: 10px; top: 4px;
z-index: 99; z-index: 60;
background: #ffffff;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 2px 8px;
font-size: 12px;
color: #606266;
white-space: nowrap;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
pointer-events: none;
}
.floor-tools {
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
z-index: 100;
display: flex;
flex-direction: column;
background: #ffffff;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
padding: 4px 0;
.el-icon {
padding: 6px 8px;
cursor: pointer;
color: #606266;
&:hover {
color: #409eff;
}
&.disabled {
color: #dcdfe6;
cursor: not-allowed;
}
}
}
.floor-hidden-mask {
position: absolute;
inset: 0;
z-index: 50;
background: rgba(0, 0, 0, 0.45);
color: #ffffff;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
} }
.unknown-component { .unknown-component {
padding: 16px 10px; padding: 16px 10px;