NoteZ_技术博客 NoteZ_技术博客
🏠 首页
  • 📚 Web技术
  • 📋 Npm笔记
  • 📑 Markdown
  • 📄 Git笔记
  • 📝 Nginx文档
  • 📓 Linux文档
  • 📖 技术文档
  • 📜 其他文档
  • 🧊 NodeJs
  • 🎡 Express
  • 🔥 Rust
  • 🎉 Koa2
  • 🍃 MongoDB
  • 🐬 MySql
  • 🥦 Oracle
  • 🍁 Python
  • 🍄 JavaScript
  • 🌰 CSS
  • 🧄 HTML
  • 🥑 Canvas
  • 🌽 Nuxt
  • 🍆 React
  • 🥜 Vue
  • 🧅 TypeScript
  • 🌶️ AI
  • 📘 分类
  • 📗 标签
  • 📙 归档
⚜️ 在线编辑 (opens new window)
  • 📁 站点收藏
  • 📦 前端组件库
  • 📊 数据可视化
  • 🌈 开源插件
  • 🎗️ 关于我
  • 🔗 友情链接
GitHub (opens new window)

NoteZ_技术博客

前端界的小学生
🏠 首页
  • 📚 Web技术
  • 📋 Npm笔记
  • 📑 Markdown
  • 📄 Git笔记
  • 📝 Nginx文档
  • 📓 Linux文档
  • 📖 技术文档
  • 📜 其他文档
  • 🧊 NodeJs
  • 🎡 Express
  • 🔥 Rust
  • 🎉 Koa2
  • 🍃 MongoDB
  • 🐬 MySql
  • 🥦 Oracle
  • 🍁 Python
  • 🍄 JavaScript
  • 🌰 CSS
  • 🧄 HTML
  • 🥑 Canvas
  • 🌽 Nuxt
  • 🍆 React
  • 🥜 Vue
  • 🧅 TypeScript
  • 🌶️ AI
  • 📘 分类
  • 📗 标签
  • 📙 归档
⚜️ 在线编辑 (opens new window)
  • 📁 站点收藏
  • 📦 前端组件库
  • 📊 数据可视化
  • 🌈 开源插件
  • 🎗️ 关于我
  • 🔗 友情链接
