index.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <div ref="divRef" :style="{ height, minHeight }" />
  3. </template>
  4. <script setup lang="ts">
  5. import { propTypes } from "@/utils/propTypes";
  6. import { globalHeaders } from "@/utils/request";
  7. import { AiEditor } from "aieditor";
  8. import "aieditor/dist/style.css";
  9. const divRef = ref();
  10. let aiEditor = ref<AiEditor | null>(null);
  11. const baseUrl = import.meta.env.VITE_APP_BASE_API;
  12. const uploadFileUrl = ref(baseUrl + '/resource/oss/upload'); // 上
  13. const headers = ref(globalHeaders());
  14. const props = defineProps({
  15. /* 编辑器的内容 */
  16. modelValue: propTypes.string.def(''),
  17. /* 高度 */
  18. height: propTypes.number.def(400),
  19. /* 最小高度 */
  20. minHeight: propTypes.number.def(400),
  21. /* 只读 */
  22. readOnly: propTypes.bool.def(false),
  23. /* 上传文件大小限制(MB) */
  24. fileSize: propTypes.number.def(5),
  25. /* 类型(base64格式、url格式) */
  26. type: propTypes.string.def('url'),
  27. placeholder: propTypes.string,
  28. });
  29. const emit = defineEmits(['update:modelValue']);
  30. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  31. const initEditor = () => {
  32. aiEditor.value = new AiEditor({
  33. element: divRef.value as Element,
  34. placeholder: props.placeholder || '请输入内容',
  35. draggable: true,
  36. content: props.modelValue,
  37. toolbarKeys: ["undo", "redo", "brush", "eraser",
  38. "|", "heading", "font-family", "font-size",
  39. "|", "bold", "italic", "underline", "strike", "link", "code", "subscript", "superscript", "hr", "todo", "emoji",
  40. "|", "highlight", "font-color",
  41. "|", "align", "line-height",
  42. "|", "bullet-list", "ordered-list", "indent-decrease", "indent-increase", "break",
  43. "|", "image", "video", "attachment", "quote", "code-block", "table",
  44. "|", "source-code", "printer", "fullscreen",
  45. ],
  46. image: {
  47. allowBase64: true,
  48. defaultSize: 350,
  49. // customMenuInvoke: (editor: AiEditor) => {
  50. // },
  51. uploadUrl: uploadFileUrl.value,
  52. uploadFormName: "file", //上传时的文件表单名称
  53. uploadHeaders: {
  54. ...headers.value
  55. },
  56. uploaderEvent: {
  57. onUploadBefore: (file, uploadUrl, headers) => {
  58. //监听图片上传之前,此方法可以不用回任何内容,但若返回 false,则终止上传
  59. return handleBeforeUpload(file);
  60. },
  61. onSuccess: (file, response) => {
  62. //监听图片上传成功
  63. //注意:
  64. // 1、如果此方法返回 false,则图片不会被插入到编辑器
  65. // 2、可以在这里返回一个新的 json 给编辑器
  66. if (response.code == 200) {
  67. proxy?.$modal.closeLoading();
  68. // 根据图片宽高设置默认显示大小
  69. return {
  70. "errorCode": 0,
  71. "data": {
  72. src: response.data.url,
  73. alt: file.name,
  74. loading: true,
  75. "align": "left",
  76. "width": "400px",
  77. "height": "auto",
  78. 'data-src': response.data.url
  79. }
  80. }
  81. }
  82. },
  83. onFailed: (file, response) => {
  84. //监听图片上传失败,或者返回的 json 信息不正确
  85. console.log('onFailed', file, response);
  86. },
  87. onError: (file, error) => {
  88. //监听图片上传错误,比如网络超时等
  89. },
  90. },
  91. bubbleMenuItems: ["AlignLeft", "AlignCenter", "AlignRight", "delete"]
  92. },
  93. video: {
  94. uploadHeaders: {
  95. ...headers.value
  96. },
  97. uploadUrl: uploadFileUrl.value,
  98. uploadFormName: "file", //上传时的文件表单名称
  99. uploaderEvent: {
  100. onUploadBefore: (file, uploadUrl, headers) => {
  101. console.log(file);
  102. //监听视频上传之前,此方法可以不用回任何内容,但若返回 false,则终止上传
  103. return handleBeforeUpload(file);
  104. },
  105. onSuccess: (file, response) => {
  106. //监听视频上传成功
  107. //注意:
  108. // 1、如果此方法返回 false,则视频不会被插入到编辑器
  109. // 2、可以在这里返回一个新的 json 给编辑器
  110. if (response.code == 200) {
  111. proxy?.$modal.closeLoading();
  112. return {
  113. "errorCode": 0,
  114. "data": {
  115. src: response.data.url,
  116. poster: "",
  117. "width": "480px",
  118. "height": "320px",
  119. controls: true,
  120. 'data-src': response.data.url
  121. }
  122. }
  123. }
  124. },
  125. onFailed: (file, response) => {
  126. //监听视频上传失败,或者返回的 json 信息不正确
  127. console.log('onFailed', file, response);
  128. },
  129. onError: (file, error) => {
  130. //监听视频上传错误,比如网络超时等
  131. },
  132. },
  133. },
  134. onChange: (aiEditor) => {
  135. // 监听到用编辑器内容发生变化了,控制台打印编辑器的 html 内容...
  136. emit('update:modelValue', aiEditor.getHtml());
  137. }
  138. })
  139. }
  140. onMounted(() => {
  141. // 等待1s,确保modelValue有值
  142. setTimeout(() => {
  143. initEditor();
  144. }, 1000);
  145. })
  146. // 图片上传前拦截
  147. const handleBeforeUpload = (file: any) => {
  148. const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];
  149. const isJPG = type.includes(file.type);
  150. //检验文件格式
  151. if (!isJPG) {
  152. proxy?.$modal.msgError(`图片格式错误!`);
  153. return false;
  154. }
  155. // 校检文件大小
  156. if (props.fileSize) {
  157. const isLt = file.size / 1024 / 1024 < props.fileSize;
  158. if (!isLt) {
  159. proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
  160. return false;
  161. }
  162. }
  163. proxy?.$modal.loading('正在上传文件,请稍候...');
  164. return true;
  165. };
  166. onUnmounted(() => {
  167. aiEditor.value && aiEditor.value.destroy();
  168. })
  169. </script>
  170. <style>
  171. .aie-content p {
  172. margin: 0;
  173. }
  174. </style>