Vue项目中集成TinyMCE富文本编辑器(图片批量上传等)

TinyMCE富文本在Vue中的使用

关于TinyMCE

实现效果

安装使用TinyMCE

第一步

第二步

1.官网申请Your Tiny API Key,并且配置访问域名:

2.使用css隐藏(这个就不讲了,不推荐使用)

3.全部由本地加载(推荐)

第三步(汉化包)

第四步(封装组件)

1.动态引入JS文件

2.自定义图片上传逻辑

3.上传图片文件路径变为相对路径

4.图片批量上传(非官方插件的使用)

总结


关于TinyMCE

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。因为项目原因,本文章是在Vue2中封装TinyMce富文本组件。

实现效果

基本效果如图,还可以自己替换图标等,我这里额外引入了图片批量上传插件,后面会讲如何使引用且使用插件,需要使用其他插件的也是一个套路。 

安装使用TinyMCE

第一步

//安装tinymce

yarn add tinymce@5.10.0 -S

//安装tinymce-vue

yarn add @tinymce/tinymce-vue@3.0.1 -S

第二步

在使用TinyMCE过程中,会出现如上图提示,是因为TinyMCE官方需要apiKey,解决办法有三种:

1.官网申请Your Tiny API Key,并且配置访问域名:

TinyMCE官网地址https://www.tiny.cloud/

注册登录后拿到apiKey

然后配置域名

 具体代码中的使用在后面。

2.使用css隐藏(这个就不讲了,不推荐使用)

3.全部由本地加载(推荐)

在node_modules中找到tinymce文件夹(注意不是@tinymce文件夹)

然后将文件移入到pubilc/tinymce文件夹中 

网上大部分相关教程都是在index.html中引入,如下:

但我在项目中出现,当浏览器左上角强制刷新页面时,可能会出现组件找不到tinymce.min.js文件问题,如下:

 看报错,很明显引入时机有问题,那就只能在每次使用组件时动态去引入tinymce.min.js文件了,具体代码在后文。

第三步(汉化包)

在public下新建/lang/zh_CN.js 如下:

文件内容如下(粘贴复制就可): 

