富文本CKEditor5简易入门,包括自定义上传图片(html版+vue.js)

一、安装及引入

官网:https://ckeditor.com/ckeditor-5/download/

我这边使用的是自定义构建,然后下载下来。
在这里插入图片描述

二、简单使用

引入js

<script src="../../../assets/plugins/ckeditor5/ckeditor.js"></script>

html:

<el-form-item label="服务内容">
     <textarea id="editor"></textarea>
</el-form-item>

js:

ClassicEditor
     .create( document.querySelector( '#editor' ))
     .then( editor => {
         this.editor = editor;
     } )
     .catch( error => {
         console.error( error );
     } );

获取富文本内容

var data = encodeURIComponent(this.editor.getData())

设置富文本内容

this.editor.setData(decodeURIComponent(content));

PS:这边进行编解码,便于存入数据库中

三、自动保存

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        autosave: {
            save( editor ) {
                // 获取编辑器中的数据
                const data = editor.getData();
                // 将数据保存到本地
                localStorage.setItem( 'autosave', data );
            },
            waitingTime: 5000 // 设置保存间隔时间为 5}
    })
    .then( editor => {
        this.editor = editor;

        // 获取本地保存的数据
        const savedData = localStorage.getItem( 'autosave' );
        if ( savedData ) {
            // 提示用户是否恢复数据 (sweatAlert:https://sweetalert.js.org/)
            swal({
                title: "提示",
                text: "检测到上次编辑的内容,是否恢复?",
                icon: "warning",
                buttons: true,
                dangerMode: true,
            }).then((willDo) => {
                if (willDo) {
                    // 将数据恢复到编辑器中
                    editor.setData( savedData );
                } else {
                    // 清空本地保存的数据
                    localStorage.removeItem( 'autosave' );
                }
            });
        }
    } )
    .catch( error => {
        console.error( error );
    } );

四、图片上传

自定义UploadAdapter.js:

/**
 * 图片压缩处理,转换为等比的高800px的图像
 * @params file File类型的图片文件
 * @return Promise<file> 返回一个promise,值为一个压缩后的图片文件
 */
function imgCutdown(file) {
    return new Promise((resolve) => {
        const render = new FileReader();
        render.onload = function(progress) {
            const target = progress.target;
            if (!target) return;

            const reuslt = target.result;
            if (typeof reuslt === "string") {
                const image = new Image();
                image.src = reuslt;
                image.onload = function() {
                    const h = 800;
                    const rate = h / image.height;
                    const canvas = document.createElement("canvas");
                    const context = canvas.getContext("2d");
                    if (!context) return;
                    canvas.width = image.width * rate;
                    canvas.height = h;
                    context.drawImage(
                        image,
                        0,
                        0,
                        image.width,
                        image.height,
                        0,
                        0,
                        canvas.width,
                        canvas.height
                    );
                    canvas.toBlob(
                        function(b) {
                            const file = new File([b], "pic", {
                                type: "image/jpeg",
                            });
                            resolve(file);
                        },
                        "image/jpeg",
                        0.5
                    );
                };
            }
        };
        render.readAsDataURL(file);
    });
}

const uploadUrl = '/backend/upload/media';

// 自定义适配器类
class MyUploadAdapter {
    constructor(loader) {
        this.loader = loader;
    }

    upload() {
        return this.loader.file.then(
            (file) =>
                new Promise((resolve, reject) => {
                    this._initRequest();
                    this._initListeners(resolve, reject, file);
                    this._sendRequest(file);
                })
        );
    }

    abort() {
        if (this.xhr) {
            this.xhr.abort();
        }
    }

    _initRequest() {
        const xhr = (this.xhr = new XMLHttpRequest());

        xhr.open("POST", uploadUrl, true);
        xhr.responseType = "json";
    }

