基于vue3+pinia2仿ChatGPT聊天实例|vite4.x仿chatgpt界面

原文:基于vue3+pinia2仿ChatGPT聊天实例|vite4.x仿chatgpt界面

基于vue3+pinia2仿ChatGPT聊天实例|vite4.x仿chatgpt界面

使用vue3+pinia2开发仿制chatgpt界面聊天实例Vue3-Chatgpt

基于Vue3.x+Pinia2+VueRouter+Vue3-Markdown等技术构建仿ChatGPT网页端聊天程序。支持经典+分栏界面布局、light/dark模式、全屏+半屏显示、Markdown语法解析、侧边栏隐藏等功能。

在这里插入图片描述

在这里插入图片描述

技术框架

  • 编辑工具Cursor
  • 框架技术Vue3+Vite4.x+Pinia2
  • 组件库VEPlus (基于vue3桌面端组件库)
  • 国际化多语言vue-i18n^9.2.2
  • 代码高亮highlight.js^11.7.0
  • 本地存储pinia-plugin-persistedstate^3.1.0
  • markdown解析vue3-markdown-it

在这里插入图片描述

在这里插入图片描述

项目目录结构

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述


 

在这里插入图片描述

在这里插入图片描述

vite.config.js配置

TypeScript

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import { parseEnv } from './src/utils/env'

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
	const viteEnv = loadEnv(mode, process.cwd())
	const env = parseEnv(viteEnv)

	return {
		plugins: [vue()],

		// base: '/',
		// mode: 'development', // development|production

		/*构建选项*/
		build: {
			// minify: 'esbuild', // 打包方式 esbuild(打包快)|terser
			// chunkSizeWarningLimit: 2000, // 打包大小警告
			// rollupOptions: {
			// 	output: {
			// 		chunkFileNames: 'assets/js/[name]-[hash].js',
			// 		entryFileNames: 'assets/js/[name]-[hash].js',
			// 		assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
			// 	}
			// }
		},
		esbuild: {
			// 打包去除 console.log 和 debugger
			drop: env.VITE_DROP_CONSOLE ? ['console', 'debugger'] : []
		},

		/*开发服务器选项*/
		server: {
			// 端口
			port: env.VITE_PORT,
			// 是否浏览器自动打开
			open: env.VITE_OPEN,
			// 开启https
			https: env.VITE_HTTPS,
			// 代理配置
			proxy: {
				// ...
			}
		},

		resolve: {
			// 设置别名
			alias: {
				'@': resolve(__dirname, 'src'),
				'@assets': resolve(__dirname, 'src/assets'),
				'@components': resolve(__dirname, 'src/components'),
				'@views': resolve(__dirname, 'src/views'),
				// 解决vue-i18n警告提示You are running the esm-bundler build of vue-i18n.
				'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
			}
		}
	}
})

main.js主入口

TypeScript

import { createApp } from 'vue'
import App from './App.vue'

// 引入Router和Store
import Router from './router'
import Store from './store'

// 引入插件配置
import Plugins from './plugins'

const app = createApp(App)

app
.use(Router)
.use(Store)
.use(Plugins)
.mount('#app')

vue3.x组件库

项目中使用的组件库是基于vue3自定义UI组件库Ve-plus。一个支持40+组件的轻量级组件库。

在这里插入图片描述

在这里插入图片描述


安装组件

TypeScript

yarn add ve-plus
npm i ve-plus --save

基于vue3.js自定义pc端组件库VePlus_xiaoyan_2018的博客-CSDN博客

整体布局

项目支持2种布局模式整体分为顶栏+侧边栏+主体内容三大模块构成。

在这里插入图片描述

在这里插入图片描述

TypeScript

<div class="ve__layout-body flex1 flexbox">
	<!-- //中间栏 -->
	<div class="ve__layout-menus flexbox" :class="{'hidden': store.config.collapse}">
		<aside class="ve__layout-aside flexbox flex-col">
			<ChatNew />
			<Scrollbar class="flex1" autohide size="4" gap="1">
				<ChatList />
			</Scrollbar>
			<ExtraLink />
			<Collapse />
		</aside>
	</div>

	<!-- //右边栏 -->
	<div class="ve__layout-main flex1 flexbox flex-col">
		<!-- 主内容区 -->
		<Main />
	</div>
