vue 腾讯云 javascript sdk + 简单富文本组件设计+实战

html 复制代码
<template>
    <div>
        <quill-editor v-model="content" ref="myQuillEditor" :options="editorOption" @change="onEditorChange"
            @input="handleInput"></quill-editor>

        <!-- 链接添加对话框 -->
        <el-dialog title="添加链接" :visible.sync="linkDialogVisible" width="30%" :close-on-click-modal="false">
            <el-form ref="linkForm" :model="linkForm" label-width="80px">
                <el-form-item label="链接地址">
                    <el-input v-model="linkForm.url" placeholder="请输入链接地址,例如:http://" autocomplete="off"
                        @blur="validateLink(linkForm.url.trim())"></el-input>
                </el-form-item>
                <el-form-item label="备注" style="margin-top: 10px;margin-bottom: 10px;">
                    <el-input v-model="linkForm.text" placeholder="请输入链接备注" :disabled="!linkForm.url"></el-input>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleLinkSubmit">确认</el-button>
                    <el-button @click="closeLinkDialog">取消</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>


        <!-- 图片设置对话框 -->
        <el-dialog title="插入图片" :visible.sync="imageDialogVisible" width="25%" :close-on-click-modal="false" @close="closeImageDialog"
            class="pic-dialog">
            <el-tabs v-model="activeTab" ref="imageTabs">
                <el-tab-pane label="本地图片" name="localImage">
                    <el-form :model="localImageForm" label-width="80px">
                        <el-form-item>
                            <el-upload ref="imageUpload" class="upload-demo" :http-request="uploadToCOS"
                                :on-success="handleImageSuccess" :on-error="handleError" accept="image/*"
                                :on-change="handleImageChange" action="#" :show-file-list="false"
                                :on-progress="handleImageUploadProgress">

                                <div class="image-item">
                                    <el-image v-if="localImageForm.imageUrl" :src="localImageForm.imageUrl"
                                        fit="contain" slot="trigger">
                                    </el-image>
                                    <img v-else src="@/assets/default-image.jpg" alt="Upload Failed Image">
                                </div>
                            </el-upload>
                            <el-progress :percentage="uploadImagePercentage" style="margin-bottom: 10px;width:200px"></el-progress>
                        </el-form-item>
                       
                        <el-form-item>
                            <el-button type="primary" @click="insertLocalImages">确认</el-button>
                            <el-button @click="closeImageDialog">取消</el-button>
                        </el-form-item>
                    </el-form>
                </el-tab-pane>
                <el-tab-pane label="链接图片" name="urlImage">
                    <el-form :model="urlImageForm" label-width="80px">
                        <el-form-item label="图片链接">
                            <el-input v-model="urlImageForm.url" placeholder="请输入图片链接" autocomplete="off"></el-input>
                            <div class="image-item" style="margin-bottom: 20px;">
                                <el-image v-if="urlImageForm.url" :src="urlImageForm.url" fit="contain"
                                   ></el-image>
                                <img v-else src="@/assets/default-image.jpg" alt="Upload Failed Image">
                            </div>
                        </el-form-item>

                        
                        <el-form-item>
                            <el-button type="primary" @click="insertURLImage">确认</el-button>
                            <el-button @click="closeImageDialog">取消</el-button>
                        </el-form-item>
                    </el-form>
                </el-tab-pane>
            </el-tabs>
        </el-dialog>


        <!-- 视频设置对话框 -->
        <el-dialog title="插入视频" :visible.sync="videoDialogVisible" width="25%" :close-on-click-modal="false" @close="closeVideoDialog"
           >
            <el-tabs v-model="activeVideoTab" ref="videoTabs">
                <el-tab-pane label="本地视频" name="localVideo">
                    <el-form :model="localVideoForm" label-width="80px">
                        <el-form-item label="视频文件" style="margin-top: 10px;margin-bottom: 20px;">
                            <!-- 视频上传 -->
                            <el-upload ref="videoUpload" class="upload-demo" :http-request="uploadToCOS"
                                :on-success="handleVideoSuccess" :on-error="handleError" :auto-upload="false"
                                :on-progress="handleUploadProgress" :show-file-list="false" accept="video/*"
                                :on-remove="handleVideoRemove" :on-change="handleVideoChange" action="#">
                                <el-button slot="trigger" size="small" type="success"
                                    style="width:300px">打开并上传</el-button>

                                <div class="video-item">
                                    <video v-if="localVideoForm.videoUrl" :src="localVideoForm.videoUrl"
                                        controls="controls" width="300px" height="150px"></video>
                                    <div slot="tip" class="el-upload__tip"></div>
                                </div>
                                <el-progress :percentage="uploadVideoPercentage" style="width:355px"></el-progress>
                            </el-upload>

                        </el-form-item>
                        <el-form-item label="设置宽度" style="margin-bottom: 20px;">
                            <el-input v-model.number="localVideoForm.width" placeholder="请输入宽度"></el-input>
                        </el-form-item>
                        <el-form-item>
                            <el-button type="primary" @click="insertLocalVideo">确认</el-button>
                            <el-button @click="closeVideoDialog">取消</el-button>
                        </el-form-item>
                    </el-form>
                </el-tab-pane>
                <el-tab-pane label="视频链接" name="urlVideo">
                    <el-form :model="urlVideoForm" label-width="80px">
                        <el-form-item label="视频链接" style="margin-top: 10px;margin-bottom: 20px;">
                            <el-input v-model="urlVideoForm.url" placeholder="请输入视频链接,例如:http://" autocomplete="off"
                            @blur="validateLink(urlVideoForm.url.trim())"></el-input>
                        </el-form-item>
                        <el-form-item>
                            <el-button type="primary" @click="insertURLVideo">确认</el-button>
                            <el-button @click="closeVideoDialog">取消</el-button>
                        </el-form-item>
                    </el-form>
                </el-tab-pane>
            </el-tabs>
        </el-dialog>
    </div>
