GridLayout.vue 7.97 KB
<template>
  <div class="grid-layout" :class="{ 'is-active': isActive, 'has-column': hasColumn }" @click="handleClick">
    <div class="button-group" v-if="isActive">
      <el-button type="text" class="col-layout-drag drag-handler" icon="icon-drag"></el-button>
      <el-button type="text" class="col-layout-delete" icon="icon-application-delete"
        @click="handleWidgetDelete(colLayoutIndex)"></el-button>
    </div>
    <el-row :gutter="gutter" class="grid-row" ref="gridRow">
      <el-col class="grid-col" :span="it.span" v-for="(it, index) in colConfig.columns" :key="index">
        <draggable v-model="it.list" v-bind="{
          group: 'column',
          ghostClass: 'ghost',
          animation: 200,
          handle: '.drag-handler'
        }" :key="it.key" class="draggable-item" @add="handleWidgetColAdd($event, colConfig, index)">
          <transition-group name="fade" tag="div" class="widget-form-list  "
            :class="{ 'no-column': firstLayoutNoColumn && index === 0 }">
            <div key="empty" class="empty__col" v-if="firstLayoutNoColumn && index === 0">
              <el-image :src="noDataImage"></el-image>
              <p class="no-data-text">拖拽栏目进行门户设计</p>
            </div>
            <template v-else>
              <template v-for="(col, idx) in it.list.filter(item => item.key)">
                <div class="grid-layout drag-handler grid-layout-sub" :key="col.key" :class="{ 'is-active': isSubActive(index,idx)}" @click.stop="handleClick(index,idx,'columnList')">
                  <div class="button-group" v-if="isSubActive(index,idx)">
                    <el-button type="text" class="col-layout-drag drag-handler" icon="icon-drag"></el-button>
                    <el-button type="text" class="col-layout-delete" icon="icon-application-delete"
                      @click.stop="handleDeleteColumn(idx, index)"></el-button>
                  </div>
                  <div class="img-wrap" >
                    <div>
                      <snapshot-img :imageData="col"></snapshot-img>
                    </div>
                  </div>
                </div>
              </template>
            </template>
          </transition-group>
        </draggable>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import Draggable from 'vuedraggable'