tinymce.addI18n("zh_CN", {
  Redo: "\u91cd\u505a",
  Undo: "\u64a4\u9500",
  Cut: "\u526a\u5207",
  Copy: "\u590d\u5236",
  Paste: "\u7c98\u8d34",
  "Select all": "\u5168\u9009",
  "New document": "\u65b0\u6587\u4ef6",
  Ok: "\u786e\u5b9a",
  Cancel: "\u53d6\u6d88",
  "Visual aids": "\u7f51\u683c\u7ebf",
  Bold: "\u7c97\u4f53",
  Italic: "\u659c\u4f53",
  Underline: "\u4e0b\u5212\u7ebf",
  Strikethrough: "\u5220\u9664\u7ebf",
  Superscript: "\u4e0a\u6807",
  Subscript: "\u4e0b\u6807",
  "Clear formatting": "\u6e05\u9664\u683c\u5f0f",
  "Align left": "\u5de6\u8fb9\u5bf9\u9f50",
  "Align center": "\u4e2d\u95f4\u5bf9\u9f50",
  "Align right": "\u53f3\u8fb9\u5bf9\u9f50",
  Justify: "\u4e24\u7aef\u5bf9\u9f50",
  "Bullet list": "\u9879\u76ee\u7b26\u53f7",
  "Numbered list": "\u7f16\u53f7\u5217\u8868",
  "Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
  "Increase indent": "\u589e\u52a0\u7f29\u8fdb",
  Close: "\u5173\u95ed",
  Formats: "\u683c\u5f0f",
  "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.":
    "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X/C/V\u7b49\u5feb\u6377\u952e\u3002",
  Headers: "\u6807\u9898",
  "Header 1": "\u6807\u98981",
  "Header 2": "\u6807\u98982",
  "Header 3": "\u6807\u98983",
  "Header 4": "\u6807\u98984",
  "Header 5": "\u6807\u98985",
  "Header 6": "\u6807\u98986",
  Headings: "\u6807\u9898",
  "Heading 1": "\u6807\u98981",
  "Heading 2": "\u6807\u98982",
  "Heading 3": "\u6807\u98983",
  "Heading 4": "\u6807\u98984",
  "Heading 5": "\u6807\u98985",
  "Heading 6": "\u6807\u98986",
  Preformatted: "\u9884\u5148\u683c\u5f0f\u5316\u7684",
  Div: "Div",
  Pre: "Pre",
  Code: "\u4ee3\u7801",
  Paragraph: "\u6bb5\u843d",
  Blockquote: "\u5f15\u6587\u533a\u5757",
  Inline: "\u6587\u672c",
  Blocks: "\u57fa\u5757",
  "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.":
    "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
  Fonts: "\u5b57\u4f53",
  "Font Sizes": "\u5b57\u53f7",
  Class: "\u7c7b\u578b",
  "Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
  OR: "\u6216",
  "Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
  Upload: "\u4e0a\u4f20",
  Block: "\u5757",
  Align: "\u5bf9\u9f50",
  Default: "\u9ed8\u8ba4",
  Circle: "\u7a7a\u5fc3\u5706",
  Disc: "\u5b9e\u5fc3\u5706",
  Square: "\u65b9\u5757",
  "Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
  "Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
  "Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
  "Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
  "Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
  "Anchor...": "\u951a\u70b9...",
  Name: "\u540d\u79f0",
  Id: "\u6807\u8bc6\u7b26",
  "Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.":
    "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
  "You have unsaved changes are you sure you want to navigate away?":
    "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
  "Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
  "Special character...": "\u7279\u6b8a\u5b57\u7b26...",
  "Source code": "\u6e90\u4ee3\u7801",
  "Insert/Edit code sample":
    "\u63d2\u5165/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
  Language: "\u8bed\u8a00",
  "Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
  "Color Picker": "\u9009\u8272\u5668",
  R: "R",
  G: "G",
  B: "B",
  "Left to right": "\u4ece\u5de6\u5230\u53f3",
  "Right to left": "\u4ece\u53f3\u5230\u5de6",
  Emoticons: "\u8868\u60c5",
  "Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
  "Metadata and Document Properties":
    "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
  Title: "\u6807\u9898",
  Keywords: "\u5173\u952e\u8bcd",
  Description: "\u63cf\u8ff0",
  Robots: "\u673a\u5668\u4eba",
  Author: "\u4f5c\u8005",
  Encoding: "\u7f16\u7801",
  Fullscreen: "\u5168\u5c4f",
  Action: "\u64cd\u4f5c",
  Shortcut: "\u5feb\u6377\u952e",
  Help: "\u5e2e\u52a9",
  Address: "\u5730\u5740",
  "Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
  "Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
  "Focus to element path":
    "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
  "Focus to contextual toolbar":
    "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
  "Insert link (if link plugin activated)":
    "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
  "Save (if save plugin activated)":
    "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
  "Find (if searchreplace plugin activated)":
    "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
  "Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
  "Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
  "Learn more...": "\u4e86\u89e3\u66f4\u591a...",
  "You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
  Plugins: "\u63d2\u4ef6",
  "Handy Shortcuts": "\u5feb\u6377\u952e",
  "Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
  "Insert/edit image": "\u63d2\u5165/\u7f16\u8f91\u56fe\u7247",
  "Alternative description": "\u66ff\u4ee3\u63cf\u8ff0",
  Accessibility: "\u8f85\u52a9\u529f\u80fd",
  "Image is decorative": "\u56fe\u50cf\u662f\u88c5\u9970\u6027\u7684",
  Source: "\u5730\u5740",
  Dimensions: "\u5927\u5c0f",
  "Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
  General: "\u666e\u901a",
  Advanced: "\u9ad8\u7ea7",
  Style: "\u6837\u5f0f",
  "Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
  "Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
  Border: "\u8fb9\u6846",
  "Insert image": "\u63d2\u5165\u56fe\u7247",
  "Image...": "\u56fe\u7247...",
  "Image list": "\u56fe\u7247\u5217\u8868",
  "Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
  "Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
  "Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
  "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
  "Edit image": "\u7f16\u8f91\u56fe\u7247",
  "Image options": "\u56fe\u7247\u9009\u9879",
  "Zoom in": "\u653e\u5927",
  "Zoom out": "\u7f29\u5c0f",
  Crop: "\u88c1\u526a",
  Resize: "\u8c03\u6574\u5927\u5c0f",
  Orientation: "\u65b9\u5411",
  Brightness: "\u4eae\u5ea6",
  Sharpen: "\u9510\u5316",
  Contrast: "\u5bf9\u6bd4\u5ea6",
  "Color levels": "\u989c\u8272\u5c42\u6b21",
  Gamma: "\u4f3d\u9a6c\u503c",
  Invert: "\u53cd\u8f6c",
  Apply: "\u5e94\u7528",
  Back: "\u540e\u9000",
  "Insert date/time": "\u63d2\u5165\u65e5\u671f/\u65f6\u95f4",
  "Date/time": "\u65e5\u671f/\u65f6\u95f4",
  "Insert/edit link": "\u63d2\u5165/\u7f16\u8f91\u94fe\u63a5",
  "Text to display": "\u663e\u793a\u6587\u5b57",
  Url: "\u5730\u5740",
  "Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
  "Current window": "\u5f53\u524d\u7a97\u53e3",
  None: "\u65e0",
  "New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
  "Open link": "\u6253\u5f00\u94fe\u63a5",
  "Remove link": "\u5220\u9664\u94fe\u63a5",
  Anchors: "\u951a\u70b9",
  "Link...": "\u94fe\u63a5...",
  "Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
  "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?":
    "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
  "The URL you entered seems to be an external link. Do you want to add the required http:// prefix?":
    "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp://:\u524d\u7f00\u5417\uff1f",
  "The URL you entered seems to be an external link. Do you want to add the required https:// prefix?":
    "\u60a8\u8f93\u5165\u7684 URL \u4f3c\u4e4e\u662f\u4e00\u4e2a\u5916\u90e8\u94fe\u63a5\u3002\u60a8\u60f3\u6dfb\u52a0\u6240\u9700\u7684 https:// \u524d\u7f00\u5417\uff1f",
  "Link list": "\u94fe\u63a5\u5217\u8868",
  "Insert video": "\u63d2\u5165\u89c6\u9891",
  "Insert/edit video": "\u63d2\u5165/\u7f16\u8f91\u89c6\u9891",
  "Insert/edit media": "\u63d2\u5165/\u7f16\u8f91\u5a92\u4f53",
  "Alternative source": "\u955c\u50cf",
  "Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
  "Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
  "Paste your embed code below:":
    "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
  Embed: "\u5185\u5d4c",
  "Media...": "\u591a\u5a92\u4f53...",
  "Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
  "Page break": "\u5206\u9875\u7b26",
  "Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
  Preview: "\u9884\u89c8",
  "Print...": "\u6253\u5370...",
  Save: "\u4fdd\u5b58",
  Find: "\u67e5\u627e",
  "Replace with": "\u66ff\u6362\u4e3a",
  Replace: "\u66ff\u6362",
  "Replace all": "\u5168\u90e8\u66ff\u6362",
  Previous: "\u4e0a\u4e00\u4e2a",
  Next: "\u4e0b\u4e00\u4e2a",
  "Find and Replace": "\u67e5\u627e\u548c\u66ff\u6362",
  "Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
  "Could not find the specified string.":
    "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
  "Match case": "\u533a\u5206\u5927\u5c0f\u5199",
  "Find whole words only": "\u5168\u5b57\u5339\u914d",
  "Find in selection": "\u5728\u9009\u533a\u4e2d\u67e5\u627e",
  Spellcheck: "\u62fc\u5199\u68c0\u67e5",
  "Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
  "No misspellings found.": "\u6ca1\u6709\u53d1\u73b0\u62fc\u5199\u9519\u8bef",
  Ignore: "\u5ffd\u7565",
  "Ignore all": "\u5168\u90e8\u5ffd\u7565",
  Finish: "\u5b8c\u6210",
  "Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
  "Insert table": "\u63d2\u5165\u8868\u683c",
  "Table properties": "\u8868\u683c\u5c5e\u6027",
  "Delete table": "\u5220\u9664\u8868\u683c",
  Cell: "\u5355\u5143\u683c",
  Row: "\u884c",
  Column: "\u5217",
  "Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
  "Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
  "Split cell": "\u62c6\u5206\u5355\u5143\u683c",
  "Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
  "Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
  "Delete row": "\u5220\u9664\u884c",
  "Row properties": "\u884c\u5c5e\u6027",
  "Cut row": "\u526a\u5207\u884c",
  "Copy row": "\u590d\u5236\u884c",
  "Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
  "Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
  "Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
  "Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
  "Delete column": "\u5220\u9664\u5217",
  Cols: "\u5217",
  Rows: "\u884c",
  Width: "\u5bbd",
  Height: "\u9ad8",
  "Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
  "Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
  Caption: "\u6807\u9898",
  "Show caption": "\u663e\u793a\u6807\u9898",
  Left: "\u5de6\u5bf9\u9f50",
  Center: "\u5c45\u4e2d",
  Right: "\u53f3\u5bf9\u9f50",
  "Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
  Scope: "\u8303\u56f4",
  Alignment: "\u5bf9\u9f50\u65b9\u5f0f",
  "H Align": "\u6c34\u5e73\u5bf9\u9f50",
  "V Align": "\u5782\u76f4\u5bf9\u9f50",
  Top: "\u9876\u90e8\u5bf9\u9f50",
  Middle: "\u5782\u76f4\u5c45\u4e2d",
  Bottom: "\u5e95\u90e8\u5bf9\u9f50",
  "Header cell": "\u8868\u5934\u5355\u5143\u683c",
  "Row group": "\u884c\u7ec4",
  "Column group": "\u5217\u7ec4",
  "Row type": "\u884c\u7c7b\u578b",
  Header: "\u8868\u5934",
  Body: "\u8868\u4f53",
  Footer: "\u8868\u5c3e",
  "Border color": "\u8fb9\u6846\u989c\u8272",
  "Insert template...": "\u63d2\u5165\u6a21\u677f...",
  Templates: "\u6a21\u677f",
  Template: "\u6a21\u677f",
  "Text color": "\u6587\u5b57\u989c\u8272",
  "Background color": "\u80cc\u666f\u8272",
  "Custom...": "\u81ea\u5b9a\u4e49...",
  "Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
  "No color": "\u65e0",
  "Remove color": "\u79fb\u9664\u989c\u8272",
  "Table of Contents": "\u5185\u5bb9\u5217\u8868",
  "Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
  "Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
  "Word count": "\u5b57\u6570",
  Count: "\u8ba1\u6570",
  Document: "\u6587\u6863",
  Selection: "\u9009\u62e9",
  Words: "\u5355\u8bcd",
  "Words: {0}": "\u5b57\u6570\uff1a{0}",
  "{0} words": "{0} \u5b57",
  File: "\u6587\u4ef6",
  Edit: "\u7f16\u8f91",
  Insert: "\u63d2\u5165",
  View: "\u89c6\u56fe",
  Format: "\u683c\u5f0f",
  Table: "\u8868\u683c",
  Tools: "\u5de5\u5177",
  "Powered by {0}": "\u7531{0}\u9a71\u52a8",
  "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help":
    "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
  "Image title": "\u56fe\u7247\u6807\u9898",
  "Border width": "\u8fb9\u6846\u5bbd\u5ea6",
  "Border style": "\u8fb9\u6846\u6837\u5f0f",
  Error: "\u9519\u8bef",
  Warn: "\u8b66\u544a",
  Valid: "\u6709\u6548",
  "To open the popup, press Shift+Enter":
    "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
  "Rich Text Area. Press ALT-0 for help.":
    "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
  "System Font": "\u7cfb\u7edf\u5b57\u4f53",
  "Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
  "Failed to load plugin: {0} from url {1}":
    "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
  "Failed to load plugin url: {0}":
    "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
  "Failed to initialize plugin: {0}":
    "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
  example: "\u793a\u4f8b",
  Search: "\u641c\u7d22",
  All: "\u5168\u90e8",
  Currency: "\u8d27\u5e01",
  Text: "\u6587\u5b57",
  Quotations: "\u5f15\u7528",
  Mathematical: "\u6570\u5b66",
  "Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
  Symbols: "\u7b26\u53f7",
  Arrows: "\u7bad\u5934",
  "User Defined": "\u81ea\u5b9a\u4e49",
  "dollar sign": "\u7f8e\u5143\u7b26\u53f7",
  "currency sign": "\u8d27\u5e01\u7b26\u53f7",
  "euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
  "colon sign": "\u5192\u53f7",
  "cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
  "french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
  "lira sign": "\u91cc\u62c9\u7b26\u53f7",
  "mill sign": "\u5bc6\u5c14\u7b26\u53f7",
  "naira sign": "\u5948\u62c9\u7b26\u53f7",
  "peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
  "rupee sign": "\u5362\u6bd4\u7b26\u53f7",
  "won sign": "\u97e9\u5143\u7b26\u53f7",
  "new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
  "dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
  "kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
  "tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
  "drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
  "german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
  "peso sign": "\u6bd4\u7d22\u7b26\u53f7",
  "guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
  "austral sign": "\u6fb3\u5143\u7b26\u53f7",
  "hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
  "cedi sign": "\u585e\u5730\u7b26\u53f7",
  "livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
  "spesmilo sign": "spesmilo\u7b26\u53f7",
  "tenge sign": "\u575a\u6208\u7b26\u53f7",
  "indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
  "turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
  "nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
  "manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
  "ruble sign": "\u5362\u5e03\u7b26\u53f7",
  "yen character": "\u65e5\u5143\u5b57\u6837",
  "yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
  "yuan character, in hong kong and taiwan":
    "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
  "yen/yuan character variant one":
    "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
  "Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
  "Could not load emoticons":
    "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
  People: "\u4eba\u7c7b",
  "Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
  "Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
  Activity: "\u6d3b\u52a8",
  "Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
  Objects: "\u7269\u4ef6",
  Flags: "\u65d7\u5e1c",
  Characters: "\u5b57\u7b26",
  "Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
  "{0} characters": "{0} \u4e2a\u5b57\u7b26",
  "Error: Form submit field collision.":
    "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
  "Error: No form element found.":
    "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
  Update: "\u66f4\u65b0",
  "Color swatch": "\u989c\u8272\u6837\u672c",
  Turquoise: "\u9752\u7eff\u8272",
  Green: "\u7eff\u8272",
  Blue: "\u84dd\u8272",
  Purple: "\u7d2b\u8272",
  "Navy Blue": "\u6d77\u519b\u84dd",
  "Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
  "Dark Green": "\u6df1\u7eff\u8272",
  "Medium Blue": "\u4e2d\u84dd\u8272",
  "Medium Purple": "\u4e2d\u7d2b\u8272",
  "Midnight Blue": "\u6df1\u84dd\u8272",
  Yellow: "\u9ec4\u8272",
  Orange: "\u6a59\u8272",
  Red: "\u7ea2\u8272",
  "Light Gray": "\u6d45\u7070\u8272",
  Gray: "\u7070\u8272",
  "Dark Yellow": "\u6697\u9ec4\u8272",
  "Dark Orange": "\u6df1\u6a59\u8272",
  "Dark Red": "\u6df1\u7ea2\u8272",
  "Medium Gray": "\u4e2d\u7070\u8272",
  "Dark Gray": "\u6df1\u7070\u8272",
  "Light Green": "\u6d45\u7eff\u8272",
  "Light Yellow": "\u6d45\u9ec4\u8272",
  "Light Red": "\u6d45\u7ea2\u8272",
  "Light Purple": "\u6d45\u7d2b\u8272",
  "Light Blue": "\u6d45\u84dd\u8272",
  "Dark Purple": "\u6df1\u7d2b\u8272",
  "Dark Blue": "\u6df1\u84dd\u8272",
  Black: "\u9ed1\u8272",
  White: "\u767d\u8272",
  "Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
  "Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
  history: "\u5386\u53f2",
  styles: "\u6837\u5f0f",
  formatting: "\u683c\u5f0f\u5316",
  alignment: "\u5bf9\u9f50",
  indentation: "\u7f29\u8fdb",
  Font: "\u5b57\u4f53",
  Size: "\u5b57\u53f7",
  "More...": "\u66f4\u591a...",
  "Select...": "\u9009\u62e9...",
  Preferences: "\u9996\u9009\u9879",
  Yes: "\u662f",
  No: "\u5426",
  "Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
  Version: "\u7248\u672c",
  "Code view": "\u4ee3\u7801\u89c6\u56fe",
  "Open popup menu for split buttons":
    "\u6253\u5f00\u5f39\u51fa\u5f0f\u83dc\u5355\uff0c\u7528\u4e8e\u62c6\u5206\u6309\u94ae",
  "List Properties": "\u5217\u8868\u5c5e\u6027",
  "List properties...": "\u6807\u9898\u5b57\u4f53\u5c5e\u6027",
  "Start list at number": "\u4ee5\u6570\u5b57\u5f00\u59cb\u5217\u8868",
  "Line height": "\u884c\u9ad8",
  comments: "\u5907\u6ce8",
  "Format Painter": "\u683c\u5f0f\u5237",
  "Insert/edit iframe": "\u63d2\u5165/\u7f16\u8f91\u6846\u67b6",
  Capitalization: "\u5927\u5199",
  lowercase: "\u5c0f\u5199",
  UPPERCASE: "\u5927\u5199",
  "Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
  "permanent pen": "\u8bb0\u53f7\u7b14",
  "Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
  "Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
  "case change": "\u6848\u4f8b\u66f4\u6539",
  "page embed": "\u9875\u9762\u5d4c\u5165",
  "Advanced sort...": "\u9ad8\u7ea7\u6392\u5e8f...",
  "Advanced Sort": "\u9ad8\u7ea7\u6392\u5e8f",
  "Sort table by column ascending": "\u6309\u5217\u5347\u5e8f\u8868",
  "Sort table by column descending": "\u6309\u5217\u964d\u5e8f\u8868",
  Sort: "\u6392\u5e8f",
  Order: "\u6392\u5e8f",
  "Sort by": "\u6392\u5e8f\u65b9\u5f0f",
  Ascending: "\u5347\u5e8f",
  Descending: "\u964d\u5e8f",
  "Column {0}": "\u5217{0}",
  "Row {0}": "\u884c{0}",
  "Spellcheck...": "\u62fc\u5199\u68c0\u67e5...",
  "Misspelled word": "\u62fc\u5199\u9519\u8bef\u7684\u5355\u8bcd",
  Suggestions: "\u5efa\u8bae",
  Change: "\u66f4\u6539",
  "Finding word suggestions": "\u67e5\u627e\u5355\u8bcd\u5efa\u8bae",
  Success: "\u6210\u529f",
  Repair: "\u4fee\u590d",
  "Issue {0} of {1}": "\u5171\u8ba1{1}\u95ee\u9898{0}",
  "Images must be marked as decorative or have an alternative text description":
    "\u56fe\u50cf\u5fc5\u987b\u6807\u8bb0\u4e3a\u88c5\u9970\u6027\u6216\u5177\u6709\u66ff\u4ee3\u6587\u672c\u63cf\u8ff0",
  "Images must have an alternative text description. Decorative images are not allowed.":
    "\u56fe\u50cf\u5fc5\u987b\u5177\u6709\u66ff\u4ee3\u6587\u672c\u63cf\u8ff0\u3002\u4e0d\u5141\u8bb8\u4f7f\u7528\u88c5\u9970\u56fe\u50cf\u3002",
  "Or provide alternative text:":
    "\u6216\u63d0\u4f9b\u5907\u9009\u6587\u672c\uff1a",
  "Make image decorative:": "\u4f7f\u56fe\u50cf\u88c5\u9970\uff1a",
  "ID attribute must be unique":
    "ID \u5c5e\u6027\u5fc5\u987b\u662f\u552f\u4e00\u7684",
  "Make ID unique": "\u4f7f ID \u72ec\u4e00\u65e0\u4e8c",
  "Keep this ID and remove all others":
    "\u4fdd\u7559\u6b64 ID \u5e76\u5220\u9664\u6240\u6709\u5176\u4ed6",
  "Remove this ID": "\u5220\u9664\u6b64 ID",
  "Remove all IDs": "\u6e05\u9664\u5168\u90e8IDs",
  Checklist: "\u6e05\u5355",
  Anchor: "\u951a\u70b9",
  "Special character": "\u7279\u6b8a\u7b26\u53f7",
  "Code sample": "\u4ee3\u7801\u793a\u4f8b",
  Color: "\u989c\u8272",
  "Document properties": "\u6587\u6863\u5c5e\u6027",
  "Image description": "\u56fe\u7247\u63cf\u8ff0",
  Image: "\u56fe\u7247",
  "Insert link": "\u63d2\u5165\u94fe\u63a5",
  Target: "\u6253\u5f00\u65b9\u5f0f",
  Link: "\u94fe\u63a5",
  Poster: "\u5c01\u9762",
  Media: "\u5a92\u4f53",
  Print: "\u6253\u5370",
  Prev: "\u4e0a\u4e00\u4e2a",
  "Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
  "Whole words": "\u5168\u5b57\u5339\u914d",
  "Insert template": "\u63d2\u5165\u6a21\u677f",
});