</template>

<script>
import { quillEditor } from "vue-quill-editor";
import { Quill } from "vue-quill-editor";
import resizeImage from "quill-image-resize-module";
import { ImageDrop } from "quill-image-drop-module";

import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";

Quill.register("modules/imageResize", resizeImage);
Quill.register("modules/imageDrop", ImageDrop);
Quill.register("modules/resizeImage", resizeImage);

import "@/styles/quillEditor.css"
import { titleConfig, toolbarOptions } from "@/js/quillEditor";

import COS from 'cos-js-sdk-v5';
import { v4 as uuidv4 } from 'uuid';

// 扩展Quill以支持视频嵌入
const BlockEmbed = Quill.import('blots/block/embed');
class VideoBlot extends BlockEmbed {
  static create(value) {
    let node = super.create();
    node.setAttribute('src', value.url);
    node.setAttribute('controls', true);
    node.setAttribute('width', value.width || '50%');
    node.setAttribute('height', value.height || 'auto');
    return node;
  }

  static value(node) {
    return {
      url: node.getAttribute('src'),
      width: node.getAttribute('width'),
      height: node.getAttribute('height')
    };
  }
}
VideoBlot.blotName = 'video';
VideoBlot.tagName = 'video';
Quill.register(VideoBlot);
export default {
    components: {
        quillEditor,
    },
    data() {
        return {
            content: "",
            editorOption: {
                modules: {
                    toolbar: {
                        container: toolbarOptions,
                        handlers: {
                            image: this.openImageDialog,
                            link: this.openLinkDialog,
                            video: this.openVideoDialog
                        },
                    },
                    imageDrop: true,
                    imageResize: {
                        displayStyles: {
                            backgroundColor: "black",
                            border: "none",
                            color: "white",
                        },
                        modules: ["Resize", "DisplaySize", "Toolbar"],
                    },
                },
                placeholder: "请输入正文....",
                theme: "snow",
            },
            linkDialogVisible: false,
            linkForm: {
                url: 'https://',
                text: '',
            },
            currentRange: null, // 保存当前选区信息
            imageDialogVisible: false,
            videoDialogVisible: false,
            activeTab: 'localImage', // 默认选中本地图片
            activeVideoTab: 'localVideo', // 默认选中本地视频
            localImageForm: {
                file: null,
                imageUrl: '', // 用于显示选中的本地图片
            },
            urlImageForm: {
                url: '',
            },
            localVideoForm: {
                file: null,
                videoUrl: '',
                width: '',
            },
            urlVideoForm: {
                url: 'https://',
                width: '',
            },
            file: null,
            editorDialogVisible: false,
            htmlData: '',
            cos: null, // 用于存储COS实例
            uploadVideoPercentage: 0,
            uploadImagePercentage:0,
            urlVideoUrl:''
        };
    },
    created() {
        // 初始化COS实例
        this.cos = new COS({
            SecretId: '', // 替换为你的SecretId
            SecretKey: '', // 替换为你的SecretKey
        });
    },
    methods: {
        urlImageFormInput() {
            console.log("是否获取焦点")
        },
        uploadToCOS({ file, onProgress, onSuccess, onError }) {
            // 生成唯一的文件名
            const uniqueFileName = this.generateUniqueFileName(file.name);
            this.cos.uploadFile({
                Bucket: '', // 替换为你的Bucket
                Region: '', // 替换为你的Region
                Key: uniqueFileName, // 使用生成的唯一文件名
                Body: file,
                SliceSize: 1024 * 1024, // 分块大小,单位为字节,这里设置为1MB
                onProgress: function (progressData) {
                    onProgress(progressData);
                }
            }, (err, data) => {
                if (err) {
                    onError(err);
                } else {
                    onSuccess(data);
                }
            });
        },
        // 打开本地图片、视频、文件
        previewFile(file) {
            const reader = new FileReader();
            reader.readAsDataURL(file);
        },
        // 生成唯一文件名
        generateUniqueFileName(originalName) {
            // 获取文件扩展名
            const extension = originalName.substring(originalName.lastIndexOf('.'));
            // 生成唯一文件名
            const uniqueName = uuidv4();
            // 返回新的文件名
            return `${uniqueName}${extension}`;
        },
        // 提交上传本地图片、视频、文件
        submitUpload(refName) {
            this.$refs[refName].submit();
        },
        /* eslint-disable */
        handleError(err, file) {
            console.error('上传失败:', err);
        },
        handleInput() {
            // 输入内容后,隐藏错误信息
            this.$emit('input', this.content.trim());
        },

        /* eslint-disable */
        onEditorChange({ quill, html, text }) {
            this.content = html;
        },
        setTitleConfig() {
            for (const item of titleConfig) {
                const tip = document.querySelector(".quill-editor " + item.Choice);
                if (!tip) continue;
                tip.setAttribute("title", item.title);
            }
        },
        // 打开插入链接对话框 *********************************************************************************
        openLinkDialog() {
            const quill = this.$refs.myQuillEditor.quill;
            if (!quill) {
                console.error('Quill instance not found');
                return;
            }

            // 获取当前选区
            this.currentRange = quill.getSelection();
            if (!this.currentRange) {
                console.error('Selection not found');
                return;
            }

            // 重新打开对话框时清空输入框
            this.linkForm.url = 'https://';
            this.linkForm.text = '';

            this.linkDialogVisible = true;
        },
        // 关闭插入链接对话框
        closeLinkDialog() {
            this.linkDialogVisible = false;
        },
        handleLinkSubmit() {
            const quill = this.$refs.myQuillEditor.quill;
            if (!quill) {
                console.error('Quill instance not found');
                return;
            }

            const { url, text } = this.linkForm;

            if (!this.currentRange) {
                console.error('Current range not found');
                return;
            }

            console.log("如果有备注文本,则插入带备注的链接");
            // 如果有备注文本,则插入带备注的链接
            if (text) {
                this.$nextTick(() => {
                    const linkHtml = `<a href="${url}" title="${text}">${text}</a>`;
                    quill.clipboard.dangerouslyPasteHTML(this.currentRange.index, linkHtml, 'user');
                    this.linkDialogVisible = false;
                });
            } else {
                // 否则直接插入普通链接
                this.$nextTick(() => {
                    const linkHtml = `<a href="${url}">${url}</a>`;
                    quill.clipboard.dangerouslyPasteHTML(this.currentRange.index, linkHtml, 'user');
                    this.linkDialogVisible = false;
                });
            }
            // 更新内容
            this.content = quill.root.innerHTML;
        },
        validateLink(url) {
            // 校验链接格式
            // const url = this.linkForm.url.trim();
            if (url && !this.validateURL(url)) {
                this.$message.error('链接地址格式不正确,请输入有效的链接地址。');
            }
        },
        validateURL(url) {
            // 修正后的正则表达式用于验证链接格式,支持 http 和 https 协议
            const urlPattern = /^(https?:\/\/)?[\w.-]+\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)?$/;
            return urlPattern.test(url);
        },
        // 打开插入图片对话框 *********************************************************************************
        openImageDialog() {
            // 获取当前光标位置
            const quill = this.$refs.myQuillEditor.quill;
            if (quill) {
                this.currentRange = quill.getSelection();
            }
            if (!this.currentRange) {
                return;
            }
            this.imageDialogVisible = true;
            // 在下一次DOM更新后模拟点击本地图片选项卡
            this.$nextTick(() => {
                const tabs = this.$refs.imageTabs;
                if (tabs) {
                    tabs.setCurrentName('localImage');
                }
            });

        },
        // 关闭插入图片对话框
        closeImageDialog() {
            this.imageDialogVisible = false;
            // 重置表单
            this.localImageForm.file = null;
            this.localImageForm.imageUrl = '';
            this.urlImageForm.url = '';
            this.uploadImagePercentage = 0;
        },

        // 2.1 插入本地图片
        insertLocalImages() {
            const { imageUrl } = this.localImageForm;
            if (!imageUrl) {
                return;
            }
            // 直接使用上传后的URL调用insertImage方法
            this.insertImage(imageUrl);
        },
        /* eslint-disable */
        handleImageSuccess(response, file) {
            console.log('图片上传成功:', response);
            const fileUrl = `https://${response.Location}`;
            this.localImageForm.imageUrl = fileUrl;
            // this.$refs.imageUpload.clearFiles(); //去掉文件列表 
        },
        handleImageChange(file) {
            if (file.raw) {
                this.file = file.raw; // 获取选择的文件对象
                this.previewFile(file.raw); // 预览图片
                this.submitUpload('imageUpload');
            }
        },
        handleImageRemove() {
            this.localImageForm.imageUrl = ''; // 重置视频文件
        },
        handleImageUploadProgress(event, file, fileList) {
            this.uploadImagePercentage = Math.round(event.loaded / event.total * 100);
        },
        // 2.2 插入链接图片
        insertURLImage() {
            const { url} = this.urlImageForm;
            this.insertImage(url);
        },
        // 2.3 图片嵌入到富文本
        insertImage(url) {
            const quill = this.$refs.myQuillEditor.quill;
            if (!quill) {
                console.error('Quill实例未找到');
                return;
            }

            const range = this.currentRange;
            // quill.clipboard.dangerouslyPasteHTML(range.index, `<img src="${url}" style="width: ${width}px; height: ${height}px;">`, 'user');
            quill.clipboard.dangerouslyPasteHTML(range.index, `<img src="${url}">`, 'user');
            this.content = quill.root.innerHTML;
            // 调整插入的图片大小并设置id
            this.$nextTick(() => {
                const imgTags = quill.root.querySelectorAll('img');
                imgTags.forEach((img, index) => {
                    if (!img.id) {
                        const imageId = `img-${Date.now()}-${index}`;
                        img.setAttribute('id', imageId);
                    }
                });
            });

            // 插入后重置对话框和表单
            this.closeImageDialog();
        },

        adjustEditorHeight() {
            const quill = this.$refs.myQuillEditor.quill;
            if (!quill) return;

            const editorElement = quill.root;
            if (!editorElement) return;

            editorElement.style.minHeight = "500px";
        },
        // 视频设置 *********************************************************************************
        // 打开插入视频对话框
        openVideoDialog() {
            // 获取当前光标位置
            const quill = this.$refs.myQuillEditor.quill;
            if (quill) {
                this.currentRange = quill.getSelection();
            }
            if (!this.currentRange) {
                return;
            }
            this.videoDialogVisible = true;
            // 在下一次DOM更新后模拟点击本地图片选项卡
            this.$nextTick(() => {
                const tabs = this.$refs.videoTabs;
                if (tabs) {
                    tabs.setCurrentName('localVideo');
                }
            });

        },
        // 关闭插入视频对话框
        closeVideoDialog() {
            this.videoDialogVisible = false;
            // 重置表单
            this.localVideoForm.file = null;
            this.localVideoForm.videoUrl = '';
            this.localVideoForm.width = '';
            this.urlVideoForm.url = '';
            this.urlVideoForm.width = '';
            // this.$refs.videoUpload.clearFiles(); // 清除上传文件列表
            this.uploadVideoPercentage = 0;
        },
        // 3.1 插入本地视频
        insertLocalVideo() {
            const { videoUrl, width } = this.localVideoForm;
            if (!videoUrl) {
                return;
            }
            // 直接使用上传后的URL调用insertVideo方法
            this.insertVideo(videoUrl, width);
        },
        handleVideoSuccess(response, file) {
            console.log('视频上传成功:', response);
            const fileUrl = `https://${response.Location}`;
            this.localVideoForm.videoUrl = fileUrl;
        },
        handleError(err, file) {
            console.error('上传失败:', err);
        },
        handleVideoChange(file) {
            if (file.raw) {
                this.file = file.raw; // 获取选择的文件对象
                this.previewFile(file.raw); // 预览视频
            }
            this.submitUpload('videoUpload')
        },
        handleVideoRemove() {
            this.localVideoForm.videoUrl = null; // 重置视频文件
        },
        handleUploadProgress(event, file, fileList) {
            this.uploadVideoPercentage = Math.round(event.loaded / event.total * 100);
        },
        // 3.2 插入链接视频
        insertURLVideo() {
            const { url, width } = this.urlVideoForm;
            this.insertVideo(url, width);
        },
        // 3.3 插入视频到富文本中
        insertVideo(url, width) {
            const quill = this.$refs.myQuillEditor.quill;
            if (!quill) {
                console.error('Quill实例未找到');
                return;
            }
            const range = this.currentRange;
            const index = range ? range.index : quill.getLength();
            quill.insertEmbed(index, 'video', {
                url: url,
                width: width,
            });
            this.content = quill.root.innerHTML;

            this.closeVideoDialog();
        },
    },
    mounted() {
        this.setTitleConfig();
    },
};
</script>