GitHub (opens new window)
  • JavaScript笔记

  • CSS笔记

  • HTML笔记

  • Canvas笔记

  • Nuxt笔记

  • React笔记

  • Vue笔记

    • markdown(md-editor-v3)简单使用
      • 功能一览
      • 预览图
      • apis
        • props
        • 事件绑定
        • 快捷键
      • 演示
        • jsx 语法项目
        • vue 模板项目
        • 上传图片
      • 示例模板代码
    • Prop 验证 与 非 Prop 的 Attribute
    • transition-group列表过渡
    • transition过渡&动画
    • vetur 中 eslint 的问题
    • vite 打包时内存溢出处理方案
    • Vite2 + Vue3添加svg的使用并通过svg实现自定义Icon组件
    • Vite项目构建优化之移除console.log和其他生产日志
    • vsCode 之 Monaco Editor 在 vue-cli3.0 中的使用方法
    • vue cli3 中的使用Luckysheet和Luckyexcel
    • vue 与 Koa 上传示例代码
    • vue 中 axios 如何取消请求,取消前面一个或多个请求
    • Vue 中 node-sass 安装失败简单解决方案
    • vue 中使用 pinia 状态管理教程
    • vue 中使用 web worker
    • vue 中封装 Axios 和 管理 API 接口
    • vue 中的 hash 与 history 两种路由模式
    • Vue 中的鼠标事件汇总
    • Vue 实现 Base64 图片上传
    • vue 强制刷新组件
    • Vue 组件中 v-mode 的本质以及实现方式
    • vue 组件中的.native 原生事件(@click.native)
    • Vue 组件中的.native原生事件(@click.native)
    • Vue 页面路由(router)权限控制和登陆验证
    • Vue 项目中如何在打包时清除 console 和 debugger
    • Vue 项目打包去除 .map 文件方法
    • Vue 预渲染之 prerender-spa-plugin 的配置使用
    • vue-cli 2.x和3.x配置移动端适配px自动转为rem
    • vue-cli3 如何实现pc端自适应
    • vue-cli3.0 开启 Gzip 压缩方法
    • Vue-cli3.0 监听路由变化的几种方式
    • Vue-cli3.0 结合 axios 解决生产环境中的跨域问题
    • vue-cli3.0移动端项目将px转换成rem
    • vue-cli3.x项目兼容ie解决方案
    • vue-router传递参数的几种方式
    • Vue2-ace-editor 基本使用方法
    • vue2.x 插槽slot使用总结
    • Vue2.x 组件通信方式
    • vue3 + vite 自适应(rem 适配)方法
    • vue3+vite:本地代理,配置proxy
    • Vue3.0 基础知识点总结
    • Vue3.0 挂载方法,添加全局属性
    • vue3.0之全局变量app.config.globalProperties的使用
    • vue3.2+elelmenui-plus+vite 按需导入ui组件库
    • vue3.x 中使用 ol (openlayer)地图
    • Vue3.x 组件通信方式
    • Vue3全局变量的定义和使用
    • Vuex使用记录
    • vue中使用html2canvas将html页面转为图片并且下载该图片
    • vue中父子组件之间的通信
    • Vue中的防抖函数封装和使用
    • Vue中配合clipboard.js实现点击按钮复制内容到剪切板
    • vue使用monaco editor时报错 Unexpected usage at EditorSimpleWorker.loadForeignModule
    • Vue列表渲染之数组、对象更新检测
    • Vue实现下拉滚动加载刷新功能
    • Vue开发中安装库经常报错
    • Vue技术框架
    • Vue生命周期
    • Vue监听store中数据变化的两种方式
    • Vue非父子组件之间通信的几种方式
    • Vue项目 解决 vuex 在页面刷新后数据丢失的问题
    • vue项目nginx部署子目录_vue 多项目部署_二级目录 copy
    • 使用 monaco-editor 汉化 右键菜单汉化
    • 使用 vite 脚手架创建 vue3 项目
    • 使用animate库
    • 使用vue实现鼠标框选文件的功能
    • 使用vue的transition标签结合css样式完成动画
    • 关于Vue项目优化方案总结
    • 关于解决vue-cli2.xx、vue-cli3.xx项目在ie中白屏的方案总结
    • 利用create-nuxt-app脚手架创建NuxtJS应用
    • 国际化插件 vue-i18n 使用方法
    • 在 vite-vue3.x 中的使用 vscode monaco-editor 方法
    • 在 Vue 中 如何使用 jsencrypt 实现 Rsa 进行加密
    • 在 vue 中使用 jsPlumb 总结
    • 在 vue 中使用 v-viewer 预览图方法
    • 在 Vue 中读取本地文本文件(兼容各种浏览器)
    • 在 vue 或 vuex 中如何使用 axios 进行请求操作
    • 在 vue 项目中 mock 的基本使用方法
    • 在 vue-cli3 项目更改 favicon 图标
    • 在 vue-cli3.0 中使用 Sass 全局变量
    • 在vue3+vite中使用markdown(v-md-editor)编辑器
    • 在vue中如何使用watch深度监听对象中值的变化
    • 基于 vue 列表拖拽排序
    • 基于 vue 图片裁剪(vue-cropper)
    • 基于 vue 的 CodeMirror 代码编辑器
    • 如何在 vue 中使用 swiper 插件创建轮播图
    • 如何在 vue 中实现代码高亮?
    • 如何在 Vue-cli3.0 中使用 prerender-spa-plugin 进行预渲染
    • 如何用 vue 实现一个虚拟列表
    • 清除项目中无用的console.log代码方法
    • 解决vue-cli3.0中提示syntax 'classProperties' isn't currently enabled的错误
    • 记录vite打包vue项目内存溢出问题及解决方法
    • Vite中使用 svg-sprite-loader加载svg文件
    • vite引入svg图片
    • vue 使用CompressionPlugin压缩解决打包后文件过大的问题
    • vue2.x递归组件 树形组件
    • vue3.x递归组件 树形组件
    • 使用Vite快速构建Vue3+ts+pinia脚手架
    • 启动vue项目时报错digital envelope routines__unsupported
    • Pinia 快速入门(使用教程)
    • 关于 Vite Vue 辨别当前所处于什么(生产、开发)环境
  • TypeScript笔记

  • AI相关笔记

  • 开发文档
  • Vue笔记
