123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- <script setup>
- import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
- import tinymce from 'tinymce'
- import { REQUEST } from '~/utils/request'
- const props = defineProps({
- id: {
- type: String,
- default: `tiny-${Date.now()}-${~~(Math.random() * 10000)}`,
- },
- htmlClass: { default: '', type: String },
- modelValue: String,
- o: {
- default() {
- return {}
- },
- type: Object,
- },
- })
- const emits = defineEmits(['update:modelValue', 'click:audio', 'click:video'])
- const content = ref('')
- let editor = null
- let checkerTimeout = null
- let isTyping = false
- function init() {
- const options = {
- selector: `#${props.id}`,
- base_url: '/tiny',
- language: 'zh-Hans',
- height: 130,
- menubar: false,
- statusbar: false,
- body_class: 'tinymce-area',
- toolbar1: 'image emoticons',
- plugins: 'image emoticons',
- autosave_restore_when_empty: false,
- content_style: 'body { margin: 0.4rem; line-height: 1; } p { margin: 0; } ',
- init_instance_callback: (_editor) => {
- _editor.on('KeyUp', (e) => {
- submitNewContent()
- })
- _editor.on('Change', (e) => {
- if (_editor.getContent() !== props.modelValue)
- submitNewContent()
- })
- _editor.on('init', (e) => {
- _editor.setContent(content.value)
- // emits('input', content.value);
- })
- editor = _editor
- },
- automatic_uploads: true,
- images_upload_handler: (blobInfo, progress) => {
- const file = blobInfo.blob()
- return REQUEST.upload({
- url: '/upload/main/file',
- data: {
- filedata: file,
- },
- onUploadProgress(progressEvent) {
- progress(~~(progressEvent.loaded / progressEvent.total * 100 | 0))
- },
- }).then((res) => {
- if (res.code === '1')
- return window.GLOBAL_CONFIG.oss + (res.data.url)
- else
- return ('')
- })
- },
- // to solve a third party URL
- // paste_preprocess: (editor, args) => {
- // if (/^https?\:\/\/.+(png|jpg)$/.test(args.content)) {
- // args.content = uploadByThirdPartyUrl(args.content);
- // }
- // }
- }
- tinymce.init(Object.assign(options, props.o))
- }
- function submitNewContent() {
- isTyping = true
- if (checkerTimeout !== null)
- clearTimeout(checkerTimeout)
- checkerTimeout = setTimeout(() => {
- isTyping = false
- }, 300)
- emits('update:modelValue', editor.getContent())
- }
- onMounted(() => {
- content.value = props.modelValue
- init()
- })
- onBeforeUnmount(() => {
- editor.destroy()
- })
- defineExpose({
- clear(v = '') {
- editor.resetContent(v)
- },
- })
- function handleClickAudioCall() {
- emits('click:audio')
- }
- function handleClickVideoCall() {
- emits('click:video')
- }
- </script>
- <template>
- <div class="relative">
- <textarea :id="id" class="tinyarea w-full h-full" v-model="content"></textarea>
- <div class="absolute right-0 top-0 h-28px px-13px flex z-1000 text-hex-666">
- <div @click="handleClickAudioCall"
- class="cursor-pointer flex_center box-content w-28px px-3px h-full rounded-sm hover:bg-hex-cce2fa">
- <i:mingcute:phone-call-fill class=" w flex_center h-6" />
- </div>
- <div @click="handleClickVideoCall"
- class="cursor-pointer flex_center box-content w-28px px-3px h-full rounded-sm hover:bg-hex-cce2fa">
- <i:wpf:video-call class=" w-6 h-6" />
- </div>
- </div>
- </div>
- </template>
- <style lang="scss">
- .tinyarea+.tox-tinymce {
- border: none;
- &.tox:not(.tox-tinymce-inline) .tox-editor-header {
- box-shadow: none;
- padding: 0 0;
- }
- .tox-tbtn {
- margin-top: 0;
- margin-bottom: 0;
- cursor: pointer;
- }
- }
- </style>
|