国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

vue3富文本編輯器的二次封裝開發(fā)-Tinymce

這篇具有很好參考價(jià)值的文章主要介紹了vue3富文本編輯器的二次封裝開發(fā)-Tinymce。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

歡迎點(diǎn)擊領(lǐng)取 -《前端開發(fā)面試題進(jìn)階秘籍》:前端登頂之巔-最全面的前端知識(shí)點(diǎn)梳理總結(jié)

專享鏈接

簡(jiǎn)介

1、安裝:pnpm add tinymce @tinymce/tinymce-vue ===> Vue3 + tinymce + @tinymce/tinymce-vue
2、功能實(shí)現(xiàn)圖片上傳、基金卡片插入、收益卡片插入、源代碼復(fù)用、最大長(zhǎng)度限制、自定義表情包插入、文本內(nèi)容輸入、預(yù)覽等功能

vue3富文本編輯器的二次封裝開發(fā)-Tinymce,tinymce,富文本編輯器,vue3

代碼展示

在components文件下創(chuàng)建TinymceEditor.vue文件作為公共組件

<template>
  <div>
    <Editor ref="EditorRefs" v-model="content" :init="myTinyInit" />
    <div class="editor_footer">
      <span v-if="wordlimit">
        <span>{{ wordLenght }}</span>
        <span> / </span>
        <span>{{ wordlimit.max }}</span> 字符
      </span>
    </div>
    <el-dialog title="自定義表情包" v-model="dialogVisible" width="45%">
      <div class="emoji">
        <div class="emoji-item" v-for="item in 40" :key="item">
          <img :src="`/src/assets/emoji/${item}.webp`" alt="" @click="chooseEmoji(item)" />
        </div>
      </div>
    </el-dialog>
    <button @click="handlePreview">預(yù)覽</button>
  </div>
</template>

<script lang="ts" setup>
import './wordlimit' // 限制字符文件
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/icons/default/icons'
import 'tinymce/themes/silver'
import 'tinymce/models/dom/model'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/link'
import 'tinymce/plugins/help'
import 'tinymce/plugins/wordcount'
import 'tinymce/plugins/code'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/fullscreen'
import '/public/tinymce/plugins/image/index.js'

import { sumLetter } from '@/utils/utilTool'
import { computed, onMounted, reactive, ref, watch } from 'vue'

const props = withDefaults(
  defineProps<{
    modelValue?: string
    plugins?: string
    toolbar?: string
    wordlimit?: any
  }>(),
  {
    plugins: 'image code wordcount wordlimit preview', // 默認(rèn)開啟工具庫
    toolbar: 'image emoji fund—icon income-icon code' // 富文本編輯器工具
  }
)

const emit = defineEmits(['input'])

const wordLenght = ref<number | string>(0)

const content = ref<string>('')

const EditorRefs = ref<any>()

const dialogVisible = ref<boolean>(false)

