vue3.0集成markdown编辑器、预览功能

您所在的位置:网站首页 markdown网站渲染 vue3.0集成markdown编辑器、预览功能

vue3.0集成markdown编辑器、预览功能

2023-06-24 22:52| 来源: 网络整理| 查看: 265

vue3.0集成markdown编辑器、预览功能 一、安装 # 使用 npm npm i @kangc/v-md-editor@next -S # 使用 yarn yarn add @kangc/v-md-editor@next 二、封装 1、项目中创建plugins文件夹 2、在 plugins 文件夹下创建 mdEditor 文件夹 3、在 mdEditor 文件夹下创建 index.js

以下为 mdEditor/index.js 内容

// 引入markdown编辑器 import VueMarkdownEditor from '@kangc/v-md-editor'; import '@kangc/v-md-editor/lib/style/base-editor.css'; // 因纳入预览插件 import VMdPreview from '@kangc/v-md-editor/lib/preview'; import '@kangc/v-md-editor/lib/style/preview.css'; //使用的是vuepress主题 import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'; import '@kangc/v-md-editor/lib/theme/style/vuepress.css'; // 引入表情插件 // import createEmojiPlugin from '@kangc/v-md-editor/lib/plugins/emoji/index'; // import '@kangc/v-md-editor/lib/plugins/emoji/emoji.css'; // 引入对齐方式插件 import createAlignPlugin from '@kangc/v-md-editor/lib/plugins/align'; // 代码高亮工具 import Prism from 'prismjs'; // markdown 编辑器注册主题、代码高亮、表情插件、对齐方式插件 VueMarkdownEditor.use(vuepressTheme, { Prism }) // .use(createEmojiPlugin()) .use(createAlignPlugin()); // markdown 预览注册主题、代码高亮、对齐方式插件 VMdPreview.use(vuepressTheme, { Prism }).use(createAlignPlugin()); export { VueMarkdownEditor, VMdPreview }; 4、在 plugins 文件夹下创建 index.js

以下为 plugins /index.js 内容

import { VueMarkdownEditor, VMdPreview } from './mdEditor/index'; const pluginList = [ VueMarkdownEditor, VMdPreview // 如果有使用到别的插件,引入放在这个位置就好 ]; const plugins = { install(app) { // 批量注册插件 改用自动引入 Object.entries(pluginList).forEach(([, v]) => { app.use(v); }); } }; export default plugins; 三、全局注册

在 main.js 文件中引入 plugins

import plugins from './plugins'; 四、封装 markdown 编辑器 import { computed, ref, reactive } from 'vue'; import VueMarkdownEditor from '@kangc/v-md-editor'; import { uploadFile } from '@/api'; // import htmlToPdf from '@/utils/htmlToPdf'; const emit = defineEmits(['update:modelValue', 'change', 'save']); const props = defineProps({ height: { type: String, default: '100%' }, placeholder: { type: String, default: '请输入内容' } }); const leftToolbar = ref( 'undo redo clear | h bold italic strikethrough quote| customToolbar2 | ul ol table hr | emoji link image code | save' ); // undo redo clear | h bold italic strikethrough quote | ul ol table hr | emoji link image code | save exportPDF const toolbar = reactive({ exportPDF: { icon: 'v-md-icon-arrow-down', title: '导出PDF', action(editor) { // toolbar点击时触发的函数 console.log(editor); // editor.save(); } }, customToolbar2: { title: '对齐方式', icon: 'icon iconfont v-md-icon-align', menus: [ { name: '左对齐', text: '左对齐', action(editor) { editor.insert(function (selected) { const prefix = '::: align-left\n'; const suffix = ':::'; const placeholder = '请输入文本'; const content = selected || placeholder; return { text: `${prefix}${content}\n${suffix}`, selected: content }; }); } }, { name: '居中对齐', text: '居中对齐', action(editor) { editor.insert(function (selected) { const prefix = '::: align-center\n'; const suffix = ':::'; const placeholder = '请输入文本'; const content = selected || placeholder; return { text: `${prefix}${content}\n${suffix}`, selected: content }; }); } }, { name: '右对齐', text: '右对齐', action(editor) { editor.insert(function (selected) { const prefix = '::: align-right\n'; const suffix = ':::'; const placeholder = '请输入文本'; const content = selected || placeholder; return { text: `${prefix}${content}\n${suffix}`, selected: content }; }); } } ] } }); const newValue = computed({ get() { return props.modelValue; }, set(value) { emit('update:modelValue', value); } }); // 内容变化时触发的事件,text 为输入的内容,html 为解析之后的 html 字符串 const handleChange = (text, html) => { const data = { text, html }; emit('change', data); }; // 上传本地图片,获取图片url, 插入编辑器 const handleUploadImage = async (event, insertImage, files) => { const file = files[0]; const name = file.name.split('.')[0]; const url = await uploadImage(file); insertImg(insertImage, url, name); }; // 向编辑器插入图片 const insertImg = (insertImage, url, name) => { insertImage({ url, desc: name // width: 'auto', // height: 'auto', }); }; // 上传图片 - 接口 const uploadImage = async file => { try { let imgUrl; const formData = new FormData(); formData.append('file', file); const { code, data, message } = await uploadFile(formData); if (code === 200) { imgUrl = data; } else { console.log(message); } return imgUrl; } catch (error) { console.log(error); } }; // 保存 const handleSave = (text, html) => { const data = { text, html }; emit('save', data); }; @import url('@/styles/fonts/markdown-ext-font-icon/iconfont.css'); 五、封装 markdown 预览 {{ anchor.title }} import { ref, nextTick, watch } from 'vue'; import { getDocumentContent } from '@/api/document/document'; const props = defineProps({ height: String, currentId: [String, Number] }); const text = ref(``); let titles = ref(null); const previewContainer = ref(); const preview = ref(); const generateTitles = () => { const anchors = preview.value.$el.querySelectorAll('h1,h2,h3,h4,h5,h6'); titles.value = Array.from(anchors).filter(title => !!title.innerText.trim()); if (!titles.value.length) { titles.value = []; return; } const hTags = Array.from(new Set(titles.value.map(title => title.tagName))).sort(); titles.value = titles.value.map(el => ({ title: el.innerText, lineIndex: el.getAttribute('data-v-md-line'), indent: hTags.indexOf(el.tagName) })); }; // 获取文档内容接口 const getDocument = async () => { try { const { code, data } = await getDocumentContent(props.currentId); if (code === 200) { text.value = data || ''; nextTick(() => { generateTitles(); }); } } catch (error) { console.log(error); } }; // 导航跳转 const handleAnchorClick = anchor => { const { lineIndex } = anchor; const heading = preview.value.$el.querySelector(`[data-v-md-line="${lineIndex}"]`); if (heading) { preview.value.scrollToTarget({ target: heading, scrollContainer: previewContainer.value, top: 60 }); } }; watch( () => props.currentId, value => { if (value) { getDocument(); } }, { deep: true, immediate: true } ); .container { display: flex; height: calc(100vh - 144px); border-radius: 6px; overflow: hidden; .nav, .preview { height: 100%; background: #f2f3f7; overflow-y: auto; } .nav { width: 300px; padding: 20px; border-right: 1px solid #eee; } .preview { width: 100%; padding-left: 10px; box-sizing: border-box; } :deep(.vuepress-markdown-body) { background: #f2f3f7; } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3