</div>

TypeScript

<template>
	<div class="vegpt__editor">
		<div class="vegpt__editor-inner">
			<Flex :gap="0">
				<Popover placement="top" trigger="click" width="150">
					<Button class="btn" type="link" icon="ve-icon-yuyin1" v-tooltip="{content: '发送语音', theme: 'light', arrow: false}"></Button>
					<template #content>
						<div class="flexbox flex-alignc flex-col" style="padding: 15px 0;">
							<Icon name="ve-icon-yuyin" size="40" color="#0fa27e" />
							<p class="fs-12 mb-15 c-999">网络不给力</p>
							<Button size="small"><i style="background:#f00;border-radius:50%;box-shadow:0 1px 2px #999;margin-right:5px;height:8px;width:8px;"></i>开始讲话</Button>
						</div>
					</template>
				</Popover>
				<Button class="btn" type="link" v-tooltip="{content: '发送图片', theme: 'light', arrow: false}">
					<Icon name="ve-icon-photo" size="16" cursor />
					<input ref="uploadImgRef" type="file" title="" accept="image/*" @change="handleUploadImage" />
				</Button>
				<Input
					class="flex1"
					ref="editorRef"
					v-model="editorText"
					type="textarea"
					:autosize="{maxRows: 4}"
					clearable
					placeholder="Prompt..."
					@keydown="handleKeydown"
					@clear="handleClear"
					style="margin: 0 5px;"
				/>
				<Button class="btn" type="link" icon="ve-icon-submit" @click="handleSubmit"></Button>
			</Flex>
		</div>
	</div>
</template>

TypeScript

import { ref, watch } from 'vue'
import { guid } from '@/utils'
import { chatStore } from '@/store/modules/chat'

const props = defineProps({
	value: { type: [String, Number] }
})
const emit = defineEmits(['clear'])

const chatState = chatStore()

const uploadImgRef = ref()
const editorRef = ref()
const editorText = ref(props.value)

// ...

// 发送会话
const handleSubmit = () => {
	editorRef.value.focus()
	if(!editorText.value) return

	let data = {
		type: 'text',
		role: 'User',
		key: guid(),
		content: editorText.value
	}
	chatState.addSession(data)
	// 清空
	editorText.value = ''
}
const handleKeydown = (e) => {
	// ctrl+enter
	if(e.ctrlKey && e.keyCode == 13) {
		handleSubmit()
	}
}
const handleClear = () => {
	emit('clear')
}
// 选择图片
const handleUploadImage = () => {
	let file = uploadImgRef.value.files[0]
	if(!file) return
	let size = Math.floor(file.size / 1024)
	console.log(size)
	if(size > 2*1024) {
		Message.danger('图片大小不能超过2M')
		uploadImgRef.value.value = ''
		return false
	}
	let reader = new FileReader()
	reader.readAsDataURL(file)
	reader.onload = function() {
		let img = this.result

		let data = {
			type: 'image',
			role: 'User',
			key: guid(),
			content: img
		}
		chatState.addSession(data)
	}
}

在这里插入图片描述

TypeScript

/**
 * 聊天状态管理
 * @author YXY  Q282310962
 */

import { defineStore } from 'pinia'
import { guid, isEmpty } from '@/utils'

