123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- <template>
- <div class="flex flex-col rounded-xl bg-[#f2f4f7] px-3 py-2 shadow">
- <div
- class="flex items-center gap-2 py-1 text-xs font-semibold text-gray-700"
- >
- <div>回复</div>
- <div class="ml-auto">字数</div>
- <el-tooltip content="复制" placement="top">
- <SvgIcon
- class="__hover"
- color="#364153"
- name="copy"
- size="16"
- @click="onCopy(ref_textarea.innerText)"
- />
- </el-tooltip>
- </div>
- <div
- ref="ref_textarea"
- class="text-text-secondary flex-1 py-1 text-[13px] break-words whitespace-pre-wrap outline-none select-text"
- contenteditable="true"
- @input="updateMarkdown"
- ></div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, computed, onMounted, reactive } from 'vue'
- import { copy } from '@/utils/czr-util'
- import { ElMessage } from 'element-plus'
- import SvgIcon from '@/components/SvgIcon/index.vue'
- const emit = defineEmits(['update:modelValue'])
- const props = defineProps({
- modelValue: {
- type: String,
- default: '',
- },
- })
- const state = reactive({
- optionsMap: new Map([
- ['123', { label: '变量1', id: '123', type: 'String' }],
- ['223', { label: '变量变量变量变量变量2', id: '223', type: 'String' }],
- [
- '333',
- {
- label: '变量变量变量变量变量变量变量变量变量变量变量3',
- id: '333',
- type: 'Number',
- },
- ],
- ]),
- })
- const ref_textarea = ref()
- const updateMarkdown = (e) => {
- const selection: any = window.getSelection()
- const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null
- let lastReplacedSpan: any = null
- const regex = /\{\{#([^#]+)#\}\}/g
- const walker = document.createTreeWalker(e.target, NodeFilter.SHOW_TEXT, null)
- while (walker.nextNode()) {
- const node: any = walker.currentNode
- const text: any = node.nodeValue
- if (regex.test(text)) {
- regex.lastIndex = 0
- const fragment = document.createDocumentFragment()
- let lastIndex = 0
- let match
- while ((match = regex.exec(text)) !== null) {
- if (match.index > lastIndex) {
- fragment.appendChild(
- document.createTextNode(text.substring(lastIndex, match.index)),
- )
- }
- const span = document.createElement('span')
- span.setAttribute('contenteditable', 'false')
- span.className = 'border-1 bg-red-400'
- const key = match[1]
- span.textContent = state.optionsMap.get(key)?.label || ''
- fragment.appendChild(span)
- lastReplacedSpan = span
- lastIndex = regex.lastIndex
- }
- if (lastIndex < text.length) {
- fragment.appendChild(document.createTextNode(text.substring(lastIndex)))
- }
- node.parentNode.replaceChild(fragment, node)
- }
- }
- if (lastReplacedSpan) {
- const newRange = document.createRange()
- newRange.setStartAfter(lastReplacedSpan)
- newRange.collapse(true)
- const newSelection: any = window.getSelection()
- newSelection.removeAllRanges()
- newSelection.addRange(newRange)
- }
- }
- const onCopy = (text) => {
- copy(text)
- ElMessage.success('复制成功!')
- }
- onMounted(() => {})
- </script>
- <style scoped lang="scss"></style>
|