赞
踩
yarn add tinymce -S;
yarn add @tinymce/tinymce-vue -S;
// component/Editor/js/importTinymce.js // 引入node_modules里的tinymce相关文件文件 // eslint-disable-next-line no-unused-vars import 'tinymce/themes/silver' // 编辑器主题,不引入则报错 import 'tinymce/icons/default' // 引入编辑器图标icon,不引入则不显示对应图标 // import 'tinymce/skins/ui/oxide/content.css' // import 'tinymce/skins/content/default/content.css' // // 引入编辑器插件(基本免费插件都在这儿了) import 'tinymce/plugins/advlist' // 高级列表 import 'tinymce/plugins/anchor' // 锚点 import 'tinymce/plugins/autolink' // 自动链接 import 'tinymce/plugins/autoresize' // 编辑器高度自适应,注:plugins里引入此插件时,Init里设置的height将失效 import 'tinymce/plugins/autosave' // 自动存稿 import 'tinymce/plugins/charmap' // 特殊字符 import 'tinymce/plugins/code' // 编辑源码 import 'tinymce/plugins/codesample' // 代码示例 import 'tinymce/plugins/directionality' // 文字方向 // import 'tinymce/plugins/emoticons' // 表情 import 'tinymce/plugins/fullpage/index' // 文档属性 import 'tinymce/plugins/fullscreen' // 全屏 import 'tinymce/plugins/help' // 帮助 import 'tinymce/plugins/hr' // 水平分割线 import 'tinymce/plugins/image' // 插入编辑图片 import 'tinymce/plugins/importcss' // 引入css import 'tinymce/plugins/insertdatetime' // 插入日期时间 import 'tinymce/plugins/link' // 超链接 import 'tinymce/plugins/lists' // 列表插件 import 'tinymce/plugins/media' // 插入编辑媒体 import 'tinymce/plugins/nonbreaking' // 插入不间断空格 import 'tinymce/plugins/pagebreak' // 插入分页符 import 'tinymce/plugins/paste' // 粘贴插件 import 'tinymce/plugins/preview'// 预览 // import 'tinymce/plugins/print'// 打印 import 'tinymce/plugins/quickbars' // 快速工具栏 import 'tinymce/plugins/save' // 保存 import 'tinymce/plugins/searchreplace' // 查找替换 // import 'tinymce/plugins/spellchecker' //拼写检查,暂未加入汉化,不建议使用 // import 'tinymce/plugins/tabfocus' // 切入切出,按tab键切出编辑器,切入页面其他输入框中 import 'tinymce/plugins/table' // 表格 import 'tinymce/plugins/template' // 内容模板 import 'tinymce/plugins/textcolor' // 文字颜色 import 'tinymce/plugins/textpattern' // 快速排版 import 'tinymce/plugins/toc' // 目录生成器 import 'tinymce/plugins/visualblocks' // 显示元素范围 import 'tinymce/plugins/visualchars' // 显示不可见字符 import 'tinymce/plugins/wordcount' // 字数统计
// component/Editor/js/config.js // 导入插件 const buttonPlugins = 'emoticons preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount textpattern autosave paste'; // 导入工具栏 const toolbar = 'fullscreen undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor \ table image | alignleft aligncenter alignright alignjustify outdent indent | \ styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | media charmap emoticons hr pagebreak insertdatetime print preview | code selectall searchreplace visualblocks | indent2em lineheight formatpainter axupimgs'; // 初始化配置 export const init = { selector: '#js_tinymce_editor', height: 550, width: '100%', cleanup: true, language_url: './tinymce/langs/zh-Hans.js', // 引入语言包文件 language: 'zh-Hans', // 语言类型 content_css: true, skin_url: './tinymce/skins/ui/oxide', // 皮肤:浅色 // skin_url: '/tinymce/skins/ui/oxide-dark',//皮肤:暗色 plugins: buttonPlugins, // 插件配置 toolbar: toolbar, // 工具栏配置,设为false则隐藏 body_class: 'panel-body', object_resizing: false, // 图片和表格是否开启在编辑器内部缩放 // menubar: false, // 菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单” //emoticons_database_url: './tinymce/emoticons/js/emojis.js', fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px', // 字体大小 nonbreaking_force_tab: true, // 此选项允许您在用户按下键盘tab键时强制TinyMCE插入三个实体 font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;', // 字体样式 微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif, 宋体=simsun,serif,仿宋体=FangSong,黑体=SimHei,Arial=arial, lineheight_formats: '0.5 0.8 1 1.2 1.5 1.75 2 2.5 3 4 5', // 行高配置,也可配置成"12px 14px 16px 20px"这种形式 branding: false, // tiny技术支持信息是否显示 resize: false, // 编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号 // statusbar: false, //最下方的元素路径和字数统计那一栏是否显示 elementpath: false, // 元素路径是否显示 content_style: 'p {margin-block-start: 0; margin-block-end: 0; color: #606D81; font-size: 14px;}; table { border: 1px}', // 直接自定义可编辑区域的css样式 // content_css: './tinymce/index.css', // 以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入 paste_data_images: true, // 图片是否可粘贴 images_upload_handler: (blobInfo, success, failure) => { // 需要在setup里面重新写,传入 url }, imagetools_toolbar: 'editimage', // 图片控制的工具栏 }
// components/Editor/index <template> <div class="tinymce-box"> <Editor v-model="editorContent" :init="initData" :disabled="isDisabled" ref="editorRef" id="js_tinymce_editor" ></Editor> </div> </template> <script setup lang="ts"> import Editor from '@tinymce/tinymce-vue'; import tinymce from 'tinymce/tinymce'; // tinymce默认hidden,不引入则不显示编辑器 import { formatTime } from '@/utils/'; import { uploadImage } from '@/api/tinymce'; const props = defineProps({ modelValue: { type: String, default: '', }, }); // 导入配置文件 import './js/importTinymce'; import { init } from './js/config'; import axios from 'axios'; onMounted(() => { // 配置信息 tinymce.init({}); }); const isDisabled = ref(true); // 初始化配置 const initData = ref( Object.assign({}, init, { init_instance_callback: (editor: any) => { // 初始化编辑器实例时要执行的函数 isDisabled.value = false; }, // 图片上传 images_upload_handler: async (blobInfo: any, success: any, failure: any) => { var form = new FormData(); form.append('file', blobInfo.blob()); const config = { headers: { 'Content-Type': 'multipart/form-data', }, }; axios .post('/my-api/vueadmin/image/upload', form, config) .then((res) => { const data = res.data.data; if (res.data.code === '00000') { success(data.imgUrl); } else { failure('上传失败!'); } }) .catch((err) => { failure('上传失败!'); }); }, }) ); const editorContent = ref(props.modelValue); const emit = defineEmits(['update:modelValue']); // 实时监听内容变化,并同步到父组件 watch( () => editorContent.value, (n) => { debounce(() => { emit('update:modelValue', editorContent.value); }); } ); const timeout = ref(); // 防抖 const debounce = (fn: any, wait = 400) => { if (timeout.value) { clearTimeout(timeout.value); fn(); } timeout.value = setTimeout(fn, wait); }; </script> <style lang="scss" scoped> .tinymce-box { width: 100%; } </style>
// views/page/edit/index.js <!-- @format --> <template> <el-card> <el-form ref="formDataRef" :model="formData" :rules="rules" label-width="120px" class="demo-formData" status-icon > <el-form-item label="短信内容" prop="region" style="width: 100%"> <Editor v-if="editDataLoading" v-model="formData.textarea" style="width: 100%" ></Editor> </el-form-item> </el-form> <div class="btn-box"> <el-button type="primary" @click="submitForm(formDataRef)"> 保存 </el-button> <el-button @click="resetForm(formDataRef)">重置</el-button> </div> </el-card> </template> <script setup lang="ts"> import Editor from '@/components/Editor/index.vue'; import type { FormInstance, FormRules } from 'element-plus'; import { IFormData } from '@/api/sms/types'; import { getSmsInfo, saveSms } from '@/api/sms/'; const propsData = defineProps({ drawerData: { type: Object, default: () => { id: ''; }, }, }); const emit = defineEmits(['closeDrawerHandle']); const editDataLoading = ref(false); const initHandle = async () => { if (propsData.drawerData?.id) { const { code, data } = await getSmsInfo(propsData.drawerData.id); if (code === '00000') { console.log(data); formData.textarea = data.textarea; formData.id = data.id; } } nextTick(() => { editDataLoading.value = true; }); }; initHandle(); const formDataRef = ref<FormInstance>(); const formData = reactive<IFormData>({ textarea: '', id: '', }); const rules = reactive<FormRules<IFormData>>({ textarea: [ { required: true, message: '请输入富文本', trigger: 'blur', }, ], }); const submitForm = (formEl: FormInstance | undefined) => { if (!formEl) return; formEl.validate(async (valid, fields) => { if (valid) { console.log(formData); const { code, data } = await saveSms(formData); if (code === '00000') { ElMessage.success(data.msg || '保存成功!'); emit('closeDrawerHandle', true); } } else { console.log(fields); ElMessage.error('保存失败!'); } }); }; const resetForm = (formEl: FormInstance | undefined) => { if (!formEl) return; formEl.resetFields(); }; </script> <style scoped></style>
DROP TABLE IF EXISTS `tinymce_content`;
CREATE TABLE `tinymce_content` (
`id` bigint NOT NULL AUTO_INCREMENT,
`textarea` longtext NOT NULL DEFAULT '' COMMENT '富文本内容',
`create_time` date NOT NULL DEFAULT '' COMMENT '添加时间',
PRIMARY KEY (`id`) USING BTREE,
) ENGINE = InnoDB AUTO_INCREMENT = 0 CHARACTER SET = utf8;
DROP TABLE IF EXISTS `tinymce_img`;
CREATE TABLE `tinymce_img` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '图片名称',
`url` varchar(100) NOT NULL DEFAULT '' COMMENT '图片地址',
`type` varchar(50) NOT NULL DEFAULT '' COMMENT '图片类型',
`blob` mediumblob DEFAULT NULL COMMENT '图片内容(不建议将图片存入数据库)',
`create_time` date NOT NULL DEFAULT '' COMMENT '添加时间',
PRIMARY KEY (`id`) USING BTREE,
) ENGINE = InnoDB AUTO_INCREMENT = 0 CHARACTER SET = utf8;
// /controllers/TinymceImgContent.php <?php namespace app\controllers; use Yii; use app\models\TinymceImg; // 链接tinymce_img数据库的文件,里面也继承了一些封装的基类,如getDbAll()、create() use app\controllers\BaseController; //封装一些基类,如response class ImageController extends BaseController { public function actionIndex() { return $this->render('index'); } public function actionUpload() { $model = new Images(); // $_FILES 获取文件信息 $fileData = $_FILES['file']; // 上传文件的临时路径 $tempFile = $fileData['tmp_name']; // 上传文件的类型 $fileType = $fileData['type']; // 尺寸大小 $size = $fileData['size']; // 名称 $name = time() . '_' . $fileData["name"]; // dd($fileData); // 是否上传的文件 if (is_uploaded_file($tempFile)) { // 从临时文件中读取内容 $img = file_get_contents($tempFile); // Yii::$app->getBasePath() 项目的根目录,设置绝对访问地址 $saveLocalUrl = Yii::$app->getBasePath() . "/web/static/" . $name; $imgLink = 'http://' . $_SERVER['HTTP_HOST'] . "/static/$name"; // move_uploaded_file 将文件存在本地web/static中 move_uploaded_file($tempFile, $saveLocalUrl); $data = [ // "img" => $img, // 存储在数据库,不建议 "date" => date("Y-m-d H:i:s"), "name" => $name, "url" => $imgLink, "type" => $fileType, ]; $row = $model->create($data); if (isset($row)) { $res = ['imgUrl' => $imgLink]; return $this->response($res); } else { $this->code = "00001"; $this->msg = "图片保存失败"; return $this->response(); } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。