feat: 多会话基础逻辑梳理
This commit is contained in:
30
src/components/business/Chat/hooks/useChat.ts
Normal file
30
src/components/business/Chat/hooks/useChat.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useHistoryStore } from '@/store'
|
||||
|
||||
export function useChat() {
|
||||
const historyStore = useHistoryStore()
|
||||
|
||||
function addChat(message: string, args?: { reversal?: boolean; error?: boolean; options?: Chat.ChatOptions }) {
|
||||
if (historyStore.historyChat.length === 0) {
|
||||
historyStore.addHistory({
|
||||
title: message,
|
||||
isEdit: false,
|
||||
data: [],
|
||||
})
|
||||
historyStore.chooseHistory(historyStore.historyChat.length - 1)
|
||||
}
|
||||
|
||||
historyStore.addChat({
|
||||
dateTime: new Date().toLocaleString(),
|
||||
message,
|
||||
reversal: args?.reversal ?? false,
|
||||
error: args?.error ?? false,
|
||||
options: args?.options ?? undefined,
|
||||
})
|
||||
}
|
||||
|
||||
function clearChat() {
|
||||
historyStore.clearChat()
|
||||
}
|
||||
|
||||
return { addChat, clearChat }
|
||||
}
|
||||
@@ -1,27 +1,26 @@
|
||||
<script setup lang='ts'>
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { NButton, NInput, useMessage } from 'naive-ui'
|
||||
import type { ChatOptions, ChatProps } from './types'
|
||||
import { Message } from './components'
|
||||
import { Layout } from './layout'
|
||||
import { useChat } from './hooks/useChat'
|
||||
import { fetchChatAPI } from '@/api'
|
||||
import { HoverButton, SvgIcon } from '@/components/common'
|
||||
import { useHistoryStore } from '@/store'
|
||||
|
||||
const ms = useMessage()
|
||||
|
||||
const historyStore = useHistoryStore()
|
||||
|
||||
const scrollRef = ref<HTMLDivElement>()
|
||||
|
||||
const ms = useMessage()
|
||||
const { addChat, clearChat } = useChat()
|
||||
|
||||
const prompt = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
const list = ref<ChatProps[]>([])
|
||||
const chatList = computed(() => list.value.filter(item => (!item.reversal && !item.error)))
|
||||
|
||||
function initChat() {
|
||||
addMessage('Hi, I am ChatGPT, a chatbot based on GPT-3.')
|
||||
}
|
||||
|
||||
onMounted(initChat)
|
||||
const list = computed<Chat.Chat[]>(() => historyStore.getCurrentChat)
|
||||
const chatList = computed<Chat.Chat[]>(() => list.value.filter(item => (!item.reversal && !item.error)))
|
||||
|
||||
async function handleSubmit() {
|
||||
if (loading.value)
|
||||
@@ -37,7 +36,7 @@ async function handleSubmit() {
|
||||
addMessage(message, { reversal: true })
|
||||
prompt.value = ''
|
||||
|
||||
let options: ChatOptions = {}
|
||||
let options: Chat.ChatOptions = {}
|
||||
const lastContext = chatList.value[chatList.value.length - 1]?.options
|
||||
|
||||
if (lastContext)
|
||||
@@ -63,21 +62,14 @@ function handleEnter(event: KeyboardEvent) {
|
||||
|
||||
function addMessage(
|
||||
message: string,
|
||||
args?: { reversal?: boolean; error?: boolean; options?: ChatOptions },
|
||||
args?: { reversal?: boolean; error?: boolean; options?: Chat.ChatOptions },
|
||||
) {
|
||||
list.value.push({
|
||||
dateTime: new Date().toLocaleString(),
|
||||
message,
|
||||
reversal: args?.reversal ?? false,
|
||||
error: args?.error ?? false,
|
||||
options: args?.options ?? undefined,
|
||||
})
|
||||
addChat(message, args)
|
||||
nextTick(() => scrollRef.value && (scrollRef.value.scrollTop = scrollRef.value.scrollHeight))
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
list.value = []
|
||||
setTimeout(initChat, 100)
|
||||
clearChat()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -96,8 +88,8 @@ function handleClear() {
|
||||
</main>
|
||||
<footer class="p-4">
|
||||
<div class="flex items-center justify-between space-x-2">
|
||||
<HoverButton tooltip="Clear conversations" @click="handleClear">
|
||||
<span class="text-xl text-[#4f555e]">
|
||||
<HoverButton tooltip="Clear conversations">
|
||||
<span class="text-xl text-[#4f555e]" @click="handleClear">
|
||||
<SvgIcon icon="ri:delete-bin-line" />
|
||||
</span>
|
||||
</HoverButton>
|
||||
|
||||
@@ -1,50 +1,65 @@
|
||||
<script setup lang='ts'>
|
||||
import { NScrollbar } from 'naive-ui'
|
||||
import type { HistoryChatProps } from '../../types'
|
||||
import { ref } from 'vue'
|
||||
import { NInput, NScrollbar } from 'naive-ui'
|
||||
import { SvgIcon } from '@/components/common'
|
||||
import { useHistoryStore } from '@/store'
|
||||
|
||||
interface Props {
|
||||
data: HistoryChatProps[]
|
||||
const historyStore = useHistoryStore()
|
||||
|
||||
const dataSources = ref(historyStore.historyChat)
|
||||
|
||||
function handleSelect(index: number) {
|
||||
historyStore.chooseHistory(index)
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(ev: 'delete', index: number): void
|
||||
(ev: 'edit', index: number): void
|
||||
function handleEdit(index: number, isEdit: boolean) {
|
||||
historyStore.editHistory(index, isEdit)
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
function handleEdit(index: number) {
|
||||
emit('delete', index)
|
||||
function handleRemove(index: number) {
|
||||
historyStore.removeHistory(index)
|
||||
}
|
||||
|
||||
function handleDelete(index: number) {
|
||||
emit('delete', index)
|
||||
function handleEnter(index: number, isEdit: boolean, event: KeyboardEvent) {
|
||||
if (event.key === 'Enter')
|
||||
handleEdit(index, isEdit)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NScrollbar class="px-4">
|
||||
<div class="flex flex-col gap-2 text-sm">
|
||||
<div v-for="(item, index) of data" :key="index">
|
||||
<div v-for="(item, index) of dataSources" :key="index">
|
||||
<a
|
||||
class="relative flex items-center gap-3 px-3 py-3 break-all rounded-md cursor-pointer bg-neutral-50 pr-14 hover:bg-neutral-100 group"
|
||||
@click="handleSelect(index)"
|
||||
>
|
||||
<span>
|
||||
<SvgIcon icon="ri:message-3-line" />
|
||||
</span>
|
||||
<div class="relative flex-1 overflow-hidden break-all text-ellipsis whitespace-nowrap max-h-5">
|
||||
<span>{{ item.title }}</span>
|
||||
<div class="relative flex-1 overflow-hidden break-all text-ellipsis whitespace-nowrap">
|
||||
<NInput
|
||||
v-if="item.isEdit"
|
||||
v-model:value="item.title"
|
||||
size="tiny"
|
||||
@keypress="handleEnter(index, false, $event)"
|
||||
/>
|
||||
<span v-else>{{ item.title }}</span>
|
||||
</div>
|
||||
<div class="absolute z-10 flex visible right-1">
|
||||
<button class="p-1">
|
||||
<SvgIcon icon="ri:edit-line" @click="handleEdit(index)" />
|
||||
</button>
|
||||
<button class="p-1" @click="handleDelete(index)">
|
||||
<SvgIcon icon="ri:delete-bin-line" />
|
||||
</button>
|
||||
<template v-if="item.isEdit">
|
||||
<button class="p-1" @click="handleEdit(index, false)">
|
||||
<SvgIcon icon="ri:save-line" />
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button class="p-1">
|
||||
<SvgIcon icon="ri:edit-line" @click="handleEdit(index, true)" />
|
||||
</button>
|
||||
<button class="p-1" @click="handleRemove(index)">
|
||||
<SvgIcon icon="ri:delete-bin-line" />
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
<script setup lang='ts'>
|
||||
import { ref } from 'vue'
|
||||
import { NButton, NLayoutSider } from 'naive-ui'
|
||||
import type { HistoryChatProps } from '../../types'
|
||||
import List from './List.vue'
|
||||
import Footer from './Footer.vue'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useAppStore, useHistoryStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const historyStore = useHistoryStore()
|
||||
|
||||
const collapsed = ref(appStore.siderCollapsed ?? false)
|
||||
|
||||
const history = ref<HistoryChatProps[]>([])
|
||||
|
||||
function handleAdd() {
|
||||
history.value.push({
|
||||
title: 'New chat',
|
||||
edit: false,
|
||||
historyStore.addHistory({
|
||||
title: '',
|
||||
isEdit: false,
|
||||
data: [],
|
||||
})
|
||||
}
|
||||
|
||||
function handleEdit(index: number) {
|
||||
history.value[index].edit = true
|
||||
}
|
||||
|
||||
function handleDelete(index: number) {
|
||||
history.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function handleCollapsed() {
|
||||
collapsed.value = !collapsed.value
|
||||
appStore.setSiderCollapsed(collapsed.value)
|
||||
@@ -51,7 +41,7 @@ function handleCollapsed() {
|
||||
New chat
|
||||
</NButton>
|
||||
</div>
|
||||
<List :data="history" @edit="handleEdit" @delete="handleDelete" />
|
||||
<List />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
export interface ChatOptions {
|
||||
conversationId?: string
|
||||
parentMessageId?: string
|
||||
}
|
||||
|
||||
export interface ChatProps {
|
||||
dateTime: string
|
||||
message: string
|
||||
reversal?: boolean
|
||||
error?: boolean
|
||||
options?: ChatOptions
|
||||
}
|
||||
|
||||
export interface HistoryChatProps {
|
||||
title: string
|
||||
edit: boolean
|
||||
data: ChatProps[]
|
||||
}
|
||||
Reference in New Issue
Block a user