个人开发实现AI套壳网站快速搭建(Vue+elementUI+SpringBoot)

news2024/9/20 5:42:03

目录

一、效果展示

二、项目概述

三、手把手快速搭建实现本项目

3.1 前端实现

3.2 后端方向

五、后续开发计划


一、效果展示

默认展示

一般对话展示:

代码对话展示:


二、项目概述

        本项目是一个基于Web的智能对话服务平台,通过后端与第三方AI公司的API接口对接,为前端用户提供了一个简洁、直观的聊天界面。该项目的核心价值在于其便捷性与普适性,让用户能够轻松接入高质量的AI对话服务,无论是寻求信息咨询、娱乐互动,还是情感陪伴,都能获得即时响应与个性化体验。

技术模块:

1.前端:采用Vue框架+elementUi框架+HTML本地存储信息

2.后端:采用SpringBoot框架进行数据响应


三、手把手快速搭建实现本项目

3.1 前端实现

前置准备工作:创建一个新的Vue模板,并导入axios

npm install 'axios'

导入elementUI

npm i element-ui -S

Vue中main.js 进行配置

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.config.productionTip = false

Vue.use(ElementUI);

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

本项目为了简单化,将项目整体仅设置为了一个Vue主视图(App.vue)

template:

<template>
  <div id="Chat">
    <el-container>
      <el-aside width="200px">

        <!-- 添加导航 -->
        <el-row class="tac" >
          <el-col :span="12" style="width: 100%;">
            <h1>个人工具网站</h1>
            <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose">
              <el-submenu index="1">
                <template slot="title">
                  <i class="el-icon-location"></i>
                  <span>人工智能助手</span>
                </template>
                <el-menu-item-group>
                  <el-menu-item index="1-1">通义千问</el-menu-item>
                  <el-menu-item index="1-2">文言一心</el-menu-item>
                  <el-menu-item index="1-2">GPT</el-menu-item>
                </el-menu-item-group>               
              </el-submenu>
              <el-menu-item index="2">
                <i class="el-icon-menu"></i>
                <span slot="title">知识星球</span>
              </el-menu-item>
              <el-menu-item index="3" >
                <i class="el-icon-document"></i>
                <span slot="title">工具集合</span>
              </el-menu-item>
              <el-menu-item index="4">
                <i class="el-icon-setting"></i>
                <span slot="title">素材集合</span>
              </el-menu-item>
            </el-menu>
          </el-col>
        </el-row>

      </el-aside>
      <el-container>
        <el-header>
          <h3>通义千问-API套壳网站</h3>
        </el-header>
        <el-main>
          <div id="ChatLayOut">
            <!-- 对话内容列举 -->
            <div v-for="(msg, index) in messages" :key="index" id="ChatBubble">
              <img :src="getImageUrl(msg.sender)" id="chatImage">
              <!-- <p id="ChatContent">{{ msg.sender }}: {{ msg.content }}</p> -->
              <div class="chat-content-wrap">
                <!-- 使用预处理后的消息内容 -->
                <div v-html="preprocessMessageContent(msg.sender+':'+msg.content) "></div>
              </div>

            </div>
          </div>

        </el-main>
        <el-footer>
          <!-- 使用flex布局使元素水平排列 -->
          <div style="display: flex; align-items: center;">
            <!-- 将输入框放入表单中 -->
            <form @submit.prevent="onFormSubmit" style="margin-left: 30%; width: 500px; margin-right: 10px;">
              <el-input id="DialogTextCSS" v-model="message" :placeholder="DialogText" :disabled="flag"
                style="flex-grow: 1; "></el-input>
            </form>
            <!-- 提交按钮 -->
            <el-button type="primary" @click="sendMessage" style="width:90px ;">提交</el-button>
            <!-- 清空按钮 -->
            <el-button type="danger" @click="deleteMessage">清空本地聊天记录</el-button>
          </div>

          <div>COPYRIGHT: CSDN-ALPHAMILK</div>
          <div>version : 测试版</div>
        </el-footer>
      </el-container>
    </el-container>
  </div>
</template>

JavaScript:

<script>
import axios from 'axios';


