目录

首页拖拽布局layout布局可拖拽保存

首页拖拽布局layout布局可拖拽保存

使用第三方插件实现拖拽布局

“grid-layout-plus”:最新版即可

效果图:

代码实现:

index文件

<script setup lang="ts">
import { ref, reactive } from 'vue'
import cardPage from "../../../components/card/index.vue";

const layout = reactive(
    (localStorage.getItem("layout") && JSON.parse(localStorage.getItem("layout"))) || [
      { x: 0, y: 1, w: 6, h: 7, i: '1' },
      { x: 6, y: 1, w: 6, h: 7, i: '2' },
      { x: 0, y: 2, w: 12, h: 5.6, i: '3' },
      { x: 0, y: 3, w: 6, h: 8, i: '4' },
      { x: 6, y: 3, w: 6, h: 8, i: '5' },
      { x: 0, y: 4, w: 12, h: 6, i: '6' },
      { x: 0, y: 5, w: 12, h: 6, i: '7' }
    ]
);
const pinkParams = ref({
  title: "我是测试Title",
  desc: "我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据",
  info: "信息信息信息信息信息信息信息信息信息信息",
  detail: "详情详情详情详情详情详情详情",
  file: "Title.xlsx",
});
const redParams = ref({
  title: "我是RED",
  desc: "我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据",
  info: "信息信息信息信息信息信息信息信息信息信息",
  detail: "详情详情详情详情详情详情详情",
  file: "RED.xlsx",
});
const blueParams = ref({
  title: "我是Blue",
  desc: "我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据",
  info: "信息信息信息信息信息信息信息信息信息信息",
  detail: "详情详情详情详情详情详情详情",
  file: "BLUE.xlsx",
});
const grayParams = ref({
  title: "我是Gray",
  desc: "我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据",
  info: "信息信息信息信息信息信息信息信息信息信息",
  detail: "详情详情详情详情详情详情详情",
  file: "Gray.xlsx",
});
const yellowParams = ref({
  title: "我是Yellow",
  desc: "我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据我是详细数据",
  info: "信息信息信息信息信息信息信息信息信息信息",
  detail: "详情详情详情详情详情详情详情",
  file: "Yellow.xlsx",
});

// 移动之后
const handleMoved = (i: number | string, newX: number, newY: number) =>{
  console.log(i, "item", newX, newY);
  console.log(layout, "layout");
  localStorage.setItem("layout", JSON.stringify(layout));
  console.log(JSON.parse(localStorage.getItem("layout")),"1111");
}
</script>

<template>
  <div style="width: 100%; height: 100%; margin-top: 10px">
    <GridLayout
        v-model:layout="layout"
        :row-height="30"
        :is-draggable="true"
        :is-resizable="false"
        :is-bounded="true"
    >
      <GridItem
          v-for="item in layout"
          :key="item.i"
          :x="item.x"
          :y="item.y"
          :w="item.w"
          :h="item.h"
          :i="item.i"
          @moved="handleMoved"
      >
        <card-page v-if="item.i==='1'">
          <template #header>{{pinkParams.title}}</template>
          <div>{{pinkParams.desc}}</div>
          <div>{{pinkParams.info}}</div>
          <div>{{pinkParams.detail}}</div>
          <div>{{pinkParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
        <card-page v-else-if="item.i==='2'">
          <template #header>{{redParams.title}}</template>
          <div>{{redParams.desc}}</div>
          <div>{{redParams.info}}</div>
          <div>{{redParams.detail}}</div>
          <div>{{redParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
        <card-page v-else-if="item.i==='3'">
          <template #header>{{blueParams.title}}</template>
          <div>{{blueParams.desc}}</div>
          <div>{{blueParams.info}}</div>
          <div>{{blueParams.detail}}</div>
          <div>{{blueParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
        <card-page v-else-if="item.i==='4'">
          <template #header>{{grayParams.title}}</template>
          <div>{{grayParams.desc}}</div>
          <div>{{grayParams.info}}</div>
          <div>{{grayParams.detail}}</div>
          <div>{{grayParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
        <card-page v-else-if="item.i==='5'">
          <template #header>{{yellowParams.title}}</template>
          <div>{{yellowParams.desc}}</div>
          <div>{{yellowParams.info}}</div>
          <div>{{yellowParams.detail}}</div>
          <div>{{yellowParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
        <card-page v-else-if="item.i==='6'">
          <template #header>{{grayParams.title}}</template>
          <div>{{grayParams.desc}}</div>
          <div>{{grayParams.info}}</div>
          <div>{{grayParams.detail}}</div>
          <div>{{grayParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
        <card-page v-else-if="item.i==='7'">
          <template #header>{{yellowParams.title}}</template>
          <div>{{yellowParams.desc}}</div>
          <div>{{yellowParams.info}}</div>
          <div>{{yellowParams.detail}}</div>
          <div>{{yellowParams.file}}</div>
          <div>{{item.i}}</div>
        </card-page>
      </GridItem>
    </GridLayout>
  </div>
</template>

<style lang="scss" scoped>
// 整个画布的背景色
.vgl-layout {
  background-color: #eee;
  // 拖拽时,画布的背景色
  --vgl-placeholder-bg: white;
}
// 每个单个画布的背景色
:deep(.vgl-item:not(.vgl-item--placeholder)) {
  background-color: #fff;
  //border: 1px solid black;
}

:deep(.vgl-item--resizing) {
  opacity: 90%;
}

:deep(.vgl-item--static) {
  background-color: #cce;
}

.text {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: auto;
  font-size: 24px;
  text-align: center;
}

.layout-json {
  padding: 10px;
  margin-top: 10px;
  background-color: #ddd;
  border: 1px solid black;
}

.columns {
  columns: 120px;
}
</style>

card文件

<template>
    <el-card class="box-card">
      <template #header>
        <div class="card-header">
          <span><slot name="header"></slot></span>
        </div>
      </template>
      <div v-if="$slots.default">
        <slot/>
      </div>
      <div v-else>111</div>
    </el-card>
</template>

<style lang="scss" scoped>
.box-card{
  width: 100%;
  :deep(.el-space__item){
    width: 100% !important;
  }
  :deep(.el-space, .el-space--horizontal ){
    width: 100% !important;
    background-color: pink !important;
  }
}
</style>

效果图:

https://i-blog.csdnimg.cn/direct/4d415565f0064db891bdd0c6361d6cbd.gif