用vue3写一个AI聊天室
效果图如下:
1、页面布局:
<template><div class="body" style="background-color: rgb(244, 245, 248); height: 730px"><div class="container"><div class="right"><div class="top">AI问答</div><div class="chat" ref="chatContainer"><divv-for="(item, i) in msgList":key="i":class="item.type == '1' ? 'rightMsg' : 'leftMsg'"><imgv-if="item.type == '0'"src="../assets/images/AI.png"alt=""/><div class="msg">{{ item.content }}</div><imgv-if="item.type == '1'"src="../assets/images/me.png"alt=""/></div><!-- <div v-if="msgList.length >= 10" class="separator">-------------- 本AI问答仅显示最近10条对话 --------------</div> --></div><div class="bottom"><input v-model="value" placeholder="请输入您想提问的内容" /><button @click="onSend"><img src="../assets/images/send.png" alt="发送" /></button></div></div></div></div>
</template>
2、封转函数(用户输入问题和AI回答问题):
const msgList = reactive([]);
//提问
const userQuestion = (question) => {var userMsg = {content: question,type: "1",id: Date.now(),};msgList.push(userMsg);
};
//回答
const AIReplay = (replay) => {var autoReplyMsg = {content: replay,type: "0",id: Date.now(),};msgList.push(autoReplyMsg);
};
3、从后端获取最近的10个对话:
const getMes = () => {getQandA({}).then((res) => {console.log(res);if (res.data.status == 200) {// 获取最近五条问答信息for (var i = 0; i < 5; i++) {userQuestion(res.data.data[i].inputMessage);AIReplay(res.data.data[i].aiResult);}scrollToNew();}});
};
4、为了使用户发送问题后内容滚动在最底处,写一个函数让其自动滚动,在发送信息和获取信息时调用该函数即可
// 等待DOM更新完成。自动滚动到最新发送的消息处
const scrollToNew = async () => {await nextTick();const chatContainer = document.querySelector(".chat");if (chatContainer) {chatContainer.scrollTop = chatContainer.scrollHeight;}
};
5、点击发送按钮,发送到后端进行处理:
const onSend = () => {// 发送用户输入的消息AIQandA({inputMessage: value.value,}).then((res) => {console.log(res);if (res.data.status == 200) {console.log(6666);AIReplay(res.data.data);scrollToNew();}});userQuestion(value.value);scrollToNew();value.value = "";
};
完整代码如下:
<template><Header></Header><div class="body" style="background-color: rgb(244, 245, 248); height: 730px"><header><div class="cover"><imgsrc="1.png"alt=""style="width: 100%; height: 100%"/></div></header><main><div class="container"><div class="right"><div class="top">AI问答</div><div class="chat" ref="chatContainer"><divv-for="(item, i) in msgList":key="i":class="item.type == '1' ? 'rightMsg' : 'leftMsg'"><imgv-if="item.type == '0'"src="../assets/images/AI.png"alt=""/><div class="msg">{{ item.content }}</div><imgv-if="item.type == '1'"src="../assets/images/me.png"alt=""/></div><!-- <div v-if="msgList.length >= 10" class="separator">-------------- 本AI问答仅显示最近10条对话 --------------</div> --></div><div class="bottom"><input v-model="value" placeholder="请输入您想提问的内容" /><button @click="onSend"><img src="../assets/images/send.png" alt="发送" /></button></div></div></div></main></div><foot></foot>
</template><script setup>
import { ref, reactive, nextTick, onMounted } from "vue";
import Header from "../components/header.vue";
import foot from "../components/foot.vue";
import { AIQandA, getQandA } from "../api/AIApi";
const value = ref("");
const msgList = reactive([]);
onMounted(() => {getMes();
});
// 等待DOM更新完成。自动滚动到最新发送的消息处
const scrollToNew = async () => {await nextTick();const chatContainer = document.querySelector(".chat");if (chatContainer) {chatContainer.scrollTop = chatContainer.scrollHeight;}
};
const userQuestion = (question) => {var userMsg = {content: question,type: "1",id: Date.now(),};msgList.push(userMsg);
};
const AIReplay = (replay) => {var autoReplyMsg = {content: replay,type: "0",id: Date.now(),};msgList.push(autoReplyMsg);
};
const getMes = () => {getQandA({}).then((res) => {console.log(res);if (res.data.status == 200) {// 获取最近五条问答信息for (var i = 0; i < 5; i++) {userQuestion(res.data.data[i].inputMessage);AIReplay(res.data.data[i].aiResult);}scrollToNew();}});
};const onSend = () => {// 发送用户输入的消息AIQandA({inputMessage: value.value,}).then((res) => {console.log(res);if (res.data.status == 200) {console.log(6666);AIReplay(res.data.data);scrollToNew();}});userQuestion(value.value);scrollToNew();value.value = "";
};
</script><style scoped lang="scss">
.body {color: #fff;font-weight: 900;letter-spacing: 2px;width: 100%;height: 100%;background-size: 50%;display: flex;align-items: center;position: relative;
}
main {/* border: 1px solid red; */width: 1400px;height: 600px;margin: 100px auto;display: flex;
}
.cover {position: absolute;top: 0px;z-index: 0;height: 180px;width: 1483px;left: 50%;margin-left: -754px;overflow: hidden;
}
.body {:deep(.slick-slide) {text-align: center;height: 100%;line-height: 100%;background: #364d79;overflow: hidden;}:deep(.slick-arrow.custom-slick-arrow) {width: 25px;height: 25px;font-size: 25px;color: #fff;background-color: rgba(31, 45, 61, 0.11);transition: ease all 0.3s;opacity: 0.3;z-index: 1;}:deep(.slick-arrow.custom-slick-arrow:before) {display: none;}:deep(.slick-arrow.custom-slick-arrow:hover) {color: #fff;opacity: 0.5;}:deep(.slick-slide h3) {color: #fff;}
}.container {z-index: 1;// border: solid 1px #bebebe;width: 85%;height: 100%;margin: -6px auto;display: flex;justify-content: center;.right {flex: 1;// border-radius: 10px;background-color: white;display: flex;flex-direction: column;height: 600px;.top {height: 70px;background-color: rgba(147, 213, 255, 0.764);width: 100%;font-size: 22px;text-align: center;line-height: 70px;}.chat {flex: 1;max-height: 580px;overflow-y: auto;padding: 10px;.leftMsg,.rightMsg {display: flex;flex-direction: row;justify-content: start;align-items: center;margin: 10px;img {width: 40px;height: 40px;border-radius: 20px;overflow: hidden;object-fit: cover;margin: 0 10px;}.msg {display: inline-block;padding: 10px;word-wrap: anywhere;max-width: 600px;background-color: #364d79;border-radius: 10px;}}.rightMsg {justify-content: end;.msg {color: black;background-color: #dfdfdf;}}}.bottom {height: 45px;display: flex;align-items: center;width: 80%;margin: 10px auto;input {width: 90%;border: 1px solid rgb(171, 171, 171);border-right: none;height: 40px;color: black;text-indent: 2px;line-height: 40px;border-radius: 10px 0 0 10px;}button {cursor: pointer;width: 10%;border: none;outline: none;height: 45px;border-radius: 0 10px 10px 0;background: linear-gradient(to right,rgb(146, 197, 255),rgb(200, 134, 200));}img {width: 20px;height: 20px;}}}
}
.separator {color: rgb(133, 132, 132);text-align: center;font-size: 15px;font-weight: normal;
}
</style>
相关文章:

用vue3写一个AI聊天室
效果图如下: 1、页面布局: <template><div class"body" style"background-color: rgb(244, 245, 248); height: 730px"><div class"container"><div class"right"><div class"…...

photomaker:customizing realistic human photos via stacked id embedding
PhotoMaker: 高效个性化定制人像照片文生图 - 知乎今天分享我们团队最新的工作PhotoMaker的技术细节。该工作开源5天Githubstar数已过6千次,已列入Github官方Trending榜第一位,PaperswithCode热度榜第一位,HuggingFace Spaces趋势榜第一位。项…...
FFmpeg - 如何在Linux上安装支持CUDA的FFmpeg
FFmpeg - 如何在Linux(Ubuntu)上安装支持CUDA的FFmpeg 笔者认为现在的很多“xx教程”只讲干什么不讲为什么,这样即使报错了看官也不知道如何解决。 在安装过程的探索部分会记录我的整个安装过程以及报错和报错的解决办法。 在省流之一步到位的方法部分会省去安装过…...

新火种AI|商汤发布下棋机器人元萝卜,率先深入家庭场景。
作者:小岩 编辑:彩云 如今提及生成式AI(AIGC),已经不算什么新鲜产物了。自2014年GAN神经网络出现,2017年Transformer架构演进,再加上2023年ChatGPT的大火,无不说明生成式AI正在有条…...

CSS实现三栏自适应布局(两边固定,中间自适应)
绝对定位的元素会脱离文档流,它们是相对于包含块(通常是最近的具有相对定位、绝对定位或固定定位属性的父元素)进行定位的。当你把一个绝对定位的元素的高度设置为100%时,它会相对于其包含块的高度来确定自己的高度。如果包含块是…...

MoCo 算法阅读记录
论文地址:🐰 何凯明大神之作,通过无监督对比学习预训练Image Encoder的表征能力。后也被许多VLP算法作为ITC的底层算法来使用。 一方面由于源代码本身并不复杂,但是要求多GPU分布式训练,以及需要下载ImageNet这个大规模…...

华为OD机试 - 数组连续和 - 滑动窗口(Java 2024 C卷 100分)
华为OD机试 2024C卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷C卷)》。 刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试…...

微店micro获得微店micro商品详情,API接口封装系列
微店商品详情API接口封装系列主要涉及注册账号、获取API密钥、选择API接口、发送请求以及处理响应等步骤。以下是详细的流程: 请求示例,API接口接入Anzexi58 一、注册账号并获取API密钥 首先,你需要在微店开放平台注册一个账号。注册成功后…...

C语言中的数据结构--链表的应用1(2)
前言 上一节我们学习了链表的概念以及链表的实现,那么本节我们就来了解一下链表具体有什么用,可以解决哪些实质性的问题,我们借用习题来加强对链表的理解,那么废话不多说,我们正式进入今天的学习 单链表相关经典算法O…...
.Net6 使用Autofac进行依赖注入
一、背景 刚接触.net 6,记录一下在.net6上是怎么使用Autofac进行动态的依赖注入的 二、注入方式 1、新建一个webapi项目,框架选择net 6 2、引用Nuget包---Autofac.Extensions.Dependency 3、在Program.cs上添加如下代码 //依赖注入 builder.Host.Us…...

第十二届蓝桥杯省赛真题(C/C++大学B组)
目录 #A 空间 #B 卡片 #C 直线 #D 货物摆放 #E 路径 #F 时间显示 #G 砝码称重 #H 杨辉三角形 #I 双向排序 #J 括号序列 #A 空间 #include <bits/stdc.h> using namespace std;int main() {cout<<256 * 1024 * 1024 / 4<<endl;return 0; } #B 卡片…...

DC40V降压恒压芯片H4120 40V转5V 3A 40V降压12V 车充降压恒压控制器
同步整流恒压芯片在现代电子设备中发挥着重要作用,为各种设备提供了稳定、高效的电源管理解决方案。 同步整流恒压芯片是一种电源管理芯片,它能够在不同电压输入条件下保持输出电压恒定。这种芯片广泛应用于各种电子设备中,如通讯设备、液晶…...

2、Qt UI控件 -- qucsdk项目使用
前言:上一篇文章讲了qucsdk的环境部署,可以在QDesigner和Qt Creator中看到qucsdk控件,这一篇来讲下在项目中使用qucsdk库中的控件。 一、准备材料 要想使用第三方库,需要三个先决条件, 1、控件的头文件 2、动/静态链…...
MATLAB算法实战应用案例精讲-【人工智能】AIGC概念三部曲(三)
目录 前言 算法原理 大模型 什么是AIGC? AIGC和Chat GPT的关系 常见的AIGC应用...

外汇110:外汇交易不同货币类别及交易注意事项!
外汇市场是一个庞大而复杂的市场,其中有各种各样的货币品种。对于外汇投资者来说,了解外汇品种的特性和走势是比较重要的。1. 货币种类 外汇市场中的货币品种可以分为主要货币、次要货币和外围货币。 主要货币:主要指美元、欧元、英镑、日元、…...
gerrit 拉取失败
在浏览器gerrit的设置界面设置的邮箱地址和在命令行使用git config --gloable user.email设置的邮箱地址必须保持一致吗 在浏览器gerrit的设置界面设置的邮箱地址和在命令行使用git config --global user.email设置的邮箱地址并不一定需要保持一致。这两个邮箱地址是独立的&am…...
大数据行业英语单词巩固20240410
20240410 Communication - 沟通 Example: Effective communication is essential for project success. 有效的沟通对于项目的成功至关重要。 Collaboration - 协作 Example: Team collaboration is crucial in achieving our goals. 团队协作对于实现我们的目标至关重要。 …...

天软特色因子看板 (2024.4 第3期)
该因子看板跟踪天软特色因子A05005(近一月单笔流出金额占比(%),该因子为近一月单笔流出金额占比(% 均值因子,用以刻画下跌时的 单成交中可能存在的抄底现象 今日为该因子跟踪第3期,跟踪其在SH000852 (中证1000) 中的表现,要点如下…...

使用QT 开发不规则窗体
使用QT 开发不规则窗体 不规则窗体贴图法的不规则窗体创建UI模板创建一个父类创建业务窗体main函数直接调用user_dialog创建QSS文件 完整的QT工程 不规则窗体 QT中开发不规则窗体有两种方法:(1)第一种方法,使用QWidget::setMask函…...
如何构建企业经营所需的商业智能(BI)能力
构建企业经营所需的商业智能(BI)能力是一项涉及诸多关键环节与细致考量的系统工程,通过科学的数据处理、分析与应用,赋能企业实现精准决策,提升运营效率,优化业务流程,并在竞争激烈的市场环境中…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...

基于谷歌ADK的 智能产品推荐系统(2): 模块功能详解
在我的上一篇博客:基于谷歌ADK的 智能产品推荐系统(1): 功能简介-CSDN博客 中我们介绍了个性化购物 Agent 项目,该项目展示了一个强大的框架,旨在模拟和实现在线购物环境中的智能导购。它不仅仅是一个简单的聊天机器人,更是一个集…...

AI书签管理工具开发全记录(十八):书签导入导出
文章目录 AI书签管理工具开发全记录(十八):书签导入导出1.前言 📝2.书签结构分析 📖3.书签示例 📑4.书签文件结构定义描述 🔣4.1. 整体文档结构4.2. 核心元素类型4.3. 层级关系4.…...