File size: 2,876 Bytes
89ce340 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
<template>
<div
class="custom-textarea"
ref="textareaRef"
:contenteditable="true"
@focus="handleFocus()"
@blur="handleBlur()"
@input="handleInput()"
v-html="text"
></div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, ref, watch } from 'vue'
import { pasteCustomClipboardString, pasteExcelClipboardString, pasteHTMLTableClipboardString } from '@/utils/clipboard'
const props = withDefaults(defineProps<{
value?: string
}>(), {
value: '',
})
const emit = defineEmits<{
(event: 'updateValue', payload: string): void
(event: 'insertExcelData', payload: string[][]): void
}>()
const textareaRef = ref<HTMLElement>()
const text = ref('')
const isFocus = ref(false)
// 自定义v-modal,同步数据
// 当文本框聚焦时,不执行数据同步
watch(() => props.value, () => {
if (isFocus.value) return
text.value = props.value
if (textareaRef.value) textareaRef.value.innerHTML = props.value
}, { immediate: true })
const handleInput = () => {
if (!textareaRef.value) return
const text = textareaRef.value.innerHTML
emit('updateValue', text)
}
// 聚焦时更新焦点标记,并监听粘贴事件
const handleFocus = () => {
isFocus.value = true
if (!textareaRef.value) return
textareaRef.value.onpaste = (e: ClipboardEvent) => {
e.preventDefault()
if (!e.clipboardData) return
const clipboardDataFirstItem = e.clipboardData.items[0]
if (clipboardDataFirstItem && clipboardDataFirstItem.kind === 'string') {
if (clipboardDataFirstItem.type === 'text/plain') {
clipboardDataFirstItem.getAsString(text => {
const clipboardData = pasteCustomClipboardString(text)
if (typeof clipboardData === 'object') return
const excelData = pasteExcelClipboardString(text)
if (excelData) {
emit('insertExcelData', excelData)
if (textareaRef.value) textareaRef.value.innerHTML = excelData[0][0]
return
}
document.execCommand('insertText', false, text)
})
}
else if (clipboardDataFirstItem.type === 'text/html') {
clipboardDataFirstItem.getAsString(html => {
const htmlData = pasteHTMLTableClipboardString(html)
if (htmlData) {
emit('insertExcelData', htmlData)
if (textareaRef.value) textareaRef.value.innerHTML = htmlData[0][0]
}
})
}
}
}
}
// 失焦时更新焦点标记,清除粘贴事件监听
const handleBlur = () => {
isFocus.value = false
if (textareaRef.value) textareaRef.value.onpaste = null
}
// 清除粘贴事件监听
onBeforeUnmount(() => {
if (textareaRef.value) textareaRef.value.onpaste = null
})
</script>
<style lang="scss" scoped>
.custom-textarea {
border: 0;
outline: 0;
-webkit-user-modify: read-write-plaintext-only;
}
</style> |