個人網站建站日記-集成Markdown編輯器
一次偶然的機會,我體驗的到了markdown的便捷,于是乎,我就著手給我的網站閑蛋博客社區集成了Markdown,現在可以自由的切換Markdown與富文本編輯的使用了。這里我特此分享記錄下安裝使用的過程。
一、安裝Markdown編輯器
這里我采用的是md-editor-v3編輯器,目前看來還是很好用的,安裝方便,使用簡單
二 pnpm安裝 pnpm install md-editor-v3
注意,直接運行的是安裝的最新版的,最新版本的使用的vue3.5以上,如果你低于3.5的版本,代碼運行的時候可能會報錯,所以安裝的是其它的版本
pnpm install md-editor-v3@4.21.1
三、頁面基本使用
話不多說,直接看代碼
<template>
<MdEditor :autoFocus="true" v-model="textContent" :toolbars="toolbars">
</MdEditor>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { MdEditor, DropdownToolbar, ToolbarNames, config } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const { checkImg } = useUpload();
const toolbars: ToolbarNames[] = [
'bold',
'underline',
'italic',
'-',
'title',
'strikeThrough',
'sub',
'sup',
'quote',
'unorderedList',
'orderedList',
'task',
'-',
'codeRow',
'code',
'link',
'image',
'table',
'mermaid',
'katex',
'-',
'revoke',
'next',
'=',
'pageFullscreen',
'fullscreen',
'preview',
'htmlPreview',
'catalog',
'github'
];
</script>
讓后運行項目如下圖

這樣就可以了,但是我們看官網,它是可以支持切換主題的,那么怎實現呢。
四、編輯器切換主題
實現它就是去使用它的#defToolbars插槽,可以實現
實現切換預覽主題
我這里只是默認使用它里面提供的幾個主題,定義預覽主題如下:
const previewThemeOptions = [
{
value: 'default',
label: 'default'
},
{
value: 'github',
label: 'github'
},
{
value: 'vuepress',
label: 'vuepress'
},
{
value: 'mk-cute',
label: 'mk-cute'
},
{
value: 'smart-blue',
label: 'smart-blue'
},
{
value: 'cyanosis',
label: 'cyanosis'
}
];
然后插槽里面的代碼
<MdEditor :autoFocus="true" v-model="textContent" :previewTheme="previewThemeSelected"
:toolbars="toolbars">
<template #defToolbars>
<DropdownToolbar title="預覽主題" :visible="showPreviewTheme" :on-change="appendixPreviewThemeChanged">
<template #overlay>
<el-select v-model="previewThemeSelected" size="small" style="width: 70px"
@change="previewThemeChange">
<el-option v-for="item in previewThemeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</template>
<template #trigger>
<el-icon class="md-editor-icon" :size="18">
<Platform />
</el-icon>
</template>
</DropdownToolbar>
</template>
</MdEditor>
同時在toolbars里面要加一個對應的索引位置,代表自己的工具欄

然后運行看下代碼部分截圖
default主題

github主題

smart-blue主題

以此類推,其它的主題我就不演示了
圖片上傳
圖片上傳要實現它的 @on-upload-img="onUploadImg"方法,參考代碼如下,也可以參考官網的寫法,比較簡單
function onUploadImg(files, callback) {
files.forEach((s) => {
let file = s;
let formData = new FormData();
formData.append('file', file);
uploadFileApi(formData).then((res) => {
let arr = [];
arr.push(res.urlPath);
callback(arr);
});
});
}
內容超鏈接target屬性
如果想實現target屬性需要安裝 markdown-it-link-attributes 插件,讓后代碼加入如下代碼
import LinkAttr from 'markdown-it-link-attributes';
config({
markdownItPlugins(plugins) {
return [
...plugins,
{
type: 'linkAttr',
plugin: LinkAttr,
options: {
matcher(href: string) {
return !href.startsWith('#');
},
attrs: {
target: '_blank'
}
}
}
];
}
});
應該沒有什么還有補充了??
五、頁面如何渲染
上面搞的差不多后,就要實現文章頁面渲染,我看網上的解決方法都是通過安裝marked插件,然后通過它把markdown語法轉成html,但是我沒有使用。因為我看md-editor-v3已經實現了預覽,并且非常簡單。就是使用MdPreview組件就好了,也不需要額外安裝調樣式。
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
<MdPreview v-else v-model="articleContent" :previewTheme="預覽主題"
:codeTheme="代碼主題" />
然后頁面加載渲染就可以了