第四步(封装组件)

在src/components文件夹中封装Tinymce组件,文件目录如下:

plugins.js用来指定哪些插件被用在当前编辑器实例中:

// Any plugins you want to use has to be imported
// Deatil plugins list see https://www.tiny.cloud/docs/plugins/

const plugins = [
  `advlist autolink lists link image charmap print preview anchor,
  searchreplace visualblocks code fullscreen,
  insertdatetime media table  help wordcount image  `
]

export default plugins

toolbar.js用来配置工具栏上可用的按钮,多个控件使用空格分隔,使用“|”来创建分组:

// Here is a list of the toolbar
// Detail list see https://www.tiny.cloud/docs/general-configuration-guide/basic-setup/#toolbarconfiguration

const toolbars = ` | undo redo  | cut copy  | bold italic forecolor backcolor |
                  alignleft aligncenter alignright alignjustify | formatselect  | image  | fullscreen preview  | help `

export default toolbars

 index.vue文件完整内容如下:

<template>
  <editor
    v-if="status === 'success'"
    v-model="myValue"
    :init="init"
  >
  </editor>
  <!--  apiKey的使用方法  -->
  <!--  <editor-->
  <!--    v-model="myValue"-->
  <!--    :init="init"-->
  <!--    :api-key="apiKey"-->
  <!--  >-->
  <!--  </editor>-->

