From 3c3153c68aa2fa404e15598f5879eb2286e7db43 Mon Sep 17 00:00:00 2001 From: CornerSkyless <573196853@qq.com> Date: Tue, 7 Mar 2023 20:31:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E4=BC=9A=E8=AF=9D=E4=B8=BA=E5=9B=BE=E7=89=87=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 33 +++++++++++++++++++++++++++++++++ src/locales/en-US.ts | 2 ++ src/locales/zh-CN.ts | 2 ++ src/locales/zh-TW.ts | 2 ++ src/views/chat/index.vue | 37 ++++++++++++++++++++++++++++++++++++- 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c9fb5aa1db..af9e920c41 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@traptitech/markdown-it-katex": "^3.6.0", "@vueuse/core": "^9.13.0", "highlight.js": "^11.7.0", + "html2canvas": "^1.4.1", "katex": "^0.16.4", "markdown-it": "^13.0.1", "naive-ui": "^2.34.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 736334a705..8cf979494e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,7 @@ specifiers: crypto-js: ^4.1.1 eslint: ^8.35.0 highlight.js: ^11.7.0 + html2canvas: ^1.4.1 husky: ^8.0.3 katex: ^0.16.4 less: ^4.1.3 @@ -39,6 +40,7 @@ dependencies: '@traptitech/markdown-it-katex': 3.6.0 '@vueuse/core': 9.13.0_vue@3.2.47 highlight.js: 11.7.0 + html2canvas: 1.4.1 katex: 0.16.4 markdown-it: 13.0.1 naive-ui: 2.34.3_vue@3.2.47 @@ -1345,6 +1347,11 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /base64-arraybuffer/1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + dev: false + /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -1666,6 +1673,12 @@ packages: resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} dev: true + /css-line-break/2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + dependencies: + utrie: 1.0.2 + dev: false + /css-render/0.15.12: resolution: {integrity: sha512-eWzS66patiGkTTik+ipO9qNGZ+uNuGyTmnz6/+EJIiFg8+3yZRpnMwgFo8YdXhQRsiePzehnusrxVvugNjXzbw==} dependencies: @@ -2757,6 +2770,14 @@ packages: lru-cache: 6.0.0 dev: true + /html2canvas/1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + dev: false + /htmlparser2/8.0.1: resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} dependencies: @@ -4488,6 +4509,12 @@ packages: engines: {node: '>=0.10'} dev: true + /text-segmentation/1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + dependencies: + utrie: 1.0.2 + dev: false + /text-table/0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -4670,6 +4697,12 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /utrie/1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + dependencies: + base64-arraybuffer: 1.0.2 + dev: false + /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 0766d7564e..5930425953 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -21,6 +21,8 @@ export default { copyCode: 'Copy Code', clearChat: 'Clear Chat', clearChatConfirm: 'Are you sure to clear this chat?', + exportImage: 'Export Image', + exportImageConfirm: 'Are you sure to export this chat to png?', deleteMessage: 'Delete Message', deleteMessageConfirm: 'Are you sure to delete this message?', deleteHistoryConfirm: 'Are you sure to clear this history?', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index a5007275cb..bc37a7041b 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -21,6 +21,8 @@ export default { copyCode: '复制代码', clearChat: '清空会话', clearChatConfirm: '是否清空会话?', + exportImage: '保存会话到图片', + exportImageConfirm: '是否将会话保存为图片?', deleteMessage: '删除消息', deleteMessageConfirm: '是否删除此消息?', deleteHistoryConfirm: '确定删除此记录?', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 1385d95e4c..58d0cb620b 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -21,6 +21,8 @@ export default { copyCode: '複製代碼', clearChat: '清空對話', clearChatConfirm: '是否清空對話?', + exportImage: '儲存對話為圖片', + exportImageConfirm: '是否將對話儲存為圖片?', deleteMessage: '刪除訊息', deleteMessageConfirm: '是否刪除此訊息?', deleteHistoryConfirm: '確定刪除此紀錄?', diff --git a/src/views/chat/index.vue b/src/views/chat/index.vue index f5e1c368e9..be9964429b 100644 --- a/src/views/chat/index.vue +++ b/src/views/chat/index.vue @@ -2,6 +2,7 @@ import { computed, onMounted, onUnmounted, ref } from 'vue' import { useRoute } from 'vue-router' import { NButton, NInput, useDialog } from 'naive-ui' +import html2canvas from 'html2canvas' import { Message } from './components' import { useScroll } from './hooks/useScroll' import { useChat } from './hooks/useChat' @@ -268,6 +269,35 @@ async function onRegenerate(index: number) { } } +function handleExport() { + if (loading.value) + return + + dialog.warning({ + title: t('chat.exportImage'), + content: t('chat.exportImageConfirm'), + positiveText: t('common.yes'), + negativeText: t('common.no'), + onPositiveClick: () => { + const ele = document.getElementById('image-wrapper') + html2canvas(ele as HTMLElement).then((canvas) => { + 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) + }) + }, + }) +} + function handleDelete(index: number) { if (loading.value) return @@ -370,7 +400,7 @@ onUnmounted(() => {