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

avatar
作者
筋斗云
阅读量:0

目录

一、效果展示

二、项目概述

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

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部署到服务器上

 .......


广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!