1# 1.中文文档
2.安装
// 安装 tinymce-vue
npm install @tinymce/tinymce-vue -S
// 安装 tinymce
npm install tinymce -S
3.tinymce 默认英文
在 public 目录下新建 tinymce,将上面下载的语言包解压到该目录 在 node_modules 里面找到 tinymce, 将 skins 目录复制到 public/tinymce 里面 最终形成以下目录形式:
4.封装 tinymce
<template>
<div class="tinymce-editor">
<Editor
v-model="content"
:init="init"
:disabled="disabled"
@onClick="onClick"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import { http } from "@/utils/http";
// 核心
import "tinymce/themes/silver/theme";
import "tinymce/models/dom/model";
// 插件
import "tinymce/icons/default";
import "tinymce/plugins/advlist";
import "tinymce/plugins/anchor";
import "tinymce/plugins/autolink";
import "tinymce/plugins/autoresize";
import "tinymce/plugins/image";
import "tinymce/plugins/link";
import "tinymce/plugins/lists";
import "tinymce/plugins/media";
import "tinymce/plugins/code";
import "tinymce/plugins/lists";
import "tinymce/plugins/table";
import "tinymce/plugins/wordcount";
// Props 定义
interface Props {
modelValue?: string;
disabled?: boolean;
plugins?: string | string[];
toolbar?: string | string[];
}
const props = withDefaults(defineProps<Props>(), {
modelValue: "",
disabled: false,
plugins: "advlist autolink lists link image table wordcount",
toolbar:
"undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media table | removeformat"
});
// Emits 定义
const emit = defineEmits<{
(e: "update:modelValue", value: string): void;
(e: "onClick", event: any, editor: any): void;
}>();
// 编辑器内容
const content = ref(props.modelValue);
// 编辑器配置
const init = {
language_url: "/tinymce/langs/zh_CN.js",
language: "zh_CN",
skin_url: "/tinymce/skins/ui/oxide",
content_css: "/tinymce/skins/content/default/content.css",
height: 300,
plugins: props.plugins,
toolbar: props.toolbar,
branding: false,
menubar: false,
// 修改图片上传处理器
images_upload_handler: async (blobInfo: any) => {
try {
const formData = new FormData(); // 修正变量名
formData.append("image", blobInfo.blob());
const response = await http.request(
"post",
"/api/cmsapi/post/image/upload",
{
data: formData,
headers: {
"Content-Type": "multipart/form-data"
}
}
);
if (response.code === 200) {
return `${import.meta.env.VITE_API_URL}/${response.data.image_url}`;
} else {
throw new Error(response.message || "上传失败");
}
} catch (error) {
console.error("上传错误:", error);
throw new Error("图片上传失败,请重试");
}
},
// 文件选择配置
file_picker_types: "image",
images_file_types: "jpg,jpeg,png,gif",
image_advtab: true,
// 允许拖拽上传
draggable_modal: true,
// 图片上传配置
images_upload_base_path: import.meta.env.VITE_API_URL,
// 图片上传URL验证
images_upload_credentials: true,
// 开启调试模式,方便排查问题
debug: true
};
// 监听 props 变化
watch(
() => props.modelValue,
newValue => {
if (content.value !== newValue) {
content.value = newValue;
}
}
);
// 监听内容变化
watch(
() => content.value,
newValue => {
emit("update:modelValue", newValue);
}
);
// 点击事件处理
const onClick = (e: any) => {
emit("onClick", e, tinymce);
};
// 清空内容方法
const clear = () => {
content.value = "";
};
// 初始化编辑器
onMounted(() => {
tinymce.init({});
});
// 暴露方法给父组件
defineExpose({
clear
});
</script>
<style lang="scss" scoped>
.tinymce-editor {
width: 100%;
}
:deep(.tox-tinymce) {
border-radius: 4px;
}
</style>
<style lang="scss">
.tox-tinymce-aux {
z-index: 99999 !important;
}
.tinymce.ui.FloatPanel {
z-index: 99;
}
</style>
5.使用
import Tinymce from "@/components/Tinymce/index.vue";
<Tinymce
v-model="localFormData.content"
:disabled="false"
@onClick="handleClick"
/>