|
|
@@ -1,371 +1,381 @@ |
|
|
|
<template> |
|
|
|
<div class="prefixCls" :style="{ width: containerWidth, height: containerHeigth }" v-loading="isLoading"> |
|
|
|
<textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }" class="tinymce-textarea"></textarea> |
|
|
|
<div |
|
|
|
class="prefixCls" |
|
|
|
:style="{ width: containerWidth, height: containerHeigth }" |
|
|
|
v-loading="isLoading" |
|
|
|
> |
|
|
|
<textarea |
|
|
|
:id="tinymceId" |
|
|
|
ref="elRef" |
|
|
|
:style="{ visibility: 'hidden' }" |
|
|
|
class="tinymce-textarea" |
|
|
|
></textarea> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import { |
|
|
|
computed, |
|
|
|
nextTick, |
|
|
|
ref, |
|
|
|
unref, |
|
|
|
watch, |
|
|
|
onDeactivated, |
|
|
|
onBeforeUnmount, |
|
|
|
defineProps, |
|
|
|
defineEmits, |
|
|
|
getCurrentInstance, |
|
|
|
onMounted, |
|
|
|
} from 'vue' |
|
|
|
import { toolbar, plugins } from './tinymce' |
|
|
|
import { bindHandlers } from './helper' |
|
|
|
import { isNumber, onMountedOrActivated, buildShortUUID } from './utils' |
|
|
|
import { |
|
|
|
computed, |
|
|
|
nextTick, |
|
|
|
ref, |
|
|
|
unref, |
|
|
|
watch, |
|
|
|
onDeactivated, |
|
|
|
onBeforeUnmount, |
|
|
|
defineProps, |
|
|
|
defineEmits, |
|
|
|
getCurrentInstance, |
|
|
|
onMounted, |
|
|
|
} from 'vue' |
|
|
|
import { toolbar, plugins } from './tinymce' |
|
|
|
import { bindHandlers } from './helper' |
|
|
|
import { isNumber, onMountedOrActivated, buildShortUUID } from './utils' |
|
|
|
|
|
|
|
const props = defineProps({ |
|
|
|
options: { |
|
|
|
type: Object, |
|
|
|
default: () => { }, |
|
|
|
}, |
|
|
|
value: { |
|
|
|
type: String, |
|
|
|
}, |
|
|
|
|
|
|
|
toolbar: { |
|
|
|
type: Array, |
|
|
|
default: toolbar, |
|
|
|
}, |
|
|
|
plugins: { |
|
|
|
type: [Array, String], |
|
|
|
default: plugins, |
|
|
|
}, |
|
|
|
modelValue: { |
|
|
|
type: String, |
|
|
|
}, |
|
|
|
height: { |
|
|
|
type: [Number, String], |
|
|
|
required: false, |
|
|
|
default: 400, |
|
|
|
}, |
|
|
|
width: { |
|
|
|
type: [Number, String], |
|
|
|
required: false, |
|
|
|
default: 'auto', |
|
|
|
}, |
|
|
|
showImageUpload: { |
|
|
|
type: Boolean, |
|
|
|
default: true, |
|
|
|
}, |
|
|
|
fileBaseUrl: { |
|
|
|
type: String, |
|
|
|
default: import.meta.env.VITE_APP_UPLOAD_URL + 'sett-minio/', |
|
|
|
}, |
|
|
|
minioBucket: { |
|
|
|
type: String, |
|
|
|
default: 'sett-minio', |
|
|
|
}, |
|
|
|
}) |
|
|
|
const emits = defineEmits([ |
|
|
|
'change', |
|
|
|
'update:modelValue', |
|
|
|
'inited', |
|
|
|
'init-error', |
|
|
|
'uploadFile', |
|
|
|
]) |
|
|
|
const { attrs } = getCurrentInstance() |
|
|
|
const tinymceId = ref(buildShortUUID('tiny-vue')) |
|
|
|
const props = defineProps({ |
|
|
|
options: { |
|
|
|
type: Object, |
|
|
|
default: () => {}, |
|
|
|
}, |
|
|
|
value: { |
|
|
|
type: String, |
|
|
|
}, |
|
|
|
|
|
|
|
const containerWidth = computed(() => { |
|
|
|
const width = props.width |
|
|
|
if (isNumber(width)) { |
|
|
|
return `${width}px` |
|
|
|
} |
|
|
|
return width |
|
|
|
}) |
|
|
|
const containerHeigth = computed(() => { |
|
|
|
const height = props.height |
|
|
|
if (isNumber(height)) { |
|
|
|
return `${height}px` |
|
|
|
} |
|
|
|
return height |
|
|
|
}) |
|
|
|
const editorRef = ref(null) |
|
|
|
const fullscreen = ref(false) |
|
|
|
const elRef = ref(null) |
|
|
|
const tinymceContent = computed(() => props.modelValue) |
|
|
|
|
|
|
|
const example_image_upload_handler = (blobInfo, progress) => |
|
|
|
new Promise((resolve, reject) => { |
|
|
|
const xhr = new XMLHttpRequest() |
|
|
|
xhr.withCredentials = false |
|
|
|
xhr.open('POST', '/minIo/upload') |
|
|
|
toolbar: { |
|
|
|
type: Array, |
|
|
|
default: toolbar, |
|
|
|
}, |
|
|
|
plugins: { |
|
|
|
type: [Array, String], |
|
|
|
default: plugins, |
|
|
|
}, |
|
|
|
modelValue: { |
|
|
|
type: String, |
|
|
|
}, |
|
|
|
height: { |
|
|
|
type: [Number, String], |
|
|
|
required: false, |
|
|
|
default: 400, |
|
|
|
}, |
|
|
|
width: { |
|
|
|
type: [Number, String], |
|
|
|
required: false, |
|
|
|
default: 'auto', |
|
|
|
}, |
|
|
|
showImageUpload: { |
|
|
|
type: Boolean, |
|
|
|
default: true, |
|
|
|
}, |
|
|
|
fileBaseUrl: { |
|
|
|
type: String, |
|
|
|
default: import.meta.env.VITE_APP_UPLOAD_URL + 'sett-minio/', |
|
|
|
}, |
|
|
|
minioBucket: { |
|
|
|
type: String, |
|
|
|
default: 'sett-minio', |
|
|
|
}, |
|
|
|
}) |
|
|
|
const emits = defineEmits([ |
|
|
|
'change', |
|
|
|
'update:modelValue', |
|
|
|
'inited', |
|
|
|
'init-error', |
|
|
|
'uploadFile', |
|
|
|
]) |
|
|
|
const { attrs } = getCurrentInstance() |
|
|
|
const tinymceId = ref(buildShortUUID('tiny-vue')) |
|
|
|
|
|
|
|
xhr.upload.onprogress = (e) => { |
|
|
|
progress((e.loaded / e.total) * 100) |
|
|
|
} |
|
|
|
const containerWidth = computed(() => { |
|
|
|
const width = props.width |
|
|
|
if (isNumber(width)) { |
|
|
|
return `${width}px` |
|
|
|
} |
|
|
|
return width |
|
|
|
}) |
|
|
|
const containerHeigth = computed(() => { |
|
|
|
const height = props.height |
|
|
|
if (isNumber(height)) { |
|
|
|
return `${height}px` |
|
|
|
} |
|
|
|
return height |
|
|
|
}) |
|
|
|
const editorRef = ref(null) |
|
|
|
const fullscreen = ref(false) |
|
|
|
const elRef = ref(null) |
|
|
|
const tinymceContent = computed(() => props.modelValue) |
|
|
|
|
|
|
|
xhr.onload = () => { |
|
|
|
if (xhr.status === 403) { |
|
|
|
reject({ message: 'HTTP Error: ' + xhr.status, remove: true }) |
|
|
|
return |
|
|
|
} |
|
|
|
const example_image_upload_handler = (blobInfo, progress) => |
|
|
|
new Promise((resolve, reject) => { |
|
|
|
const xhr = new XMLHttpRequest() |
|
|
|
xhr.withCredentials = false |
|
|
|
xhr.open('POST', '/minIo/upload') |
|
|
|
|
|
|
|
if (xhr.status < 200 || xhr.status >= 300) { |
|
|
|
reject('HTTP Error: ' + xhr.status) |
|
|
|
return |
|
|
|
} |
|
|
|
xhr.upload.onprogress = (e) => { |
|
|
|
progress((e.loaded / e.total) * 100) |
|
|
|
} |
|
|
|
|
|
|
|
const json = JSON.parse(xhr.responseText) |
|
|
|
xhr.onload = () => { |
|
|
|
if (xhr.status === 403) { |
|
|
|
reject({ message: 'HTTP Error: ' + xhr.status, remove: true }) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if (!json) { |
|
|
|
reject('Invalid JSON: ' + xhr.responseText) |
|
|
|
return |
|
|
|
} |
|
|
|
if (xhr.status < 200 || xhr.status >= 300) { |
|
|
|
reject('HTTP Error: ' + xhr.status) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
const imgUrl = props.fileBaseUrl + json.data.ossFilePath |
|
|
|
const json = JSON.parse(xhr.responseText) |
|
|
|
|
|
|
|
emits('uploadFile', { |
|
|
|
imgUrl, |
|
|
|
...json.data, |
|
|
|
}) |
|
|
|
resolve(imgUrl) |
|
|
|
} |
|
|
|
xhr.onerror = () => { |
|
|
|
reject('图片上传错误,错误码: ' + xhr.status) |
|
|
|
if (!json) { |
|
|
|
reject('Invalid JSON: ' + xhr.responseText) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
const formData = new FormData() |
|
|
|
formData.append('file', blobInfo.blob()) //此处与源文档不一样 |
|
|
|
formData.append('bucket', props.minioBucket) //此处与源文档不一样 |
|
|
|
xhr.send(formData) |
|
|
|
}) |
|
|
|
const initOptions = computed(() => { |
|
|
|
const { height, options, toolbar, plugins } = props |
|
|
|
const publicPath = '/' |
|
|
|
return { |
|
|
|
selector: `#${unref(tinymceId)}`, |
|
|
|
language: 'zh-Hans', //注意大小写 |
|
|
|
resize: 'both', //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号 |
|
|
|
font_formats: |
|
|
|
'微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;', //字体样式 |
|
|
|
plugins, |
|
|
|
height, |
|
|
|
toolbar, //工具栏配置,设为false则隐藏 |
|
|
|
statusbar: false, |
|
|
|
relative_urls: false, |
|
|
|
remove_script_host: false, |
|
|
|
convert_urls: false,//绝对路径 |
|
|
|
// 此处为图片上传处理函数,这个直接用了base64的图片形式上传图片, |
|
|
|
images_upload_handler: example_image_upload_handler, |
|
|
|
content_style: 'img {width:100%;height:100%;vertical-align:top}', |
|
|
|
image_dimensions: false, // 默认的宽度和高度设置 |
|
|
|
document_base_url: props.fileBaseUrl, |
|
|
|
paste_data_images: true, //图片是否可粘贴 |
|
|
|
file_picker_types: 'image', //file image media分别对应三个类型文件的上传:link插件,image和axupimgs插件,media插件。想屏蔽某个插件的上传就去掉对应的参数 |
|
|
|
file_picker_callback: function (callback, value, meta) { |
|
|
|
//文件分类 |
|
|
|
let filetype = |
|
|
|
'.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4' |
|
|
|
//后端接收上传文件的地址 |
|
|
|
let upurl = '/minIo/upload' |
|
|
|
//为不同插件指定文件类型及后端地址 |
|
|
|
switch (meta.filetype) { |
|
|
|
case 'image': |
|
|
|
filetype = '.jpg, .jpeg, .png, .gif' |
|
|
|
upurl = '/minIo/upload' |
|
|
|
break |
|
|
|
case 'media': |
|
|
|
filetype = '.mp3, .mp4' |
|
|
|
upurl = '/minIo/upload' |
|
|
|
break |
|
|
|
case 'file': |
|
|
|
default: |
|
|
|
} |
|
|
|
//模拟出一个input用于添加本地文件 |
|
|
|
let input = document.createElement('input') |
|
|
|
input.setAttribute('type', 'file') |
|
|
|
input.setAttribute('accept', filetype) |
|
|
|
input.click() |
|
|
|
input.onchange = function () { |
|
|
|
let file = this.files[0] |
|
|
|
let xhr, formData |
|
|
|
xhr = new XMLHttpRequest() |
|
|
|
xhr.withCredentials = false |
|
|
|
xhr.open('POST', upurl) |
|
|
|
xhr.onload = function () { |
|
|
|
let json |
|
|
|
if (xhr.status != 200) { |
|
|
|
return |
|
|
|
} |
|
|
|
json = JSON.parse(xhr.responseText) |
|
|
|
const imgUrl = props.fileBaseUrl + json.data.ossFilePath |
|
|
|
emits('uploadFile', { |
|
|
|
imgUrl, |
|
|
|
...json.data, |
|
|
|
}) |
|
|
|
callback(imgUrl) |
|
|
|
} |
|
|
|
formData = new FormData() |
|
|
|
formData.append('file', file) |
|
|
|
formData.append('bucket', props.minioBucket) //此处与源文档不一样 |
|
|
|
xhr.send(formData) |
|
|
|
} |
|
|
|
}, |
|
|
|
...options, |
|
|
|
setup: (editor) => { |
|
|
|
editorRef.value = editor |
|
|
|
editor.on('init', (e) => { |
|
|
|
initSetup(e) |
|
|
|
}) |
|
|
|
}, |
|
|
|
} |
|
|
|
}) |
|
|
|
const imgUrl = props.fileBaseUrl + json.data.ossFilePath |
|
|
|
|
|
|
|
const disabled = computed(() => { |
|
|
|
const { options } = props |
|
|
|
const getdDisabled = options && Reflect.get(options, 'readonly') |
|
|
|
const editor = unref(editorRef) |
|
|
|
if (editor) { |
|
|
|
editor.setMode(getdDisabled ? 'readonly' : 'design') |
|
|
|
emits('uploadFile', { |
|
|
|
imgUrl, |
|
|
|
...json.data, |
|
|
|
}) |
|
|
|
resolve(imgUrl) |
|
|
|
} |
|
|
|
return getdDisabled ?? false |
|
|
|
}) |
|
|
|
|
|
|
|
watch( |
|
|
|
() => attrs.disabled, |
|
|
|
() => { |
|
|
|
const editor = unref(editorRef) |
|
|
|
if (!editor) { |
|
|
|
return |
|
|
|
} |
|
|
|
editor.setMode(attrs.disabled ? 'readonly' : 'design') |
|
|
|
xhr.onerror = () => { |
|
|
|
reject('图片上传错误,错误码: ' + xhr.status) |
|
|
|
} |
|
|
|
) |
|
|
|
|
|
|
|
onBeforeUnmount(() => { |
|
|
|
destory() |
|
|
|
}) |
|
|
|
|
|
|
|
onDeactivated(() => { |
|
|
|
destory() |
|
|
|
const formData = new FormData() |
|
|
|
formData.append('file', blobInfo.blob()) //此处与源文档不一样 |
|
|
|
formData.append('bucket', props.minioBucket) //此处与源文档不一样 |
|
|
|
xhr.send(formData) |
|
|
|
}) |
|
|
|
const initOptions = computed(() => { |
|
|
|
const { height, options, toolbar, plugins } = props |
|
|
|
const publicPath = '/' |
|
|
|
return { |
|
|
|
selector: `#${unref(tinymceId)}`, |
|
|
|
language: 'zh-Hans', //注意大小写 |
|
|
|
resize: 'both', //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号 |
|
|
|
font_formats: |
|
|
|
'微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;', //字体样式 |
|
|
|
plugins, |
|
|
|
height, |
|
|
|
toolbar, //工具栏配置,设为false则隐藏 |
|
|
|
statusbar: false, |
|
|
|
relative_urls: false, |
|
|
|
remove_script_host: false, |
|
|
|
convert_urls: false, //绝对路径 |
|
|
|
// 此处为图片上传处理函数,这个直接用了base64的图片形式上传图片, |
|
|
|
images_upload_handler: example_image_upload_handler, |
|
|
|
content_style: 'img {width:100%;height:100%;vertical-align:top}', |
|
|
|
image_dimensions: false, // 默认的宽度和高度设置 |
|
|
|
document_base_url: props.fileBaseUrl, |
|
|
|
paste_data_images: true, //图片是否可粘贴 |
|
|
|
file_picker_types: 'image', //file image media分别对应三个类型文件的上传:link插件,image和axupimgs插件,media插件。想屏蔽某个插件的上传就去掉对应的参数 |
|
|
|
file_picker_callback: function (callback, value, meta) { |
|
|
|
//文件分类 |
|
|
|
let filetype = |
|
|
|
'.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4' |
|
|
|
//后端接收上传文件的地址 |
|
|
|
let upurl = '/minIo/upload' |
|
|
|
//为不同插件指定文件类型及后端地址 |
|
|
|
switch (meta.filetype) { |
|
|
|
case 'image': |
|
|
|
filetype = '.jpg, .jpeg, .png, .gif' |
|
|
|
upurl = '/minIo/upload' |
|
|
|
break |
|
|
|
case 'media': |
|
|
|
filetype = '.mp3, .mp4' |
|
|
|
upurl = '/minIo/upload' |
|
|
|
break |
|
|
|
case 'file': |
|
|
|
default: |
|
|
|
} |
|
|
|
//模拟出一个input用于添加本地文件 |
|
|
|
let input = document.createElement('input') |
|
|
|
input.setAttribute('type', 'file') |
|
|
|
input.setAttribute('accept', filetype) |
|
|
|
input.click() |
|
|
|
input.onchange = function () { |
|
|
|
let file = this.files[0] |
|
|
|
let xhr, formData |
|
|
|
xhr = new XMLHttpRequest() |
|
|
|
xhr.withCredentials = false |
|
|
|
xhr.open('POST', upurl) |
|
|
|
xhr.onload = function () { |
|
|
|
let json |
|
|
|
if (xhr.status != 200) { |
|
|
|
return |
|
|
|
} |
|
|
|
json = JSON.parse(xhr.responseText) |
|
|
|
const imgUrl = props.fileBaseUrl + json.data.ossFilePath |
|
|
|
emits('uploadFile', { |
|
|
|
imgUrl, |
|
|
|
...json.data, |
|
|
|
}) |
|
|
|
callback(imgUrl) |
|
|
|
} |
|
|
|
formData = new FormData() |
|
|
|
formData.append('file', file) |
|
|
|
formData.append('bucket', props.minioBucket) //此处与源文档不一样 |
|
|
|
xhr.send(formData) |
|
|
|
} |
|
|
|
}, |
|
|
|
...options, |
|
|
|
setup: (editor) => { |
|
|
|
editorRef.value = editor |
|
|
|
editor.on('init', (e) => { |
|
|
|
initSetup(e) |
|
|
|
}) |
|
|
|
}, |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
function destory() { |
|
|
|
if (tinymce !== null) { |
|
|
|
// tinymce?.remove?.(unref(initOptions).selector!); |
|
|
|
} |
|
|
|
const disabled = computed(() => { |
|
|
|
const { options } = props |
|
|
|
const getdDisabled = options && Reflect.get(options, 'readonly') |
|
|
|
const editor = unref(editorRef) |
|
|
|
if (editor) { |
|
|
|
editor.setMode(getdDisabled ? 'readonly' : 'design') |
|
|
|
} |
|
|
|
return getdDisabled ?? false |
|
|
|
}) |
|
|
|
|
|
|
|
function initSetup(e) { |
|
|
|
watch( |
|
|
|
() => attrs.disabled, |
|
|
|
() => { |
|
|
|
const editor = unref(editorRef) |
|
|
|
if (!editor) { |
|
|
|
return |
|
|
|
} |
|
|
|
const value = props.modelValue || '' |
|
|
|
editor.setMode(attrs.disabled ? 'readonly' : 'design') |
|
|
|
} |
|
|
|
) |
|
|
|
|
|
|
|
onBeforeUnmount(() => { |
|
|
|
destory() |
|
|
|
}) |
|
|
|
|
|
|
|
editor.setContent(value) |
|
|
|
bindModelHandlers(editor) |
|
|
|
bindHandlers(e, attrs, unref(editorRef)) |
|
|
|
onDeactivated(() => { |
|
|
|
destory() |
|
|
|
}) |
|
|
|
|
|
|
|
function destory() { |
|
|
|
if (tinymce !== null) { |
|
|
|
// tinymce?.remove?.(unref(initOptions).selector!); |
|
|
|
} |
|
|
|
const isLoading = ref(false) |
|
|
|
function initEditor() { |
|
|
|
const el = unref(elRef) |
|
|
|
if (el) { |
|
|
|
el.style.visibility = '' |
|
|
|
} |
|
|
|
isLoading.value = true |
|
|
|
tinymce |
|
|
|
.init(unref(initOptions)) |
|
|
|
.then((editor) => { |
|
|
|
emits('inited', editor) |
|
|
|
}) |
|
|
|
.catch((err) => { |
|
|
|
emits('init-error', err) |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
isLoading.value = false |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function initSetup(e) { |
|
|
|
const editor = unref(editorRef) |
|
|
|
if (!editor) { |
|
|
|
return |
|
|
|
} |
|
|
|
const value = props.modelValue || '' |
|
|
|
|
|
|
|
function setValue(editor, val, prevVal) { |
|
|
|
if ( |
|
|
|
editor && |
|
|
|
typeof val === 'string' && |
|
|
|
val !== prevVal && |
|
|
|
val !== editor.getContent({ format: attrs.outputFormat }) |
|
|
|
) { |
|
|
|
editor.setContent(val) |
|
|
|
} |
|
|
|
editor.setContent(value) |
|
|
|
bindModelHandlers(editor) |
|
|
|
bindHandlers(e, attrs, unref(editorRef)) |
|
|
|
} |
|
|
|
const isLoading = ref(false) |
|
|
|
function initEditor() { |
|
|
|
const el = unref(elRef) |
|
|
|
if (el) { |
|
|
|
el.style.visibility = '' |
|
|
|
} |
|
|
|
isLoading.value = true |
|
|
|
tinymce |
|
|
|
.init(unref(initOptions)) |
|
|
|
.then((editor) => { |
|
|
|
emits('inited', editor) |
|
|
|
}) |
|
|
|
.catch((err) => { |
|
|
|
emits('init-error', err) |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
isLoading.value = false |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function bindModelHandlers(editor) { |
|
|
|
const modelEvents = attrs.modelEvents ? attrs.modelEvents : null |
|
|
|
const normalizedEvents = Array.isArray(modelEvents) |
|
|
|
? modelEvents.join(' ') |
|
|
|
: modelEvents |
|
|
|
function setValue(editor, val, prevVal) { |
|
|
|
if ( |
|
|
|
editor && |
|
|
|
typeof val === 'string' && |
|
|
|
val !== prevVal && |
|
|
|
val !== editor.getContent({ format: attrs.outputFormat }) |
|
|
|
) { |
|
|
|
editor.setContent(val) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
watch( |
|
|
|
() => props.modelValue, |
|
|
|
(val, prevVal) => { |
|
|
|
setValue(editor, val, prevVal) |
|
|
|
} |
|
|
|
) |
|
|
|
function bindModelHandlers(editor) { |
|
|
|
const modelEvents = attrs.modelEvents ? attrs.modelEvents : null |
|
|
|
const normalizedEvents = Array.isArray(modelEvents) |
|
|
|
? modelEvents.join(' ') |
|
|
|
: modelEvents |
|
|
|
|
|
|
|
watch( |
|
|
|
() => props.value, |
|
|
|
(val, prevVal) => { |
|
|
|
setValue(editor, val, prevVal) |
|
|
|
}, |
|
|
|
{ |
|
|
|
immediate: true, |
|
|
|
} |
|
|
|
) |
|
|
|
watch( |
|
|
|
() => props.modelValue, |
|
|
|
(val, prevVal) => { |
|
|
|
setValue(editor, val, prevVal) |
|
|
|
} |
|
|
|
) |
|
|
|
|
|
|
|
editor.on(normalizedEvents || 'change keyup undo redo', () => { |
|
|
|
const content = editor.getContent({ format: attrs.outputFormat }) |
|
|
|
emits('update:modelValue', content) |
|
|
|
emits('change', content) |
|
|
|
}) |
|
|
|
watch( |
|
|
|
() => props.value, |
|
|
|
(val, prevVal) => { |
|
|
|
setValue(editor, val, prevVal) |
|
|
|
}, |
|
|
|
{ |
|
|
|
immediate: true, |
|
|
|
} |
|
|
|
) |
|
|
|
|
|
|
|
editor.on('FullscreenStateChanged', (e) => { |
|
|
|
fullscreen.value = e.state |
|
|
|
}) |
|
|
|
} |
|
|
|
editor.on(normalizedEvents || 'change keyup undo redo', () => { |
|
|
|
const content = editor.getContent({ format: attrs.outputFormat }) |
|
|
|
emits('update:modelValue', content) |
|
|
|
emits('change', content) |
|
|
|
}) |
|
|
|
|
|
|
|
function getUploadingImgName(name) { |
|
|
|
return `[uploading:${name}]` |
|
|
|
} |
|
|
|
const src = './tinymce/js/tinymce/tinymce.min.js' |
|
|
|
const dynamicLoadScript = () => { |
|
|
|
const tinymcemin = document.getElementById('tinymcemin') |
|
|
|
if (!tinymcemin) { |
|
|
|
const script = document.createElement('script') |
|
|
|
script.src = src // src url for the third-party library being loaded. |
|
|
|
script.id = 'tinymcemin' |
|
|
|
document.body.appendChild(script) |
|
|
|
script.onload = function () { |
|
|
|
initEditor() |
|
|
|
} |
|
|
|
} else { |
|
|
|
editor.on('FullscreenStateChanged', (e) => { |
|
|
|
fullscreen.value = e.state |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function getUploadingImgName(name) { |
|
|
|
return `[uploading:${name}]` |
|
|
|
} |
|
|
|
const src = './tinymce/js/tinymce/tinymce.min.js' |
|
|
|
const dynamicLoadScript = () => { |
|
|
|
const tinymcemin = document.getElementById(tinymceId.value) |
|
|
|
if (!tinymcemin) { |
|
|
|
const script = document.createElement('script') |
|
|
|
script.src = src // src url for the third-party library being loaded. |
|
|
|
script.id = 'tinymcemin' |
|
|
|
document.body.appendChild(script) |
|
|
|
script.onload = function () { |
|
|
|
initEditor() |
|
|
|
} |
|
|
|
} else { |
|
|
|
initEditor() |
|
|
|
} |
|
|
|
onMounted(() => { |
|
|
|
if (!initOptions.value.inline) { |
|
|
|
tinymceId.value = buildShortUUID('tiny-vue') |
|
|
|
} |
|
|
|
dynamicLoadScript() |
|
|
|
}) |
|
|
|
// onMountedOrActivated(() => { |
|
|
|
// if (!initOptions.value.inline) { |
|
|
|
// tinymceId.value = buildShortUUID('tiny-vue') |
|
|
|
// } |
|
|
|
// nextTick(() => { |
|
|
|
// setTimeout(() => { |
|
|
|
// initEditor() |
|
|
|
// }, 30) |
|
|
|
// }) |
|
|
|
// }) |
|
|
|
} |
|
|
|
onMounted(() => { |
|
|
|
console.log('onMountedtiny'); |
|
|
|
if (!initOptions.value.inline) { |
|
|
|
tinymceId.value = buildShortUUID('tiny-vue') |
|
|
|
} |
|
|
|
dynamicLoadScript() |
|
|
|
}) |
|
|
|
// onMountedOrActivated(() => { |
|
|
|
// if (!initOptions.value.inline) { |
|
|
|
// tinymceId.value = buildShortUUID('tiny-vue') |
|
|
|
// } |
|
|
|
// nextTick(() => { |
|
|
|
// setTimeout(() => { |
|
|
|
// initEditor() |
|
|
|
// }, 30) |
|
|
|
// }) |
|
|
|
// }) |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |