前言
随着 chatgpt
智能聊天继续火爆,越来越多的开发者参加其中。明天给大家分享 vite4.x
搭建繁难网页版仿 ChatGpt 聊天程序 Vue3ChatGPT。
技术框架
- 编辑工具: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
vue3-chatgpt
反对 两种布局模板、dark+light 模式、全屏 + 半屏展现、Markdown 语法解析、侧边栏收起 等性能。
我的项目构造
基于 vite4.x 构建我的项目,采纳 vue3 setup 语法糖编码。
main.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')
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'
}
}
}
})
公共布局模板
整体分为顶部导航栏、左侧菜单栏、右侧主体栏。
<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>
/**
* 聊天状态治理
* @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'] // 设置缓存键
} */
})
如上图:聊天框采纳 Input 组件实现性能。
<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>
OK,基于 vue3 开发仿造 chatgpt 实例就先分享这么多,心愿大家能喜爱~~
https://segmentfault.com/a/1190000042710924
https://segmentfault.com/a/1190000040711708