|
|
<template>
|
|
|
<div class="container">
|
|
|
<!-- 引用 HeaderBar.vue 组件 -->
|
|
|
<HeaderBar />
|
|
|
|
|
|
<!-- 主容器 -->
|
|
|
<div class="main-container">
|
|
|
<!-- 侧边栏 -->
|
|
|
<aside class="sidebar card">
|
|
|
<h2 class="sidebar-title">历史对话</h2>
|
|
|
<ul class="history-list">
|
|
|
<li class="history-item"
|
|
|
v-for="conversation in conversations"
|
|
|
:key="conversation.id"
|
|
|
@click="selectConversation(conversation)">
|
|
|
<span class="history-text">{{ conversation.title }}</span>
|
|
|
<div class="history-actions">
|
|
|
<button class="action-btn" title="编辑" @click.stop="editConversationTitle(conversation)">
|
|
|
<img src="@/assets/images/edit.png" alt="编辑" />
|
|
|
</button>
|
|
|
<button class="action-btn" title="删除" @click.stop="deleteConversation(conversation)">
|
|
|
<img src="@/assets/images/delete.png" alt="删除" />
|
|
|
</button>
|
|
|
</div>
|
|
|
</li>
|
|
|
</ul>
|
|
|
<!-- 新建对话按钮 -->
|
|
|
<button class="new-chat-btn" @click="createNewChat">
|
|
|
<span>新建对话</span>
|
|
|
</button>
|
|
|
</aside>
|
|
|
|
|
|
<!-- 聊天区域 -->
|
|
|
<main class="chat-area card">
|
|
|
<div class="chat-content">
|
|
|
<!-- 欢迎消息始终显示在最上方 -->
|
|
|
<div class="welcome-message">
|
|
|
<div class="bot-avatar">
|
|
|
<img src="@/assets/images/aiRobot.png" alt="机器人头像" />
|
|
|
</div>
|
|
|
<div class="welcome-text">我可以回答各种问题,欢迎提问</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 对话消息 -->
|
|
|
<template v-if="currentConversation">
|
|
|
<div v-for="message in currentConversation.messages"
|
|
|
:key="message.id"
|
|
|
:class="message.isUser ? 'user-message' : 'bot-message'">
|
|
|
<template v-if="message.isUser">
|
|
|
<div class="message-text">{{ message.text }}</div>
|
|
|
<div class="user-avatar">
|
|
|
<img src="@/assets/images/aiRobot.png" alt="用户头像" />
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="bot-avatar">
|
|
|
<img src="@/assets/images/aiRobot.png" alt="机器人头像" />
|
|
|
</div>
|
|
|
<div class="message-text">{{ message.text }}</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
|
|
|
<!-- 输入区域 -->
|
|
|
<div class="input-area">
|
|
|
<div class="input-container">
|
|
|
<textarea class="message-input" placeholder="请输入您的问题..." v-model="message"></textarea>
|
|
|
<button class="send-button" @click="sendMessage">发送</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</main>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref } from 'vue';
|
|
|
import HeaderBar from '@/components/HeaderBar.vue';
|
|
|
|
|
|
// 定义 HistoryItem 接口
|
|
|
interface HistoryItem {
|
|
|
id: number; // 唯一标识符
|
|
|
text: string; // 对话内容
|
|
|
}
|
|
|
|
|
|
// 定义消息类型
|
|
|
interface Message {
|
|
|
id: number;
|
|
|
text: string;
|
|
|
isUser: boolean; // true 表示用户消息,false 表示机器人消息
|
|
|
}
|
|
|
|
|
|
// 定义对话类型
|
|
|
interface Conversation {
|
|
|
id: number;
|
|
|
title: string; // 对话的标题,可以是第一条用户消息的前20个字符
|
|
|
messages: Message[]; // 存储对话中的所有消息
|
|
|
timestamp: number; // 对话创建时间
|
|
|
}
|
|
|
|
|
|
// 历史对话列表
|
|
|
const historyItems = ref<HistoryItem[]>([
|
|
|
{ id: 1, text: '这是一个历史对话' },
|
|
|
{ id: 2, text: '这是另一个历史对话' },
|
|
|
]);
|
|
|
|
|
|
// 当前输入的消息
|
|
|
const message = ref('');
|
|
|
|
|
|
// 聊天内容列表
|
|
|
const chatMessages = ref<HistoryItem[]>([]);
|
|
|
|
|
|
// 发送消息
|
|
|
const sendMessage = async () => {
|
|
|
const msg = message.value.trim();
|
|
|
if (!msg) return;
|
|
|
|
|
|
// 如果没有当前对话,创建新对话
|
|
|
if (!currentConversation.value) {
|
|
|
currentConversation.value = {
|
|
|
id: Date.now(),
|
|
|
title: msg.slice(0, 20) + (msg.length > 20 ? '...' : ''),
|
|
|
messages: [],
|
|
|
timestamp: Date.now()
|
|
|
};
|
|
|
conversations.value.push(currentConversation.value);
|
|
|
}
|
|
|
|
|
|
// 添加用户消息
|
|
|
const userMessage: Message = {
|
|
|
id: Date.now(),
|
|
|
text: msg,
|
|
|
isUser: true
|
|
|
};
|
|
|
currentConversation.value.messages.push(userMessage);
|
|
|
|
|
|
// 清空输入框
|
|
|
message.value = '';
|
|
|
|
|
|
// 模拟机器人回复
|
|
|
try {
|
|
|
// TODO: 这里替换为实际的 AI 接口调用
|
|
|
const botResponse = await mockBotResponse(msg);
|
|
|
const botMessage: Message = {
|
|
|
id: Date.now(),
|
|
|
text: botResponse,
|
|
|
isUser: false
|
|
|
};
|
|
|
currentConversation.value.messages.push(botMessage);
|
|
|
} catch (error) {
|
|
|
console.error('发送消息失败:', error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 编辑历史对话
|
|
|
const editItem = (item: HistoryItem) => {
|
|
|
const newText = prompt('编辑对话内容:', item.text);
|
|
|
if (newText !== null) {
|
|
|
item.text = newText.trim();
|
|
|
const chatItem = chatMessages.value.find((chat) => chat.id === item.id);
|
|
|
if (chatItem) {
|
|
|
chatItem.text = newText.trim(); // 同步更新聊天内容
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 删除历史对话
|
|
|
const deleteItem = (item: HistoryItem) => {
|
|
|
historyItems.value = historyItems.value.filter((history) => history.id !== item.id);
|
|
|
chatMessages.value = chatMessages.value.filter((chat) => chat.id !== item.id); // 同步删除聊天内容
|
|
|
};
|
|
|
|
|
|
// 定义对话列表
|
|
|
const conversations = ref<Conversation[]>([]);
|
|
|
|
|
|
// 当前选中的对话
|
|
|
const currentConversation = ref<Conversation | null>(null);
|
|
|
|
|
|
// 选择对话
|
|
|
const selectConversation = (conversation: Conversation) => {
|
|
|
currentConversation.value = conversation;
|
|
|
};
|
|
|
|
|
|
// 删除对话
|
|
|
const deleteConversation = (conversation: Conversation) => {
|
|
|
conversations.value = conversations.value.filter(c => c.id !== conversation.id);
|
|
|
if (currentConversation.value?.id === conversation.id) {
|
|
|
currentConversation.value = null;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 创建新对话
|
|
|
const createNewChat = () => {
|
|
|
const newConversation: Conversation = {
|
|
|
id: Date.now(),
|
|
|
title: '新对话',
|
|
|
messages: [],
|
|
|
timestamp: Date.now()
|
|
|
};
|
|
|
conversations.value.push(newConversation);
|
|
|
currentConversation.value = newConversation;
|
|
|
};
|
|
|
|
|
|
// 编辑对话标题
|
|
|
const editConversationTitle = (conversation: Conversation) => {
|
|
|
const newTitle = prompt('编辑对话标题:', conversation.title);
|
|
|
if (newTitle !== null && newTitle.trim() !== '') {
|
|
|
conversation.title = newTitle.trim();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 模拟机器人回复
|
|
|
const mockBotResponse = async (msg: string): Promise<string> => {
|
|
|
return new Promise(resolve => {
|
|
|
setTimeout(() => {
|
|
|
resolve(`这是对 "${msg}" 的回复`);
|
|
|
}, 1000);
|
|
|
});
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
/* 重置默认样式 */
|
|
|
* {
|
|
|
margin: 0;
|
|
|
padding: 0;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
body {
|
|
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
|
|
background: linear-gradient(135deg, #f5f1ff 0%, #e8ddff 100%);
|
|
|
min-height: 100vh;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* 主容器 */
|
|
|
.main-container {
|
|
|
display: flex;
|
|
|
height: 100%; /* 确保主容器的高度是100% */
|
|
|
}
|
|
|
|
|
|
.container {
|
|
|
position: absolute;
|
|
|
background-color: transparent;
|
|
|
left: 100px;
|
|
|
top: 2%;
|
|
|
width: 94%;
|
|
|
height: 100%;
|
|
|
}
|
|
|
|
|
|
/* 历史对话栏 */
|
|
|
.sidebar {
|
|
|
width: 300px;
|
|
|
background: rgba(255, 255, 255, 0.5);
|
|
|
backdrop-filter: blur(10px);
|
|
|
border: 2px solid rgba(133, 88, 207, 0.5);
|
|
|
padding: 20px;
|
|
|
margin-left: 150px;
|
|
|
overflow-y: auto;
|
|
|
height: 80%; /* 设置高度与 .chat-area 的高度一致 */
|
|
|
margin-top: 5%; /* 确保与 .chat-area 的顶部对齐 */
|
|
|
border-radius:10px;
|
|
|
transform: translateX(100px);
|
|
|
}
|
|
|
|
|
|
.sidebar-title {
|
|
|
font-size: 26px;
|
|
|
font-weight: bold;
|
|
|
color: #333;
|
|
|
margin-bottom: 20px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
|
|
|
.history-list {
|
|
|
list-style: none;
|
|
|
}
|
|
|
|
|
|
.history-item {
|
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
border: 2px solid rgba(133, 88, 207, 0.5);
|
|
|
border-radius: 8px;
|
|
|
padding: 12px 16px;
|
|
|
margin-bottom: 12px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
cursor: pointer;
|
|
|
transition: all 0.3s;
|
|
|
}
|
|
|
|
|
|
.history-item:hover {
|
|
|
background: rgba(156, 136, 255, 0.1);
|
|
|
border-color: #9c88ff;
|
|
|
}
|
|
|
|
|
|
.history-text {
|
|
|
font-size: 18px;
|
|
|
color: #666;
|
|
|
flex: 1;
|
|
|
}
|
|
|
|
|
|
.history-actions {
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
|
|
|
.action-btn {
|
|
|
width: 24px;
|
|
|
height: 24px;
|
|
|
background: none;
|
|
|
border: none;
|
|
|
cursor: pointer;
|
|
|
border-radius: 4px;
|
|
|
transition: color 0.3s;
|
|
|
}
|
|
|
|
|
|
.action-btn:hover {
|
|
|
color: #9c88ff;
|
|
|
}
|
|
|
|
|
|
.new-chat-btn {
|
|
|
width: 100%;
|
|
|
padding: 12px 16px;
|
|
|
background: linear-gradient(45deg, #9c88ff, #ff6b9d);
|
|
|
color: white;
|
|
|
border: none;
|
|
|
border-radius: 8px;
|
|
|
font-size: 18px;
|
|
|
cursor: pointer;
|
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
|
text-align: center;
|
|
|
margin-top: 20px;
|
|
|
}
|
|
|
|
|
|
.new-chat-btn:hover {
|
|
|
transform: translateY(-2px);
|
|
|
box-shadow: 0 4px 15px rgba(156, 136, 255, 0.3);
|
|
|
}
|
|
|
|
|
|
.new-chat-btn:active {
|
|
|
transform: translateY(0);
|
|
|
}
|
|
|
|
|
|
/* 聊天区域 */
|
|
|
.chat-area {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
background: rgba(255, 255, 255, 0.5);
|
|
|
border: 2px solid rgba(133, 88, 207, 0.5);
|
|
|
margin-top: 5%;
|
|
|
margin-left: 7%;
|
|
|
width: 60%;
|
|
|
height: 80%;
|
|
|
border-radius: 10px;
|
|
|
}
|
|
|
|
|
|
.chat-content {
|
|
|
flex: 1;
|
|
|
padding: 40px;
|
|
|
overflow-y: auto;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 16px;
|
|
|
}
|
|
|
|
|
|
.welcome-message {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
border: 1px solid rgba(200, 180, 255, 0.3);
|
|
|
border-radius: 20px;
|
|
|
padding: 16px 24px;
|
|
|
box-shadow: 0 4px 20px rgba(156, 136, 255, 0.1);
|
|
|
max-width: 60%;
|
|
|
margin-bottom: 16px;
|
|
|
}
|
|
|
|
|
|
.bot-avatar {
|
|
|
width: 40px;
|
|
|
height: 40px;
|
|
|
background: transparent;
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-size: 18px;
|
|
|
color: white;
|
|
|
margin-right: 16px;
|
|
|
}
|
|
|
|
|
|
.bot-avatar img {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
|
|
|
.welcome-text {
|
|
|
font-size: 20px;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.user-message {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: flex-end;
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
border: 1px solid rgba(200, 180, 255, 0.3);
|
|
|
border-radius: 20px;
|
|
|
padding: 16px 24px;
|
|
|
box-shadow: 0 4px 20px rgba(156, 136, 255, 0.1);
|
|
|
max-width: 60%;
|
|
|
margin-bottom: 16px;
|
|
|
align-self: flex-end;
|
|
|
}
|
|
|
|
|
|
.user-text {
|
|
|
font-size: 16px;
|
|
|
color: #333;
|
|
|
margin-right: 16px;
|
|
|
}
|
|
|
|
|
|
.user-avatar {
|
|
|
width: 40px;
|
|
|
height: 40px;
|
|
|
background: transparent;
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.user-avatar img {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
|
|
|
.bot-message {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
border: 1px solid rgba(200, 180, 255, 0.3);
|
|
|
border-radius: 20px;
|
|
|
padding: 16px 24px;
|
|
|
box-shadow: 0 4px 20px rgba(156, 136, 255, 0.1);
|
|
|
max-width: 60%;
|
|
|
margin-bottom: 16px;
|
|
|
align-self: flex-start;
|
|
|
}
|
|
|
|
|
|
.message-text {
|
|
|
font-size: 16px;
|
|
|
color: #333;
|
|
|
margin: 0 16px;
|
|
|
}
|
|
|
|
|
|
/* 输入区域 */
|
|
|
.input-area {
|
|
|
padding: 30px;
|
|
|
border-top: 1px solid rgba(200, 180, 255, 0.3);
|
|
|
background: rgba(255, 255, 255, 0.5);
|
|
|
backdrop-filter: blur(10px);
|
|
|
height: 300px;
|
|
|
display: flex; /* 使用 Flexbox 布局 */
|
|
|
justify-content: center; /* 水平方向居中 */
|
|
|
align-items: center; /* 垂直方向居中 */
|
|
|
border-radius: 0 0 10px 10px; /* 设置下两个角为圆角,值为 20px */
|
|
|
}
|
|
|
|
|
|
.input-container {
|
|
|
display: flex;
|
|
|
width: 100%;
|
|
|
gap: 16px;
|
|
|
align-items: flex-end; /* 子元素在垂直方向底部对齐 */
|
|
|
position: relative; /* 设置相对定位以便按钮可以绝对定位 */
|
|
|
}
|
|
|
|
|
|
.message-input {
|
|
|
flex: 1;
|
|
|
height: 230px;
|
|
|
padding: 16px;
|
|
|
border: 1px solid rgba(200, 180, 255, 0.5);
|
|
|
border-radius: 12px;
|
|
|
resize: none;
|
|
|
font-size: 20px;
|
|
|
font-family: inherit;
|
|
|
outline: none;
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
transition: border-color 0.3s;
|
|
|
}
|
|
|
|
|
|
.message-input:focus {
|
|
|
border-color: #9c88ff;
|
|
|
}
|
|
|
|
|
|
.send-button {
|
|
|
position: absolute;
|
|
|
bottom: 20px;
|
|
|
right: 20px;
|
|
|
padding: 12px 24px;
|
|
|
background: linear-gradient(45deg, #9c88ff, #ff6b9d);
|
|
|
color: white;
|
|
|
border: none;
|
|
|
border-radius: 8px;
|
|
|
font-size: 20px;
|
|
|
cursor: pointer;
|
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
.send-button:hover {
|
|
|
transform: translateY(-2px);
|
|
|
box-shadow: 0 4px 15px rgba(156, 136, 255, 0.3);
|
|
|
}
|
|
|
|
|
|
.send-button:active {
|
|
|
transform: translateY(0);
|
|
|
}
|
|
|
|
|
|
/* 响应式设计 */
|
|
|
@media (max-width: 768px) {
|
|
|
.sidebar {
|
|
|
width: 250px;
|
|
|
}
|
|
|
|
|
|
.search-bar {
|
|
|
max-width: 200px;
|
|
|
}
|
|
|
|
|
|
.nav-icons {
|
|
|
gap: 12px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
.main-container {
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.sidebar {
|
|
|
width: 100%;
|
|
|
height: 150px;
|
|
|
border-right: none;
|
|
|
border-bottom: 1px solid rgba(200, 180, 255, 0.3);
|
|
|
}
|
|
|
|
|
|
.chat-area {
|
|
|
height: calc(100vh - 220px);
|
|
|
margin-top: 150px; /* 调整为150px以适应HeaderBar和侧边栏的高度 */
|
|
|
}
|
|
|
|
|
|
.search-bar {
|
|
|
display: none;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 滚动条样式 */
|
|
|
::-webkit-scrollbar {
|
|
|
width: 6px;
|
|
|
}
|
|
|
|
|
|
::-webkit-scrollbar-track {
|
|
|
background: rgba(200, 180, 255, 0.1);
|
|
|
border-radius: 3px;
|
|
|
}
|
|
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
|
background: rgba(156, 136, 255, 0.3);
|
|
|
border-radius: 3px;
|
|
|
}
|
|
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
|
background: rgba(156, 136, 255, 0.5);
|
|
|
}
|
|
|
</style>
|