Untitled

 avatar
unknown
javascript
4 months ago
6.1 kB
1
Indexable
<template>
  <div class="main-container">
    <h3>Панель элементов</h3>
    <div class="elements-panel">
      <div
        v-for="element in elements"
        :key="element.id"
        class="panel-item"
        :style="{ width: element.width * 50 + 'px' }"
        draggable="true"
        @dragstart="onDragStart($event, element.id, true)"
        @dragend="onDragEnd"
      >
        {{ element.id }}
      </div>
    </div>

    <h3>Сетка</h3>
    <div class="grid-container">
      <div
        v-for="(cell, index) in grid"
        :key="index"
        class="grid-cell"
        @dragover.prevent
        @drop="onDrop($event, index)"
      ></div>
      
      <div
        v-for="(cell, index) in grid"
        :key="'item-' + index"
        v-if="cell !== null"
        class="grid-item"
        :style="{
          left: cell.pos * 50 + 'px',
          width: (cell.width * 50) + 'px'
        }"
        draggable="true"
        @dragstart="onDragStart($event, cell.id, false)"
        @dragend="onDragEnd"
        @click="selectElement(cell)"
      >
        {{ cell.id }}
      </div>
    </div>

    <div v-if="selectedElement" class="form-container">
      <h3>Редактирование: {{ selectedElement.id }}</h3>
      <label>
        Ширина (ячейки):
        <input type="number" v-model.number="newWidth" min="1" max="12" />
      </label>
      <button @click="applyWidthChange">Применить</button>
      <button @click="removeElement">Удалить</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      grid: Array(12).fill(null),
      elements: [
        { id: "a", width: 2 },
        { id: "b", width: 1 },
        { id: "c", width: 3 },
      ],
      draggedElement: null,
      draggedFromPanel: false,
      selectedElement: null,
      newWidth: null,
    };
  },
  methods: {
    onDragStart(event, id, fromPanel) {
      if (fromPanel) {
        this.draggedElement = this.elements.find((el) => el.id === id);
        this.draggedFromPanel = true;
      } else {
        this.draggedElement = this.grid.find((cell) => cell && cell.id === id);
        this.draggedFromPanel = false;
      }
      event.dataTransfer.setData("text/plain", id);
      event.dataTransfer.effectAllowed = "move";
    },
    onDragEnd() {
      this.draggedElement = null;
      this.draggedFromPanel = false;
    },
    onDrop(event, dropPos) {
      if (!this.draggedElement) return;
      const element = this.draggedElement;
      const { width } = element;

      if (this.canPlace(dropPos, width, element)) {
        if (this.draggedFromPanel) {
          // Убираем элемент из панели
          this.elements = this.elements.filter((el) => el.id !== element.id);
          // Создаём новый объект, чтобы реактивно обновить
          const newElement = { ...element, pos: dropPos };
          this.$set(this.grid, dropPos, newElement);
        } else {
          // Перемещаем элемент внутри сетки
          const oldPos = element.pos;
          this.$set(this.grid, oldPos, null);

          const updatedElement = { ...element, pos: dropPos };
          this.$set(this.grid, dropPos, updatedElement);

          // Если выбранный элемент был перемещён, обновим ссылку
          if (this.selectedElement && this.selectedElement.id === element.id) {
            this.selectedElement = updatedElement;
          }
        }
        this.draggedElement = null;
      } else {
        alert("Невозможно разместить элемент здесь!");
      }
    },
    canPlace(pos, width, element) {
      if (pos + width > this.grid.length) return false;
      for (let i = pos; i < pos + width; i++) {
        const cellEl = this.grid[i];
        if (cellEl !== null && cellEl !== element) {
          return false;
        }
      }
      return true;
    },
    selectElement(element) {
      this.selectedElement = element;
      this.newWidth = element.width;
    },
    applyWidthChange() {
      if (!this.selectedElement) return;
      const element = this.selectedElement;
      const oldWidth = element.width;
      const oldPos = element.pos;
      const newW = this.newWidth;

      // Удаляем с текущей позиции
      this.$set(this.grid, oldPos, null);

      const updatedElement = { ...element, width: newW };
      if (this.canPlace(oldPos, newW, updatedElement)) {
        this.$set(this.grid, oldPos, updatedElement);
        this.selectedElement = updatedElement; // Обновляем ссылку
      } else {
        // Откат
        this.$set(this.grid, oldPos, element);
        alert("Недостаточно места для новой ширины");
      }
    },
    removeElement() {
      if (!this.selectedElement) return;
      const element = this.selectedElement;
      this.$set(this.grid, element.pos, null);
      this.selectedElement = null;
    },
  },
};
</script>

<style scoped>
.main-container {
  font-family: Arial, sans-serif;
  padding: 20px;
}

.elements-panel {
  display: flex;
  margin-bottom: 20px;
}

.panel-item {
  background: #ddd;
  margin-right: 10px;
  text-align: center;
  cursor: grab;
  height: 50px;
  line-height: 50px;
  border: 1px solid #aaa;
  user-select: none;
}

.grid-container {
  position: relative;
  width: 600px;
  height: 50px;
  border: 1px solid #000;
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.grid-cell {
  width: 50px;
  height: 50px;
  border-right: 1px dotted #ccc;
  box-sizing: border-box;
  position: relative;
}

.grid-cell:last-child {
  border-right: none;
}

.grid-item {
  position: absolute;
  top: 0;
  height: 50px;
  background: #8cf;
  border: 1px solid #44f;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  user-select: none;
  box-sizing: border-box;
}

.form-container {
  margin-top: 20px;
  border: 1px solid #444;
  padding: 10px;
  background: #f9f9f9;
}
</style>
Editor is loading...
Leave a Comment