1223 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			1223 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | ||
|   <view class="home-container">
 | ||
|     <header-bar
 | ||
|       title="源小新"
 | ||
|       leftIcon="list"
 | ||
|       @leftClick="handleLeftClick"
 | ||
|     ></header-bar>
 | ||
| 
 | ||
|     <!-- 首页 -->
 | ||
|     <view class="main-content" v-if="!isChat">
 | ||
|       <view class="welcome-message">
 | ||
|         <image
 | ||
|           class="avatar"
 | ||
|           src="/static/common/images/images_logo.png"
 | ||
|         ></image>
 | ||
| 
 | ||
|         <text class="message-text"
 | ||
|           >Hi~ 我是源小新,你们的AI校园助手,非常高兴认识您,我可以为你答疑解惑。</text
 | ||
|         >
 | ||
|       </view>
 | ||
| 
 | ||
|       <view class="qa-section">
 | ||
|         <view class="qa-header">
 | ||
|           <text class="qa-title">大家都在问</text>
 | ||
|           <view class="more-link">
 | ||
|             <text class="more-text">换一批</text>
 | ||
|           </view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <view class="qa-list">
 | ||
|           <view
 | ||
|             class="qa-item"
 | ||
|             v-for="(item, index) in questionList"
 | ||
|             :key="index"
 | ||
|           >
 | ||
|             <text class="qa-question">{{ item }}</text>
 | ||
|           </view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <button class="chat-button" @click="handleStartChat">
 | ||
|           <image
 | ||
|             class="chat-icon"
 | ||
|             src="/static/common/images/icon_chat.png"
 | ||
|           ></image>
 | ||
|           <text class="chat-text">开启对话</text>
 | ||
|         </button>
 | ||
|       </view>
 | ||
| 
 | ||
|       <view class="feature-grid">
 | ||
|         <view
 | ||
|           class="feature-item"
 | ||
|           v-for="(item, index) in features"
 | ||
|           :key="index"
 | ||
|           :style="{
 | ||
|             background: item.background,
 | ||
|           }"
 | ||
|           @click="handleFeatureClick(item)"
 | ||
|         >
 | ||
|           <image :src="item.icon" class="feature-icon"></image>
 | ||
|           <text class="feature-text">{{ item.title }}</text>
 | ||
|         </view>
 | ||
|       </view>
 | ||
|     </view>
 | ||
| 
 | ||
|     <!-- 对话 -->
 | ||
|     <view class="chat-container" v-if="isChat">
 | ||
|       <scroll-view
 | ||
|         class="chat-scroll"
 | ||
|         scroll-y
 | ||
|         :scroll-into-view="scrollToView"
 | ||
|         scroll-with-animation
 | ||
|         :scroll-top="scrollTop"
 | ||
|         :show-scrollbar="true"
 | ||
|         @scrolltolower="onScrollToLower"
 | ||
|         @scrolltoupper="onScrollToUpper"
 | ||
|       >
 | ||
|         <!-- 头部卡片 -->
 | ||
|         <!-- 后端让先注释 -->
 | ||
|         <!-- <view class="chat-card">
 | ||
|           <view class="chat-card-title">源小新AI校园小助手</view>
 | ||
|           <view class="chat-card-desc"
 | ||
|             >我是你们的AI校园助手,我可以为您答疑解惑。</view
 | ||
|           >
 | ||
|           <view class="chat-card-questions">
 | ||
|             <view class="question-item">学校哪些专业比较好?</view>
 | ||
|             <view class="question-item">如何报考学校综合素质评价招生?</view>
 | ||
|             <view class="question-item">学校有那些专业?</view>
 | ||
|             <view class="question-item"
 | ||
|               >学校在录取时有没有一些专业会有特殊...</view
 | ||
|             >
 | ||
|             <view class="question-item">我什么时候能够知道自己是否被录取?</view>
 | ||
|           </view>
 | ||
|         </view> -->
 | ||
|         <view class="chat-card">
 | ||
|           <view class="chat-card-title">源小新AI校园小助手</view>
 | ||
|           <view class="chat-card-desc"
 | ||
|             >我是你们的AI校园助手,我可以为你答疑解惑。</view
 | ||
|           >
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 对话内容区域 -->
 | ||
|         <view class="chat-content">
 | ||
|           <!-- 上拉刷新loading -->
 | ||
|           <view class="loading-more" v-if="isLoading">
 | ||
|             <u-loading mode="circle" color="#4370fe"></u-loading>
 | ||
|           </view>
 | ||
| 
 | ||
|           <!-- 到顶部提示 -->
 | ||
|           <view class="no-more-data" v-if="noMoreData">
 | ||
|             <text>已经到顶了</text>
 | ||
|           </view>
 | ||
| 
 | ||
|           <!-- 消息循环渲染 -->
 | ||
|           <block
 | ||
|             v-for="(message, index) in messageGroups"
 | ||
|             :key="'msg-' + index"
 | ||
|           >
 | ||
|             <!-- 时间 -->
 | ||
|             <view class="message-time" v-if="message.timeLabel !== 0">
 | ||
|               {{ message.displayTime }}
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 用户消息 -->
 | ||
|             <view
 | ||
|               class="message-right"
 | ||
|               v-if="message.interactMode === 0"
 | ||
|               :id="'msg-' + message.id"
 | ||
|             >
 | ||
|               <view class="message-content">
 | ||
|                 <text>{{ message.message }}</text>
 | ||
|               </view>
 | ||
|               <image
 | ||
|                 class="user-avatar"
 | ||
|                 src="/static/common/images/avatar.png"
 | ||
|                 mode="scaleToFill"
 | ||
|               />
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- AI消息 -->
 | ||
|             <view
 | ||
|               class="message-left"
 | ||
|               v-if="message.interactMode === 1"
 | ||
|               :id="'msg-' + message.id"
 | ||
|             >
 | ||
|               <image
 | ||
|                 class="ai-avatar"
 | ||
|                 src="/static/common/images/avatar_ai.png"
 | ||
