总结
创建面板,并将图层添加到面板中。当尝试删除图层时 面板中的最后一个图层被删除,而不是图层项目 选择销毁。
完整问题:
理解 Vue.js Lower Third 图形控制器中的 control.html
control.html 文件是用于创建和管理层的 Web 应用程序的重要组成部分。该界面使用 Vue.js 构建,允许用户配置和设计高度自定义的层。
control.html 概述
control.html 页面具有用户友好的界面,包含多个部分,每个部分专用于设计过程的特定方面:
设计编辑器部分:提供用于向面板添加各种面板元素的选择菜单。
主体部分特别具有交互性,因为它支持拖放功能,允许用户对面板元素重新排序。
问题:图层删除不正确
control.html 界面中遇到的一个值得注意的问题是删除特定层。当尝试从面板中删除特定层时,正确的层没有被破坏。
故障排除和解决方案
为了解决此问题,采取了几个步骤来诊断和纠正问题:
索引验证:首先确认传递给destroyLayer方法的索引是准确的。这涉及添加 console.log 语句来输出当前索引并验证它们与预期目标的一致性。
唯一键:在 v-for 指令中,通过将 :key 属性绑定到每个层的唯一属性(通常是 id,而不是数组索引)来为每个层提供唯一键。这有助于 Vue 清楚地识别每个 DOM 元素,从而保持重新排序和删除等操作的保真度。
简化的 destroyLayer 功能:destroyLayer 方法经过简化,可直接使用 splice 删除层,无需额外的反应性黑客,例如 $set 或 $forceUpdate,这些通常是不必要的,并且可以掩盖反应性问题的根本原因。
https://jsfiddle.net/4vh80j35/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue.js Control Application</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.2/Sortable.min.js"></script>
<style>
.container {
border: 2px solid #000;
padding: 10px;
margin-top: 10px;
}
.panel {
border: 1px solid blue;
margin: 5px;
padding: 5px;
position: relative;
}
.destroy-btn {
color: white;
background-color: red;
padding: 2px 5px;
cursor: pointer;
position: absolute;
right: 5px;
top: 5px;
}
.layer {
border: 1px solid green;
margin: 3px;
padding: 3px;
position: relative;
background-color: #efefef;
}
.draggable-container {
min-height: 50px;
}
</style>
</head>
<body>
<div id="app">
<div class="editor">
<button @click="createPanel">Create Panel</button>
<div v-if="panels.length > 0">
<label for="panelSelect">Choose a panel to add a layer:</label>
<select id="panelSelect" v-model="selectedPanel">
<option v-for="(panel, index) in panels" :value="index">Panel {{ index + 1 }}</option>
</select>
<button @click="createLayer">Add Layer to Selected Panel</button>
</div>
</div>
<div class="container">
<div class="panel" v-for="(panel, pIndex) in panels" :key="`panel-${pIndex}`" :data-index="pIndex">
<span class="destroy-btn" @click="destroyPanel(pIndex)">[destroy]</span>
Panel {{ pIndex + 1 }}
<div class="draggable-container">
<div class="layer" v-for="(layer, lIndex) in panel.layers" :key="`layer-${lIndex}`">
Layer {{ lIndex + 1 }} (in Panel {{ pIndex + 1 }})
<span class="destroy-btn" @click.stop="destroyLayer(pIndex, lIndex)">[destroy]</span>
</div>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
panels: [],
selectedPanel: null
},
mounted() {
this.$nextTick(() => {
this.panels.forEach((_, index) => {
this.makeSortable(index);
});
});
},
methods: {
createPanel() {
const newPanelIndex = this.panels.push({ layers: [] }) - 1;
this.$nextTick(() => {
this.makeSortable(newPanelIndex);
});
if (this.selectedPanel === null) {
this.selectedPanel = 0;
}
},
createLayer() {
if (this.selectedPanel !== null && this.selectedPanel < this.panels.length) {
this.panels[this.selectedPanel].layers.push({});
}
},
makeSortable(panelIndex) {
const container = this.$el.querySelector(`.panel[data-index="${panelIndex}"] .draggable-container`);
Sortable.create(container, {
group: 'shared', // set the group to 'shared' for all containers
onAdd: (evt) => {
console.log(`New ${evt.newIndex} from ${evt.oldIndex} in panel`);
const item = this.panels[evt.oldIndex].layers.splice(evt.oldIndex, 1)[0];
this.panels[evt.newIndex].layers.splice(evt.newIndex, 0, item);
},
onUpdate: (evt) => {
const panelIndex = evt.to.dataset.index;
const item = this.panels[panelIndex].layers.splice(evt.oldIndex, 1)[0];
this.panels[panelIndex].layers.splice(evt.newIndex, 0, item);
},
});
},
destroyPanel(index) {
this.panels.splice(index, 1);
if (index === this.selectedPanel) {
this.selectedPanel = (this.panels.length > 0) ? 0 : null;
}
},
destroyLayer(panelIndex, layerIndex) {
console.log(`Attempting to destroy layer ${layerIndex} of ${this.panels[panelIndex].layers.length} in panel ${panelIndex}`);
if (panelIndex < this.panels.length) {
const layers = this.panels[panelIndex].layers;
if (layerIndex < layers.length) {
// Removes the layer from the panel
layers.splice(layerIndex, 1);
}
// If the panel becomes empty and it's the selected one, deselect it
if (!layers.length && this.selectedPanel === panelIndex) {
this.selectedPanel = null;
}
}
},
}
});
</script>
</body>
</html>
在下面的示例中,id 被添加到每一层,以便您可以观察破坏:
new Vue({
el: '#app',
data: {
panels: [],
selectedPanel: null
},
mounted() {
this.$nextTick(() => {
this.panels.forEach((_, index) => {
this.makeSortable(index);
});
});
},
methods: {
createPanel() {
const newPanelIndex = this.panels.push({ layers: [] }) - 1;
this.$nextTick(() => {
this.makeSortable(newPanelIndex);
});
if (this.selectedPanel === null) {
this.selectedPanel = 0;
}
},
createLayer() {
if (this.selectedPanel !== null && this.selectedPanel < this.panels.length) {
const id = this.panels[this.selectedPanel].layers.length ? Math.max(...this.panels[this.selectedPanel].layers.map(o => o.id)) + 1 : 0
this.panels[this.selectedPanel].layers.push({id});
}
},
makeSortable(panelIndex) {
const container = this.$el.querySelector(`.panel[data-index="${panelIndex}"] .draggable-container`);
Sortable.create(container, {
group: 'shared', // set the group to 'shared' for all containers
onAdd: (evt) => {
console.log(`New ${evt.newIndex} from ${evt.oldIndex} in panel`);
const item = this.panels[evt.oldIndex].layers.splice(evt.oldIndex, 1)[0];
this.panels[evt.newIndex].layers.splice(evt.newIndex, 0, item);
},
onUpdate: (evt) => {
const panelIndex = evt.to.dataset.index;
const item = this.panels[panelIndex].layers.splice(evt.oldIndex, 1)[0];
this.panels[panelIndex].layers.splice(evt.newIndex, 0, item);
},
});
},
destroyPanel(index) {
this.panels.splice(index, 1);
if (index === this.selectedPanel) {
this.selectedPanel = (this.panels.length > 0) ? 0 : null;
}
},
destroyLayer(panelIndex, layerIndex) {
console.log(`Attempting to destroy layer ${layerIndex} of ${this.panels[panelIndex].layers.length} in panel ${panelIndex}`);
if (panelIndex < this.panels.length) {
const layers = this.panels[panelIndex].layers;
if (layerIndex < layers.length) {
// Removes the layer from the panel
layers.splice(layerIndex, 1);
}
// If the panel becomes empty and it's the selected one, deselect it
if (!layers.length && this.selectedPanel === panelIndex) {
this.selectedPanel = null;
}
}
},
}
});
.container {
border: 2px solid #000;
padding: 10px;
margin-top: 10px;
}
.panel {
border: 1px solid blue;
margin: 5px;
padding: 5px;
position: relative;
}
.destroy-btn {
color: white;
background-color: red;
padding: 2px 5px;
cursor: pointer;
position: absolute;
right: 5px;
top: 5px;
}
.layer {
border: 1px solid green;
margin: 3px;
padding: 3px;
position: relative;
background-color: #efefef;
}
.draggable-container {
min-height: 50px;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.2/Sortable.min.js"></script>
<div id="app">
<div class="editor">
<button @click="createPanel">Create Panel</button>
<div v-if="panels.length > 0">
<label for="panelSelect">Choose a panel to add a layer:</label>
<select id="panelSelect" v-model="selectedPanel">
<option v-for="(panel, index) in panels" :value="index">Panel {{ index + 1 }}</option>
</select>
<button @click="createLayer">Add Layer to Selected Panel</button>
</div>
</div>
<div class="container">
<div class="panel" v-for="(panel, pIndex) in panels" :key="`panel-${pIndex}`" :data-index="pIndex">
<span class="destroy-btn" @click="destroyPanel(pIndex)">[destroy]</span>
Panel {{ pIndex + 1 }}
<div class="draggable-container">
<div class="layer" v-for="(layer, lIndex) in panel.layers" :key="`layer-${lIndex}`">
Layer {{ lIndex + 1 }} (in Panel {{ pIndex + 1 }}) id: {{layer.id}}
<span class="destroy-btn" @click.stop="destroyLayer(pIndex, lIndex)">[destroy]</span>
</div>
</div>
</div>
</div>
</div>