Commit f6db356e authored by tyyin lan's avatar tyyin lan

feat(首页): 模型回复内容支持下载和复制

parent d8bfea84
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/> />
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_6vkupfekpjd.css" /> <link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_3ywre5b9kt4.css" />
<title>Model Link</title> <title>Model Link</title>
</head> </head>
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dompurify": "^3.2.0", "dompurify": "^3.2.0",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"file-saver": "^2.0.5",
"github-markdown-css": "^5.7.0", "github-markdown-css": "^5.7.0",
"highlight.js": "^11.10.0", "highlight.js": "^11.10.0",
"howler": "^2.2.4", "howler": "^2.2.4",
...@@ -52,6 +53,7 @@ ...@@ -52,6 +53,7 @@
"@commitlint/types": "^19.5.0", "@commitlint/types": "^19.5.0",
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
"@intlify/unplugin-vue-i18n": "^4.0.0", "@intlify/unplugin-vue-i18n": "^4.0.0",
"@types/file-saver": "^2.0.7",
"@types/howler": "^2.2.12", "@types/howler": "^2.2.12",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.16.5", "@types/node": "^20.16.5",
...@@ -68,6 +70,7 @@ ...@@ -68,6 +70,7 @@
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^9.28.0", "eslint-plugin-vue": "^9.28.0",
"globals": "^15.9.0", "globals": "^15.9.0",
"html-docx-js-typescript": "^0.1.5",
"husky": "^9.1.6", "husky": "^9.1.6",
"lint-staged": "^15.2.10", "lint-staged": "^15.2.10",
"naive-ui": "^2.39.0", "naive-ui": "^2.39.0",
......
This diff is collapsed.
...@@ -65,6 +65,14 @@ const textContentParser = throttle( ...@@ -65,6 +65,14 @@ const textContentParser = throttle(
watchEffect(() => { watchEffect(() => {
textContentParser(props.rawTextContent) textContentParser(props.rawTextContent)
}) })
function getRenderTextContent() {
return renderTextContent.value
}
defineExpose({
getRenderTextContent,
})
</script> </script>
<template> <template>
......
import { saveAs } from 'file-saver'
export function downloadFile(resource: string | Blob | File, fileName?: string) {
return new Promise((resolve) => {
if (fileName) {
saveAs(resource, fileName)
return
}
saveAs(resource)
resolve(null)
})
}
<script setup lang="ts"> <script setup lang="ts">
import { computed, readonly, ref } from 'vue' import { computed, readonly, ref, useTemplateRef } from 'vue'
import { CheckOne, Down } from '@icon-park/vue-next' import { CheckOne, Down } from '@icon-park/vue-next'
import type { MessageItemInterface } from '../types' import type { MessageItemInterface } from '../types'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import MessageBubbleLoading from './message-bubble-loading.vue' import MessageBubbleLoading from './message-bubble-loading.vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { copyToClip } from '@/utils/copy'
import { asBlob } from 'html-docx-js-typescript'
import { throttle } from 'lodash-es'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import { downloadFile } from '@/utils/download-file'
interface Props { interface Props {
messageItem: MessageItemInterface messageItem: MessageItemInterface
...@@ -15,6 +20,8 @@ const props = defineProps<Props>() ...@@ -15,6 +20,8 @@ const props = defineProps<Props>()
const { t } = useI18n() const { t } = useI18n()
const userStore = useUserStore() const userStore = useUserStore()
const markdownRenderRef = useTemplateRef<InstanceType<typeof MarkdownRender>>('markdownRenderRef')
const agentDefaultAvatarUrl = readonly({ url: 'https://gsst-poe-sit.gz.bcebos.com/icon/agent-avatar.png' }) const agentDefaultAvatarUrl = readonly({ url: 'https://gsst-poe-sit.gz.bcebos.com/icon/agent-avatar.png' })
const isShowReasoningContent = ref(true) const isShowReasoningContent = ref(true)
...@@ -33,6 +40,32 @@ const name = computed(() => { ...@@ -33,6 +40,32 @@ const name = computed(() => {
function handleShowReasoningContentSwitch() { function handleShowReasoningContentSwitch() {
isShowReasoningContent.value = !isShowReasoningContent.value isShowReasoningContent.value = !isShowReasoningContent.value
} }
const handleContentCopy = throttle(
() => {
copyToClip(props.messageItem.content).then(() => {
window.$message.success(t('common_module.copy_success_message'))
})
},
1000,
{ leading: true, trailing: false },
)
const handleContentDownload = throttle(
() => {
if (markdownRenderRef.value) {
const content = markdownRenderRef.value.getRenderTextContent()
asBlob(content).then((data) => {
downloadFile(data as Blob).then(() => {
window.$message.success(t('common_module.copy_success_message'))
})
})
}
},
1000,
{ leading: true, trailing: false },
)
</script> </script>
<template> <template>
...@@ -66,6 +99,7 @@ function handleShowReasoningContentSwitch() { ...@@ -66,6 +99,7 @@ function handleShowReasoningContentSwitch() {
/> />
<template v-else> <template v-else>
<MarkdownRender <MarkdownRender
ref="markdownRenderRef"
:raw-text-content=" :raw-text-content="
messageItem.reasoningContent messageItem.reasoningContent
? messageItem.reasoningContent ? messageItem.reasoningContent
...@@ -103,6 +137,7 @@ function handleShowReasoningContentSwitch() { ...@@ -103,6 +137,7 @@ function handleShowReasoningContentSwitch() {
</div> </div>
<template v-else> <template v-else>
<MarkdownRender <MarkdownRender
ref="markdownRenderRef"
:raw-text-content=" :raw-text-content="
messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content') messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content')
" "
...@@ -115,6 +150,20 @@ function handleShowReasoningContentSwitch() { ...@@ -115,6 +150,20 @@ function handleShowReasoningContentSwitch() {
</template> </template>
</div> </div>
<div
v-show="isAgentMessage && messageItem.content && !messageItem.isAnswerLoading"
class="py-[10px] pr-[14px] text-end"
>
<i
class="iconfont icon-download hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px]"
@click="handleContentDownload"
></i>
<i
class="iconfont icon-copy1 hover:text-theme-color transform cursor-pointer text-[14px]"
@click="handleContentCopy"
></i>
</div>
<div <div
v-show="isAgentMessage && messageItem.pluginName" v-show="isAgentMessage && messageItem.pluginName"
class="mt-[7px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]" class="mt-[7px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment