tinymce 富文本编辑器的使用

432 阅读8分钟
<template>
  <div class="editor-container">
    <textarea v-if="!initOptions.inline" :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }"></textarea>
    <el-dialog v-model="dialogVisible" title="图片列表" width="860" top="5vh" :before-close="imgChooseCancel">
      <div v-loading="imgLoading" class="tanBox" element-loading-text="正在获取中,预计等待1-2分钟,请稍后...">
        <div v-if="imgBtnFlag" style="text-align: center; margin-top: 30px">
          <el-empty description="还没有获取图片,点击获取图片,预计等待1-2分钟">
            <el-button type="primary" @click="goCrawling">获取图片</el-button>
          </el-empty>
        </div>
        <div v-if="imgList.length">
          <div
            v-for="(item, index) in imgList"
            :key="index"
            class="tanItem"
            :class="{ selected: isSelected(index) }"
            @click="imgClick(index)"
          >
            <el-icon class="iconClass" color="#409eff"><Select /></el-icon>
            <img :src="typeof item === 'string' ? item : undefined" />
          </div>
        </div>
        <div v-if="!imgList.length && !imgBtnFlag" style="margin: 0 auto">
          <el-empty description="暂无图片" />
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="imgChooseCancel">取消</el-button>
          <el-button type="primary" @click="imgChooseSure">确认</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router';
import { Select } from '@element-plus/icons-vue';
import type { Editor, RawEditorSettings } from 'tinymce'; // tinymce 要求  5.10.3
import tinymce from 'tinymce/tinymce';
import { ElMessage } from 'element-plus';
// import { pathConfig } from '@/service/request/opinionIndex';
import 'tinymce/themes/silver';
import 'tinymce/icons/default/icons';
import 'tinymce/plugins/advlist';
import 'tinymce/plugins/code';
import 'tinymce/plugins/fullscreen';
import 'tinymce/plugins/link';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/media';
import 'tinymce/plugins/image';
import 'tinymce/plugins/autoresize';
import 'tinymce/plugins/table';
import './importword/index.js';
import 'tinymce/plugins/colorpicker';
import 'tinymce/plugins/textcolor';
import 'tinymce/plugins/contextmenu';
import 'tinymce/plugins/paste';
import { $DictApi } from '@/service/api/dict';
// eslint-disable-next-line import/order
import { computed, nextTick, onBeforeUnmount, onDeactivated, onMounted, ref, unref, watch } from 'vue';
import { Rewrite } from '@/service/api/rewrite';
import { plugins, toolbar, content_style, fontsize_formats, font_formats, toolbarSimple } from './const';
interface RowVO {
  [key: string]: any;
}
// 这一段是我自己的东西,获取本地缓存相关的
const $route = useRoute();
const props = defineProps({
  value: { type: String, default: '' }, // html 字符串
  disabled: { type: Boolean, default: false }, // 是否禁用
  simple: { type: Boolean, default: false }, // 是否禁用
  getText: { type: Boolean, default: false }, // 是否禁用
  disabledBtn: { type: Boolean, default: false }, // 是否禁用
  width: { type: [Number, String], default: '100%' }, // 宽
  height: { type: [Number, String] }, // 高
  defaultId: { type: String, default: '' }, // 高

  // imgList: { type: Array, default: () => [] },

  /** tinymce 配置项 */
  options: { type: Object, default: () => {} }
});

// 插入图片
const imgLoading = ref<boolean>(false);
const imgList = ref<string[]>([]); // 这是一个 string[] 类型的数组
const tuList = ref<Array<any>>([]);
const getImgList = async () => {
  imgList.value = [];
  const params = {
    articleId: $route.query.id
  };
  const response: RowVO = await Rewrite.article.queryImageList(params);
  if (response.data.code === '0000') {
    tuList.value = response.data.data;
    tuList.value.forEach(async item => {
      // imgList.value.push(`${pathConfig.baseURL}/RewriteArticleController/download?token=${item.imgPath}`);
      imgList.value.push(item.imgPath);
    });
  }
};

const imgBtnFlag = ref<boolean>(false);
const timer = ref<NodeJS.Timeout | null>(null);
//
const getImgStatus = async () => {
  if (timer.value) {
    clearTimeout(timer.value);
  }

  const params = {
    id: $route.query.id
  };
  const response: RowVO = await Rewrite.article.imgStatus(params);
  if (response.data.code === '0000') {
    if (response.data.data === '3') {
      getImgList();
      imgBtnFlag.value = false;
      imgLoading.value = false;
    } else if (response.data.data === '0' || response.data.data === '4') {
      imgBtnFlag.value = true;
      imgLoading.value = false;
    } else {
      imgBtnFlag.value = false;
      imgLoading.value = true;
      console.log(timer.value, 'timer.value');

      timer.value = setTimeout(() => {
        getImgStatus();
      }, 1000);
    }
  }
};

const goCrawling = async () => {
  const params = {
    id: $route.query.id
  };
  const response: RowVO = await Rewrite.article.imgInitStatus(params);
  if (response.data.code === '0000') {
    getImgStatus();
  }
};
// 插入图片

const emits = defineEmits(['update:value', 'change']);
const editorRef = ref<any>(null);
const elRef = ref<any>(null);
const dialogVisible = ref<boolean>(false);

const tinymceId = ref<string>(props.defaultId ? props.defaultId : `tiny-vue${String(Date.now())}`);

