用vue3写一个AI聊天室

news2025/6/26 16:59:11

效果图如下:

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">
            <div
              v-for="(item, i) in msgList"
              :key="i"
              :class="item.type == '1' ? 'rightMsg' : 'leftMsg'"
            >
              <img
                v-if="item.type == '0'"
                src="../assets/images/AI.png"
                alt=""
              />
              <div class="msg">{{ item.content }}</div>
              <img
                v-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">
        <img
          src="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">
            <div
              v-for="(item, i) in msgList"
              :key="i"
              :class="item.type == '1' ? 'rightMsg' : 'leftMsg'"
            >
              <img
                v-if="item.type == '0'"
                src="../assets/images/AI.png"
                alt=""
              />
              <div class="msg">{{ item.content }}</div>
              <img
                v-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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1584112.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot3 + uniapp 对接 阿里云0SS 实现上传图片视频到 0SS 以及 0SS 里删除图片视频的操作(最新)

SpringBoot3 uniapp 对接 阿里云0SS 实现上传图片视频到 0SS 以及 0SS 里删除图片视频的操作 最终效果图uniapp 的源码UpLoadFile.vuedeleteOssFile.jshttp.js SpringBoot3 的源码FileUploadController.javaAliOssUtil.java 最终效果图 uniapp 的源码 UpLoadFile.vue <tem…

Netty出坑记

NIO&#xff1a; 一个线程处理多个请求 BIO&#xff1a; 阻塞 netty 编码解码 TFO&#xff1a; 校验cookie合法性&#xff0c;不合法 TCP流程 设计QQ&#xff1a; 登录过程&#xff0c;client TCP协议向server发送信息&#xff0c;HTTP协议下载信息 发消息&#xff1a;clie…

Win10系统VScode远程连接VirtualBox安装的Ubuntu20.04.5

1.打开虚拟机&#xff0c;在中端中输入命令: sudo apt-get install openssh-server 安装ssh 我这里已经安装完成&#xff0c;故显示是这样 2.输入命令&#xff1a;sudo systemctl start ssh 启动远程连接 注意&#xff0c;如果使用VirtualBox安装的虚拟机&#xff0c;需要启用…

故障诊断 | Matlab实现基于小波包结合鹈鹕算法优化卷积神经网络DWT-POA-CNN实现电缆故障诊断算法

故障诊断 | Matlab实现基于小波包结合鹈鹕算法优化卷积神经网络DWT-POA-CNN实现电缆故障诊断算法 目录 故障诊断 | Matlab实现基于小波包结合鹈鹕算法优化卷积神经网络DWT-POA-CNN实现电缆故障诊断算法分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现基于小波…

【Qt 学习笔记】QWidget的geometry属性及window frame的影响

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ QWidget的geometry属性 文章编号&#xff1a;Qt 学习笔记 / 16 文章目…

Flutter Your project requires a newer version of the Kotlin Gradle plugin

在开发Flutter项目的时候,遇到这个问题Flutter Your project requires a newer version of the Kotlin Gradle plugin 解决方案分两步: 1、在android/build.gradle里配置最新版本的kotlin 根据提示的kotlin官方网站搜到了Kotlin的最新版本是1.9.23,如下图所示: 同时在Ko…

【现代C++】默认函数和删除函数

现代C中的默认函数和删除函数特性允许开发者更精确地控制类的行为&#xff0c;特别是关于对象拷贝、赋值、析构和构造的行为。这些特性可以帮助开发者避免不必要的对象拷贝&#xff0c;防止资源泄露&#xff0c;以及避免一些常见的编程错误。 1. 默认函数&#xff08;Defaulte…

docker 安装canal

一、新建文件夹 新建文件夹logs, 新建文件canal.properties instance.properties docker.compose.yml canal.propertie 修改如下&#xff1a; 修改instance.properties内容如下 1.1 canal.properties ################################################# ######### …

ES6 关于Class类的继承 extends(2024-04-10)

1、简介 类Class 可以通过extends关键字实现继承&#xff0c;让子类继承父类的属性和方法。extends 的写法比 ES5 的原型链继承&#xff0c;要清晰和方便很多。 class Foo {constructor(x, y) {this.x x;this.y y;console.log(父类构造函数)}toString() {return ( this.x …

