ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • VUE + Quill.js 에디터 #Editor #에디터 #vue
    프론트엔드/Nuxt 2023. 6. 14. 16:47
    반응형
    SMALL

    1. cdn 연결하기

    * nuxt ssr일 경우 nuxt.config.js가 아니라 default.vue 레이아웃에다 직접 링크 넣어야됨

    (다른 레이아웃 파일 사용 시 그 파일에다 삽입)

     

    @ default.vue

    <template>
        <div id="app">
            <header-vue />
    
            <pop />
    
            <section class="flex">
                <aside class="left-side"></aside>
    
                <transition name="page" mode="out-in">
                    <Nuxt />
                </transition>
            </section>
    
            <footer-vue />
        </div>
    
    </template>
    
    <script>
    
    export default {
        head() {
            return {
                link: [
                    {rel: 'stylesheet', type: 'text/css', href: '//cdn.quilljs.com/1.3.4/quill.snow.css'},
         
                ],
    
                script: [
                    {
                        src: '//cdn.quilljs.com/1.3.6/quill.min.js',
                        defer: true
                    },
                ],
            }
        },
    }
    </script>

     

    2. 프론트 세팅

    @ InputEditor.js

    <template>
        <div class="ql-editor">
            <div id="editor" ref="editor" style="height:400px;"></div>
            <input type="file" id="getFile" accept="image/*" style="position: absolute; z-index:-1; opacity:0; left:-1000px; bottom:-1000px;" @change="changeImg">
        </div>
    
    </template>
    <script>
    
    export default {
        components: {},
    
        props: {
            default: "",
    
            required: {
                default: true
            },
        },
    
        data() {
            return {
                value: this.default,
                editor: "",
            }
        },
    
        methods: {
            changeImg(event){
                let formData = new FormData();
    
                formData.append("file", event.target.files[0]);
    
                this.$axios.post("/api/images", formData)
                    .then(response => {
                        let url = response.data.data;
    
                        const range = this.editor.getSelection();
    
                       this.editor.insertEmbed(range.index, 'image', url);
    
                        /*this.editor.root.innerHTML += `<img src="${response.data.data}" alt=""/>`
                        return response.data;*/
                    });
            },
    
            changeContents() {
                this.$emit("change", this.editor.root.innerHTML);
            },
    
            imageHandler() {
                document.getElementById('getFile').click();
            }
        },
    
        mounted() {
            let self = this;
            const toolbarOptions = [
                ['bold', 'italic', 'underline', 'strike'],
                ['blockquote', 'code-block'],
                [{'header': 1}, {'header': 2}],
                [{'list': 'ordered'}, {'list': 'bullet'}],
                [{'script': 'sub'}, {'script': 'super'}],
                [{'indent': '-1'}, {'indent': '+1'}],
                [{'direction': 'rtl'}],
                [{'size': ['small', false, 'large', 'huge']}],
                [{'header': [1, 2, 3, 4, 5, 6, false]}],
                [{'color': []}, {'background': []}],
                [{'font': []}],
                [{'align': []}],
                ['clean', 'image']
            ]
    
            this.editor = new Quill(this.$refs.editor, {
                modules: {
                    toolbar: {
                        container: toolbarOptions,
                        handlers: {
                            'image' : self.imageHandler
                        }
                    }
                },
                theme: 'snow',
            })
    
            this.editor.on("text-change", () => {
                this.changeContents();
            })
    
            this.editor.pasteHTML(this.default);
    
            // this.$refs.editor.quill.setContents(this.default);
            // this.$store.commit('setQuillInstance', quill)
        },
    
        watch: {
            value: function (value, oldValue) {
                this.$emit("change", value);
            }
        }
    }
    </script>

    @ example.vue

    <template>
        <input-editor :default="form.description" @change="(data) => {form.description = data}"/>
    </template>
    <script>
    
    import Form from "@/utils/Form";
    export default {
        data(){
            return {
                form: new Form(this.$axios, {
                    description: "",
                })
            }
        },
    }
    </script>

     

    3. 백엔드 세팅

    @ Image.php (migration도 만들되, 컬럼은 id만 있으면 됨)

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use Spatie\MediaLibrary\HasMedia;
    use Spatie\MediaLibrary\InteractsWithMedia;
    
    class Image extends Model implements HasMedia
    {
        use HasFactory, InteractsWithMedia;
    
        protected $appends = [
            "file",
        ];
    
        public function registerMediaCollections():void
        {
            $this->addMediaCollection('file')->singleFile();
        }
    
        public function getFileAttribute()
        {
            if($this->hasMedia('file')) {
                $media = $this->getMedia('file')[0];
    
                return [
                    "name" => $media->file_name,
                    "url" => $media->getFullUrl()
                ];
            }
    
            return null;
        }
    }

    @ api.php

        Route::post("/images", [\App\Http\Controllers\Api\ImageController::class, "store"]);

    @ Api/ImageController.php 

    <?php
    
    namespace App\Http\Controllers\Api;
    
    use App\Http\Controllers\Controller;
    use App\Models\Image;
    use Illuminate\Http\Request;
    
    class ImageController extends ApiController
    {
        public function store(Request $request)
        {
            $request->validate([
                "file" => "required"
            ]);
    
            $image = Image::create();
    
            $image->addMedia($request->file)->toMediaCollection("file", "s3");
    
            $media = $image->getMedia('file')[0];
    
            return $this->respondSuccessfully($media->getFullUrl());
        }
    }
    LIST

    댓글

Designed by Tistory.