Vite-WeGPT聊天AI实例|vue3+pinia仿ChatGPT聊天界面
基于vue3.x+vite4+pinia2仿chatgpt聊天模拟实例Vue3-WeGPT。
基于Vite4.x+Vue3+Pinia2+VEPlus+Vue3-Markdown等技术实现仿ChatGPT聊天AI界面实例。整体界面简洁清新、支持2种界面布局、暗黑+亮色模式、全屏+半屏展示、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
- 样式处理:sass^1.62.0
特征
- 最新前端技术栈 vite4、vue3、pinia2、vue-router、vue-i18n
- 支持中文/英文/繁体多语言
- 支持dark/light两种模式
- 提供2种模板布局
- 支持半屏/全屏展示
- 支持更换背景皮肤
- 搭配轻量级vue3组件库ve-plus
项目结构
整个项目采用vite4.x搭建项目,vue3 setup语法编码,运行编译速度极快。
Vue3组件库
项目依然采用自己开发的vue3自定义UI组件库VE-Plus。提供40+常用组件,运行轻巧、风格清新。
安装使用
yarn add ve-plus npm install ve-plus -S cnpm install ve-plus -S
https://www.cnblogs.com/xiaoyan2017/p/17170454.html
布局模板
提供了经典+分栏两种布局展示模板。
<script setup> import { computed } from 'vue' import { appStore } from '@/store/modules/app' // 引入布局模板 import Classic from './layout/classic/index.vue' import Columns from './layout/columns/index.vue' const store = appStore() const config = computed(() => store.config) const LayoutConfig = { classic: Classic, columns: Columns } </script> <template> <div class="vegpt__container" :class="{'is-half': store.config.halfScreen}" :style="{'--themeSkin': store.config.skin}"> <component :is="LayoutConfig[config.layout]" /> </div> </template> <style lang="scss" scoped></style>
<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>
vite.config.js配置
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' } } } })
Vue3主入口文件
在main.js引入路由、Pinia状态管理及注册一些公共组件。
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')
chatgpt聊天框
聊天框使用Input组件实现,设置type=textarea,支持多行自适应高度。
<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 class="dot"></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>
<script setup> 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 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) } } // ... </script>
vue3项目现在比较推荐pinia替代vuex进行状态管理。使用 pinia 和 pinia-plugin-persistedstate 看看本地存储。
import { createPinia } from 'pinia' // 引入pinia本地持久化存储 import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate) export default pinia
/** * 聊天状态管理 * @author YXY Q:282310962 */ 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'] // 设置缓存键 } */ })
OK,以上就是vue3.x开发仿制ChatGPT聊天功能的介绍,希望大家喜欢~~💪
最后附上一个Vite+Electron桌面MacUI实例
https://www.cnblogs.com/xiaoyan2017/p/14926338.html
本文为博主原创文章,未经博主允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)