Vuejs & Vuetify - 在对话框中使用 Tinymce 作为组件需要重新加载

Posted

技术标签:

【中文标题】Vuejs & Vuetify - 在对话框中使用 Tinymce 作为组件需要重新加载【英文标题】:Vuejs & Vuetify - Using Tinymce as a component in a dialog requires reloading 【发布时间】:2021-08-05 08:08:30 【问题描述】:

我正在使用 tinymce 作为组件来创建和编辑文章

https://www.tiny.cloud/docs/integrations/vue/

我也在使用 vuetify 和组件 v-dialog,我所有的表单都在这个模式中

但是,每次我更改 tinymce 组件的实例时,我都必须通过更改 key 属性来重新加载组件

我认为一个例子更相关

我在开发环境中有一个 Web 服务

https://service-dev.alexisgatuingt.fr/

id:test@***.com

通过:p@ssw0rd!

复制:

转到“文章”部分 打开模态“Créer unarticles”(编辑器位于底部) 关闭对话框 并用mdi-pencil图标打开编辑模式

编辑器里终于没有内容了,我得点击“Relancer l'éditeur”来加载内容

“投资组合”部分也是如此

你可以创建、编辑、删除任何你想要的,这是一个开发环境

这是我的代码(简化):

modal.vue (modal 的根组件)

<template>
    <div :style="`z-index:$index !important; display: inherit`">
        <!-- scroll bar = 15px -->
        <v-dialog v-model="dialog" persistent transition="dialog-top-transition" :hide-overlay="hideOverlay"
            :max- overlay-color="primary">
            <v-form v-model="formValidation" ref="form" lazy-validation enctype='multipart/form-data'
                :retain-focus="false">
                <slot name="content"></slot>
            </v-form>
        </v-dialog>
        <div style="display: inherit" @click="dialog = true">
            <slot></slot>
        </div>
    </div>
</template>

创建.vue

<template>
    <modal title="Créer un article" description="Pour créer un article, renseigner les champs ci-dessous : "
        v-on:submit="submit" ref="modal" show-cancel label="Enregistrer">
        <slot></slot>
        <template v-slot:content>
            <v-btn @click="reload">Relancer l'éditeur</v-btn>
            <v-col cols="12" @focusin.stop>
                <editor id="create-article" :key="key" :api-key="apiKey" v-model="request.content"
                    :init="editorOptions"></editor>
            </v-col>
        </template>
    </modal>
</template>

<script>
import editorOptions, apiKey from "&/plugins/editor";
import generateRandomKey from "@/utils";

export default 
    name: "create",
    data() 
        return 
            key: '',
            apiKey,
            editorOptions,
            request: 
                content: '',
            ,
        
    ,
    methods: 
        submit() 
        ,
        reload() 
            this.key = generateRandomKey();
        ,
    

</script>

edit.vue

<template>
    <modal title="Modifier l'article" description="Pour modifier cet article, renseigner les champs ci-dessous : "
        v-on:submit="submit" ref="modal" show-cancel label="Enregistrer">
        <slot></slot>
        <template v-slot:content>
            <v-btn @click="reload">Relancer l'éditeur</v-btn>
            <v-col cols="12" @focusin.stop>
                <editor :id="item.slug" :api-key="apiKey" :key="key" v-model="request.content"
                    :init="editorOptions"></editor>
            </v-col>
        </template>
    </modal>
</template>

<script>
import editorOptions, apiKey from "&/plugins/editor";
import generateRandomKey from "@/utils";

export default 
    name: "edit",
    props: 
        item: 
            type: Object,
            required: true,
        
    ,
    data() 
        return 
            key: '',
            apiKey,
            editorOptions,
            request: 
                content: '',
            ,
        
    ,
    mounted() 
        this.request.content = this.$props.item.content ?? '';
    ,
    methods: 
        reload() 
            this.key = generateRandomKey();
        ,
        submit() 

        ,
    

</script>

<style scoped>

</style>

editor.js

import store from '&/store';
import mixin from '@/store/mixin';
import Crud from "&/api/crud";

const MediaController = new Crud('media');

let imagesList = [];

