Files
YGChatCS/pages/chat/ChatMainList.vue
2025-07-26 18:16:14 +08:00

306 lines
8.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="chat-container" @touchend="handleTouchEnd">
<!-- 顶部的背景 -->
<ChatTopBgImg class="chat-container-bg"></ChatTopBgImg>
<view class="chat-content">
<!-- 顶部自定义导航栏 -->
<view class="nav-bar-container" :style="{
paddingTop: statusBarHeight + 'px',
}">
<ChatTopNavBar @openDrawer="openDrawer"></ChatTopNavBar>
</view>
<!-- 消息列表可滚动区域 -->
<scroll-view
scroll-y
:scroll-into-view="lastMsgId"
:scroll-with-animation="true"
class="area-msg-list"
>
<!-- logo栏 -->
<ChatTopBanner class="chat-container-top-bannar"></ChatTopBanner>
<view class="area-msg-list-content" v-for="item in chatMsgList" :key="item.msgId" :id="item.msgId">
<template v-if="item.msgType === MessageRole.AI">
<ChatCardAI class="message-item-ai" :text="item.msg">
</ChatCardAI>
</template>
<template v-else-if="item.msgType === MessageRole.ME">
<ChatCardMine class="message-item-mine" :text="item.msg">
</ChatCardMine>
</template>
<template v-else>
<ChatCardOther class="message-item-other" :text="item.msg">
<OneFeelMK001/>
<OneFeelMK002/>
</ChatCardOther>
</template>
</view>
<!-- 底部锚点用于滚动到底部 -->
<view :id="lastMsgId"></view>
</scroll-view>
<!-- 输入框区域 -->
<view class="footer-area">
<ChatMoreTips @replySent="handleReply" :itemList="guideWords"></ChatMoreTips>
<ChatQuickAccess @replySent="handleReply"></ChatQuickAccess>
<ChatInputArea
v-model:inputMessage="inputMessage"
:holdKeyboard="holdKeyboard"
@send="sendMessage"
@noHideKeyboard="handleNoHideKeyboard"
/>
</view>
</view>
</view>
</template>
<script setup >
import { onMounted, getCurrentInstance, nextTick } from 'vue'
import { ref } from 'vue'
import { defineEmits } from 'vue'
import { onLoad } from '@dcloudio/uni-app';
import ChatTopBanner from './ChatTopBanner.vue';
import ChatTopBgImg from './ChatTopBgImg.vue';
import ChatTopNavBar from './ChatTopNavBar.vue';
import ChatCardAI from './ChatCardAI.vue';
import ChatCardMine from './ChatCardMine.vue';
import ChatCardOther from './ChatCardOther.vue';
import ChatQuickAccess from './ChatQuickAccess.vue';
import ChatMoreTips from './ChatMoreTips.vue';
import ChatInputArea from './ChatInputArea.vue'
import CommandWrapper from '@/components/CommandWrapper/index.vue'
import { MessageRole, ChatModel, MessageType } from '../../model/ChatModel';
import OneFeelMK001 from '../module/OneFeelMK001.vue';
import OneFeelMK002 from '../module/OneFeelMK002.vue';
import request from '../../request/base/request';
import { agentChatStream } from '../../request/api/AgentChatStream';
import { mainPageData } from '../../request/api/MainPageData';
// 导航栏相关
const statusBarHeight = ref(20);
const timer = ref(null)
const holdKeyboard = ref(false) // focus时点击页面的时候不收起键盘
const holdKeyboardFlag = ref(true) // 是否在键盘弹出,点击界面时关闭键盘
const chatMsgList = ref([])
const inputMessage = ref('')
const mainPageDataModel = ref({})
const guideWords = ref([])
// 锚点ID控制滚动位置
const lastMsgId = ref('anchor-bottom');
// 打开抽屉
const emits = defineEmits(['openDrawer'])
const openDrawer = () => {
emits('openDrawer')
console.log('=============打开抽屉')
}
const handleReply = (text) => {
loadMessage(text)
scrollToBottom()
};
onLoad(() => {
uni.getSystemInfo({
success: (res) => {
statusBarHeight.value = res.statusBarHeight || 20;
}
});
});
onMounted(() => {
// getMainPageData()
initData()
})
const getMainPageData = async() => {
mainPageDataModel.value = await mainPageData()
guideWords.value = mainPageDataModel.value.guideWords
}
const initData = () => {
const msg = {
msgId: `msg_${0}`,
msgType: MessageRole.AI,
msg: '查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!',
}
chatMsgList.value.push(msg)
const msg1 = {
msgId: `msg_${1}`,
msgType: MessageRole.OTHER,
msg: '',
}
chatMsgList.value.push(msg1)
}
const handleTouchEnd = () => {
// #ifdef MP-WEIXIN
clearTimeout(timer.value)
timer.value = setTimeout(() => {
// 键盘弹出时点击界面则关闭键盘
if (handleNoHideKeyboard) {
uni.hideKeyboard()
}
holdKeyboardFlag.value = true
}, 50)
// #endif
}
// 点击输入框、发送按钮时,不收键盘
const handleNoHideKeyboard = () => {
// #ifdef MP-WEIXIN
holdKeyboardFlag.value = false
// #endif
}
// 发送消息
const sendMessage = (message) => {
console.log("inputMessage list:", message)
if (!message.trim()) return;
handleNoHideKeyboard()
// 发送消息代码
loadMessage(message)
inputMessage.value = ''
scrollToBottom()
}
const loadMessage = (text) => {
const newMsg = {
msgId: `msg_${chatMsgList.value.length}`,
msgType: MessageRole.ME,
msg: text,
msgContent: {
type: MessageType.TEXT,
text: text
}
}
chatMsgList.value.push(newMsg)
console.log("发送的新消息:",JSON.stringify(newMsg))
sendChat(text)
}
const scrollToBottom = () => {
// 短暂指向最新消息的ID触发滚动
lastMsgId.value = `${chatMsgList.value[chatMsgList.value.length - 1].msgId}`;
// 等待DOM更新后切回底部锚点确保下次能继续滚动到底
nextTick(() => {
nextTick(() => {
lastMsgId.value = 'anchor-bottom';
});
});
}
let loadingTimer = null;
let typeWriterTimer = null;
let aiMsgBuffer = ''; // 全局缓冲区
let isTyping = false; // 是否正在打字
const sendChat = (text) => {
console.log('=============会话测试')
const args = {
conversationId: "1931957498711957505",
agentId: "1",
messageType: 0,
messageContent: text
}
// 1. 插入AI消息
const aiMsg = {
msgId: `msg_${chatMsgList.value.length}`,
msgType: MessageRole.AI,
msg: '加载中.',
msgContent: {
type: MessageType.TEXT,
url: ''
}
}
chatMsgList.value.push(aiMsg)
const aiMsgIndex = chatMsgList.value.length - 1
// 动态加载中动画
let dotCount = 1;
loadingTimer && clearInterval(loadingTimer);
loadingTimer = setInterval(() => {
dotCount = dotCount % 3 + 1;
chatMsgList.value[aiMsgIndex].msg = '加载中' + '.'.repeat(dotCount);
}, 400);
aiMsgBuffer = '';
isTyping = false;
if (typeWriterTimer) {
clearTimeout(typeWriterTimer);
typeWriterTimer = null;
}
// 2. 流式接收内容
agentChatStream(args, (chunk) => {
console.log('分段内容:', chunk)
if (chunk && chunk.content) {
// 收到内容,停止动画
if (loadingTimer) {
clearInterval(loadingTimer);
loadingTimer = null;
}
// 把新内容追加到缓冲区
aiMsgBuffer += chunk.content;
// 启动打字机(只启动一次)
if (!isTyping) {
isTyping = true;
chatMsgList.value[aiMsgIndex].msg = '';
typeWriter();
}
}
if (chunk && chunk.finish) {
// 结尾处理:确保剩余内容全部输出
const finishInterval = setInterval(() => {
if (aiMsgBuffer.length === 0) {
clearInterval(finishInterval);
isTyping = false;
scrollToBottom();
}
}, 50);
}
});
// 打字机函数
function typeWriter() {
if (aiMsgBuffer.length > 0) {
chatMsgList.value[aiMsgIndex].msg += aiMsgBuffer[0];
aiMsgBuffer = aiMsgBuffer.slice(1);
nextTick(() => {
scrollToBottom();
});
typeWriterTimer = setTimeout(typeWriter, 30);
} else {
// 等待新内容到来,不结束
typeWriterTimer = setTimeout(typeWriter, 30);
}
}
}
</script>
<style lang="scss" scoped>
@import "styles/ChatMainList.scss";
</style>