1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VueQuillはVue3用のリッチテキストエディタを簡単に実装できるコンポーネントです。

さらに quill-image-uploaderを使用することで、画像を Base64ではなくサーバーにアップロードできます。
このガイドでは、以下の内容を説明します

  • VueQuill と Quill-image-uploader のセットアップ
  • Laravel を用いた画像アップロード機能の構築
  • Vue コンポーネントの構成例
  • Laravel コントローラーの処理例

1. 必要なパッケージのインストール

以下のパッケージをインストールします。

terminal
npm install @vueup/vue-quill@latest --save
npm install quill-image-uploader --save

2. VueQuill のセットアップ

QuillEditor.vue
<template>
<div>
    <div>
    <QuillEditor
        v-model:content="localContent"
        :options="editorOptions"
        contentType="html"
        @update:content="handleContentChange"
    />
    </div>
</div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'

import Quill from 'quill'
import { QuillEditor } from '@vueup/vue-quill'
import QuillImageUploader from 'Quill-image-uploader'

import axios from 'axios';

Quill.register('modules/imageUploader', QuillImageUploader)

import '@vueup/vue-quill/dist/vue-quill.snow.css';

const props = defineProps({
    initialContent: {
        type: String,
        default: ''
    },
    content: {
        type: String,
        default: ''
    },
    uploadUrl: {
        type: String,
        default: '/hogehoge'
  }
})
const emit = defineEmits([
    'update:content',
    'image-uploaded'
])
const localContent = ref(props.initialContent)


const editorOptions = {
    theme: 'snow',
    modules: {
     toolbar: {
		  //ツールバーカスタム可能
	      container: [['bold', 'italic', 'underline'], ['image']],
	    }
	    //画像をstorageに保存処理                                                                                                 
        imageUploader: {
            upload: async (file) => {
                try {
                    const formData = new FormData();
                    formData.append('image', file);
                    const response = await axios.post(props.uploadUrl, formData, {
                    });
                    if (response.data && response.data.filePath) {
                        emit('image-uploaded', {
                            fileName: file.name,
                            filePath: response.data.filePath
                        });
                        return response.data.filePath;
                    }
                } catch (error) {
                    console.error('画像アップロードエラー:', error);
                    throw error;
                }
            }
        }
    }
}

// コンテンツ更新時のハンドラー
const handleContentChange = (newContent) => {
    emit('update:content', newContent)
}

watch(() => props.content, (newContent) => {
    if (newContent !== localContent.value) {
        localContent.value = newContent
    }
})

onMounted(() => {
   // 初期値がある場合は設定
  if (props.initialContent) {
    localContent.value = props.initialContent
  }
})
</script>

ツールバーのカスタム参考サイト

3. エディターをフォームに統合

Form.vue
<template>
    <div>
        <QuillEditor 
          v-model:content="editorContent" 
          :initial-content="initialContent"
          @update:content="handleContentUpdate" 
          @image-uploaded="handleImageUpload"/>

    <div>
        <v-btn @click.prevent="handleCancel">
            キャンセル
        </v-btn>

        <v-btn. click.prevent="onClick">
            作成
        </v-btn>
    </div>
			</template>

<script setup lang="ts">
import { ref, computed, PropType } from 'vue'

import { router } from '@inertiajs/vue3';

import QuillEditor from '@/Components/QuillEditor.vue'

import '@vueup/vue-quill/dist/vue-quill.snow.css';
import 'quill/dist/quill.snow.css';

const props = defineProps({
    contents: {
        type: String as PropType<string | null>,
        default: ''
    }
})
// 初期値を計算する
const initialContent = computed(() => {
    return props.contents ?? ''
})

const editorContent = ref<string | null>(props.contents ?? null)
const uploadedImages = ref<Array<{ fileName: string, filePath: string }>>([])

// キャンセルボタンを押した時の処理
const handleCancel = () => {
    editorContent.value = props.contents ?? ''
}

const handleImageUpload = (imageData: { fileName: string, filePath: string }) => {
    uploadedImages.value.push(imageData)
}
const handleContentUpdate = (newContent) => {
    editorContent.value = newContent
}
const onClick = () => {
    router.post(route('post先URL'), {
        content: editorContent.value ?? '',
    })
}
</script>

4. Laravel サイドの処理

EditorController
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class EditorController extends Controller
{
    public function edit()
    {
     $contents = EditorContent::first();
        return Inertia::render('Editor', ['contents' => $contents->content]);
    }

    public function uploadImage(Request $request)
    {
        $request->validate(['image' => 'required|image|mimes:jpeg,png,jpg,gif']);

        if ($request->hasFile('image')) {
            $file = $request->file('image');
            $fileName = uniqid() . '.' . $file->getClientOriginalExtension();
            $path = $file->storeAs('images/hoge', $fileName, 'public');
						
			//ファイル名をvueにreturn
            return response()->json(['filePath' => Storage::url($path)]);
        }

        return response()->json(['error' => 'アップロードに失敗しました'], 400);
    }

    public function create(Request $request)
    {
		//updateOrCreate
        EditorContent::updateOrCreate(['key' => hoge], ['content' => $request->content]);
        return redirect()->route('editor.edit');
    }
}

imageUploaderはプレーンなjsonを受け取りファイル名をbass64から受け取ったファイル名に上書きするみたいなのでaxiosを使用。
(勉強中のため他に良い方法があるかも)

5. エディターコンテンツを表示

index.vue
<template>
  <div class="ql-editor" v-html="content"></div>
</template>

<script setup>
    import '@vueup/vue-quill/dist/vue-quill.snow.css';
</script>

6.終わり

VueQuillは簡単に導入できるが、画像がbass64にエンコードされDBが見づらくなるので解決するために、quill-image-uploaderを使用してみました。
画像は挿入するだけで、配置やサイズの変更は出来ないのでモジュール追加が必要です。
参考になれば幸いです。

1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?