编辑器工具栏

预览
主要是为了方便编辑,增加一些简单的快捷按钮。
当前支持:
- 将选中行设置为对应的标题
 
- 文字常见的样式设置:加粗(
**)、倾斜(*)、下划线(<u></u>)、删除线(~~) 
- 插入页面分隔符(
------------) 
- 插入行内代码、代码块
 
- 插入行内公式、公式块
 
- 插入mermaid、plantuml图片代码
 
- 插入链接
 
- Emoji表情图标和特殊字符已支持输入
 
后续会继续补充:
- 字体样式、大小、颜色,选中文字变为引用
 
- 文字左对齐、右对齐、居中对齐、两侧对齐
 
- 有序列表、无序列表
 
- 插入图片、图片居中、表格、标记、分类
 
- 锚点链接、任务列表 
 
- material样式
 
- 其他
 
实现
因为软件架构原因,工具栏和编辑器不是同一个vue组件,无法通过props等动作进行处理。
这里使用了eventbus,进行vue组件间的通信
1. 定义event-bus.js
| TypeScript | 
|---|
 | // event-bus.js
const EventBus = {
  // 你可以使用 ref 创建一个响应式的数据源(如果需要的话)
  // 也可以使用 Map 或其他数据结构来存储事件和回调
  callbacks: new Map(),
  $on(eventName, callback) {
    if (!this.callbacks.has(eventName)) {
      this.callbacks.set(eventName, [])
    }
    this.callbacks.get(eventName).push(callback)
  },
  $emit(eventName, payload) {
    if (this.callbacks.has(eventName)) {
      this.callbacks.get(eventName).forEach((callback) => {
        callback(payload)
      })
    }
  },
  $off(eventName, callback) {
    if (this.callbacks.has(eventName)) {
      this.callbacks.set(
        eventName,
        this.callbacks.get(eventName).filter((cb) => cb !== callback)
      )
    }
  },
  // 修改 $off 方法以支持移除所有事件的所有监听器
  $offAll() {
    this.callbacks.clear()
  }
}
export default EventBus
  | 
 
2. 在工具栏组件中触发事件:
| TypeScript | 
|---|
 | import EventBus from '../../event-bus'
function handleUpdateHeaderFormat(header: string) {
    EventBus.$emit('monaco-editor-update-header-format', header)
}
  | 
 
3. 在编辑器组件中监听事件
| TypeScript | 
|---|
 | import EventBus from '../../event-bus'
onMounted(() => {
    EventBus.$on('monaco-editor-update-header-format', (value: string) => {
        updateConvertFontHeaderFormat(editorInstance, value)
    })
    onBeforeUnmount(() => {
        EventBus.$off('monaco-editor-update-header-format', (value: string) => {
            updateConvertFontHeaderFormat(editorInstance, value)
        })
    })
})
  | 
 
4. 利用monaco的API处理
| TypeScript | 
|---|
 | function updateHandleFontStyle(
    editor: monaco.editor.IStandaloneCodeEditor,
    startStr: string,
    endStr: string
) {
    // 获取当前的选择范围
    const selection = editor.getSelection()
    if (!selection || selection.isEmpty()) {
        // 没有选择,则直接插入字符串
        insertTextAfterCursor(editor, startStr + endStr)
        return
    }
    // 获取编辑器模型, 确保模型存在
    const model = editor.getModel()
    if (!model) return
    const { startLineNumber, startColumn, endLineNumber, endColumn } = selection
    let selectRange: Range = selection
    const selectedText = model.getValueInRange(selection)
    // 选择的部分就有*
    let newText: string
    if (selectedText.startsWith(startStr) && selectedText.endsWith(endStr)) {
        newText = selectedText.substring(startStr.length, selectedText.length - endStr.length)
    } else {
        // 选择的部分没有*,向前后增加三个字符
        const lineLength = model.getLineLength(endLineNumber)
        const start = Math.max(1, startColumn - startStr.length)
        const end = Math.min(lineLength + 1, endColumn + endStr.length)
        selectRange = new monaco.Range(startLineNumber, start, endLineNumber, end)
        const content = model.getValueInRange(selectRange)
        // 三个*,说明文字本来是加粗倾斜的,去掉倾斜
        if (content.startsWith(startStr) && content.endsWith(endStr)) {
            newText = content.substring(startStr.length, content.length - endStr.length)
        } else {
            selectRange = selection
            newText = `${startStr}${selectedText}${endStr}`
        }
    }
    const edit = {
        range: selectRange, // 这是一个空范围,表示插入位置
        text: newText, // 要插入的文本
        forceMoveMarkers: false // 如果需要,强制移动标记(如断点)
    }
    editor.executeEdits('updateHandleFontItalic', [edit])
}
  |