目录
[3.1 前端实现](#3.1 前端实现)
[3.2 后端方向](#3.2 后端方向)
一、效果展示
默认展示
一般对话展示:
代码对话展示:
二、项目概述
本项目是一个基于Web的智能对话服务平台,通过后端与第三方AI公司的API接口对接,为前端用户提供了一个简洁、直观的聊天界面。该项目的核心价值在于其便捷性与普适性,让用户能够轻松接入高质量的AI对话服务,无论是寻求信息咨询、娱乐互动,还是情感陪伴,都能获得即时响应与个性化体验。
技术模块:
1.前端:采用Vue框架+elementUi框架+HTML本地存储信息
2.后端:采用SpringBoot框架进行数据响应
三、手把手快速搭建实现本项目
3.1 前端实现
前置准备工作:创建一个新的Vue模板,并导入axios
bash
npm install 'axios'
导入elementUI
bash
npm i element-ui -S
Vue中main.js 进行配置
javascript
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:
html
<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:
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:
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文件下):
javascript
const { defineConfig } = require('@vue/cli-service')
module.exports = {
devServer: {
port: 8081, // 将端口设置为你想要的端口号
},
};
运行:在控制台启动程序
bash
npm run serve
打开浏览器:前端的配置改为(localhost:8081)
3.2 后端方向
创建一个SpringBoot项目
在项目pom.xml文件导入以下依赖:
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(十分简单,一毛钱就能开通))
java
@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:
java
@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部署到服务器上
.......