chatgpt这么火?前端如何实现类似chatgpt的对话页面

news2025/2/23 6:57:45

📋 个人简介

  • 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者😜
  • 📝 个人主页:馆主阿牛🔥
  • 🎉 支持我:点赞👍+收藏⭐️+留言📝
  • 📣 系列专栏:前端实用小demo🍁
  • 💬格言:迄今所有人生都大写着失败,但不妨碍我继续向前!🔥

请添加图片描述

目录

    • 📋 个人简介
  • 前言
    • 最终效果
    • 页面布局
      • flex布局一
      • flex布局二(重点)
    • js部分
    • 完整代码
  • 结语

前言

自从去年11月份chatgpt出圈之后,他的热度就居高不减,也出现了很多人借助接口开发的国内版本,那么本篇博客就从前端的角度来看看前端如何实现类似chatgpt的对话功能!

最终效果

源码在文末获取!

在这里插入图片描述
因为这是我写在一个项目中的,单独提出来可能配色效果不同,但功能和做法只要我们掌握了,那么自己想怎么写就怎么写!

页面布局

这一块比较简单,分析过chatgpt的页面的就会知道,他的页面布局方式是采用flex布局的,flex布局确实好用,那么我也是基于Bootsrap+jquery+flex布局完成了简易版的对话功能!主要有两个地方用到了flex布局!

flex布局一

在这里插入图片描述
这里的头像和文字采用的就是flex布局,并且文字和图片顶部对齐,防止文字较多依旧和图片中间对齐的bug。
需要设置css:

display: flex;
align-items: flex-start;

其中align-items: flex-start;的作用就是让文字与图片顶部对齐!

在这里插入图片描述

flex布局二(重点)

第二处用到flex布局的地方就是这个搜索框:
在这里插入图片描述
很多人觉得这个对话框很简单,flex布局实现输入框和按钮在同一行确实简单,但你要好好看看chatgpt的官网,都是有小细节的,这里面还是有很多知识点的。

首先,我要说的是这个输入框用的textarea,而不是input,区别在于,input输入的内容是不能换行的,但textarea文本框可以,但使用textarea的问题是,参数rows设置为一行,这个文本框的高度会很低,达不到chatgpt的那个页面要求,rows设置大一点或者这个文本框的高度给高一点会有一个问题就是输入时他的光标不会在文本框的高度中间,而是在第一行,我们是没法通过其他方式让输入光标垂直居中的,因此这也不符合chatgpt页面的要求,所以这确实是个值的学习的一点!看了chatgpt页面的做法后,我悟了,下面一张图来说明chatgpt是如何做的:

在这里插入图片描述
如图,你只要将textarea边框取消掉,然后focus伪类将边框效果也取消掉,外边再套一个div边框将textarea文本框和按钮套在里面就好了!

.ipt{
   display:flex;
   align-items: center;
   position: absolute;
   bottom: 60px;
   margin: 0 15px;
   padding-right: 15px;
   border-radius: 15px;
   width: calc(100% - 30px);
   height: 50px;
   border: 1px solid #e7eaec;
}
.ipt textarea {
   resize: none;
   overflow-y: auto;
   border: none;
   box-shadow: none;
}
.ipt textarea:focus{
   border: none !important;
   box-shadow: none !important;
}

最后,将这个输入框定位到页面底部就好!

js部分

首先,页面部分,我们添加消息到页面,包括用户的问题以及ai的回复,添加消息到页面时需要向上滚动:

// 添加用户消息到窗口
function addUserMessage(message) {
  var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + userIcon + '"><p class="message-text">' + message + '</p></div>');
  chatWindow.append(messageElement);
  chatInput.val('');
  chatWindow.animate({ scrollTop: chatWindow.prop('scrollHeight') }, 500);
}

// 添加回复消息到窗口
function addBotMessage(message) {
  var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + botIcon + '"><p class="message-text">' + message + '</p></div>');
  chatWindow.append(messageElement);
  chatInput.val('');
  chatWindow.animate({ scrollTop: chatWindow.prop('scrollHeight') }, 500);
}

这里消息添加带页面后,清空了输入框的内容,接下来还需要给输入框添加加一个键盘事件,也就是点击enter键也可以发送消息!

// 处理 Enter 键按下
chatInput.keypress(function(e) {
  if (e.which == 13) {
    chatBtn.click();
  }
});

最后就是发送消息与获得消息的一部分了:

