index.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <template>
  2. <div v-if="state.mounted" :style="style" class="ba-editor wangeditor" v-loading="state.loading">
  3. <Toolbar class="wangeditor-toolbar" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
  4. <Editor ref="editor" :style="state.editorStyle" v-model="state.value" :defaultConfig="state.editorConfig"
  5. :mode="mode" @onCreated="handleCreated" @onChange="handleChange" v-bind="$attrs" />
  6. </div>
  7. </template>
  8. <script setup lang="ts">
  9. import "@wangeditor/editor/dist/css/style.css"; // 引入 css
  10. import { onBeforeUnmount, reactive, shallowRef, onMounted, CSSProperties, watch, nextTick, ref } from "vue";
  11. import { IEditorConfig, IToolbarConfig, i18nChangeLanguage, IDomEditor } from "@wangeditor/editor";
  12. import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
  13. import { useConfig } from "/@/stores/config";
  14. import { fileUpload } from "/@/api/common";
  15. import NProgress from "nprogress";
  16. import { stat } from "fs";
  17. interface Props {
  18. // 编辑区高度
  19. height?: string;
  20. mode?: "default" | "simple";
  21. placeholder?: string;
  22. modelValue: string;
  23. // https://www.wangeditor.com/v5/toolbar-config.html#getconfig
  24. toolbarConfig?: Partial<IToolbarConfig>;
  25. // https://www.wangeditor.com/v5/editor-config.html#placeholder
  26. editorConfig?: Partial<IEditorConfig>;
  27. // 编辑区style
  28. editorStyle?: CSSProperties;
  29. // 整体的style
  30. style?: CSSProperties;
  31. disable?: boolean;
  32. }
  33. type VideoInsertFnType = (url: string, poster: string) => void
  34. type ImgInsertFnType = (url: string, alt: string, href: string) => void
  35. const props = withDefaults(defineProps<Props>(), {
  36. height: "320px",
  37. mode: "default",
  38. placeholder: "请输入内容...",
  39. modelValue: "",
  40. disable: false,
  41. toolbarConfig: () => {
  42. return {};
  43. },
  44. editorConfig: () => {
  45. return {};
  46. },
  47. editorStyle: () => {
  48. {
  49. return {};
  50. }
  51. },
  52. style: () => {
  53. return {};
  54. }
  55. });
  56. const config = useConfig();
  57. const editorRef = shallowRef();
  58. const emits = defineEmits<{
  59. (e: "update:modelValue", value: string): void
  60. }>();
  61. const state: {
  62. mounted: boolean
  63. value: string
  64. editorConfig: Partial<IEditorConfig>
  65. editorStyle: CSSProperties
  66. loading: boolean
  67. } = reactive({
  68. mounted: false,
  69. value: props.modelValue,
  70. editorConfig: props.editorConfig,
  71. editorStyle: props.editorStyle,
  72. loading: false
  73. });
  74. onMounted(() => {
  75. i18nChangeLanguage(config.lang.defaultLang == "zh-cn" ? "zh-CN" : config.lang.defaultLang);
  76. state.editorConfig.placeholder = props.placeholder;
  77. // 图片上传配置
  78. state.editorConfig.MENU_CONF = {};
  79. state.editorConfig.MENU_CONF["uploadImage"] = {
  80. fieldName: "file",
  81. maxFileSize: 10 * 1024 * 1024, // 10M
  82. onProgress(progress: number) {
  83. NProgress.inc();
  84. },
  85. async customUpload(file: File, insertFn: ImgInsertFnType) {
  86. NProgress.configure({ showSpinner: true });
  87. NProgress.start();
  88. let fd = new FormData();
  89. fd.append("file", file);
  90. state.loading = true;
  91. fileUpload(fd)
  92. .then((res) => {
  93. if (res.code == 1) {
  94. insertFn(res.data.previewUrl, res.data.originalFilename, res.data.previewUrl);
  95. }
  96. NProgress.done();
  97. state.loading = false;
  98. })
  99. .catch((err) => {
  100. NProgress.done();
  101. state.loading = false;
  102. });
  103. }
  104. };
  105. // 视频上传配置
  106. state.editorConfig.MENU_CONF["uploadVideo"] = {
  107. fieldName: "file",
  108. onProgress(progress: number) {
  109. NProgress.inc();
  110. },
  111. async customUpload(file: File, insertFn: VideoInsertFnType) {
  112. NProgress.configure({ showSpinner: true });
  113. NProgress.start();
  114. let fd = new FormData();
  115. fd.append("file", file);
  116. state.loading = true;
  117. fileUpload(fd)
  118. .then((res) => {
  119. console.log("上传视频", res.data);
  120. if (res.code == 1) {
  121. let poster = "http://admin.baoxiaojia.dingsenhulian.com/uploadFile/1596051044679421952.jpg";
  122. insertFn(res.data.previewUrl, poster);
  123. }
  124. NProgress.done();
  125. state.loading = false;
  126. })
  127. .catch((err) => {
  128. NProgress.done();
  129. state.loading = false;
  130. });
  131. }
  132. };
  133. state.editorStyle.height = props.height;
  134. state.editorStyle["overflow-y"] = "hidden";
  135. state.mounted = true;
  136. //设置只读
  137. nextTick(() => {
  138. if (props.disable) {
  139. editorRef.value.disable();
  140. }
  141. });
  142. });
  143. const editor = ref();
  144. // 自定义粘贴事件
  145. const handlePaste = (
  146. editor: IDomEditor,
  147. event: ClipboardEvent,
  148. callback: (val: boolean) => void
  149. ) => {
  150. // editor.insertText('test')
  151. callback(false);
  152. };
  153. // 组件销毁时,也及时销毁编辑器
  154. onBeforeUnmount(() => {
  155. if (editorRef.value == null) return;
  156. editorRef.value.destroy();
  157. });
  158. const handleCreated = (editor: any) => {
  159. editorRef.value = editor; // 记录 editor 实例
  160. };
  161. const handleChange = () => {
  162. emits("update:modelValue", editorRef.value.getHtml());
  163. };
  164. const getRef = () => {
  165. return editorRef.value;
  166. };
  167. defineExpose({
  168. getRef
  169. });
  170. watch(
  171. () => props.modelValue,
  172. (newVal) => {
  173. state.value = newVal;
  174. }
  175. );
  176. </script>
  177. <style lang="scss">
  178. .ba-editor {
  179. border: 1px solid var(--color-sub-3);
  180. z-index: 9999;
  181. :deep(.w-e-scroll) {
  182. scrollbar-width: none;
  183. &::-webkit-scrollbar {
  184. width: 5px;
  185. }
  186. &::-webkit-scrollbar-thumb {
  187. background: #eaeaea;
  188. border-radius: var(--el-border-radius-base);
  189. box-shadow: none;
  190. -webkit-box-shadow: none;
  191. }
  192. &:hover {
  193. &::-webkit-scrollbar-thumb:hover {
  194. background: #c8c9cc;
  195. }
  196. }
  197. }
  198. }
  199. .wangeditor-toolbar {
  200. border-bottom: 1px solid var(--color-sub-3);
  201. }
  202. video {
  203. width: 100%;
  204. }
  205. </style>