前言
最近在写一个博客,在写编辑文章页面的时候想找一个可以支持 Markdown 的编辑器,而且支持 GFM 的一些特性,还有数学公式。
正好想到以前用思源的时候偶然看到的一个项目,就去找了一下,发现这个是最满足我的需求的一个项目了。其他项目要不就是不支持 Latex 或者 GFM,要不就是不是很好看(当然,如果有推荐的可以在评论区发一下)。
Vditor 还是有一点不太符合我的需求,就是我从洛谷把文章复制下来后有一些用行内公式裹起来的单个数字的 Vditor 无法解析,比如说 1
这种的。
我等会可以考虑一下洛谷的 Markdown 编辑器。
About
Vditor – 一款浏览器端的 Markdown 编辑器,支持所见即所得(富文本)、即时渲染(类似 Typora)和分屏预览模式 (b3log.org)
用法(React)
import Vditor from 'vditor';
import "vditor/dist/index.css"
import React, { useState, useRef } from 'react';
import { useEffect} from "react"
const MyVditor = () => {
titleRef = useRef(null)
const [vd, setVd] = useState()
useEffect(() => {
const vditor = new Vditor("vditor", {
height: "100%", // 减去顶部导航栏的高度
width:"100%",
upload: {
url: endpoint + "/api/console/upload", // 上传接口地址,必填
// linkToImgUrl: endpoint + '/api/console/upload/fetch',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`
},
},
after: () => {
if (vditorValue === "") vditor.setValue("")
else vditor.setValue(vditorValue)
vditorr = vditor
setVd(vditor)
}
})
// Clear the effect
return () => {
vd?.destroy()
setVd(undefined)
}
}, [])
return <div className="flex flex-col h-full w-full">
<Input placeholder="Title" onChange={change1} id="title" defaultValue={title} size="large" ref={titleRef}/>
<div id="vditor" style={{ height: "100px" }} className="vditor" />
</div>
}
前端大概是这样,其中几个选项的解释如下:
height
:这个选项是设置编辑器的高度的,注意这里不仅可以填写数值比如说100
表示100px
,还可以用字符串比如说100%
表示占满父元素的高度。width
:这个和height
差不多,只不过这个是控制宽度的。upload
:这个是重点,配置好这个字段可以让用户粘贴图片或者上传图片。- 这个字段的难点其实不太在前端,主要是在后端如何接受前端发送过来的文件。对于后端官方没有给太多的解释,需要自己去探索。(也有可能是我漏看了什么东西)
url
是后端用来接受文件的 url,前端用 POST 把数据发送给后端。header
这个是可以上传一些用户的自定义请求头,比如说身份验证。
接下来就到了后端的难点:处理前端 upload
发送过来的文件。
先贴一个代码:
import Router from 'express';
import bodyParser from 'body-parser';
import multer from 'multer';
import { v4 as uuidv4 } from 'uuid';
const router = Router();
// 配置 Multer 存储策略
const storage = multer.diskStorage({
destination: 'uploads/',
filename: (req, file, cb) => {
const uniqueName = `${uuidv4()}-${file.originalname}`;
cb(null, uniqueName);
},
});
const upload = multer({ storage });
// 处理单文件上传(字段名 "file")
router.post('/upload', upload.single("file[]", 5), (req, res) => {
if (!req.file) {
return res.status(400).json({ code: 1, msg: "未上传文件" });
}
res.json({
code: 0,
msg: "文件上传成功",
data: { "succMap":{"file0.jpg":`${req.protocol}://${req.get('host')}/uploads/${req.file.filename}`} }, // 返回 URL 数组
});
});
export default router;
我后端是用 Node.js 的 Express 框架写的,所以在处理文件的时候就用了 multer
。
这里有一个特别坑的点,就是前端发送的 filename
是 file[]
,而不是 file
,估计是为了多文件的时候比较统一。
后端的返回格式也需要特别注意,但是这个在开发文档里面已经写出来了,所以不算特别坑。
我这个代码之处理了单文件,没有处理多文件,但是估计不难。