vue2使用wangEditor:上传图片,视频,设置表格,自定义初始化字体和大小
需求背景:由于门户字体大小默认20px,字体是自定义的思源黑体,新增新闻时,富文本编辑器需要和门户保持一致,需要重新自定义富文本编辑器的配置。
一.安装@wangeditor/editor,@wangeditor/editor-for-vue插件,通过npm,yarn安装
二.封装组件
<template><!-- :class="readOnly ? 'click-disabled' : ''" --><divstyle="border: 1px solid #ccc"contenteditable="false"><Toolbarstyle="border-bottom: 1px solid #ccc":editor="editor":defaultConfig="toolbarConfig":mode="mode"/><Editorstyle="height: 500px; overflow-y: hidden;":editor="editor":defaultConfig="editorConfig":mode="mode"@onCreated="onCreated"@onChange="handleChange"/></div>
</template><script>
import "@/assets/font_icon/iconfont.css"; // 引入思源黑体字体
import { uploadRequest } from "@/api/noticeManage/index.js";
import Vue from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { Loading } from "element-ui";
import { Message } from "element-ui";
export default Vue.extend({components: { Editor, Toolbar },props: {value: {type: String,default: "",},readOnly: {type: Boolean,default: false,},},data() {return {editor: null,toolbarConfig: {toolbarKeys: ["headerSelect","blockquote","|","bold","underline","italic",{key: "group-more-style",title: "更多",iconSvg:'<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>',menuKeys: ["through", "code", "sup", "sub", "clearStyle"],},"color","bgColor","|","fontSize","fontFamily","lineHeight","|","bulletedList","numberedList","todo",{key: "group-justify",title: "对齐",iconSvg:'<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',menuKeys: ["justifyLeft","justifyRight","justifyCenter","justifyJustify",],},{key: "group-indent",title: "缩进",iconSvg:'<svg viewBox="0 0 1024 1024"><path d="M0 64h1024v128H0z m384 192h640v128H384z m0 192h640v128H384z m0 192h640v128H384zM0 832h1024v128H0z m0-128V320l256 192z"></path></svg>',menuKeys: ["indent", "delIndent"],},"|","insertLink",{key: "group-image",title: "图片",iconSvg:'<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',menuKeys: ["insertImage", "uploadImage"],},{key: "group-video",title: "视频",iconSvg:'<svg viewBox="0 0 1024 1024"><path d="M981.184 160.096C837.568 139.456 678.848 128 512 128S186.432 139.456 42.816 160.096C15.296 267.808 0 386.848 0 512s15.264 244.16 42.816 351.904C186.464 884.544 345.152 896 512 896s325.568-11.456 469.184-32.096C1008.704 756.192 1024 637.152 1024 512s-15.264-244.16-42.816-351.904zM384 704V320l320 192-320 192z"></path></svg>',menuKeys: ["insertVideo", "uploadVideo"],},"insertTable","codeBlock","divider","|","undo","redo","|","fullScreen",],},editorConfig: {placeholder: "请输入内容...",readOnly: false,hoverbarKeys: {table: {menuKeys: ["enter","tableHeader","insertTableRow","deleteTableRow","insertTableCol","deleteTableCol","deleteTable",],},},MENU_CONF: {fontSize: {fontSizeList: ["10px","12px","14px","16px","18px","20px","22px","24px","26px","28px","30px","32px","34px","36px",],},// 字体配置 - 添加思源黑体fontFamily: {fontFamilyList: ["思源黑体", // 本地思源黑体 Medium 版本"黑体","仿宋","楷体","标楷体","华文仿宋","华文楷体","宋体","微软雅黑","Arial","Tahoma","Verdana","Times New Roman","Courier New",],},// 上传图片uploadImage: {async customUpload(file, insertFn) {if (!file.type.includes("image")) {Message.error("请选择图片");return;}if (file.size > 30 * 1024 * 1024) {Message.error("图片大小不能超过" + 30 + "MB");return;}const form = new FormData();form.append("file", file);form.append("type", "1"); // 附件类型form.append("secondType", "spxq"); // 多属性区分类型const loading = Loading.service({lock: true,text: "Loading",spinner: "el-icon-loading",background: "rgba(0, 0, 0, 0.7)",});uploadRequest(form).then((res) => {if (res.code == 200) {insertFn(res.result.fileUrl,res.fileName,res.result.fileUrl);loading.close();} else {loading.close();}}).catch((error) => {Message.success(error.message);});},},// 上传视频uploadVideo: {async customUpload(file, insertFn) {if (!file.type.includes("video")) {Message.error("请选择视频");return;}if (file.size > 500 * 1024 * 1024) {Message.error("视频大小不能超过" + 500 + "MB");return;}const form = new FormData();form.append("file", file);form.append("type", "2"); // 附件类型form.append("secondType", "video"); // 多属性区分类型const loading = Loading.service({lock: true,text: "Loading",spinner: "el-icon-loading",background: "rgba(0, 0, 0, 0.7)",});uploadRequest(form).then((res) => {if (res.code == 200) {insertFn(res.result.fileUrl);loading.close();} else {loading.close();}}).catch((error) => {Message.success(error.message);loading.close();});},},},},mode: "default", // or 'simple'};},created() {console.log(this.$attrs.readOnly, 1111);this.editorConfig.readOnly = this.readOnly;},methods: {onCreated(editor) {this.editor = Object.seal(editor);// 初始化编辑器内容if (this.value) {editor.setHtml(this.value);}// 设置默认字号// 设置placeholder样式const placeholder = document.querySelector(".w-e-text-placeholder");if (placeholder) {placeholder.style.fontSize = "20px";placeholder.style.fontFamily = "思源黑体";}this.editor.children.forEach((item) => {// 设置段落缩进和行高item.indent = "2em";item.lineHeight = "2";// 遍历并设置文本样式if (item.children) {item.children.forEach((ele) => {if (ele) {ele.fontSize = "20px";ele.fontFamily = "思源黑体";}});}});},handleChange() {// 编辑器内容变化时,触发 input 事件通知父组件const html = this.editor.getHtml();console.log(html, 2222);if (html !== "<p><br></p>") {this.$emit("input", html);}},},watch: {// 监听父组件传入的 value 变化,更新编辑器内容value(newVal) {if (this.editor && newVal !== this.editor.getHtml()) {this.editor.setHtml(newVal);}},},beforeDestroy() {const editor = this.editor;if (editor == null) return;// 清理字体observerif (this._fontObserver) {this._fontObserver.disconnect();this._fontObserver = null;}editor.destroy();},
});
</script>
<style lang="scss" scoped>
/* 思源黑体样式设置 - 提高权重覆盖全局body字体 *//* 针对编辑器容器的高权重选择器 */
.wang-editor-container {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;
}
* {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;
}
/* 编辑器内容区域 - 使用多重选择器提高权重 */
:deep(.w-e-text-container),
:deep(.w-e-text),
:deep(.w-e-text-container *),
:deep(.el-dialog__body .w-e-text-container),
:deep(.el-dialog .w-e-text-container) {/* 设置思源黑体为默认字体,权重高于body */font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;
}/* 确保所有文本元素都使用思源黑体 - 高权重选择器 */
:deep(.w-e-text-container p),
:deep(.w-e-text-container div),
:deep(.w-e-text-container span),
:deep(.w-e-text-container h1),
:deep(.w-e-text-container h2),
:deep(.w-e-text-container h3),
:deep(.w-e-text-container h4),
:deep(.w-e-text-container h5),
:deep(.w-e-text-container h6),
:deep(.w-e-text-container li),
:deep(.w-e-text-container ul),
:deep(.w-e-text-container ol),
:deep(.w-e-text-container blockquote),
:deep(.w-e-text-container strong),
:deep(.w-e-text-container em),
:deep(.w-e-text-container u),
:deep(.w-e-text-container i),
:deep(.w-e-text-container b) {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;
}/* 编辑器可编辑区域 */
:deep([contenteditable="true"]) {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;
}/* 占位符文本 */
:deep(.w-e-text-placeholder) {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;top: 10px !important;
}/* 工具栏字体选择器 */
:deep(.w-e-bar-item-menu-list) {.w-e-bar-item-menu-item[data-value="思源黑体"] {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;}
}/* 覆盖全局body样式的高权重选择器 */
div[style*="border: 1px solid #ccc"] :deep(.w-e-text-container),
div[style*="border: 1px solid #ccc"] :deep(.w-e-text-container *) {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;
}/* 确保在任何情况下都使用思源黑体 */
:deep(.w-e-text-container) {* {font-family: "思源黑体", "Source Han Sans CN", sans-serif !important;}
}h1 {font-size: 36px !important;
}
.title {margin: 0 !important;
}
.w-e-text-placeholder {top: 10px !important;
}
.click-disabled {pointer-events: none; /* 禁用点击 */opacity: 0.5; /* 可选:视觉提示 */
}
</style>
<style src="@wangeditor/editor/dist/css/style.css"></style>
覆盖编辑器字体和字体大小重点逻辑:
1.引入import “@/assets/font_icon/iconfont.css”; // 引入思源黑体字体
2.MENU_CONF.fontFamily.fontFamilyList下面添加思源黑体
3.在onCreated设置placeholder样式和内容的大小(组件263-284行)
4.style下面css样式覆盖(组件318-389行)
在这里插入代码片
三.页面引用
<template><div class="app-add"><el-dialog:title="dialogTitle + '公告信息'":visible.sync="dialogVisible"fullscreencenter:close-on-click-modal="false":close-on-press-escape="false":before-close="cancel"><el-formv-if="dialogVisible":model="form"ref="formRef"class="form-container":rules="formRules"label-width="80px"><el-row v-if="formStatus == 1"><WangEditorv-model="form.content":readOnly="false"></WangEditor></el-form-item></el-col></el-col></el-row></el-form><span slot="footer" class="dialog-footer"><el-button @click="cancel">取 消</el-button><el-buttonv-if="formStatus == 1"type="primary":loading="btnLoading"@click="debouncedClick"v-preventReClick>提 交</el-button></span></el-dialog></div>
</template>
<script>
import {getNoticeAddMethod,getNoticeEditMethod,uploadRequest,
} from "@/api/noticeManage/index.js";
import WangEditor from "@/components/wangEditor/index.vue";
// import { VueEditor } from "vue2-editor";
import { debounceSubmit } from "@/mixins/debounce.js";
export default {mixins: [debounceSubmit],components: {WangEditor,},props: {dialogVisible: {type: Boolean,},formStatus: { type: Number },defaultForm: { type: Object },dialogTitle: { type: String }, // 判断为新增还是编辑},data() {return {auditDialogVisible: false,// 遮罩层loading: false,// 表单参数form: {},editorSettings: {bounds: "self",},btnLoading: false,}},watch: {defaultForm: {handler(val) {this.form = val;},immediate: true,deep: true,},},methods: {click() {this.emitSave();},changeHandle(index) {this.currentTab = index;},cancel() {this.btnLoading = false;this.$emit("emitSave", false);},audit() {this.auditDialogVisible = true;},/* 保存按钮 */emitSave() {const _this = this;_this.form;this.$refs.formRef.validate((valid) => {if (valid) {this.btnLoading = true;if (_this.dialogTitle == "新增") {getNoticeAddMethod(_this.form).then((res) => {if (res.code == 200) {this.$message({type: "success",message: "新增成功",});_this.$emit("emitSave", false);_this.$emit("getNoticeListData");} else {this.$message({type: "error",message: "新增失败",});}}).finally(() => {this.btnLoading = false;});} else {getNoticeEditMethod(_this.form).then((res) => {if (res.code == 200) {this.$message({type: "success",message: "编辑成功",});_this.$emit("emitSave", false);_this.$emit("getNoticeListData");} else {this.$message({type: "error",message: "编辑失败",});}}).finally(() => {this.btnLoading = false;});}}});},// 富文本图片上传async handleImageAdded(file, Editor, cursorLocation, resetUploader) {const formData = new FormData();formData.append("file", file);formData.append("type", "1"); // 附件类型formData.append("secondType", "spxq"); // 多属性区分类型const res = await uploadRequest(formData);Editor.insertEmbed(cursorLocation, "image", res.result.fileUrl);resetUploader();},},
};
</script>
三.新增一个空内容提交后,后端字段返回的空字串会导致富文本默认的字体失效,应该返回null(时间稍稍有点久,由于项目加密了,自己留一下这个问题)