NoteZ
2022-07-23
目录

markdown(md-editor-v3)简单使用

# md-editor-v3

vue3 项目下的 Markdown 编辑器,,使用 jsx 语法开发,支持在 tsx 项目使用。采用了替换 class 名称的方式实现暗黑主题切换。

文档与在线预览:传送门 (opens new window)

在线尝试示例:传送门 (opens new window)

同系列react版本:md-editor-rt (opens new window)

# 功能一览

  1. 快捷插入内容工具栏、编辑器浏览器全屏、页面内全屏等;
  2. 内置的白色主题和暗黑主题,支持绑定切换;
  3. 支持快捷键插入内容;
  4. 支持使用 prettier 格式化内容(使用 CDN 方式引入,只支持格式化 md 内容,可在代码内设置关闭);
  5. 支持多语言,支持自行扩展语言;
  6. 支持复制粘贴上传图片,图片裁剪上传;
  7. 支持仅预览模式(不显示编辑器,只显示 md 预览内容,无额外监听)
  8. ...

更多功能待后续更新,若有想要的功能未开发,请留言~

# 预览图

默认模式下:

暗黑模式下:

# apis

# props

名称 类型 默认值 响应式 说明
modelValue String '' √ md 编辑内容,vue 模板支持双向绑定(v-model="value")
theme 'light' | 'dark' 'light' √ 主题切换
editorClass String '' √ 编辑器最外层样式
hljs Object null x 项目中使用到了 highlight,可将实例直接传递,生产环境则不会请求 CDN,需要手动导入支持的高亮代码样式
highlightJs String highlight.js@11.2.0 (opens new window) x highlightJs CDN
highlightCss String atom-one-dark@11.2.0 (opens new window) x 预览高亮代码样式
historyLength Number 10 x 最大记录操作数(太大会占用内存)
pageFullScreen Boolean false x 浏览器内全屏
preview Boolean true x 预览模式
htmlPreview Boolean false x html 预览
previewOnlyv1.3.0 Boolean false x 仅预览模式,不显示 bar 和编辑框,不支持响应式,仅能初始设置一次
language String 'zh-CN' √ 内置中英文('zh-CN','en-US'),可自行扩展其他语言,同时可覆盖内置的中英文
languageUserDefined Array [{key: StaticTextDefaultValue}] √ 通过这里扩展语言,修改 language 值为扩展 key 即可,类型申明可手动导入
toolbars Array [all] √ 选择性展示工具栏,可选内容如下[toolbars]
toolbarsExcludev1.1.4 Array [] √ 选择性不展示工具栏,内容同toolbars
prettier Boolean true x 是否启用 prettier 优化 md 内容
prettierCDN String standalone@2.4.0 (opens new window) x
prettierMDCDN String parser-markdown@2.4.0 (opens new window) x
editorNamev1.3.2delete String 'editor' x 当在同一页面放置了多个编辑器,最好提供该属性以区别某些带有 ID 的内容,v1.3.2 后版本编辑器自动生成唯一 ID,不再需要手动设置
cropperCssv1.2.0 String cropper.min.css@1.5.12 (opens new window) x cropper css url
cropperJsv1.2.0 String cropper.min.js@1.5.12 (opens new window) x cropper js url
iconfontJsv1.3.2 String iconfont (opens new window) x 矢量图标链接,无外网时,下载 js 到内网,提供链接
editorIdv1.4.0 String random x 编辑器唯一标识,非必须项,用于后续支持ssr时,防止产生服务端与客户端渲染内容不一致错误提示
tabWidthv1.4.0 Number 2 x 编辑器 TAB 键位等于空格数
showCodeRowNumberv1.4.3 (opens new window) Boolean false x 代码块显示行号
screenfullv1.4.3 (opens new window) Object null x 全屏插件实例,项目中有使用可以将其传入,这样编辑器不再会使用 cdn 引入
screenfullJsv1.4.3 (opens new window) String 5.1.0 (opens new window) x cdn 链接
previewThemev1.4.3 (opens new window) 'default' | 'github' | 'vuepress' 'default' √ 预览内容主题

