AI大语言模型LLM学习-基于Vue3的AI问答页面

系列文章


1. AI大语言模型LLM学习-入门篇
2. AI大语言模型LLM学习-Token及流式响应
3. AI大语言模型LLM学习-WebAPI搭建


前言


在上一篇博文中,我们使用Flask这一Web框架结合LLM模型实现了后端流式WebAPI接口,本篇将基于Vue3实现AI问答页面,本人习惯使用HBuilder进行前端页面的开发,当然各位网友可以选择自己喜欢的前端开发IDE,比如VS Code。


一、设计思路

打开一个主流的AI对话页面,比如我注册的是阿里的通义千问
可以看到页面的效果如图所示:
在这里插入图片描述
根据页面效果,可以大致把内容分为如下3部分:

  1. 标题
  2. 问答对话,右侧为用户输入的问题,左侧为AI的回答;此部分需要自定义组件,左右布局,可复用,采用循环实现
  3. 底部输入区域,包含输入框及发送按钮

二、编码实现


1.项目新建

在这里插入图片描述

2.项目结构

在这里插入图片描述

3.代码部分

3.1 安装并引入element-plus

npm install element-plus --save

关于element-plus相关组件的使用,参考element-plus官网

  • mian.js配置
import { createApp } from 'vue'
import App from './App.vue'

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

const app = createApp(App)
//引入element-plus
app.use(ElementPlus)
app.mount('#app')

3.2 api接口进行代理

注意:api接口不能直接在html页面中进行调用,存在跨域访问的问题,需要在vite.config.js添加代理配置。
生产环境部署时可以采用nginx对api接口进行反向代理解决跨域问题。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server:{
	  proxy:{
		  '/chat':{
				   target:"http://127.0.0.1:2024/",
				   changeOrigin: true,
		  },
	  }
  }
})

3.3对话组件

chat.vue代码如下:

<script setup>
import { ref } from 'vue'
defineProps({
  msg: Object
})
const count = ref(0)
</script>
<template>
<div class="chat">
	<!--问题-->
	<div style="text-align: right;">
		<div class="el-card chat-right" >
			{{msg.question}}
		</div>
	</div>
	<!--AI回答-->
	<div style="text-align: left;">
		<div class="el-card chat-left">
			{{msg.answer}}
		</div>
	</div>
</div>
</template>
<style scoped>
.chat{
	max-width: 1000px;
	margin: 0 auto;
	padding-top: 10px;
	padding-bottom: 10px;
}
.ai-img
{
	height: 36px;
	width: 36px;
}
.chat-left
{
	background-color: #f5f6f7!important;
	display: inline-block;
	box-sizing: border-box;
	width: auto;
	text-align: left;
	border-radius: 12px;
	line-height: 24px;
	max-width: 100%;
	padding: 12px 16px;
	white-space: pre-wrap;
	
}
.chat-right
{
	background-color: #e0dfff;
	display: inline-block;
	box-sizing: border-box;
	width: auto;
	color: #3f3f3f;
	border-radius: 12px;
	line-height: 24px;
	max-width: 100%;
	padding: 12px 16px;
	white-space: pre-wrap;
}
</style>

3.4主体页面

代码如下:

<template>
	<div class="common-layout">
	    <el-container style="height:100%;width:100%;margin: 0 auto;">
			 <el-header style="height: 50px; width: 100% ;backgroundColor:rgba(0,102,255,.06)">
				<p class="centered-text">AI-历史人物</p>
			 </el-header>
			 <el-main id="chat">
				  <chat v-for="item in form.msgList" :msg=item></chat>
			 </el-main>
			 <el-row style="margin: 0 auto;padding-left: 20px;padding-right: 20px;">
				 <div style="width: 100%;">
					<el-input style="float: left;width: 90%;" @keyup.enter="sendMsg" v-model="form.input"></el-input>
					<el-button @click="sendMsg" style="float: right; height: 42px;line-height: 42px;" >发送</el-button>
				 </div>
				 <div style="margin: 0 auto;">
					<p style="color: red;font-size: 11px;">
					 服务生成的所有内容均由人工智能模型生成,其生成内容的准确性和完整性无法保证,不代表我们的态度或观点
					</p>
				 </div>
			 </el-row>
		</el-container>
	</div>
</template>
<script setup>
import { reactive,nextTick, ref } from 'vue'
import chat from './components/chat.vue'
	const form = reactive({
	  input: '',//输入
	  msgList:[] //消息列表
	});
	
	async function sendMsg()
	{
		var keyword=form.input;
		if(form.input.length>0)
		{
			var msg={
				question:keyword,
				answer:"AI生成中..."
			};
			form.msgList.push(msg);
			
			form.input="";
			setScrollToBottom();
			const response=await fetch('/chat',{
				method:"post",
				headers:{'Content-Type':'application/json'},
				body:JSON.stringify({
					question:keyword
				})
			});
			if (!response.ok) {
				throw new Error(`HTTP error! status: ${response.status}`);
			}
			
			const reader = response.body.getReader();
			let decoder = new TextDecoder();
			let resultData = '';
			
			var str="";
			
			msg={
				question:keyword,
				answer:str
			};
			form.msgList.pop();
			form.msgList.push(msg);
			while (true) {
				const { done, value } = await reader.read();
				if (done) break;
				resultData = decoder.decode(value);
				console.log(resultData);
				str+=resultData;
				msg={
					question:keyword,
					answer:str
				};
				form.msgList.pop();
				form.msgList.push(msg);
				setScrollToBottom();
			}
		}
		
	}
	
	/*内容显示过多时自动滑动*/
	async function setScrollToBottom() {
	  await nextTick()
	  let chat = document.querySelector("#chat")
	  chat.scrollTop = chat.scrollHeight
	}
	
</script>
<style>
html,body{
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
	border: 0;
}
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  height:100%;min-width: 380px;
}
.common-layout{
	height: 100%;
}
#chat{
	height: calc(100vh - 150px);
}
.el-input{
	height: 45px;
	border-radius: 12px;
	box-sizing: border-box;
}
</style>

运行效果展示


在这里插入图片描述

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值