<template>
|
<div>
|
<div :id="id"></div>
|
|
<Modal
|
v-model="showHTMLModal"
|
:mask-closable="false"
|
:width="900"
|
:fullscreen="full"
|
:z-index="10001"
|
>
|
<div slot="header">
|
<div class="ivu-modal-header-inner">编辑HTML代码</div>
|
<a @click="handleFull" class="modal-fullscreen">
|
<Icon
|
v-show="!full"
|
type="ios-expand"
|
class="model-fullscreen-icon"
|
/>
|
<Icon
|
v-show="full"
|
type="ios-contract"
|
class="model-fullscreen-icon"
|
/>
|
</a>
|
<a @click="showHTMLModal = false" class="ivu-modal-close">
|
<Icon type="ios-close" class="ivu-icon-ios-close" />
|
</a>
|
</div>
|
<monaco
|
id="monaco1"
|
v-if="showHTMLModal"
|
v-model="dataEdit"
|
language="html"
|
:height="monacoHeight"
|
ref="monaco1"
|
/>
|
<div slot="footer">
|
<Button @click="editHTMLOk" type="primary">保存提交</Button>
|
</div>
|
</Modal>
|
<Modal
|
v-model="showClearModal"
|
:closable="false"
|
:mask-closable="false"
|
:width="416"
|
:footer-hide="true"
|
:z-index="10001"
|
>
|
<div class="ivu-modal-confirm">
|
<div class="ivu-modal-confirm-head">
|
<div
|
class="ivu-modal-confirm-head-icon ivu-modal-confirm-head-icon-confirm"
|
>
|
<i class="ivu-icon ivu-icon-ios-help-circle"></i>
|
</div>
|
<div class="ivu-modal-confirm-head-title">确认清空</div>
|
</div>
|
<div class="ivu-modal-confirm-body">确认要清空编辑器内容?</div>
|
<div class="ivu-modal-confirm-footer">
|
<Button type="text" @click="showClearModal = false">取消</Button>
|
<Button @click="clear" type="primary">确定</Button>
|
</div>
|
</div>
|
</Modal>
|
</div>
|
</template>
|
|
<script>
|
import { uploadFile } from "@/api/index";
|
import E from "wangeditor";
|
import xss from "xss";
|
import monaco from "@/views/my-components/xboot/monaco";
|
export default {
|
components: {
|
monaco,
|
},
|
name: "editor",
|
props: {
|
id: {
|
type: String,
|
default: "editor",
|
},
|
value: String,
|
zIndex: {
|
type: Number,
|
default: 1,
|
},
|
height: {
|
type: [Number, String],
|
default: 300,
|
},
|
placeholder: {
|
type: String,
|
default: "在这里输入内容",
|
},
|
base64: {
|
type: Boolean,
|
default: false,
|
},
|
uploadPic: {
|
type: Boolean,
|
default: true,
|
},
|
uploadImgTimeout: {
|
type: Number,
|
default: 10000,
|
},
|
uploadVideo: {
|
type: Boolean,
|
default: true,
|
},
|
uploadVideoTimeout: {
|
type: Number,
|
default: 300000,
|
},
|
expandHtml: {
|
type: Boolean,
|
default: true,
|
},
|
expandClear: {
|
type: Boolean,
|
default: true,
|
},
|
showFullScreen: {
|
type: Boolean,
|
default: true,
|
},
|
openXss: {
|
type: Boolean,
|
default: false,
|
},
|
},
|
data() {
|
return {
|
editor: null,
|
data: this.value, // 富文本数据
|
dataEdit: "", // 编辑数据
|
showHTMLModal: false, // 显示html
|
full: false, // html全屏开关
|
showClearModal: false,
|
monacoHeight: 500,
|
fullHeight: 100,
|
};
|
},
|
methods: {
|
initEditor() {
|
let that = this;
|
this.editor = new E(`#${this.id}`);
|
const {
|
$,
|
BtnMenu,
|
DropListMenu,
|
PanelMenu,
|
DropList,
|
Panel,
|
Tooltip,
|
} = E;
|
// 扩展菜单
|
class EditHTMLMenu extends BtnMenu {
|
constructor(editor) {
|
const $elem = E.$(
|
`<div class="w-e-menu" data-title="编辑源代码"><i class="ivu-icon ivu-icon-md-code-working" style="font-size: 22px;"></i></div>`
|
);
|
super($elem, editor);
|
}
|
// 菜单点击事件
|
clickHandler() {
|
that.editHTML();
|
}
|
// 菜单激活状态
|
tryChangeActive() {}
|
}
|
class ClearMenu extends BtnMenu {
|
constructor(editor) {
|
const $elem = E.$(
|
`<div class="w-e-menu" data-title="清空"><i class="ivu-icon ivu-icon-md-trash" style="font-size: 18px;"></i></div>`
|
);
|
super($elem, editor);
|
}
|
// 菜单点击事件
|
clickHandler() {
|
that.showClearModal = true;
|
}
|
// 菜单激活状态
|
tryChangeActive() {}
|
}
|
if (this.expandHtml) {
|
// 扩展注册菜单 将菜单加入到 editor.config.menus 中
|
this.editor.menus.extend("editHTMLMenu", EditHTMLMenu);
|
this.editor.config.menus = this.editor.config.menus.concat(
|
"editHTMLMenu"
|
);
|
}
|
if (this.expandClear) {
|
this.editor.menus.extend("clearMenu", ClearMenu);
|
this.editor.config.menus = this.editor.config.menus.concat("clearMenu");
|
}
|
// 全屏
|
this.editor.config.showFullScreen = this.showFullScreen;
|
// 编辑内容绑定数据
|
this.editor.config.onchange = (html) => {
|
if (this.openXss) {
|
this.data = xss(html);
|
} else {
|
this.data = html;
|
}
|
this.$emit("input", this.data);
|
this.$emit("on-change", this.data);
|
};
|
// 高度
|
this.editor.config.height = this.height;
|
// z-index
|
this.editor.config.zIndex = this.zIndex;
|
// placeholder
|
this.editor.config.placeholder = this.placeholder;
|
// 图片上传超时时间
|
this.editor.config.uploadImgTimeout = this.uploadImgTimeout;
|
// 视频上传超时时间
|
this.editor.config.uploadVideoTimeout = this.uploadVideoTimeout;
|
// 图片上传
|
if (this.uploadPic) {
|
if (this.base64) {
|
// 使用 base64 保存图片
|
this.editor.config.uploadImgShowBase64 = true;
|
} else {
|
// 配置上传图片服务器端地址
|
this.editor.config.uploadImgServer = uploadFile;
|
// xboot如要header中传入token鉴权
|
this.editor.config.uploadImgHeaders = {
|
accessToken: that.getStore("accessToken"),
|
};
|
this.editor.config.uploadFileName = "file";
|
this.editor.config.uploadImgHooks = {
|
before: function (xhr, editor, files) {
|
// 图片上传之前触发
|
},
|
success: function (xhr, editor, result) {
|
// 图片上传并返回结果,图片插入成功之后触发
|
},
|
fail: function (xhr, editor, result) {
|
// 图片上传并返回结果,但图片插入错误时触发
|
that.$Message.error("上传图片失败");
|
},
|
error: function (xhr, editor) {
|
// 图片上传出错时触发
|
that.$Message.error("上传图片出错");
|
},
|
timeout: function (xhr, editor) {
|
// 图片上传超时时触发
|
that.$Message.error("上传图片超时");
|
},
|
// 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
|
customInsert: function (insertImg, result, editor) {
|
if (result.success == true) {
|
let url = result.result;
|
insertImg(url);
|
that.$Message.success("上传图片成功");
|
} else {
|
that.$Message.error(result.message);
|
}
|
},
|
};
|
}
|
}
|
// 视频上传
|
if (this.uploadVideo) {
|
this.editor.config.uploadVideoServer = uploadFile;
|
this.editor.config.uploadVideoHeaders = {
|
accessToken: that.getStore("accessToken"),
|
};
|
this.editor.config.uploadVideoName = "file";
|
this.editor.config.uploadVideoHooks = {
|
before: function (xhr, editor, files) {
|
// 视频上传之前触发
|
},
|
success: function (xhr, editor, result) {
|
// 视频上传并返回结果,图片插入成功之后触发
|
},
|
fail: function (xhr, editor, result) {
|
// 视频上传并返回结果,但图片插入错误时触发
|
that.$Message.error("上传视频失败");
|
},
|
error: function (xhr, editor) {
|
// 图片上传出错时触发
|
that.$Message.error("上传视频出错");
|
},
|
timeout: function (xhr, editor) {
|
// 图片上传超时时触发
|
that.$Message.error("上传视频超时");
|
},
|
// 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
|
customInsert: function (insertVideo, result, editor) {
|
if (result.success == true) {
|
let url = result.result;
|
insertVideo(url);
|
that.$Message.success("上传视频成功");
|
} else {
|
that.$Message.error(result.message);
|
}
|
},
|
};
|
}
|
this.editor.config.customAlert = function (info) {
|
// info 是需要提示的内容
|
// that.$Message.info(info);
|
};
|
this.editor.create();
|
if (this.value) {
|
if (this.openXss) {
|
this.editor.txt.html(xss(this.value));
|
} else {
|
this.editor.txt.html(this.value);
|
}
|
}
|
},
|
handleFull() {
|
this.full = !this.full;
|
if (this.full) {
|
this.monacoHeight = this.fullHeight;
|
} else {
|
this.monacoHeight = 500;
|
}
|
setTimeout(() => {
|
this.$refs.monaco1.layout();
|
}, 10);
|
},
|
editHTML() {
|
this.dataEdit = this.data;
|
this.showHTMLModal = true;
|
},
|
editHTMLOk() {
|
this.editor.txt.html(this.dataEdit);
|
this.$emit("input", this.dataEdit);
|
this.$emit("on-change", this.dataEdit);
|
this.showHTMLModal = false;
|
},
|
clear() {
|
this.data = "";
|
this.editor.txt.html(this.data);
|
this.$emit("input", this.data);
|
this.$emit("on-change", this.data);
|
this.showClearModal = false;
|
},
|
setData(value) {
|
if (!this.editor) {
|
this.initEditor();
|
}
|
if (value != this.data) {
|
this.data = value;
|
this.editor.txt.html(this.data);
|
this.$emit("input", this.data);
|
this.$emit("on-change", this.data);
|
}
|
},
|
},
|
beforeDestroy() {
|
// 调用销毁 API 对当前编辑器实例进行销毁
|
if (this.editor != null) {
|
this.editor.destroy();
|
}
|
},
|
watch: {
|
value(val) {
|
this.setData(val);
|
},
|
},
|
mounted() {
|
this.initEditor();
|
this.fullHeight = Number(document.body.clientHeight - 144);
|
},
|
};
|
</script>
|
|
<style lang="less">
|
.w-e-text-container .placeholder {
|
z-index: -1;
|
}
|
|
.w-e-toolbar p,
|
.w-e-text-container p,
|
.w-e-menu-panel p {
|
font-size: 1em !important;
|
}
|
</style>
|