赞
踩
复制粘贴修改一下直接用,也写了相关的注释。
- npm install @wangeditor/editor
- npm install @wangeditor/editor-for-vue
- <template>
- <div style="border: 1px solid #ccc">
- <Toolbar
- style="border-bottom: 1px solid #ccc"
- :editor="editor"
- :defaultConfig="toolbarConfig"
- :mode="mode"
- />
- <Editor
- style="height: 500px; overflow-y: hidden"
- v-model="html"
- :defaultConfig="editorConfig"
- :mode="mode"
- @onCreated="onCreated"
- @onChange="onChange"
- />
- </div>
- </template>
-
- <script>
- // import Location from "@/utils/location";
- import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
- import { Boot, IModuleConf, DomEditor } from "@wangeditor/editor";
- import { getToken } from "@/utils/auth";
- import MyPainter from "./geshi";
- const menu1Conf = {
- key: "geshi", // 定义 menu key :要保证唯一、不重复(重要)
- factory() {
- return new MyPainter(); // 把 `YourMenuClass` 替换为你菜单的 class
- },
- };
- const module = {
- // JS 语法
- menus: [menu1Conf],
-
- // 其他功能,下文讲解...
- };
- Boot.registerModule(module);
- export default {
- components: { Editor, Toolbar },
- props: {
- relationKey: {
- type: String,
- default: "",
- },
- },
- created() {
- console.log(this.editorConfig.MENU_CONF.uploadImage.meta.activityKey);
- },
- data() {
- return {
- // 富文本实例
- editor: null,
- // 富文本正文内容
- html: "",
- // 编辑器模式
- mode: "default", // or 'simple'
- // 工具栏配置
- toolbarConfig: {
- //新增菜单
- insertKeys: {
- index: 32,
- keys: ["geshi"],
- },
- //去掉网络上传图片和视频
- excludeKeys: ["insertImage", "insertVideo"],
- },
- // 编辑栏配置
- editorConfig: {
- placeholder: "请输入相关内容......",
- // 菜单配置
- MENU_CONF: {
- // ===================
- // 上传图片配置
- // ===================
- uploadImage: {
- // 文件名称
- fieldName: "contentAttachImage",
- server: Location.serverPath + "/editor-upload/upload-image",
- headers: {
- Authorization: "Bearer " + getToken(),
- },
- meta: {
- activityKey: this.relationKey,
- },
- // 单个文件的最大体积限制,默认为 20M
- maxFileSize: 20 * 1024 * 1024,
- // 最多可上传几个文件,默认为 100
- maxNumberOfFiles: 10,
- // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
- allowedFileTypes: ["image/*"],
- // 跨域是否传递 cookie ,默认为 false
- withCredentials: true,
- // 超时时间,默认为 10 秒
- timeout: 5 * 1000,
- // 自定义插入图片操作
- customInsert: (res, insertFn) => {
- if (res.errno == -1) {
- this.$message.error("上传失败!");
- return;
- }
- insertFn(Location.serverPath + res.data.url, "", "");
- this.$message.success("上传成功!");
- },
- },
- // =====================
- // 上传视频配置
- // =====================
- uploadVideo: {
- // 文件名称
- fieldName: "contentAttachVideo",
- server: Location.serverPath + "/editor-upload/upload-video",
- headers: {
- Authorization: "Bearer " + getToken(),
- },
- meta: {
- activityKey: this.relationKey,
- },
- // 单个文件的最大体积限制,默认为 60M
- maxFileSize: 60 * 1024 * 1024,
- // 最多可上传几个文件,默认为 100
- maxNumberOfFiles: 3,
- // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
- allowedFileTypes: ["video/*"],
- // 跨域是否传递 cookie ,默认为 false
- withCredentials: true,
- // 超时时间,默认为 10 秒
- timeout: 15 * 1000,
- // 自定义插入图片操作
- customInsert: (res, insertFn) => {
- if (res.errno == -1) {
- this.$message.error("上传失败!");
- return;
- }
- insertFn(Location.serverPath + res.data.url, "", "");
- this.$message.success("上传成功!");
- },
- },
- },
- },
-
- // ===== data field end =====
- };
- },
- methods: {
- // =============== Editor 事件相关 ================
- // 1. 创建 Editor 实例对象
- onCreated(editor) {
- this.editor = Object.seal(editor); // 一定要用 Object.seal() ,否则会报错
- this.$nextTick(() => {
- const toolbar = DomEditor.getToolbar(this.editor);
- const curToolbarConfig = toolbar.getConfig();
- console.log("【 curToolbarConfig 】-39", curToolbarConfig);
- });
- },
- // 2. 失去焦点事件
- onChange(editor) {
- this.$emit("change", this.html);
- },
-
- // =============== Editor操作API相关 ==============
- insertText(insertContent) {
- const editor = this.editor; // 获取 editor 实例
- if (editor == null) {
- return;
- }
- // 执行Editor的API插入
- editor.insertText(insertContent);
- },
-
- // =============== 组件交互相关 ==================
- // closeEditorBeforeComponent() {
- // this.$emit("returnEditorContent", this.html);
- // },
- closeContent(){
- this.html=''
- },
- // ========== methods end ===============
- },
- mounted() {
- // ========== mounted end ===============
- },
- beforeDestroy() {
- const editor = this.editor;
- if (editor == null) {
- return;
- }
- editor.destroy();
- console.log("销毁编辑器!");
- },
- };
- </script>
- <style lang="scss" scoped>
- // 对默认的p标签进行穿透
- ::v-deep .editorStyle .w-e-text-container [data-slate-editor] p {
- margin: 0 !important;
- }
- </style>
- <style src="@wangeditor/editor/dist/css/style.css"></style>