export default {
  data() {
    return {
      message: '',
      messages: [],
      Identify: '',
      senderType: '', // 新增一个变量来标识发送者类型
      flag:false,
      DialogText:'请您输入内容',
    }
  },
  mounted() {
    // 页面加载时从localStorage读取消息
    const savedMessages = JSON.parse(localStorage.getItem('messages'));
    if(savedMessages===null){
      this.messages.push({sender: "AI", content: "欢迎使用通义千问API的套壳网站,请您通过输入内容到下方的文本框并提交即可开启聊天"});
    }
    if (savedMessages) {
      this.messages = savedMessages;
    }

  },
  methods: {
    scrollToBottom() {
  this.$nextTick(() => {
    // 尝试手动触发一次重绘,看是否有助于解决滚动问题
    const chatLayout = this.$el.querySelector('#ChatLayOut');
    if (chatLayout) {
      // 强制浏览器重绘
      void chatLayout.offsetHeight; 

      setTimeout(() => {
        console.log('scrollHeight:', chatLayout.scrollHeight);
        window.scrollTop = chatLayout.scrollHeight;
        console.log('scrollTop after setting:', chatLayout.scrollTop);
      }, 100); // 增加延时时间以确保元素尺寸和内容更新完成
    }
  });
},

    sendMessage() {
      if (this.message.trim() !== '') {
        // 设置身份为用户
        this.senderType = '用户';
        this.messages.push({sender: this.senderType, content: this.message});
        localStorage.setItem('messages', JSON.stringify(this.messages)); // 保存消息到localStorage

        //禁用对话框
        this.flag = true;
        this.DialogText = '请您耐心等待AI的回答';

        // //进行滚动操作,滚动到最新消息
        // this.scrollToBottom(); 


        // 调用接口获取AI生成的内容
        axios.get('http://localhost:8080/Test/Chat',
          {
            params:{
              message : this.message
            }
          }

        ).then((response) => {
          // 设置身份为AI
          this.senderType = 'AI';
          this.messages.push({sender: this.senderType, content: response.data});
          localStorage.setItem('messages', JSON.stringify(this.messages));
          
          //解除对话框
          this.flag = false;
          this.DialogText = '请您输入内容';
        });
        this.message = ''; // 清空输入框
      }else{
        alert("输入不能为空噢!");
      }

    },
    deleteMessage(){
      localStorage.removeItem("messages");
      this.messages = [];
      this.messages.push({sender: "AI", content: "欢迎使用通义千问API的套壳网站,请您通过输入内容到下方的文本框并提交即可开启聊天"});

    },
    getImageUrl(sender) {
      if (sender === 'AI') {
        return 'https://img.alicdn.com/imgextra/i3/O1CN01sffRIx1nb3dXCKdFC_!!6000000005107-2-tps-1024-1024.png';
      } else {
        return 'https://bpic.51yuansu.com/pic3/cover/00/94/68/58dcd742dd10d_610.jpg?x-oss-process=image/resize,h_360,m_lfit/sharpen,100';
      }
    },

    onFormSubmit() {
      this.sendMessage();
    },

preprocessMessageContent(content) {
  const codeBlockRegex = /```(.*?)```/gs;
  const sortTextRegex = /\*\*(.*?)\*\*/gs;
 
  let tempContent = content.replace(sortTextRegex, `<p class="sort-text">$1</p>`);
  let processedContent = tempContent.replace(codeBlockRegex, `<pre class="code-block"><code>$1</code></pre>`);
  let segments = processedContent.split(/```.*?```/gs); // 分割代码块
  
  segments = segments.filter(segment => segment.trim());

  
  let finalContent = segments.map((segment) => {
    return `<p class="content-common">${segment}</p>`;
  }).join('');

  return finalContent;
}
  },
  handleOpen(key, keyPath) {
        console.log(key, keyPath);
      },
      handleClose(key, keyPath) {
        console.log(key, keyPath);
      }

}

</script>

css:

<style>
  .el-header, .el-footer {
    background-color: #B3C0D1;
    color: #333;
    text-align: center;
    line-height: 60px;
  }
  
  .el-aside {
    background-color: #D3DCE6;
    color: #333;
    text-align: center;
    line-height: 200px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)
  }
  
  .el-main {
    background-color: #E9EEF3;
    color: #333;
    text-align: center;
    line-height: 160px;
    height: 1000px;
  }
  
  body > .el-container {
    margin-bottom: 40px;
  }
  
  .el-container:nth-child(5) .el-aside,
  .el-container:nth-child(6) .el-aside {
    line-height: 260px;
  }
  
  .el-container:nth-child(7) .el-aside {
    line-height: 320px;
  }

 #ChatBubble{
  
  position: relative;
  padding: 10px;
  border-radius: 10px;
  margin-bottom: 30px;
  max-width: 70%;
  background-color: white;
  line-height: normal;
 }
/* 用户和AI的不同样式 */
#ChatBubble.user {
  background-color: #E0F2F7; /* 用户气泡颜色 */
  float: left;
  clear: both;
  margin-right: 30px;
}

