Commit c7afe541 authored by shangtx's avatar shangtx

chore: wangeditor v5 版本

parent b54bf3c1
<!--
基于wangEditor的vue的富文本编辑器,请通过组件的ref直接设置和获取富文本内容
由于wangEditor本身的特性(也许是bug),通过双向绑定和回调函数的方式直接修改外部的内容变量会引发末尾回车键失效的问题
文档地址 https://www.wangeditor.com/doc/
-->
<template> <template>
<a-spin :spinning="loading"> <!-- <a-spin :spinning="loading"> -->
<div class="wangeditor-atkj-wrapper" :id="id"></div> <div class="wangeditor-atkj-wrapper">
</a-spin> <div id="wangeditor-toolbar-container"></div>
<div
id="wangeditor-editor-container"
:style="{ height: `${height}px` }"
></div>
</div>
<!-- </a-spin> -->
</template> </template>
<script> <script>
// 引入 wangEditor // 引入 wangEditor
import wangEditor from 'wangeditor' import { createEditor, createToolbar } from '@wangeditor/editor'
import '@wangeditor/editor/dist/css/style.css'
export default { export default {
name: 'RichTextEditor', name: 'RichTextEditor',
props: { props: {
id: {
type: String,
default: () => 'wangeditor-atkj-wrapper-default-id'
},
height: { height: {
type: Number, type: Number,
default: () => 300 default: () => 300
...@@ -37,6 +35,7 @@ export default { ...@@ -37,6 +35,7 @@ export default {
data() { data() {
return { return {
editor: null, editor: null,
toolbar: null,
loading: false loading: false
} }
}, },
...@@ -45,173 +44,82 @@ export default { ...@@ -45,173 +44,82 @@ export default {
this.editor = null this.editor = null
}, },
mounted() { mounted() {
if (this.editor != null) { const editorConfig = {
return MENU_CONF: {}
}
this.init()
},
activated() {
if (this.editor != null) {
return
} }
this.init() // const token = sessionStorage.getItem('Access-Token')
// 上传图片设置
// editorConfig.MENU_CONF['uploadImage'] = {
// server: `${process.env.VUE_APP_URL}upload?fileType=IMG`,
// timeout: 5000,
// fieldName: 'files',
// headers: { Authorization: token },
// customInsert: function (result, insertImgFn) {
// insertImgFn(result.data.urls[0])
// }
// }
// editorConfig.MENU_CONF['uploadVideo'] = {
// server: `${process.env.VUE_APP_URL}upload?fileType=VIDEO`,
// timeout: 5000,
// fieldName: 'files',
// headers: { Authorization: token },
// onBeforeUpload() {
// this.loading = true
// },
// onFailed() {
// this.loading = false
// },
// onError() {
// this.loading = false
// },
// customInsert(res, insertFn) {
// if (res.code == 200) {
// insertFn(res.data.urls[0])
// } else {
// this.$message.error('上传失败')
// this.loading = false
// }
// }
// }
const editor = createEditor({
selector: `#wangeditor-editor-container`,
config: editorConfig,
mode: 'default'
})
createToolbar({
editor: editor,
selector: `#wangeditor-toolbar-container`,
config: {},
mode: 'default'
})
this.editor = editor
}, },
methods: { methods: {
// 设置内容 // 设置内容
setContent(v) { setContent(v) {
this.editor.txt.html(v) this.editor.setHtml(v)
}, },
// 获取内容 // 获取内容
getContent() { getContent() {
return this.editor.txt.html() return this.editor.getHtml()
}, },
// 清空内容 // 清空内容
clear() { clear() {
this.editor.txt.clear() this.editor.clear()
},
init() {
const _this = this
const editor = new wangEditor(`.wangeditor-atkj-wrapper#${this.id}`)
// 配置 onchange 回调函数,将数据同步到 vue 中
editor.config.showLinkImg = false //关闭网络路径图片方式
editor.config.uploadImgServer = `${process.env.VUE_APP_URL}upload?fileType=IMG`
editor.config.uploadFileName = 'files' // formdata中的name属性
var token = ''
if (this.isSessionStorage) {
token = sessionStorage.getItem('Access-Token')
} else {
token = localStorage.getItem('Access-Token')
}
editor.config.uploadImgHeaders = {
// 设置请求头
Authorization: token // 设置请求头
}
// 设置高度
editor.config.height = this.height
editor.config.uploadVideoServer = `${process.env.VUE_APP_URL}upload?fileType=VIDEO`
editor.config.uploadVideoName = 'files'
editor.config.uploadVideoHeaders = {
Authorization: localStorage.getItem('token') // 设置请求头
}
editor.config.uploadVideoMaxSize = 1024 * this.videoSizeLimit
editor.config.zIndex = 0
editor.config.uploadImgHooks = {
// 图片上传并返回结果,但图片插入错误时触发
fail: function (xhr, editor, result) {
console.error('richTextError', result)
},
// eslint-disable-next-line no-unused-vars
success: function (xhr, editor, result) {
// 图片上传并返回结果,图片插入成功之后触发
},
/**
* insertImgFn 可把图片插入到编辑器,传入图片 src ,执行函数即可
* result 即服务端返回的接口
*/
customInsert: function (insertImgFn, result) {
insertImgFn(result.data.urls[0])
}
}
editor.config.uploadVideoHooks = {
// 视频上传并返回了结果,想要自己把视频插入到编辑器中
// 例如服务器端返回的不是 { errno: 0, data: { url : '.....'} } 这种格式,可使用 customInsert
customInsert: function (insertVideoFn, result) {
// result 即服务端返回的接口
// insertVideoFn 可把视频插入到编辑器,传入视频 src ,执行函数即可
if (result.code == 200) {
insertVideoFn(result.data.urls[0])
} else {
_this.$message.error('上传失败')
_this.loading = false
}
},
before() {
_this.loading = true
},
fail() {
_this.loading = false
},
// 上传视频出错,一般为 http 请求的错误
error() {
_this.loading = false
},
// 上传视频超时
timeout() {
_this.loading = false
}
}
// 自定义插入视频, 主要用来处理loading状态
editor.config.customInsertVideo = function (url) {
const videoElement = document.createElement('video')
videoElement.setAttribute('controls', 'controls')
videoElement.setAttribute('style', 'max-width:100%')
videoElement.setAttribute('src', url)
// 4、loadeddata:视频下载监听。当当前帧的数据已加载,但没有足够的数据来播放指定音频/视频的下一帧时触发
// eslint-disable-next-line no-unused-vars
videoElement.addEventListener('loadeddata', function (e) {
_this.loading = false
})
/**
* 此处参考
* https://github.com/wangeditor-team/wangEditor/blob/master/src/editor/command.ts insertElem
*/
const range = editor.selection.getRange()
if (range == null) return
if (range.insertNode) {
range.insertNode(videoElement)
}
}
// 内容change
// eslint-disable-next-line no-unused-vars
editor.config.onchange = (_newHtml) => {}
// 创建编辑器
editor.create()
this.editor = editor
} }
}, }
// beforeDestroy() {
// 调用销毁 API 对当前编辑器实例进行销毁
// }
} }
</script> </script>
<style lang="less"> <style lang="less">
.wangeditor-atkj-wrapper { .wangeditor-atkj-wrapper {
.home { border: 1px solid rgb(243, 243, 243);
width: 1200px; z-index: 100; /* 按需定义 */
margin: auto; .wangeditor-toolbar-container {
position: relative; border-bottom: 1px solid #ccc;
}
.home .btn {
position: absolute;
right: 0;
top: 0;
padding: 5px 10px;
cursor: pointer;
}
.home h3 {
margin: 30px 0 15px;
}
.w-e-text-container {
border: 1px solid #c9d8db;
border-top: none;
z-index: 25;
}
.w-e-toolbar {
z-index: 28 !important;
} }
} }
</style> </style>
<template> <template>
<table-template :soul="this"> <a-spin :spinning="loading">
<template #action="{ record }"> <div class="wangeditor-atkj-wrapper">
<a-space> <div id="wangeditor-toolbar-container"></div>
<template v-if="record.type == 1"> <div id="wangeditor-editor-container"></div>
<a @click="add(record)">添加服务项</a> </div>
<a @click="editCategory(record)">修改信息</a> </a-spin>
</template>
<template v-if="record.type == 2">
<a @click="edit(record)">修改信息</a>
</template>
</a-space>
</template>
<template #enable="{ record, index }">
<a-switch
v-if="hasPerm('SYS0210101.ENABLE') && record.type == 2"
:checked="record.enabled"
checked-children="启用"
un-checked-children="禁用"
:loading="loadingKeys.includes(record.id)"
@click="enableChange(record, index)"
/>
</template>
<template #showInHome="{ record, index }">
<a-switch
v-if="hasPerm('SYS0210101.ENABLE') && record.type == 2"
:checked="record.showInHome"
checked-children="展示"
un-checked-children="隐藏"
:loading="showInHomeLoadingKeys.includes(record.id)"
@click="showInHomeChange(record, index)"
/>
</template>
<template #free>
<SubClassEdit ref="SubClassEdit" :success="reset" />
<CategoryEdit ref="CategoryEdit" :success="reset" />
</template>
</table-template>
</template> </template>
<script> <script>
import { TableTemplate, TableScript, SearchType } from '@/components/table' import { createEditor, createToolbar } from '@wangeditor/editor'
import { getCategoryList, saveOrUpdateSubClass } from '@/api/serv' import '@wangeditor/editor/dist/css/style.css'
import SubClassEdit from './SubClassEdit.vue'
import CategoryEdit from './CategoryEdit.vue'
import dayjs from 'dayjs'
const columns = [
{
title: '名称',
dataIndex: 'serviceName'
},
{
title: '排序号',
width: 130,
dataIndex: 'sequence'
},
{
title: '是否启用',
scopedSlots: { customRender: 'enable' },
enum: 'SYS0002',
dataIndex: 'enabled',
filter: { type: SearchType.ENUM }
},
{
title: '首页展示',
scopedSlots: { customRender: 'showInHome' },
enum: 'SYS0002',
dataIndex: 'showInHome',
filter: { type: SearchType.ENUM }
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 200,
sorter: true,
customRender: (text) => dayjs(text).format('YYYY-MM-DD HH:mm:ss')
},
{ title: '操作', scopedSlots: { customRender: 'action' }, width: 190 }
]
export default { export default {
name: 'ServiceList', name: 'ServiceList',
mixins: [TableScript, {}],
components: { TableTemplate, SubClassEdit, CategoryEdit },
data() { data() {
return { return {
columns, loading: false
useYScroll: true,
title: '服务管理',
pagination: false,
codes: [['SYS0002'], []],
loadingKeys: [],
showInHomeLoadingKeys: [],
rowKey: 'key'
} }
}, },
methods: { methods: {},
queryData: getCategoryList, mounted() {
loadSuccess() { const editorConfig = {
this.expandedRowKeys = this.dataSource.map((d) => d.id + '') MENU_CONF: {}
}, }
enableChange(record, index) { const token = sessionStorage.getItem('Access-Token')
if (this.loadingKeys.includes(record.id)) { // 上传图片设置
return editorConfig.MENU_CONF['uploadImage'] = {
server: `${process.env.VUE_APP_URL}upload?fileType=IMG`,
timeout: 5000,
fieldName: 'files',
headers: { Authorization: token },
customInsert: function (result, insertImgFn) {
insertImgFn(result.data.urls[0])
} }
this.loadingKeys.push(record.id) }
saveOrUpdateSubClass({ id: record.id, enabled: !record.enabled })
.then(({ code }) => { editorConfig.MENU_CONF['uploadVideo'] = {
if (code == 200) { server: `${process.env.VUE_APP_URL}upload?fileType=VIDEO`,
this.$message.success('保存成功') timeout: 5000,
const pIndex = this.dataSource.findIndex( fieldName: 'files',
(d) => d.id == record.categoryId headers: { Authorization: token },
) onBeforeUpload() {
this.dataSource[pIndex].children[index].enabled = !record.enabled // _this.loading = true
} },
}) onFailed() {
.finally(() => { // _this.loading = false
this.loadingKeys.splice(this.loadingKeys.indexOf(record.id), 1) },
}) onError() {
}, // _this.loading = false
showInHomeChange(record, index) { },
if (this.loadingKeys.includes(record.id)) { customInsert(res, insertFn) {
return if (res.code == 200) {
insertFn(res.data.urls[0])
} else {
this.$message.error('上传失败')
// _this.loading = false
}
} }
this.showInHomeLoadingKeys.push(record.id)
saveOrUpdateSubClass({ id: record.id, showInHome: !record.showInHome })
.then(({ code }) => {
if (code == 200) {
this.$message.success('保存成功')
const pIndex = this.dataSource.findIndex(
(d) => d.id == record.categoryId
)
this.dataSource[pIndex].children[index].showInHome =
!record.showInHome
}
})
.finally(() => {
this.showInHomeLoadingKeys.splice(
this.showInHomeLoadingKeys.indexOf(record.id),
1
)
})
},
edit(record) {
const categoryName = this.dataSource.filter(
(d) => d.id == record.categoryId
)[0].serviceName
this.$refs.SubClassEdit.show({
id: record.id,
categoryId: record.categoryId,
categoryName
})
},
add(record) {
this.$refs.SubClassEdit.show({
id: null,
categoryId: record.id,
categoryName: record.serviceName
})
},
editCategory(record) {
this.$refs.CategoryEdit.show(record.id)
} }
const editor = createEditor({
selector: `#wangeditor-editor-container`,
config: editorConfig,
mode: 'default'
})
createToolbar({
editor: editor,
selector: `#wangeditor-toolbar-container`,
config: {},
mode: 'default'
})
}
}
</script>
<style lang="less">
.wangeditor-atkj-wrapper {
border: 1px solid rgb(243, 243, 243);
z-index: 100; /* 按需定义 */
#wangeditor-toolbar-container {
border-bottom: 1px solid rgb(235, 235, 235);
}
#wangeditor-editor-container {
height: 800px;
} }
} }
</script> </style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment