LoginSignup
1
1

More than 1 year has passed since last update.

Vue Grid Layoutで座席表を作ってみた

Last updated at Posted at 2023-04-09

こんにちはWebCrewバッ苦エンドエンジニア1年目の久保田です。
今日は社内の座席方をいろいろあって作ることになり、その際に使ったVue Grid Layoutが面白かったので記事に残してみます。

Vue Grid Layoutとは

まずVue Grid Layoutとはなんぞやって話ですが、とりあえずgridで組んだオブジェクトを自由にドラッグ&ドロップで動かしたらサイズを変えたりできるVueのライブラリです。便利ですね。
物は試しなので、とりあえずリンク先でいろいろ動かしてみてください。

サイズを変えたりできるのはもちろんですが、staticにして一部だけ不可変に設定することもできます。

実装方法

使い方も簡単で、VueのCLI立ち上げて下のコードpackage.jsonに記載するだけです。

package.json
    "vue-grid-layout": "^2.4.0"

これで各コンポーネントで使えるようになります。
今回は簡単な座席表っぽいものを作ってみます。
コンポーネントはなんでもいいですが、お試しなのでHelloWordl.vueにそのまま書いていきます。

基本的な書き方は以下の通りです。

HelloWorld.vue
<template>
  <div class="hello">
    <grid-layout :layout.sync="layout"
                :col-num="24"
                :row-height="48"
                :is-draggable="draggable"
                :is-resizable="resizable"
                :vertical-compact="true"
                :use-css-transforms="true"
    >
        <grid-item v-for="item in layout"
                  :key="item"
                  :static="item.static"
                  :x="item.x"
                  :y="item.y"
                  :w="item.w"
                  :h="item.h"
                  :i="item.i"
        >
            <span class="text">{{itemTitle(item)}}</span>
        </grid-item>
    </grid-layout>
  </div>
</template>

<script>
import { GridLayout, GridItem } from "vue-grid-layout"


export default {
  components: {
      GridLayout,
      GridItem
  },
  data() {
      return {
          layout: [
              {"x":1,"y":1,"w":1,"h":1,"i":"0", static: false}
,
          draggable: true,
          resizable: true,
          index: 0
      }
  },
  methods: {
      itemTitle(item) {
          let result = item.i;
          if (item.static) {
              result += " - Static";
          }
          return result;
      }
  }
}
</script>

<style scoped>
html {
  margin: 0;
  padding: 0;
}
.hello {
  height: 100vh;
}

.vue-grid-layout {
  background: #eee;
  height: 100;
}

.vue-grid-item:not(.vue-grid-placeholder) {
  background: #ccc;
  border: 1px solid black;
}

.vue-grid-item .resizing {
  opacity: 0.9;
}

.vue-grid-item .static {
  background: #cce;
}

.vue-grid-item .text {
  font-size: 24px;
  text-align: center;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  height: 100%;
  width: 100%;
}

.vue-grid-item .no-drag {
  height: 100%;
  width: 100%;
}

.vue-grid-item .minMax {
  font-size: 12px;
}

.vue-grid-item .add {
  cursor: pointer;
}

.vue-draggable-handle {
  position: absolute;
  width: 20px;
  height: 20px;
  top: 0;
  left: 0;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
  background-position: bottom right;
  padding: 0 8px 8px 0;
  background-repeat: no-repeat;
  background-origin: content-box;
  box-sizing: border-box;
  cursor: pointer;
}
</style>

これだけでとりあえずオブジェクトを一つ作る事ができます。
dataの中身を増やしていくことでオブジェクトの数を増やす事が可能です。
便利な点としては、ハッシュでx,yの位置やw.hといったサイズを書くだけで簡単に実装できる点です。また、staticやdeaggableも個別に付け加えることで実装できるのでv-for内で完結します。
便利ですね。

主なプロパティについても記載しておきます。

プロパティ 効果
x int x軸の位置
y int y軸の位置
w int 横幅
h int 縦幅
static boolean 他のオブジェクトの変更も含めて動かせるかどうか
draggable boolean ドラッグできるか否か
resizable boolean サイズ変更できるか否か

だいたいこんな感じです、めちゃくちゃ簡単です。

最後に簡単な座席表作って終わります。

Vue.js:HelloWorld.vue
<template>
  <div class="hello">
    <grid-layout :layout.sync="layout"
                :col-num="24"
                :row-height="48"
                :is-draggable="draggable"
                :is-resizable="resizable"
                :vertical-compact="true"
                :use-css-transforms="true"
    >
        <grid-item v-for="item in layout"
                  :key="item"
                  :static="item.static"
                  :x="item.x"
                  :y="item.y"
                  :w="item.w"
                  :h="item.h"
                  :i="item.i"
        >
            <span class="text">{{itemTitle(item)}}</span>
        </grid-item>
    </grid-layout>
  </div>
</template>

<script>
import { GridLayout, GridItem } from "vue-grid-layout"


export default {
  components: {
      GridLayout,
      GridItem
  },
  data() {
      return {
          layout: [
              {"x":1,"y":1,"w":1,"h":1,"i":"0", static: false},
              {"x":8,"y":1,"w":1,"h":1,"i":"1", static: false},
              {"x":12,"y":1,"w":1,"h":1,"i":"2", static: false},
              {"x":16,"y":1,"w":1,"h":1,"i":"3", static: false},
              {"x":19,"y":1,"w":1,"h":1,"i":"4", static: false},
              {"x":20,"y":1,"w":1,"h":1,"i":"5", static: false},
              {"x":19,"y":1,"w":1,"h":1,"i":"6", static: false},
              {"x":20,"y":1,"w":1,"h":1,"i":"7", static: false},
              {"x":1,"y":2,"w":23,"h":1,"i":"99", static: true},
              {"x":1.5,"y":4,"w":1,"h":1,"i":"8", static: false},
              {"x":1,"y":5,"w":1,"h":1,"i":"9", static: false},
              {"x":1,"y":6,"w":1,"h":1,"i":"10", static: false},
              {"x":1,"y":7,"w":1,"h":1,"i":"11", static: false},
              {"x":1,"y":7,"w":1,"h":1,"i":"12", static: false},
              {"x":2,"y":5,"w":1,"h":1,"i":"13", static: false},
              {"x":2,"y":6,"w":1,"h":1,"i":"14", static: false},
              {"x":2,"y":7,"w":1,"h":1,"i":"15", static: false},
              {"x":2,"y":7,"w":1,"h":1,"i":"16", static: false},
              {"x":4,"y":4,"w":1,"h":1,"i":"17", static: false},
              {"x":4,"y":4,"w":1,"h":1,"i":"18", static: false},
              {"x":4,"y":5,"w":1,"h":1,"i":"19", static: false},
              {"x":4,"y":6,"w":1,"h":1,"i":"20", static: false},
              {"x":4,"y":7,"w":1,"h":1,"i":"21", static: false},
              {"x":5,"y":7,"w":1,"h":1,"i":"22", static: false},
              {"x":5,"y":5,"w":1,"h":1,"i":"23", static: false},
              {"x":5,"y":6,"w":1,"h":1,"i":"24", static: false},
              {"x":5,"y":7,"w":1,"h":1,"i":"25", static: false},
              {"x":5,"y":7,"w":1,"h":1,"i":"26", static: false},
              {"x":7,"y":3,"w":1,"h":1,"i":"27", static: false},
              {"x":7,"y":4,"w":1,"h":1,"i":"28", static: false},
              {"x":7,"y":5,"w":1,"h":1,"i":"29", static: false},
              {"x":7,"y":6,"w":1,"h":1,"i":"30", static: false},
              {"x":7,"y":7,"w":1,"h":1,"i":"31", static: false},
              {"x":8,"y":3,"w":1,"h":1,"i":"32", static: false},
              {"x":8,"y":4,"w":1,"h":1,"i":"33", static: false},
              {"x":8,"y":5,"w":1,"h":1,"i":"34", static: false},
              {"x":8,"y":6,"w":1,"h":1,"i":"35", static: false},
              {"x":8,"y":7,"w":1,"h":1,"i":"36", static: false},
              {"x":10,"y":3,"w":1,"h":1,"i":"37", static: false},
              {"x":10,"y":4,"w":1,"h":1,"i":"38", static: false},
              {"x":10,"y":5,"w":1,"h":1,"i":"39", static: false},
              {"x":10,"y":6,"w":1,"h":1,"i":"40", static: false},
              {"x":10,"y":7,"w":1,"h":1,"i":"41", static: false},
              {"x":11,"y":3,"w":1,"h":1,"i":"42", static: false},
              {"x":11,"y":4,"w":1,"h":1,"i":"43", static: false},
              {"x":11,"y":5,"w":1,"h":1,"i":"44", static: false},
              {"x":11,"y":6,"w":1,"h":1,"i":"45", static: false},
              {"x":11,"y":7,"w":1,"h":1,"i":"46", static: false},
              {"x":13,"y":3,"w":1,"h":1,"i":"47", static: false},
              {"x":13,"y":4,"w":1,"h":1,"i":"48", static: false},
              {"x":13,"y":5,"w":1,"h":1,"i":"49", static: false},
              {"x":13,"y":6,"w":1,"h":1,"i":"50", static: false},
              {"x":13,"y":7,"w":1,"h":1,"i":"51", static: false},
              {"x":14,"y":3,"w":1,"h":1,"i":"52", static: false},
              {"x":14,"y":4,"w":1,"h":1,"i":"53", static: false},
              {"x":14,"y":5,"w":1,"h":1,"i":"54", static: false},
              {"x":14,"y":6,"w":1,"h":1,"i":"55", static: false},
              {"x":14,"y":7,"w":1,"h":1,"i":"56", static: false},
              {"x":16,"y":3,"w":1,"h":1,"i":"57", static: false},
              {"x":16,"y":4,"w":1,"h":1,"i":"58", static: false},
              {"x":16,"y":5,"w":1,"h":1,"i":"59", static: false},
              {"x":16,"y":6,"w":1,"h":1,"i":"60", static: false},
              {"x":16,"y":7,"w":1,"h":1,"i":"61", static: false},
              {"x":17,"y":3,"w":1,"h":1,"i":"62", static: false},
              {"x":17,"y":4,"w":1,"h":1,"i":"63", static: false},
              {"x":17,"y":5,"w":1,"h":1,"i":"64", static: false},
              {"x":17,"y":6,"w":1,"h":1,"i":"65", static: false},
              {"x":17,"y":7,"w":1,"h":1,"i":"66", static: false},
              {"x":19,"y":3,"w":1,"h":1,"i":"67", static: false},
              {"x":19,"y":4,"w":1,"h":1,"i":"68", static: false},
              {"x":19,"y":5,"w":1,"h":1,"i":"69", static: false},
              {"x":19,"y":6,"w":1,"h":1,"i":"70", static: false},
              {"x":19,"y":7,"w":1,"h":1,"i":"71", static: false},
              {"x":20,"y":3,"w":1,"h":1,"i":"72", static: false},
              {"x":20,"y":4,"w":1,"h":1,"i":"73", static: false},
              {"x":20,"y":5,"w":1,"h":1,"i":"74", static: false},
              {"x":20,"y":6,"w":1,"h":1,"i":"75", static: false},
              {"x":20,"y":7,"w":1,"h":1,"i":"76", static: false},
              {"x":22,"y":3,"w":1,"h":1,"i":"77", static: false},
              {"x":22,"y":4,"w":1,"h":1,"i":"78", static: false},
              {"x":22,"y":5,"w":1,"h":1,"i":"79", static: false},
              {"x":22,"y":6,"w":1,"h":1,"i":"80", static: false},
              {"x":22,"y":7,"w":1,"h":1,"i":"81", static: false},
              {"x":23,"y":3,"w":1,"h":1,"i":"82", static: false},
              {"x":23,"y":4,"w":1,"h":1,"i":"83", static: false},
              {"x":23,"y":5,"w":1,"h":1,"i":"84", static: false},
              {"x":23,"y":6,"w":1,"h":1,"i":"85", static: false},
              {"x":23,"y":7,"w":1,"h":1,"i":"86", static: false},
              {"x":1,"y":8,"w":23,"h":1,"i":"100", 

          ],
          draggable: true,
          resizable: true,
          index: 0
      }
  },
  methods: {
      itemTitle(item) {
          let result = item.i;
          if (item.static) {
              result += " - Static";
          }
          return result;
      }
  }
}
</script>

<style scoped>
html {
  margin: 0;
  padding: 0;
}
.hello {
  height: 100vh;
}

.vue-grid-layout {
  background: #eee;
  height: 100;
}

.vue-grid-item:not(.vue-grid-placeholder) {
  background: #ccc;
  border: 1px solid black;
}

.vue-grid-item .resizing {
  opacity: 0.9;
}

.vue-grid-item .static {
  background: #cce;
}

.vue-grid-item .text {
  font-size: 24px;
  text-align: center;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  height: 100%;
  width: 100%;
}

.vue-grid-item .no-drag {
  height: 100%;
  width: 100%;
}

.vue-grid-item .minMax {
  font-size: 12px;
}

.vue-grid-item .add {
  cursor: pointer;
}

.vue-draggable-handle {
  position: absolute;
  width: 20px;
  height: 20px;
  top: 0;
  left: 0;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
  background-position: bottom right;
  padding: 0 8px 8px 0;
  background-repeat: no-repeat;
  background-origin: content-box;
  box-sizing: border-box;
  cursor: pointer;
}


</style>

以上です、Vueには他にもドラッグ&ドロップできるライブラリいくつかあるみたいなので試してみたいと思います。
ありがとうございました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1