#ChatBubble.AI {
  background-color: #ECEFF1; /* AI气泡颜色 */
  float: right;
  clear: both;
  margin-left: 30px;
}

/* 指向箭头,这里仅示例用户气泡右边的箭头 */
#ChatBubble.user::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -15px;
  transform: translateY(-50%);
  border-style: solid;
  border-width: 10px 15px 10px 0;
  border-color: transparent #E0F2F7;
}


/* 可能需要清除浮动,避免布局问题 */
#dialog-display::after {
  content: "";
  display: block;
  clear: both;
}

#chatImage{
  float: left;
  margin-top: 17px;
  margin-right: 10px;
  height: 30px;
  width: 30px;
  background-color:#faeeee;
}


 #Topic{
  background-color: #f6f6fe;
  border-radius: 10px;
  height: 60px;
 }

#chat{
 height: 56px;
 width: 100%;
 background-color: pink;

}
.el-footer{
  background-color: #f7f8fc;
}
.el-main{
  background-color: #f7f8fc;
}
#ChatContent { 
  line-height: 1.5; /* 或者根据需要调整 */
   padding: 0; /* 取消内边距 */ 
  } 

#ChatLayOut{
  margin-left: 20%;
}

.el-header{
  background-color: #333;
}
h3{
  color: #E9EEF3;
}

.el-aside{
  background: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);

  /* 实现右边border-radi */
  border-top-right-radius: 30px;
}
/* 明亮风格的代码块,文字及行号全部左对齐 */
.code-block {
  background-color: #f8f8f8; /* 明亮背景 */
  color: #333; /* 深色文字 */
  font-family: 'Courier New', monospace; /* 适合代码的字体 */
  white-space: pre-wrap; /* 保留空格和换行 */
  border-radius: 5px; /* 边角圆润 */
  overflow-x: auto; /* 横向滚动条,如果需要 */
  line-height: 1.5;
  padding: 10px;
  position: relative; /* 为行号预留位置 */
}

/* 显示所有行的行号,确保向左对齐 */
.code-block::before {
  content: counter(line);
  counter-increment: line;
  position: absolute; /* 行号绝对定位 */
  left: 0; /* 行号紧贴左侧 */
  margin-left: 15px; /* 与代码内容的距离,可根据需要调整 */
  text-align: left; /* 行号左对齐 */
  width: 30px; /* 行号宽度 */
  color: #666; /* 行号颜色,可调整 */
  display: block; /* 每行前面均显示 */
  line-height: inherit; /* 继承代码块的行高 */
}

/* 确保代码内容也左对齐 */
.code-block code {
  display: block; /* 确保代码块内代码作为独立块显示 */
  padding-left: 45px; /* 为代码内容预留行号和额外的间距 */
  text-align: left; /* 确保代码文本左对齐 */
}


.content-common {
  /* 为普通文本内容定义样式 */
  margin-bottom: 10px; /* 示例:增加段落间距 */
  line-height: 1.5; /* 示例:调整行高 */
}

.el-col-12 {
  width: 100%;
}

.sort-text {
  font-weight: bold; /* 设置为粗体 */
  text-align: left; /* 文本左对齐 */
  line-height: normal; /* 行高设置为正常,确保与未加样式时的文本行高一致 */
}

</style>

最后配置端口为8081(在vue.config.js文件下):

const { defineConfig } = require('@vue/cli-service')
module.exports = {
  devServer: {
    port: 8081, // 将端口设置为你想要的端口号
  },
};

运行:在控制台启动程序

npm run serve

打开浏览器:前端的配置改为(localhost:8081)


3.2 后端方向

创建一个SpringBoot项目

在项目pom.xml文件导入以下依赖:

        <!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dashscope-sdk-java</artifactId>
            <version>2.8.2</version>
        </dependency>
        <!--okhttp3 依赖-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>

由于后端功能十分简单,仅需要一个Utils和一个Controller即可

Utils:(注意:这里要填自己申请的APIKey(十分简单,一毛钱就能开通))