const initOptions = computed((): RawEditorSettings => {
  const publicPath = '/kb-web/';
  return {
    selector: `#${unref(tinymceId)}`,
    height: props.height ? props.height : '',
    autoresize_min_height: 100, // 设置编辑器最小高度
    width: props.width,
    toolbar: `${props.simple ? toolbarSimple : toolbar} ${props.disabledBtn ? 'myCustomToolbarButton' : ''} `,
    plugins: `${plugins} ${props.height ? '' : 'autoresize'} `,
    menubar: false,
    resize: false,
    statusbar: false,
    language_url: `${publicPath}tinymce/langs/zh_Hans.js`,
    language: 'zh-Hans',
    branding: false,
    default_link_target: '_blank',
    paste_as_text: false, // 粘贴内容时强制为纯文本q
    paste_data_images: true, // 是否支持粘贴图片
    skin: 'oxide',
    skin_url: `${publicPath}tinymce/skins/ui/oxide`,
    content_style, // 自定义内容样式
    content_css: `${publicPath}tinymce/skins/ui/oxide/content.min.css`,
    ...props.options,
    // paste_webkit_styles: 'color font-size',
    fontsize_formats,
    font_formats: '',
    style_formats_merge: true,
    paste_webkit_styles: 'all', // 允许粘贴的WebKit样式
    paste_merge_formats: true, // 合并粘贴的格式
    paste_retain_style_properties: 'all', // 保留所有样式属性
    // importword_options: {
    //   extract_css: true // 确保开启CSS提取(没生效)
    // },
    importword_styles: 'all',
    // 添加额外的样式处理插件暂时没用

    // external_plugins: {
    //   powerpaste: `${publicPath}tinymce/powerpaste/plugin.min.js` // ${this.baseUrl}
    // },
    // powerpaste_word_import: 'merge', // 参数:propmt, merge, clear
    // powerpaste_html_import: 'merge', // propmt, merge, clear
    // powerpaste_allow_local_images: true, // 粘贴图片
    // 文本的处理:例如去掉a标签
    paste_preprocess(pluginApi, data) {
      // // 使用DOM解析器来处理HTML内容
      // const domParser = new DOMParser();
      // const doc = domParser.parseFromString(data.content, 'text/html');
      // // 查找所有的a标签并移除
      // const links = doc.querySelectorAll('a');
      // for (let i = links.length - 1; i >= 0; i--) {
      //   const link = links[i];
      //   link.parentNode.replaceChild(document.createTextNode(link.textContent), link);
      // }
      // // 将处理后的HTML内容赋回到粘贴数据中
      // data.content = doc.documentElement.innerHTML;
      // console.log(data.content, 'data.content');
    },

    // 导入word
    importword_handler(editor, files, next) {
      const file_name = files[0].name;
      if (file_name.substr(file_name.lastIndexOf('.') + 1) == 'docx') {
        editor.notificationManager.open({
          text: '正在转换中...',
          type: 'info',
          closeButton: false
        });
        next(files);
      } else {
        editor.notificationManager.open({
          text: '目前仅支持docx文件格式,若为doc,请将扩展名改为docx',
          type: 'warning',
          timeout: 3000
        });
      }
    },
    file_picker_callback(callback, value, meta) {
      // 文件分类
      const filetype = '.pdf, .txt,  .doc, .docx, .xls, .xlsx, .ppt, .pptx,';
      // 后端接收上传文件的地址
      // 为不同插件指定文件类型及后端地址
      const input = document.createElement('input');
      input.setAttribute('type', 'file');
      input.setAttribute('accept', filetype);

      input.click();
      // eslint-disable-next-line func-names
      input.onchange = async function () {
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const that: any = this;
        const file = that.files[0];
        const formData = new FormData();
        formData.append('file', file, file.name);
        const result = await $DictApi.fileUpload(formData);
        if (result && result.data && result.data.data) {
          // const data = result.data.data as any;
          callback(result.data.data.filePath, { text: result.data.data.fileName });
          return true;
        }
        return false;
      };
    },
    // images_dataimg_filter(img) {
    //   return img.hasAttribute('internal-blob');
    // },

    async images_upload_handler(blobInfo, succFun, failFun) {
      console.log('2222');

      // const fileData = new FormData();
      // const file = blobInfo.blob();
      // fileData.append('file', file as File);
      // const result = await $DictApi.fileUpload(fileData);
      // if (result && result.data && result.data.data) {
      //   // const data = result.data.data as any;
      //   return true;
      // }
      // return false;
    },
    style_formats: [
      {
        title: 'Line Height',
        items: [
          { title: '1', inline: 'span', styles: { 'line-height': '1' } },
          { title: '1.5', inline: 'span', styles: { 'line-height': '1.5' } },
          { title: '2', inline: 'span', styles: { 'line-height': '2' } },
          { title: '2.5', inline: 'span', styles: { 'line-height': '2.5' } },
          { title: '3', inline: 'span', styles: { 'line-height': '3' } },
          { title: '3.5', inline: 'span', styles: { 'line-height': '3.5' } },
          { title: '4', inline: 'span', styles: { 'line-height': '4' } },
          { title: '4.5', inline: 'span', styles: { 'line-height': '4.5' } },
          { title: '5', inline: 'span', styles: { 'line-height': '5' } },
          { title: '5.5', inline: 'span', styles: { 'line-height': '5.5' } },
          { title: '6', inline: 'span', styles: { 'line-height': '6' } }
        ]
      },
      {
        title: 'Letter Spacing',
        items: [
          { title: '-0.5px', inline: 'span', styles: { 'letter-spacing': '-0.5px' } },
          { title: '-1px', inline: 'span', styles: { 'letter-spacing': '-1px' } },
          { title: 'Default', inline: 'span', styles: { 'letter-spacing': 'normal' } },
          { title: '0.5px', inline: 'span', styles: { 'letter-spacing': '0.5px' } },
          { title: '1px', inline: 'span', styles: { 'letter-spacing': '1px' } },
          { title: '1.5px', inline: 'span', styles: { 'letter-spacing': '1.5px' } },
          { title: '2px', inline: 'span', styles: { 'letter-spacing': '2px' } },
          { title: '2.5px', inline: 'span', styles: { 'letter-spacing': '2.5px' } },
          { title: '3px', inline: 'span', styles: { 'letter-spacing': '3px' } },
          { title: '3.5px', inline: 'span', styles: { 'letter-spacing': '3.5px' } },
          { title: '4px', inline: 'span', styles: { 'letter-spacing': '4px' } },
          { title: '4.5px', inline: 'span', styles: { 'letter-spacing': '4.5px' } },
          { title: '5.5px', inline: 'span', styles: { 'letter-spacing': '5.5px' } },
          { title: '5.6px', inline: 'span', styles: { 'letter-spacing': '5.6px' } },
          { title: '5.7px', inline: 'span', styles: { 'letter-spacing': '5.7px' } },
          { title: '5.8px', inline: 'span', styles: { 'letter-spacing': '5.8px' } },
          { title: '6.0px', inline: 'span', styles: { 'letter-spacing': '6.0px' } },
          { title: '6.5px', inline: 'span', styles: { 'letter-spacing': '6.5px' } },
          { title: '7.5px', inline: 'span', styles: { 'letter-spacing': '7.5px' } },
          { title: '8.5px', inline: 'span', styles: { 'letter-spacing': '8.5px' } },
          { title: '9.5px', inline: 'span', styles: { 'letter-spacing': '9.5px' } },
          { title: '10.5px', inline: 'span', styles: { 'letter-spacing': '10.5px' } },
          { title: '11px', inline: 'span', styles: { 'letter-spacing': '11px' } },
          { title: '11.5px', inline: 'span', styles: { 'letter-spacing': '11.5px' } },
          { title: '12px', inline: 'span', styles: { 'letter-spacing': '12px' } },
          { title: '12.5px', inline: 'span', styles: { 'letter-spacing': '12.5px' } },
          { title: '13px', inline: 'span', styles: { 'letter-spacing': '13px' } },
          { title: '13.5px', inline: 'span', styles: { 'letter-spacing': '13.5px' } },
          { title: '14px', inline: 'span', styles: { 'letter-spacing': '14px' } },
          { title: '14.5px', inline: 'span', styles: { 'letter-spacing': '14.5px' } },
          { title: '15px', inline: 'span', styles: { 'letter-spacing': '15px' } },
          { title: '15.5px', inline: 'span', styles: { 'letter-spacing': '15.5px' } },
          { title: '16px', inline: 'span', styles: { 'letter-spacing': '16px' } },
          { title: '16.5px', inline: 'span', styles: { 'letter-spacing': '16.5px' } },
          { title: '17px', inline: 'span', styles: { 'letter-spacing': '17px' } },
          { title: '17.5px', inline: 'span', styles: { 'letter-spacing': '17.5px' } },
          { title: '18px', inline: 'span', styles: { 'letter-spacing': '18px' } },
          { title: '18.5px', inline: 'span', styles: { 'letter-spacing': '18.5px' } },
          { title: '19px', inline: 'span', styles: { 'letter-spacing': '19px' } },
          { title: '19.5px', inline: 'span', styles: { 'letter-spacing': '19.5px' } },
          { title: '20px', inline: 'span', styles: { 'letter-spacing': '20px' } },
          { title: '20.5px', inline: 'span', styles: { 'letter-spacing': '20.5px' } },
          { title: '21px', inline: 'span', styles: { 'letter-spacing': '21px' } },
          { title: '21.5px', inline: 'span', styles: { 'letter-spacing': '21.5px' } },
          { title: '22px', inline: 'span', styles: { 'letter-spacing': '22px' } },
          { title: '22.5px', inline: 'span', styles: { 'letter-spacing': '22.5px' } },
          { title: '23px', inline: 'span', styles: { 'letter-spacing': '23px' } },
          { title: '23.5px', inline: 'span', styles: { 'letter-spacing': '23.5px' } },
          { title: '24px', inline: 'span', styles: { 'letter-spacing': '24px' } },
          { title: '24.5px', inline: 'span', styles: { 'letter-spacing': '24.5px' } },
          { title: '25px', inline: 'span', styles: { 'letter-spacing': '25px' } },
          { title: '25.5px', inline: 'span', styles: { 'letter-spacing': '25.5px' } },
          { title: '26px', inline: 'span', styles: { 'letter-spacing': '26px' } },
          { title: '26.5px', inline: 'span', styles: { 'letter-spacing': '26.5px' } },
          { title: '27px', inline: 'span', styles: { 'letter-spacing': '27px' } },
          { title: '27.5px', inline: 'span', styles: { 'letter-spacing': '27.5px' } },
          { title: '28px', inline: 'span', styles: { 'letter-spacing': '28px' } },
          { title: '28.5px', inline: 'span', styles: { 'letter-spacing': '28.5px' } },
          { title: '29px', inline: 'span', styles: { 'letter-spacing': '29px' } },
          { title: '29.5px', inline: 'span', styles: { 'letter-spacing': '29.5px' } },
          { title: '30px', inline: 'span', styles: { 'letter-spacing': '30px' } },
          { title: '30.5px', inline: 'span', styles: { 'letter-spacing': '30.5px' } },
          { title: '31px', inline: 'span', styles: { 'letter-spacing': '31px' } },
          { title: '31.5px', inline: 'span', styles: { 'letter-spacing': '31.5px' } },
          { title: '32px', inline: 'span', styles: { 'letter-spacing': '32px' } },
          { title: '32.5px', inline: 'span', styles: { 'letter-spacing': '32.5px' } },
          { title: '33px', inline: 'span', styles: { 'letter-spacing': '33px' } },
          { title: '33.5px', inline: 'span', styles: { 'letter-spacing': '33.5px' } },
          { title: '34px', inline: 'span', styles: { 'letter-spacing': '34px' } },
          { title: '34.5px', inline: 'span', styles: { 'letter-spacing': '34.5px' } },
          { title: '35px', inline: 'span', styles: { 'letter-spacing': '35px' } },
          { title: '35.5px', inline: 'span', styles: { 'letter-spacing': '35.5px' } },
          { title: '36px', inline: 'span', styles: { 'letter-spacing': '36px' } },
          { title: '36.5px', inline: 'span', styles: { 'letter-spacing': '36.5px' } },
          { title: '37px', inline: 'span', styles: { 'letter-spacing': '37px' } },
          { title: '37.5px', inline: 'span', styles: { 'letter-spacing': '37.5px' } },
          { title: '38px', inline: 'span', styles: { 'letter-spacing': '38px' } },
          { title: '38.5px', inline: 'span', styles: { 'letter-spacing': '38.5px' } },
          { title: '39px', inline: 'span', styles: { 'letter-spacing': '39px' } },
          { title: '39.5px', inline: 'span', styles: { 'letter-spacing': '39.5px' } },
          { title: '40px', inline: 'span', styles: { 'letter-spacing': '40px' } },
          { title: '40.5px', inline: 'span', styles: { 'letter-spacing': '40.5px' } },
          { title: '41px', inline: 'span', styles: { 'letter-spacing': '41px' } },
          { title: '41.5px', inline: 'span', styles: { 'letter-spacing': '41.5px' } },
          { title: '42px', inline: 'span', styles: { 'letter-spacing': '42px' } },
          { title: '42.5px', inline: 'span', styles: { 'letter-spacing': '42.5px' } },
          { title: '43px', inline: 'span', styles: { 'letter-spacing': '43px' } },
          { title: '43.5px', inline: 'span', styles: { 'letter-spacing': '43.5px' } },
          { title: '44px', inline: 'span', styles: { 'letter-spacing': '44px' } },
          { title: '44.5px', inline: 'span', styles: { 'letter-spacing': '44.5px' } },
          { title: '45px', inline: 'span', styles: { 'letter-spacing': '45px' } },
          { title: '45.5px', inline: 'span', styles: { 'letter-spacing': '45.5px' } },
          { title: '46px', inline: 'span', styles: { 'letter-spacing': '46px' } },
          { title: '46.5px', inline: 'span', styles: { 'letter-spacing': '46.5px' } },
          { title: '47px', inline: 'span', styles: { 'letter-spacing': '47px' } },
          { title: '47.5px', inline: 'span', styles: { 'letter-spacing': '47.5px' } },
          { title: '48px', inline: 'span', styles: { 'letter-spacing': '48px' } },
          { title: '48.5px', inline: 'span', styles: { 'letter-spacing': '48.5px' } },
          { title: '49px', inline: 'span', styles: { 'letter-spacing': '49px' } },
          { title: '49.5px', inline: 'span', styles: { 'letter-spacing': '49.5px' } },
          { title: '50px', inline: 'span', styles: { 'letter-spacing': '50px' } }
        ]
      }
    ],
    // fontsize_formats : [],
    setup: (editor: Editor) => {
      editorRef.value = editor;
      editor.ui.registry.addButton('myCustomToolbarButton', {
        text: '原文图片',
        onAction: () => {
          if ($route.query.id) {
            dialogVisible.value = true;
            getImgStatus();
          } else {
            ElMessage.warning('未选择文章');
          }
        }
      });
      editor.on('init', () => initSetup());
    }
  };
});
const selectedIndices = ref<number[]>([]); // 被选中的图片的索引
const isSelected = index => {
  return selectedIndices.value.includes(index);
};