真實效果可以點這里 https://www.xiandanplay.com/article/view?id=17143377224138752&articleCategoryId=16078840161206272
六、代碼示例
以下代碼僅僅是我的業務代碼,可以參考,具體的可以根據需要自行更改
<template>
<MdEditor :autoFocus="true" v-model="textContent" :previewTheme="previewThemeSelected"
:codeTheme="codeThemeSelected" @on-upload-img="onUploadImg" :toolbars="toolbars">
<template #defToolbars>
<DropdownToolbar title="預覽主題" :visible="showPreviewTheme" :on-change="appendixPreviewThemeChanged">
<template #overlay>
<el-select v-model="previewThemeSelected" size="small" style="width: 70px"
@change="previewThemeChange">
<el-option v-for="item in previewThemeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</template>
<template #trigger>
<el-icon class="md-editor-icon" :size="18">
<Platform />
</el-icon>
</template>
</DropdownToolbar>
<DropdownToolbar title="代碼主題" :visible="showCodeTheme" :on-change="appendixCodeThemeChanged">
<template #overlay>
<el-select v-model="codeThemeSelected" size="small" style="width: 70px" @change="codeThemeChange">
<el-option v-for="item in codeThemeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</template>
<template #trigger>
<el-icon class="md-editor-icon" :size="18">
<Postcard />
</el-icon>
</template>
</DropdownToolbar>
</template>
</MdEditor>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { MdEditor, DropdownToolbar, ToolbarNames, config } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import { Marked } from 'marked';
import { useUpload } from '@/hooks/useUpload';
import { uploadFileApi } from '@/api/uploadFile';
import { CloudStorageType } from '@/utils/globalDeclare';
import LinkAttr from 'markdown-it-link-attributes';
config({
markdownItPlugins(plugins) {
return [
...plugins,
{
type: 'linkAttr',
plugin: LinkAttr,
options: {
matcher(href: string) {
return !href.startsWith('#');
},
attrs: {
target: '_blank'
}
}
}
];
}
});
const { checkImg } = useUpload();
const marked = new Marked({ gfm: true });
const props = defineProps({
codeTheme: {
type: String,
default: 'default'
},
previewTheme: {
type: String,
default: 'default'
}
});
const toolbars: ToolbarNames[] = [
'bold',
'underline',
'italic',
'-',
'title',
'strikeThrough',
'sub',
'sup',
'quote',
'unorderedList',
'orderedList',
'task',
'-',
'codeRow',
'code',
'link',
'image',
'table',
'mermaid',
'katex',
'-',
'revoke',
'next',
0,
1,
'=',
'pageFullscreen',
'fullscreen',
'preview',
'htmlPreview',
'catalog',
'github'
];
const textContent = ref<string>();
const emit = defineEmits(['changeTheme']);
const showPreviewTheme = ref(false);
const showCodeTheme = ref(false);
const previewThemeOptions = [
{
value: 'default',
label: 'default'
},
{
value: 'github',
label: 'github'
},
{
value: 'vuepress',
label: 'vuepress'
},
{
value: 'mk-cute',
label: 'mk-cute'
},
{
value: 'smart-blue',
label: 'smart-blue'
},
{
value: 'cyanosis',
label: 'cyanosis'
}
];
const codeThemeOptions = [
{
value: 'atom',
label: 'atom'
},
{
value: 'a11y',
label: 'a11y'
},
{
value: 'github',
label: 'github'
},
{
value: 'gradient',
label: 'gradient'
},
{
value: 'kimbie',
label: 'kimbie'
},
{
value: 'paraiso',
label: 'paraiso'
},
{
value: 'qtcreator',
label: 'qtcreator'
},
{
value: 'stackoverflow',
label: 'stackoverflow'
}
];
const previewThemeSelected = ref<string>(props.previewTheme);
const codeThemeSelected = ref<string>(props.codeTheme);
init();
function init() {
let themeStore = localStorage.getItem('mdv3_theme_store');
if (themeStore) {
let arr = themeStore.split('|');
codeThemeSelected.value = arr[0];
previewThemeSelected.value = arr[1];
emit('changeTheme', {
codeTheme: codeThemeSelected.value,
previewTheme: previewThemeSelected.value
});
}
}
function previewThemeChange(selected) {
setThemeStore('preview', selected);
}
function codeThemeChange(selected) {
setThemeStore('code', selected);
}
function setThemeStore(themeType, themeSelected) {
let theme: any = {};
if (themeType == 'code') {
localStorage.setItem(
'mdv3_theme_store',
themeSelected + '|' + previewThemeSelected.value
);
theme.codeTheme = themeSelected;
codeThemeSelected.value = themeSelected;
theme.previewTheme = previewThemeSelected.value;
} else {
localStorage.setItem(
'mdv3_theme_store',
codeThemeSelected.value + '|' + themeSelected
);
theme.codeTheme = codeThemeSelected.value;
theme.previewTheme = themeSelected;
previewThemeSelected.value = themeSelected;
}
emit('changeTheme', theme);
}
function appendixPreviewThemeChanged() {
if (showPreviewTheme.value) {
showPreviewTheme.value = false;
} else {
showPreviewTheme.value = true;
}
}
function appendixCodeThemeChanged() {
if (showCodeTheme.value) {
showCodeTheme.value = false;
} else {
showCodeTheme.value = true;
}
}
function onUploadImg(files, callback) {
files.forEach((s) => {
let file = s;
let result: boolean = checkImg(file, 2);
if (result == false) {
return;
}
let formData = new FormData();
formData.append('file', file);
formData.append('cloudStorageType', CloudStorageType.Qiniu);
uploadFileApi(formData).then((res) => {
let arr = [];
arr.push(res.urlPath);
callback(arr);
});
});
}
function setContent(content) {
textContent.value = content;
}
function getTheme() {
let theme = {
codeTheme: codeThemeSelected.value,
previewTheme: previewThemeSelected.value
};
return theme;
}
function getText() {
const plainText = marked
.parse(textContent.value)
.replace(/<\/?[^>]+(>|$)/g, '')
.trim();
return plainText;
}
const getContent = () => {
return textContent.value
};
defineExpose({ getText, getTheme, setContent, getContent });
</script>
作者:程序員奶牛
個人開源網站:https://www.xiandanplay.com
源碼地址:https://gitee.com/MrHanchichi/xian-dan

浙公網安備 33010602011771號