【electron+vue3+ts实战便笺exe】终章:markdown编辑器以及右键功能实现

1,354 阅读5分钟

【I便笺】项目是当时学习electron、vue3、ts所创建的项目,也由原来的手写简易的富文本变成了现在所见所得的markdown编辑器,本文也是【I便笺】electron+vue3+ts的终章,本文主要重点描述对所见所得markdown编辑器以及编辑器右键功能实现。也许后面考虑用tauri重构。

github
github: github.com/heiyehk/ele…
包下载
release: github.com/heiyehk/ele…

主要功能如下

  • 编辑器替换
    • 所见所得markdown编辑器替换
    • 增加本地图片缓存的实现(原base64展示)
    • 编辑器右键功能实现(需要注意的是在h5中操作navigator.clipboard得在https环境或者localhost)才能使用
  • 为便笺添加沉浸式的输入方式
  • 部分bug的fix以及部分优化

编辑electron入口文件,增加atom协议

在原来对编辑器粘贴之后是base64格式展示,后面替换本地缓存剪切板的图片,由本地路径去展示,需要增加一个对本地访问的协议。比如浏览器访问文件管理的协议则是file:///

通过protocol去注册一个atom的协议拦截本地资源替换。

参考协议 | protocol (protocol) - Electron 中文开发手册

protocol.registerFileProtocol('atom', (request, callback) => {
  const url = request.url.substring(7);
  callback(decodeURI(path.normalize(url)));
});

vditor所见所得编辑器

什么是vditor

Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。

关于所见所得编辑器也是参考了很久,最终选择了这个。这个编辑器有个很强大的功能就是对复制的内容可以转化为markdown格式,而且很多code也能转化为源格式的markdown,最重要的是免费开源。

自定义toolbar

  • 工具栏,可使用 name 进行简写: toolbar: ['emoji', 'br', 'bold', '|', 'line'] 。默认值参见 src/ts/util/Options.ts
  • name 可枚举为: emoji , headings , bold , italic , strike , | , line , quote , list , ordered-list , check ,outdent ,indent , code , inline-code , insert-after , insert-before ,undo , redo , upload , link , table , record , edit-mode , both , preview , fullscreen , outline , code-theme , content-theme , export, devtools , info , help , br
  • name 不在枚举中时,可以添加自定义按钮,格式如下:
new Vditor('vditor', {
  toolbar: [
    {
      hotkey: '⇧⌘S',
      name: 'sponsor',
      tipPosition: 's',
      tip: 'costom',
      className: 'right',
      icon: '',
      click: () => void,
    }],
})
const toolbar: IMenuItem[] = [
  {
    name: 'bold',
    icon: '<i class="iconfont icon-editor-bold"></i>'
  },
  {
    name: 'italic',
    icon: '<i class="iconfont icon-italic"></i>'
  },
  {
    name: 'line',
    icon: '<i class="iconfont icon-underline"></i>'
  },
  {
    name: 'strike',
    icon: '<i class="iconfont icon-strikethrough"></i>'
  },
  {
    name: 'list',
    icon: '<i class="iconfont icon-ul"></i>'
  },
  {
    name: 'list',
    icon: '<i class="iconfont icon-ol"></i>'
  },
  {
    name: 'code',
    icon: '<i class="iconfont icon-code"></i>'
  }
];

使用方法

new Vditor(editorRef.value as HTMLElement, {
  cache: {
    enable: false
  },
  linkPrefix: '#',
  toolbar,
  undoDelay: 0,
  value: props.modelValue,
  placeholder: '记笔记...',
  input: value => {
    emits('on-input', vditor.value?.getHTML(), value);
    emits('update:modelValue', value);
  },
  after: () => {
    vditorLoad();
  },
  link: {
    isOpen: false,
    click: el => {
      // 从浏览器打开链接
      if (urlRegExp.test(el.textContent!)) {
        shell.openExternal(el.textContent!);
      }
    }
  },
  image: {
    isPreview: false,
    preview: openImageAsNewWindow
  },
  upload: {
    // @ts-ignore
    handler: async files => {
      insertImage(files[0]);
    }
  }
});

/**
 * 编辑器加载完毕
 * 进行聚焦,滚动条滚动到底部
 */