const imgClick = val => {
  if (isSelected(val)) {
    selectedIndices.value = selectedIndices.value.filter(i => i !== val);
  } else {
    selectedIndices.value.push(val);
  }
};

onMounted(() => {
  if (!unref(initOptions).inline) {
    // tinymceId.value = `tiny-vue${props.defaultId ? props.defaultId : String(Date.now())}`;
    tinymceId.value = `${props.defaultId ? props.defaultId : `tiny-vue${String(Date.now())}`}`;
  }
  nextTick(() => {
    setTimeout(() => {
      initEditor();
    }, 30);
  });
});

onBeforeUnmount(() => {
  destory();
});

onDeactivated(() => {
  destory();
});

/** 初始化富文本 */
async function initEditor() {
  const el = unref(elRef);
  if (!el) return;
  el.style.visibility = '';
  // tinymce.init(unref(initOptions));
  try {
    const options = unref(initOptions);
    // 初始化 TinyMCE
    tinymce.init(options);
  } catch (error) {
    console.error('获取字体格式失败:', error);
    // 错误处理或者设置默认字体格式
    tinymce.init({
      ...unref(initOptions),
      font_formats // 确保这里的 font_formats 是你的默认字体格式字符串
    });
  }
}

// function replaceImgSrcWithAlt1(htmlString) {
//   const parser = new DOMParser();
//   const doc = parser.parseFromString(htmlString, 'text/html');