    _initListeners(resolve, reject, file) {
        const xhr = this.xhr;
        const loader = this.loader;
        const genericErrorText = `Couldn't upload file: ${file.name}.`;

        xhr.addEventListener("error", () => reject(genericErrorText));
        xhr.addEventListener("abort", () => reject());
        xhr.addEventListener("load", () => {
            const response = xhr.response;

            if (!response || response.error) {
                return reject(
                    response && response.error ? response.error.message : genericErrorText
                );
            }

            resolve({
                default: response.data,
            });
        });

        if (xhr.upload) {
            xhr.upload.addEventListener("progress", (evt) => {
                if (evt.lengthComputable) {
                    loader.uploadTotal = evt.total;
                    loader.uploaded = evt.loaded;
                }
            });
        }
    }

    async _sendRequest(file) {
        const data = new FormData();

        // 判断如果上传图片大于1M,则进行压缩处理
        if (file.size > 1000 * 1024) {
            file = await imgCutdown(file);
        }

        // 上传参数就根据后端的处理而设置了
        data.append("file", file);
        data.append("name", file.name);
        data.append("group", "image");

        this.xhr.send(data);
    }
}

后端代码:

@RestController
@RequestMapping("/upload")
public class UploadController extends BaseController {

    @Autowired
    private QNYService qnyService;

    @Value("${QNY_DOMAIN}")
    private String QNY_DOMAIN;

    @RequestMapping("media")
    public ApiResponse uploadImage(HttpServletRequest request, String group) throws IOException {
        MultipartFile file = ((StandardMultipartHttpServletRequest) request).getFile("file");
        String key = "upload/" + group + "/" + this.get32UUID();
        qnyService.uploadFile(file, key);
        return ApiResponse.buildOk(QNY_DOMAIN+key);
    }
}

public class ApiResponse {
    int code;
    String msg;
    Object data;
}

QNYServiceImpl:

@Service
@Slf4j
public class QNYServiceImpl implements QNYService {
    @Value("${QNY_ACCESS_KEY}")
    String QNY_ACCESS_KEY;

    @Value("${QNY_SECRET_KEY}")
    String QNY_SECRET_KEY;

    private static final String BUCKET_NAME = "ehu";

    @Autowired
    private RedisService redisService;

    @Override
    public void deleteImg(String url) {
        Configuration cfg = new Configuration(Region.huadong());
        Auth auth = Auth.create(QNY_ACCESS_KEY, QNY_SECRET_KEY);
        BucketManager bucketManager = new BucketManager(auth, cfg);
        try {
            bucketManager.delete(BUCKET_NAME, url);
        } catch (QiniuException ex) {
            log.error("qiniuyun delete fail code {} msg {}", ex.code(), ex.response.toString(), ex);
        }
    }

    @Override
    public String getUploadToken() {
        String token = redisService.getQNYToken();
        if( token == null){
            Auth auth = Auth.create(QNY_ACCESS_KEY, QNY_SECRET_KEY);
            token = auth.uploadToken(BUCKET_NAME);
            redisService.setQNYToken(token);
        }
        return token;
    }