- 自定义上传图片接口
- uploadImage: {
- // 文件名称
- fieldName: "contentAttachImage",
- // server: '/api/v1/public/uploadFile',
- headers: {
- Authorization: "Bearer " + getToken(),
- },
- meta: {
- activityKey: this.relationKey,
- },
- // 单个文件的最大体积限制,默认为 20M
- maxFileSize: 20 * 1024 * 1024,
- // 最多可上传几个文件,默认为 100
- maxNumberOfFiles: 10,
- // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
- allowedFileTypes: ["image/*"],
- // 跨域是否传递 cookie ,默认为 false
- withCredentials: true,
- // 超时时间,默认为 10 秒
- timeout: 5 * 1000,
- 这里设置
- customUpload: async (file, insertFn) => {
- console.log(file, "file");
- let formData = new FormData()
- const sub = "order";
- formData.append('file', file)
- formData.append("sub", sub);
- formData.append("type", "1");
- let res = await getUploadImg(formData)
- insertFn(res.data.full_path, '', '');
- },
- customInsert: (res, insertFn) => {
- if (res.errno == -1) {
- this.$message.error("上传失败!");
- return;
- }
- // insertFn(res.data.url, "", "");
- this.$message.success("上传成功!");
- },
- },

-
- import {
- SlateEditor,
- SlateText,
- SlateElement,
- SlateTransforms,
- DomEditor,
- // Boot,
- } from "@wangeditor/editor";
- // Boot.registerMenu(menu1Conf);
- import { Editor } from "slate";
- export default class MyPainter {
- constructor() {
- this.title = "格式刷"; // 自定义菜单标题
- // 这里是设置格式刷的样式图片跟svg都可以,但是注意要图片大小要小一点,因为要应用到鼠标手势上
- this.iconSvg = ``;
- this.tag = "button"; //注入的菜单类型
- this.savedMarks = null; //保存的样式
- this.domId = null; //这个可要可不要
- this.editor = null; //编辑器示例
- this.parentStyle = null; //储存父节点样式
- this.mark = "";
- this.marksNeedToRemove = []; // 增加 mark 的同时,需要移除哪些 mark (互斥,不能共存的)
- }
- clickHandler(e) {
- console.log(e, "e"); //无效
- }
- //添加或者移除鼠标事件
- addorRemove = (type) => {
- const dom = document.body;
- if (type === "add") {
- dom.addEventListener("mousedown", this.changeMouseDown);
- dom.addEventListener("mouseup", this.changeMouseup);
- } else {
- //赋值完需要做的清理工作
- this.savedMarks = undefined;
- dom.removeEventListener("mousedown", this.changeMouseDown);
- dom.removeEventListener("mouseup", this.changeMouseup);
- document.querySelector("#w-e-textarea-1").style.cursor = "auto";
- }
- };
-
- //处理重复键名值不同的情况
- handlerRepeatandNotStyle = (styles) => {
- const addStyles = styles[0];
- const notVal = [];
- for (const style of styles) {
- for (const key in style) {
- const value = style[key];
- if (!addStyles.hasOwnProperty(key)) {
- addStyles[key] = value;
- } else {
- if (addStyles[key] !== value) {
- notVal.push(key);
- }
- }
- }
- }
- for (const key of notVal) {
- delete addStyles[key];
- }
- return addStyles;
- };
-
- // 获取当前选中范围的父级节点
- getSelectionParentEle = (type, func) => {
- if (this.editor) {
- const parentEntry = SlateEditor.nodes(this.editor, {
- match: (node) => SlateElement.isElement(node),
- });
- let styles = [];
- for (const [node] of parentEntry) {
- styles.push(this.editor.toDOMNode(node).style); //将node对应的DOM对应的style对象加入到数组
- }
- styles = styles.map((item) => {
- //处理不为空的style
- const newItem = {};
- for (const key in item) {
- const val = item[key];
- if (val !== "") {
- newItem[key] = val;
- }
- }
- return newItem;
- });
- type === "get"
- ? func(type, this.handlerRepeatandNotStyle(styles))
- : func(type);
- }
- };
-
- //获取或者设置父级样式
- getorSetparentStyle = (type, style) => {
- if (type === "get") {
- this.parentStyle = style; //这里是个样式对象 例如{textAlign:'center'}
- } else {
- SlateTransforms.setNodes(
- this.editor,
- { ...this.parentStyle },
- {
- mode: "highest", // 针对最高层级的节点
- }
- );
- }
- };
-
- //这里是将svg转换为Base64格式
- addmouseStyle = () => {
- const icon = ``; // 这里是给鼠标手势添加图标
- // 将字符串编码为Base64格式
- const base64String = btoa(icon);
- // 生成数据URI
- const dataUri = `data:image/svg+xml;base64,${base64String}`;
- // 将数据URI应用于鼠标图标
- document.querySelector(
- "#w-e-textarea-1"
- ).style.cursor = `url('${dataUri}'), auto`;
- };
- changeMouseDown = () => {}; //鼠标落下
-
- changeMouseup = () => {
- //鼠标抬起
- if (this.editor) {
- const editor = this.editor;
- const selectTxt = editor.getSelectionText(); //获取文本是否为null
- if (this.savedMarks && selectTxt) {
- //先改变父节点样式
- this.getSelectionParentEle("set", this.getorSetparentStyle);
- // 获取所有 text node
- const nodeEntries = SlateEditor.nodes(editor, {
- //nodeEntries返回的是一个迭代器对象
- match: (n) => SlateText.isText(n), //这里是筛选一个节点是否是 text
- universal: true, //当universal为 true 时,Editor.nodes会遍历整个文档,包括根节点和所有子节点,以匹配满足条件的节点。当universal为 false 时,Editor.nodes只会在当前节点的直接子节点中进行匹配。
- });
- // 先清除选中节点的样式
- for (const node of nodeEntries) {
- const n = node[0]; //{text:xxxx}
- const keys = Object.keys(n);
- keys.forEach((key) => {
- if (key === "text") {
- // 保留 text 属性
- return;
- }
- // 其他属性,全部清除
- SlateEditor.removeMark(editor, key);
- });
- }
- // 再赋值新样式
- for (const key in this.savedMarks) {
- if (Object.hasOwnProperty.call(this.savedMarks, key)) {
- const value = this.savedMarks[key];
- editor.addMark(key, value);
- }
- }
- this.addorRemove("remove");
- }
- }
- };
-
- getValue(editor) {
- // return "MyPainter"; // 标识格式刷菜单
- const mark = this.mark;
- console.log(mark, "mark");
- const curMarks = Editor.marks(editor);
- // 当 curMarks 存在时,说明用户手动设置,以 curMarks 为准
- if (curMarks) {
- return curMarks[mark];
- } else {
- const [match] = Editor.nodes(editor, {
- // @ts-ignore
- match: (n) => n[mark] === true,
- });
- return !!match;
- }
- }
-
- isActive(editor, val) {
- const isMark = this.getValue(editor);
- return !!isMark;
- // return !!DomEditor.getSelectedNodeByType(editor, "geshi");
- // return false;
- }
-
- isDisabled(editor) {
- //是否禁用
- return false;
- }
- exec(editor, value) {
- //当菜单点击后触发
- // console.log(!this.isActive());
- console.log(value, "value");
- this.editor = editor;
- this.domId = editor.id.split("-")[1]
- ? `w-e-textarea-${editor.id.split("-")[1]}`
- : undefined;
- if (this.isDisabled(editor)) return;
- const { mark, marksNeedToRemove } = this;
- if (value) {
- // 已,则取消
- editor.removeMark(mark);
- } else {
- // 没有,则执行
- editor.addMark(mark, true);
- this.savedMarks = SlateEditor.marks(editor); // 获取当前选中文本的样式
- this.getSelectionParentEle("get", this.getorSetparentStyle); //获取父节点样式并赋值
- // this.addmouseStyle(); //点击之后给鼠标添加样式
- this.addorRemove("add"); //处理添加和移除事件函数
- // 移除互斥、不能共存的 marks
- if (marksNeedToRemove) {
- marksNeedToRemove.forEach((m) => editor.removeMark(m));
- }
- }
- if (
- editor.isEmpty() ||
- editor.getHtml() == "<p><br></p>" ||
- editor.getSelectionText() == ""
- )
- return; //这里是对没有选中或者没内容做的处理
- }
- }

- <el-form-item label="内容">
- <WangEdit v-model="form.content" ref="wangEdit" @change="change"></WangEdit>
- </el-form-item>
-
-
- // js
- const WangEdit = () => import("@/views/compoments/WangEdit.vue");
- export default {
- name: "Notice",
- components: {
- WangEdit,
- },
- data(){
- return{
- form:{
- }
- }
- },
-
- methods: {
- change(val) {
- console.log(val,'aa');
- this.form.content=val
- },
- // 取消按钮
- cancel() {
- this.open = false;
- this.form={};
- this.$refs.wangEdit.closeContent();
- },
- }
-

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。