feat:增加历史消息模块

This commit is contained in:
2026-05-31 21:14:32 +08:00
parent 41ef49a04f
commit d47383cd95
9 changed files with 426 additions and 139 deletions

View File

@@ -1,145 +1,100 @@
<template>
<div class="app">
<!-- 头部 -->
<header class="header">食刻 AI</header>
<div class="layout">
<!-- 移动端抽屉 -->
<el-drawer
v-model="drawerVisible"
direction="ltr"
:size="250"
class="mobile-drawer"
>
<div class="sidebar"><session-message /></div>
</el-drawer>
<!-- 聊天区域 -->
<main v-if="messages.length" class="chat-box">
<div v-for="(item, index) in messages" :key="index" class="message" :class="item.role">
<!-- 用户 -->
<div v-if="item.role === 'user'" class="bubble">
{{ item.text }}
<el-aside v-if="!isMobile" width="250px" class="pc-aside">
<div class="sidebar"><session-message /></div>
</el-aside>
<!-- 主内容 -->
<el-container class="main-container">
<el-header class="header">
<el-icon v-if="isMobile" @click="drawerVisible = true"><Fold/></el-icon>
<div class="website-title">
网站标题
</div>
</el-header>
<!-- AI -->
<MdPreview v-else :modelValue="item.text" class="md"/>
</div>
<div v-if="loading" class="loading">
<el-icon class="is-loading"><Loading /></el-icon>
思考中
</div>
</main>
<!-- 输入区 -->
<footer class="input-area">
<el-input v-model="input" placeholder="请输入你的食材" @keyup.enter="send" clearable/>
<el-button type="primary" :loading="loading" @click="send">发送</el-button>
</footer>
<el-main class="main-body">
<agent />
</el-main>
</el-container>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Loading } from "@element-plus/icons-vue";
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/preview.css';
<script lang="ts" setup>
import Agent from "./components/Agent.vue";
import SessionMessage from "./components/SessionMessage.vue";
import { ref, onMounted, onUnmounted } from 'vue'
import { Fold } from '@element-plus/icons-vue'
interface Message {
role: "user" | "ai";
text: string;
const isMobile = ref(window.innerWidth < 768)
const drawerVisible = ref(false)
const onResize = () => {
isMobile.value = window.innerWidth < 768
if (!isMobile.value) drawerVisible.value = false
}
const input = ref("");
const messages = ref<Message[]>([]);
const loading = ref(false);
const send = async () => {
if (!input.value.trim()) return;
const userMsg = input.value;
input.value = "";
messages.value.push({ role: "user", text: userMsg });
loading.value = true;
try {
const response = await fetch(
"/chief-agent-api/chat/stream",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: userMsg })
}
);
messages.value.push({ role: "ai", text: "" });
const reader = response.body!.getReader();
const decoder = new TextDecoder("utf-8");
let aiText = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
aiText += decoder.decode(value, { stream: true });
messages.value[messages.value.length - 1].text = aiText;
}
} finally {
loading.value = false;
}
};
onMounted(() => window.addEventListener('resize', onResize))
onUnmounted(() => window.removeEventListener('resize', onResize))
</script>
<style lang="scss" scoped>
.app {
<style lang="scss">
.layout {
display: flex;
flex-direction: column;
gap: 10px;
padding: 5px;
height: 100%;
.header {
text-align: center;
font-size: 18px;
font-weight: 600;
.pc-aside {
background: gainsboro;
color: #fff;
}
.chat-box {
background-color: #FBF9F5;
.sidebar {
padding: 16px;
color: #333;
}
.main-container {
flex: 1;
overflow-y: auto;
padding: 10px;
.message {
margin-bottom: 12px;
}
.user {
text-align: right;
}
.ai {
text-align: left;
}
.bubble {
display: inline-block;
padding: 5px 10px;
border-radius: 14px;
max-width: 85%;
word-break: break-word;
line-height: 1.6;
background: #409eff;
color: #fff;
}
.md {
background: #f0f2f5;
border-radius: 14px;
padding: 5px;
}
}
.input-area {
min-width: 0;
display: flex;
gap: 8px;
}
flex-direction: column;
.loading {
text-align: center;
color: #909399;
font-size: 14px;
margin: 8px 0;
.header {
height: 50px;
background: #fff;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.el-icon {
position: absolute;
left: 12px;
cursor: pointer;
}
.website-title {
font-size: 18px;
font-weight: 600;
}
}
.main-body {
flex: 1;
padding: 10px;
}
}
}
</style>
</style>