//   const imgTags = doc.querySelectorAll('img');
//   if (imgTags && imgTags.length) {
//     imgTags.forEach(img => {
//       const alt = img?.getAttribute('alt');
//       if (alt) {
//         // 提取图片后缀
//         const src = img?.getAttribute('src');
//         const matches = src?.match(/(\.(jpg|jpeg|png))(?![\w\W]*\.(jpg|jpeg|png))(\?.*)?$/i);
//         if (matches) {
//           const imageSuffix = matches[1];
//           // 替换src属性
//           let newSrc = alt + imageSuffix;
//           // 去掉图片后缀之后的参数部分
//           newSrc = newSrc?.replace(/(\.(jpg|jpeg|png))(.*)$/i, '\$1');
//           img?.setAttribute('src', newSrc);
//         }
//       }
//     });
//   }

//   const bodyContent = doc.body.innerHTML;

//   return bodyContent;
// }

function hasBase64Img(imgElements) {
  for (const img of imgElements) {
    const src = img.getAttribute('src');
    if (src && src.startsWith('data:image/')) {
      return true; // 如果有一个src是base64格式的,返回true
    }
  }
  return false; // 如果所有的src都不是base64格式的,返回false
}

async function replaceImgSrcWithAlt(htmlString) {
  // 创建一个DOM元素来解析HTML字符串
  const parser = new DOMParser();
  const doc = parser.parseFromString(htmlString, 'text/html');

  // 获取所有img标签
  const imgElements = doc.querySelectorAll('img');

  // 如果没有img标签,直接返回原始的HTML字符串
  if (imgElements.length === 0) {
    return htmlString;
  }

  const baseFlag = hasBase64Img(imgElements);
  if (baseFlag) {
    // 遍历所有img标签
    for (const imgElement of Array.from(imgElements)) {
      // 获取img标签的src属性
      const src = imgElement.getAttribute('src');
      // 检查src是否为base64格式的图片
      if (src && src.startsWith('data:image')) {
        // 将base64格式的图片转换为Blob对象
        const base64Data = src.split(',')[1];
        const byteCharacters = atob(base64Data);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
          byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray], { type: 'image/png' });

        // 将Blob对象添加到FormData中
        const formData = new FormData();
        formData.append('file', blob, 'image.png');

        try {
          // eslint-disable-next-line no-await-in-loop
          const result = await $DictApi.fileUpload(formData);
          // eslint-disable-next-line max-depth
          if (result && result.data && result.data.data) {
            const uploadedSrc = result.data.data.filePath;
            imgElement.setAttribute('src', uploadedSrc);
          }
        } catch (error) {
          console.error('上传图片出错:', error);
        }
      } else {
        const src1 = src?.replace(/(\.(jpg|jpeg|png))(.*)$/i, '\$1') || '';
        imgElement.setAttribute('src', src1);
      }
    }
    return doc.body.innerHTML;
  }
  return htmlString;
}

