YingXingAI/pages/home/index/index.vue

1101 lines
27 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="home-container">
<view class="header">
<div class="header-left">
<u-icon
class="header-left-icon"
name="list"
@click="handleLeftClick"
></u-icon>
</div>
<text class="header-title">源小新</text>
<div class="header-right"></div>
</view>
<!-- 首页 -->
<view class="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="isChat = true">
<image
class="chat-icon"
src="/static/common/images/icon_chat.png"
></image>
<text class="chat-text">开启对话</text>
</button>
</view> -->
<view class="start-chat">
<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"
@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"
@scrolltoupper="loadMoreMessages"
@scroll="onScroll"
>
<!-- 头部卡片 -->
<!-- 后端让先注释 -->
<!-- <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">
<!-- 消息循环渲染 -->
<block
v-for="(message, index) in messageGroups"
:key="'msg-' + index"
>
<!-- 时间 -->
<view class="message-time" v-if="message.timeLabel === 1">
{{ 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">
<markdown-viewer :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"; // 导入新组件
export default {
components: {
CustomTabBar,
AdvicePhone,
MarkdownViewer,
PerfectInfo,
ChatHistory, // 注册新组件
},
data() {
return {
isChat: false,
currentConversationId: "",
advicePhoneShow: false,
perfectInfoShow: false,
questionList: [
"学习哪些专业比较好?",
"处于香港学校综合录取的城市招生?",
"学校有新华专业?",
"学校录取科目与有一些专业会有特殊...",
"我什么时候能推知道自己是否被录取?",
],
features: [
{
title: "招办在线",
icon: "/static/common/images/icon_admissions.png",
},
{
title: "留言板",
icon: "/static/common/images/icon_messageBoard.png",
},
{
title: "电话咨询",
icon: "/static/common/images/icon_phone.png",
},
],
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() {
this.$nextTick(() => {
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() {},
mounted() {
this.$nextTick(() => {
this.scrollToBottom();
});
},
updated() {
this.$nextTick(() => {
this.scrollToBottom();
});
},
methods: {
handleLeftClick() {
this.$u.api.GetConversationPage().then((res) => {
this.chatHistoryList3 = res.data;
console.log("this.chatHistoryList3", this.chatHistoryList3);
this.popupShow = true;
});
},
selectChatItem(index) {
this.activeIndex = index;
},
loadMoreMessages() {
// 加载更多历史消息的实现
console.log("加载更多历史消息");
},
onScroll(event) {
// 滚动事件处理
},
scrollToBottom() {
// 使用nextTick确保DOM已更新
this.$nextTick(() => {
// 获取滚动区域的高度
const query = uni.createSelectorQuery().in(this);
query
.select(".chat-content")
.boundingClientRect((data) => {
if (data) {
// 计算滚动到底部的位置
const scrollHeight = data.height;
// 设置滚动位置加100px确保滚动到底部
this.scrollTop = scrollHeight + 100;
}
})
.exec();
});
},
handleFeatureClick(item) {
if (item.title === "电话咨询") {
this.advicePhoneShow = true;
return;
} else {
this.$refs.uToast.show({
title: "暂未开放",
type: "warning",
});
return;
}
},
// 修改发送消息的方法
sendMessageFn() {
if (!this.messageValue) {
return;
}
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 = "";
this.$u.api
.SendMessageApi({
query: sendMessage,
conversationId: this.currentConversationId,
})
.then((res) => {
console.log("res.....", res);
const data = res.data;
this.currentConversationId = data.conversationId;
// 创建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);
});
},
// 格式化时间的辅助方法
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加载对应的对话内容
const conversationId = data.conversationId;
this.currentConversationId = conversationId;
this.isChat = true;
this.messageGroups = [];
this.$u.api
.GetConversationDetail({
"Item1.Id": conversationId,
PageIndex: "1",
PageSize: "20",
})
.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;
});
});
},
handleCreateConversation() {
// 关闭弹窗
this.popupShow = false;
this.isChat = true;
this.currentConversationId = "";
this.messageGroups = [];
},
handleStartChat() {
this.isChat = true;
this.currentConversationId = "";
this.messageGroups = [];
},
},
};
</script>
<style lang="scss" scoped>
page {
height: 100vh;
background-image: url("@/static/common/images/images_bg.png");
width: 100%;
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: 0 88rpx;
}
.home-container {
height: 100vh;
// background-color: #f5f7fc;
padding-bottom: calc(
112rpx + env(safe-area-inset-bottom)
); /* 为自定义tabBar预留空间 */
padding-top: 88rpx;
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #ffffff;
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
z-index: 99;
.header-left {
width: 40rpx;
font-size: 36rpx;
}
.header-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.header-right {
width: 40rpx;
}
}
.content {
padding: 30rpx;
padding-top: 100rpx;
.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: 42rpx;
height: 34rpx;
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: 210rpx;
border-radius: 16rpx;
background-color: #fafafc;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
// gap: 20rpx;
flex: 1;
.feature-icon {
width: 80rpx;
height: 80rpx;
margin-bottom: 12rpx;
}
.feature-text {
font-size: 26rpx;
color: #333333;
}
}
}
}
}
.drawer-container {
padding: 32rpx;
display: flex;
flex-direction: column;
height: 100vh;
background-color: #ffffff;
.chat-history-list {
flex: 1;
// padding: 30rpx 40rpx;
height: calc(100vh - 200rpx);
.chat-day {
display: flex;
margin: 20rpx 0 20rpx 30rpx;
.day-text {
font-size: 24rpx;
color: #999999;
font-family: PingFang SC;
margin-bottom: 10rpx;
}
}
.chat-item {
padding: 24rpx 30rpx;
border-radius: 16rpx;
// margin-bottom: 30rpx;
.chat-text {
font-size: 28rpx;
color: #303030;
font-family: PingFang SC;
}
&-active {
background-color: #4f6aff;
.chat-text {
color: #fff;
}
}
}
}
.drawer-footer {
height: 130rpx;
border: 1rpx solid #eeeeee;
border-radius: 12rpx;
padding: 0 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
.user-info {
display: flex;
align-items: center;
.user-avatar {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
margin-right: 30rpx;
}
.user-name {
font-size: 28rpx;
font-weight: bold;
font-family: DouyinSans;
color: #333;
}
}
.settings {
padding: 10rpx;
}
}
}
/* 响应式布局 - 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;
}
}
}
}
.chat-container {
display: flex;
flex-direction: column;
padding: 30rpx;
box-sizing: border-box;
.chat-scroll {
flex: 1;
flex-shrink: 0;
// overflow-y: auto; /* 使用auto而不是scroll */ // 添加这行会导致滚动条不见
}
// 测试按钮样式
.test-button {
position: fixed;
bottom: 180rpx;
right: 30rpx;
background-color: #4370fe;
color: #ffffff;
padding: 16rpx 24rpx;
border-radius: 10rpx;
font-size: 28rpx;
z-index: 999;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.2);
}
.chat-card {
// background-color: #ffffff;
// border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
.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;
.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;
/* 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;
}
}
}
}
}
}
</style>