// 处理用户输入
chatBtn.click(function() {
  var message = chatInput.val();
  if (message.length == 0){
    common_ops.alert("请输入内容!")  // 弹窗  
    return  
  }
  addUserMessage(message);
  
  chatBtn.attr('disabled',true) // 消息发送后让提交按钮不可点击

  // 发送信息到后台
  $.ajax({
    url: '/chat',
    method: 'POST',
    data: {
      "prompt": JSON.stringify(message)
    },
    success: function(res) {
      res = JSON.parse(res);
      addBotMessage(res.content);
      chatBtn.attr('disabled',false)  // 成功接受消息后让提交按钮再次可以点击
    },
    error: function(jqXHR, textStatus, errorThrown) {
      addBotMessage('<span style="color:red;">' + '出错啦!请稍后再试!' + '</span>');
      chatBtn.attr('disabled',false) 
    }
  });
});

这些逻辑都很简单,我不再总结,需要注意的是,我在发送消息到后台等待相应的过程让按钮的状态是不可点击的,直到后台返回消息才可以进行下一次问答!但这里我没有处理键盘事件,也就是说你可以点击enter继续向后台发送消息,这也是一个bug,只不过我没有处理,你们不需要的可以去掉这个键盘事件就好了,当然也可以在发送消息到获得回答的这个时间段像禁用发送按钮一样,禁止enter键盘事件或者解绑这个键盘事件,这个你们自己去完成,这里我不在多说(总要留点东西让你们自己去思考去感悟!)

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="../../static/css/bootstrap.min.css" rel="stylesheet">
    <title>chat</title>
    <style>
        .answer{
          width: 100%;
          position: relative;
          height: 70vh;
        }
    
        .ipt{
          display:flex;
          align-items: center;
          position: absolute;
          bottom: 60px;
          margin: 0 15px;
          padding-right: 15px;
          border-radius: 15px;
          width: calc(100% - 30px);
          height: 50px;
          border: 1px solid #e7eaec;
        }
        .ipt textarea {
          resize: none;
          overflow-y: auto;
          border: none;
          box-shadow: none;
        }
        .ipt textarea:focus{
            border: none !important;
            box-shadow: none !important;
        }

        #chatWindow {
          max-height: calc(70vh - 120px);
          height:auto;
          overflow-y: auto;
        }
    
        .message-bubble {
            padding: 10px;
            margin: 5px;
            display: flex;
            align-items: flex-start;
            border-bottom: 1px dashed #e7eaec;
        }
    
    
        .message-bubble p {
            font-size: 18px;
            margin-left:15px;
        }
    
        .chat-icon {
            width: 30px;
            height: 30px;
            border-radius: 3px;
        }
    </style>
</head>
<body>
    <div>
      <div class="row">
          <div class="col-xs-12">
            <div>
              <h1 class="text-center m-b-lg">Chat with ChatGPT</h1>
            </div>
            <div class="answer">
              <div id="chatWindow" class="mb-3"></div>
              <div class="input-group ipt">
                <div class="col-xs-12">
                  <textarea id="chatInput" class="form-control" rows="1"></textarea>
                </div>
                <button id="chatBtn" class="btn btn-primary" type="button">Go !</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div> 
</body>
<script src="../../static/plugins/jquery-2.1.1.js"></script>
<script src="../../static/js/bootstrap.min.js"></script>
<script src="../../static/plugins/layer/layer.js"></script>
<script src="../../static/js/common.js"></script>
<script>
    $(document).ready(function() {
        var chatBtn = $('#chatBtn');
        var chatInput = $('#chatInput');
        var chatWindow = $('#chatWindow');
        var userIcon = '/static/images/user/{{ current_user.avatar }}'
        var botIcon = '/static/images/aichat/chatgpt.png';
        

        // 添加用户消息到窗口
        function addUserMessage(message) {
          var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + userIcon + '"><p class="message-text">' + message + '</p></div>');
          chatWindow.append(messageElement);
          chatInput.val('');
          chatWindow.animate({ scrollTop: chatWindow.prop('scrollHeight') }, 500);
        }

        // 添加回复消息到窗口
        function addBotMessage(message) {
          var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + botIcon + '"><p class="message-text">' + message + '</p></div>');
          chatWindow.append(messageElement);
          chatInput.val('');
          chatWindow.animate({ scrollTop: chatWindow.prop('scrollHeight') }, 500);
        }

        // 处理用户输入
        chatBtn.click(function() {
          var message = chatInput.val();
          if (message.length == 0){
            common_ops.alert("请输入内容!")    
            return  
          }
          addUserMessage(message);

          // messages.push({"role": "user", "content": message})
          
          chatBtn.attr('disabled',true) // 消息发送后让提交按钮不可点击

          // 发送信息到后台
          $.ajax({
            url: '/chat',
            method: 'POST',
            data: {
              "prompt": JSON.stringify(message)
            },
            success: function(res) {
              res = JSON.parse(res);
              addBotMessage(res.content);
              chatBtn.attr('disabled',false)  // 成功接受消息后让提交按钮再次可以点击
            },
            error: function(jqXHR, textStatus, errorThrown) {
              addBotMessage('<span style="color:red;">' + '出错啦!请稍后再试!' + '</span>');
              chatBtn.attr('disabled',false) 
            }
          });
        });

        // 处理 Enter 键按下
        chatInput.keypress(function(e) {
          if (e.which == 13) {
            chatBtn.click();
          }
        });
  });
