如图
1、下载依赖
2、elm引入(可省略)
main.js 或者 按需引入
3、cv
<template>
<div style="background: #f1f3f4">
<div style="width: 100%; height: 42px">
<!-- 工具栏 -->
<Toolbar
id="tool-container"
style="
width: 100%;
background: #ffffff;
border-bottom: 1px solid #e8e8e8;
position: fixed;
top: 0;
z-index: 999;
border-top: 1px solid #e5e5ea;
"
:editor="editor"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
</div>
<!-- 包裹盒子 -->
<div style="width: 100%">
<!-- 最大盒子 -->
<div style="width: 100%; background: #f1f3f4; border-radius: 0px">
<div style="height: 10px"></div>
<!-- 编辑器 -->
<div
style="
overflow-y: hidden;
width: 768px;
margin: 0 auto;
background-color: #fff;
border: 1px solid #e8e8e8;
box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
padding: 48px 72px;
"
>
<!-- 标题栏 -->
<div
style="
height: 54px;
margin: 0 10px;
border-bottom: 1px solid #e8e8e8;
"
>
<el-input
type="text"
placeholder="请输入文章标题(1~24个字)"
v-model="chapterTitle"
maxlength="24"
show-word-limit
>
</el-input>
</div>
<!-- 编辑栏 -->
<Editor
id="editor-container"
style="
min-height: 800px;
width: 100%;
text-align: justify;
border-bottom: 1px solid #e8e8e8;
"
v-model="chapterContent"
:defaultConfig="editorConfig"
:defaultContent="jsonContent"
:mode="mode"
@onCreated="onCreated"
>
</Editor>
<div style="height: 40px; line-height: 40px; text-align: end">
Lv Jj
</div>
<!-- 进度条 -->
<el-progress
v-show="showProgress"
color="#ff570f"
:text-inside="true"
:stroke-width="15"
:percentage="progress"
></el-progress>
</div>
<div style="height: 50px"></div>
</div>
</div>
</div>
</template>
<script>
import Vue from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
// import { getOss } from "@/api/upload.js";
export default Vue.extend({
data() {
let that = this;
return {
progress: 0, // 进度条数据
showProgress: false, //进度条的显示
url: "",
chapterTitle: "", // 章节标题
chapterContent: "", // 章节内容 编辑器初始化内容的位置
// editor配置开始 -------------------------------------------------
editor: null,
toolbarConfig: {
// 工具栏配置
toolbarKeys: [
// 撤销栏
"undo",
"redo",
"|",
// 正文栏
"fontSize",
"bold",
{
key: "group-list",
title: "列表",
iconSvg:
'<svg t="1670983367428" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6698" width="200" height="200"><path d="M187.392 70.656q28.672 0 48.64 19.456t19.968 48.128l0 52.224q0 28.672-19.968 48.64t-48.64 19.968l-54.272 0q-27.648 0-47.616-19.968t-19.968-48.64l0-52.224q0-28.672 19.968-48.128t47.616-19.456l54.272 0zM889.856 70.656q27.648 0 47.616 19.456t19.968 48.128l0 52.224q0 28.672-19.968 48.64t-47.616 19.968l-437.248 0q-28.672 0-48.64-19.968t-19.968-48.64l0-52.224q0-28.672 19.968-48.128t48.64-19.456l437.248 0zM187.392 389.12q28.672 0 48.64 19.968t19.968 48.64l0 52.224q0 27.648-19.968 47.616t-48.64 19.968l-54.272 0q-27.648 0-47.616-19.968t-19.968-47.616l0-52.224q0-28.672 19.968-48.64t47.616-19.968l54.272 0zM889.856 389.12q27.648 0 47.616 19.968t19.968 48.64l0 52.224q0 27.648-19.968 47.616t-47.616 19.968l-437.248 0q-28.672 0-48.64-19.968t-19.968-47.616l0-52.224q0-28.672 19.968-48.64t48.64-19.968l437.248 0zM187.392 708.608q28.672 0 48.64 19.968t19.968 47.616l0 52.224q0 28.672-19.968 48.64t-48.64 19.968l-54.272 0q-27.648 0-47.616-19.968t-19.968-48.64l0-52.224q0-27.648 19.968-47.616t47.616-19.968l54.272 0zM889.856 708.608q27.648 0 47.616 19.968t19.968 47.616l0 52.224q0 28.672-19.968 48.64t-47.616 19.968l-437.248 0q-28.672 0-48.64-19.968t-19.968-48.64l0-52.224q0-27.648 19.968-47.616t48.64-19.968l437.248 0z" p-id="6699"></path></svg>',
menuKeys: ["bulletedList", "numberedList"],
},
"divider",
"blockquote",
{
key: "group-justify",
title: "对齐",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
menuKeys: [
"justifyLeft",
"justifyCenter",
"justifyRight",
"justifyJustify",
],
},
"todo",
{
key: "group-more-style", // 必填,要以 group 开头
title: "...", // 必填
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>', // 可选
menuKeys: [
"italic",
"through",
"underline",
"sup",
"sub",
"indent",
"delIndent",
// 'codeBlock',
// 'code',
"clearStyle",
"lineHeight",
], // 下级菜单 key ,必填
},
"|",
// 颜色栏
"bgColor",
"color",
"|",
{
key: "group-image",
title: "图片",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',
menuKeys: ["insertImage", "uploadImage"],
// menuKeys: ['insertImage']
},
{
key: "group-video",
title: "视频",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M981.184 160.096C837.568 139.456 678.848 128 512 128S186.432 139.456 42.816 160.096C15.296 267.808 0 386.848 0 512s15.264 244.16 42.816 351.904C186.464 884.544 345.152 896 512 896s325.568-11.456 469.184-32.096C1008.704 756.192 1024 637.152 1024 512s-15.264-244.16-42.816-351.904zM384 704V320l320 192-320 192z"></path></svg>',
menuKeys: ["insertVideo", "uploadVideo"],
// menuKeys: ['insertVideo']
},
"insertLink",
"emotion",
"insertTable",
"|",
],
// 135编辑器的位置
// insertKeys: {
// index: 21, // 插入的位置,基于当前的 toolbarKeys
// keys: ['codeSelectLang']
// }
},
// 菜单配置
editorConfig: {
placeholder: "输入正文",
hoverbarKeys: {
text: {
menuKeys: [
"fontSize",
"insertLink",
"bulletedList",
"|",
"bold",
"through",
"color",
"bgColor",
"clearStyle",
],
},
image: {
menuKeys: ["imageWidth100", "deleteImage"],
},
video: {
menuKeys: [],
},
},
MENU_CONF: {
// 配置字号
fontSize: {
fontSizeList: [
{ name: "H1", value: "20px" },
{ name: "H2", value: "19px" },
{ name: "H3", value: "18px" },
{ name: "正文", value: "17px" },
],
},
lineHeight: {
lineHeightList: ["1", "1.5", "1.6", "1.75", "2", "3"],
},
// 配置上传图片
uploadImage: {
//上传图片
async customUpload(file, insertFn) {
// file 即选中的文件
// 自己实现上传,并得到图片 url alt href
// 最后插入图片
console.log(file);
// 判断文件格式是否符合要求规范
if (!/\.(bmp|tiff|gif|png|jpeg|jpg)$/.test(file.name)) {
alert("图片类型必须是,bmp/tiff/gif/png/jpeg/jpg中的一种");
return false;
}
that
.getClient()
.then(function (client) {
client
.put("zsdl/image/" + new Date().getTime(), file)
.then(function (res) {
that.$message.success("上传图片成功");
// 上传图片,返回地址
console.log(res, "res-----");
console.log("上传图片sucess:" + res.url);
// 回显图片
insertFn(res.url);
})
.catch(function (err) {
// console.log('上传失败')
that.$message.error(err);
// console.log(err)
});
})
.catch(function (error) {
that.$message.error(error.message);
// console.log('失败' + error.message)
});
},
maxFileSize: 10 * 1024 * 1024, // 10M 图片大小限制
fieldName: "file", //上传类型
allowedFileTypes: [
"bmp/*",
"tiff/*",
"gif/*",
"png/*",
"jpeg/*",
"jpg",
], // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
// 自定义上传参数,传递图片时需要带一些参数过去写在这。参数会被添加到 formData 中,一起上传到服务端。
meta: {
// token: 'xxx',
// otherKey: 'yyy'
// file:''
},
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: false,
// 自定义设置请求头,比如添加token之类的
headers: {
// Accept: 'text/x-json',
// otherKey: 'xxx'
},
// 跨域是否传递 cookie ,默认为 false
withCredentials: true,
// 超时时间,默认为 10 秒
timeout: 10 * 1000,
// 上传进度的回调函数,可以用来显示进度条
onProgress(progress) {
// progress 是 0-100 的数字
console.log("progress", progress);
},
// 自定义插入图片
customInsert(res, insertFn) {
// 因为自定义插入导致onSuccess与onFailed回调函数不起作用,自己手动处理
// 先关闭等待的Message
this.Message.closeAll();
if (res.code === 200) {
this.Message.success({
message: `${res.data.originalName} 上传成功`,
});
} else {
this.Message.error({
message: `${res.data.originalName} 上传失败,请重新尝试`,
});
}
insertFn(res.data.link, res.data.name, res.data.link);
},
// 单个文件上传成功之后
onSuccess(file, res) {
console.log(`${file.name} 上传成功`, res);
},
// 单个文件上传失败
onFailed(file, res) {
console.log(`${file.name} 上传失败`, res);
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
console.log(`${file.name} 上传出错`, err, res);
},
},
// 配置上传视频
uploadVideo: {
//上传视频
async customUpload(file, insertFn) {
// file 即选中的文件
// 自己实现上传,并得到图片 url alt href
// 最后插入图片
console.log(file);
// 判断文件格式是否符合要求规范
if (
!/\.(mpeg|avi|navi|asf|wmv|mov|3gp|mp4|m4v|vob)$/.test(
file.name
)
) {
alert(
"视频类型必须是,mpeg/avi/navi/asf/wmv/mov/3gp/mp4/m4v/vob中的一种"
);
return false;
}
that
.getClient()
.then(function (client) {
client
.multipartUpload(
"zsdl/video/" + new Date().getTime(),
file,
{
progress(p) {
console.log(p, "进度-----");
that.showProgress = true;
if (p == 1) {
setTimeout(() => {
that.$message.success("上传成功");
that.showProgress = false;
}, 3000);
}
that.progress = Number((p * 100).toFixed(0));
that.progress =
that.progress > 100 ? 100 : that.progress;
},
}
)
// .put('zsdl/video/' + new Date().getTime(), file)
.then(function (res) {
that.$message.success("上传视频成功");
// 上传视频,返回地址
console.log(res, "res-----");
// that.videoAddress = res.res.requestUrls[0].split('?')[0] //这个也很重要,必须切割,要不链接显示错误
console.log(
"上传视频sucess:" +
res.res.requestUrls[0].split("?")[0]
);
// 回显视频
insertFn(res.res.requestUrls[0].split("?")[0]);
})
.catch(function (err) {
// console.log('上传失败')
that.$message.error(err);
// console.log(err)
});
})
.catch(function (error) {
that.$message.error(error.message);
// console.log('失败' + error.message)
});
},
// 单个文件的最大体积限制,默认为 10M
maxFileSize: 50 * 1024 * 1024, // 50M
fieldName: "your-custom-name", //上传类型
// 最多可上传几个文件,默认为 5
maxNumberOfFiles: 3,
allowedFileTypes: [
"mpeg/*",
"avi/*",
"navi/*",
"asf/*",
"wmv/*",
"mov/*",
"3gp/*",
"mp4/*",
"m4v/*",
"vob/*",
], // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
// 自定义上传参数,传递图片时需要带一些参数过去写在这。参数会被添加到 formData 中,一起上传到服务端。
meta: {
// token: 'xxx',
// otherKey: 'yyy'
},
// 自定义设置请求头,比如添加token之类的
headers: {
// Accept: 'text/x-json',
// otherKey: 'xxx'
},
metaWithUrl: false, // 将 meta 拼接到 url 参数中,默认 false
withCredentials: true, // 跨域是否传递 cookie ,默认为 false
//上传之前触发
onBeforeUpload(file) {
// file 选中的文件,格式如 { key: file }
this.Message({
message: "视频正在上传中,请耐心等待",
duration: 0,
customClass: "uploadTip",
iconClass: "el-icon-loading",
showClose: true,
});
return file;
// 可以 return
// 1. return file 或者 new 一个 file ,接下来将上传
// 2. return false ,不上传这个 file
},
// 自定义插入视频
customInsert(res, insertFn) {
// 因为自定义插入导致onSuccess与onFailed回调函数不起作用,自己手动处理
// 先关闭等待的Message
this.Message.closeAll();
if (res.code === 200) {
this.Message.success({
message: `${res.data.originalName} 上传成功`,
});
} else {
this.Message.error({
message: `${res.data.originalName} 上传失败,请重新尝试`,
});
}
insertFn(res.data.link, res.data.link);
},
// 上传进度的回调函数,可以用来显示进度条
onProgress(progress) {
// progress 是 0-100 的数字
console.log("progress", progress);
},
// // 单个文件上传成功之后
onSuccess(file, res) {
console.log(`${file.name} 上传成功`, res);
},
// 单个文件上传失败
onFailed(file, res) {
console.log(`${file.name} 上传失败`, res);
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
console.log(`${file.name} 上传出错`, err, res);
},
// 插入图片到富文本编辑器回显
// customInsert(res, insertFn) {
// console.log(res, '视频插入')
// res 即服务端的返回结果
// let url = res.data.url;
// let poster = res.data.poster;
// 从 res 中找到 url poster ,然后插入
//参数url是视频地址,poster是视频封面图片,后端如果不返回,可以考虑写死一个固定的封面图
// insertFn(url, poster)
// }
},
},
},
// 默认编辑栏的配置
jsonContent: [
{
type: "paragraph",
children: [{ text: "", fontSize: "17px" }],
// lineHeight ,关于默认行高的设置,可查看源码或通过官方demo
// https://www.wangeditor.com/demo/index.html
// 输入文字,设置默认字体、行高之后 在控制台输入 ,this.editor.children 会显示如下内容:
/*
*[{"type": "paragraph", "children": [ { "text": "faskdfjaslkdfj" } ], "lineHeight": "2.5" } ]
* 按对应格式设置 jsonContent 即可
* */
fontSize: "17px",
lineHeight: 1.75,
color: "#4a4a4a",
},
// {
// type: 'image',
// imageWidth100: true
// }
],
mode: "default", // or 'simple'
};
},
mounted() {},
methods: {
//获取oss阿里云图上传的client
// getClient() {
// return new Promise((resolve) => {
// getOss({})
// .then((res) => {
// // console.log(res.data)
// const OSS = require("ali-oss");
// let clinet = new OSS({
// accessKeyId: xxx,
// accessKeySecret: xxx,
// bucket: "xxx",
// region: "xxx",
// stsToken: xxx,
// });
// resolve(clinet);
// })
// .catch((error) => {
// resolve(error.message);
// });
// });
// },
// wangeditor的方法---必须要用
onCreated(editor) {
this.editor = Object.seal(editor); // 一定要用 Object.seal() ,否则会报错
},
},
computed: {},
components: { Editor, Toolbar },
activated() {
// console.log('actived')
let docClasses = document.body.classList;
docClasses.add("white-content");
},
deactivated() {
// console.log('deactivated')
let docClasses = document.body.classList;
docClasses.remove("white-content");
},
beforeDestroy() {
const editor = this.editor;
if (editor == null) return;
editor.destroy(); // 组件销毁时,及时销毁编辑器
},
});
</script>
<style src="@wangeditor/editor/dist/css/style.css"></style>
<style>
* {
margin: 0;
padding: 0;
}
#tool-container {
display: flex;
justify-content: center;
}
.el-input__inner {
width: 748px;
border: none;
font-size: 24px;
font-weight: 500;
padding: 0;
color: #4a4a4a;
}
</style>
上传地址,各自配置。有错误欢迎大佬指教!私必回