Vue3+tdesign+tailwindcss封装的WangEditor富文本编辑器组件

以下详细说明基于Vue3+tdesign+tailwindcss(v3)封装的WangEditor富文本编辑器组件。

一、组件结构说明

1. 导入依赖

javascript 复制代码
import "@wangeditor/editor/dist/css/style.css"; // 样式文件
import { Editor, Toolbar } from "@wangeditor/editor-for-vue"; // 编辑器组件

2. 组件属性(Props)

javascript 复制代码
const props = defineProps({
  height: {
    type: String,
    default: "300", // 编辑器高度,默认300px
  },
  disabled: {
    type: Boolean,
    default: false, // 是否禁用编辑器
  },
});

3. 核心配置

  1. 工具栏配置
javascript 复制代码
const toolbarConfig = { excludeKeys: ["fullScreen"] }; // 移除全屏功能
  1. 编辑器配置
javascript 复制代码
const editorConfig = {
  placeholder: "请输入内容...",
  MENU_CONF: {
    uploadImage: {
      // 图片上传配置
      server: "/api/admin/sys/file/upload",
      maxFileSize: 20 * 1024 * 1024, // 20MB
      // ... 其他配置
    },
    uploadVideo: {
      // 视频上传配置
      server: "/api/admin/sys/file/upload",
      maxFileSize: 100 * 1024 * 1024, // 100MB
      maxNumberOfFiles: 5,
      // ... 其他配置
    }
  }
};

二、主要功能实现

1. 图片上传功能

javascript 复制代码
uploadImage: {
  // 支持的图片格式
  onBeforeUpload(file) {
    const fileSuffixList = [".png", ".jpg", ".jpeg", ".gif", ".bmp"];
    const keys = Object.keys(file);
    const ext = "." + file[keys[0]].extension;
    return fileSuffixList.includes(ext.toLowerCase()) ? file : false;
  },
  // 自定义图片插入
  customInsert(res, insertFn) {
    let url = res.data.url;
    let alt = res.data.name;
    let href = res.data.url;
    insertFn(url, alt, href);
  }
}

2. 视频上传功能

javascript 复制代码
uploadVideo: {
  // 支持的视频格式
  onBeforeUpload(file) {
    const fileSuffixList = [".mp4", ".webm", ".ogg"];
    const keys = Object.keys(file);
    const ext = "." + file[keys[0]].extension;
    return fileSuffixList.includes(ext.toLowerCase()) ? file : false;
  }
}

3. 预览功能

javascript 复制代码
const modalVisible = ref(false);
const openPreview = () => {
  modalVisible.value = true;
};

三、使用方法

1. 基础使用

vue 复制代码
<template>
  <CustomEditor
    v-model="content"
    height="500"
  />
</template>

<script setup>
import { ref } from 'vue';
import CustomEditor from '@/components/CustomEditor.vue';

const content = ref('');
</script>

2. 禁用模式

vue 复制代码
<template>
  <CustomEditor
    v-model="content"
    :disabled="true"
  />
</template>

3. 格式化内容

vue 复制代码
<template>
  <CustomEditor
    v-model.format="content"
  />
</template>

四、特殊功能说明

1. 内容格式化

javascript 复制代码
const [valueHtml, modifiers] = defineModel({
  set(value) {
    if (modifiers.format) {
      // 将 text-align: justify 转换为 text-align: initial
      value = value.replace(
        /(<p\s+[^>]*style="[^"]*?)text-align:\s*justify\s*;?([^"]*?">)/gi,
        "$1text-align: initial;$2"
      );
    }
    return value;
  },
});

2. 样式处理

scss 复制代码
.custom-editor {
  // 保持标题样式
  h1, h2, h3, h4, h5, h6 {
    font-size: revert;
    font-weight: revert;
    line-height: normal;
    color: initial;
  }

  // 保持媒体元素样式
  img, svg, video, canvas, audio, iframe, embed, object {
    display: revert;
  }
}

五、注意事项

  1. 安装依赖
bash 复制代码
npm install @wangeditor/editor @wangeditor/editor-for-vue
  1. 权限验证
  • 上传接口需要携带token
javascript 复制代码
headers: {
  Authorization: localStorage.getItem("token")
}
  1. 生命周期处理
javascript 复制代码
// 组件销毁时清理编辑器实例
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});
  1. 文件上传限制
  • 图片:最大20MB,支持png/jpg/jpeg/gif/bmp
  • 视频:最大100MB,支持mp4/webm/ogg,最多同时上传5个

六、完整代码

js 复制代码
// CustomEditor.vue
<script setup>
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import {
  onMounted,
  onBeforeUnmount,
  shallowRef,
  defineProps,
  reactive,
  defineModel,
  ref,
} from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
const props = defineProps({
  height: {
    type: String,
    default: "300",
  },
  disabled: {
    type: Boolean,
    default: false,
  },
});
const editorStyle = reactive({
  height: props.height + "px",
  overflowY: "hidden",
});

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();

// 内容 HTML
const [valueHtml, modifiers] = defineModel({
  set(value) {
    if (modifiers.format) {
      // 格式化
      // 处理 p 标签上的样式
      // style 中如果有 "text-align: justify;"
      // 如果碰到上面代码,会自动转换为 "text-align: initial;"
      value = value.replace(
        /(<p\s+[^>]*style="[^"]*?)text-align:\s*justify\s*;?([^"]*?">)/gi,
        "$1text-align: initial;$2",
      );
      return value;
    }
    return value;
  },
});

// 模拟 ajax 异步获取内容
onMounted(() => {
  setTimeout(() => {
    console.log(editorRef.value.getConfig(), editorRef.value.config);
    if (props.disabled) editorRef.value.disable();
  }, 0);
});

