关于 Vditor

官方:https://b3log.org/vditor/

Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React、Angular

详情可以在官网看看

在 Typecho 中使用 Vditor Render

关于Vditor前端解析的一些想法

Vditor.preview()函数内简单来说有两个参数,第一个是mount id,一个是text(markdown)

比如说

Vditor.preview('vditor', `## H2标题`)
//效果为:<div id="vditor"><h2>H2标题</h2></div>

因此如果需要在Typecho里面解析的话,就需要获得文章的markdown 有两种方法,又或者只有一种:

第一种方法

通过$this->cid获取文章的cid,之后使用Typecho:DB() get到数据,SELECT获取到markdown,拿出来之后经过函数处理(短代码)再输出markdown 输出后用JavaScript获取,使用Vditor.preview() 即可

其实这种方法超级超级麻烦,而且typecho 关于db的函数怪怪的,我好几次用都有问题

第二种

倒是我看见handsome好像不是这么做的,暂时没有深入了解,当作TODO

其实在Typecho中还有一个叫做:text,这样子就会获取到当前文章的markdown了,那么我只需要处理后给Vditor解析就好了

也就是说我可以这样:

<?php
    $content = Content::parseContentPublic();
    echo '<textarea id="md_text" style="display: none;" class="hide" >'.htmlspecialchars($this->text). '</textarea>';
    echo '<div id="vditor"></div>'
    ?>
<script>
	var md_text = document.getElementById('md_text');
	Vditor.preview(document.getElementById('vditor'), md_text.value})
</script>

基本原理呢其实就是通过输出$this->text到一个可读取的一个地方,这个地方使用了textarea,众所周知,textarea能够用value读取到里面的数据,所以md_text.value就是markdown,document.getElementById('vditor')就是输出渲染的地方

插件中实现 Vditor Editor

众所周知,插件有专门提供的接口,我们分别在writepost和page里面添加东西,使用同一个函数,因为要加进去的东西也没有区别哈哈哈

// 添加文章编辑选项
Typecho_Plugin::factory('admin/write-post.php')->bottom = array(__CLASS__, 'Editor_addFooter');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array(__CLASS__, 'Editor_addFooter');
// 修改编辑器
Typecho_Plugin::factory('admin/write-post.php')->richEditor = array(__CLASS__, 'Editor_richEditor');
Typecho_Plugin::factory('admin/write-page.php')->richEditor = array(__CLASS__, 'Editor_richEditor');

上面的代码分别是开了页面的底部接口,和编辑器的接口,我们使用addFooter对底部加东西,在richEditor中嵌入vditor editor,那么两个函数就比如说像这个样子

    public static function Editor_addFooter() {
        echo '<link rel="stylesheet" href="'.$GLOBALS['assetURL'].'css/plugins/editor.css">';
    }
    public static function Editor_richEditor(){
        $options = Helper::options();
        echo <<<EOF
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/font-awesome.min.css">
        <link rel="stylesheet" href="{$GLOBALS['assetURL']}css/plugins/editor.css">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.css"/>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js"></script>
        <script src="{$GLOBALS['assetURL']}js/plugins/editor.js"></script>
        <script>
            window.EditorConf = {
                'i18n': {
                    'ok': '确定',
                    'cancel': '取消',
                    'toolbar': '工具栏',
                    'markdownDisabled': '本文Markdown解析已禁用!',
                    'insertAllImages': '插入所有图片',
                    'required': '必须填写',
                    'button': '按钮',
                }
            };
        </script>
  
    EOF;
    }

可能需要解释一下$GLOBALS['assetURL']这个东西是什么,在这里就一律解释为$GLOBALS['assetURL'] = Typecho_Widget::widget('Widget_Options')->themeUrl.'/assets/';

JavaScript激活

到这里PHP写完了,接着就是核心的JavaScript

const originText = $('#text');
let editor = new Vditor('vditor', {
                    "cache": {
                        "enable": false,
                        "cid": $('input[name="cid"]').val()
                    },
                    "value": originText.val(),
                    mode: 'ir', //可以修改这个来更改默认模式
                }
            );

非常的简单,易懂,详细的可以去 https://ld246.com/article/1549638745630#options 了解了解

新增功能

我们可以自己自定义一个toolbar,就像这样:

const originText = $('#text');
let editor = new Vditor('vditor', {
                    "cache": {
                        "enable": false,
                        "cid": $('input[name="cid"]').val()
                    },
                    "value": originText.val(),
                    mode: 'ir',
  									"toolbar": [
        "emoji",
        "headings",
        "bold",
        "italic",
        "strike",
        "link",
        "|",
        "list",
        "ordered-list",
        "check",
        "outdent",
        "indent",
        "|",
        "quote",
        "line",
        "code",
        "inline-code",
        "insert-before",
        "insert-after",
        "|",
        "upload",
        "record",
        "table",
        "|",
        "undo",
        "redo",
        "|",
        "fullscreen",
        "edit-mode",
        {
            name: "more",
            toolbar: [
                "export",
                "outline",
                "preview",
                "info",
                "help",
            ],
        }
  ]
                }
            );

为什么是这样子写的呢?看一下文档怎么说的

options.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: '成为赞助者',
      className: 'right',
      icon: '<svg t="1589994565028" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2808" width="32" height="32"><path d="M506.6 423.6m-29.8 0a29.8 29.8 0 1 0 59.6 0 29.8 29.8 0 1 0-59.6 0Z" fill="#0F0F0F" p-id="2809"></path><path d="M717.8 114.5c-83.5 0-158.4 65.4-211.2 122-52.7-56.6-127.7-122-211.2-122-159.5 0-273.9 129.3-273.9 288.9C21.5 562.9 429.3 913 506.6 913s485.1-350.1 485.1-509.7c0.1-159.5-114.4-288.8-273.9-288.8z" fill="#FAFCFB" p-id="2810"></path><path d="M506.6 926c-22 0-61-20.1-116-59.6-51.5-37-109.9-86.4-164.6-139-65.4-63-217.5-220.6-217.5-324 0-81.4 28.6-157.1 80.6-213.1 53.2-57.2 126.4-88.8 206.3-88.8 40 0 81.8 14.1 124.2 41.9 28.1 18.4 56.6 42.8 86.9 74.2 30.3-31.5 58.9-55.8 86.9-74.2 42.5-27.8 84.3-41.9 124.2-41.9 79.9 0 153.2 31.5 206.3 88.8 52 56 80.6 131.7 80.6 213.1 0 103.4-152.1 261-217.5 324-54.6 52.6-113.1 102-164.6 139-54.8 39.5-93.8 59.6-115.8 59.6zM295.4 127.5c-72.6 0-139.1 28.6-187.3 80.4-47.5 51.2-73.7 120.6-73.7 195.4 0 64.8 78.3 178.9 209.6 305.3 53.8 51.8 111.2 100.3 161.7 136.6 56.1 40.4 88.9 54.8 100.9 54.8s44.7-14.4 100.9-54.8c50.5-36.3 108-84.9 161.7-136.6 131.2-126.4 209.6-240.5 209.6-305.3 0-74.9-26.2-144.2-73.7-195.4-48.2-51.9-114.7-80.4-187.3-80.4-61.8 0-127.8 38.5-201.7 117.9-2.5 2.6-5.9 4.1-9.5 4.1s-7.1-1.5-9.5-4.1C423.2 166 357.2 127.5 295.4 127.5z" fill="#141414" p-id="2811"></path><path d="M353.9 415.6m-33.8 0a33.8 33.8 0 1 0 67.6 0 33.8 33.8 0 1 0-67.6 0Z" fill="#0F0F0F" p-id="2812"></path><path d="M659.3 415.6m-33.8 0a33.8 33.8 0 1 0 67.6 0 33.8 33.8 0 1 0-67.6 0Z" fill="#0F0F0F" p-id="2813"></path><path d="M411.6 538.5c0 52.3 42.8 95 95 95 52.3 0 95-42.8 95-95v-31.7h-190v31.7z" fill="#5B5143" p-id="2814"></path><path d="M506.6 646.5c-59.6 0-108-48.5-108-108v-31.7c0-7.2 5.8-13 13-13h190.1c7.2 0 13 5.8 13 13v31.7c0 59.5-48.5 108-108.1 108z m-82-126.7v18.7c0 45.2 36.8 82 82 82s82-36.8 82-82v-18.7h-164z" fill="#141414" p-id="2815"></path><path d="M450.4 578.9a54.7 27.5 0 1 0 109.4 0 54.7 27.5 0 1 0-109.4 0Z" fill="#EA64F9" p-id="2816"></path><path d="M256 502.7a32.1 27.5 0 1 0 64.2 0 32.1 27.5 0 1 0-64.2 0Z" fill="#EFAFF9" p-id="2817"></path><path d="M703.3 502.7a32.1 27.5 0 1 0 64.2 0 32.1 27.5 0 1 0-64.2 0Z" fill="#EFAFF9" p-id="2818"></path></svg>',
      click () {alert('捐赠地址:https://ld246.com/sponsor')},
    }],
})
说明默认值
name唯一标示-
iconsvg 图标-
tip提示-
tipPosition提示位置:'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'e'-
hotkey快捷键,格式为⇧⌘/⌘/⌥⌘-
suffix插入编辑器中的后缀-
prefix插入编辑器中的前缀-
click(event: Event, vditor: IVditor)自定义按钮点击时触发的事件-
className样式名''
toolbar?: Array<options.toolbar>子菜单-

明白了吧?

就此,激活插件就能用了~

集成于主题中

其实在主题里面就是那样,主题里面也可以使用插件的接口,在这里就直接摆出来吧,其实是一样的

<?
/*
 * @FilePath: /Mix-Pro/system/Interface/Editor_Plugin.php
 * @author: Wibus
 * @Date: 2021-07-03 08:49:24
 * @LastEditors: Wibus
 * @LastEditTime: 2021-07-04 06:50:39
 * Coding With IU
 */
// 添加文章编辑选项
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('Editor_Plugin', 'Editor_addFooter');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('Editor_Plugin', 'Editor_addFooter');
// 修改编辑器
Typecho_Plugin::factory('admin/write-post.php')->richEditor = array('Editor_Plugin', 'Editor_richEditor');
Typecho_Plugin::factory('admin/write-page.php')->richEditor = array('Editor_Plugin', 'Editor_richEditor');

class Editor_Plugin{

    public static function Editor_addFooter() {
        echo '<link rel="stylesheet" href="'.$GLOBALS['assetURL'].'css/plugins/editor.css">';
    }
    public static function Editor_richEditor(){
        $options = Helper::options();
        echo <<<EOF
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/font-awesome.min.css">
        <link rel="stylesheet" href="{$GLOBALS['assetURL']}css/plugins/editor.css">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.css"/>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js"></script>
        <script src="{$GLOBALS['assetURL']}js/plugins/editor.js"></script>
    EOF;
    }
}

最后效果

这个是使用的ir模式,可选模式:sv, ir, wysiwyg

  • 所见即所得(WYSIWYG)
  • 即时渲染(IR)
  • 分屏预览(SV)

修改mode即可

注意的事情

需要注意的是有两行是必须加的,这些是vditor依赖的东西来的~

        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.css"/>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js"></script>