上传后如何在cropper中加载占位符

How to load placeholder in cropper after upload

我正在使用来自 cropperjs 的 vue-cropper 4.1 和 Nuxt 2.13。当我的裁剪器加载时它显示一个占位符,在选择图像并上传后,裁剪器仍然显示所选图像,但我想再次加载占位符。我应该怎么办?我试过 replace() 但没有用。这是我的代码:

注意:我的代码中有一些 this 您可能找不到参考。它们通过 mixins 全局添加到我的项目中。 editProduct 是我的 vuex 中的一个状态,它在全局添加时与其动作相同。

<template>
<div class="pt-6">
    <v-row class="ma-0">
        <v-col cols="12" sm="12" md="5" class="pa-0" >
            <v-card flat class="pb-4" max-width="450px">
                <client-only>
                    <vue-cropper
                    ref="cropper"
                    :aspect-ratio="1/1"
                    :src="url"
                    dragMode="move"
                    />
                </client-only>
                <v-card flat class="d-flex justify-center theme__main__color my-2">
                    <v-btn text icon class="mx-1 text_main_color" @click.prevent="rotate(-45)">
                        <v-icon>mdi-rotate-left</v-icon>
                    </v-btn>
                    <v-btn text icon class="mx-1 text_main_color" ref="flipX" @click.prevent="flipX">
                        <v-icon>mdi-flip-horizontal</v-icon>
                    </v-btn>
                    <v-btn text icon class="mx-1 text_main_color" @click.prevent="reset">
                        <v-icon>mdi-border-all-variant</v-icon>
                    </v-btn>
                    <v-btn text icon class="mx-1 text_main_color" ref="flipY" @click.prevent="flipY">
                        <v-icon>mdi-flip-vertical</v-icon>
                    </v-btn>
                    <v-btn text icon class="mx-1 text_main_color" @click.prevent="rotate(45)">
                        <v-icon>mdi-rotate-right</v-icon>
                    </v-btn>
                    <a style="display:none;" ref="flipX" href="#" @click.prevent="flipX"></a>
                    <a style="display:none;" ref="flipY" href="#" @click.prevent="flipY"></a>
                </v-card>
                <v-list container inline class="transparent text-center pa-0">
                    <v-list-item class="px-0">
                        <v-list-item class="px-1">
                            <v-btn
                            width="100%"
                            class="theme__btn__w text_main_color"
                            :loading="isSelecting"
                            @click="onChooseClick"
                            >{{lang.chooseimage}}</v-btn>
                        </v-list-item>
                        <v-list-item class="px-1">
                            <v-btn
                            width="100%"
                            class="theme__btn__s text_main_color"
                            @click.prevent="uploadImage"
                            :disabled="isUploading || !selectedFile"
                            >{{lang.upload}}</v-btn>
                            <input
                            ref="uploader"
                            class="d-none"
                            type="file"
                            accept="image/*"
                            @change="onFileChanged"
                            >
                        </v-list-item>
                    </v-list-item>
                </v-list>
            </v-card>
        </v-col>

        <v-col cols="12" sm="12" md="7" class="pa-0" v-if="storedImg">
            <v-row class="ma-0">
                <!-- img 1 -->
                <v-col cols="12" sm="6" md="4" class="pa-1 mb-4" v-for="(image, index) in storedImg" :key="index">
                    <v-card flat>
                        <div tile class="rounded-r felx float-right text_main_color theme__btn__p pa-1" height="100%">
                            <div><v-icon class="text_main_color" medium>mdi-sort-variant</v-icon></div>
                            <div><span class="my-1 productlist__btn__sort">Hold to order</span></div>
                        </div>
                        <v-img class="theme__main__color rounded-l" max-width="141" :src="imagePathCreator(image.url)"></v-img>
                        <v-btn text class="theme__cta__color text_main_color mt-2" min-width="141" @click.prevent="deleteImg(index, image.id)">{{lang.delete}}</v-btn>   
                    </v-card>
                </v-col>
            </v-row>
        </v-col>
        
        <!-- btn -->
        <addproductbtn :section="section" />
    </v-row>
</div>
</template>
<style>

</style>
<script>
import addproductbtn from '~/components/global/cms/addproductbtn'
import {ALERT_TYPE, ALERT_METHOD} from '~/plugins/constants.js'
import VueCropper from 'vue-cropperjs';
import 'cropperjs/dist/cropper.css';


