有个虚拟服务器建设网站,优化设计答案六年级上册,舟山城乡建设培训中心网站,seo推广代理欢迎点击领取 -《前端面试题进阶指南》#xff1a;前端登顶之巅-最全面的前端知识点梳理总结
*分享一个使用比较久的#x1fa9c;
简介 1、安装#xff1a;pnpm add tinymce / pnpm add tinymce/tinymce-vue Vue3 tinymce tinymce/tinymce-vue 2、功能实现图片上传…欢迎点击领取 -《前端面试题进阶指南》前端登顶之巅-最全面的前端知识点梳理总结
*分享一个使用比较久的
简介 1、安装pnpm add tinymce / pnpm add tinymce/tinymce-vue Vue3 tinymce tinymce/tinymce-vue 2、功能实现图片上传、基金卡片插入、收益卡片插入、源代码复用、最大长度限制、自定义表情包插入、文本内容输入、预览等功能 代码展示
在components文件下创建TinymceEditor.vue文件作为公共组件
templatedivEditor refEditorRefs v-modelcontent :initmyTinyInit /div classeditor_footerspan v-ifwordlimitspan{{ wordLenght }}/spanspan / /spanspan{{ wordlimit.max }}/span 字符/span/divel-dialog title自定义表情包 v-modeldialogVisible width45%div classemojidiv classemoji-item v-foritem in 40 :keyitemimg :src/src/assets/emoji/${item}.webp alt clickchooseEmoji(item) //div/div/el-dialogbutton clickhandlePreview预览/button/div
/templatescript langts 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.jsimport { sumLetter } from /utils/utilTool
import { computed, onMounted, reactive, ref, watch } from vueconst props withDefaults(defineProps{modelValue?: stringplugins?: stringtoolbar?: stringwordlimit?: any}(),{plugins: image code wordcount wordlimit preview, // 默认开启工具库toolbar: image emoji fund—icon income-icon code // 富文本编辑器工具}
)const emit defineEmits([input])const wordLenght refnumber | string(0)const content refstring()const EditorRefs refany()const dialogVisible refboolean(false)const myTinyInit reactive({width: 100%,height: 600, // 默认高度statusbar: false,language_url: /tinymce/langs/zh_CN.js, // 配置汉化- 需下载对应汉化包引入language: zh_CN, // 语言标识branding: false, // 不显示右下角logoauto_update: false, // 不进行自动更新resize: true, // 可以调整大小menubar: false, // 关闭顶部菜单skin_url: /tinymce/skins/ui/oxide, // 手动引入CSScontent_css: /tinymce/skins/content/default/content.css, // 手动引入CSStoolbar_mode: wrap,plugins: props?.plugins, // 插件toolbar: props?.toolbar, // 功能按钮wordlimit: props?.wordlimit, // 字数限制image_caption: false,paste_data_images: true,//粘贴图片后自动上传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) { // 自定义图标内容及触发点击事件等功能editor.ui.registry.addIcon(fund—icon,svg t1696250970925 classicon viewBox0 0 1024 1024 version1.1 xmlnshttp://www.w3.org/2000/svg p-id24834 width21 height21path dM512 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-id24835/pathpath dM624.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-id24836/path/svg)editor.ui.registry.addIcon(income-icon,svg t1696250530786 classicon viewBox0 0 1024 1024 version1.1 xmlnshttp://www.w3.org/2000/svg p-id15004 width21 height21path dM920 152v720H104V152h816z m-67.2 67.2H171.2v585.6h681.6V219.2z fill#2c2c2c p-id15005/pathpath dM32 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-id15006/pathpath dM450.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-id15007/pathpath dM300.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-id15008/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 32imgNode.height 32imgNode.style vertical-align: bottom;imgNode.src /src/assets/emoji/${item}.webp // 注意写你的项目相对路径range.insertNode(imgNode)dialogVisible.value falseeditor.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.activeEditoreditor.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 }
)
/scriptscript langts
export default { name: TinymceEditor }
/scriptstyle scoped langscss
.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
创建wordlimit.ts文件作为限制字符的触发条件
import tinymce from tinymce/tinymce
import { ElMessage } from element-plus
import { sumLetter } from /utils/utilTooltinymce.PluginManager.add(wordlimit, function (editor): any {const pluginName 字数限制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 nullconst toast function (message) {close close.close()close ElMessage.error(message)return}// 默认配置const defaults {spaces: false, // 是否含空格isInput: false, // 是否在超出后还可以输入maxMessage: 超出最大输入字符数量,changeCallback: () {}, // 自定义的回调方法changeMaxCallback: () {},toast // 提示弹窗}class WordLimit {constructor(editor, options) {options Tools.extend(defaults, options)let preCount 0let _wordCount 0let oldContent editor.getContent()const WordCount editor.plugins.wordcounteditor.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 _wordCountif (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 个字)}oldContent editor.getContent()})}}const setup function () {if (!options !options.max) return falseif (!editor.plugins.wordcount) return toast(请先在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 nullif (wordNum?.innerText?.indexOf(字符) -1) wordNum.click()new WordLimit(editor, options)},300)}setup()return {getMetadata: function () {return {name: pluginName}}}
})
使用
templatediv classpost_contaninerdiv stylewidth: 100%TinymceEditor v-modelcontent inputinputContent :wordlimit{ max: 300 } //div/div
/templatescript langts setup
import { ref } from vueconst content ref(Hello World)const inputContent (newVal) {console.log(newVal)content.value newVal
}
/scriptstyle scoped langscss
.post_contaniner {.right {flex: 1;box-shadow: 0 1px 10px 3px #dbdbdb;margin-right: 10px;padding: 10px;box-sizing: border-box;}
}
/style