MediaController.list(
    perPage: 999,
    search: 'image',
    page: 1,
).then((r) => 
    r.data.forEach(e => imagesList.push(
        title: e?.oldName,
        value: e?.url
    ));
)

let method = mixin(store).methods;

const toolbar = ['searchreplace h1 h2 h3 h4 h5 h6 bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo', 'removeformat subscript superscript code codesample hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'];

const plugins = ['advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount'];

const editorOptions = 
    //selector: "textarea",
    height: 500,
    image_uploadtab: true,
    menubar: true,
    relative_urls: false,
    remove_script_host: false,
    convert_urls: true,
    images_upload_credentials: true,
    image_list: imagesList,
    images_upload_handler: (blobInfo, success, failure, progress) => 
        let form = new FormData();
        form.append('file', blobInfo.blob(), blobInfo.filename());
        form.append('type', "image/*");
        form.append('old_name', blobInfo.filename());
        MediaController.add(form).then((r) => 
            success(r.data.url);
        ).catch((e) => 
            method.showError(e);
            failure("Un problème est survenu");
        )
    ,
    plugins,
    toolbar,


const apiKey = '#######################';

export editorOptions, apiKey

模态激活器

<component is="edit"> // can be create
    <v-btn color="primary">
        Modifier
    </v-btn>
</component>

更新:

我也试过

mounted() 
    this.$nextTick(() => 
        this.$watch(
            () => this.$refs.modal.dialog,
            (v) => v ? this.reload() : null,
        )
    );
,

提前谢谢你

【问题讨论】:

【参考方案1】:

在为大致相同的问题挣扎了很多小时后,对我有用的是以下几种方法的组合:

带有包含 TincyMCE 对话框的外部组件,将 eager 设置为 True,并将 Retain-focus 设置为 False。

<v-dialog
    v-if="someCondition"
    v-model="openEditorModal"
    max-
    scrollable
    :retain-focus="false"
    @click:outside="closeEditorModal"
    @keydown="closeOnKeydown"
    :eager="true"
/>

封装 TinyMCE 的组件有条件地用 v-if 显示其内容:

<MceEditor
  v-if="showMce"
  :inline="true"
  :content="someObject.text
  @change="someObjectChange($event)"
></MceEditor>

延迟 TinyMCE 的创建并在包装组件卸载时将其销毁:

  public mounted() 
    this.$nextTick(() => 
      setTimeout(() => 
        this.showMce = true;
      , 3000);
    );
  

  public beforeUnmount() 
    this.showMce = false;
  

丑陋,但有效。

【讨论】:

节省了潜在的时间浪费【参考方案2】:

您应该为每个编辑器实例只设置一次(安装时)不同的键,然后 Vue 将为每个编辑器实例保留单独的状态。例如:

创建.vue

mounted()
  this.key = "editor-create"

edit.vue

mounted() 
  this.key = `editor-edit-$this.item.id`

或者为了简化,直接在模板中设置:

<editor key="editor-create" ...></editor>
<editor :key="`editor-edit-$item.id`" ...></editor>

【讨论】:

不,它不起作用,我只是用代码尝试更新了问题,它也不起作用 您如何获取您的item 数据?将它分配给 this.request.content 时是否已定义?您可以在分配之前对其进行控制台记录吗? 对于创建组件没有道具。但是对于编辑是的,它是一个道具。 item 是要更新的实体 好的,我认为这可能是一个 TinyMCE 错误。请参阅此报告github.com/tinymce/tinymce-vue/issues/230。它有类似的行为,它可能会帮助你

以上是关于Vuejs & Vuetify - 在对话框中使用 Tinymce 作为组件需要重新加载的主要内容,如果未能解决你的问题,请参考以下文章

如何在对话框 vuetify 中设置布局行?

VueJS 和 Vuetify - 用于 v-select 的数组项在数据更新后不刷新

如何将 vuetify 组件正确地动态传递到 vueJs 组件中

使用 Vuetify 的导航栏中的动态按钮

如何在带有 vuetify 和 vuex 的 Vuejs 项目中使用 Jest?节点模块和 Vuetify 问题

VueJS + Vuex + Vuetify 导航抽屉