@Component
public class AICHAT {
    public static String callWithMessage(String message)
            throws NoApiKeyException, ApiException, InputRequiredException {
        Generation gen = new Generation();
        Constants.apiKey="xxxxxx";//TODO:这里填写自己申请的APIKEY
        MessageManager msgManager = new MessageManager(10);
        Message systemMsg =
                Message.builder().role(Role.SYSTEM.getValue()).content("You are a helpful assistant.").build();
        Message userMsg = Message.builder().role(Role.USER.getValue()).content(message).build();//这里填写对话内容
        msgManager.add(systemMsg);
        msgManager.add(userMsg);
        QwenParam param =
                QwenParam.builder().model(Generation.Models.QWEN_TURBO).messages(msgManager.get())
                        .resultFormat(QwenParam.ResultFormat.MESSAGE)
                        .topP(0.8)
                        .enableSearch(true)
                        .build();
        GenerationResult result = gen.call(param);
        String Message = extractContentFromResult(result);
        System.out.println(Message);
        return Message;
    }

//    仅获取JSON结果中message字段的信息
    public static String extractContentFromResult(GenerationResult result) {
        if (result != null && result.getOutput() != null && !result.getOutput().getChoices().isEmpty()) {
            Message message = result.getOutput().getChoices().get(0).getMessage();
            return message.getContent();
        }
        return null; // 或者返回一个默认值
    }

}

ChatController:

@RestController
@RequestMapping("/Test")
@CrossOrigin
public class ChatController {
    @Autowired
    AICHAT aichat;
    @GetMapping("/Chat")
    public String GetParameter(String message) {

        try {
            if (message != null) {
                String AiResponse = null;
                try {
                    AiResponse = aichat.callWithMessage(message);
                } catch (ApiException | NoApiKeyException | InputRequiredException e) {
                    System.out.println(e.getMessage());
                }
                return AiResponse;
            }
        } catch (Exception e) {
            return "出错了>_<"+e.getMessage();
        }
        return  null;
    }
    
}

 启动(Application):

前后端联调测试:


五、后续开发计划

后续改进计划:

后续将会修改许多的bug,并加入许多新的功能,一步步将其打造成一个能够实现商业化的,满足普通人可以使用的通用网站。关注后即可获取最新的动态

1.加入多个可用个人免费的API,让切换AI模型能够方便快捷

2.加入用户管理,满足以后实现商业化的一步

3.加入动画效果,让聊天更生动

4.加入语音输入功能,与语音输出功能。实现外语教师功能

5.将项目通过nginx部署到服务器上

 .......


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

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

相关文章

【图解大数据技术】Hive、HBase

【图解大数据技术】Hive、HBase Hive数据仓库Hive的执行流程Hive架构数据导入Hive HBaseHBase简介HBase架构HBase的列式存储HBase建表流程HBase数据写入流程HBase数据读取流程 Hive Hive是基于Hadoop的一个数据仓库工具&#xff0c;Hive的数据存储在HDFS上&#xff0c;底层基于…

41割队伍

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/387 题目描述 给定 𝑛n 个数字 𝑎1,�…

运维.Linux下执行定时任务(中:Cron的常用替代方案)

运维系列 Linux下执行定时任务&#xff08;中&#xff1a;Cron的常用替代方案&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAd…

阿里云智能编程助手的安装使用

https://help.aliyun.com/document_detail/2590613.html 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码优化、注释生成、代码解释、研发智能问答、异常报错排查等能力&a…

前端存储都有哪些

cookie 、sessionStorage、localStorange、http缓存 、indexDB cookie 由服务器设置&#xff0c;在客户端存储&#xff0c;然后每次发起同源请求时&#xff0c;发送给服务器端。cookie最多能存储4K数据&#xff0c;它的生存时间由expires属性指定&#xff0c;并且cookie只能被…

Mathematica训练课(46)-- 一些常用的画图函数

在前面的课程中&#xff0c;我们已经梳理了Plot的画图用法&#xff0c;今天就详细梳理一下其他的画图函数用法&#xff1b; 1. 画一条直线 2. Circle(圆) 3. Disk&#xff08;圆盘&#xff09; 4. 画出一个矩形 5. 箭头

MAS0902量产工具分享,MAS0902A开卡教程,MAS0901量产工具下载

MAS0902和MAS1102都是基于SATA3.2技术开发的DRAM-less SSD控制芯片&#xff0c;简单来说就是SATA协议无缓存主控。下面是我摸索的麦光黑金300 240G SSD开卡修复简易教程&#xff0c;也就是MAS0902量产过程&#xff1a; 注意&#xff1a;开卡转接线必须要用ASM1153E或JMS578主控…

Github Page 使用手册(保姆级教程!)

搭建个人网站&#xff1f;没有服务器&#xff1f;那不如尝试一下 Github Page &#xff01; 最近我正好在搭建个人网站&#xff0c;于是就写一篇博客来详细介绍 Github Page 的使用、部署方式吧&#xff01; 一、进入 Github 访问&#xff1a;github.com 如果你没有 github…

Redisson(分布式锁、限流)

