Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE

原文:Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE - 技术经验 - W3xue

基于electron25+vite4+vue3仿制chatgpt客户端聊天模板ElectronChatGPT

electron-chatgpt 使用最新桌面端技术Electron25.x结合Vite4.x全家桶技术开发跨端模仿ChatGPT智能聊天程序模板。支持经典+分栏两种布局、暗黑+明亮主题模式,集成electron封装多窗口及通讯功能。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第1张图片

技术栈

  • 编码工具:vscode
  • 框架技术:electron25+vite4+vue3+pinia2
  • 组件库:veplus (基于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

项目结构

基于electron最新版本融合vite4.x技术搭建模仿chatgpt桌面端程序。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第2张图片

如果对electron+vite4创建跨端应用及多开窗口感兴趣,可以去看看之前的这两篇分享文章。

https://www.cnblogs.com/xiaoyan2017/p/17436076.html

https://www.cnblogs.com/xiaoyan2017/p/17442502.html

随着electron快速迭代更新,加上vite极速编译,二者配合创建的应用运行速度超快。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第3张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第4张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第5张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第6张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第7张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第8张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第9张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第10张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第11张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第12张图片

Vue3桌面UI组件库

考虑到项目比较轻量级,所以采用自研vue3组件库ve-plus

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第13张图片

关于veplus组件库这里不作过多介绍,之前有过一篇分享文章,大家可以去看看。

https://www.cnblogs.com/xiaoyan2017/p/17170454.html

项目布局

项目整体大致分为顶部导航工具栏+左侧会话记录/操作链接+右侧会话区/编辑框等模块。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第14张图片

 
  

Electron主进程入口

根目录下新建 electron-main.js 作为主进程入口文件。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第15张图片

 
  
  1. /**
  2. * 主进程入口
  3. * @author YXY
  4. */
  5. const { app, BrowserWindow } = require('electron')
  6. const MultiWindow = require('./src/multiwindow')
  7. // 屏蔽安全警告
  8. // ectron Security Warning (Insecure Content-Security-Policy)
  9. process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
  10. const createWindow = () => {
  11. let win = new MultiWindow()
  12. win.createWin({isMainWin: true})
  13. }
  14. app.whenReady().then(() => {
  15. createWindow()
  16. app.on('activate', () => {
  17. if(BrowserWindow.getAllWindows().length === 0) createWindow()
  18. })
  19. })
  20. app.on('window-all-closed', () => {
  21. if(process.platform !== 'darwin') app.quit()
  22. })

使用electron的vite插件,在vite.config.js中配置入口。

 
  
  1. import { defineConfig, loadEnv } from 'vite'
  2. import vue from '@vitejs/plugin-vue'
  3. import electron from 'vite-plugin-electron'
  4. import { resolve } from 'path'
  5. import { parseEnv } from './src/utils/env'
  6. export default defineConfig(({ command, mode }) => {
  7. const viteEnv = loadEnv(mode, process.cwd())
  8. const env = parseEnv(viteEnv)
  9. return {
  10. plugins: [
  11. vue(),
  12. electron({
  13. // 主进程入口文件
  14. entry: 'electron-main.js'
  15. })
  16. ],
  17. /*构建选项*/
  18. build: {
  19. /* minify: 'esbuild', // 打包方式 esbuild(打包快)|terser
  20. chunkSizeWarningLimit: 2000, // 打包大小警告
  21. rollupOptions: {
  22. output: {
  23. chunkFileNames: 'assets/js/[name]-[hash].js',
  24. entryFileNames: 'assets/js/[name]-[hash].js',
  25. assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
  26. }
  27. } */
  28. // 如果打包方式是terser,则配置如下
  29. /* minify: "terser",
  30. terserOptions: {
  31. compress: {
  32. // 去掉所有console和debugger
  33. // drop_console: true,
  34. // drop_debugger: true,
  35. drop_console: command !== 'serve',
  36. drop_debugger: command !== 'serve',
  37. //pure_funcs:['console.log'] // 移除console.log
  38. }
  39. } */
  40. },
  41. esbuild: {
  42. // 打包去除 console.log 和 debugger
  43. drop: env.VITE_DROP_CONSOLE && command === 'build' ? ["console", "debugger"] : []
  44. },
  45. /*开发服务器选项*/
  46. server: {
  47. // 端口
  48. port: env.VITE_PORT,
  49. // ...
  50. },
  51. resolve: {
  52. // 设置别名
  53. alias: {
  54. '@': resolve(__dirname, 'src'),
  55. '@assets': resolve(__dirname, 'src/assets'),
  56. '@components': resolve(__dirname, 'src/components'),
  57. '@views': resolve(__dirname, 'src/views')
  58. }
  59. }
  60. }
  61. })

需要注意:由于目前Electron 尚未支持 "type": "module",需要在package.json中去掉,并且配置 "main": "electron-main.js", 入口。

Electron自定义无边框窗口工具栏

创建窗口的时候配置 frame: false 参数,创建的窗口则没有系统顶部导航栏及边框。拖拽区域/最大化/最小化及关闭按钮均需要自定义操作。

通过设置css3属性 -webkit-app-region: drag ,则可对自定义区域进行拖拽操作,设置后按钮/链接点击则会失效,这时通过对按钮或链接设置-webkit-app-region: no-drag就可恢复事件响应。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第16张图片

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第17张图片

不过设置-webkit-app-region: drag,点击鼠标右键,会出现上图系统菜单,经过一番调试,windows下可以暂时通过如下方法屏蔽右键菜单。

 
  
  1. // 屏蔽系统右键菜单
  2. win.hookWindowMessage(278, () => {
  3. win.setEnabled(false)
  4. setTimeout(() => {
  5. win.setEnabled(true)
  6. }, 100)
  7. return true
  8. })

components/titlebar目录自定义工具栏条。

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

 
  
 
  

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第18张图片

在 index.vue 中引入 control.vue 操作按钮,并支持自定义左侧、标题等功能。

 
  

Electron创建系统托盘图标

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第19张图片

 
  
  1. // 创建系统托盘图标
  2. createTray() {
  3. console.log('——+——+——Start Create Tray!')
  4. console.log(__dirname)
  5. console.log(join(process.env.ROOT, 'resource/tray.ico'))
  6. const trayMenu = Menu.buildFromTemplate([
  7. {
  8. label: '打开主界面',
  9. icon: join(process.env.ROOT, 'resource/home.png'),
  10. click: () => {
  11. try {
  12. for(let i in this.group) {
  13. let win = this.getWin(i)
  14. if(!win) return
  15. // 是否主窗口
  16. if(this.group[i].isMainWin) {
  17. if(win.isMinimized()) win.restore()
  18. win.show()
  19. }
  20. }
  21. } catch (error) {
  22. console.log(error)
  23. }
  24. }
  25. },
  26. {
  27. label: '设置中心',
  28. icon: join(process.env.ROOT, 'resource/setting.png'),
  29. click: () => {
  30. for(let i in this.group) {
  31. let win = this.getWin(i)
  32. if(win) win.webContents.send('win__ipcData', { type: 'CREATE_WIN_SETTING', value: null })
  33. }
  34. },
  35. },
  36. {
  37. label: '锁屏',
  38. icon: join(process.env.ROOT, 'resource/lock.png'),
  39. click: () => null,
  40. },
  41. {
  42. label: '关闭托盘闪烁',
  43. click: () => {
  44. this.flashTray(false)
  45. }
  46. },
  47. {type: 'separator'},
  48. /* {
  49. label: '重启',
  50. click: () => {
  51. // app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })
  52. // app.exit(0)
  53. }
  54. }, */
  55. {
  56. label: '关于',
  57. click: () => {
  58. for(let i in this.group) {
  59. let win = this.getWin(i)
  60. if(win) win.webContents.send('win__ipcData', { type: 'CREATE_WIN_ABOUT', value: null })
  61. }
  62. }
  63. },
  64. {
  65. label: '关闭应用并退出',
  66. icon: join(process.env.ROOT, 'resource/quit.png'),
  67. click: () => {
  68. dialog.showMessageBox(this.main, {
  69. title: '询问',
  70. message: '确定要退出应用程序吗?',
  71. buttons: ['取消', '最小化托盘', '退出应用'],
  72. type: 'error',
  73. noLink: false, // true传统按钮样式 false链接样式
  74. cancelId: 0
  75. }).then(res => {
  76. console.log(res)
  77. const index = res.response
  78. if(index == 0) {
  79. console.log('取消')
  80. }if(index == 1) {
  81. console.log('最小化托盘')
  82. for(let i in this.group) {
  83. let win = this.getWin(i)
  84. if(win) win.hide()
  85. }
  86. }else if(index == 2) {
  87. console.log('退出应用')
  88. try {
  89. for(let i in this.group) {
  90. let win = this.getWin(i)
  91. if(win) win.webContents.send('win__ipcData', { type: 'WIN_LOGOUT', value: null })
  92. }
  93. // app.quit 和 app.exit(0) 都可退出应用。
  94. // 前者可以被打断并触发一些事件,而后者将强制应用程序退出而不触发任何事件或允许应用程序取消操作。
  95. app.quit()
  96. } catch (error) {
  97. console.log(error)
  98. }
  99. }
  100. })
  101. }
  102. }
  103. ])
  104. this.tray = new Tray(this.trayIco1)
  105. this.tray.setContextMenu(trayMenu)
  106. this.tray.setToolTip(app.name)
  107. this.tray.on('double-click', () => {
  108. console.log('double clicked')
  109. })
  110. // 开启托盘闪烁
  111. // this.flashTray(true)
  112. }

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第20张图片

托盘图标、右键菜单图标及打包图标均在resource目录下。

Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第21张图片

Electron打包脚本electron-builder

在根目录新建一个electron打包配置文件electron-builder.json。

 
  
  1. {
  2. "productName": "Electron-ChatGPT",
  3. "appId": "com.yxy.electron-chatgpt-vue3",
  4. "copyright": "Copyright ? 2023-present Andy",
  5. "compression": "maximum",
  6. "asar": true,
  7. "directories": {
  8. "output": "release/${version}"
  9. },
  10. "nsis": {
  11. "oneClick": false,
  12. "allowToChangeInstallationDirectory": true,
  13. "perMachine": true,
  14. "deleteAppDataOnUninstall": true,
  15. "createDesktopShortcut": true,
  16. "createStartMenuShortcut": true,
  17. "shortcutName": "ElectronVite4Vue3"
  18. },
  19. "win": {
  20. "icon": "./resource/shortcut.ico",
  21. "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",
  22. "target": [
  23. {
  24. "target": "nsis",
  25. "arch": ["ia32"]
  26. }
  27. ]
  28. },
  29. "mac": {
  30. "icon": "./resource/shortcut.icns",
  31. "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
  32. },
  33. "linux": {
  34. "icon": "./resource",
  35. "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
  36. }
  37. }

Electron主渲染进程通讯传值

由于electron主渲染进程一般都是单窗口之间进行传值。如果需要在多个窗口间传值,如切换主题功能,则需要在渲染进程发送请求,主进程监听后再发送请求给渲染进程(App.vue中监听)。

 
  
  1. class="toolbar__item"
  2. :title="`切换 暗黑/明亮 模式(当前 ${appState.config.isDark ? '暗黑' : '明亮'}模式)`"
  3. @click="changeMode"
  4. >
  • // 主题切换
  • const changeMode = () => {
  • appState.config.isDark = !appState.config.isDark
  • ipcRenderer.send('win__postData', appState.config.isDark)
  • }
  • 在主进程中使用ipcMain.on监听。

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

    然后在渲染进程App.vue页面监听并处理通讯传值。

     
      
    1. /**
    2. * 接收主进程发送的事件
    3. */
    4. ipcRenderer.on('win__postData', (e, data) => {
    5. console.log('——+——+——receive multiwin data:', data)
    6. switch(data.type) {
    7. // 退出登录
    8. case 'WIN_LOGOUT':
    9. appState.$reset()
    10. break;
    11. // 布局切换
    12. case 'CHANGE_LAYOUT':
    13. appState.config.layout = data.value
    14. break;
    15. // 切换主题
    16. case 'CHANGE_MODE':
    17. appState.config.isDark = data.value
    18. appState.changeDark()
    19. break;
    20. // 侧边栏收缩
    21. case 'CHANGE_COLLAPSE':
    22. appState.config.collapse = data.value
    23. break;
    24. }
    25. })

    这样就能简单实现多窗口传值了。如果大家有其他方法,欢迎一起交流学习哈~

    Ok,基于electron25+vue3开发桌面端仿chatgpt聊天实例就先分享到这里,希望对大家有所帮助??

    最后附上一个Vue3+Tauri跨端聊天项目

    https://www.cnblogs.com/xiaoyan2017/p/16830689.html

    Electron-ChatGPT桌面端ChatGPT实例|electron25+vue3聊天AI模板EXE_第22张图片

    本文为博主原创文章,未经博主允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)

    原文链接:https://www.cnblogs.com/xiaoyan2017/p/17468074.html

    你可能感兴趣的:(大数据可视化,electron,chatgpt,前端)