|
|
|
@ -1,7 +1,8 @@
|
|
|
|
|
<script setup lang='ts'>
|
|
|
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
|
import { useRoute } from 'vue-router'
|
|
|
|
|
import { NButton, NInput, useDialog } from 'naive-ui'
|
|
|
|
|
import { NButton, NInput, useDialog, useMessage } from 'naive-ui'
|
|
|
|
|
import html2canvas from 'html2canvas'
|
|
|
|
|
import { Message } from './components'
|
|
|
|
|
import { useScroll } from './hooks/useScroll'
|
|
|
|
|
import { useChat } from './hooks/useChat'
|
|
|
|
@ -16,6 +17,7 @@ let controller = new AbortController()
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const dialog = useDialog()
|
|
|
|
|
const ms = useMessage()
|
|
|
|
|
|
|
|
|
|
const chatStore = useChatStore()
|
|
|
|
|
|
|
|
|
@ -268,6 +270,46 @@ async function onRegenerate(index: number) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleExport() {
|
|
|
|
|
if (loading.value)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
const d = dialog.warning({
|
|
|
|
|
title: t('chat.exportImage'),
|
|
|
|
|
content: t('chat.exportImageConfirm'),
|
|
|
|
|
positiveText: t('common.yes'),
|
|
|
|
|
negativeText: t('common.no'),
|
|
|
|
|
onPositiveClick: async () => {
|
|
|
|
|
try {
|
|
|
|
|
d.loading = true
|
|
|
|
|
const ele = document.getElementById('image-wrapper')
|
|
|
|
|
const canvas = await html2canvas(ele as HTMLDivElement)
|
|
|
|
|
const imgUrl = canvas.toDataURL('image/png')
|
|
|
|
|
const tempLink = document.createElement('a')
|
|
|
|
|
tempLink.style.display = 'none'
|
|
|
|
|
tempLink.href = imgUrl
|
|
|
|
|
tempLink.setAttribute('download', 'chat-shot.png')
|
|
|
|
|
if (typeof tempLink.download === 'undefined')
|
|
|
|
|
tempLink.setAttribute('target', '_blank')
|
|
|
|
|
|
|
|
|
|
document.body.appendChild(tempLink)
|
|
|
|
|
tempLink.click()
|
|
|
|
|
document.body.removeChild(tempLink)
|
|
|
|
|
window.URL.revokeObjectURL(imgUrl)
|
|
|
|
|
d.loading = false
|
|
|
|
|
ms.success(t('chat.exportSuccess'))
|
|
|
|
|
Promise.resolve()
|
|
|
|
|
}
|
|
|
|
|
catch (error: any) {
|
|
|
|
|
ms.error(t('chat.exportFailed'))
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
d.loading = false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleDelete(index: number) {
|
|
|
|
|
if (loading.value)
|
|
|
|
|
return
|
|
|
|
@ -360,9 +402,8 @@ onUnmounted(() => {
|
|
|
|
|
id="scrollRef"
|
|
|
|
|
ref="scrollRef"
|
|
|
|
|
class="h-full overflow-hidden overflow-y-auto"
|
|
|
|
|
:class="[isMobile ? 'p-2' : 'p-4']"
|
|
|
|
|
>
|
|
|
|
|
<div class="w-full max-w-screen-xl m-auto">
|
|
|
|
|
<div id="image-wrapper" class="w-full max-w-screen-xl m-auto" :class="[isMobile ? 'p-2' : 'p-4']">
|
|
|
|
|
<template v-if="!dataSources.length">
|
|
|
|
|
<div class="flex items-center justify-center mt-4 text-center text-neutral-300">
|
|
|
|
|
<SvgIcon icon="ri:bubble-chart-fill" class="mr-2 text-3xl" />
|
|
|
|
@ -403,6 +444,11 @@ onUnmounted(() => {
|
|
|
|
|
<SvgIcon icon="ri:delete-bin-line" />
|
|
|
|
|
</span>
|
|
|
|
|
</HoverButton>
|
|
|
|
|
<HoverButton @click="handleExport">
|
|
|
|
|
<span class="text-xl text-[#4f555e] dark:text-white">
|
|
|
|
|
<SvgIcon icon="ri:download-2-line" />
|
|
|
|
|
</span>
|
|
|
|
|
</HoverButton>
|
|
|
|
|
<NInput
|
|
|
|
|
v-model:value="prompt"
|
|
|
|
|
type="textarea"
|
|
|
|
|