const toolbarConfig = { excludeKeys: ["fullScreen"] };
const editorConfig = {
  placeholder: "请输入内容...",
  MENU_CONF: {
    uploadImage: {
      server: "/api/admin/sys/file/upload",
      fieldName: "file",
      // 单个文件的最大体积限制,默认为 2M
      maxFileSize: 20 * 1024 * 1024, // 10M
      headers: {
        Authorization: localStorage.getItem("token"),
      },
      onBeforeUpload(file) {
        // TS 语法
        // onBeforeUpload(file) {    // JS 语法
        // file 选中的文件,格式如 { key: file }
        // 图片限制 .png, .jpg, .jpeg, .gif, .bmp
        const fileSuffixList = [".png", ".jpg", ".jpeg", ".gif", ".bmp"];
        const keys = Object.keys(file);
        const ext = "." + file[keys[0]].extension;
        if (fileSuffixList.includes(ext.toLowerCase())) {
          return file;
        } else {
          return false;
        }

        // 可以 return
        // 1. return file 或者 new 一个 file ,接下来将上传
        // 2. return false ,不上传这个 file
      },
      // 自定义插入图片
      customInsert(res, insertFn) {
        // JS 语法
        // res 即服务端的返回结果

        // 从 res 中找到 url alt href ,然后插入图片
        console.log(res);
        let url = res.data.url;
        let alt = res.data.name;
        let href = res.data.url;
        insertFn(url, alt, href);
      },
    },
    uploadVideo: {
      server: "/api/admin/sys/file/upload",
      // form-data fieldName ,默认值 'wangeditor-uploaded-video'
      fieldName: "file",

      // 单个文件的最大体积限制,默认为 10M
      maxFileSize: 100 * 1024 * 1024, // 100M

      // 最多可上传几个文件,默认为 5
      maxNumberOfFiles: 5,

      // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
      allowedFileTypes: ["video/*"],

      // 自定义增加 http  header
      headers: {
        Authorization: localStorage.getItem("token"),
      },
      onBeforeUpload(file) {
        // JS 语法
        // file 选中的文件,格式如 { key: file }
        // 视频格式
        const fileSuffixList = [".mp4", ".webm", ".ogg"];
        const keys = Object.keys(file);
        const ext = "." + file[keys[0]].extension;
        if (fileSuffixList.includes(ext.toLowerCase())) {
          return file;
        } else {
          return false;
        }

        // 可以 return
        // 1. return file 或者 new 一个 file ,接下来将上传
        // 2. return false ,不上传这个 file
      },
      // 自定义插入视频
      customInsert(res, insertFn) {
        // JS 语法
        // res 即服务端的返回结果

        // 从 res 中找到 url poster ,然后插入视频
        console.log(res);
        let url = res.data.url;
        let alt = res.data.name;
        let href = res.data.url;
        insertFn(url, alt, href);
      },
    },
  },
};

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});

const handleCreated = (editor) => {
  editorRef.value = editor; // 记录 editor 实例,重要!
};

const modalVisible = ref(false);
const openPreview = () => {
  modalVisible.value = true;
};
</script>
<template>
  <div class="custom-editor">
    <Toolbar
      style="border-bottom: 1px solid #ccc"
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
    />
    <div class="flex justify-end my-2">
      <t-button variant="outline" @click="openPreview" :disabled="false">
        <template #icon><t-icon name="browse"></t-icon></template>
        预览
      </t-button>
    </div>
    <Editor
      :style="editorStyle"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      @onCreated="handleCreated"
    />
    <t-dialog
      v-model:visible="modalVisible"
      width="754px"
      :cancelBtn="null"
      :confirmBtn="null"
      placement="center"
      header="预览"
    >
      <div class="w-full h-70vh overflow-y-auto" v-html="valueHtml"></div>
    </t-dialog>
  </div>
</template>
<style lang="scss">
.custom-editor {
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    font-size: revert;
    font-weight: revert;
    line-height: normal;
    color: initial;
  }

  img,
  svg,
  video,
  canvas,
  audio,
  iframe,
  embed,
  object {
    display: revert;
  }
}
</style>

这个组件提供了一个完整的富文本编辑解决方案,适合用在需要富文本编辑功能的后台管理系统中。通过合理的配置和使用,可以满足大多数富文本编辑的需求。

相关推荐
晓得迷路了29 分钟前
栗子前端技术周刊第 74 期 - 2025 Vue.js 现状报告、Element Plus X、Material UI v7...
前端·javascript·vue.js
SuperherRo1 小时前
Web开发-JS应用&VueJS框架&Vite构建&启动打包&渲染XSS&源码泄露&代码审计
前端·javascript·vue.js·xss·源码泄露·启动打包
Json_10 小时前
Vue computed Option 计算选项
前端·vue.js·深度学习
李小白6611 小时前
Vue背景介绍+声明式渲染+数据响应式
前端·javascript·vue.js
萌萌哒草头将军11 小时前
🚀🚀🚀Zod 深度解析:TypeScript 运行时类型安全的终极实践指南
javascript·vue.js·react.js
boy快快长大12 小时前
【VUE】day08黑马头条小项目
前端·javascript·vue.js
Java&Develop13 小时前
vue2拦截器 拦截后端返回的数据,并判断是否需要登录
前端·javascript·vue.js
倔强青铜三13 小时前
WXT浏览器插件开发中文教程(27)----发布插件
前端·javascript·vue.js
大阔14 小时前
使用 VueRouter 和 TypeScript 打造优雅的模块化路由系统
前端·vue.js
ZoeLandia14 小时前
vue3源码分析 -- watch
前端·vue.js·源码