// const lastHtml = ref<string>('');
// const zLastHtml = async () => {
//   lastHtml.value = await replaceImgSrcWithAlt(props.value);
// };
// zLastHtml();
const imgChooseCancel = () => {
  dialogVisible.value = false;
  imgLoading.value = false;
  selectedIndices.value = [];

  // clearTimeout(timer.value);
};

const imgChooseSure = () => {
  if (selectedIndices.value && !selectedIndices.value.length) {
    ElMessage.warning('请选择图片');
    return;
  }
  // editorRef.value.insertContent(`<img src="${imageUrl}" alt="Image" />`);
  const images = selectedIndices.value.map(index => imgList.value[index]);
  const imageTags = images.map(image => `<img src="${image}" alt="Image" />`).join('');
  editorRef.value.insertContent(imageTags);
  imgChooseCancel();
};

/** 初始化富文本配置 */
function initSetup() {
  const editor = unref(editorRef);
  if (!editor) return;
  const value = props.value || '';

  editor.setContent(replaceImgSrcWithAlt(value));
  bindModelHandlers(editor);
}

/** 注销 */
function destory() {
  console.log(tinymce, unref(initOptions).selector!, '333333333destorydestorydestory');

  if (tinymce !== null) {
    tinymce?.remove?.(unref(initOptions).selector!);
  }
}