|                 mode="scaleToFill"
 | ||
|               />
 | ||
|               <view class="message-content">
 | ||
|                 <!-- 加载动画 -->
 | ||
|                 <view v-if="message.isLoading" class="loading-dots">
 | ||
|                   <view class="dot"></view>
 | ||
|                   <view class="dot"></view>
 | ||
|                   <view class="dot"></view>
 | ||
|                 </view>
 | ||
|                 <!-- 正常消息内容 -->
 | ||
|                 <markdown-viewer v-else :content="message.message" />
 | ||
|               </view>
 | ||
|             </view>
 | ||
|           </block>
 | ||
|         </view>
 | ||
|       </scroll-view>
 | ||
| 
 | ||
|       <!-- 底部工具栏 -->
 | ||
|       <view class="chat-footer">
 | ||
|         <!-- 悬浮工具栏 -->
 | ||
|         <!-- <view class="floating-tabs">
 | ||
|           <view
 | ||
|             class="tab-item"
 | ||
|             @click="handleFeatureClick({ title: '招生在线' })"
 | ||
|           >
 | ||
|             <image
 | ||
|               class="tab-icon"
 | ||
|               src="/static/common/images/icon_admissions2.png"
 | ||
|               mode="scaleToFill"
 | ||
|             />
 | ||
| 
 | ||
|             <text>招生在线</text>
 | ||
|           </view>
 | ||
|           <view
 | ||
|             class="tab-item"
 | ||
|             @click="handleFeatureClick({ title: '留言板' })"
 | ||
|           >
 | ||
|             <image
 | ||
|               class="tab-icon"
 | ||
|               src="/static/common/images/icon_messageBoard2.png"
 | ||
|               mode="scaleToFill"
 | ||
|             />
 | ||
|             <text>留言板</text>
 | ||
|           </view>
 | ||
|           <view
 | ||
|             class="tab-item"
 | ||
|             @click="handleFeatureClick({ title: '电话咨询' })"
 | ||
|           >
 | ||
|             <image
 | ||
|               class="tab-icon"
 | ||
|               src="/static/common/images/icon_phone2.png"
 | ||
|               mode="scaleToFill"
 | ||
|             />
 | ||
|             <text>电话咨询</text>
 | ||
|           </view>
 | ||
|         </view> -->
 | ||
| 
 | ||
|         <view class="input-container">
 | ||
|           <view class="input-area">
 | ||
|             <input
 | ||
|               v-model="messageValue"
 | ||
|               type="text"
 | ||
|               class="chat-input"
 | ||
|               :focus="true"
 | ||
|               placeholder="请输入内容"
 | ||
|               placeholder-style="color: #adadad;"
 | ||
|               @confirm="sendMessageFn"
 | ||
|             />
 | ||
|             <view class="send-btn" @click="sendMessageFn">
 | ||
|               <image
 | ||
|                 class="send-icon"
 | ||
|                 src="/static/common/images/icon_send.png"
 | ||
|                 mode="scaleToFill"
 | ||
|               />
 | ||
|             </view>
 | ||
|           </view>
 | ||
|         </view>
 | ||
|       </view>
 | ||
|     </view>
 | ||
| 
 | ||
|     <!-- 对话弹出层 -->
 | ||
|     <chat-history
 | ||
|       :show.sync="popupShow"
 | ||
|       :chat-history-list3="chatHistoryList3"
 | ||
|       :user-name="vuex_user ? vuex_user.Name : ''"
 | ||
|       @select-conversation="handleSelectConversation"
 | ||
|       @create-conversation="handleCreateConversation"
 | ||
|     ></chat-history>
 | ||
| 
 | ||
|     <!-- 完善信息弹出层 -->
 | ||
|     <perfect-info :show.sync="perfectInfoShow"></perfect-info>
 | ||
| 
 | ||
|     <!-- 咨询电话弹出层 -->
 | ||
|     <advice-phone :show.sync="advicePhoneShow"></advice-phone>
 | ||
| 
 | ||
|     <!-- 提示 -->
 | ||
|     <u-toast ref="uToast" />
 | ||
|   </view>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import MarkdownViewer from "@/components/markdown-viewer/markdown-viewer";
 | ||
| import CustomTabBar from "@/components/custom-tab-bar/custom-tab-bar.vue";
 | ||
| import AdvicePhone from "@/components/AdvicePhone.vue";
 | ||
| import PerfectInfo from "@/components/PerfectInfo.vue";
 | ||
| import ChatHistory from "@/components/ChatHistory.vue"; // 导入新组件
 | ||
| import HeaderBar from "@/components/HeaderBar.vue"; // 导入头部组件
 | ||
| 
 | ||
