feat: 单消息复制和删除功能

This commit is contained in:
ChenZhaoYu
2023-02-23 11:49:05 +08:00
parent 80d77663a7
commit 25c725c6e8
4 changed files with 120 additions and 12 deletions

View File

@@ -110,6 +110,22 @@ export const useChatStore = defineStore('chat-store', {
}
},
deleteChatByUuid(uuid: number, index: number) {
if (!uuid || uuid === 0) {
if (this.chat.length) {
this.chat[0].data.splice(index, 1)
this.recordState()
}
return
}
const chatIndex = this.chat.findIndex(item => item.uuid === uuid)
if (chatIndex !== -1) {
this.chat[chatIndex].data.splice(index, 1)
this.recordState()
}
},
clearChatByUuid(uuid: number) {
if (!uuid || uuid === 0) {
if (this.chat.length) {

View File

@@ -1,7 +1,9 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { NDropdown } from 'naive-ui'
import { computed, h } from 'vue'
import { marked } from 'marked'
import includeCode from '@/utils/functions/includeCode'
import { SvgIcon } from '@/components/common'
interface Props {
inversion?: boolean
@@ -10,8 +12,45 @@ interface Props {
loading?: boolean
}
interface Emit {
(ev: 'copy'): void
(ev: 'delete'): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
const options = [
{
label: 'Copy',
key: 'copy',
icon: renderIcon('ri:file-copy-2-line'),
},
{
label: 'Delete',
key: 'delete',
icon: renderIcon('ri:delete-bin-6-line'),
},
]
function renderIcon(icon: string) {
return () => {
return h(SvgIcon, { icon })
}
}
function handleSelect(key: string | number) {
switch (key) {
case 'copy':
emit('copy')
break
case 'delete':
emit('delete')
break
}
}
const wrapClass = computed(() => {
return [
'text-wrap',
@@ -34,6 +73,7 @@ const text = computed(() => {
</script>
<template>
<NDropdown trigger="click" :options="options" @select="handleSelect">
<div :class="wrapClass">
<template v-if="loading">
<span class="w-[5px] h-[20px] block animate-blink" />
@@ -43,6 +83,7 @@ const text = computed(() => {
<div v-else class="leading-relaxed break-all" v-html="text" />
</template>
</div>
</NDropdown>
</template>
<style lang="less">

View File

@@ -13,12 +13,22 @@ interface Props {
interface Emit {
(ev: 'regenerate'): void
(ev: 'copy'): void
(ev: 'delete'): void
}
defineProps<Props>()
const emit = defineEmits<Emit>()
function handleCopy() {
emit('copy')
}
function handleDelete() {
emit('delete')
}
function handleRegenerate() {
emit('regenerate')
}
@@ -37,7 +47,14 @@ function handleRegenerate() {
{{ dateTime }}
</span>
<div class="flex items-end mt-2">
<Text :inversion="inversion" :error="error" :text="text" :loading="loading" />
<Text
:inversion="inversion"
:error="error"
:text="text"
:loading="loading"
@copy="handleCopy"
@delete="handleDelete"
/>
<button
v-if="!inversion"
class="mb-2 ml-2 transition text-neutral-400 hover:text-neutral-800"

View File

@@ -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 { useClipboard } from '@vueuse/core'
import { Message } from './components'
import { useScroll } from './hooks/useScroll'
import { useChat } from './hooks/useChat'
@@ -14,6 +15,9 @@ let controller = new AbortController()
const route = useRoute()
const dialog = useDialog()
const ms = useMessage()
const { copy, isSupported } = useClipboard()
const chatStore = useChatStore()
@@ -195,6 +199,34 @@ async function onRegenerate(index: number) {
}
}
async function handleCopy(index: number) {
if (loading.value)
return
if (isSupported.value) {
await copy(dataSources.value[index].text)
ms.success('Copied to clipboard')
}
else {
ms.error('Copy to clipboard is not supported')
}
}
function handleDelete(index: number) {
if (loading.value)
return
dialog.warning({
title: 'Delete Message',
content: 'Are you sure to delete this message?',
positiveText: 'Yes',
negativeText: 'No',
onPositiveClick: () => {
chatStore.deleteChatByUuid(+uuid, index)
},
})
}
function handleClear() {
if (loading.value)
return
@@ -273,6 +305,8 @@ onUnmounted(() => {
:error="item.error"
:loading="item.loading"
@regenerate="onRegenerate(index)"
@copy="handleCopy(index)"
@delete="handleDelete(index)"
/>
<div class="flex justify-center">
<NButton v-if="loading" ghost @click="handleStop">