</template>
<script>
import Editor from "@tinymce/tinymce-vue"
import {uploadFile} from "@/request";
import config from "@/config";
import plugins from './plugins'
import toolbar from './toolbar'

const defaultConfig = {
  width: 1000,
  height: 600,
  menubar: true,
  language: 'zh_CN'
}
export default {
  components: {
    Editor
  },
  props: {
    value: {
      type: String,
      default: "",
    },
  config: {
    type: Object,
    default: () => {
      return {
        width: 1000,
        height: 600,
        menubar: true,
        language: 'zh_CN'
      }
    }
  }
  },
  data() {
    return {
      myValue: this.value,
      apiKey: "在TinyMCE中申请的apiKey",//使用本地加载js的话可以不用apiKey'
      init: {
        plugins,
        toolbar,
        convert_urls: false,// 上传图片文件路径变为相对路径的解决方法
        removed_menuitems: 'media',
        width: Object.assign(defaultConfig, this.config).width,
        height: Object.assign(defaultConfig, this.config).height,
        menubar: Object.assign(defaultConfig, this.config).menubar,
        language: Object.assign(defaultConfig, this.config).language,
        language_url: "/tinymce/langs/zh_CN.js",// 汉化包
        resize: 'both',
        // 自定义图片上传
        images_upload_handler: async (blobInfo, success, failure) => {
          try {
            let formData = new FormData();
            formData.append("file", blobInfo.blob())
            formData.append("type", 1);
            let res = await uploadFile(formData);
            let url = `${config.downloadUrl}${res.url}`
            success(url)
          } catch (err) {
            console.log(err)
          }
        },
      },
      // tinymce组件script
      tinyScript: null,
      status: 'pending'
    }
  },

  watch: {
    value(newValue) {
      this.myValue = newValue;
    },
    myValue(newValue) {
      this.$emit("input", newValue);
    },
  },
  created() {
    this.initTinyMceScript().then(() => {
      tinymce.init({})
      this.status = 'success'
    })
  },
  methods: {
    initTinyMceScript() {
      return new Promise(resolve => {
        if (this.tinyScript) {
          return resolve(null)
        }
        this.tinyScript = document.createElement("script");
        this.tinyScript.type = "text/javascript";
        this.tinyScript.src = "/tinymce/tinymce.min.js";
        document.body.appendChild(this.tinyScript);
        this.tinyScript.onload = () => {
          resolve(null)
        }
      })
    },
    desTinyMceScript() {
      if (this.tinyScript) {
        document.body.removeChild(this.tinyScript)
        this.tinyScript = null
      }
    }
  },
  beforeDestroy() {
    this.desTinyMceScript()
  }
}
</script>

