赞
踩
基于Vue3.x+Pinia2+VueRouter+Vue3-Markdown等技术构建仿ChatGPT网页端聊天程序。支持经典+分栏界面布局、light/dark模式、全屏+半屏显示、Markdown语法解析、侧边栏隐藏等功能。
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' } } } })
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自定义UI组件库Ve-plus。一个支持40+组件的轻量级组件库。
安装组件
yarn add ve-plus
npm i ve-plus --save
https://blog.csdn.net/yanxinyun1990/article/details/129312570
项目支持2种布局模式,整体分为顶栏+侧边栏+主体内容三大模块构成。
<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>
<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>
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) } }
/** * 聊天状态管理 * @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'] // 设置缓存键 } */ })
好了,基于vue3+vite4+pinia2开发模仿chatgpt聊天就分享到这里。
Tauri-Vue3聊天实例|Tauri跨端聊天
uniapp-ttlive短视频聊天|uniapp+uview仿抖音实例
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。