YingXingAI/pages/home/index/index.vue

1249 lines
33 KiB
Vue
Raw Normal View History

2025-06-30 16:09:43 +08:00
<template>
2025-07-02 17:07:33 +08:00
<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>
2025-07-04 15:30:23 +08:00
<div class="header-right"></div>
2025-07-02 17:07:33 +08:00
</view>
<!-- 首页 -->
2025-07-11 13:49:59 +08:00
<view class="main-content" v-if="!isChat">
2025-07-02 17:07:33 +08:00
<view class="welcome-message">
<image
class="avatar"
src="/static/common/images/images_logo.png"
></image>
2025-07-02 17:07:33 +08:00
<text class="message-text"
>Hi~ 我是源小新你们的AI校园助手非常高兴认识您我可以为你答疑解惑</text
2025-07-02 17:07:33 +08:00
>
</view>
<!-- <view class="qa-section">
2025-07-02 17:07:33 +08:00
<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"
2025-07-02 17:07:33 +08:00
></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)"
2025-07-02 17:07:33 +08:00
>
<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"
2025-07-02 17:07:33 +08:00
>
<!-- 头部卡片 -->
<!-- 后端让先注释 -->
<!-- <view class="chat-card">
2025-07-02 17:07:33 +08:00
<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>
2025-07-02 17:07:33 +08:00
<!-- 对话内容区域 -->
<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>
2025-07-02 17:07:33 +08:00
<!-- 消息循环渲染 -->
<block
v-for="(message, index) in messageGroups"
:key="'msg-' + index"
2025-07-02 17:07:33 +08:00
>
<!-- 时间 -->
2025-07-14 11:14:58 +08:00
<view class="message-time" v-if="message.timeLabel !== 0">
{{ message.displayTime }}
</view>
2025-07-02 17:07:33 +08:00
<!-- 用户消息 -->
<view
class="message-right"
v-if="message.interactMode === 0"
:id="'msg-' + message.id"
2025-07-02 17:07:33 +08:00
>
<view class="message-content">
<text>{{ message.message }}</text>
2025-07-02 17:07:33 +08:00
</view>
<image
class="user-avatar"
src="/static/common/images/avatar.png"
mode="scaleToFill"
/>
</view>
2025-07-02 17:07:33 +08:00
<!-- 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" />
2025-07-02 17:07:33 +08:00
</view>
</view>
2025-07-02 17:07:33 +08:00
</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"
/>
2025-07-02 17:07:33 +08:00
<text>招生在线</text>
</view>
<view
class="tab-item"
@click="handleFeatureClick({ title: '留言板' })"
>
<image
class="tab-icon"
src="/static/common/images/icon_messageBoard2.png"
mode="scaleToFill"
/>
2025-07-02 17:07:33 +08:00
<text>留言板</text>
</view>
<view
class="tab-item"
@click="handleFeatureClick({ title: '电话咨询' })"
>
<image
class="tab-icon"
src="/static/common/images/icon_phone2.png"
mode="scaleToFill"
/>
2025-07-02 17:07:33 +08:00
<text>电话咨询</text>
</view>
</view> -->
2025-07-02 17:07:33 +08:00
<view class="input-container">
<view class="input-area">
<input
v-model="messageValue"
2025-07-02 17:07:33 +08:00
type="text"
class="chat-input"
:focus="true"
2025-07-02 17:07:33 +08:00
placeholder="请输入内容"
placeholder-style="color: #adadad;"
@confirm="sendMessageFn"
2025-07-02 17:07:33 +08:00
/>
<view class="send-btn" @click="sendMessageFn">
<image
class="send-icon"
src="/static/common/images/icon_send.png"
mode="scaleToFill"
/>
2025-07-02 17:07:33 +08:00
</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" />
2025-07-02 17:07:33 +08:00
</view>
2025-06-30 16:09:43 +08:00
</template>
<script>
import MarkdownViewer from "@/components/markdown-viewer/markdown-viewer";
2025-07-02 17:07:33 +08:00
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"; // 导入新组件
2025-07-02 17:07:33 +08:00
export default {
components: {
CustomTabBar,
AdvicePhone,
MarkdownViewer,
PerfectInfo,
ChatHistory, // 注册新组件
2025-07-02 17:07:33 +08:00
},
data() {
return {
isChat: false,
currentConversationId: "",
advicePhoneShow: false,
perfectInfoShow: false,
isLoading: false, // loadingMore
isLoadingMore: false, // 是否正在加载更多的标志位
noMoreData: false, // 是否已加载全部历史消息
isSwitchingConversation: false, // 是否正在切换对话的标志位
pageQuery: {
PageIndex: 1,
PageSize: 20,
},
2025-07-02 17:07:33 +08:00
questionList: [
"学习哪些专业比较好?",
"处于香港学校综合录取的城市招生?",
"学校有新华专业?",
"学校录取科目与有一些专业会有特殊...",
"我什么时候能推知道自己是否被录取?",
],
features: [
{
title: "招办在线",
icon: "/static/common/images/icon_admissions.png",
2025-07-02 17:07:33 +08:00
},
{
title: "留言板",
icon: "/static/common/images/icon_messageBoard.png",
2025-07-02 17:07:33 +08:00
},
{
title: "电话咨询",
icon: "/static/common/images/icon_phone.png",
2025-07-02 17:07:33 +08:00
},
],
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"
// }
// ]
// }
2025-07-02 17:07:33 +08:00
],
activeIndex: 0,
commonQuestions: [
"新生报到流程",
"如何报考学校综合合录取评澳市评招生?",
"学校有新华专业?",
"学校录取科目与有一些专业会有特殊...",
"我什么时候能推知道自己是否被录取?",
],
scrollToView: "",
// 修改后的messageGroups数据结构
2025-07-02 17:07:33 +08:00
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: "",
// },
2025-07-02 17:07:33 +08:00
],
scrollTop: 0,
messageValue: "", // 输入框内容
2025-07-02 17:07:33 +08:00
};
},
watch: {
// 确保消息更新后自动滚动到底部
messageGroups: {
handler() {
// 只有在不是加载更多的情况下才滚动到底部
if (!this.isLoadingMore) {
this.scrollToBottom();
}
2025-07-02 17:07:33 +08:00
},
deep: true,
},
vuex_user: {
handler(newValue) {
// console.log('vuex_user',newValue);
if (newValue && newValue.Sex === 2) {
// this.perfectInfoShow = true;
}
},
immediate: true, // 这会使得组件创建时立即执行一次handler
deep: true,
},
2025-07-02 17:07:33 +08:00
},
onLoad() {},
2025-07-02 17:07:33 +08:00
methods: {
handleLeftClick() {
this.$u.api.GetConversationPage().then((res) => {
this.chatHistoryList3 = res.data;
2025-07-11 13:43:04 +08:00
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;
});
},
2025-07-02 17:07:33 +08:00
scrollToBottom() {
// 如果正在加载更多,不执行滚动
if (this.isLoadingMore) return;
2025-07-02 17:07:33 +08:00
// 使用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);
2025-07-02 17:07:33 +08:00
});
},
handleFeatureClick(item) {
if (item.title === "电话咨询") {
this.advicePhoneShow = true;
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);
});
},
2025-07-02 17:07:33 +08:00
},
};
2025-06-30 16:09:43 +08:00
</script>
<style lang="scss" scoped>
2025-07-02 17:07:33 +08:00
.home-container {
height: 100vh;
// background-color: #f5f7fc;
2025-07-02 17:07:33 +08:00
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;
2025-07-02 17:07:33 +08:00
.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 {
2025-07-04 15:30:23 +08:00
width: 40rpx;
2025-07-02 17:07:33 +08:00
font-size: 36rpx;
}
.header-title {
font-size: 36rpx;
font-weight: bold;
2025-07-02 17:07:33 +08:00
color: #333333;
}
2025-07-04 15:30:23 +08:00
.header-right {
width: 40rpx;
}
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.main-content {
2025-07-02 17:07:33 +08:00
padding: 30rpx;
padding-top: 100rpx;
2025-07-02 17:07:33 +08:00
.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;
2025-07-02 17:07:33 +08:00
.avatar {
display: inline-block;
width: 42rpx;
height: 34rpx;
margin-right: 12rpx;
}
.message-text {
font-size: 28rpx;
2025-07-02 17:07:33 +08:00
color: #333333;
font-family: PingFang SC;
vertical-align: super;
}
}
.qa-section {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 32rpx;
2025-07-02 17:07:33 +08:00
.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;
2025-07-02 17:07:33 +08:00
margin-right: 12rpx;
}
.chat-text {
color: #ffffff;
font-size: 32rpx;
2025-07-02 17:07:33 +08:00
}
}
}
.feature-grid {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
display: flex;
justify-content: space-between;
margin-top: 32rpx;
2025-07-02 17:07:33 +08:00
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;
2025-07-02 17:07:33 +08:00
}
.feature-text {
font-size: 26rpx;
color: #333333;
}
}
}
}
2025-07-11 13:49:59 +08:00
.chat-container {
display: flex;
flex-direction: column;
padding: 0 30rpx;
2025-07-11 13:49:59 +08:00
box-sizing: border-box;
height: calc(100vh - 88rpx - 146rpx);
position: relative;
overflow: hidden;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.chat-scroll {
flex: 1;
height: calc(100vh - 88rpx - 146rpx);
overflow-y: scroll;
2025-07-11 13:49:59 +08:00
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.chat-card {
// background-color: #ffffff;
// border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 32rpx;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.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 {
2025-07-02 17:07:33 +08:00
font-family: PingFang SC;
2025-07-11 13:49:59 +08:00
font-weight: 500;
font-size: 24rpx;
color: #9ba5c7;
line-height: 24rpx;
margin-bottom: 12rpx;
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.chat-card-questions {
display: flex;
flex-direction: column;
gap: 16rpx;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.question-item {
font-size: 28rpx;
color: #333333;
line-height: 1.5;
2025-07-02 17:07:33 +08:00
}
}
}
2025-07-11 13:49:59 +08:00
.chat-content {
flex: 1;
padding: 20rpx 0;
margin-bottom: 150rpx;
2025-07-02 17:07:33 +08:00
.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;
}
2025-07-11 13:49:59 +08:00
.message-time {
text-align: center;
font-size: 24rpx;
color: #999999;
padding: 20rpx;
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.message-left,
.message-right {
display: flex;
margin-bottom: 40rpx;
align-items: flex-start;
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.message-left {
justify-content: flex-start;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.ai-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
background-color: #f0f0f0;
flex-shrink: 0;
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.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);
}
}
}
2025-07-11 13:49:59 +08:00
/* Markdown内容样式调整 */
/deep/ .markdown-container {
padding: 0;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
/* 覆盖默认的padding避免额外空白 */
p {
margin: 0;
padding: 0;
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
/* 确保代码块不会超出气泡 */
pre {
max-width: 100%;
overflow-x: auto;
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
/* 调整图片大小 */
img {
max-width: 100%;
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
/* 调整表格样式 */
table {
font-size: 24rpx;
}
}
}
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.message-right {
justify-content: flex-end;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.user-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-left: 16rpx;
background-color: #f0f0f0;
flex-shrink: 0;
}
2025-07-11 13:49:59 +08:00
.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;
}
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.chat-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
2025-07-11 13:49:59 +08:00
.floating-tabs {
display: flex;
justify-content: flex-start;
margin: 0 0 20rpx 20rpx;
2025-07-11 13:49:59 +08:00
.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;
2025-07-11 13:49:59 +08:00
.tab-icon {
width: 28rpx;
height: 28rpx;
margin-right: 10rpx;
}
2025-07-11 13:49:59 +08:00
text {
font-size: 28rpx;
color: #333333;
}
}
2025-07-02 17:07:33 +08:00
}
2025-07-11 13:49:59 +08:00
.input-container {
padding: 32rpx;
background-color: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.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;
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.send-btn {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.send-icon {
width: 40rpx;
height: 46rpx;
}
}
2025-07-02 17:07:33 +08:00
}
}
}
2025-07-11 13:49:59 +08:00
}
}
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
/* 响应式布局 - PC端样式 */
@media screen and (min-width: 768px) {
.home-container {
.content {
max-width: 1200rpx;
margin: 0 auto;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.qa-section {
.qa-list {
2025-07-02 17:07:33 +08:00
display: flex;
2025-07-11 13:49:59 +08:00
flex-wrap: wrap;
2025-07-02 17:07:33 +08:00
2025-07-11 13:49:59 +08:00
.qa-item {
width: 48%;
margin-right: 2%;
2025-07-02 17:07:33 +08:00
}
}
}
2025-07-11 13:49:59 +08:00
.feature-grid {
max-width: 1200rpx;
margin: 40rpx auto 0;
}
2025-07-02 17:07:33 +08:00
}
}
}
</style>