封装的组件代码都在上面,然后简单讲下上面代码中踩过的坑及注意点:

1.动态引入JS文件

前面说过在项目中可能存在tinymce.min.js文件引入时机问题导致的报错,所以我们需要在组件内部动态引用js,代码如下,封装创建和销毁方法,在页面创建script标签并给属性赋值,然后监听加载完成,当加载完成后再去执行后续操作。

    initTinyMceScript() {
      return new Promise(resolve => {
        if (this.tinyScript) {
          return resolve(null)
        }
        this.tinyScript = document.createElement("script");
        this.tinyScript.type = "text/javascript";
        this.tinyScript.src = "/tinymce/tinymce.min.js";
        document.body.appendChild(this.tinyScript);
        this.tinyScript.onload = () => {
          resolve(null)
        }
      })
    },
    desTinyMceScript() {
      if (this.tinyScript) {
        document.body.removeChild(this.tinyScript)
        this.tinyScript = null
      }
    }

 要记得销毁噢:

 beforeDestroy() {
    this.desTinyMceScript()
  }

2.自定义图片上传逻辑

有关图片上传的参数:

使用 images_upload_handler 可自定义上传处理逻辑。使用该配置,则无需使用其他上传配置选项,具体使用如下:

 // 自定义图片上传
        images_upload_handler: async (blobInfo, success, failure) => {
          try {
            let formData = new FormData();
            formData.append("file", blobInfo.blob())
            formData.append("type", 1);
            let res = await uploadFile(formData);
            let url = `${config.downloadUrl}${res.url}`
            success(url)
          } catch (err) {
            console.log(err)
          }
        },

