我正在构建一个使用 Vue Draggable 库和 Tip Tap Editor 库的 Nuxt JS Web 应用程序。在我的用例中,我可以将“组件”拖到我的网页中,例如图表和编辑器。
当我将一个元素拖入我的网页时,draggable
会运行到 cloneComponent
组件上,它会计算出我拖入页面中的组件类型。
我的编辑器存储在一个单独的对象中,其中使用随机 ID 在它们之间进行通信,因为我无法直接在拖入我的页面的组件中初始化 Tip Tap 编辑器。
然而,由于某种原因,尽管我的数据存在,但我收到一个错误,其中 editor
显然无法找到,因此编辑器从未显示在我的网页上,但它确实存在,我正在努力找出原因:
无法读取未定义的属性“editor”
<template>
<div>
<!-- Report Navigation & Controls -->
<section class="bg-white fixed inset-x-0 top-0 w-full z-40" ref="editorNavbar">
<div v-if="!isPreviewMode" class="max-w-7xl mx-auto px-7 py-7">
<!-- Screen: Components -->
<div class="grid grid-cols-12">
<div class="col-span-12">
<draggable
class="flex"
:list="components"
:group="{ name: 'components', pull: 'clone', put: false }"
:clone="cloneComponent"
>
<div class="p-3 border border-4 border-gray-300 rounded-md text-center h-24 w-24 flex items-center justify-center mr-6 cursor-move" v-for="(component, index) in components" :key="index">
<div>
<span v-html="component.icon"></span>
<span class="block mt-2 text-gray-500 text-sm font-medium">{{ component.title }}</span>
</div>
</div>
</draggable>
</div>
</div>
</div>
</section>
<!-- Report Canvas -->
<draggable
class="dragArea h-auto pb-96"
:list="report"
group="components"
>
<div
v-for="(component, index) in report"
:key="index"
>
<!-- Editor -->
<!-- BUG: editors[component.componentID].editor not defined? -->
<div v-if="component.type == 'editor'">
<div class="mb-4">
<div class="bg-white px-2 pt-2 pb-0">
<editor-menu-bar :editor="editors[component.componentID].editor" v-slot="{ commands, isActive }">
<div class="menubar">
<button
class="menubar__button w-9 h-9 p-2"
:class="{ 'bg-gray-100 rounded-sm': isActive.bold() }"
@click="commands.bold"
>
<img class="h-5 w-5 mx-auto" src="~/assets/images/icons/bold.svg" />
</button>
</div>
</editor-menu-bar>
</div>
<div class="bg-white rounded-md flex">
<editor-content :editor="editors[component.componentID].editor" class="editor-box block w-full" />
</div>
</div>
</div>
</div>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import { Editor, EditorContent, EditorMenuBar } from 'tiptap'
import {
Blockquote,
CodeBlock,
HardBreak,
Heading,
OrderedList,
BulletList,
ListItem,
TodoItem,
TodoList,
Bold,
Code,
Italic,
Link,
Strike,
Underline,
History
} from 'tiptap-extensions'
export default {
layout: 'reportBuilder',
components: {
draggable,
EditorMenuBar,
EditorContent,
},
data () {
return {
id: this.$route.params.report ? this.$route.params.report : null,
rawReport: null,
editors: {},
charts: [],
report: [],
components: [{
title: 'Text',
icon: '<svg class="mx-auto" xmlns="http://www.w3.org/2000/svg" fill="none" height="24" width="24" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>',
type: 'editor',
name: '',
datasets: []
}, {
title: 'Chart',
icon: '<svg class="mx-auto" xmlns="http://www.w3.org/2000/svg" fill="none" height="24" width="24" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z" /></svg>',
type: 'chart',
name: '',
datasets: []
}]
}
},
methods: {
/*
** Clone a component into our canvas
*/
cloneComponent ({ type, name, datasets }, compID = null) {
const id = compID ? compID : Math.round(window.performance.now())
// TODO: reverse and somehow get editors to show
if (compID) {
if (type == 'editor') {
this.editors[id] = {
editor: new Editor({
extensions: [
new Blockquote(),
new CodeBlock(),
new HardBreak(),
new Heading({ levels: [1, 2, 3] }),
new BulletList(),
new OrderedList(),
new ListItem(),
new TodoItem(),
new TodoList(),
new Bold(),
new Code(),
new Italic(),
new Link(),
new Strike(),
new Underline(),
new History()
],
content: '',
}),
content: ''
}
}
}
this.report.push({
componentID: id ?? 0,
type: type ?? '',
name: name ?? '',
datasets: datasets ?? []
})
},
/*
** Reset report
*/
resetReport () {
this.rawReport = null
this.editors = []
this.charts = []
this.report = []
this.savableReport = []
this.sourcesToFind = []
},
/*
** Get report
*/
getReport () {
this.resetReport()
try {
this.$axios.get(
`${process.env.API_URL}/api/reports/view/${this.id}`,
{
timeout: 5000
}
).then(res => {
if (!res.data.success) return
this.rawReport = res.data.report
const {
saved_at,
discovery_filters
} = this.rawReport
// if there is report data linked to this report
if (this.rawReport.report_data) {
for (const [index, filter] of this.rawReport.report_data.entries()) {
const clonable = {
...{
type: filter.type
},
...filter
}
this.cloneComponent(clonable, filter.componentID)
}
}
if (discovery_filters) this.sourcesToFind = discovery_filters
this.savedAt = saved_at
})
} catch (err) { }
}
}
}
</script>
对于额外的上下文,如果 new Editor
可以只是被拖入网页的组件的一部分,那就太好了,但是在我保存我的报告后,cloneComponent
函数以及一些额外的必须运行逻辑才能检索报告数据,这就是 componentID
的用武之地。
归根结底,为什么 editor
存在于我的对象中时未定义?