const SnapshotImg = () => import('@/components/portal/SnapshotImg.vue')
export default {
  name: 'GridLayout',
  components: {
    Draggable,
    SnapshotImg
  },
  props: {
    gutter: {
      type: Number,
      default: 24
    },
    colConfig: {
      type: Object,
      default: () => { }
    },
    select: {
      type: Object,
      default: () => { }
    },
    colLayoutIndex: {
      type: Number
    },
    data: {
      type: Object,
      default: () => { }
    }
  },
  data() {
    return {
      selectWidget: this.select,
      num: 0,
      noDataImage: require('@/assets/nodata_images/drag.png'),
    }
  },
  computed: {
    isActive() {
      return this.selectWidget.key === this.colConfig.key
    },
    isSubActive(){
      return function(columnsIndex,columnsListIndex){
          return this.selectWidget.key === this.colConfig.columns[columnsIndex].list[columnsListIndex].key
      }
      
    },
    hasColumn() {
      return this.colConfig.columns.some(item => item.list.length > 0)
    },
    firstLayoutNoColumn() {
      return (
        this.data.list[0].columns[0].list.length < 1 &&
        this.colLayoutIndex === 0
      )
    }
  },

  watch: {
    select(val) {
      this.selectWidget = val
    },
    selectWidget: {
      handler(val) {
        this.$emit('update:select', val)
      },
      deep: true
    },
    'colConfig.columns': {
      handler(val) {
        this.num = Math.max.apply(Math, val.map(item => { return item.list.length }))

      },
      immediate: true,
      deep: true
    }
  },
  methods: {
    handleWidgetColAdd($event, colConfig, index) {
      const currentIndex =
        colConfig.columns[index].list.length <= 1 ? 0 : $event.newIndex
      this.setColumnData(currentIndex, colConfig, index)
    },
    setColumnData(newIndex, colConfig, index) {
      const key = `${Date.parse(new Date())}_${Math.ceil(Math.random() * 10e6)}`
      this.$set(colConfig.columns[index].list, newIndex, {
        ...colConfig.columns[index].list[newIndex],
        key
      })
      this.selectWidget = colConfig.columns[index].list[newIndex]
    },
    handleClick(index,idx,type) {
      if(type){// 区分点击元素
        this.selectWidget = this.data.list[this.colLayoutIndex].columns[index].list[idx]
      }else{
        this.selectWidget = this.data.list[this.colLayoutIndex]
      }
    },
    handleWidgetDelete(index) {
      if (this.data.list.length - 1 === index) {
        if (index === 0) {
          this.selectWidget = {}
        } else {
          this.selectWidget = this.data.list[index - 1]
        }
      } else {
        this.selectWidget = this.data.list[index + 1]
      }

      this.$nextTick(() => {
        this.data.list.splice(index, 1)
      })
    },
    handleDeleteColumn(colIndex, index) {
      if (
        this.data.list.length > 0 &&
        this.data.list[this.colLayoutIndex].columns.length > 0
      ) {
        this.data.list[this.colLayoutIndex].columns[index].list.splice(
          colIndex,
          1
        )
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@mixin horizontalVerticalCenter {
  display: flex;
  align-items: center;
  justify-content: center;
}

.grid-layout {
  min-height: 208px;
  border: 1px dashed #b5b5b5;
  margin-bottom: 12px;

  .grid-row {
    min-height: 192px;
    margin: 8px !important;
  }

  .grid-col,
  .draggable-item,
  .widget-form-list {
    min-height: 192px;
  }

  .draggable-item {
    background: #f9f9f9;
    height: calc(100% - 12px);
  }

  .draggable-content__header {
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border: 1px solid #e6e6e6;
    margin-bottom: 16px;
    background: #fff;

    .title {
      flex: 1;
      color: #222222;
      font-size: 18px;
      padding: 16px 18px;
      font-weight: bold;
      cursor: move;

      &::before {
        position: relative;
        top: 3px;
        display: inline-block;
        content: '';
        width: 4px;
        height: 19px;
        margin-right: 10px;
        background: #409eff;
      }
    }

    .btn-group {
      padding: 5px 18px;
    }

    i {
      font-size: 14px;
      color: #666666;
    }
  }

  .ghost {
    background: rgb(171, 209, 228);
  }

  .draggable-content__header {
    &:hover {
      box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
    }
  }
}

.has-column {
  min-height: 56px;

  .grid-row,
  .widget-form-list,
  .grid-col,
  .draggable-item {
    min-height: 56px;
  }
}

.no-column {
  @include horizontalVerticalCenter;

  .empty__col {
    width: 100%;
    height: 100%;
    @include horizontalVerticalCenter;
  }
}

.grid-layout_one {
  .grid-col {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

.grid-layout_two,
.grid-layout_three {
  .grid-col {
    &:first-child {
      padding-left: 0 !important;
    }

    &:last-child {
      padding-right: 0 !important;
    }
  }
}

.is-active {
  border: 2px solid #409eff;
  position: relative;

  .button-group {
    position: absolute;
    z-index: 9;
    top: -32px;
    left: -2px;
  }

  .col-layout-delete,
  .col-layout-drag {
    width: 38px;
    height: 32px;
    padding: 0;
    line-height: 30px;
    background: #409EFF;
    color:#fff;
  }

  .col-layout-delete {
    margin-left: 0;
    border-left: none;
    border-radius: 0 2px 2px 0;
  }

  ::v-deep {
    .col-layout-drag {
      cursor: move;
      border-radius: 2px 0 0 2px;

      .icon-tuozhuai {
        font-weight: bold;
      }
    }
  }
}
.img-wrap {
  position:relative;
  .btn-group{
    position:absolute;
    top:0;
    right:0;
    .el-button{
      padding:5px;
    }
  }
}
.grid-layout-sub{
  border: 1px solid #DCDEE0;
  &.is-active {
    border: 2px solid #409eff!important;
  }
}
::v-deep{
  .el-row{
    display:flex;
  }
}
</style>