| export default {
 | ||
|   components: {
 | ||
|     CustomTabBar,
 | ||
|     AdvicePhone,
 | ||
|     MarkdownViewer,
 | ||
|     PerfectInfo,
 | ||
|     ChatHistory,
 | ||
|     HeaderBar, // 注册头部组件
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       isChat: false,
 | ||
|       currentConversationId: "",
 | ||
|       advicePhoneShow: false,
 | ||
|       perfectInfoShow: false,
 | ||
|       isLoading: false, // loadingMore
 | ||
|       isLoadingMore: false, // 是否正在加载更多的标志位
 | ||
|       noMoreData: false, // 是否已加载全部历史消息
 | ||
|       isSwitchingConversation: false, // 是否正在切换对话的标志位
 | ||
| 
 | ||
|       pageQuery: {
 | ||
|         PageIndex: 1,
 | ||
|         PageSize: 20,
 | ||
|       },
 | ||
| 
 | ||
|       questionList: [
 | ||
|         "学习哪些专业比较好?",
 | ||
|         "处于香港学校综合录取的城市招生?",
 | ||
|         "学校有新华专业?",
 | ||
|         "学校录取科目与有一些专业会有特殊...",
 | ||
|         "我什么时候能推知道自己是否被录取?",
 | ||
|       ],
 | ||
|       features: [
 | ||
|         {
 | ||
|           title: "在线咨询",
 | ||
|           icon: "/static/common/images/icon_admissions.png",
 | ||
|           path: "/pages/home/admissions/index",
 | ||
|           background: "linear-gradient(0deg, #F4FBFE 0%, #F4FBFE 100%)",
 | ||
|         },
 | ||
|         // {
 | ||
|         //   title: "留言板",
 | ||
|         //   icon: "/static/common/images/icon_messageBoard.png",
 | ||
|         //   path: "/pages/home/messageBoard/index",
 | ||
|         // },
 | ||
|         {
 | ||
|           title: "电话咨询",
 | ||
|           icon: "/static/common/images/icon_phone.png",
 | ||
|           background: "linear-gradient(0deg, #F4FBF9 0%, #F4FBF9 100%)",
 | ||
|         },
 | ||
|       ],
 | ||
|       popupShow: false,
 | ||
|       chatHistoryList3: [
 | ||
|         // {
 | ||
|         //   "id": "今天",
 | ||
|         //   "conversation": [
 | ||
|         //     {
 | ||
|         //       "title": "你好",
 | ||
|         //       "id": "0d03f23c-9c5b-45ac-ae95-be51e9383a62",
 | ||
|         //       "startTime": "2025-07-10T15:28:02.832316"
 | ||
|         //     },
 | ||
|         //     {
 | ||
|         //       "title": "你知道今年的录取分数线吗",
 | ||
|         //       "id": "771ab434-348c-4a35-bd03-adebc21436d8",
 | ||
|         //       "startTime": "2025-07-10T15:11:32.885001"
 | ||
|         //     }
 | ||
|         //   ]
 | ||
|         // },
 | ||
|         // {
 | ||
|         //   "id": "周三",
 | ||
|         //   "conversation": [
 | ||
|         //     {
 | ||
|         //       "title": "今天是周三",
 | ||
|         //       "id": "0d03f23c-9c5b-45ac-ae95-be51e9383a62",
 | ||
|         //       "startTime": "2025-07-09T15:28:02.832316"
 | ||
|         //     },
 | ||
|         //     {
 | ||
|         //       "title": "你知道今年的录取分数线吗",
 | ||
|         //       "id": "771ab434-348c-4a35-bd03-adebc21436d8",
 | ||
|         //       "startTime": "2025-07-09T15:11:32.885001"
 | ||
|         //     }
 | ||
|         //   ]
 | ||
|         // }
 | ||
|       ],
 | ||
|       activeIndex: 0,
 | ||
|       commonQuestions: [
 | ||
|         "新生报到流程",
 | ||
|         "如何报考学校综合合录取评澳市评招生?",
 | ||
|         "学校有新华专业?",
 | ||
|         "学校录取科目与有一些专业会有特殊...",
 | ||
|         "我什么时候能推知道自己是否被录取?",
 | ||
|       ],
 | ||
|       scrollToView: "",
 | ||
|       // 修改后的messageGroups数据结构
 | ||
|       messageGroups: [
 | ||
|         // {
 | ||
|         //   id: "9cac8661-bf09-4b63-ab15-1a0a36b921e0",
 | ||
|         //   message: "你知道今年的录取分数线吗",
 | ||
|         //   sendDate: "2025-07-10T15:11:32.886075",
 | ||
|         //   isSend: true,
 | ||
|         //   isRead: false,
 | ||
|         //   interactMode: 0,
 | ||
|         //   messageType: 0,
 | ||
|         //   timeLabel: 1,
 | ||
|         //   displayTime: "15:11",
 | ||
|         // },
 | ||
|         // {
 | ||
|         //   id: "02306fc3-c821-4a23-ad66-0bd77d154105",
 | ||
|         //   message:
 | ||
|         //     "目前,2025年的录取分数线还没有正式公布,因为学校需要等招生工作结束后才会公布最终的录取分数线。不过,你可以参考2023-2024年的录取分数线作为大致的参考。\n\n如果你想知道具体专业的录取分数线,可以告诉我你所在省份和科类(历史类或物理类),我可以根据往年数据给你一些参考建议。如果需要更准确的信息,建议关注江西新能源科技职业学院的官网(www.jxnee.edu.cn)或者直接联系招生办(电话:0790-6764666/6765666)。",
 | ||
|         //   sendDate: "2025-07-10T15:11:32.88644",
 | ||
|         //   isSend: true,
 | ||
|         //   isRead: false,
 | ||
|         //   interactMode: 1,
 | ||
|         //   messageType: 0,
 | ||
|         //   timeLabel: 0,
 | ||
|         //   displayTime: "",
 | ||
|         // },
 | ||
|       ],
 | ||
|       scrollTop: 0,
 | ||
| 
 | ||
|       messageValue: "", // 输入框内容
 | ||
|     };
 | ||
|   },
 | ||
|   watch: {
 | ||
|     // 确保消息更新后自动滚动到底部
 | ||
|     messageGroups: {
 | ||
|       handler() {
 | ||
|         // 只有在不是加载更多的情况下才滚动到底部
 | ||
|         if (!this.isLoadingMore) {
 | ||
|           this.scrollToBottom();
 | ||
|         }
 | ||
|       },
 | ||
|       deep: true,
 | ||
|     },
 | ||
| 
 | ||
|     vuex_user: {
 | ||
|       handler(newValue) {
 | ||
|         // console.log('vuex_user',newValue);
 | ||
|         if (newValue && newValue.Sex === 2) {
 | ||
|           // this.perfectInfoShow = true;
 | ||
|         }
 | ||
|       },
 | ||
|       immediate: true, // 这会使得组件创建时立即执行一次handler
 | ||
|       deep: true,
 | ||
|     },
 | ||
|   },
 | ||
|   onLoad() {},
 | ||