响应式=x,该属性只支持设置,不支持响应式更新~

[toolbars]

[
  'bold',
  'underline',
  'italic',
  'strikeThrough',
  'title',
  'sub',
  'sup',
  'quote',
  'unorderedList',
  'orderedList',
  'codeRow',
  'code',
  'link',
  'image',
  'table',
  'revoke',
  'next',
  'save',
  'pageFullscreen',
  'fullscreen',
  'preview',
  'htmlPreview',
  'github'
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

自定义语言,需要替换的内容如下(某些字段若不主动提供,可能会造成页面不美观):

[StaticTextDefaultValue]

export interface StaticTextDefaultValue {
  // 工具栏hover title提示
  toolbarTips?: ToolbarTips;
  // 标题下拉框内容
  titleItem?: {
    h1?: string;
    h2?: string;
    h3?: string;
    h4?: string;
    h5?: string;
    h6?: string;
  };
  // 添加链接或图片时弹窗提示
  linkModalTips?: {
    title?: string;
    descLable?: string;
    descLablePlaceHolder?: string;
    urlLable?: string;
    UrlLablePlaceHolder?: string;
    buttonOK?: string;
    buttonUpload?: string;
  };
  // 裁剪图片弹窗提示,v1.2.0
  clipModalTips?: {
    title?: string;
    buttonUpload?: string;
  };
  // 预览代码中复制代码提示,v1.1.4
  copyCode?: {
    text?: string;
    tips?: string;
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 事件绑定

名称 入参 说明
onChange v:String 内容变化事件(当前与textare的oninput事件绑定,每输入一个单字即会触发)
onSave v:String 保存事件,快捷键与保存按钮均会触发
onUploadImg files:Array, callback:Function 上传图片事件,弹窗会等待上传结果,务必将上传后的 urls 作为 callback 入参回传
onHtmlChanged h:String html 变化回调事件,用于获取预览 html 代码
onGetCatalogv1.4.0 list: HeadList[] 动态获取markdown目录

# 快捷键

主要以CTRL搭配对应功能英文单词首字母,冲突项添加SHIFT,再冲突替换为ALT。

键位 功能 说明 版本标记
TAB 空格 通过tabWidth属性预设 TAB 键位新增空格长度,默认 2,支持多行 v1.4.0
SHIFT + TAB 取消空格 同上,一次取消两个空格,支持多行 v1.4.0
CTRL + C 复制 选中时复制选中内容,未选中时复制当前行内容 v1.4.0
CTRL + X 剪切 选中时剪切选中内容,未选中时剪切当前行 v1.4.0
CTRL + D 删除 选中时删除选中内容,未选中时删除当前行 v1.4.0
CTRL + S 保存 触发编辑器的onSave回调 v1.0.0
CTRL + B 加粗 **加粗** v1.0.0
CTRL + U 下划线 <u>下划线</u> v1.0.0
CTRL + I 斜体 *斜体* v1.0.0
CTRL + 1-6 1-6 级标题 # 标题 v1.0.0
CTRL + ↑ 上角标 <sup>上角标</sup> v1.0.0
CTRL + ↓ 下角标 <sub>下角标</sub> v1.0.0
CTRL + Q 引用 > 引用 v1.0.0
CTRL + O 有序列表 1. 有序列表 v1.0.0
CTRL + L 链接 [链接](https://imzbf.cc) v1.0.0
CTRL + Z 撤回 触发编辑器内内容撤回,与系统无关 v1.0.0
CTRL + SHIFT + S 删除线 ~删除线~ v1.0.0
CTRL + SHIFT + U 无序列表 - 无序列表 v1.0.0
CTRL + SHIFT + C 块级代码 多行代码块 v1.0.0
CTRL + SHIFT + I 图片链接 ![图片](https://imzbf.cc) v1.0.0
CTRL + SHIFT + Z 前进一步 触发编辑器内内容前进,与系统无关 v1.0.0
CTRL + SHIFT + F 美化内容 v1.0.0
CTRL + ALT + C 行内代码 行内代码块 v1.0.0
CTRL + SHIFT + ALT + T 表格 \|表格\| v1.4.0

# 演示

# jsx 语法项目

import { defineComponent, reactive } from 'vue';
import Editor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';

export default defineComponent({
  setup() {
    const md = reactive({
      text: 'default markdown content'
    });
    return () => (
      <Editor hljs={hljs} modelValue={md.text} onChange={(value) => (md.text = value)} />
    );
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# vue 模板项目

<template>
  <editor v-model="text" pageFullScreen></editor>
</template>

<script>
import { defineComponent } from 'vue';
import Editor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

export default defineComponent({
  name: 'VueTemplateDemo',
  components: { Editor },
  data() {
    return {
      text: '默认值'
    };
  }
});
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 上传图片

默认可以选择多张图片,支持粘贴板上传图片。

注意:粘贴板上传时,如果是网页上的 gif 图,无法正确上传为 gif 格式!

async onUploadImg(files: Array<File>, callback: (urls: string[]) => void) {
  const res = await Promise.all(
    files.map((file) => {
      return new Promise((rev, rej) => {
        const form = new FormData();
        form.append('file', file);

        axios
          .post('/api/img/upload', form, {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          })
          .then((res) => rev(res))
          .catch((error) => rej(error));
      });
    })
  );

  callback(res.map((item: any) => item.data.url));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 示例模板代码

<template>
  <div class="notez-markdown" ref="editorDom" tabindex="1">
    <MdEditorV3
      @onUploadImg="handleOnUploadImg"
      v-model="content"
      :editor-id="editorId"
      :preview-theme="previewTheme"
      :toolbars="store.state.toolbars"
      :previewOnly="previewOnly"
      placeholder="请输入内容..."
      :preview="true"
    >
      <template #defToolbars>
        <MdEditorV3.DropdownToolbar
          title="emoji"
          :visible="data.emojiVisible"
          :onChange="emojiVisibleChanged"
        >
          <template #overlay>
            <div class="emoji-container">
              <ol class="emojis-box">
                <li
                  v-for="(item, i) in store.state.emojis"
                  :key="i"
                  @click="handlerEmoji(item)"
                >
                  {{ item }}
                </li>
              </ol>
            </div>
          </template>
          <template #trigger>
            <svg class="md-icon biao-qing" viewBox="0 0 1024 1024">
              <path
                d="M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m0-102.4c226.742857 0 409.6-182.857143 409.6-409.6S738.742857 102.4 512 102.4 102.4 285.257143 102.4 512s182.857143 409.6 409.6 409.6z m-204.8-358.4h409.6c0 113.371429-91.428571 204.8-204.8 204.8s-204.8-91.428571-204.8-204.8z m0-102.4c-43.885714 0-76.8-32.914286-76.8-76.8s32.914286-76.8 76.8-76.8 76.8 32.914286 76.8 76.8-32.914286 76.8-76.8 76.8z m409.6 0c-43.885714 0-76.8-32.914286-76.8-76.8s32.914286-76.8 76.8-76.8c43.885714 0 76.8 32.914286 76.8 76.8s-32.914286 76.8-76.8 76.8z"
              ></path>
            </svg>
          </template>
        </MdEditorV3.DropdownToolbar>
        <MdEditorV3.NormalToolbar title="标记" @on-click="handlerOnMark">
          <template #trigger>
            <svg
              class="md-icon mark"
              viewBox="0 0 1024 1024"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                fill="currentColor"
                d="M256 128v698.88l196.032-156.864a96 96 0 0 1 119.936 0L768 826.816V128H256zm-32-64h576a32 32 0 0 1 32 32v797.44a32 32 0 0 1-51.968 24.96L531.968 720a32 32 0 0 0-39.936 0L243.968 918.4A32 32 0 0 1 192 893.44V96a32 32 0 0 1 32-32z"
              ></path>
            </svg>
          </template>
        </MdEditorV3.NormalToolbar>
      </template>
    </MdEditorV3>
  </div>
</template> 
<script setup>
import {
  // computed,
  // onBeforeUnmount,
  // onMounted,
  ref,
  nextTick,
  watch,
  // defineEmits,
  reactive,
} from "vue";
import axios from "@/axios";
import { useStore } from "vuex";
import MdEditorV3 from "md-editor-v3";
import "md-editor-v3/lib/style.css";
import "@/assets/iconfont.js";

import screenfull from "screenfull";

import katex from "katex";
import "katex/dist/katex.min.css";

import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";

import mermaid from "mermaid";

import highlight from "highlight.js";
// import 'highlight.js/styles/atom-one-dark-reasonable.css';

import prettier from "prettier";
import parserMarkdown from "prettier/parser-markdown";

MdEditorV3.config({
  editorExtensions: {
    prettier: {
      prettierInstance: prettier,
      parserMarkdownInstance: parserMarkdown,
    },
    highlight: {
      instance: highlight,
    },
    screenfull: {
      instance: screenfull,
    },
    katex: {
      instance: katex,
    },
    cropper: {
      instance: Cropper,
    },
    mermaid: {
      instance: mermaid,
    },
  },
});

//动态添加css样式文件
function setAddCodeStyle(cssName) {
  let dom = document.querySelector("#code-style");
  if (dom) {
    dom.remove();
  }
  let link = document.createElement("link");
  link.setAttribute("href", `/code/styles/${cssName}.css`);
  link.setAttribute("rel", "stylesheet");
  link.setAttribute("id", "code-style");
  document.querySelector("head").appendChild(link);
}

// import { getItem } from "@/utils/tools";
// import cloneDeep from "lodash.clonedeep";
// import { Loading, CircleCheck } from "@element-plus/icons-vue";
// vuex
const store = useStore();
// const fileData = JSON.parse(JSON.stringify(store.state.fileData));
const editorId = ref("md-editor-id");
const previewTheme = ref("smart-blue"); //default、github、vuepress、mk-cute、smart-blue、cyanosis
// const codeTheme = ref("atom"); //atom、a11y、github、gradient、kimbie、paraiso、qtcreator、stackoverflow
setAddCodeStyle("atom-one-dark"); // a11y-dark  atom-one-dark monokai monokai-sublime vs vs2015 xcode paraiso-dark gradient-dark a11y-dark paraiso-dark

const data = reactive({
  emojiVisible: false,
});

const props = defineProps({
  content: {
    type: String,
  },
  save: {
    type: Function,
  },
  previewOnly: {
    default: false,
  },
});
const content = ref(props.content);

const handlerEmoji = (emoji) => {
  // 获取输入框
  const textarea = document.querySelector(`#${editorId.value}-textarea`);
  // 获取鼠标位置
  const endPoint = textarea.selectionStart;
  const prefixStr = textarea.value.substring(0, endPoint);
  const suffixStr = textarea.value.substring(endPoint, textarea.value.length);
  content.value = `${prefixStr}${emoji}${suffixStr}`;
  nextTick(() => {
    textarea.setSelectionRange(1 + endPoint, 1 + endPoint);
    textarea.focus();
  });
};

const handlerOnMark = () => {
  // 获取输入框
  const textarea = document.querySelector(`#${editorId.value}-textarea`);
  // 获取选中的内容
  const selection = window.getSelection()?.toString();
  // 获取鼠标位置
  const endPoint = textarea.selectionStart;

  // 生成标记文本
  const markStr = `@${selection}@`;

  // 根据鼠标位置分割旧文本
  // 前半部分
  const prefixStr = textarea.value.substring(0, endPoint);
  // 后半部分
  const suffixStr = textarea.value.substring(
    endPoint + (selection?.length || 0)
  );
  // console.log( );
  // emit("onChange", `${prefixStr}${markStr}${suffixStr}`);
  content.value = `${prefixStr}${markStr}${suffixStr}`;
  nextTick(() => {
    textarea.setSelectionRange(endPoint, markStr.length + endPoint);
    textarea.focus();
  });
};
const emojiVisibleChanged = (visible) => {
  data.emojiVisible = visible;
};

const handleOnUploadImg = async (files, callback) => {
  // const user = getItem("user") || {};
  const res = await Promise.all(
    Array.from(files).map((file) => {
      return new Promise((rev, rej) => {
        const form = new FormData();
        form.append("file", file);
        axios
          .post("/notez/images/post", form)
          .then((res) => rev(res))
          .catch((error) => rej(error));
      });
    })
  );

  callback(res.map((item) => item.data.data.url));
};

// const emit = defineEmits(["on-share-click"]);

// const loading = ref(true);

// const fileData = computed(() => store.state.fileData);

//防抖
let timer = null;
const anti_shake = () => {
  return function () {
    clearInterval(timer);
    timer = setTimeout(() => {
      handleSave(false);
    }, 800);
  };
};
const ctrl = anti_shake();

let isUpdate = false;
setTimeout(() => {
  isUpdate = true;
}, 3000);
watch(content, (val) => {
  // if (isUpdate && store.state.isAutoSave) ctrl(val);
});

const handleSave = (mark) => {
  if (content.value || props.save) props.save(mark);
};

// 编辑器事件监听
const editorDom = ref(null);
nextTick(() => {
  editorDom.value.onkeydown = (e) => {
    e = e || window.event;
    if (e.ctrlKey && e.keyCode == 83) {
      // ctr+S
      handleSave(true);
      return false;
    }
  };
});
// 分享
// const handleShare = () => {
//   emit("on-share-click", store.state.fileData);
// };

// // 加载数据
// const setContentData = (val) => {
//   nextTick(() => {
//     content.value = val;
//   });
// };
const getContent = () => {
  return content.value;
};
defineExpose({ getContent });

// // 组件销毁时,也及时销毁编辑器
// onBeforeUnmount(() => {
//   if (content.value || props.save)
//   console.log(fileData.name);
//     props.save({ data: content.value, file: fileData });
//   // const editor = getEditor(editorId);
//   // // sessionContent("set", store.state.fileData.bid, editor.children);
//   // if (editor == null) return;
//   // editor.destroy();
//   // removeEditor(editorId);
// });
</script>
<style lang="less" scoped>
.notez-markdown {
  :deep(.md-divider) {
    height: 45px;
  }

  .md {
    border: 0;
    height: calc(100vh - 65px);
  }
  :deep(.md-fullscreen){
    height:initial;
  }
  :deep(.mk-cute-theme) {
    color: initial;
  }
}
</style>      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#Vue
上次更新: 2024/11/25, 15:23:15
自定义Hooks获取窗口大小
Prop 验证 与 非 Prop 的 Attribute

← 自定义Hooks获取窗口大小 Prop 验证 与 非 Prop 的 Attribute→

最近更新
01
Gitea数据备份与还原
03-10
02
Linux 中使用 rsync 同步文件目录教程
03-10
03
Linux 使用 rsync 互相传输同步文件的简单步骤
03-08
更多文章>
Theme by Vdoing | Copyright © 2019-2025 NoteZ,All rights reserved | 冀ICP备2021027292号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式