<style scoped>
/* 可以添加自定义样式 */
.image-item {
    margin-top: 20px;
    width: 150px;
    height: 150px;
    overflow: hidden;
    border: 2px dashed #ccc;
    background-color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    cursor: pointer;
    /* margin-bottom: 20px; */
}

.video-item {
    margin-top: 20px;
    width: 300px;
    height: 150px;
    overflow: hidden;
    border: 2px dashed #ccc;
    background-color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    cursor: pointer;
}

.progress-bar {
    width: 300px;
    height: 10px;
    background-color: #f0f0f0;
    margin-top: 10px;
}

.progress-bar-inner {
    height: 100%;
    background-color: #409eff;
}
</style>

<style scoped>
.pic-dialog {
    .el-dialog__header {
        border-bottom: none;
    }

    .el-dialog__body {
        padding-top: 0;
        margin-right: 10px;
    }

    .dialog-content {
        padding: 0 40px;
    }

    .el-dialog__footer {
        padding: 10px 10px 10px;
        border-top: none;
    }
}

.ql-editor.ql-blank /deep/ {
    min-height: 500px !important;
    /* 设置空内容时的最小高度 */
}

.quill-editor {
    line-height: normal;
}
</style>

quillEditor.css

css 复制代码
/* 字体风格 */
/* 处理下拉字体选择器中选项的文本溢出并显示省略号 */
.ql-snow .ql-picker.ql-font .ql-picker-label::before {
  width: 88px; /* 设置下拉选项宽度,可以根据需要调整 */
  white-space: nowrap; /* 不换行显示 */
  overflow: hidden; /* 隐藏溢出部分 */
  text-overflow: ellipsis; /* 使用省略号显示溢出文本 */
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimSun"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimSun"]::before {
  content: "宋体";
  font-family: "SimSun";
}

