electron25-vue3-chatgpt桌面端聊天程序

2023-6-17 15 6/17

基于vite.js+electron25.x+pinia2仿制chatgpt聊天应用实例ElectronChatGPT

electron-chatgpt 支持暗黑+亮色主题、支持创建多开窗口管理器。

技术栈

  • 编辑器:vscode
  • 框架技术:electron25+vite4+vue3+pinia2
  • 组件库:ve-plus (基于vue3自定义组件库)
  • 打包工具:electron-builder^23.6.0
  • 调试工具:electron-devtools-installer^3.2.0
  • 代码高亮:highlight.js^11.7.0
  • markdown插件:vue3-markdown-it
  • 本地存储插件:pinia-plugin-persistedstate^3.1.0
  • electron+vite插件:vite-plugin-electron^0.11.2

 

electron25-vue3-chatgpt桌面端聊天程序

项目结构图

electron25-vue3-chatgpt桌面端聊天程序

整个项目结构层级清晰,electron整合vite4.x实现桌面端程序,采用vue3 setup语法编码开发。

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

electron25-vue3-chatgpt桌面端聊天程序

布局模板图

整个项目大致分为顶部操作栏+侧边栏会话区/链接+右侧主体三个模块。

electron25-vue3-chatgpt桌面端聊天程序

<template>    <div class="vegpt__layout flexbox flex-col">        <!-- //导航操作栏 -->        <Toolbar />                <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>    </div></template>

electron-main主进程入口

新建一个electron-main.js文件,用于主线程入口配置文件。

electron25-vue3-chatgpt桌面端聊天程序

/** * 主进程入口 * @author YXY */
const { app, BrowserWindow } = require('electron')
const MultiWindow = require('./src/multiwindow')
// 屏蔽安全警告// ectron Security Warning (Insecure Content-Security-Policy)process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
const createWindow = () => {    let win = new MultiWindow()    win.createWin({isMainWin: true})}
app.whenReady().then(() => {    createWindow()    app.on('activate', () => {        if(BrowserWindow.getAllWindows().length === 0) createWindow()    })})
app.on('window-all-closed', () => {    if(process.platform !== 'darwin') app.quit()})

紧接着在 vite.config.js 中引入 vite-plugin-electron 插件并配置入口。