|   methods: {
 | ||
|     handleLeftClick() {
 | ||
|       this.$u.api.GetConversationPage().then((res) => {
 | ||
|         this.chatHistoryList3 = res.data;
 | ||
|         if (this.chatHistoryList3.length > 0) {
 | ||
|           this.chatHistoryList3 = res.data.map((group) => {
 | ||
|             // 对每个组的conversation数组进行倒序排序
 | ||
|             return {
 | ||
|               ...group,
 | ||
|               conversation: group.conversation.sort((a, b) => {
 | ||
|                 // 将日期字符串转换为时间戳并比较(倒序)
 | ||
|                 return (
 | ||
|                   new Date(b.startTime).getTime() -
 | ||
|                   new Date(a.startTime).getTime()
 | ||
|                 );
 | ||
|               }),
 | ||
|             };
 | ||
|           });
 | ||
|         }
 | ||
|         console.log("this.chatHistoryList3", this.chatHistoryList3);
 | ||
| 
 | ||
|         this.popupShow = true;
 | ||
|       });
 | ||
|     },
 | ||
| 
 | ||
|     scrollToBottom() {
 | ||
|       // 如果正在加载更多,不执行滚动
 | ||
|       if (this.isLoadingMore) return;
 | ||
| 
 | ||
|       // 使用nextTick确保DOM已更新
 | ||
|       this.$nextTick(() => {
 | ||
|         // setTimeout(() => {
 | ||
|         const query = uni.createSelectorQuery().in(this);
 | ||
|         query
 | ||
|           .select(".chat-content")
 | ||
|           .boundingClientRect((data) => {
 | ||
|             if (data) {
 | ||
|               console.log("chat-content高度:", data.height);
 | ||
| 
 | ||
|               // 同时设置scrollTop和scrollToView
 | ||
|               this.scrollTop = data.height + 200;
 | ||
|               // if (this.messageGroups.length > 0) {
 | ||
|               //   const lastMessage =
 | ||
|               //     this.messageGroups[this.messageGroups.length - 1];
 | ||
|               //   this.scrollToView = "msg-" + lastMessage.id;
 | ||
|               //   console.log("设置scrollToView:", this.scrollToView);
 | ||
|               // }
 | ||
|             }
 | ||
|           })
 | ||
|           .exec();
 | ||
|         // }, 300);
 | ||
|       });
 | ||
|     },
 | ||
|     handleFeatureClick(item) {
 | ||
|       if (item.title === "电话咨询") {
 | ||
|         this.advicePhoneShow = true;
 | ||
|         return;
 | ||
|       } else if (item.path) {
 | ||
|         uni.navigateTo({
 | ||
|           url: item.path,
 | ||
|         });
 | ||
|         return;
 | ||
|       } else {
 | ||
|         this.$refs.uToast.show({
 | ||
|           title: "暂未开放",
 | ||
|           type: "warning",
 | ||
|         });
 | ||
| 
 | ||
|         return;
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     // 修改发送消息的方法
 | ||
|     sendMessageFn() {
 | ||
|       if (!this.messageValue) {
 | ||
|         return;
 | ||
|       }
 | ||
| 
 | ||
|       // 重置加载更多标志位
 | ||
|       this.isLoadingMore = false;
 | ||
| 
 | ||
|       const sendMessage = this.messageValue;
 | ||
|       console.log("发送消息", sendMessage);
 | ||
| 
 | ||
|       // 创建新的用户消息对象
 | ||
|       const userMessage = {
 | ||
|         id: Math.random().toString(36).substring(2, 15),
 | ||
|         message: sendMessage,
 | ||
|         sendDate: "",
 | ||
|         isSend: true,
 | ||
|         isRead: false,
 | ||
|         interactMode: 0, // 用户消息
 | ||
|         messageType: 0,
 | ||
|         timeLabel: 0,
 | ||
|         displayTime: "",
 | ||
|       };
 | ||
| 
 | ||
|       // 添加到消息列表
 | ||
|       this.messageGroups.push(userMessage);
 | ||
|       this.messageValue = "";
 | ||
| 
 | ||
|       // 立即添加一个AI回复的加载状态消息
 | ||
|       const loadingMessage = {
 | ||
|         id: "loading_" + Math.random().toString(36).substring(2, 15),
 | ||
|         message: "",
 | ||
|         sendDate: "",
 | ||
|         isSend: true,
 | ||
|         isRead: false,
 | ||
|         interactMode: 1, // AI消息
 | ||
|         messageType: 0,
 | ||
|         timeLabel: 0,
 | ||
|         displayTime: "",
 | ||
|         isLoading: true, // 标记为加载状态
 | ||
|       };
 | ||
| 
 | ||
|       // 添加加载状态消息到列表
 | ||
|       this.messageGroups.push(loadingMessage);
 | ||
| 
 | ||
|       this.$u.api
 | ||
|         .SendMessageApi({
 | ||
|           query: sendMessage,
 | ||
|           conversationId: this.currentConversationId,
 | ||
|         })
 | ||
|         .then((res) => {
 | ||
|           console.log("res.....", res);
 | ||
|           const data = res.data;
 | ||
| 
 | ||
|           this.currentConversationId = data.conversationId;
 | ||
| 
 | ||
|           // 从消息列表中移除加载状态消息
 | ||
|           this.messageGroups = this.messageGroups.filter(
 | ||
|             (msg) => !msg.isLoading
 | ||
|           );
 | ||
| 
 | ||
|           // 创建AI回复消息对象
 | ||
|           const aiMessage = {
 | ||
|             id:
 | ||
|               data.conversationId ||
 | ||
|               Math.random().toString(36).substring(2, 15),
 | ||
|             message: data.content,
 | ||
|             sendDate: "",
 | ||
|             isSend: true,
 | ||
|             isRead: false,
 | ||
|             interactMode: 1, // AI消息
 | ||
|             messageType: 0,
 | ||
|             timeLabel: 0,
 | ||
|             displayTime: "",
 | ||
|           };
 | ||
| 
 | ||
|           // 添加到消息列表
 | ||
|           this.messageGroups.push(aiMessage);
 | ||
|         })
 | ||
|         .catch((error) => {
 | ||
|           console.error("API请求失败:", error);
 | ||
| 
 | ||
|           // 从消息列表中移除加载状态消息
 | ||
|           this.messageGroups = this.messageGroups.filter(
 | ||
|             (msg) => !msg.isLoading
 | ||
|           );
 | ||
| 
 | ||
|           // 添加错误消息
 | ||
|           const errorMessage = {
 | ||
|             id: "error_" + Math.random().toString(36).substring(2, 15),
 | ||
|             message: "请求失败,请稍后重试",
 | ||
|             sendDate: "",
 | ||
|             isSend: true,
 | ||
|             isRead: false,
 | ||
|             interactMode: 1, // AI消息
 | ||
|             messageType: 0,
 | ||
|             timeLabel: 0,
 | ||
|             displayTime: "",
 | ||
|           };
 | ||
| 
 | ||
|           this.messageGroups.push(errorMessage);
 | ||
|         });
 | ||
|     },
 | ||
| 
 | ||
|     // 格式化时间的辅助方法
 | ||
|     formatTime(date) {
 | ||
|       const hours = date.getHours().toString().padStart(2, "0");
 | ||
|       const minutes = date.getMinutes().toString().padStart(2, "0");
 | ||
|       return `${hours}:${minutes}`;
 | ||
|     },
 | ||
| 
 | ||
|     // 处理选中的对话
 | ||
|     handleSelectConversation(data) {
 | ||
|       // 关闭弹窗
 | ||
|       this.popupShow = false;
 | ||
| 
 | ||
|       console.log("选中的对话:", data);
 | ||
|       // 根据conversationId加载对应的对话内容
 | ||
|       this.currentConversationId = data.conversationId;
 | ||
| 
 | ||
|       this.isChat = true;
 | ||
| 
 | ||
|       // 设置切换对话标志位,防止触发上拉刷新
 | ||
|       this.isSwitchingConversation = true;
 | ||
| 
 | ||
|       // 重置消息列表和分页相关状态
 | ||
|       this.messageGroups = [];
 | ||
|       this.pageQuery.PageIndex = 1;
 | ||
|       this.isLoadingMore = false;
 | ||
|       this.noMoreData = false;
 | ||
| 
 | ||
|       this.$u.api
 | ||
|         .GetConversationDetail({
 | ||
|           "Item1.Id": this.currentConversationId,
 | ||
|           PageIndex: this.pageQuery.PageIndex,
 | ||
|           PageSize: this.pageQuery.PageSize,
 | ||
|         })
 | ||
|         .then((res) => {
 | ||
|           console.log("GetConversationDetail.....", res.item2);
 | ||
| 
 | ||
|           // 将消息按sendDate升序排列,时间相同时用户消息(interactMode=0)排在前面
 | ||
|           this.messageGroups = res.item2.sort((a, b) => {
 | ||
|             const timeA = new Date(a.sendDate).getTime();
 | ||
|             const timeB = new Date(b.sendDate).getTime();
 | ||
| 
 | ||
|             // 如果时间相同,按interactMode排序(0排在前,1排在后)
 | ||
|             if (timeA === timeB) {
 | ||
|               return a.interactMode - b.interactMode;
 | ||
|             }
 | ||
| 
 | ||
|             // 否则按时间升序排列
 | ||
|             return timeA - timeB;
 | ||
|           });
 | ||
|         })
 | ||
|         .finally(() => {
 | ||
|           // 延迟重置切换对话标志位,确保滚动事件处理完成
 | ||
|           setTimeout(() => {
 | ||
|             this.isSwitchingConversation = false;
 | ||
|           }, 500);
 | ||
|         });
 | ||
|     },
 | ||
| 
 | ||
|     // 在开始新对话或选择对话时重置相关状态
 | ||
|     handleCreateConversation() {
 | ||
|       // 关闭弹窗
 | ||
|       this.popupShow = false;
 | ||
|       this.isChat = true;
 | ||
| 
 | ||
|       // 设置切换对话标志位,防止触发上拉刷新
 | ||
|       this.isSwitchingConversation = true;
 | ||
| 
 | ||
|       this.currentConversationId = "";
 | ||
|       this.messageGroups = [];
 | ||
| 
 | ||
|       // 重置分页和加载状态
 | ||
|       this.pageQuery.PageIndex = 1;
 | ||
|       this.isLoadingMore = false;
 | ||
|       this.noMoreData = false;
 | ||
| 
 | ||
|       // 延迟重置切换对话标志位
 | ||
|       setTimeout(() => {
 | ||
|         this.isSwitchingConversation = false;
 | ||
|       }, 500);
 | ||
|     },
 | ||
| 
 | ||
|     // 开始新对话
 | ||
|     handleStartChat() {
 | ||
|       this.isChat = true;
 | ||
| 
 | ||
|       // 设置切换对话标志位,防止触发上拉刷新
 | ||
|       this.isSwitchingConversation = true;
 | ||
| 
 | ||
|       this.currentConversationId = "";
 | ||
|       this.messageGroups = [];
 | ||
| 
 | ||
|       // 重置分页和加载状态
 | ||
|       this.pageQuery.PageIndex = 1;
 | ||
|       this.isLoadingMore = false;
 | ||
|       this.noMoreData = false;
 | ||
| 
 | ||
|       // 延迟重置切换对话标志位
 | ||
|       setTimeout(() => {
 | ||
|         this.isSwitchingConversation = false;
 | ||
|       }, 500);
 | ||
|     },
 | ||
| 
 | ||
|     // 滚动到底部事件处理
 | ||
|     onScrollToLower() {
 | ||
|       console.log("已滚动到底部");
 | ||
|     },
 | ||
| 
 | ||
|     // 滚动到顶部事件处理
 | ||
|     onScrollToUpper() {
 | ||
|       console.log("触发上拉刷新");
 | ||
| 
 | ||
|       // 如果已经没有更多数据或正在切换对话,不再触发上拉刷新
 | ||
|       if (this.noMoreData || this.isSwitchingConversation) {
 | ||
|         return;
 | ||
|       }
 | ||
| 
 | ||
|       // 设置加载标志位为true,防止触发滚动到底部
 | ||
|       this.isLoadingMore = true;
 | ||
|       this.isLoading = true;
 | ||
| 
 | ||
|       this.pageQuery.PageIndex++;
 | ||
|       this.$u.api
 | ||
|         .GetConversationDetail({
 | ||
|           "Item1.Id": this.currentConversationId,
 | ||
|           PageIndex: this.pageQuery.PageIndex,
 | ||
|           PageSize: this.pageQuery.PageSize,
 | ||
|         })
 | ||
|         .then((res) => {
 | ||
|           console.log("GetConversationDetail.....", res.item2);
 | ||
| 
 | ||
|           // 如果返回的数据为空数组,说明没有更多历史消息了
 | ||
|           if (!res.item2 || res.item2.length === 0) {
 | ||
|             this.noMoreData = true; // 设置标记,不再触发上拉刷新
 | ||
|             this.$refs.uToast.show({
 | ||
|               title: "已经到顶了",
 | ||
|               type: "warning",
 | ||
|               duration: 1500,
 | ||
|             });
 | ||
|             return;
 | ||
|           }
 | ||
| 
 | ||
|           // 将消息按sendDate升序排列,时间相同时用户消息(interactMode=0)排在前面
 | ||
|           const nextPageMessageGroups = res.item2.sort((a, b) => {
 | ||
|             const timeA = new Date(a.sendDate).getTime();
 | ||
|             const timeB = new Date(b.sendDate).getTime();
 | ||
| 
 | ||
|             // 如果时间相同,按interactMode排序(0排在前,1排在后)
 | ||
|             if (timeA === timeB) {
 | ||
|               return a.interactMode - b.interactMode;
 | ||
|             }
 | ||
| 
 | ||
|             // 否则按时间升序排列
 | ||
|             return timeA - timeB;
 | ||
|           });
 | ||
| 
 | ||
|           this.messageGroups = [
 | ||
|             ...nextPageMessageGroups,
 | ||
|             ...this.messageGroups,
 | ||
|           ];
 | ||
|         })
 | ||
|         .finally(() => {
 | ||
|           setTimeout(() => {
 | ||
|             this.isLoading = false;
 | ||
|           }, 300);
 | ||
|         });
 | ||
|     },
 | ||
|   },
 | ||
| };
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .home-container {
 | ||
|   height: 100vh;
 | ||
|   // background-color: #f5f7fc;
 | ||
|   padding-bottom: calc(
 | ||
|     112rpx + env(safe-area-inset-bottom)
 | ||
|   ); /* 为自定义tabBar预留空间 */
 | ||
|   padding-top: 88rpx;
 | ||
|   background-image: url("@/static/common/images/images_bg.png");
 | ||
|   width: 100%;
 | ||
|   background-repeat: no-repeat;
 | ||
|   background-size: 100% 100%;
 | ||
|   background-position: 0 88rpx;
 | ||
|   background-attachment: fixed;
 | ||
| 
 | ||
|   /* Header样式移至HeaderBar组件 */
 | ||
| 
 | ||
|   .main-content {
 | ||
|     padding: 30rpx;
 | ||
|     padding-top: 60rpx;
 | ||
| 
 | ||
|     .welcome-message {
 | ||
|       //   display: inline-block;
 | ||
|       //   flex-wrap: wrap;
 | ||
|       background-color: rgba(255, 255, 255, 0.8);
 | ||
|       border-radius: 16rpx;
 | ||
|       padding: 24rpx;
 | ||
|       margin-bottom: 32rpx;
 | ||
|       // margin-bottom: 100rpx;
 | ||
| 
 | ||
|       .avatar {
 | ||
|         display: inline-block;
 | ||
|         width: 50rpx;
 | ||
|         height: 44rpx;
 | ||
|         margin-right: 12rpx;
 | ||
|       }
 | ||
| 
 | ||
|       .message-text {
 | ||
|         font-size: 28rpx;
 | ||
|         color: #333333;
 | ||
|         font-family: PingFang SC;
 | ||
|         vertical-align: super;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .qa-section {
 | ||
|       background-color: #ffffff;
 | ||
|       border-radius: 16rpx;
 | ||
|       padding: 30rpx;
 | ||
|       margin-bottom: 32rpx;
 | ||
| 
 | ||
|       .qa-header {
 | ||
|         display: flex;
 | ||
|         justify-content: space-between;
 | ||
|         align-items: center;
 | ||
|         margin-bottom: 30rpx;
 | ||
| 
 | ||
|         .qa-title {
 | ||
|           font-size: 32rpx;
 | ||
|           font-weight: 500;
 | ||
|           color: #333;
 | ||
|         }
 | ||
| 
 | ||
|         .more-link {
 | ||
|           .more-text {
 | ||
|             font-size: 26rpx;
 | ||
|             color: #999;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .qa-list {
 | ||
|         .qa-item {
 | ||
|           margin-bottom: 24rpx;
 | ||
| 
 | ||
|           .qa-question {
 | ||
|             font-size: 28rpx;
 | ||
|             color: #333;
 | ||
|             line-height: 1.5;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .chat-button {
 | ||
|         display: flex;
 | ||
|         align-items: center;
 | ||
|         justify-content: center;
 | ||
|         background-color: #4377fe;
 | ||
|         border-radius: 16rpx;
 | ||
|         height: 88rpx;
 | ||
|         width: 100%;
 | ||
|         margin-top: 30rpx;
 | ||
|         border: none;
 | ||
| 
 | ||
|         .chat-icon {
 | ||
|           width: 38rpx;
 | ||
|           height: 32rpx;
 | ||
|           margin-right: 12rpx;
 | ||
|         }
 | ||
| 
 | ||
|         .chat-text {
 | ||
|           color: #ffffff;
 | ||
|           font-size: 32rpx;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .start-chat {
 | ||
|       .chat-button {
 | ||
|         display: flex;
 | ||
|         align-items: center;
 | ||
|         justify-content: center;
 | ||
|         background-color: #4377fe;
 | ||
|         border-radius: 16rpx;
 | ||
|         height: 88rpx;
 | ||
|         width: 100%;
 | ||
|         margin-top: 30rpx;
 | ||
|         border: none;
 | ||
| 
 | ||
|         .chat-icon {
 | ||
|           width: 38rpx;
 | ||
|           height: 32rpx;
 | ||
|           margin-right: 12rpx;
 | ||
|         }
 | ||
| 
 | ||
|         .chat-text {
 | ||
|           color: #ffffff;
 | ||
|           font-size: 32rpx;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .feature-grid {
 | ||
|       background-color: #ffffff;
 | ||
|       border-radius: 16rpx;
 | ||
|       padding: 30rpx;
 | ||
|       display: flex;
 | ||
|       justify-content: space-between;
 | ||
|       margin-top: 32rpx;
 | ||
|       gap: 30rpx;
 | ||
| 
 | ||
|       .feature-item {
 | ||
|         height: 150rpx;
 | ||
|         border-radius: 16rpx;
 | ||
|         display: flex;
 | ||
|         justify-content: flex-start;
 | ||
|         align-items: center;
 | ||
|         padding-left: 30rpx;
 | ||
|         gap: 20rpx;
 | ||
|         flex: 1;
 | ||
| 
 | ||
|         .feature-icon {
 | ||
|           width: 80rpx;
 | ||
|           height: 80rpx;
 | ||
|           margin-top: 16rpx;
 | ||
|         }
 | ||
| 
 | ||
|         .feature-text {
 | ||
|           font-size: 26rpx;
 | ||
|           color: #333333;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .chat-container {
 | ||
|     display: flex;
 | ||
|     flex-direction: column;
 | ||
|     padding: 0 30rpx;
 | ||
|     box-sizing: border-box;
 | ||
|     height: calc(100vh - 88rpx - 146rpx);
 | ||
|     position: relative;
 | ||
|     overflow: hidden;
 | ||
| 
 | ||
|     .chat-scroll {
 | ||
|       flex: 1;
 | ||
|       height: calc(100vh - 88rpx - 146rpx);
 | ||
|       overflow-y: scroll;
 | ||
|     }
 | ||
| 
 | ||
|     .chat-card {
 | ||
|       // background-color: #ffffff;
 | ||
|       // border-radius: 16rpx;
 | ||
|       padding: 32rpx;
 | ||
|       margin-bottom: 32rpx;
 | ||
| 
 | ||
|       .chat-card-title {
 | ||
|         font-family: DouyinSans;
 | ||
|         font-weight: bold;
 | ||
|         font-size: 36rpx;
 | ||
|         color: #5255e6;
 | ||
|         // line-height: 24rpx;
 | ||
|         background: linear-gradient(-56deg, #4d50dd 0%, #3e6aff 100%);
 | ||
|         -webkit-background-clip: text;
 | ||
|         -webkit-text-fill-color: transparent;
 | ||
|         margin-bottom: 12rpx;
 | ||
|       }
 | ||
| 
 | ||
|       .chat-card-desc {
 | ||
|         font-family: PingFang SC;
 | ||
|         font-weight: 500;
 | ||
|         font-size: 24rpx;
 | ||
|         color: #9ba5c7;
 | ||
|         line-height: 24rpx;
 | ||
|         margin-bottom: 12rpx;
 | ||
|       }
 | ||
| 
 | ||
|       .chat-card-questions {
 | ||
|         display: flex;
 | ||
|         flex-direction: column;
 | ||
|         gap: 16rpx;
 | ||
| 
 | ||
|         .question-item {
 | ||
|           font-size: 28rpx;
 | ||
|           color: #333333;
 | ||
|           line-height: 1.5;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .chat-content {
 | ||
|       flex: 1;
 | ||
|       padding: 20rpx 0;
 | ||
|       margin-bottom: 150rpx;
 | ||
| 
 | ||
|       .loading-more {
 | ||
|         text-align: center;
 | ||
|         margin-bottom: 32rpx;
 | ||
|       }
 | ||
| 
 | ||
|       .no-more-data {
 | ||
|         text-align: center;
 | ||
|         font-size: 24rpx;
 | ||
|         color: #999;
 | ||
|         margin-bottom: 32rpx;
 | ||
|         padding: 10rpx 0;
 | ||
|       }
 | ||
| 
 | ||
|       .message-time {
 | ||
|         text-align: center;
 | ||
|         font-size: 24rpx;
 | ||
|         color: #999999;
 | ||
|         padding: 20rpx;
 | ||
|       }
 | ||
| 
 | ||
|       .message-left,
 | ||
|       .message-right {
 | ||
|         display: flex;
 | ||
|         margin-bottom: 40rpx;
 | ||
|         align-items: flex-start;
 | ||
|       }
 | ||
| 
 | ||
|       .message-left {
 | ||
|         justify-content: flex-start;
 | ||
| 
 | ||
|         .ai-avatar {
 | ||
|           width: 64rpx;
 | ||
|           height: 64rpx;
 | ||
|           border-radius: 50%;
 | ||
|           margin-right: 16rpx;
 | ||
|           background-color: #f0f0f0;
 | ||
|           flex-shrink: 0;
 | ||
|         }
 | ||
| 
 | ||
|         .message-content {
 | ||
|           background-color: #ffffff;
 | ||
|           max-width: 70%;
 | ||
|           padding: 20rpx 24rpx;
 | ||
|           border-radius: 0 16rpx 16rpx 16rpx;
 | ||
|           font-size: 28rpx;
 | ||
|           line-height: 1.5;
 | ||
| 
 | ||
|           /* 加载动画样式 */
 | ||
|           .loading-dots {
 | ||
|             display: flex;
 | ||
|             align-items: center;
 | ||
|             justify-content: flex-start;
 | ||
| 
 | ||
|             .dot {
 | ||
|               display: inline-block;
 | ||
|               width: 12rpx;
 | ||
|               height: 12rpx;
 | ||
|               border-radius: 50%;
 | ||
|               background-color: #cccccc;
 | ||
|               margin: 0 6rpx;
 | ||
|               opacity: 0.6;
 | ||
|               animation: dot-flashing 1.5s infinite linear alternate;
 | ||
| 
 | ||
|               &:nth-child(1) {
 | ||
|                 animation-delay: 0s;
 | ||
|               }
 | ||
| 
 | ||
|               &:nth-child(2) {
 | ||
|                 animation-delay: 0.5s;
 | ||
|               }
 | ||
| 
 | ||
|               &:nth-child(3) {
 | ||
|                 animation-delay: 1s;
 | ||
|               }
 | ||
|             }
 | ||
| 
 | ||
|             @keyframes dot-flashing {
 | ||
|               0% {
 | ||
|                 opacity: 0.6;
 | ||
|                 transform: scale(1);
 | ||
|               }
 | ||
|               50% {
 | ||
|                 opacity: 1;
 | ||
|                 transform: scale(1.2);
 | ||
|                 background-color: #4370fe;
 | ||
|               }
 | ||
|               100% {
 | ||
|                 opacity: 0.6;
 | ||
|                 transform: scale(1);
 | ||
|               }
 | ||
|             }
 | ||
|           }
 | ||
| 
 | ||
|           /* Markdown内容样式调整 */
 | ||
|           /deep/ .markdown-container {
 | ||
|             padding: 0;
 | ||
| 
 | ||
|             /* 覆盖默认的padding,避免额外空白 */
 | ||
|             p {
 | ||
|               margin: 0;
 | ||
|               padding: 0;
 | ||
|             }
 | ||
| 
 | ||
|             /* 确保代码块不会超出气泡 */
 | ||
|             pre {
 | ||
|               max-width: 100%;
 | ||
|               overflow-x: auto;
 | ||
|             }
 | ||
| 
 | ||
|             /* 调整图片大小 */
 | ||
|             img {
 | ||
|               max-width: 100%;
 | ||
|             }
 | ||
| 
 | ||
|             /* 调整表格样式 */
 | ||
|             table {
 | ||
|               font-size: 24rpx;
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .message-right {
 | ||
|         justify-content: flex-end;
 | ||
| 
 | ||
|         .user-avatar {
 | ||
|           width: 64rpx;
 | ||
|           height: 64rpx;
 | ||
|           border-radius: 50%;
 | ||
|           margin-left: 16rpx;
 | ||
|           background-color: #f0f0f0;
 | ||
|           flex-shrink: 0;
 | ||
|         }
 | ||
| 
 | ||
|         .message-content {
 | ||
|           background-color: #4370fe;
 | ||
|           color: #ffffff;
 | ||
|           max-width: 70%;
 | ||
|           padding: 20rpx 24rpx;
 | ||
|           border-radius: 16rpx 0 16rpx 16rpx;
 | ||
|           font-size: 28rpx;
 | ||
|           line-height: 1.5;
 | ||
|           text-align: left;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .chat-footer {
 | ||
|       position: fixed;
 | ||
|       bottom: 0;
 | ||
|       left: 0;
 | ||
|       right: 0;
 | ||
| 
 | ||
|       .floating-tabs {
 | ||
|         display: flex;
 | ||
|         justify-content: flex-start;
 | ||
|         margin: 0 0 20rpx 20rpx;
 | ||
| 
 | ||
|         .tab-item {
 | ||
|           display: flex;
 | ||
|           flex-direction: row;
 | ||
|           align-items: center;
 | ||
|           justify-content: center;
 | ||
|           background-color: #ffffff;
 | ||
|           padding: 8rpx 24rpx;
 | ||
|           border-radius: 50rpx;
 | ||
|           box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
 | ||
|           margin-right: 12rpx;
 | ||
| 
 | ||
|           .tab-icon {
 | ||
|             width: 28rpx;
 | ||
|             height: 28rpx;
 | ||
|             margin-right: 10rpx;
 | ||
|           }
 | ||
| 
 | ||
|           text {
 | ||
|             font-size: 28rpx;
 | ||
|             color: #333333;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .input-container {
 | ||
|         padding: 32rpx;
 | ||
|         background-color: #fff;
 | ||
|         box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
 | ||
| 
 | ||
|         .input-area {
 | ||
|           display: flex;
 | ||
|           align-items: center;
 | ||
|           background-color: #f2f4f9;
 | ||
|           border-radius: 20rpx;
 | ||
|           padding: 16rpx 24rpx;
 | ||
|           box-sizing: border-box;
 | ||
| 
 | ||
|           .chat-input {
 | ||
|             flex: 1;
 | ||
|             height: 40rpx;
 | ||
|             font-size: 28rpx;
 | ||
|             color: #333;
 | ||
|             background-color: transparent;
 | ||
|           }
 | ||
| 
 | ||
|           .send-btn {
 | ||
|             width: 50rpx;
 | ||
|             height: 50rpx;
 | ||
|             display: flex;
 | ||
|             align-items: center;
 | ||
|             justify-content: center;
 | ||
| 
 | ||
|             .send-icon {
 | ||
|               width: 40rpx;
 | ||
|               height: 46rpx;
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /* 响应式布局 - PC端样式 */
 | ||
| @media screen and (min-width: 768px) {
 | ||
|   .home-container {
 | ||
|     .content {
 | ||
|       max-width: 1200rpx;
 | ||
|       margin: 0 auto;
 | ||
| 
 | ||
|       .qa-section {
 | ||
|         .qa-list {
 | ||
|           display: flex;
 | ||
|           flex-wrap: wrap;
 | ||
| 
 | ||
|           .qa-item {
 | ||
|             width: 48%;
 | ||
|             margin-right: 2%;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .feature-grid {
 | ||
|         max-width: 1200rpx;
 | ||
|         margin: 40rpx auto 0;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |