Vueでファイルのドラッグ&ドロップ

https://trueman-developer.blogspot.com/2018/01/html-css-javascript.html をVue向けに。
TypeScriptと単一ファイルコンポーネントを使用しているので適宜読み替え。







index.html




<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>template</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
  <script defer src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script defer src="./built/main.js"></script>
</head>

<body>
  <div id="app" class="hidden">
    <dragfile-component></dragfile-component>
  </div>
</body>

</html>



main.ts



コンポーネントを呼び出すのみ。

import Vue from "vue"
import dragfileComponent from "./dragfile.vue"

const v = new Vue({
  el: "#app",
  components: {
    dragfileComponent,
  },
  data() {
    return {
    }
  },
  methods: {
    handleTemplate() {
      //
    },
  },
})

// 画面初期化までは非表示 IE11対策
v.$nextTick(() => {
  const app = document.getElementById("app")
  if (app) {
    app.classList.remove("hidden")
  }
})



main.ts



単一ファイルコンポーネントを使用している。
重要部分はここ。

<template>
  <div>
    <div class="draggable" v-bind:class="{ dragover: isDragOver }"
      @dragleave.prevent="onDragLeave"
      @dragover.prevent="onDragOver"
      @drop.prevent="onDrop">
      <p>Drag & Drop</p>
    </div>
    <ul>
      <li v-for="fileName in fileNameList" v-bind:key="fileName">
        {{ fileName }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import Vue from "vue"

export default Vue.extend({
  props: {
    //
  },
  data() {
    return {
      isDragOver: false,
      fileNameList: [] as string[],
    }
  },
  /** Vue構築完了時の処理 */
  created: function init() {
    this.msg = "Hello Vue"
  },
  methods: {
    // ドラッグ範囲に入ったときの挙動 classを変えている
    onDragOver(event: any) {
      this.isDragOver = true
      // console.log(event)
    },

    // ファイルがドロップされたときの挙動 ここでファイルを操作する
    onDrop(event: any) {
      this.isDragOver = false
      console.log(event)
      const files = event.dataTransfer.files as FileList
      // tslint:disable-next-line:prefer-for-of
      for (let i = 0; i < files.length; i++) {
        console.log(files[i])
        this.fileNameList.push(files[i].name)
      }
    },

    // ドラッグ範囲外に出たときの挙動 classを変えている
    onDragLeave(event: any) {
      this.isDragOver = false
      // console.log(event)
    },
  },
})
</script>


@dragleave, @dragover,@drop がそれぞれのイベント。
.preventを指定すると既存のイベントが無効になる。
呼び出された関数内で操作できないこともない。



main.css



マウスオーバーしたときに背景色を変えている。

.draggable {
  width: 30%;
  height: 30%;
  top: 0;
  left: 0;
  right: 0;
  margin: auto;
  text-align: center;
  bottom: 0;
  position: fixed;
  border: black dotted 1px;
}

.dragover {
  background-color: aquamarine;
}




webpack.config.js



VueだけでなくTypeScriptも使用している。

var path = require('path')
var webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  entry: {
    index: './16_Vue/1699_DragFile/main.ts',
  },
  output: {
    path: path.resolve(__dirname, './built'),
    publicPath: '/built/',
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve("src"),
        use: [
          {
            loader: "thread-loader",
            options: {
              workers: require('os').cpus().length - 1,
            }
          }
          // your expensive loader (e.g babel-loader)
        ]
      },
      {
        enforce: 'pre',
        test: /\.ts$/,
        loader: 'tslint-loader',
        exclude: /(node_modules)/,
        options: {
          configFile: 'tslint.json'
        }
      },
      {
        test: /\.ts$/,
        exclude: /node_modules|vue\/src/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/],
          transpileOnly: true
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            'ts': 'ts-loader!tslint-loader',
          },
          // extractCSS: plugin
          // other vue-loader options go here
        }
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true
  },
  externals: {
    "vue": "Vue",
    'element-ui': 'ElementUI'
  },
  cache: true,
  performance: {
    hints: false
  },
  devtool: 'source-map',
  plugins: [
    // make sure to include the plugin for the magic
    new VueLoaderPlugin()
  ],


}


完成イメージ



上記例だと以下のような感じ。
ドロップされたファイルがリスト表示される。




実際に組み込むなら受け取ったファイルをコンポーネントの呼び出し元で受け取れるようにするとなおよし。

2018年8月7日火曜日