export const chatStore = defineStore('chat', {
	state: () => ({
		// 聊天会话记录
		sessionId: '',
		session: []
	}),
	getters: {},
	actions: {
		// 创建新会话
		createSession(ssid) {
			this.sessionId = ssid
			this.session.push({
				sessionId: ssid,
				title: '',
				data: []
			})
		},

		// 新增会话
		addSession(message) {
			// 判断当前会话uuid是否存在不存在创建新会话
			if(!this.sessionId) {
				const ssid = guid()
				this.createSession(ssid)
			}
			this.session.map(item => {
				if(item.sessionId == this.sessionId) {
					if(!item.title) {
						item.title = message.content
					}
					item.data.push(message)
				}
			})
			// ...
		},

		// 获取会话
		getSession() {
			return this.session.find(item => item.sessionId == this.sessionId)
		},

		// 移除会话
		removeSession(ssid) {
			const index = this.session.findIndex(item => item?.sessionId === ssid)
			if(index > -1) {
				this.session.splice(index, 1)
			}
			this.sessionId = ''
		},
		// 删除某一条会话
		deleteSession(ssid) {
			// ...
		},

		// 清空会话
		clearSession() {
			this.session = []
			this.sessionId = ''
		}
	},
	// 本地持久化存储(默认存储localStorage)
	persist: true
	/* persist: {
		// key: 'chatStore', // 不设置则是默认app
		storage: localStorage,
		paths: ['aa', 'bb'] // 设置缓存键
	} */
})

好了基于vue3+vite4+pinia2开发模仿chatgpt聊天就分享到这里。

Tauri-Vue3聊天实例|Tauri跨端聊天

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/41543.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《动手学深度学习》(pytorch版本)中`d2lzh_pytorch`包问题

《动手学深度学习》&#xff08;pytorch版本&#xff09;中d2lzh_pytorch包问题

Redis安装与配置指南:适用于Windows、Mac和Linux系统的详细教程

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

opencv-07-感兴趣区域(ROI)

在图像处理过程中&#xff0c;我们可能会对图像的某一个特定区域感兴趣&#xff0c;该区域被称为感兴趣区 域&#xff08;Region of Interest&#xff0c;ROI&#xff09;。在设定感兴趣区域 ROI 后&#xff0c;就可以对该区域进行整体操作。 以下是一些 OpenCV ROI应用场景 …

华为认证的题库,不仅能考试,还能帮你提升技能

1、OSPF协议在哪种状态下确定DD报文的主从关系&#xff1f; A. 2-way B.Exchange C. ExStart D. Full 2、在VRP操作系统中&#xff0c;如何进入OSPF区域0的视图&#xff1f;A. [Huawei-ospf-1]area 0 B.[Huawei]ospf area 0 C. [Huawei-ospf-1]area 0 enable D. [Huawe…

Python批量实现Word、EXCLE、PPT转PDF文件

一、绪论背景 在日常办公和文档处理中&#xff0c;有时我们需要将多个Word文档、Excel表格或PPT演示文稿转换为PDF文件。将文档转换为PDF格式的好处是它可以保留文档的布局和格式&#xff0c;并且可以在不同平台上进行方便的查看和共享。 本篇博文将介绍如何使用Python编程语言…

服务机器人应用

随着时代的发展&#xff0c;机器人技术在各个领域越来越普及。在服务领域&#xff0c;服务机器人的应用也越来越受到人们的欢迎。服务机器人将会在商业、医疗、教育、酒店等领域得到应用&#xff0c;并成为未来发展的趋势。 在商业领域中&#xff0c;服务机器人可以承担很多工作…

鲸鱼优化算法MATLAB代码

论文 Seyedali Mirjalili,Andrew Lewis. The Whale Optimization Algorithm[J]. Advances in Engineering Software,2016,95.func_plot.m % This function draw the benchmark functionsfunction func_plot(func_name)[lb,ub,dim,fobj]Get_Functions_details(func_name);switch…

轮廓提取demo

note 步骤&#xff1a; 1.滤波(使用高斯核对原图卷积) 2.取梯度(使用sobel核对步骤1之后的图卷积得到x,y两个方向的梯度分量) 3.合成梯度图(x,y两个方向的梯度分量相加) 4.取梯度平方和矩阵(x*x y*y) 5.取八邻域掩膜(3x3矩阵) 6.根据掩膜&#xff0c;合成梯度图&#x…

2023最新版本Activiti7系列-事件篇