const myTinyInit = reactive({
  width: '100%',
  height: 600, // 默認(rèn)高度
  statusbar: false,
  language_url: '/tinymce/langs/zh_CN.js', // 配置漢化-> 需下載對(duì)應(yīng)漢化包引入
  language: 'zh_CN', // 語言標(biāo)識(shí)
  branding: false, // 不顯示右下角logo
  auto_update: false, // 不進(jìn)行自動(dòng)更新
  resize: true, // 可以調(diào)整大小
  menubar: false, // 關(guān)閉頂部菜單
  skin_url: '/tinymce/skins/ui/oxide', // 手動(dòng)引入CSS
  content_css: '/tinymce/skins/content/default/content.css', // 手動(dòng)引入CSS
  toolbar_mode: 'wrap',
  plugins: props?.plugins, // 插件
  toolbar: props?.toolbar, // 功能按鈕
  wordlimit: props?.wordlimit, // 字?jǐn)?shù)限制
  image_caption: false,
  paste_data_images: true,

  //粘貼圖片后,自動(dòng)上傳
  urlconverter_callback: function (url, node, on_save, name) {
    return url
  },

  images_upload_handler: (blobInfo) =>
    new Promise((resolve, reject) => {
      console.log(blobInfo.blob())
      const formData = new FormData()
      formData.append('file', blobInfo.blob(), blobInfo.filename())
      resolve('https://szx-bucket1.oss-cn-hangzhou.aliyuncs.com/picgo/image-20230512090059968.png')
      // axios
      //   .post(`/api/backend/upload`, formData, {
      //     headers: {
      //       'Content-Type': 'multipart/form-data',
      //       Authorization: 'Bearer ' + store.state.user.accessToken,
      //     },
      //   })
      //   .then((res) => {
      //     if (res.data.code === 1) {
      //       resolve(`/image_manipulation${res.data.data.filePath}`)
      //     } else {
      //       ElNotification.warning(res.data.msg)
      //     }
      //   })
      //   .catch((error) => {
      //     reject(error)
      //   })
    }),

  setup: (editor) => { // 自定義圖標(biāo)內(nèi)容及觸發(fā)點(diǎn)擊事件等功能
    editor.ui.registry.addIcon(
      'fund—icon',
      '<svg t="1696250970925" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24834" width="21" height="21"><path d="M512 133.12c208.91648 0 378.88 169.96352 378.88 378.88s-169.96352 378.88-378.88 378.88-378.88-169.96352-378.88-378.88 169.96352-378.88 378.88-378.88m0-71.68c-248.83712 0-450.56 201.72288-450.56 450.56s201.72288 450.56 450.56 450.56 450.56-201.72288 450.56-450.56-201.72288-450.56-450.56-450.56z" fill="#2c2c2c" p-id="24835"></path><path d="M624.74752 263.6288a35.72224 35.72224 0 0 0-25.344 10.496L512 361.52832 424.59648 274.1248a35.73248 35.73248 0 0 0-25.344-10.496 35.84 35.84 0 0 0-25.344 61.17888L451.07712 401.9712H348.16a35.84 35.84 0 1 0 0 71.68h128v66.56H348.16a35.84 35.84 0 1 0 0 71.68h128v133.12a35.84 35.84 0 1 0 71.68 0v-133.12h128a35.84 35.84 0 1 0 0-71.68h-128v-66.56h128a35.84 35.84 0 1 0 0-71.68h-102.91712l77.16352-77.16352a35.84 35.84 0 0 0-25.33888-61.17888z" fill="#2c2c2c" p-id="24836"></path></svg>'
    )
    editor.ui.registry.addIcon(
      'income-icon',
      '<svg t="1696250530786" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15004" width="21" height="21"><path d="M920 152v720H104V152h816z m-67.2 67.2H171.2v585.6h681.6V219.2z" fill="#2c2c2c" p-id="15005"></path><path d="M32 152m7 0l946 0q7 0 7 7l0 53.2q0 7-7 7l-946 0q-7 0-7-7l0-53.2q0-7 7-7Z" fill="#2c2c2c" p-id="15006"></path><path d="M450.906 417.788l122.187 122.19 115.4-115.401 47.518 47.517-115.4 115.4-47.517 47.518-122.189-122.19-115.399 115.401-47.517-47.517 162.917-162.918z" fill="#2c2c2c" p-id="15007"></path><path d="M300.8 718.4H368v86.4h-67.2v-86.4z m120-86.4H488v172.8h-67.2V632z m120 48H608v124.8h-67.2V680z m120-67.2H728v192h-67.2v-192z" fill="#2c2c2c" p-id="15008"></path></svg>'
    )

    editor.ui.registry.addButton('emoji', {
      icon: 'emoji',
      tooltip: '自定義表情包',
      onAction: () => {
        dialogVisible.value = true
      }
    })

    editor.ui.registry.addButton('fund—icon', {
      icon: 'fund—icon',
      tooltip: '基金',
      onAction: () => {
        editor.insertContent('Hello')
      }
    })

    editor.ui.registry.addButton('income-icon', {
      icon: 'income-icon',
      tooltip: '曬收益',
      onAction: () => {
        editor.insertContent('Hello')
      }
    })
  },

  init_instance_callback: (editor: any) => {
    editor.on('input', () => getEditorWordLen())
  }
})