其中图片上传uploadFile()方法如下,这里就使用自己项目中封装的图片上传方法就OK: 

/**
 * 上传文件
 * @param formData formData对象
 */
const uploadFile = (formData: FormData) => request({
  url: cusConfig.uploadUrl,
  method: "POST",
  headers: {
    'Content-Type': 'multipart/form-data'
  },
  data: formData
})

如此就实现了图片的自定义上传。

3.上传图片文件路径变为相对路径

使用TinyMCE上传图片文件,接口返回相对根目录的地址,但是在TinyMCE编辑器源文件里路径变成了../../路径。

这很明显不是我们想要的,这样的路径后面是拿不到图片的,这里就需要配置convert_urls:false解决问题:

convert_urls: false,// 上传图片文件路径变为相对路径的解决方法

4.图片批量上传(非官方插件的使用)

首先去大佬的Tiny中文文档下载插件中文文档地址

 将解压得到的文件夹,放到pubilc/tinymce目录下的plugins文件夹内:

并且创建index.js文件,内容如下:

// Exports the "axupimgs" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/axupimgs')
//   ES2015:
//     import 'tinymce/plugins/axupimgs'
require('./plugin.js');

接下来就去 toolbar.js和plugins.js中添加axupimgs:

 此插件依赖image插件,不能单独使用,我们前面已经配置了图片的自定义上传,这里就不需要再做配置,功能如下:

总结

TinyMCE确实是一款易用、且功能强大的所见即所得的富文本编辑器,并且可扩展性非常强,如果文中出现有瑕疵的地方各位通过评论或者私信联系我,我们一起进步,有兴趣的伙伴可以关注订阅一下:点击查看更多实用技巧及技术

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琢鸣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值