const oldVal = ref();
const newVal = ref();
/** 默认事件注册 */
function bindModelHandlers(editor) {
  watch(
    () => props.value,
    async (val: any, val1: any) => {
      oldVal.value = val1;
      newVal.value = val;

      const bb = await replaceImgSrcWithAlt(val);
      setValue(editor, bb);
    },
    {
      immediate: true
    }
  );

  watch(
    () => props.disabled,
    (val: boolean) => {
      const editor = unref(editorRef);
      if (!editor) return;
      editor.setMode(val ? 'readonly' : 'design');
    },
    { immediate: true }
  );

  // function debounce(func, delay) {
  //   let timeoutId;
  //   return (...args) => {
  //     // eslint-disable-next-line consistent-this
  //     const context = this;

  //     clearTimeout(timeoutId);

  //     timeoutId = setTimeout(() => {
  //       func.apply(context, args);
  //     }, delay);
  //   };
  // }

  // 防抖函数
  let timeout: any = null;
  type GreetingFunction = (flag: boolean) => void;
  const debounce = (flag: boolean, func: GreetingFunction, wait: number) => {
    return function () {
      clearTimeout(timeout);
      timeout = setTimeout(function () {
        func(flag);
      }, wait);
    };
  };
  const debouncedCallback = debounce(
    false,
    async () => {
      const content = editor.getContent({ format: 'html' });
      const contentText = editor.getContent({ format: 'text' });

      // console.log(oldVal.value, ' oldVal.value');
      // console.log(content, ' newVal.value');
      // await replaceImgSrcWithAlt(content);

      emits('update:value', content);
      if (props.getText) {
        emits('change', { content, contentText });
      } else {
        emits('change', content);
      }
    },
    300
  ); // 设置防抖时间为300毫秒

  editor.on('change keyup undo redo', debouncedCallback);

  // editor.on('change keyup undo redo', async () => {
  //   const content = editor.getContent({ format: 'html' });
  //   const contentText = editor.getContent({ format: 'text' });

  //   // console.log(props.value, '1111');
  //   // console.log(content, 'contentcontentcontentcontent');

  //   console.log(oldVal.value, ' oldVal.value');
  //   console.log(content, ' newVal.value');

  //   // eslint-disable-next-line no-self-compare

  //   // if (oldVal.value !== content) {
  //   emits('update:value', content);
  //   if (props.getText) {
  //     emits('change', { content, contentText });
  //   } else {
  //     emits('change', content);
  //   }
  //   // }
  // });

  editor.on('ExecCommand', e => {
    const command = e.command;
    // 这里你可以检查执行的命令是什么
    console.log('Executed command:', e.target.value);
    // 根据不同的命令做不同的处理
    if (command === 'mceApplyTextcolor' || command === 'ForeColor') {
      console.log('文字颜色被修改了。');
    }
    if (command === 'FontSize' || command === 'mceFontSize') {
      console.log('字体大小被修改了。');
    }
    if (command === 'FontName') {
    }
  });

  // editor.on("change", async () => {
  //     const content: string = editor.getContent();
  //     console.log(content)
  //     // 重写内容
  //     editor.setContent(content);
  //     // 光标重写
  //     editor.selection.select(editor.getBody(), true);
  //     editor.selection.collapse(false);
  // });
  //
  // //  失焦时,自动回传数据,考虑加入以下事件 keyup undo redo
  // editor.on("blur", () => {
  //     const content = editor.getContent();
  //     emits("update:value", content);
  //     emits("change", content);
  // });
}
/** 写入数据 */
function setValue(editor: Editor, val: string) {
  const pp = editor?.selection?.getBookmark(2); // 光标重写
  if (editor && val) {
    const cur = editor.getContent();
    if (cur !== val) {
      editor.setContent(val);
      setTimeout(() => {
        // 定位最后的位置
        editor.selection.moveToBookmark(pp); // 光标重写
      }, 1);
    }
  }
}
</script>

<style scoped lang="scss">
.tox .tox-dialog--width-lg {
  background-color: red;
}
.tanBox {
  overflow: hidden;
  max-height: 70vh;
  overflow-y: auto;
  .tanItem {
    width: 200px;
    height: 200px;
    float: left;
    margin-left: 20px;
    margin-top: 20px;
    border: 1px #eee solid;
    padding: 10px;
    position: relative;
    .iconClass {
      position: absolute;
      top: 5px;
      right: 5px;
      display: none;
    }

    img {
      display: block;
      max-width: 100%;
      max-height: 100%;
    }
  }
  .tanItem.selected {
    border: 1px #409eff solid;
    .iconClass {
      display: block;
    }
  }
}
</style>
<!-- ./importword/index.js ./tinymce/const.js -->