首页 > 其他分享 >Vite-WeGPT聊天AI实例|vue3+pinia仿ChatGPT聊天界面

Vite-WeGPT聊天AI实例|vue3+pinia仿ChatGPT聊天界面

时间:2023-05-07 09:33:23浏览次数:54  
标签:vue const AI sessionId 聊天 pinia vue3 import Vite

基于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聊天功能的介绍,希望大家喜欢~~

标签:vue,const,AI,sessionId,聊天,pinia,vue3,import,Vite
From: https://www.cnblogs.com/xiaoyan2017/p/17378903.html

相关文章

  • 【java】javamail+freemarker生成邮件模板,并发送邮件
    一、前言      在上一篇博客中小编向大家介绍了发送带附件的邮件,实践一下也是不错的。这一篇博客是为下一篇博客进行铺垫的,因为项目中需要一个推送的功能,要把推送的信息灵活的显示到一个固有的模板上。所以为了达到这个目的,小编就引入了freemarker。下面向大家介绍。二、What......
  • Flink Chain任务链分隔
    Chain分隔文章目录Chain分隔如何切断任务链?startNewChain与disableChaining区别全局切断任务链(chain)web端效果查看隔离后依赖链忙碌程度什么是Backpressured(被压/反压)?代码样例参考文献如何切断任务链?由于共享slot的存在,当一个任务链的计算量特别庞大时,且只在一个slot上执行......
  • 《花雕学AI》WeTab+ChatGPT:让浏览器变成你的智能助手
    引言:浏览器是我们日常使用的最重要的工具之一,它可以帮助我们获取信息、娱乐、学习、工作等。但是,传统的浏览器往往不能满足我们的个性化需求,也不能给我们提供智能化的服务。那么,有没有一种浏览器可以让我们的体验更加高效、有趣、个性化呢?答案是肯定的。WeTab和ChatGPT就是这样......
  • vite.config.ts配置文件
    import{defineConfig}from'vite'importvuefrom'@vitejs/plugin-vue'import{resolve}from'path'importvueSetupExtendfrom'vite-plugin-vue-setup-extend'importAutoImporttfrom'unplugin-auto-import/v......
  • Bing的AI聊天使用体验
    Bing开启了AI聊天功能,我们这里做一个简单的测评,看看各种AI是否达到预期效果。PS:没有“魔法”的各位就不用看下去了1.登陆打开edge,遇到的第一个问题就是,使用“魔法”后,登陆报错0x80190001(不登录每天的聊天次数有限)搜索资料后发现一个好用的解决方式,下载fiddler,打开win......
  • 利用AidLux实现热成像电力巡检项目操作演示
    本项目参考AidLux五月实战训练营内容:基于热成像的巡检及AidLux工程方案。利用AIdlux平台和手机移动端算力,轻松落地部署基于热成像智能巡检项目。检测视频效果如下:https://www.bilibili.com/video/BV1Fg4y1571s/?vd_source=5d3ae1cbed185c1432e0f3004ca324fc......
  • vite vue3 EsLint配置
    1、安装ESLintnpmi-Deslint2、初始化配置EsLintnpxeslint--init2-1、选择模式 2-2、选择语言模块 2-3、选择语言框架 2-4、是否使用ts 2-5、代码在哪里运行2-6、选择一个风格2-7、你想遵循哪一种风格指南2-8、希望配置文件是什么格式2-9、依赖......
  • rgi main --input_sequence temp/out_pro.fa --output_file result/protein --inpu
    这是一个命令行命令,用于对temp/out_pro.fa文件进行抗菌基因分析。参数的含义如下:rgi:表示运行resistantgeneidentifier(rgi)程序。main:指定使用rgi的主要模式。--input_sequencetemp/out_pro.fa:指定输入序列文件名和路径。--output_fileresult/protein:指......
  • 使用Aidlux,轻松落地电力巡检AI应用
    本项目参考AidLuxAI实战训练营内容,3-4个课时落地AI应用电力线路是电力系统的重要组成部分,它的安全可靠运行直接关系到一个国家经济的稳定发展。电力线路一旦出现故障,则有可能影响到成片区域的供电安全,严重的甚至造成不可估量的损失。因此,预防电力线路故障预防历来是电力......
  • mysql explain 字段说明
    EXPLAIN语句提供有关MySQL执行语句的信息,每个表返回一行信息。处理语句时,读取它们的顺序列出输出中的表执行EXPLAIN语句会打印出下列内容id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra一、id记录标识符二、select_type......