目录
一、效果展示
默认展示
一般对话展示:
代码对话展示:
二、项目概述
本项目是一个基于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部署到服务器上
.......