|
|
@@ -19,18 +19,15 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
data() {
|
|
|
- return {
|
|
|
- editor: null,
|
|
|
- toolbar: null
|
|
|
- }
|
|
|
+ return {}
|
|
|
},
|
|
|
watch: {
|
|
|
content(newVal) {
|
|
|
// 外部 prop 变化(异步加载详情)才回填,避免输入过程中光标跳动与循环触发
|
|
|
// setHtml 在内部 Slate state 半销毁状态下会抛 "proxy has been revoked",
|
|
|
// 用 try-catch 兜住,不让 watcher 异常把整个 Vue 组件标为 error 状态
|
|
|
- if (this.editor && newVal !== this.editor.getHtml()) {
|
|
|
- try { this.editor.setHtml(newVal || '') } catch (e) { /* swallow */ }
|
|
|
+ if (this._editor && newVal !== this._editor.getHtml()) {
|
|
|
+ try { this._editor.setHtml(newVal || '') } catch (e) { /* swallow */ }
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
@@ -48,7 +45,7 @@ export default {
|
|
|
// 2) 新建的 editor 工具栏被旧 DOM 遮挡,视觉上像"不可点击"
|
|
|
// mounted 已经完成创建,这里只做幂等占位;内容回填由 watch content → setHtml 处理
|
|
|
init() {
|
|
|
- if (!this.editor) {
|
|
|
+ if (!this._editor) {
|
|
|
this._createEditor()
|
|
|
}
|
|
|
},
|
|
|
@@ -61,13 +58,13 @@ export default {
|
|
|
// 一点页面就抛 "Cannot perform 'get' on a proxy that has been revoked"。
|
|
|
// 同步调用 + try-catch 兜住 destroy 链内部任何抛错,保证 emit("destroyed")
|
|
|
// 至少能走到,监听器能被摘掉。
|
|
|
- if (this.editor) {
|
|
|
- try { this.editor.destroy() } catch (e) { /* swallow */ }
|
|
|
- this.editor = null
|
|
|
+ if (this._editor) {
|
|
|
+ try { this._editor.destroy() } catch (e) { /* swallow */ }
|
|
|
+ this._editor = null
|
|
|
}
|
|
|
- if (this.toolbar) {
|
|
|
- try { this.toolbar.destroy() } catch (e) { /* swallow */ }
|
|
|
- this.toolbar = null
|
|
|
+ if (this._toolbar) {
|
|
|
+ try { this._toolbar.destroy() } catch (e) { /* swallow */ }
|
|
|
+ this._toolbar = null
|
|
|
}
|
|
|
},
|
|
|
_createEditor() {
|
|
|
@@ -113,31 +110,18 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this.editor = createEditor({
|
|
|
+ // 不要把 wangEditor 实例放进 Vue data。Vue 2 会递归观察 Slate
|
|
|
+ // 的 children/selection 等内部对象,生成响应式 Proxy;Slate 在
|
|
|
+ // 下一次操作时会撤销旧 Proxy,随后 selectionchange/editor.focus
|
|
|
+ // 就会报 "Cannot perform 'get' on a proxy that has been revoked"。
|
|
|
+ // 以实例私有字段保存,避免 Vue 对编辑器状态做响应式代理。
|
|
|
+ const editor = createEditor({
|
|
|
selector: this.$refs.editorContainer,
|
|
|
config: editorConfig,
|
|
|
mode: 'default'
|
|
|
})
|
|
|
|
|
|
- // 兜底:把 editor.apply 包一层 try-catch。Slate.js 在每次 op 后会
|
|
|
- // revoke 旧 state 代理;如果 selectionchange 触发的 editor.select →
|
|
|
- // apply 路径撞上一个 stale 代理(常见于 setHtml 跑了一串 op 后,
|
|
|
- // 监听器 closure 还指向早期 state),会直接 Uncaught。捕获后该次
|
|
|
- // 选择同步丢弃,不影响后续输入;后续 Slate 自己的 op 流程仍走原 apply,
|
|
|
- // 这里只是把"已是 normal"的封死,不是替换实现。
|
|
|
- if (this.editor && typeof this.editor.apply === 'function') {
|
|
|
- const origApply = this.editor.apply
|
|
|
- this.editor.apply = function(op) {
|
|
|
- try {
|
|
|
- return origApply.call(this, op)
|
|
|
- } catch (err) {
|
|
|
- if (err && err.message && err.message.indexOf('proxy has been revoked') !== -1) {
|
|
|
- return null
|
|
|
- }
|
|
|
- throw err
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ this._editor = editor
|
|
|
|
|
|
// 工具栏:列出 wangEditor 5 已注册的全部有效菜单 key
|
|
|
// 注:wangEditor 把删除线菜单命名为 "through","mergeCells" 不是注册菜单
|
|
|
@@ -178,8 +162,8 @@ export default {
|
|
|
]
|
|
|
}
|
|
|
|
|
|
- this.toolbar = createToolbar({
|
|
|
- editor: this.editor,
|
|
|
+ this._toolbar = createToolbar({
|
|
|
+ editor,
|
|
|
selector: this.$refs.toolbarContainer,
|
|
|
config: toolbarConfig,
|
|
|
mode: 'default'
|
|
|
@@ -200,6 +184,6 @@ export default {
|
|
|
}
|
|
|
.wangeditor-wrapper .editor-container {
|
|
|
min-height: 600px;
|
|
|
- height: auto !important;
|
|
|
+ height: 600px !important;
|
|
|
}
|
|
|
</style>
|