.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimHei"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimHei"]::before {
  content: "黑体";
  font-family: "SimHei";
}

.ql-snow
  .ql-picker.ql-font
  .ql-picker-label[data-value="Microsoft-YaHei"]::before,
.ql-snow
  .ql-picker.ql-font
  .ql-picker-item[data-value="Microsoft-YaHei"]::before {
  content: "微软雅黑";
  font-family: "Microsoft YaHei";
}

.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="KaiTi"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="KaiTi"]::before {
  content: "楷体";
  font-family: "KaiTi";
}

.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="FangSong"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="FangSong"]::before {
  content: "仿宋";
  font-family: "FangSong";
}

.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Arial"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Arial"]::before {
  content: "Arial";
  font-family: "Arial";
}

.ql-snow
  .ql-picker.ql-font
  .ql-picker-label[data-value="Times-New-Roman"]::before,
.ql-snow
  .ql-picker.ql-font
  .ql-picker-item[data-value="Times-New-Roman"]::before {
  content: "Times New Roman";
  font-family: "Times New Roman";
}

.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="sans-serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="sans-serif"]::before {
  content: "sans-serif";
  font-family: "sans-serif";
}

.ql-font-SimSun { font-family: "SimSun"; }
.ql-font-SimHei { font-family: "SimHei"; }
.ql-font-Microsoft-YaHei { font-family: "Microsoft YaHei"; }
.ql-font-KaiTi { font-family: "KaiTi"; }
.ql-font-FangSong { font-family: "FangSong"; }
.ql-font-Arial { font-family: "Arial"; }
.ql-font-Times-New-Roman { font-family: "Times New Roman"; }
.ql-font-sans-serif { font-family: "sans-serif"; }