const initContent = computed(() => {
  return props.modelValue
})

// 選擇自定義表情包
const chooseEmoji = (item) => {
  const editor = EditorRefs.value.getEditor()
  const range = editor.selection.getRng()
  const imgNode = editor.getDoc().createElement('img')
  imgNode.width = 32
  imgNode.height = 32
  imgNode.style = 'vertical-align: bottom;'
  imgNode.src = `/src/assets/emoji/${item}.webp` // 注意寫你的項(xiàng)目相對(duì)路徑
  range.insertNode(imgNode)
  dialogVisible.value = false
  editor.execCommand('seleceAll')
  editor.selection.getRng().collapse()
  editor.focus()
}

const getEditorWordLen = () => {
  const content = tinymce.activeEditor.getContent({ format: 'text' })
  const wordObj = sumLetter(content)
  wordLenght.value = wordObj?.txt?.length || 0
}

const handlePreview = () => {
  const editor = tinymce.activeEditor
  editor.on('preview', (editor) => {
    console.log(editor)
  })
}

onMounted(() => {
  tinymce.init({})
  setTimeout(() => getEditorWordLen(), 800)
})

watch(
  initContent,
  (newVal) => {
    content.value = newVal
  },
  { deep: true, immediate: true }
)

watch(
  content,
  (newVal) => {
    emit('input', newVal)
  },
  { deep: true }
)
</script>

<script lang="ts">
export default { name: 'TinymceEditor' }
</script>

<style scoped lang="scss">
.emoji {
  display: flex;
  flex-wrap: wrap;
}

.emoji-item {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-left: 10px;
  margin-bottom: 8px;
  cursor: pointer;

  img {
    width: 48px;
    height: 48px;
  }
}

.editor_footer {
  margin-top: 20px;
  font-size: 13px;
}
</style>

創(chuàng)建wordlimit.ts文件,作為限制字符的觸發(fā)條件文章來源地址http://www.zghlxwxcb.cn/news/detail-728227.html

import tinymce from 'tinymce/tinymce'
import { ElMessage } from 'element-plus'
import { sumLetter } from '@/utils/utilTool'

tinymce.PluginManager.add('wordlimit', function (editor): any {
  const pluginName = '字?jǐn)?shù)限制'
  const app = tinymce.util.Tools.resolve('tinymce.util.Delay')
  const Tools = tinymce.util.Tools.resolve('tinymce.util.Tools')
  const wordlimit_event = editor.getParam('ax_wordlimit_event', 'SetContent Undo Redo Keyup input paste')
  const options = editor.getParam('wordlimit', {}, 'object')
  let close = null

  const toast = function (message) {
    close && close.close()
    close = ElMessage.error(message)
    return
  }

  // 默認(rèn)配置
  const defaults = {
    spaces: false, // 是否含空格
    isInput: false, // 是否在超出后還可以輸入
    maxMessage: '超出最大輸入字符數(shù)量!',
    changeCallback: () => {}, // 自定義的回調(diào)方法
    changeMaxCallback: () => {},
    toast // 提示彈窗
  }

  class WordLimit {
    constructor(editor, options) {
      options = Tools.extend(defaults, options)
      let preCount = 0
      let _wordCount = 0
      let oldContent = editor.getContent()
      const WordCount = editor.plugins.wordcount

      editor.on(wordlimit_event, function (e) {
        const content = editor.getContent() || e.content || ''
        if (!options.spaces) {
          _wordCount = WordCount.body.getCharacterCount()
        } else {
          _wordCount = WordCount.body.getCharacterCountWithoutSpaces()
        }
        options.changeCallback({
          ...options,
          editor,
          num: _wordCount,
          content,
          ...sumLetter(content)
        })
        if (_wordCount > options.max) {
          preCount = _wordCount
          if (options.isInput == !1) {
            editor.setContent(oldContent)
            if (!options.spaces) {
              _wordCount = WordCount.body.getCharacterCount()
            } else {
              _wordCount = WordCount.body.getCharacterCountWithoutSpaces()
            }
          }
          editor.getBody().blur()
          editor.fire('wordlimit', {
            maxCount: options.max,
            wordCount: _wordCount,
            preCount: preCount,
            isPaste: e.type === 'paste' || e.paste || false
          })
          toast('最多只能輸入' + options.max + '個(gè)字')
        }
        oldContent = editor.getContent()
      })
    }
  }

  const setup = function () {
    if (!options && !options.max) return false
    if (!editor.plugins.wordcount) return toast('請(qǐng)先在tinymce的plugins配置wordlimit之前加入wordcount插件')
    app.setEditorTimeout(
      editor,
      function () {
        const editDom = editor.getContainer()
        const wordNum: any = editDom.querySelector('button.tox-statusbar__wordcount')
        const statusbarpath: any = editDom.querySelector('.tox-statusbar__path')
        statusbarpath ? statusbarpath.remove() : void null
        if (wordNum?.innerText?.indexOf('字符') == -1) wordNum.click()
        new WordLimit(editor, options)
      },
      300
    )
  }

  setup()

  return {
    getMetadata: function () {
      return {
        name: pluginName
      }
    }
  }
})