const vditorLoad = () => {
  vditor.value?.setValue(props.modelValue);
  vditor.value?.focus();
  // vditor的内容容器
  const editorContainer = document.querySelector('.vditor-ir .vditor-reset');
  const editorScrollHeight = editorContainer?.scrollHeight ?? 0;
  editorContainer!.scrollTop = editorScrollHeight;
};

vditor的toolbar是自由扩展的,无缝兼容之前的操作功能,功能也是比较强的一个markdown编辑器,只不过输入方式增加了markdown输入方式。虽然有一些操作vditor暂未实现,但是笔者通过源代码去做了一些扩展并提了pr。

右键功能

右键功能在刚开始的时候就开发了一个右键插件,所以只需要关注右键弹窗的内容功能就行。

复制图片、文字

思路:在点击右键后,对选中的文本、图片、链接去做校验,如果是图片就展示图片的右键item展示,否则去判断是否含有选中的文本如果都有没有的话则禁用掉复制功能。

// 判断点击节点
switch (targetName) {
  case 'IMG':
    // 复制图片
    copyImage(target as HTMLImageElement);
    break;
    ......
  default:
    // vditor自带了获取选中文本的函数,所以只需要把文本内容写入剪切板即可
    const text = vditor.value?.getSelection();
    navigator.clipboard.writeText(text as string);
}

copyImage功能,这里其实应该去通过图片字节去判断图片是什么格式,然后再写入blob.type。

/**
 * 复制图片
 * @param dom
 */
export const copyImage = async (dom: HTMLImageElement) => {
  const base64Url = await convertImgToBase64(dom.src, 'image/png');
  const blob = await convertBase64UrlToBlob(base64Url, 'image/png');
  // 向剪切板写入流数据
  navigator.clipboard.write([
    new ClipboardItem({
      [blob.type]: blob
    })
  ]);
};

粘贴图片、文字、带样式的html

思路:去判断剪切板内是否含有内容,并判断剪切板item的类型,从图片到文本依次去做校验。

const clipboardList = await window.navigator.clipboard.read().catch(() => {
  return [];
});
if (clipboardList && clipboardList.length) {
  const firstClipboard = clipboardList[0];
  if (!firstClipboard) return;

  const firstClipboardTypes = firstClipboard.types;

  // 粘贴图片
  if (firstClipboardTypes && firstClipboardTypes[0].includes('image')) {
    insertImage(await firstClipboard.getType(firstClipboardTypes[0]));
    return;
  }

  // 粘贴html
  if (
    firstClipboardTypes &&
    firstClipboardTypes.includes('text/html') &&
    firstClipboardTypes.includes('text/plain')
  ) {
    const html = await(await firstClipboard.getType('text/html')).text();
    console.log('html', html);
    vditor.value?.insertValue(html.substring(0, html.length - 18));
    return;
  }

  // 粘贴文本
  if (firstClipboardTypes && firstClipboardTypes.includes('text/plain')) {
    const text = await(await firstClipboard.getType('text/plain')).text();
    console.log('text', text);
    vditor.value?.insertValue(text);
  }
}

打开图片和当前选中图片文件夹

这个就比较简单一点,判断是否是本地引入的。之前注册的协议就派上用场了。判断开头是否atom即可,需要注意的是斜杠在不同系统上的应用,WindowsOS是 /,而MacOS和LinuxOS则是\。因为没有mac系统,所以这块暂未作处理。

targetImg.src.startsWith('atom')

然后通过electron自带的shell就可以去实现,

打开文件夹

shell.showItemInFolder(targetImg.src.replace('atom:///', ''))

打开图片

直接唤起系统默认图片预览打开该图片

shell.openPath(targetImg.src.replace('atom:///', ''))

结尾

本次主要的内容还是编辑器的改动较大,其他的基本上是小优化,也许后面会用tauri去进行重构,因为electron的包体积太大了,很明显不符合【便笺】的便捷性。

注:该项目是本人当初为了学习electronvue3ts而开发的功能,并非是一款产品

以上就是本篇主要开发内容了,有错误的地方可帮忙在下方评论。如果有想学习或想具体了解的可以Email、私信联系。