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

Alphamilk 2024-07-05 09:31:02 阅读 83

目录

一、效果展示

二、项目概述

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

3.1 前端实现

3.2 后端方向

五、后续开发计划


一、效果展示

默认展示

一般对话展示:

代码对话展示:


二、项目概述

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

技术模块:

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

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


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

3.1 前端实现

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

<code>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">code>

<el-container>

<el-aside width="200px">code>

<!-- 添加导航 -->

<el-row class="tac" >code>

<el-col :span="12" style="width: 100%;">code>

<h1>个人工具网站</h1>

<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose">code>

<el-submenu index="1">code>

<template slot="title">code>

<i class="el-icon-location"></i>code>

<span>人工智能助手</span>

</template>

<el-menu-item-group>

<el-menu-item index="1-1">通义千问</el-menu-item>code>

<el-menu-item index="1-2">文言一心</el-menu-item>code>

<el-menu-item index="1-2">GPT</el-menu-item>code>

</el-menu-item-group>

</el-submenu>

<el-menu-item index="2">code>

<i class="el-icon-menu"></i>code>

<span slot="title">知识星球</span>code>

</el-menu-item>

<el-menu-item index="3" >code>

<i class="el-icon-document"></i>code>

<span slot="title">工具集合</span>code>

</el-menu-item>

<el-menu-item index="4">code>

<i class="el-icon-setting"></i>code>

<span slot="title">素材集合</span>code>

</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">code>

<!-- 对话内容列举 -->

<div v-for="(msg, index) in messages" :key="index" id="ChatBubble">code>

<img :src="getImageUrl(msg.sender)" id="chatImage">code>

<!-- <p id="ChatContent">{ { msg.sender }}: { { msg.content }}</p> -->code>

<div class="chat-content-wrap">code>

<!-- 使用预处理后的消息内容 -->

<div v-html="preprocessMessageContent(msg.sender+':'+msg.content) "></div>code>

</div>

</div>

</div>

</el-main>

<el-footer>

<!-- 使用flex布局使元素水平排列 -->

<div style="display: flex; align-items: center;">code>

<!-- 将输入框放入表单中 -->

<form @submit.prevent="onFormSubmit" style="margin-left: 30%; width: 500px; margin-right: 10px;">code>

<el-input id="DialogTextCSS" v-model="message" :placeholder="DialogText" :disabled="flag"code>

style="flex-grow: 1; "></el-input>code>

</form>

<!-- 提交按钮 -->

<el-button type="primary" @click="sendMessage" style="width:90px ;">提交</el-button>code>

<!-- 清空按钮 -->

<el-button type="danger" @click="deleteMessage">清空本地聊天记录</el-button>code>

</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>`);code>

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>`;code>

}).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文件导入以下依赖:

<code> <!-- 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:这里填写自己申请的APIKEYcode>

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

 .......




声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。