注意Redisson是基于Redis的&#xff0c;所以必须先引入Redis配置&#xff08;参考SpringBoot集成Redis文章&#xff09; 1. 集成Redisson 引入依赖 <!-- 二选一,区别是第一个自动配置&#xff0c;第二个还需要手动配置也就是第二步自定义配置&#xff0c;注意版本号&…

【吊打面试官系列-MyBatis面试题】MyBatis 框架适用场合?

大家好&#xff0c;我是锋哥。今天分享关于 【MyBatis 框架适用场合 &#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MyBatis 框架适用场合&#xff1f; 1、MyBatis 专注于 SQL 本身&#xff0c;是一个足够灵活的 DAO 层解决方案。 2、对性能的要求很高&#…

苹果电脑移动硬盘不能写入怎么办 读取移动硬盘的磁盘管理软件 Paragon NTFS for Mac永久激活

对于使用苹果电脑的用户们&#xff0c;我们经常会使用到移动硬盘来拷贝大量的文件。一般的移动硬盘的容量都比较大&#xff0c;再加上国内大多数人使用的都是 Windows 系统&#xff0c;为了通用与方便&#xff0c;所以硬盘的分区一般都是 NTFS 格式的。对于 Windows 系统的 NTF…

LLDP 基本原理

LLDP 简介 定义 LLDP&#xff08;Link Layer Discovery Protocol&#xff0c;链路层发现协议&#xff09;是 IEEE 802.1ab 中定义的第二层发现&#xff08;Layer 2 Discovery&#xff09;协议。 LLDP 提供了一种标准的链路层发现方式&#xff0c;可以将本端设备的主要能力、…

西安高校大学智能制造实验室数字孪生可视化系统平台建设项目验收

随着工业4.0时代的到来&#xff0c;智能制造成为推动制造业转型升级的关键。为了培养学生的创新能力和实践能力&#xff0c;西安高校大学决定建设智能制造实验室&#xff0c;并引入数字孪生技术&#xff0c;构建可视化系统平台。项目旨在通过数字孪生技术&#xff0c;实现对制造…

【吴恩达深度学习笔记系列】Logistic Regression 【理论】

Binary Classification: Logistic Regression: y ^ σ ( w T x b ) \hat{y}\sigma{(w^T xb)} y^​σ(wTxb) using sigmoid function σ 1 1 e − z \sigma \frac{1}{1e^{-z}} σ1e−z1​. 【torch.sigmoid(x)】 Sigmoid ( x ) 1 1 e − x \text{Sigmoid}(x)\frac{1}{…

运维锅总详解Nginx

本文尝试从Nginx特性及优缺点、为什么具有文中所述的优缺点、Nginx工作流程、Nginx最佳实践及历史演进等角度对其进行详细分析。希望对您有所帮助。 Nginx特性及优缺点 Nginx简介 Nginx&#xff08;发音为 “engine-x”&#xff09;是一款高性能的开源Web服务器及反向代理服…

[OtterCTF 2018]Play Time

还是这个程序 。。要找到游戏名字查看 进程 psscan pstree pslist 0x000000007d686b30 Rick And Morty 3820 2728 0x000000000b59a000 2018-08-04 19:32:55 UTC0000 0x000000007d7cb740 LunarMS.exe 708 2728 0x00000000731cb000 2018-08-04 19:27:39 UTC0000…

安全架构概述_1.信息安全面临的威胁

在当今以计算机、网络和软件为载体的数字化服务几乎成为人类赖以生存的手段。与之而来的计算机犯罪呈现指数上升趋势&#xff0c;因此&#xff0c;信息的可用性、完整性、机密性、可控性和不可抵赖性等安全保障显得尤为重要&#xff0c;而满足这些诉求&#xff0c;离不开好的安…

【mysql的行记录格式】

记录头信息 除了变长字段长度列表、NULL值列表之外&#xff0c;还有一个用于描述记录的记录头信息&#xff0c;它是由固定的5个字节组成。5个字节也就是40个二进制位&#xff0c;不同的位代表不同的意思&#xff0c;如图&#xff1a; 记录的真实数据 对于record_format_demo表来…

操作系统期末复习考题二

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文☀️☀️☀️三、总结&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f680;&am…

算法-位运算基础

文章目录 前置知识1. 交换两个数2. 比较两个数的大小3. leetcode268 寻找缺失的数字4. leetcode136 只出现一次的数字5. leetcode260 只出现一次的数字|||6. leetcode137 只出现一次的数字||7. 2/3的幂8. 大于等于该数字的最小2的幂9. leetcode201 数字范围按位与10. 位运算中分…