/* 字体大小 */
.ql-snow .ql-picker.ql-size .ql-picker-label::before { content: "字体大小"; }
.ql-snow .ql-picker.ql-size .ql-picker-item::before { content: "常规"; }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before{
  content: "14px";
  font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before{
  content: "16px";
  font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before{
  content: "18px";
  font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before{
  content: "20px";
  font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="22px"]::before{
  content: "22px";
  font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="26px"]::before{
  content: "26px";
  font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="30px"]::before {
  content: "30px";
  font-size: 14px;
}

.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
  content: "14px";
  font-size: 14px;
}

.ql-size-14px { font-size: 14px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
  content: "16px";
  font-size: 16px;
}

.ql-size-16px { font-size: 16px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
  content: "18px";
  font-size: 18px;
}

.ql-size-18px { font-size: 18px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
  content: "20px";
  font-size: 20px;
}

.ql-size-20px { font-size: 20px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="22px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="22px"]::before {
  content: "22px";
  font-size: 22px;
}

.ql-size-22px { font-size: 22px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="26px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="26px"]::before {
  content: "26px";
  font-size: 26px;
}

.ql-size-26px { font-size: 26px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="28px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="28px"]::before {
  content: "28px";
  font-size: 28px;
}

.ql-size-28px { font-size: 28px; }

/* .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="30px"]::before, */
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="30px"]::before {
  content: "30px";
  font-size: 30px;
}

.ql-size-30px { font-size: 30px; }

/* 段落大小 */
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
  content: "标题1";
}