</script>
</html>

这里面用到的layer.js就是一个弹窗组件,百度可以搜到,common.js是我自己对layer.js方法的封装,这个页面其实你不这两个js文件也行,因为整个页面只有下面的代码用到了弹窗:

if (message.length == 0){
 	common_ops.alert("请输入内容!")    
  	return  
}

其实简陋点,一个alert就搞定了!

结语

如果你觉得博主写的还不错的话,可以订阅下面的这个flask专栏,这是博主唯一的付费专栏,我做的这个页面也是最近项目的一部分,这个项目也是用flask做的,我会将他全部总结开源到这个flask专栏中。

【flask从入门到实战】专栏9.9火热订阅中,已包含两个项目,全站独一无二的脚手架搭建,直接复制简单无脑操作,项目结构类似Django,感兴趣的可以看看哦!

🏰系列专栏
👉flask框架快速入门

其他专栏请前往博主主页查看!

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

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

相关文章

【毕业季|进击的技术er】作为一名职场人,精心总结的嵌入式学习路线图

活动地址&#xff1a;毕业季进击的技术er 文章目录0、作者介绍1、前言2、嵌入式基础必备知识2.1、学习内容2.2、学习建议2.3、学习资料3、嵌入式入门篇——51单片机3.1、学习内容3.2、学习建议3.3、学习资料4、STM32进阶篇4.1、学习内容4.2、学习建议4.3、学习资料5、小而美的R…

目标跟踪算法综述

前言: 目标跟踪是计算机视觉领域研究的一个热点问题&#xff0c;其利用视频或图像序列的上下文信息&#xff0c;对目标的外观和运动信息进行建模&#xff0c;从而对目标运动状态进行预测并标定目标的位置。目标跟踪算法从构建模型的角度可以分为生成式(generative)模型和判别…

Vue--》搭配Bootstrap实现Vue的列表增删功能

在日常开发中&#xff0c;我们可以用 “拿来主义” 借助Bootstarp现成的一些样式&#xff0c;快速生成我们想要的页面布局&#xff0c;避免书写大量的HTML和CSS代码&#xff0c;省下了许多不必要的时间。 当我们想要生成表单表格时我们可以查看Bootstrap的官方文档&#xff0c;…

vue2响应式原理

首先要知道vue2 是2013年 基于 ES5开发出来的 我们常说的重渲染就是重新运行render函数 vue2响应式原理简单来说就是vue官网上的这图片 通过 Object.defineProperty 遍历对象的每一个属性&#xff0c;把每一个属性变成一个 getter 和 setter 函数&#xff0c;读取属性的时候…

前端学习之CSS

目录 引言 1. 什么是CSS? 2. 在HTML中使用CSS的三方式(基于CSS2. 0) 2.1 内联定义 2.2 样式块 2.3 引入文件 引言 大家不要觉得CSS也是一种语言&#xff0c;需要花费很多的时间去学习&#xff0c;不要恐惧它&#xff0c;其实CSS只是一个很小的知识点&#xff0c;我们在…

【web渗透】SSRF漏洞超详细讲解

&#x1f495;&#x1f495;&#x1f495; 博主昵称&#xff1a;摆烂阳&#x1f495;&#x1f495;&#x1f495; &#x1f970;博主主页跳转链接 &#x1f469;‍&#x1f4bb;博主研究方向&#xff1a;web渗透测试 、python编程 &#x1f4c3; 博主寄语&#xff1a;希望本篇文…

Vue3的优点,为什么要使用Vue3

1.为什么要使用Vue3&#xff1a; 这里主要通过介绍Vue3的优点以及与Vue2的对比来解答这个问题。 Vue3的六大亮点 1.性能比Vue2快1.2~2倍&#xff1b; 性能的提升主要是通过响应式系统的提升&#xff08;vue3使用proxy对象重写响应式&#xff09;以及编译优化&#xff08;优…

Web项目【用户管理系统】完整版

