Vue 销毁可排序元素

问题描述 投票:0回答:1

总结

创建面板,并将图层添加到面板中。当尝试删除图层时 面板中的最后一个图层被删除,而不是图层项目 选择销毁。

完整问题:

理解 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>
vue.js draggable jquery-ui-sortable destroy splice
1个回答
0
投票

在下面的示例中,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>

© www.soinside.com 2019 - 2024. All rights reserved.