原文:基于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 和 debuggerdrop: env.VITE_DROP_CONSOLE ? ['console', 'debugger'] : []},/*开发服务器选项*/server: {// 端口port: env.VITE_PORT,// 是否浏览器自动打开open: env.VITE_OPEN,// 开启httpshttps: 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><Inputclass="flex1"ref="editorRef"v-model="editorText"type="textarea":autosize="{maxRows: 4}"clearableplaceholder="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) returnlet data = {type: 'text',role: 'User',key: guid(),content: editorText.value}chatState.addSession(data)// 清空editorText.value = ''
}
const handleKeydown = (e) => {// ctrl+enterif(e.ctrlKey && e.keyCode == 13) {handleSubmit()}
}
const handleClear = () => {emit('clear')
}
// 选择图片
const handleUploadImage = () => {let file = uploadImgRef.value.files[0]if(!file) returnlet 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.resultlet 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 = ssidthis.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', // 不设置则是默认appstorage: localStorage,paths: ['aa', 'bb'] // 设置缓存键} */
})
好了基于vue3+vite4+pinia2开发模仿chatgpt聊天就分享到这里。
Tauri-Vue3聊天实例|Tauri跨端聊天