<think>
我们已经在之前的回答中实现了在Vue2中使用TinyMCE,并在选中文本后通过自定义右键菜单添加了“数据源”选项。下面我将整理一个更清晰、完整的解决方案,并参考站内引用中的相关内容。
### 解决方案步骤
#### 1. 初始化TinyMCE编辑器并配置自定义右键菜单命令
根据引用[1]和[3]的思路,我们需要在TinyMCE的初始化配置中通过`setup`函数添加自定义命令,并覆盖默认的右键菜单。
```vue
<template>
<div>
<editor
id="my-tinymce"
v-model="content"
:init="editorInit"
/>
<!-- 自定义右键菜单 -->
<div v-show="showMenu" :style="menuStyle" class="custom-context-menu">
<div @click="handleDataSource">数据源</div>
<!-- 可以添加更多自定义选项 -->
</div>
</div>
</template>
<script>
import Editor from '@tinymce/tinymce-vue'
export default {
components: { Editor },
data() {
return {
content: '',
showMenu: false,
menuX: 0,
menuY: 0
}
},
computed: {
menuStyle() {
return {
left: `${this.menuX}px`,
top: `${this.menuY}px`,
position: 'fixed'
}
}
},
methods: {
// 显示自定义菜单
showCustomMenu(event) {
// 获取编辑器实例
const editor = window.tinymce.get('my-tinymce')
if (!editor) return
// 获取选中内容
const selectedContent = editor.selection.getContent()
if (!selectedContent) return // 没有选中内容则不显示
// 设置菜单位置
this.menuX = event.clientX
this.menuY = event.clientY
this.showMenu = true
// 点击其他地方关闭菜单
document.addEventListener('click', this.closeMenu)
},
// 关闭菜单
closeMenu() {
this.showMenu = false
document.removeEventListener('click', this.closeMenu)
},
// 处理数据源选项
handleDataSource() {
const editor = window.tinymce.get('my-tinymce')
const selectedContent = editor.selection.getContent()
alert(`处理数据源,选中的内容是:${selectedContent}`)
// 这里可以实现打开数据源选择对话框等操作
this.closeMenu()
}
},
mounted() {
// 在编辑器区域内绑定右键点击事件
const editorContainer = document.getElementById('my-tinymce')
if (editorContainer) {
// 监听编辑器区域的右键点击
editorContainer.addEventListener('contextmenu', (e) => {
e.preventDefault() // 阻止默认右键菜单
this.showCustomMenu(e)
})
}
},
beforeDestroy() {
// 移除事件监听
const editorContainer = document.getElementById('my-tinymce')
if (editorContainer) {
editorContainer.removeEventListener('contextmenu', this.showCustomMenu)
}
document.removeEventListener('click', this.closeMenu)
}
}
</script>
<style>
.custom-context-menu {
background: white;
border: 1px solid #ddd;
box-shadow: 2px 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
}
.custom-context-menu div {
padding: 8px 15px;
cursor: pointer;
}
.custom-context-menu div:hover {
background: #f0f0f0;
}
</style>
```
#### 2. 关键配置说明
- **覆盖默认右键菜单**:通过监听编辑器区域的`contextmenu`事件,阻止默认菜单并显示自定义菜单。
- **菜单定位**:使用事件对象的`clientX`和`clientY`来定位菜单。
- **获取选中内容**:通过TinyMCE的API `editor.selection.getContent()`获取选中的内容。
#### 3. 注意事项
- **多实例处理**:如果页面有多个TinyMCE编辑器,需要为每个编辑器分配唯一的ID,并分别绑定事件。
- **菜单层级**:确保自定义菜单的z-index足够高,以免被其他元素覆盖。
- **销毁事件**:在组件销毁前移除事件监听,避免内存泄漏。
### 引用说明
此方案结合了以下站内引用的思路:
- 引用[1]:介绍了在Vue项目中集成TinyMCE的基本方法和常见问题解决。
- 引用[3]:展示了如何通过自定义插件扩展TinyMCE的功能,虽然本文未直接使用插件方式,但自定义命令的思路类似。
### 相关问题
§§ 相关问题 §§
1. 如何在自定义右键菜单中同时保留TinyMCE的部分原生选项(如复制、粘贴)?
2. 如何根据选中内容的类型(文字、图片、表格)动态显示不同的右键菜单?
3. 在Vue2中如何封装这个自定义右键菜单为可复用组件?
4. 如何优化自定义右键菜单的性能,避免频繁的DOM操作?
[^1]: [【超详细】vue项目:Tinymce富文本使用教程以及踩坑总结+业务需要功能扩展](https://example.com)
[^3]: [Tinymce自定义插件示例](https://example.com)
</think>
### Vue2 中使用 TinyMCE 添加右键「数据源」选项的完整方案
#### 核心步骤
1. **覆盖默认右键菜单**
禁用 TinyMCE 默认菜单,绑定自定义事件:
```js
editorInit: {
contextmenu: false, // 禁用默认菜单[^1]
setup: (editor) => {
editor.on('contextmenu', (e) => {
e.preventDefault();
this.showCustomMenu(e);
});
}
}
```
2. **实现自定义菜单组件**
使用轻量级右键菜单库(如引用[2]推荐):
```bash
yarn add vue-simple-context-menu
```
```vue
<vue-simple-context-menu
element-id="editorMenu"
:options="menuOptions"
ref="contextMenu"
@option-clicked="handleMenuClick"
/>
```
3. **配置菜单选项**
```js
data() {
return {
menuOptions: [
{ name: '数据源', class: 'data-source-class' },
{ name: '复制', type: 'divider' },
//...
],
selectedContent: ''
}
}
```
4. **显示右键菜单**
```js
methods: {
showCustomMenu(e) {
const editor = tinymce.get('editorId');
this.selectedContent = editor.selection.getContent();
this.$refs.contextMenu.showMenu({
top: e.clientY,
left: e.clientX
});
},
handleMenuClick(option) {
if (option.name === '数据源') {
this.handleDataSource();
}
}
}
```
5. **实现数据源功能**
```js
handleDataSource() {
// 示例:打开数据源选择模态框
this.$modal.show('data-source-modal', {
content: this.selectedContent
});
// 实际业务中可对接API
axios.get('/api/data-sources', {
params: { keywords: this.selectedContent }
}).then(response => {
// 处理数据源返回结果
});
}
```
#### 关键配置说明
| 配置项 | 说明 | 示例值 |
|--------|------|--------|
| `contextmenu` | 禁用默认菜单 | `false` |
| `element-id` | 菜单容器ID | `"editorMenu"` |
| `options` | 菜单选项数组 | `[{ name: "数据源" }]` |
| `selection.getContent()` | 获取选中内容 | 编辑器API调用 |
#### 完整示例代码
```vue
<template>
<div>
<editor
id="tinymceEditor"
v-model="content"
:init="editorInit"
/>
<vue-simple-context-menu
element-id="tinymceMenu"
:options="menuOptions"
ref="contextMenu"
@option-clicked="handleMenuClick"
/>
<modal name="data-source-modal">
<DataSourceSelector :selectedText="selectedContent"/>
</modal>
</div>
</template>
<script>
import Editor from '@tinymce/tinymce-vue';
import VueSimpleContextMenu from 'vue-simple-context-menu';
export default {
components: { Editor, VueSimpleContextMenu },
data() {
return {
content: '',
selectedContent: '',
menuOptions: [
{ name: '数据源', icon: 'database' },
{ name: 'divider1', type: 'divider' },
{ name: '复制' },
{ name: '粘贴' }
],
editorInit: {
height: 500,
contextmenu: false,
setup: (editor) => {
editor.on('contextmenu', this.showCustomMenu);
}
}
}
},
methods: {
showCustomMenu(e) {
const editor = tinymce.get('tinymceEditor');
this.selectedContent = editor.selection.getContent();
this.$refs.contextMenu.showMenu({ top: e.clientY, left: e.clientX });
},
handleMenuClick(option) {
if (option.name === '数据源') this.$modal.show('data-source-modal');
}
}
}
</script>
```
#### 注意事项
1. **菜单定位优化**
需考虑页面滚动偏移量:
```js
showCustomMenu(e) {
const scrollY = window.pageYOffset;
this.$refs.contextMenu.showMenu({
top: e.clientY + scrollY,
left: e.clientX
});
}
```
2. **多实例隔离**
当存在多个编辑器时,使用唯一ID标识:
```js
<editor :id="'editor-' + editorId" ... />
```
3. **销毁处理**
在组件销毁时移除监听:
```js
beforeDestroy() {
const editor = tinymce.get('tinymceEditor');
editor.off('contextmenu', this.showCustomMenu);
}
```
### 相关问题