目录 &#x1f482; 个人主页: 爱吃豆的土豆&#x1f91f; 版权: 本文由【爱吃豆的土豆】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 &#x1f3c6;人必有所执&#xff0c;方能有所成&#xff01; &…

vite3+vue3 项目打包优化实战之-视图分析(rollup-plugin-visualizer)、CDN引入、依赖分包、gzip压缩、history404问题

文章目录写在前面build 视图分析依赖文件第三方库CDN引入依赖文件分包gzip压缩文件部署前配置history路由模式的404问题最后写在前面 vue项目在线下环境开发完成后&#xff0c;我们就需要项目的打包上线了&#xff0c;除了要知道打包命令npm run build 之外&#xff0c;我们还…

前端之vue3使用WebSocket

vue3使用WebSocketWebSocketWebSocket说明WebSocket图示客户端使用WebSocketvue3中, 客户端使用WebSocket步骤更多方法WebSocket WebSocket说明 WebSocket 是全双工网络通信通信协议&#xff0c;实现了客户端和服务器的平等对话&#xff0c;任何一方都可以主动发送数据。并且…

Vue 采用blob下载后端返回的pdf流或者excel流文件乱码问题解决方案

流文件乱码问题解决方案问题介绍&#xff1a;一、前端方式解决&#xff1a;二、后端方式解决&#xff1a;三、文件预览实现四、点击按钮打开新窗口预览问题介绍&#xff1a; 打开或者预览全是乱码。预览pdf如下图&#xff1a; 解决办法&#xff1a; 1. 后端接口返回的blob文…

如何在vue中实现文件预览功能

文件流 如何将各种文件的文件流(blob)转化为线上可以直接预览的数据&#xff0c;这里简单介绍四种不同类型的文件预览。分别是pdf&#xff0c;docx&#xff0c;xlsx&#xff0c;jpg/png/jpeg等。有一个事情是需要重点注意的&#xff0c;文件流必须保证能够被正常下载解析后才可…

前端使用jswebrtc实现视频流播放

JSWebrtc对浏览器的Webrtc做了简单的封装,支持SRS的RTC流的播放. html代码: JSWeb播放器可以通过HTML创建,只需给指定元素添加CSS样式 jswebrtc即可: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta na…

猿创征文|我的前端——【HTML5】基础成长学习之路

文章目录 前言 一、网页的基本组成 1.什么是网页 2.什么是HTML 3.网页的形成 二、常用的浏览器 1.常用的浏览器 2.浏览器内核 三、Web标准 1.为什么需要web标准 2.Web标准的构成 前言 在一次机缘巧合之下了解并接触到CSDN&#xff0c;从此开启了我IT学习之路&#x…

API 低代码开发:接口大师,一套开发、管理和提供接口的产品框架

目录 一、简介 二、“器”有所用 三、“三大”平台/系统使用手册 ⭐️1、API接口系统手册⭐️ 访问在线接口 在线接口文档列表 接口文档详情页 搜索接口 ⭐️2、Platform开放平台手册⭐️ 访问开放平台 注册并登录开发者账号 创建应用 查看接口权限 调用开发接口 获…

uniCloud使用

uni-app 是是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/QQ/钉钉/淘宝&#xff09;、快应用等多个平台。 1 创建uni-app项…

前端获取mac地址

1.通过getMac库获取mac地址 通过getMac库来获取&#xff1a;getmac - npmGet the MAC address of the current machine you are on.. Latest version: 5.20.0, last published: a year ago. Start using getmac in your project by running npm i getmac. There are 201 other…

CSS 如何实现文字渐变色 ?

CSS 实现文字渐变色 CSS 实现文字渐变&#xff0c;有两种方法&#xff1a; 1. background 属性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><style>.text-gradient {background-image: linear-gradien…

【微信小程序】WXSS和全局、页面配置

&#x1f352;观众老爷们好呀&#xff0c;小程序系列更新&#xff0c;上文我们讲解了小程序中WXML 中的条件渲染和列表渲染&#xff0c;那么接下来&#xff0c;就让我们走进微信小程序的WXSS以及小程序配置吧&#xff01; &#x1f352;今天的内容也是非常重要&#xff0c;赶紧…

8种css居中实现的详细实现方式了

这是一篇关于居中对齐方式的总结 开篇之前&#xff0c;先问一下大家都知道几种居中的实现方式&#xff1f; 面试时答出来两三个就不错了&#xff0c;就怕面试官还让你继续说。今天就来总结一下这些居中的方式 使用flex布局设置居中。使用flex 时也能通过给子项设置margin: au…