You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>