Angular 项目中引入 editor.md
版本介绍
本项目由 @angular/cli@13.1.2
生成 Angular 13.1.0
,引入的是 editor.md v1.5.0
版本。
文件目录
项目根目录
├── src
│ ├── ...
│ └── assets
│ ├── ...
│ └── plugins
│ ├── jquery.min.js
│ ├── require.min.js
│ ├── sea.js
│ ├── seajs-main.js
│ ├── zepto.min.js
│ └── editormd
editormd
文件目录大致为
此目录是由官网直接在下来的压缩包解压后,对部分文件做了删减
引入
index.html
中只引入了核心样式以及脚本文件
<link rel="stylesheet" href="assets/plugins/editormd/css/editormd.min.css">
<link rel="stylesheet" href="assets/plugins/editormd/css/editormd.preview.min.css">
<script type="text/javascript" src="assets/plugins/jquery.min.js"></script>
<script defer type="text/javascript" src="assets/plugins/editormd/editormd.min.js"></script>
注册组件
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { AfterViewInit, OnDestroy, Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
declare const editormd: any;
@Component({
selector: 'app-editor-md',
template: `<div [id]="editorMdId"><textarea [placeholder]="placeholder" [(ngModel)]="editorContent"></textarea></div>`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => EditorMdComponent),
multi: true
}
]
})
export class EditorMdComponent implements AfterViewInit, OnDestroy, ControlValueAccessor {
editorMdId: string = '';
editorContent: string = '';
@Input() theme: string = 'twilight'; // editor.md的样式主题,对应 editormd/lib/codemirror/theme
@Input() disabled: boolean = false;
@Input() watch: boolean = true; // 实时预览
@Input() defaulted: 'markdown' | 'html' = 'markdown';
@Input() placeholder: string = '请输入内容(支持markdown格式)';
@Output() loaded = new EventEmitter<boolean>();
@Output() changed = new EventEmitter<string>()
// editormd 对象
private markdownEditor: any;
private editorCustomConfig = {
width: '100%',
height: 640,
path: 'assets/plugins/editormd/lib/', // editor.md的支持插件存放位置
fontSize: '14px',
codeFold: true,
syncScrolling: true,
saveHTMLToTextarea: true, // 保存 HTML 到 Textarea
searchReplace: true,
htmlDecode: 'style,script,iframe|on*', // 开启 HTML 标签解析,为了安全性,默认不开启
toolbar: true, // 工具栏
toolbarIcons: function () {
// 自定义工具栏
// return editormd.toolbarModes["mini"]; // full, simple, mini
return ["undo",
"redo",
"|",
"bold",
"del",
"italic",
"quote",
"ucwords",
"uppercase",
"lowercase",
"|",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"|",
"list-ul",
"list-ol",
"hr",
"|",
"link",
"reference-link",
"image",
"code",
"preformatted-text",
"code-block",
"table",
"datetime",
"html-entities",
"pagebreak",
"|",
"watch",
"preview",
"fullscreen",
"clear",
"search",];
},
previewCodeHighlight: true, // 关闭预览 HTML 的代码块高亮,默认开启
emoji: false, // 是否支持表情
taskList: true, // 是否支持任务列表
tocm: false, // Using [TOCM]
// tex: false, // 开启科学公式TeX语言支持,默认关闭
// flowChart: false, // 开启流程图支持,默认关闭
// sequenceDiagram: false, // 开启时序/序列图支持,默认关闭,
dialogLockScreen: true, // 设置弹出层对话框不锁屏,全局通用,默认为true
dialogShowMask: true, // 设置弹出层对话框显示透明遮罩层,全局通用,默认为true
dialogDraggable: true, // 设置弹出层对话框不可拖动,全局通用,默认为true
dialogMaskOpacity: 0.45, // 设置透明遮罩层的透明度,全局通用,默认值为0.1
dialogMaskBgColor: '#000', // 设置透明遮罩层的背景颜色,全局通用,默认为#fff
imageUpload: true, // 是否支持图片本地上传
imageFormats: ['jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'], // 支持的图片格式
imageUploadURL: '/fileOSS/editoruploader', // 上传附件的服务路径
};
private onChange = (_: any) => { };
private onTouched = () => { };
constructor() {
this.editorMdId = `MD${this.generateId(6, 16)}`;
}
ngAfterViewInit() {
this.editorStartup();
}
ngOnDestroy() {
if (this.markdownEditor) {
this.markdownEditor.editor.remove();
}
}
writeValue(obj: string): void {
this.editorContent = obj;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
private editorStartup(): void {
this.createEditor(this.editorCustomConfig);
}
private createEditor(editorConfig: any): void {
const that = this;
that.markdownEditor = editormd(that.editorMdId, {
...editorConfig,
watch: that.disabled ? true : that.watch,
theme: that.theme,
previewTheme: that.theme,
editorTheme: that.theme,
readOnly: that.disabled,
placeholder: that.placeholder,
onload: function () {
that.loaded.emit(true);
// 防止渲染为空的情况
if (that.editorContent) {
this.setMarkdown(that.editorContent);
}
if (that.disabled) {
this.previewing();
}
// this.resize("100%", 640);
// this.fullscreen();
// this.watch().fullscreen();
// this.width("100%");
// this.height(480);
},
onchange: function () {
if (that.defaulted == 'html') {
that.onChange(this.getHTML());
that.changed.emit(this.getHTML());
} else {
that.onChange(this.getMarkdown());
that.changed.emit(this.getMarkdown());
}
},
});
}
/**
* ID生成器
* @param len 生成的ID的长度
* @param radix 生成的ID的混淆器长度
* @returns
*/
private generateId(len: number, radix: number) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
var uuid = [],
i;
radix = radix || chars.length;
if (len) {
// Compact form
for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
} else {
// rfc4122, version 4 form
var r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | (Math.random() * 16);
uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
}
结语
以上代码便是 editor.md
的一个简单封装,简单的使用就可以是 <app-editor-md placeholder="请输入内容" [(ngModel)]="content"></app-editor-md>
,如非不需要form表单绑定,可以再行变更。
如有问题,还请提出,我会及时改正。