Richtext.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <template>
  2. <div class="richText">
  3. <div class="box" style="">
  4. <Toolbar
  5. class="boxToolbar"
  6. :editor="editorRef"
  7. :defaultConfig="toolbarConfig"
  8. :mode="mode"
  9. />
  10. <Editor
  11. class="boxEditor"
  12. v-model="valueHtml"
  13. :defaultConfig="editorConfig"
  14. :mode="mode"
  15. @onCreated="handleCreated"
  16. @onChange="handleChange"
  17. @onDestroyed="handleDestroyed"
  18. @onFocus="handleFocus"
  19. @onBlur="handleBlur"
  20. @customAlert="customAlert"
  21. @customPaste="customPaste"
  22. />
  23. </div>
  24. </div>
  25. </template>
  26. <script setup>
  27. import { useRouter } from "vue-router";
  28. import { ElMessage, ElMessageBox } from "element-plus";
  29. import { Calendar } from "@element-plus/icons-vue";
  30. import { dayjs } from "element-plus";
  31. import lodash, { reduce } from "lodash";
  32. import { uploadFile } from "@/api/uploadFile";
  33. import "@wangeditor/editor/dist/css/style.css"; // 引入 css
  34. import { onBeforeUnmount, nextTick, ref, shallowRef, onMounted } from "vue";
  35. import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
  36. import { DomEditor, createToolbar } from "@wangeditor/editor";
  37. // 编辑器实例,必须用 shallowRef
  38. const editorRef = shallowRef();
  39. const props = defineProps({
  40. fatherMessage: {},
  41. });
  42. const emit = defineEmits(["richtextClick"]);
  43. // 内容 HTML
  44. const valueHtml = ref();
  45. const toolbarConfig = {
  46. // JS 语法
  47. /* 工具栏配置 */
  48. excludeKeys: [
  49. "insertLink",
  50. "insertVideo",
  51. "insertTable",
  52. "fullScreen",
  53. "insertImage",
  54. // "group-image",
  55. "group-video",
  56. "todo",
  57. ],
  58. };
  59. const uploadImg = async (file, insertFn) => {
  60. // let imgData = new FormData();
  61. console.log(file);
  62. let srcImg = file;
  63. let data = new FormData();
  64. data.set("file", srcImg);
  65. let res = await uploadFile(data);
  66. console.log(res, "导入照片");
  67. if (res.code == 200) {
  68. let href = res.data.fileUrl;
  69. insertFn(href);
  70. ElMessage({
  71. type: "success",
  72. showClose: true,
  73. message: res.message,
  74. center: true,
  75. });
  76. } else {
  77. ElMessage({
  78. type: "error",
  79. showClose: true,
  80. message: res.message,
  81. center: true,
  82. });
  83. }
  84. };
  85. const editorConfig = {
  86. placeholder: "请输入内容......",
  87. MENU_CONF: {
  88. uploadImage: {
  89. // 自定义上传图片 方法
  90. customUpload: uploadImg,
  91. // 自定义插入图片 方法
  92. // customInsert: insertImg,
  93. //上传图片配置
  94. // server: "https:// jgy-1325577833.cos.ap-guangzhou.myqcloud.com", //上传接口地址
  95. fieldName: "file", //上传文件名
  96. methods: "post",
  97. metaWithUrl: false, // 参数拼接到 url 上
  98. // 单个文件上传成功之后
  99. // onSuccess(file, res) {
  100. // console.log(file, res);
  101. // },
  102. // 自定义插入图片
  103. customInsert(res, insertFn) {
  104. console.log(res);
  105. // insertFn(res.url)
  106. },
  107. },
  108. // insertImage: {
  109. // onInsertedImage(imageNode) {
  110. // if (imageNode == null) return;
  111. // const { src, alt, url, href } = imageNode;
  112. // console.log("inserted image", src, alt, url, href);
  113. // },
  114. // checkImage: customCheckImageFn, // 也支持 async 函数
  115. // parseImageSrc: customParseImageSrc, // 也支持 async 函数
  116. // },
  117. // editImage: {
  118. // onUpdatedImage(imageNode) {
  119. // if (imageNode == null) return;
  120. // const { src, alt, url } = imageNode;
  121. // console.log("updated image", src, alt, url);
  122. // },
  123. // checkImage: customCheckImageFn, // 也支持 async 函数
  124. // parseImageSrc: customParseImageSrc, // 也支持 async 函数
  125. // },
  126. },
  127. };
  128. const mode = ref("default"); // 默认模式
  129. // const mode = ref("simple"); // 简易模式
  130. const handleChange = (editor) => {
  131. // console.log("change:", editor);
  132. // console.log("change:", editor.getText());
  133. // console.log("change:", editor.getHtml());
  134. emit("richtextClick", {
  135. html: editor.getHtml(),
  136. text: editor.getText(),
  137. });
  138. };
  139. const handleDestroyed = (editor) => {
  140. console.log("destroyed", editor);
  141. };
  142. const handleFocus = (editor) => {
  143. console.log("focus", editor);
  144. const toolbar = DomEditor.getToolbar(editor);
  145. const curToolbarConfig = toolbar.getConfig();
  146. console.log(curToolbarConfig.toolbarKeys); // 当前菜单排序和分组
  147. };
  148. const handleBlur = (editor) => {
  149. console.log("blur", editor);
  150. };
  151. const customAlert = (info, type) => {
  152. alert(`【自定义提示】${type} - ${info}`);
  153. };
  154. // 粘贴事件对象
  155. const customPaste = (editor, event, callback) => {
  156. console.log("ClipboardEvent 粘贴事件对象", event);
  157. const html = event.clipboardData.getData("text/html"); // 获取粘贴的 html
  158. const text = event.clipboardData.getData("text/plain"); // 获取粘贴的纯文本
  159. const rtf = event.clipboardData.getData("text/rtf"); // 获取 rtf 数据(如从 word wsp 复制粘贴)
  160. // console.log(html);
  161. // 自定义插入内容
  162. // editor.insertText("自定义插入内容");
  163. // 返回 false ,阻止默认粘贴行为
  164. // event.preventDefault();
  165. // callback(false); // 返回值(注意,vue 事件的返回值,不能用 return)
  166. // 返回 true ,继续默认的粘贴行为
  167. callback(true);
  168. };
  169. // // 自定义图片上传
  170. // editorConfig.MENU_CONF["uploadImage"] = {
  171. // async customUpload(file, insertFn) {
  172. // let formData = new FormData();
  173. // formData.append("files", file);
  174. // try {
  175. // // 这里结合实际场景写自己上传图片的逻辑,此处代码仅为示例
  176. // const { data } = await upload(formData);
  177. // // 对图片进行处理,同样需要结合实际场景
  178. // data.forEach((item) => {
  179. // insertFn(item, "image", item);
  180. // });
  181. // } catch (error) {
  182. // console.log(error);
  183. // }
  184. // },
  185. // };
  186. const handleCreated = (editor) => {
  187. editorRef.value = editor; // 记录 editor 实例,重要!
  188. // console.log(props.fatherMessage);
  189. editor.setHtml(props.fatherMessage.data);
  190. if (props.fatherMessage.flag) {
  191. editor.enable();
  192. } else {
  193. editor.disable();
  194. }
  195. };
  196. // 模拟 ajax 异步获取内容
  197. onMounted(() => {
  198. // console.log(props.fatherMessage.data);
  199. });
  200. // 组件销毁时,也及时销毁编辑器
  201. onBeforeUnmount(() => {
  202. console.log("销毁组件");
  203. const editor = editorRef.value;
  204. if (editor == null) return;
  205. editor.destroy();
  206. });
  207. </script>
  208. <style scoped lang="scss">
  209. .richText {
  210. width: 100%;
  211. .box {
  212. .boxToolbar {
  213. border-top: 1px solid #ccc;
  214. border-left: 1px solid #ccc;
  215. border-right: 1px solid #ccc;
  216. }
  217. .boxEditor {
  218. min-height: 200px !important;
  219. max-height: 500px !important;
  220. overflow: auto;
  221. border: 1px solid #ccc;
  222. }
  223. }
  224. }
  225. </style>