import { defineConfig, loadEnv } from 'vite'import vue from '@vitejs/plugin-vue'import electron from 'vite-plugin-electron'import { resolve } from 'path'import { parseEnv } from './src/utils/env'
export default defineConfig(({ command, mode }) => {  const viteEnv = loadEnv(mode, process.cwd())  const env = parseEnv(viteEnv)
  return {    plugins: [      vue(),      electron({        // 主进程入口文件        entry: 'electron-main.js'      })    ],        /*构建选项*/    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]',          }      } */            // 如果打包方式是terser,则配置如下      /* minify: "terser",      terserOptions: {        compress: {          // 去掉所有console和debugger          // drop_console: true,          // drop_debugger: true,
          drop_console: command !== 'serve',          drop_debugger: command !== 'serve',          //pure_funcs:['console.log'] // 移除console.log        }      } */    },    esbuild: {      // 打包去除 console.log 和 debugger      drop: env.VITE_DROP_CONSOLE && command === 'build' ? ["console", "debugger"] : []    },
    /*开发服务器选项*/    server: {      // 端口      port: env.VITE_PORT,      // ...    },
    resolve: {      // 设置别名      alias: {        '@': resolve(__dirname, 'src'),        '@assets': resolve(__dirname, 'src/assets'),        '@components': resolve(__dirname, 'src/components'),        '@views': resolve(__dirname, 'src/views')      }    }  }})

由于electron暂未支持type: module,需要在package.json中去掉,并设置main入口。

electron25-vue3-chatgpt桌面端聊天程序

electron创建无导航窗口

electron25-vue3-chatgpt桌面端聊天程序

在创建窗口的时候,设置frame: false属性,窗口则无边框和顶部导航栏。

设置css3属性 -webkit-app-region: drag ,则可对自定义区域进行拖拽操作。-webkit-app-region: no-drag则是取消拖拽功能。

electron25-vue3-chatgpt桌面端聊天程序

titlebar/control.vue自定义最大化/最小化/关闭按钮。

 

<template>    <div class="vegpt__control ve__nodrag">        <div class="vegpt__control-btns" :style="{'color': color}">            <slot />            <div v-if="isTrue(minimizable)" class="btn win-btn win-min" @click="handleMin"><i class="iconfont ve-icon-minimize"></i></div>            <div v-if="isTrue(maximizable) && winCfg.window.resizable" class="btn win-btn win-maxmin" @click="handleRestore">                <i class="iconfont" :class="isMaximized ? 've-icon-maxrestore' : 've-icon-maximize'"></i>            </div>            <div v-if="isTrue(closable)" class="btn win-btn win-close" @click="handleQuit"><i class="iconfont ve-icon-close"></i></div>        </div>    </div></template>

<script setup>    import { onMounted, ref } from 'vue'    import { winCfg, setWin } from '@/multiwindow/actions'    import { appStore } from '@/pinia/modules/app'    import { isTrue } from '@/utils'
    const appState = appStore()
    const props = defineProps({        // 标题颜色        color: String,
        // 窗口是否可以最小化        minimizable: { type: [Boolean, String], default: true },        // 窗口是否可以最大化        maximizable: { type: [Boolean, String], default: true },        // 窗口是否可以关闭        closable: { type: [Boolean, String], default: true }    })
    // 是否最大化    let isMaximized = ref(false)
    onMounted(() => {        window.electronAPI.invoke('win__isMaximized').then(data => {            console.log(data)            isMaximized.value = data        })        window.electronAPI.receive('win__hasMaximized', (e, data) => {            console.log(data)            isMaximized.value = data        })    })
    // 最小化    const handleMin = () => {        window.electronAPI.send('win__minimize')    }    // 最大化/还原    const handleRestore = () => {        window.electronAPI.invoke('win__max2min').then(data => {            console.log(data)            isMaximized.value = data        })    }    // 关闭窗体    const handleQuit = () => {        if(winCfg.window.isMainWin) {            MessageBox.confirm('应用提示', '是否最小化到托盘, 不退出程序?', {                type: 'warning',                cancelText: '最小化至托盘',                confirmText: '残忍退出',                confirmType: 'danger',                width: 300,                callback: action => {                    if(action == 'confirm') {                        appState.$reset()                        setWin('close')                    }else if(action == 'cancel') {                        setWin('hide', winCfg.window.id)                    }                }            })        }else {            setWin('close', winCfg.window.id)        }    }</script>

electron25-vue3-chatgpt桌面端聊天程序

在titlebar/index.vue中引入control.vue文件。

<template>    <div class="vegpt__titlebar" :class="{'fixed': isTrue(fixed), 'transparent fixed': isTrue(transparent)}">        <div class="vegpt__titlebar-wrapper flexbox flex-alignc ve__drag" :style="{'background': bgcolor, 'color': color, 'z-index': zIndex}">            <slot name="left">                <img src="/logo.png" height="20" style="margin-left: 10px;" />            </slot>            <div class="vegpt__titlebar-title" :class="{'center': isTrue(center)}">                <slot name="title">{{ title || winCfg.window.title || env.VITE_APPTITLE }}</slot>            </div>
            <!-- 控制按钮 -->            <Control :minimizable="minimizable" :maximizable="maximizable" :closable="closable">                <slot name="btn" />            </Control>        </div>    </div></template>

顶部导航栏支持自定义背景颜色、文字颜色、标题居中、左右自定义插槽等功能。

electron-builder打包配置

新建electron-builder.json打包参数配置文件。

electron25-vue3-chatgpt桌面端聊天程序

{    "productName": "Electron-ChatGPT",    "appId": "com.yxy.electron-chatgpt-vue3",    "copyright": "Copyright © 2023-present Andy",    "compression": "maximum",    "asar": true,    "directories": {        "output": "release/${version}"    },    "nsis": {        "oneClick": false,        "allowToChangeInstallationDirectory": true,        "perMachine": true,        "deleteAppDataOnUninstall": true,        "createDesktopShortcut": true,        "createStartMenuShortcut": true,        "shortcutName": "ElectronVite4Vue3"    },    "win": {        "icon": "./resource/shortcut.ico",        "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",        "target": [            {                "target": "nsis",                "arch": ["ia32"]            }        ]    },    "mac": {        "icon": "./resource/shortcut.icns",        "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"    },    "linux": {        "icon": "./resource",        "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"    }}

electron多窗口透传参数

electron实现主渲染进程多窗口传值。

electron25-vue3-chatgpt桌面端聊天程序

<div    class="toolbar__item"    :title="`切换 暗黑/明亮 模式(当前 ${appState.config.isDark ? '暗黑' : '明亮'}模式)`"    @click="changeMode">    <Icon :name="appState.config.isDark ? 've-icon-sunny' : 've-icon-yewan'" /></div>
// 主题切换const changeMode = () => {    appState.config.isDark = !appState.config.isDark    ipcRenderer.send('win__postData', appState.config.isDark)}

在主进程中通过ipcMain.on监听。

// 主/渲染进程传参ipcMain.on('win__postData', (event, args) => {    mainWin.webContents.send('win__postData', args)})

在渲染进程App.vue页面接收监听响应。

/** * 接收主进程发送的事件 */ipcRenderer.on('win__postData', (e, data) => {    console.log('——+——+——receive multiwin data:', data)
    switch(data.type) {        // 退出登录        case 'WIN_LOGOUT':            appState.$reset()            break;        // 布局切换        case 'CHANGE_LAYOUT':            appState.config.layout = data.value            break;        // 切换主题        case 'CHANGE_MODE':            appState.config.isDark = data.value            appState.changeDark()            break;        // 侧边栏收缩        case 'CHANGE_COLLAPSE':            appState.config.collapse = data.value            break;      }})

这样基本就简单实现多窗口透传参数了。

以上就是electron25.x+vite4.x整合开发桌面端仿chatgpt实例的一些分享。

- THE END -

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论