    @Override
    public String uploadUrlFile(String url, String prefix) {
        Configuration cfg = new Configuration(Region.huadong());
        Auth auth = Auth.create(QNY_ACCESS_KEY,QNY_SECRET_KEY);
        BucketManager bucketManager = new BucketManager(auth,cfg);
        try {
            FetchRet fetch = bucketManager.fetch(url, BUCKET_NAME, prefix + UuidUtil.get32UUID());
            return fetch.key;
        } catch (QiniuException e) {
            log.error("qiniuyun upload fail url{}", url, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void uploadFile(MultipartFile file, String key) throws IOException {
        Configuration cfg = new Configuration(Region.huadong());
        Auth auth = Auth.create(QNY_ACCESS_KEY, QNY_SECRET_KEY);
        String upToken = auth.uploadToken(BUCKET_NAME);
        UploadManager uploadManager = new UploadManager(cfg);
        uploadManager.put(file.getBytes(), key, upToken);
    }
}

html:

<script src="../../../assets/plugins/ckeditor5/UploadAdapter.js"></script>

<script type="text/javascript">
...
	mounted() {
		ClassicEditor
		  .create( document.querySelector( '#editor' ))
		   .then( editor => {
		       editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
		           return new MyUploadAdapter(loader);
		       };
		       this.editor = editor;
		   } )
		   .catch( error => {
		       console.error( error );
		   } );
	}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/44671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

能直接运营的校园跑腿代买拿寄取小程序开发

​说到做跑腿生意&#xff0c;除了做同城跑腿配送外&#xff0c;校园跑腿可是即成本又低又好操作的一个项目。 一般省会城市大学大专基本都是有好几所的&#xff0c;学校的特点是人员密集&#xff0c;跑腿配送周期短&#xff0c;且配送人员好招募&#xff0c;推广人员好招募。…

【Postman】- 基本用法

一、用例集 1.1 用例集 Collections&#xff1a;用例集。目录下可以创建子目录。 1.2 导出用例集 1.3 导入用例集 二、Postman断言 断言&#xff1a;让程序判断预期结果和实际结果是否一致 2.1 特点 Postman的断言是使用JavaScript语言编写的&#xff0c;写在"Tests&…

智慧导诊系统源码:基于springboot+redis+mybatis plus和mysql开发

智慧导诊系统源码 智慧导诊小程序源码&#xff0c;智慧导诊APP源码 人们经常去医院以不知道挂什么科而烦恼&#xff0c;有些病人不方便问又不好意思问。在互联网医院中挂号且又不知该挂什么科&#xff0c;找什么类型的医生&#xff0c;这些不足&#xff0c;给患者带来了极大的…

Ubuntu下打开QtCreator环境变量LD_LIBRARY_PATH与终端不一致

问题描述&#xff1a; 在unbuntu下使用QtCreator编译、运行程序时&#xff0c;总是出现XXX.so: cannot open shared object file: No such file or directory这类问题&#xff0c;但是在终端中编译或者运行程序则不会出现这些问题。在网上查了好久才明白QtCreator在打开时&…

CSS3 Flexbox

Flex 是 Flexible Box 的缩写&#xff0c;意为弹性盒子布局。 CSS3中一种新的布局模式&#xff1a;W3C在2009年提出的一种布局方案&#xff0c;一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。其目的是提供一种更加有效的方式来对一个容器…

怎么学习JavaWeb开发? - 易智编译EaseEditing

学习JavaWeb开发可以按照以下步骤进行&#xff1a; 掌握Java基础&#xff1a; 在学习JavaWeb开发之前&#xff0c;确保你对Java编程语言有一定的掌握&#xff0c;包括面向对象编程、基本语法、数据类型、流程控制等。 学习HTML、CSS和JavaScript&#xff1a; JavaWeb开发主要…

# jellyfin安装设置使用散记

jellyfin安装设置使用散记 文章目录 jellyfin安装设置使用散记0 软件简介1 安装2 视频转码问题2.1 局域网转码情况测试&#xff08;不同网段&#xff09;2.2 局域网jellyfin app默认转码问题解决2.3 外网转码情况测试 3 一些坑4 插件5 最后 0 软件简介 Jellyfin 是一个自由的软…

专项练习-04编程语言-03JAVA-01

1. 以下有关构造方法的说法&#xff0c;正确的是&#xff1a;&#xff08;&#xff09; A 一个类的构造方法可以有多个 B 构造方法在类定义时被调用 C 构造方法只能由对象中的其他方法调用 D 构造方法可以和类同名&#xff0c;也可以和类名不同 正确答案&#xff1a;A 官方解析…

智能井盖:科技赋能城市脚下安全

在智能化飞速发展的今天&#xff0c;智能井盖作为城市基础设施的一部分&#xff0c;正逐渐走进人们的视野。它利用现代科技手段&#xff0c;实现了对城市井盖的实时监控、及时响应和高效管理&#xff0c;为城市管理、市民出行等方面带来了诸多便利。 城市中井盖数量庞大&#x…

【ribbon】Ribbon的使用与原理

负载均衡介绍 负载均衡&#xff08;Load Balance&#xff09;&#xff0c;其含义就是指将负载&#xff08;工作任务&#xff09;进行平衡、分摊到多个操作单元上进行运行&#xff0c;例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等&#xff0c;从而协同…

[Cotex-M3学习教程]-0.1-Cortex-M3概述

目录 1 Cortex-M3概述 1.1 ARM 处理器 1.2 cortex-M3介绍 1.3 cortex-M3结构概览图 1.4 cortex-M3组件 1.4.1 内核系统 1.4.2 NVIC 1.4.3 寄存器组 控制寄存器&#xff08;CONTROL&#xff09; 程序计数寄存器&#xff08;PC:R15&#xff09; 堆栈指针寄存器&#xf…

Raki的读paper小记:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

Abstract&Introduction&Related Work 研究任务 对大模型进行部分微调 已有方法和相关工作 现有技术通常通过扩展模型深度引入推理延迟&#xff08;Houlsby 等人&#xff0c;2019&#xff1b;Rebuffi 等人&#xff0c;2017&#xff09;&#xff0c;或通过减少模型可用序…

28.JavaWeb-Elasticsearch

1.Elasticsearch概述 Elasticsearch 是一个分布式的全文检索引擎。采用Java语言开发&#xff0c;基于Apache协议的开源项目&#xff0c;具有实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速的特点。 1.1 全文检索引擎 分为通用搜索引擎&#xff08;百度、谷歌&…

基于Java+SpringBoot+vue前后端分离在线商城系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

java项目之人事管理系统(ssm+mysql+jsp)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的人事管理系统。技术交流和部署相关看文章末尾&#xff01; 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Java 框架&…

【MySQL进阶(一)】MySQL在Linux中的配置信息和数据备份工具

MySQL在Linux中安装的话可以看这篇博客&#xff1a;MySQL在Linux中的安装&#xff0c;我觉得总结的很好。 my.cnf 中的配置信息 当 MySQL 启动的时候&#xff0c;会从安装目录中加载软件数据&#xff0c;即使用 mysqld 工具初始化设置的 --basedir&#xff0c;会从数据目录中…

【iOS】—— block,KVC,KVO,Category等问题解答

文章目录 block1.block的原理是怎样的&#xff1f;本质是什么&#xff1f;2.__block的作用是什么&#xff1f;有什么使用注意点&#xff1f;3.block的属性修饰词为什么是copy&#xff1f;使用block有哪些使用注意&#xff1f;4.block在修改NSMutableArray&#xff0c;需不需要添…

不止工具:音视频开发「利器」的新机遇

Boxing的制胜关键是快、准、稳&#xff0c;与“音视频开发”有异曲同工之妙。 数字化浪潮席卷、视频化形态加速、终端性能挑战加剧、端侧算力遭遇瓶颈...... 是否存在一种可能性&#xff0c;让所有企业从复杂的音视频开发工程中抽身&#xff0c;重新回归业务本身&#xff1f; …

消息队列总结(3)- RabbitMQ Kafka RocketMQ高可用方案

目录 1. 什么是高可用&#xff1f; 1.1 常见的高可用方法 1.2 消息队列的高可用 2. RabbitMQ的高可用方案 2.1 镜像队列 2.2 消息生产的确认机制 2.3 消息的持久化 3. Kafka的高可用方案 3.1 消息备份 3.2 ISR & IEO & HW 3.3 消息生产的确认机制 4. Rocke…

微信小程序开发使用echarts报错Cannot read property ‘getAttribute‘ of undefined

如图&#xff0c;我在小程序圈中的区域渲染echarts图标报错了&#xff0c;报错提示Cannot read property getAttribute of undefined 这里的canvas &#xff0c;width ,height,dpr获取为 undefined 分析问题&#xff1a; 初始化图表时传递错误的参数&#xff1a; 在 onShow 生…