.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
  content: "标题2";
}

.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
  content: "标题3";
}

.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
  content: "标题4";
}

.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
  content: "标题5";
}

.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
  content: "标题6";
}

.ql-snow .ql-picker.ql-header .ql-picker-item::before {
  content: "常规";
}

/* .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, */
.ql-snow .ql-picker.ql-header .ql-picker-label::before {
  content: "标题大小";
}

/* 默认设置 */
.ql-snow .ql-editor { font-size: 14px; }
/* 查看样式 */
.view-editor .ql-toolbar { display: none; }
.view-editor .ql-container.ql-snow { border: 0; }
.view-editor .ql-container.ql-snow .ql-editor { padding: 0; }
/* 编辑样式 */
.edit-editor .ql-toolbar { display: block; }
.edit-editor .ql-container.ql-snow {
  border: 1px solid #ccc;
  min-height: inherit;
}

quillEditor.js

javascript 复制代码
import { Quill } from "vue-quill-editor";
// 自定义字体大小
const sizes = [false,"14px","16px","18px","20px","22px","26px","28px","30px",];
const Size = Quill.import("formats/size");
Size.whitelist = sizes;
// 自定义字体
const fonts = ["SimSun","SimHei","Microsoft-YaHei","KaiTi","FangSong","Arial","Times-New-Roman","sans-serif",];
var Font = Quill.import("formats/font");
Font.whitelist = fonts;
Quill.register(Font, true);

// 工具栏相关配置
export const toolbarOptions = [
  ["bold", "italic", "underline"], // 加粗 斜体 下划线 删除线 
  [{ size: sizes }], // 字体大小
  [{ header: [1, 2, 3, 4, 5, false] }], // 标题
  [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
  // [{ font: fonts }], // 字体种类
  ["link", "image", "video","clean"], // 链接、图片、视频
];

// 设置工具栏中文提示
export const titleConfig = [
  { Choice: ".ql-insertMetric", title: "跳转配置" },
  { Choice: ".ql-bold", title: "加粗" },
  { Choice: ".ql-italic", title: "斜体" },
  { Choice: ".ql-header", title: "段落格式" },
  { Choice: ".ql-strike", title: "删除线" },
  // { Choice: ".ql-font", title: "字体" },
  { Choice: ".ql-align", title: "对齐方式" },
  { Choice: ".ql-color", title: "字体颜色" },
  { Choice: ".ql-background", title: "背景颜色" },
  { Choice: ".ql-image", title: "图像" },
  { Choice: ".ql-video", title: "视频" },
  { Choice: ".ql-link", title: "添加链接" },
  { Choice: ".ql-clean", title: "清除字体格式" },
  { Choice: ".ql-size .ql-picker-item:nth-child(2)", title: "标准" },
];
相关推荐
还是大剑师兰特29 分钟前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
一只小白菜~35 分钟前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding40 分钟前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
man201743 分钟前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
吖秧吖1 小时前
three.js 杂记
开发语言·前端·javascript
前端小超超1 小时前
vue3 ts项目结合vant4 复选框+气泡弹框实现一个类似Select样式的下拉选择功能
前端·javascript·vue.js
大叔是90后大叔1 小时前
vue3中查找字典列表中某个元素的值
前端·javascript·vue.js