42-软件部署实战(下):IAM系统安全加固、水平扩缩容实战

IAM应用安全性加固 iam-apiserver、iam-authz-server、MariaDB、Redis和MongoDB这些服务&#xff0c;都提供了绑定监听网卡的功能。将服务绑定到内网网卡上。 我们也可以通过iptables来实现类似的功能&#xff0c;通过将安全问题统一收敛到iptables规则&#xff0c;可以使我…

基于GAN的图像补全实战

数据与代码地址见文末 论文地址:http://iizuka.cs.tsukuba.ac.jp/projects/completion/data/completion_sig2017.pdf 1.概述 图像补全,即补全图像中的覆盖和缺失部分, 网络整体结构如下图所示,整体网络结构还是采取GAN,对于生成器,网络结构采取Unet的形式,首先使用卷积…

Asp .Net Core 系列:集成 Refit 和 RestEase 声明式 HTTP 客户端库

背景 .NET 中 有没有类似 Java 中 Feign 这样的框架&#xff1f;经过查找和实验&#xff0c;发现 在 .NET 平台上&#xff0c;虽然没有直接的 Feign 框架的端口&#xff0c;但是有一些类似的框架和库&#xff0c;它们提供了类似的功能和设计理念。下面是一些在 .NET 中用于声明…

web安全学习笔记(9)

记一下第十三课的内容。 准备工作&#xff1a;在根目录下创建template目录&#xff0c;将login.html放入其中&#xff0c;在该目录下新建一个reg.html。在根目录下创建一个function.php 一、函数声明与传参 PHP中的函数定义和其他语言基本上是相同的。我们编辑function.php …

HTTP与HTTPS:深度解析两种网络协议的工作原理、安全机制、性能影响与现代Web应用中的重要角色

HTTP (HyperText Transfer Protocol) 和 HTTPS (Hypertext Transfer Protocol Secure) 是互联网通信中不可或缺的两种协议&#xff0c;它们共同支撑了全球范围内的Web内容传输与交互。本文将深度解析HTTP与HTTPS的工作原理、安全机制、性能影响&#xff0c;并探讨它们在现代Web…

AI大模型引领未来智慧科研暨ChatGPT自然科学高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

修复 Windows 上的 PyTorch 1.1 github 模型加载权限错误

问题: 在 Windows 计算机上执行示例 github 模型加载时,生成了 master.zip 文件的权限错误(请参阅下面的错误堆栈跟踪)。 错误堆栈跟踪: 在[4]中:en2de = torch.hub.load(pytorch/fairseq, transformer.wmt16.en-de, tokenizer=moses, bpe=subword_nmt) 下载:“https://…

Linux网络的封包和拆包

一般使用socket 到令牌环网然后向上逐渐拆包 MTU:最大的传输单元 以太网&#xff1a;1500 mss&#xff1a;网络类型&#xff0c;线路&#xff0c;以及特性相关

SpringCloudAlibaba-概述(一)

目录地址&#xff1a; SpringCloudAlibaba整合-CSDN博客 记录SpringCloudAlibaba的整合过程 一、简单概述一下项目情况 项目主要有4个模块和4个微服务&#xff1b; 项目结构如下&#xff1a; mall&#xff1a;父工程 -- common&#xff1a;公共组件&#xff0c;存放公用的实…

Vue.js------vue基础

1. 能够了解更新监测, key作用, 虚拟DOM, diff算法2. 能够掌握设置动态样式3. 能够掌握过滤器, 计算属性, 侦听器4. 能够完成品牌管理案例 一.Vue基础_更新监测和key 1.v-for更新监测 目标&#xff1a;目标结构变化, 触发v-for的更新 情况1: 数组翻转情况2: 数组截取情况3…

专为玄学设计的7B大模型:依托10,000+高质量指令,全面覆盖玄学领域的大模型

前言 现如今&#xff0c;AI大模型已经无孔不入&#xff0c;连玄学领域也"在劫难逃"。前有新闻称数百万人正在"求神拜佛"般地与ChatGPT交流&#xff0c;后又有教堂聘请"AI传教士"协助神职人员进行宗教仪式。可以说&#xff0c;"科学的尽头就…