调整自动引入
This commit is contained in:
272
components/AiEditor/AiEditor.vue
Normal file
272
components/AiEditor/AiEditor.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<div class="AiEditor" :class="{noBorder}" @click="editing=true">
|
||||
<div ref="AiEditorInstance" style="-webkit-user-select:text;"/>
|
||||
<div class="bottomPanel" :class="{fixed:isFullScreen}">
|
||||
<slot v-if="$slots.bottom" name="bottom"/>
|
||||
<div v-else-if="maxlength" class="fontCount">{{ [editorText.length, maxlength].join(" / ") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 原组件wangeditor封装
|
||||
* 修改者:Kubbo
|
||||
*/
|
||||
import E from 'wangeditor'
|
||||
|
||||
export default {
|
||||
name: "AiEditor",
|
||||
inject: {
|
||||
elFormItem: {default: ""},
|
||||
elForm: {default: ''},
|
||||
},
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "change"
|
||||
},
|
||||
props: {
|
||||
value: {type: String, required: true, default: ""},
|
||||
placeholder: {default: '请输入正文'},
|
||||
conf: Object,
|
||||
instance: {type: Function},
|
||||
maxlength: Number,
|
||||
valid: {type: Boolean, default: true},
|
||||
noBorder: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ins: null,
|
||||
isFullScreen: false,
|
||||
isPasteStyle: true,//粘贴是否携带格式
|
||||
origin: "",
|
||||
editorText: "",
|
||||
editing: false//自动赋值问题,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
validateState() {
|
||||
return ['', 'success'].includes(this.elFormItem?.validateState)
|
||||
},
|
||||
extra() {
|
||||
return {
|
||||
fullscreen(editor) {
|
||||
if (editor?.ins.$toolbarElem) {
|
||||
let btn = E.$(`<div class="w-e-menu" data-title="全屏"><i class="w-e-icon-fullscreen"/></div>`)
|
||||
btn.on("click", () => {
|
||||
editor.isFullScreen = !editor.isFullScreen
|
||||
editor.isFullScreen ? editor.ins.fullScreen() : editor.ins.unFullScreen()
|
||||
})
|
||||
editor.ins.$toolbarElem.append(btn)
|
||||
}
|
||||
},
|
||||
preview(editor) {
|
||||
if (editor?.ins.$toolbarElem) {
|
||||
let btn = E.$(`<div class="w-e-menu" data-title="预览"><i class="el-icon-monitor"/></div>`)
|
||||
btn.on("click", () => {
|
||||
editor.$refs.preview.dialog = true
|
||||
})
|
||||
editor.ins.$toolbarElem.append(btn)
|
||||
}
|
||||
},
|
||||
pasteWithStyle(editor) {
|
||||
if (editor?.ins.$toolbarElem) {
|
||||
let btn = E.$(`<div class="w-e-menu w-e-active" data-title="粘贴格式"><i class="iconfont iconCopy"/></div>`)
|
||||
editor.origin = JSON.parse(JSON.stringify(editor.value))
|
||||
btn.on("click", () => {
|
||||
editor.isPasteStyle = !editor.isPasteStyle
|
||||
if (editor.isPasteStyle) {
|
||||
editor.setContent(editor.origin)
|
||||
btn.addClass("w-e-active")
|
||||
} else {
|
||||
editor.origin = JSON.parse(JSON.stringify(editor.value))
|
||||
editor.setContent(editor.editorText)
|
||||
btn.removeClass("w-e-active")
|
||||
}
|
||||
})
|
||||
editor.ins.$toolbarElem.append(btn)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
customConfig() {
|
||||
let init = ["fullscreen", "preview", "pasteWithStyle"]
|
||||
if (this.maxlength > 0) init = init.slice(0, 2)
|
||||
return {
|
||||
debug: true,
|
||||
pasteFilterStyle: !this.isPasteStyle,
|
||||
showFullScreen: false,
|
||||
zIndexFullScreen: 1000,
|
||||
zIndex: 98,
|
||||
focus: false,
|
||||
menus: [],
|
||||
init,
|
||||
customUploadImg: (files, insert) => {
|
||||
this.uploadFile(files, insert)
|
||||
},
|
||||
customUploadVideo: (files, insert) => {
|
||||
this.uploadFile(files, insert)
|
||||
},
|
||||
onfocus: () => {
|
||||
this.editing = true
|
||||
},
|
||||
onblur: () => {
|
||||
this.editing = false
|
||||
},
|
||||
onchange: html => {
|
||||
if (this.maxlength > 0) {
|
||||
this.editorText = html?.replace(/<\/?.+?\/?>/g, '')
|
||||
if (this.editorText.length > this.maxlength) {
|
||||
this.ins.history.revoke()
|
||||
// this.editorText = this.editorText.substring(0, this.maxlength)
|
||||
// this.setContent(this.editorText)
|
||||
}
|
||||
} else {
|
||||
this.editorText = html
|
||||
}
|
||||
this.$emit("change", this.editorText)
|
||||
},
|
||||
pasteTextHandle: str => this.isPasteStyle ? str : str?.replace(/<\/?.+?\/?>/g, ''),
|
||||
...this.conf
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initEditor() {
|
||||
let {placeholder, customConfig} = this
|
||||
this.ins = new E(this.$refs.AiEditorInstance)
|
||||
this.ins.config = {...this.ins.config, ...customConfig, placeholder}
|
||||
this.ins.create()
|
||||
customConfig.init.map(e => this.extra?.[e]?.(this))
|
||||
this.value && this.setContent(this.value)
|
||||
|
||||
},
|
||||
setContent(data) {
|
||||
if (this.ins) {
|
||||
this.ins.txt.html(data)
|
||||
this.editing = false
|
||||
}
|
||||
},
|
||||
uploadFile(files, insert) {
|
||||
files && files.map(e => {
|
||||
let formData = new FormData()
|
||||
formData.append('file', e)
|
||||
this?.instance?.post(`/admin/file/add`, formData).then(res => {
|
||||
if (res && res.data) {
|
||||
res.data.map(m => {
|
||||
let item = m.split(";")
|
||||
insert(item[0])
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 表单验证
|
||||
* @param componentName
|
||||
* @param eventName
|
||||
* @param params
|
||||
*/
|
||||
dispatch(componentName, eventName, params) {
|
||||
let parent = this.$parent || this.$root;
|
||||
let name = parent.$options.componentName;
|
||||
|
||||
while (parent && (!name || name !== componentName)) {
|
||||
parent = parent.$parent;
|
||||
|
||||
if (parent) {
|
||||
name = parent.$options.componentName;
|
||||
}
|
||||
}
|
||||
if (parent) {
|
||||
parent.$emit.apply(parent, [eventName].concat(params));
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(v) {
|
||||
this.dispatch('ElFormItem', 'el.form.change', [v]);
|
||||
if (v && !this.editing) {
|
||||
this.setContent(v)
|
||||
}
|
||||
},
|
||||
placeholder(v) {
|
||||
if (this.ins) {
|
||||
document.querySelector('.AiEditor .placeholder').innerHTML = v
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
!this.ins && this.initEditor()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep.AiEditor {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
&.noBorder {
|
||||
.w-e-toolbar, .w-e-text-container {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 菜单区 */
|
||||
.w-e-toolbar {
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
-webkit-box-lines: multiple;
|
||||
|
||||
.w-e-menu {
|
||||
line-height: 24px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
z-index: 10002 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 编辑区 */
|
||||
.w-e-text {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
word-break: break-all;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fontCount {
|
||||
pointer-events: none;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.bottomPanel {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 24px;
|
||||
z-index: 99;
|
||||
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
.w-e-text-container, .w-e-toolbar {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.fontCount {
|
||||
color: red
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user