export default {
    components:{
        VueCropper,
        'addproductbtn': addproductbtn
    },
    props:['err'],
    data(){
        return{
            isSelecting: false,
            selectedFile: null,
            url: null,
            placeholder: '/images/placeholder/place-800.png',
            section: 'img',
            pImages: [],
            pId: null,
            cropData: null,
            isUploading: false
        }
    },
    computed:{
        storedImg(){
            return this.editProduct.images
        }
    },
    methods:{

        // ***** Cropper Methods ***** \
        flipX() {
            const dom = this.$refs.flipX;
            let scale = dom.getAttribute('data-scale');
            scale = scale ? -scale : -1;
            this.$refs.cropper.scaleX(scale);
            dom.setAttribute('data-scale', scale);
        },
        flipY() {
            const dom = this.$refs.flipY;
            let scale = dom.getAttribute('data-scale');
            scale = scale ? -scale : -1;
            this.$refs.cropper.scaleY(scale);
            dom.setAttribute('data-scale', scale);
        },
        reset() {
            this.$refs.cropper.reset();
            },
        rotate(deg) {
            this.$refs.cropper.rotate(deg);
        },
        getData(){
            this.cropData = this.$refs.cropper.getData(true);
        },

        // ***** Component Methods ***** \ 
        onChooseClick(){
            this.isSelecting = true
            window.addEventListener('focus', () => {
                this.isSelecting = false
            }, { once: true })

            this.$refs.uploader.click()
        },
        onFileChanged(e) {
            // **** CROPPER CHOOSE FILE **** //
            this.selectedFile = e.target.files[0];
            if (this.selectedFile.type.indexOf('image/') === -1) {
                this.noty(ALERT_TYPE[0],lang.errimagefile);
                return;
            }
            if (typeof FileReader === 'function') {
                const reader = new FileReader();
                reader.onload = (event) => {
                    this.url = event.target.result;
                    // rebuild cropperjs with the updated source
                    this.$refs.cropper.replace(event.target.result);
                };
                reader.readAsDataURL(this.selectedFile);
            } else {
                this.noty(ALERT_TYPE[0],lang.something_wrong);
            }

        },
        async uploadImage(){
            this.getData()
            
            if(this.notEmpty(this.selectedFile) && this.notEmpty(this.pId) && 
            this.notEmpty(this.cropData)){
                this.isUploading = true
                const data = new FormData()
                data.append('image', this.selectedFile)
                data.append('pId', this.pId)
                data.append('w', this.cropData.width)
                data.append('h', this.cropData.height)
                data.append('x', this.cropData.x)
                data.append('y', this.cropData.y)
                data.append('r', this.cropData.rotate)
                data.append('sx', this.cropData.scaleX)
                data.append('sy', this.cropData.scaleY)
                let response = await this.axiosPost('product/createproimg', data)
                if(this.resOk(response.status)){
                    const newImages = {"id": response.data.id,"url": response.data.url}
                    this.setEditProductImg(newImages)
                    this.selectedFile = null
                }
                
            }
            this.isUploading = false
        },
    },
    created(){
        this.url = this.placeholder
    }
}
</script>

在 Cropperjs 上搜索和阅读文档后,我终于找到了解决方案。 图片上传后,我使用 distroy() 然后 replace() 像这样:

async uploadImage(){
    this.getData()
    
    if(this.notEmpty(this.selectedFile) && this.notEmpty(this.editProduct.pId) && this.notEmpty(this.cropData)){
        // this.isUploading = true
        const data = new FormData()
        data.append('image', this.selectedFile)
        data.append('pId', this.editProduct.pId)
        data.append('w', this.cropData.width)
        data.append('h', this.cropData.height)
        data.append('x', this.cropData.x)
        data.append('y', this.cropData.y)
        data.append('r', this.cropData.rotate)
        data.append('sx', this.cropData.scaleX)
        data.append('sy', this.cropData.scaleY)
        let response = await this.axiosPost('product/createproimg', data)
        if(this.resOk(response.status)){
            const newImages = {"id": response.data.id,"url": response.data.url}
            this.setEditProductImg(newImages)
            this.myArray.unshift(newImages)
            this.originalArray.push(newImages)
            this.selectedFile = null

//*********** THIS PART REPLACE CROPPER WHITH MY PLACEHOLDER IMAGE **************\

            this.$refs.cropper.destroy()
            this.$refs.cropper.replace(this.placeholder)

//**** this.placeholder is the placeholder.jpg file path in my static folder ****\

            this.isUploading = false
        }else{
            this.isUploading = false
        }
    }
    this.isUploading = false
},