【代码优化】AI:聊天对话 index.vue 代码梳理 40%(message 部分)

pull/474/MERGE
YunaiV 2024-07-08 13:00:24 +08:00
parent 2b0789112f
commit 1a6afa3263
4 changed files with 65 additions and 102 deletions

View File

@ -12,6 +12,7 @@ export interface ChatConversationVO {
temperature: number // 温度参数
maxTokens: number // 单条回复的最大 Token 数量
maxContexts: number // 上下文的最大 Message 数量
createTime?: Date // 创建时间
// 额外字段
systemMessage?: string // 角色设定
modelName?: string // 模型名字

View File

@ -1,7 +1,7 @@
<template>
<div ref="messageContainer" style="height: 100%; overflow-y: auto; position: relative">
<div ref="messageContainer" class="h-100% overflow-y relative">
<div class="chat-list" v-for="(item, index) in list" :key="index">
<!-- 靠左 message -->
<!-- 靠左 messagesystemassistant 类型 -->
<div class="left-message message-item" v-if="item.type !== 'user'">
<div class="avatar">
<el-avatar :src="roleAvatar" />
@ -14,16 +14,16 @@
<MarkdownView class="left-text" :content="item.content" />
</div>
<div class="left-btns">
<el-button class="btn-cus" link @click="noCopy(item.content)">
<el-button class="btn-cus" link @click="copyContent(item.content)">
<img class="btn-image" src="@/assets/ai/copy.svg" />
</el-button>
<el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)">
<img class="btn-image" src="@/assets/ai/delete.svg" style="height: 17px" />
<img class="btn-image h-17px" src="@/assets/ai/delete.svg" />
</el-button>
</div>
</div>
</div>
<!-- 靠右 message -->
<!-- 靠右 messageuser 类型 -->
<div class="right-message message-item" v-if="item.type === 'user'">
<div class="avatar">
<el-avatar :src="userAvatar" />
@ -36,15 +36,11 @@
<div class="right-text">{{ item.content }}</div>
</div>
<div class="right-btns">
<el-button class="btn-cus" link @click="noCopy(item.content)">
<el-button class="btn-cus" link @click="copyContent(item.content)">
<img class="btn-image" src="@/assets/ai/copy.svg" />
</el-button>
<el-button class="btn-cus" link @click="onDelete(item.id)">
<img
class="btn-image"
src="@/assets/ai/delete.svg"
style="height: 17px; margin-right: 12px"
/>
<img class="btn-image h-17px mr-12px" src="@/assets/ai/delete.svg" />
</el-button>
<el-button class="btn-cus" link @click="onRefresh(item)">
<el-icon size="17"><RefreshRight /></el-icon>
@ -63,23 +59,25 @@
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { formatDate } from '@/utils/formatTime'
import MarkdownView from '@/components/MarkdownView/index.vue'
import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
import { useClipboard } from '@vueuse/core'
import { PropType } from 'vue'
import { ArrowDownBold, Edit, RefreshRight } from '@element-plus/icons-vue'
import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
import { ChatConversationVO } from '@/api/ai/chat/conversation'
import { useUserStore } from '@/store/modules/user'
import userAvatarDefaultImg from '@/assets/imgs/avatar.gif'
import roleAvatarDefaultImg from '@/assets/ai/gpt.svg'
const message = useMessage() //
const { copy } = useClipboard() // copy
// ()
const userStore = useUserStore()
// ()
const messageContainer: any = ref(null)
const isScrolling = ref(false) //
const userStore = useUserStore()
const userAvatar = computed(() => userStore.user.avatar ?? userAvatarDefaultImg)
const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg)
@ -95,12 +93,16 @@ const props = defineProps({
}
})
const { list } = toRefs(props) //
const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // emits
// ============ ==============
/** 滚动到底部 */
const scrollToBottom = async (isIgnore?: boolean) => {
// 使 nextTick dom
await nextTick(() => {
// TODO @fannextick idea 绿线
//使nexttickdom
if (isIgnore || !isScrolling.value) {
messageContainer.value.scrollTop =
messageContainer.value.scrollHeight - messageContainer.value.offsetHeight
@ -122,75 +124,48 @@ function handleScroll() {
}
}
/**
* 复制
*/
const noCopy = async (content) => {
copy(content)
ElMessage({
message: '复制成功!',
type: 'success'
})
}
/**
* 删除
*/
const onDelete = async (id) => {
// message
await ChatMessageApi.deleteChatMessage(id)
ElMessage({
message: '删除成功!',
type: 'success'
})
//
emits('onDeleteSuccess')
}
/**
* 刷新
*/
const onRefresh = async (message: ChatMessageVO) => {
emits('onRefresh', message)
}
/**
* 编辑
*/
const onEdit = async (message: ChatMessageVO) => {
emits('onEdit', message)
}
/**
* 回到底部
*/
/** 回到底部 */
const handleGoBottom = async () => {
const scrollContainer = messageContainer.value
scrollContainer.scrollTop = scrollContainer.scrollHeight
}
/**
* 回到顶部
*/
/** 回到顶部 */
const handlerGoTop = async () => {
const scrollContainer = messageContainer.value
scrollContainer.scrollTop = 0
}
// list
// TODO @fan
const { list, conversationId } = toRefs(props)
watch(list, async (newValue, oldValue) => {
console.log('watch list', list)
})
defineExpose({ scrollToBottom, handlerGoTop }) // parent
// parent
defineExpose({ scrollToBottom, handlerGoTop })
// ============ ==============
// emits
const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit'])
/** 复制 */
const copyContent = async (content) => {
await copy(content)
message.success('复制成功!')
}
// onMounted
/** 删除 */
const onDelete = async (id) => {
// message
await ChatMessageApi.deleteChatMessage(id)
message.success('删除成功!')
//
emits('onDeleteSuccess')
}
/** 刷新 */
const onRefresh = async (message: ChatMessageVO) => {
emits('onRefresh', message)
}
/** 编辑 */
const onEdit = async (message: ChatMessageVO) => {
emits('onEdit', message)
}
/** 初始化 */
onMounted(async () => {
messageContainer.value.addEventListener('scroll', handleScroll)
})
@ -199,15 +174,7 @@ onMounted(async () => {
<style scoped lang="scss">
.message-container {
position: relative;
//top: 0;
//bottom: 0;
//left: 0;
//right: 0;
//width: 100%;
//height: 100%;
overflow-y: scroll;
//padding: 0 15px;
//z-index: -1;
}
//
@ -231,11 +198,6 @@ onMounted(async () => {
justify-content: flex-start;
}
.avatar {
//height: 170px;
//width: 170px;
}
.message {
display: flex;
flex-direction: column;
@ -272,7 +234,6 @@ onMounted(async () => {
color: #fff;
display: inline;
background-color: #267fff;
color: #fff;
box-shadow: 0 0 0 1px #267fff;
border-radius: 10px;
padding: 10px;

View File

@ -1,31 +1,35 @@
<!-- 消息列表为空时展示 prompt 列表 -->
<template>
<div class="chat-empty">
<!-- title -->
<!-- title -->
<div class="center-container">
<div class="title"> AI</div>
<div class="title"> AI</div>
<div class="role-list">
<div class="role-item" v-for="prompt in promptList" :key="prompt.prompt" @click="handlerPromptClick(prompt)">
{{prompt.prompt}}
<div
class="role-item"
v-for="prompt in promptList"
:key="prompt.prompt"
@click="handlerPromptClick(prompt)"
>
{{ prompt.prompt }}
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const promptList = ref<any[]>() //
promptList.value = [
const promptList = [
{
"prompt": "今天气怎么样?",
prompt: '今天气怎么样?'
},
{
"prompt": "写一首好听的诗歌?",
prompt: '写一首好听的诗歌?'
}
]
] // prompt
const emits = defineEmits(['onPrompt'])
/** 选中 prompt 点击 */
const handlerPromptClick = async ({ prompt }) => {
emits('onPrompt', prompt)
}

View File

@ -9,13 +9,10 @@
</div>
</div>
</template>
<script setup lang="ts">
const emits = defineEmits(['onNewConversation'])
/**
* 新建 conversation 聊天对话
*/
/** 新建 conversation 聊天对话 */
const handlerNewChat = () => {
emits('onNewConversation')
}