Files
YGChatCS/pages/chat/ChatMainList.vue
2025-06-29 23:41:37 +08:00

420 lines
9.9 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">
<!-- 顶部的背景 -->
<chat-top-bg-img class="chat-container-bg"></chat-top-bg-img>
<!-- 顶部自定义导航栏 -->
<view class="nav-bar-container" :style="{
paddingTop: statusBarHeight + 'px',
backgroundColor: navBgColor,
}">
<chat-top-nav-bar @openDrawer="openDrawer"></chat-top-nav-bar>
</view>
<view class="chat-container-msg-list">
<!-- logo栏 -->
<chat-top-banner class="chat-container-top-bannar"></chat-top-banner>
<!-- 消息列表可滚动区域 -->
<scroll-view
scroll-y
:scroll-into-view="lastMsgId"
:scroll-with-animation="true"
class="area-msg-list"
>
<view style="padding: 6px 12px;">
<OneFeelMK001></OneFeelMK001>
</view>
<view style="padding: 6px 12px;">
<OneFeelMK001></OneFeelMK001>
</view>
<view style="padding: 6px 12px;">
<OneFeelMK001></OneFeelMK001>
</view>
<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 message-item-ai" :text="item.msg">
<image v-if="item.msgContent && item.msgContent.type === MessageType.IMAGE" src="/static/logo.png" style="width: 100px;height: 100px;"></image>
<OneFeelMK001></OneFeelMK001>
</ChatCardAI>
</template>
<template v-else-if="item.msgType === MessageRole.ME">
<ChatCardMine class="message-item message-item-mine" :text="item.msg">
</ChatCardMine>
</template>
<template v-else>
<text class="message-item message-item-other">{{item.msg}}</text>
</template>
</view>
<!-- 底部锚点用于滚动到底部 -->
<view :id="lastMsgId"></view>
</scroll-view>
<!-- 输入框区域 -->
<view class="footer-area">
<ChatMoreTips @replySent="handleReply"></ChatMoreTips>
<ChatQuickAccess @replySent="handleReply"></ChatQuickAccess>
<view class="area-input">
<!-- 发送语音 -->
<view class="input-container-voice">
<image src='/static/input_voice_icon.png'></image>
</view>
<!-- 输入框 -->
<textarea
class="textarea"
type="text"
placeholder="快速订票,呼叫服务"
cursor-spacing="65"
confirm-type='done'
v-model="inputMessage"
@confirm="sendMessage"
@touchend="handleNoHideKeyboard"
:confirm-hold="true"
auto-height
:show-confirm-bar='false'
:hold-keyboard="holdKeyboard"
maxlength="300"
/>
<view class="input-container-send" @click="sendMessage">
<image src='/static/input_send_icon.png'></image>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onMounted, getCurrentInstance, nextTick } from 'vue'
import { ref, watch } 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 ChatQuickAccess from './ChatQuickAccess.vue';
import ChatMoreTips from './ChatMoreTips.vue';
import { MessageRole, ChatModel, MessageType } from '../../model/ChatModel';
import OneFeelMK001 from '../module/OneFeelMK001.vue';
const instance = getCurrentInstance(); // 获取当前组件实例
// 导航栏相关
const statusBarHeight = ref(20);
const navBgColor = ref('rgba(66, 173, 249, 0)');
const navOpacity = ref(0);
const timer = ref(null)
const holdKeyboard = ref(false) // focus时点击页面的时候不收起键盘
const holdKeyboardFlag = ref(true) // 是否在键盘弹出,点击界面时关闭键盘
const chatMsgList = ref<ChatModel[]>([])
const inputMessage = ref('')
// 锚点ID控制滚动位置
const lastMsgId = ref('anchor-bottom');
// 针对小程序键盘收起问题处理
// #ifdef MP-WEIXIN
holdKeyboard.value = true
// #endif
// 滚动临界值根据实际banner高度调整
const SCROLL_THRESHOLD = 200;
const handleScroll = (e) => {
const scrollTopVal = e.detail.scrollTop;
// 计算透明度 (0-1之间)
let opacity = Math.min(scrollTopVal / SCROLL_THRESHOLD, 1);
// 如果滚动到接近顶部透明度设为0
if (scrollTopVal < 10) {
opacity = 0;
}
navOpacity.value = opacity;
navBgColor.value = `rgba(66, 173, 249, ${opacity})`;
};
// 打开抽屉
const emits = defineEmits(['openDrawer'])
const openDrawer = () => {
emits('openDrawer')
console.log('=============打开抽屉')
}
const handleReply = (text: string) => {
loadMessage(text)
scrollToBottom()
};
onLoad(() => {
uni.getSystemInfo({
success: (res) => {
statusBarHeight.value = res.statusBarHeight || 20;
}
});
});
onMounted(() => {
initData()
})
const initData = () => {
const msg: ChatModel = {
msgId: `msg_${0}`,
msgType: MessageRole.AI,
msg: '查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!',
}
chatMsgList.value.push(msg)
}
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 = () => {
if (!inputMessage.value.trim()) return;
handleNoHideKeyboard()
// 发送消息代码
loadMessage(inputMessage.value)
inputMessage.value = ''
scrollToBottom()
}
const loadMessage = (text: string) => {
const newMsg: ChatModel = {
msgId: `msg_${chatMsgList.value.length}`,
msgType: MessageRole.ME,
msg: text,
msgContent: {
type: MessageType.TEXT,
text: text
}
}
chatMsgList.value.push(newMsg)
let type = chatMsgList.value.length % 3 === 0
const newMsgAI: ChatModel = {
msgId: `msg_${chatMsgList.value.length}`,
msgType: MessageRole.AI,
msg: `我是ai,你输入的内容是:${text}`,
msgContent: {
type: type ? MessageType.IMAGE : MessageType.TEXT,
url: ''
}
}
chatMsgList.value.push(newMsgAI)
console.log("发送的新消息:",JSON.stringify(newMsg))
}
const scrollToBottom = () => {
// 短暂指向最新消息的ID触发滚动
lastMsgId.value = `${chatMsgList.value[chatMsgList.value.length - 1].msgId}`;
// 等待DOM更新后切回底部锚点确保下次能继续滚动到底
nextTick(() => {
lastMsgId.value = 'anchor-bottom';
});
}
</script>
<style lang="scss" scoped>
.chat-container {
width: 100vw;
height: 100vh;
background-color: #E9F3F7;
display: flex;
flex-direction: column;
overflow: hidden !important;
position: relative;
.chat-container-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 0;
height: 270px;
background: linear-gradient( 180deg, #42ADF9 0%, #6CD1FF 51%, #E9F3F7 99%);
}
/* 顶部导航栏样式 */
.nav-bar-container {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
transition: all 0.3s ease;
}
.chat-container-msg-list {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
z-index: 1;
overflow: hidden;
}
.chat-container-top-bannar {
width: 100vw;
flex-shrink: 0;
touch-action: none;
}
.area-msg-list {
width: 100vw;
flex: 1;
overflow-y: auto;
min-height: 0;
padding: 4px 0 0;
overscroll-behavior: contain; /* 阻止滚动穿透 */
-webkit-overflow-scrolling: touch;
display: flex;
flex-direction: column;
.area-msg-list-content {
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.message-item {
display: flex;
}
.message-item-ai {
justify-content: flex-start;
}
.message-item-mine {
justify-content: flex-end;
}
.message-item-other {
justify-content: center;
background-color: white;
margin: 6px 12px;
padding: 8px 24px;
border-radius: 4px;
font-size: 14px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
text {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #333333;
}
}
}
}
.footer-area {
width: 100vw;
flex-shrink: 0;
padding: 4px 0 24px 0;
background-color: #E9F3F7;
touch-action: pan-x; /* 仅允许横向触摸滚动 */
overflow-x: auto; /* 允许横向滚动 */
overflow-y: hidden; /* 禁止垂直滚动 */
}
.area-input {
display: flex;
align-items: center;
border-radius: 22px;
background-color: #FFFFFF;
box-shadow: 0px 0px 20px 0px rgba(52,25,204,0.05);
margin: 0 12px;
.input-container-voice {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
flex-shrink: 0;
align-self: flex-end;
image {
width: 22px;
height: 22px;
}
}
.input-container-send {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
flex-shrink: 0;
align-self: flex-end;
image {
width: 28px;
height: 28px;
}
}
.textarea {
flex: 1;
max-height: 92px;
min-height: 22px;
font-size: 16px;
line-height: 22px;
margin-bottom: 2px;
align-items: center;
}
}
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
color: transparent;
}
</style>