perf: 优化复制逻辑
This commit is contained in:
parent
3b033d0ed4
commit
dbb57d8894
|
@ -0,0 +1,18 @@
|
|||
export function copyToClip(text: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const input: HTMLTextAreaElement = document.createElement('textarea')
|
||||
input.setAttribute('readonly', 'readonly')
|
||||
input.value = text
|
||||
document.body.appendChild(input)
|
||||
input.select()
|
||||
if (document.execCommand('copy'))
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(input)
|
||||
resolve(text)
|
||||
}
|
||||
catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, onUpdated, ref } from 'vue'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import mdKatex from '@traptitech/markdown-it-katex'
|
||||
import mila from 'markdown-it-link-attributes'
|
||||
import hljs from 'highlight.js'
|
||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||
import { t } from '@/locales'
|
||||
import { copyToClip } from '@/utils/copy'
|
||||
|
||||
interface Props {
|
||||
inversion?: boolean
|
||||
|
@ -22,7 +23,7 @@ const { isMobile } = useBasicLayout()
|
|||
const textRef = ref<HTMLElement>()
|
||||
|
||||
const mdi = new MarkdownIt({
|
||||
html: true,
|
||||
html: false,
|
||||
linkify: true,
|
||||
highlight(code, language) {
|
||||
const validLang = !!(language && hljs.getLanguage(language))
|
||||
|
@ -61,7 +62,45 @@ function highlightBlock(str: string, lang?: string) {
|
|||
return `<pre class="code-block-wrapper"><div class="code-block-header"><span class="code-block-header__lang">${lang}</span><span class="code-block-header__copy">${t('chat.copyCode')}</span></div><code class="hljs code-block-body ${lang}">${str}</code></pre>`
|
||||
}
|
||||
|
||||
defineExpose({ textRef })
|
||||
function addCopyEvents() {
|
||||
if (textRef.value) {
|
||||
const copyBtn = textRef.value.querySelectorAll('.code-block-header__copy')
|
||||
copyBtn.forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const code = btn.parentElement?.nextElementSibling?.textContent
|
||||
if (code) {
|
||||
copyToClip(code).then(() => {
|
||||
btn.textContent = '复制成功'
|
||||
setTimeout(() => {
|
||||
btn.textContent = '复制代码'
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function removeCopyEvents() {
|
||||
if (textRef.value) {
|
||||
const copyBtn = textRef.value.querySelectorAll('.code-block-header__copy')
|
||||
copyBtn.forEach((btn) => {
|
||||
btn.removeEventListener('click', () => {})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
addCopyEvents()
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
addCopyEvents()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
removeCopyEvents()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script setup lang='ts'>
|
||||
import { computed, ref } from 'vue'
|
||||
import { NDropdown } from 'naive-ui'
|
||||
import { NDropdown, useMessage } from 'naive-ui'
|
||||
import AvatarComponent from './Avatar.vue'
|
||||
import TextComponent from './Text.vue'
|
||||
import { SvgIcon } from '@/components/common'
|
||||
import { copyText } from '@/utils/format'
|
||||
import { useIconRender } from '@/hooks/useIconRender'
|
||||
import { t } from '@/locales'
|
||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||
import { copyToClip } from '@/utils/copy'
|
||||
|
||||
interface Props {
|
||||
dateTime?: string
|
||||
|
@ -30,6 +30,8 @@ const { isMobile } = useBasicLayout()
|
|||
|
||||
const { iconRender } = useIconRender()
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const textRef = ref<HTMLElement>()
|
||||
|
||||
const asRawText = ref(props.inversion)
|
||||
|
@ -64,7 +66,7 @@ const options = computed(() => {
|
|||
function handleSelect(key: 'copyText' | 'delete' | 'toggleRenderType') {
|
||||
switch (key) {
|
||||
case 'copyText':
|
||||
copyText({ text: props.text ?? '' })
|
||||
handleCopy()
|
||||
return
|
||||
case 'toggleRenderType':
|
||||
asRawText.value = !asRawText.value
|
||||
|
@ -78,6 +80,16 @@ function handleRegenerate() {
|
|||
messageRef.value?.scrollIntoView()
|
||||
emit('regenerate')
|
||||
}
|
||||
|
||||
async function handleCopy() {
|
||||
try {
|
||||
await copyToClip(props.text || '')
|
||||
message.success('复制成功')
|
||||
}
|
||||
catch {
|
||||
message.error('复制失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import { onMounted, onUpdated } from 'vue'
|
||||
import { copyText } from '@/utils/format'
|
||||
|
||||
export function useCopyCode() {
|
||||
function copyCodeBlock() {
|
||||
const codeBlockWrapper = document.querySelectorAll('.code-block-wrapper')
|
||||
codeBlockWrapper.forEach((wrapper) => {
|
||||
const copyBtn = wrapper.querySelector('.code-block-header__copy')
|
||||
const codeBlock = wrapper.querySelector('.code-block-body')
|
||||
if (copyBtn && codeBlock) {
|
||||
copyBtn.addEventListener('click', () => {
|
||||
if (navigator.clipboard?.writeText)
|
||||
navigator.clipboard.writeText(codeBlock.textContent ?? '')
|
||||
else
|
||||
copyText({ text: codeBlock.textContent ?? '', origin: true })
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => copyCodeBlock())
|
||||
|
||||
onUpdated(() => copyCodeBlock())
|
||||
}
|
|
@ -8,7 +8,6 @@ import html2canvas from 'html2canvas'
|
|||
import { Message } from './components'
|
||||
import { useScroll } from './hooks/useScroll'
|
||||
import { useChat } from './hooks/useChat'
|
||||
import { useCopyCode } from './hooks/useCopyCode'
|
||||
import { useUsingContext } from './hooks/useUsingContext'
|
||||
import HeaderComponent from './components/Header/index.vue'
|
||||
import { HoverButton, SvgIcon } from '@/components/common'
|
||||
|
@ -27,8 +26,6 @@ const ms = useMessage()
|
|||
|
||||
const chatStore = useChatStore()
|
||||
|
||||
useCopyCode()
|
||||
|
||||
const { isMobile } = useBasicLayout()
|
||||
const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat()
|
||||
const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll()
|
||||
|
|
Loading…
Reference in New Issue