346 lines
12 KiB
Vue
346 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<div ref="editorRef"></div>
|
|
<input type="file" style="display: none" />
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
const { $, BtnMenu, Panel, Tooltip } = wangEditor
|
|
|
|
class PDFMenu extends BtnMenu {
|
|
constructor(editor) {
|
|
const $elem = wangEditor.$(
|
|
`<div class="w-e-menu" data-title="上传pdf">
|
|
<i class="fa fa-file-pdf-o" aria-hidden="true" /></i>
|
|
</div>`
|
|
)
|
|
super($elem, editor)
|
|
}
|
|
|
|
clickHandler() {
|
|
const _this = this
|
|
const inputFileElement = document.querySelector('#'+this.editor.textElemId).parentElement.parentElement.nextElementSibling
|
|
inputFileElement.click()
|
|
inputFileElement.addEventListener('change', function () {
|
|
const file = this.files[0];
|
|
const fileName = file.name
|
|
|
|
if (!fileName.toLowerCase().endsWith('.pdf')) {
|
|
alert('请上传pdf格式的文件')
|
|
_this.clearInputFile()
|
|
return
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append('file', file, file.name)
|
|
/*axios.post("/platform/sys/file/uploadDynamicReturnUrl", formData, {
|
|
headers: {
|
|
"Content-Type": "multipart/form-data"
|
|
}
|
|
})*/
|
|
axios.post("/platform/sys/file/uploadDynamicReturnUrl", formData, {}).then(response => {
|
|
console.log(response.data)
|
|
if (response.data.code === 0) {
|
|
const html = `<a target="_blank" href="${window.location.origin}${response.data.data}">
|
|
${file.name}
|
|
</a>`
|
|
_this.editor.txt.html(html)
|
|
} else {
|
|
alert(response.data.msg + ',上传pdf失败,请联系管理员!')
|
|
_this.clearInputFile()
|
|
}
|
|
}).catch(error => {
|
|
console.log(error)
|
|
_this.clearInputFile()
|
|
alert('上传pdf失败,请联系管理员!')
|
|
});
|
|
});
|
|
}
|
|
|
|
clearInputFile() {
|
|
const obj = document.getElementById('fileInput');
|
|
if(obj) {
|
|
obj.outerHTML = obj.outerHTML
|
|
}
|
|
}
|
|
|
|
tryChangeActive() {
|
|
this.active()
|
|
}
|
|
|
|
}
|
|
|
|
//注册上传word按钮
|
|
class DocMenu extends BtnMenu {
|
|
constructor(editor) {
|
|
const $elem = wangEditor.$(
|
|
`<div class="w-e-menu" data-title="上传文档">
|
|
<i class="fa fa-file-word-o" aria-hidden="true" /></i>
|
|
</div>`
|
|
)
|
|
super($elem, editor)
|
|
}
|
|
|
|
clickHandler() {
|
|
const fileInputId = this.editor.textElemId + "input-file"
|
|
const existingInput = document.getElementById(fileInputId)
|
|
if (existingInput) {
|
|
existingInput.remove()
|
|
}
|
|
|
|
const input = document.createElement("input")
|
|
input.type = "file"
|
|
input.accept = "application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
input.id = fileInputId
|
|
input.style.display = "none"
|
|
input.addEventListener("change", (event) => {
|
|
const files = event.target.files
|
|
if (!files || files.length === 0) {
|
|
return
|
|
}
|
|
|
|
let loading = null
|
|
if (window.ELEMENT) {
|
|
loading = ELEMENT.Loading.service({
|
|
lock: true,
|
|
text: "文件读取中",
|
|
spinner: "el-icon-loading",
|
|
background: "rgba(0, 0, 0, 0.7)"
|
|
})
|
|
} else if (window.vant) {
|
|
loading = vant.Toast.loading({
|
|
message: "文件读取中",
|
|
forbidClick: true,
|
|
overlay: true,
|
|
duration: 0
|
|
})
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append("file", files[0])
|
|
axios
|
|
.post("/platform/sys/file/convertHtml", formData, {
|
|
headers: { "Content-Type": "multipart/form-data", "x-requested-with": "XMLHttpRequest" }
|
|
})
|
|
.then((response) => {
|
|
loading.close()
|
|
if (response.data.code === 0) {
|
|
this.editor.txt.html(response.data.data)
|
|
} else {
|
|
if (window.ELEMENT) {
|
|
this.$message.error(response.data.msg)
|
|
} else if (window.vant) {
|
|
vant.Toast(response.data.msg)
|
|
} else {
|
|
alert(response.data.msg)
|
|
}
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.log(error)
|
|
alert("上传word失败,请联系管理员!")
|
|
})
|
|
.finally(() => {
|
|
input.remove()
|
|
})
|
|
})
|
|
input.click()
|
|
document.body.appendChild(input)
|
|
}
|
|
|
|
tryChangeActive() {}
|
|
}
|
|
|
|
wangEditor.registerMenu("docMenu", DocMenu)
|
|
wangEditor.registerMenu('pdfMenuKey', PDFMenu)
|
|
module.exports = {
|
|
name: "textEditor",
|
|
props: {
|
|
content: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 定义值属性,用于接收父组件传递的值
|
|
value: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
height: {
|
|
type: Number,
|
|
default: 300
|
|
},
|
|
// 富文本编辑器占位提示,透传给 wangEditor 初始化配置。
|
|
placeholder: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 编辑区顶部红色提示,只做展示,不写入编辑器正文内容。
|
|
topTip: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
menus: {
|
|
type: Array,
|
|
default: () => {
|
|
return [
|
|
"head", //标题
|
|
"bold", //加粗
|
|
"fontSize", //字号
|
|
"fontName", //字体
|
|
"italic", //斜体
|
|
"underline", //下划线
|
|
"strikeThrough", //删除线
|
|
"indent", //缩进
|
|
"lineHeight", //行高
|
|
"foreColor", //文字颜色
|
|
"backColor", //背景颜色
|
|
//'link',//链接
|
|
"list", //序列
|
|
"todo", //待办
|
|
"justify", //对齐
|
|
"quote", //引用
|
|
// 'emoticon',//表情
|
|
"image",
|
|
//'video',
|
|
"table",
|
|
//'code',
|
|
"splitLine", //分割线
|
|
"undo",
|
|
"redo"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
editor: null,
|
|
cursorPos: 0,
|
|
topTipElement: null
|
|
}
|
|
},
|
|
mounted() {
|
|
this.initEditor()
|
|
},
|
|
|
|
beforeDestroy() {
|
|
this.removeTopTip()
|
|
},
|
|
|
|
watch: {
|
|
value: {
|
|
handler: function (newValue) {
|
|
this.$nextTick(() => {
|
|
const content = newValue || ""
|
|
if (this.editor.txt.html() !== content) {
|
|
const selection = this.editor.selection
|
|
const range = selection.getRange() // 保存当前选区
|
|
this.editor.txt.html(content)
|
|
// 如果之前有选区,则恢复光标位置
|
|
if (range) {
|
|
selection.restoreSelection()
|
|
}
|
|
}
|
|
})
|
|
},
|
|
immediate: true
|
|
},
|
|
topTip: {
|
|
handler: function () {
|
|
this.$nextTick(() => {
|
|
this.renderTopTip()
|
|
})
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
initEditor() {
|
|
let _this = this
|
|
this.editor = new wangEditor(this.$refs.editorRef)
|
|
this.editor.config.focus = false
|
|
//内容change回调
|
|
this.editor.config.onchange = (newHtml) => {
|
|
this.cursorPos = this.editor.selection.getCursorPos()
|
|
const range = this.editor.selection.getRange()
|
|
range.setStart(range.startContainer, Math.max(this.cursorPos, range.endOffset))
|
|
this.$emit("input", newHtml)
|
|
}
|
|
this.editor.config.linkImgCheck = function (imgSrc, alt, href) {
|
|
// 1. 返回 true ,说明检查通过
|
|
return true
|
|
}
|
|
|
|
//配置图片上传
|
|
this.editor.config.customUploadImg = function (resultFiles, insertImgFn) {
|
|
// resultFiles 是 input 中选中的文件列表
|
|
// insertImgFn 是获取图片 url 后,插入到编辑器的方法
|
|
Promise.all(_this.uploadFiles(resultFiles)).then((res) => {
|
|
res.forEach((v) => {
|
|
if (v.data && v.data.code === 0) {
|
|
insertImgFn(v.data.data)
|
|
} else {
|
|
this.$message.error("图片上传失败")
|
|
}
|
|
})
|
|
})
|
|
}
|
|
this.editor.config.pasteFilterStyle = false
|
|
this.editor.config.height = this.height
|
|
this.editor.config.menus = this.menus
|
|
// wangEditor 的 placeholder 必须在 create 前设置,父页面传入才会生效。
|
|
this.editor.config.placeholder = this.placeholder
|
|
this.editor.create()
|
|
this.renderTopTip()
|
|
},
|
|
renderTopTip() {
|
|
if (!this.editor || !this.$refs.editorRef) {
|
|
return
|
|
}
|
|
const textContainer = this.$refs.editorRef.querySelector(".w-e-text-container")
|
|
if (!textContainer) {
|
|
return
|
|
}
|
|
if (!this.topTip) {
|
|
this.removeTopTip()
|
|
return
|
|
}
|
|
if (!this.topTipElement) {
|
|
this.topTipElement = document.createElement("div")
|
|
this.topTipElement.setAttribute("contenteditable", "false")
|
|
this.topTipElement.style.color = "#f56c6c"
|
|
this.topTipElement.style.fontSize = "12px"
|
|
this.topTipElement.style.lineHeight = "20px"
|
|
this.topTipElement.style.padding = "6px 10px 0"
|
|
this.topTipElement.style.userSelect = "none"
|
|
this.topTipElement.style.pointerEvents = "none"
|
|
}
|
|
// 提示节点放在可编辑正文区域外层,保证用户无法删除且保存内容不携带该提示。
|
|
this.topTipElement.innerText = this.topTip
|
|
if (textContainer.firstChild !== this.topTipElement) {
|
|
textContainer.insertBefore(this.topTipElement, textContainer.firstChild)
|
|
}
|
|
},
|
|
removeTopTip() {
|
|
if (this.topTipElement && this.topTipElement.parentNode) {
|
|
this.topTipElement.parentNode.removeChild(this.topTipElement)
|
|
}
|
|
},
|
|
uploadFiles(resultFiles) {
|
|
return resultFiles.map(async (file) => {
|
|
const formData = new FormData()
|
|
formData.append("file", file)
|
|
return axios.post("/platform/sys/file/uploadDynamicReturnUrl", formData, {
|
|
headers: {
|
|
"Content-Type": "multipart/form-data"
|
|
}
|
|
})
|
|
})
|
|
}
|
|
},
|
|
created() {}
|
|
}
|
|
</script>
|
|
|
|
<style scoped></style>
|