乐趣区

关于chatgpt:electron25vue3chatgpt基于vite4electron客户端仿制chatgpt聊天应用

这段时间一些在钻研 electron25 交融 vite4 开发跨端利用,果然真香!随着 electron 疾速迭代降级,响应速度越来越快,加上 vite.js 极速编译构建能力,二者联合堪称锦上添花。

electron-chatGPT 反对 亮色 / 暗黑 两种主题模式。

应用技术

  • 开发工具:vscode
  • 技术框架:electron25+vite4+vue3+vueRouter@4+pinia2
  • 组件库:veplus (基于 vue3 自定义组件库)
  • 打包工具:electron-builder^23.6.0
  • 调试工具:electron-devtools-installer^3.2.0
  • markdown 插件:vue3-markdown-it
  • 缓存插件:pinia-plugin-persistedstate^3.1.0
  • electron 整合 vite 插件:vite-plugin-electron^0.11.2

我的项目阐明目录

我的项目布局框架

如下图:整个我的项目大抵分为顶部操作区 + 侧边栏 + 主体内容区。

<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 主过程入口配置

在我的项目根目录新建一个 electron-main.js 作为主过程入口文件。

/**
 * 主过程入口
 * @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,引入 electron 联合 vite 插件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: {// ...},
    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 自定义操作栏(最大化 / 最小化 / 敞开)

通过 BrowserWindow 创立新窗口,配置frame: false,即可实现无边框窗体。

设置 css3 属性 -webkit-app-region: drag,顶部操作栏则可自定义拖拽动作。

管制按钮 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>
<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 配置打包参数

在我的项目根目录新建一个 electron-builder.json 文件,用于自定义打包参数。

{
    "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}"
    }
}

好了,以上就是 vite4 整合 electron25 开发桌面端模拟 chatgpt 聊天程序。

https://segmentfault.com/a/1190000042710924

https://segmentfault.com/a/1190000043667464

退出移动版