事件篇 事件&#xff08;event&#xff09;通常用于为流程生命周期中发生的事情建模。事件总是图形化为圆圈。在BPMN 2.0中&#xff0c;有两种主要的事件分类&#xff1a;*捕获&#xff08;catching&#xff09;与抛出&#xff08;throwing&#xff09;*事件。 捕获: 当流程执…

1haclon 简单操作

文章目录 *读取图片 read_image(Image,claudia) *转换为灰度 rgb1_to_gray(Image,GrayImage)阈值分割 区域连接 获取最衣服 *读取图片 read_image(Image,claudia) *转换为灰度 select_shape (Connection, SelectedRegions, area, and, 40963.3, 44724.8) rgb1_to_gray(Image,Gr…

TCP实现FTP功能

目录 server client makefile 运行顺序 FTP&#xff08;File Transfer Protocol&#xff09;是一种用于在计算机网络上传输文件的标准协议。 它允许用户通过网络将文件从一个计算机&#xff08;称为FTP服务器&#xff09;传输到另一个计算机&#xff08;称为FTP客户端&…

orbslam3 生成标定板rosrun kalibr kalibr_create_target_pdf --type

rosrun kalibr kalibr_create_target_pdf --type apriltag --nx 6 --ny 6 --tsize 0.08 --tspace 0.3小师妹要做相机视觉标定&#xff0c;需要制作棋盘格&#xff0c;无奈其电脑有些卡&#xff0c;对此毫无经验的博主从头开始安装&#xff08;此前博主已经安装了ROS环境&#x…

grpc --- protoc生成的pb.go文件的位置

目录 一、环境相关版本二、go_package配置为当前目录下三、go_package配置为指定目录四、结论 一、环境相关版本 go v1.20.5 protoc v4.24.0 protoc-gen-go v1.26.0protoc-gen-go版本过高时需要指定包名&#xff0c;即go_package 二、go_package配置为…

git rebase 合并提交

一. 合并提交步骤 git log --oneline 查看当前提交记录 git rebase -i HEAD~2 选择最后提交的2条记录进行合并进入编辑界面,将c865404的pick改为f, 表示向前合并也就是向cc5a54合并 编辑完之后:wq 保存并退出git rebase --continuegit push --force origin feature/v1.2 推送…

WEB阶段_CSSJS篇(附代码笔记)

&#xff08;一&#xff09;、使用DIVCSS布局首页 1、HTML的块标记 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><div>div1</div><div>div2</div><…

青岛大学_王卓老师【数据结构与算法】Week05_06_栈的顺序表示_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c; 另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础…

No.2(3)——双指针算法实现平方数组排序

双指针算法指的是&#xff0c;从数组的两侧开辟指针变量进行查找&#xff0c;这类问题往往通过暴力&#xff08;双循环&#xff09;可以解出&#xff0c;而采用双指针相当于用空间换取时间&#xff0c;省略双层循环中重复的部分。 对于一个含有负数的有序数组&#xff0c;要求保…

一本通1910:【00NOIP普及组】计算器的改良题解

今天是编程集训的第二天&#xff0c;也是我来到CSDN整整1年。感谢所有阅读过我的文章的人&#xff0c;谢谢。 今天的比赛难度略低于昨天&#xff0c;但这道题也卡了我好久。 进入正题 题目&#xff1a; 题目描述&#xff1a; NCL是一家专门从事计算器改良与升级的实验室&a…

项目名称:智能家居边缘网关项目

一&#xff0c;项目介绍 软件环境: C语言 硬件环境: STM32G030C8TX单片机开发板 开发工具: Linux平台GCC交叉编译环境以及ukeil (1)边缘网关概念 边缘网关是部署在网络边缘侧的网关&#xff0c;通过网络联接、协议转换等功能联接物理和数字世界&#xff0c;提供轻量化的联接管…

C#基础--进程和线程的认识

C#基础–进程和线程的认识 一、基础概念 1. 什么是进程&#xff1f; 进程并不是物理的东西&#xff0c;是虚拟出来的&#xff0c;是一种概念。当一个程序开始运行时&#xff0c;它就是一个进程&#xff0c;进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又…