使用
<template>
  <div class="post_contaniner">
    <div style="width: 100%">
      <TinymceEditor v-model="content" @input="inputContent" :wordlimit="{ max: 300 }" />
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const content = ref('Hello World')

const inputContent = (newVal) => {
  console.log(newVal)
  content.value = newVal
}
</script>

<style scoped lang="scss">
.post_contaniner {
  .right {
    flex: 1;
    box-shadow: 0 1px 10px 3px #dbdbdb;
    margin-right: 10px;
    padding: 10px;
    box-sizing: border-box;
  }
}
</style>

到了這里,關(guān)于vue3富文本編輯器的二次封裝開發(fā)-Tinymce的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • vue3使用quill富文本編輯器,保姆級(jí)教程,富文本踩坑解決

    vue3使用quill富文本編輯器,保姆級(jí)教程,富文本踩坑解決

    本文是封裝成組件使用 先放效果圖 先封裝組件,建立如下目錄 全部代碼如下, 使用 本文是第二個(gè)頁面使用這個(gè)富文本編輯器有可能watch監(jiān)聽中找不到ref,如果不能正常使用可以稍微改裝下在onMounted里賦值然后在setValue里拋出就好 保姆級(jí)教程,有問題歡迎提出

    2024年02月11日
    瀏覽(27)
  • Vue3 代碼塊高亮顯示并可使用富文本編輯器編輯(highlight.js + wangEditor)

    Vue3 代碼塊高亮顯示并可使用富文本編輯器編輯(highlight.js + wangEditor)

    在Vue項(xiàng)目中實(shí)現(xiàn)以下功能: ??功能1. 在頁面中顯示代碼,并將其中的高亮顯示。 ??功能2. 允許對(duì)代碼塊進(jìn)行編輯,編輯時(shí)代碼也高亮顯示。 ??功能3. 可在編輯器中添加多個(gè)代碼塊,動(dòng)態(tài)渲染代碼高亮。 ? Step1: 安裝所需插件(本文使用npm安裝,若需

    2023年04月21日
    瀏覽(66)
  • vue3富文本編輯器vue-quill-editor、圖片縮放ImageResize詳細(xì)配置及使用教程

    vue3富文本編輯器vue-quill-editor、圖片縮放ImageResize詳細(xì)配置及使用教程

    官網(wǎng)地址:https://vueup.github.io/vue-quill/ 效果圖 ?1、安裝 2、在vue.config.js中添加配置,否則quill-image-resize-module會(huì)出現(xiàn)Cannot read property ‘imports‘ of undefined報(bào)錯(cuò)問題 3、創(chuàng)建quillTool.js(用于添加超鏈接、視頻) 4、完整代碼

    2024年02月04日
    瀏覽(33)
  • vue3+ts+tinynce富文本編輯器+htmlDocx+file-saver 配合實(shí)現(xiàn)word下載

    vue3+ts+tinynce富文本編輯器+htmlDocx+file-saver 配合實(shí)現(xiàn)word下載

    vue3 請(qǐng)下載html-docx-js-typescript,否則會(huì)報(bào)錯(cuò)類型問題 row.report效果及數(shù)據(jù) 調(diào)用

    2024年02月11日
    瀏覽(25)
  • 小程序Editor富文本編輯器-封裝使用用法

    小程序Editor富文本編輯器-封裝使用用法

    本文章主要講述editor中小程序的圖片上傳和pc端數(shù)據(jù)不同步的問題,建議多看下代碼 完整代碼在最下面 1、創(chuàng)建個(gè)用于組件封裝的editor文件夾,存放三個(gè)文件 ?2、editor.vue文件是主要封裝代碼 3、editor-icon.css文件樣式 5、如果上傳圖片失敗或者是圖片裂開,和pc端數(shù)據(jù)不同步的話

    2024年02月11日
    瀏覽(25)
  • Tinymce富文本編輯器二次開發(fā)電子病歷時(shí)解決的bug

    Tinymce富文本編輯器二次開發(fā)電子病歷時(shí)解決的bug

    本文是在Tinymce富文本編輯器添加自定義toolbar,二級(jí)菜單,自定義表單,簽名的基礎(chǔ)之上進(jìn)行一些bug記錄,功能添加,以及模版的應(yīng)用和打印 項(xiàng)目描述 建立電子病歷模版—錄入(電子病歷模版和電子病歷打印模版)—查看電子病歷和打印病歷模版 建立電子病歷----添加一個(gè)電

    2024年04月10日
    瀏覽(125)
  • Vue3中搜索表單的二次封裝

    最近使用Vue3+ElementPlus開發(fā)項(xiàng)目,從整體上構(gòu)思組件的封裝。能寫成組件的內(nèi)容都進(jìn)行封裝,方便多個(gè)地方使用。 受AntDesign的啟發(fā),在項(xiàng)目中有搜索表單+table+分頁的地方可以封裝為一個(gè)組件,只需要對(duì)組件傳入table的列,組成一個(gè)配置項(xiàng),通過配置可以顯示搜索表單、table項(xiàng)的

    2024年02月11日
    瀏覽(85)
  • Vue3和TypeScript下基于Axios的二次封裝教程

    Vue3和TypeScript下基于Axios的二次封裝教程

    學(xué)習(xí)如何在Vue3項(xiàng)目中使用TypeScript和Axios進(jìn)行二次封裝,實(shí)現(xiàn)更靈活的網(wǎng)絡(luò)請(qǐng)求處理。詳細(xì)教程包括攔截器設(shè)置和類型擴(kuò)展。

    2024年02月05日
    瀏覽(584)
  • 8款 Vue 富文本編輯器

    8款 Vue 富文本編輯器

    https://www.wangeditor.com/ http://tinymce.ax-z.cn/ https://www.itxst.com/tiptap/tutorial.html https://ckeditor.com/ckeditor-5/ https://quilljs.com/ https://www.froala.com/ https://summernote.org/ 基于bootstrap,比較突出的優(yōu)點(diǎn)就是能保持復(fù)制過來的東西的原有樣式,并且比較流暢。 https://www.cnblogs.com/xxflz/p/9777075.html https

    2024年02月12日
    瀏覽(79)
  • Vue + 富文本編輯器:打印模板設(shè)計(jì)

    Vue + 富文本編輯器:打印模板設(shè)計(jì)

    前言: 有的項(xiàng)目需要用到打印,如果只有少數(shù)的地方需要用到打印,一般只需要固定模板進(jìn)行打印就行了,但是我們的項(xiàng)目總是與眾不同,明明只要固定模板就可以完成需求的,非要添加一個(gè)靈活的打印模板,而且還涉及到拖拉填充文本,真是腦細(xì)胞不知道死掉了多少! ! !

    2024年02月09日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包