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封装多窗口及通讯功能。

技术栈

  • 编码工具: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+vite4创建跨端应用及多开窗口感兴趣,可以去看看之前的这两篇分享文章。

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

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

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

Vue3桌面UI组件库

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

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

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

项目布局

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

 
  1. <template>
  2. <div class="vegpt__layout flexbox flex-col">
  3. <!-- //顶部工具栏 -->
  4. <Toolbar />
  5. <div class="ve__layout-body flex1 flexbox">
  6. <!-- //侧边栏 -->
  7. <div class="ve__layout-menus flexbox" :class="{'hidden': store.config.collapse}">
  8. <aside class="ve__layout-aside flexbox flex-col">
  9. <ChatNew />
  10. <Scrollbar class="flex1" autohide size="4" gap="1">
  11. <ChatList />
  12. </Scrollbar>
  13. <ExtraLink />
  14. <Collapse />
  15. </aside>
  16. </div>
  17. <!-- //主体区域 -->
  18. <div class="ve__layout-main flex1 flexbox flex-col">
  19. <Main />
  20. </div>
  21. </div>
  22. </div>
  23. </template>

Electron主进程入口

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

 
  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就可恢复事件响应。

不过设置-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自定义最大化/最小化/关闭按钮

 
  1. <template>
  2. <div class="vegpt__control ve__nodrag">
  3. <div class="vegpt__control-btns" :style="{'color': color}">
  4. <slot />
  5. <div v-if="isTrue(minimizable)" class="btn win-btn win-min" @click="handleMin"><i class="iconfont ve-icon-minimize"></i></div>
  6. <div v-if="isTrue(maximizable) && winCfg.window.resizable" class="btn win-btn win-maxmin" @click="handleRestore">
  7. <i class="iconfont" :class="isMaximized ? 've-icon-maxrestore' : 've-icon-maximize'"></i>
  8. </div>
  9. <div v-if="isTrue(closable)" class="btn win-btn win-close" @click="handleQuit"><i class="iconfont ve-icon-close"></i></div>
  10. </div>
  11. </div>
  12. </template>
 
  1. <template>
  2. <div class="vegpt__control ve__nodrag">
  3. <div class="vegpt__control-btns" :style="{'color': color}">
  4. <slot />
  5. <div v-if="isTrue(minimizable)" class="btn win-btn win-min" @click="handleMin"><i class="iconfont ve-icon-minimize"></i></div>
  6. <div v-if="isTrue(maximizable) && winCfg.window.resizable" class="btn win-btn win-maxmin" @click="handleRestore">
  7. <i class="iconfont" :class="isMaximized ? 've-icon-maxrestore' : 've-icon-maximize'"></i>
  8. </div>
  9. <div v-if="isTrue(closable)" class="btn win-btn win-close" @click="handleQuit"><i class="iconfont ve-icon-close"></i></div>
  10. </div>
  11. </div>
  12. </template>
  13. <script setup>
  14. import { onMounted, ref } from 'vue'
  15. import { winCfg, setWin } from '@/multiwindow/actions'
  16. import { appStore } from '@/pinia/modules/app'
  17. import { isTrue } from '@/utils'
  18. const appState = appStore()
  19. const props = defineProps({
  20. // 标题颜色
  21. color: String,
  22. // 窗口是否可以最小化
  23. minimizable: { type: [Boolean, String], default: true },
  24. // 窗口是否可以最大化
  25. maximizable: { type: [Boolean, String], default: true },
  26. // 窗口是否可以关闭
  27. closable: { type: [Boolean, String], default: true }
  28. })
  29. // 是否最大化
  30. let isMaximized = ref(false)
  31. onMounted(() => {
  32. window.electronAPI.invoke('win__isMaximized').then(data => {
  33. console.log(data)
  34. isMaximized.value = data
  35. })
  36. window.electronAPI.receive('win__hasMaximized', (e, data) => {
  37. console.log(data)
  38. isMaximized.value = data
  39. })
  40. })
  41. // 最小化
  42. const handleMin = () => {
  43. window.electronAPI.send('win__minimize')
  44. }
  45. // 最大化/还原
  46. const handleRestore = () => {
  47. window.electronAPI.invoke('win__max2min').then(data => {
  48. console.log(data)
  49. isMaximized.value = data
  50. })
  51. }
  52. // 关闭窗体
  53. const handleQuit = () => {
  54. if(winCfg.window.isMainWin) {
  55. MessageBox.confirm('应用提示', '是否最小化到托盘, 不退出程序?', {
  56. type: 'warning',
  57. cancelText: '最小化至托盘',
  58. confirmText: '残忍退出',
  59. confirmType: 'danger',
  60. width: 300,
  61. callback: action => {
  62. if(action == 'confirm') {
  63. appState.$reset()
  64. setWin('close')
  65. }else if(action == 'cancel') {
  66. setWin('hide', winCfg.window.id)
  67. }
  68. }
  69. })
  70. }else {
  71. setWin('close', winCfg.window.id)
  72. }
  73. }
  74. </script>

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

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

Electron创建系统托盘图标

 
  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. }

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

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. <div
  2. class="toolbar__item"
  3. :title="`切换 暗黑/明亮 模式(当前 ${appState.config.isDark ? '暗黑' : '明亮'}模式)`"
  4. @click="changeMode"
  5. >
  6. <Icon :name="appState.config.isDark ? 've-icon-sunny' : 've-icon-yewan'" />
  7. </div>
  8. // 主题切换
  9. const changeMode = () => {
  10. appState.config.isDark = !appState.config.isDark
  11. ipcRenderer.send('win__postData', appState.config.isDark)
  12. }

在主进程中使用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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/54871.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:用Python打造一个快捷聊天程序

用Python打造一个快捷聊天程序 如果你想要快速建立一个简单的聊天程序&#xff0c;Python是一个很好的选择。Python是一种易于学习和使用的编程语言&#xff0c;并且它有许多库和框架&#xff0c;可以使你能够快速地搭建一个聊天应用程序。下面我们简单介绍如何使用Python构建…

大咖思辨-42 | 大模型狂飙背后的推动力是什么?让我们一探究竟!

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 随着ChatGPT、GPT-4、Copilot的火热出圈&#xff0c;大模型已经成为AI领域的研究热点与必争之地。2023年3月28日&#xff0c;由AI TIME、清华校友总会AI大数据专委会联合主办&#xff0c;有孚网络、智谱AI、金地…

通用人工智能+智能车舱,商汤绝影带来怎样一番景象?

/ 导读 / 进入4月&#xff0c;中国科技圈迎来最卷的时刻&#xff0c;这可能中国AI史上一个重要节点。ChatGPT、GPT-4的出圈掀起了通用人工智能&#xff08;AGI&#xff09;的技术狂潮&#xff0c;引爆了一场以大模型为代表的生成式AI狂飙&#xff0c;也催生了新的研究范式。 转…

一文带您快速梳理ChatGPT、GPT4 和OpenAPI的关系

最近最火的几个词无疑是ChatGPT、GPT4 和OpenAPI&#xff0c;那么这三者究竟有什么关系呢&#xff1f;本文将带您进行快速梳理&#xff01; OpenAPI是一家公司 OpenAI是一家专注人工智能研究的公司&#xff0c;使命是确保人工智能造福全人类。官网&#xff1a; https://openai.…

趣挨踢 | 用大数据扒一扒蔡徐坤的真假流量粉

戳蓝字“CSDN云计算”关注我们哦&#xff01; 作者&#xff1a;AlfredWu 转自&#xff1a; Alfred数据室 前段时间央视新闻公开披露流量明星数据造假。作为一名数据猿&#xff0c;我们秉着好奇心点开了NBA新春贺岁形象大使蔡徐坤的微博&#xff0c;发现他的微博转发量除了最新…

生成身份证校验码(c语言)

生成身份证校验码 描述格式样例题解及详细注释 描述 我国的居民身份证是18位字符的编码&#xff0c;每个人的编码都是唯一的&#xff0c;校验规则如下&#xff1a; ∑&#xff08;ai*wi&#xff09;mod 11 1 i表示号码字符从左至右包括校验码字符在内的位置序号&#xff1b…

chatgpt赋能python:Python生成4位验证码的方法

Python生成4位验证码的方法 在网络安全中&#xff0c;验证码是一种常见的安全技术&#xff0c;它可以有效地防止恶意攻击&#xff0c;保护网站和应用程序安全。在这篇文章中&#xff0c;我们将介绍使用Python生成4位验证码的方法&#xff0c;并探讨如何在实际应用中使用它。 …

让ChatGPT3.5写的一个PHP图形验证码,大家觉得怎么样?

闲来无事让ChatGPT3.5写的一个PHP图形验证码&#xff0c;并运行了一下&#xff0c;感觉验证码有点丑。。。 下面是ChatGPT3.5写的代码和使用说明&#xff0c;各位大佬感受一下&#xff01; <?php // 创建图像 $imageWidth 150; $imageHeight 50; $image imagecreatetru…

开源商业化,走出“射手假说”迷雾

随着过去十年开源技术的“复兴”及全面发展&#xff0c;开源技术与商业化的关系日益紧密&#xff0c;成为了业内备受关注的话题。虽然在开源技术的发展历程中&#xff0c;“商业化”曾是一道难以逾越的障碍&#xff0c;但随着时间推移与业界不断探索&#xff0c;如今的开源技术…

【成为架构师系列】领导力就是两件事:断物和识人

领导力——是战略与执行的原动力 所以执行力和战略一定要结合起来。 战略的执行和落实需要掌握核心能力的人,所以领导力——是战略与执行的原动力。 简单地说,领导力就是两件事——“断物”和“识人”。 “断物”代表战略思考,“识人”代表执行能力。 目录 导读 领导力就是…

澳网:王雅繁0:2不敌本土名将 遗憾止步第二轮

资料图&#xff1a;王雅繁在比赛中。中新社记者 张畅 摄 中新网1月16日电 16日&#xff0c;2019赛季澳大利亚网球公开赛继续展开争夺&#xff0c;在女单第二轮的比赛中&#xff0c;中国金花王雅繁挑战15号种子巴蒂。比赛中王雅繁整场被对手压制&#xff0c;2&#xff1a;6/3&…

样本量很少如何获得最佳的效果?最新小样本学习工具包来啦!

关注公众号&#xff0c;发现CV技术之美 开发者简介 王雅晴&#xff0c;PaddleFSL负责人、飞桨高级开发者技术专家&#xff08;高级PPDE&#xff09;。2019年毕业于香港科技大学计算机科学及工程学系。通过百度公司AIDU计划加入百度研究院商业智能实验室&#xff0c;现任资深研发…

基于飞桨的小样本学习工具包助你举一反三

王雅晴&#xff0c;PaddleFSL负责人、飞桨高级开发者技术专家&#xff08;高级PPDE&#xff09;。2019年博士毕业于香港科技大学计算机科学及工程学系。通过百度公司AIDU计划加入百度研究院商业智能实验室&#xff0c;现任资深研发工程师及研究员。研究方向为机器学习&#xff…

《柳叶刀》专刊 | 城市设计、交通与人群健康:篇一

编辑团队 原文/ Billie Giles Corti&#xff0c;Anne Vrnez-Moudon,et al. 翻译/ 杨佳慧 校核/ 慧思慧想 文献/ 张美华 编辑/ 众山小 排版/ 王雅桐 一览众山小-可持续城市与交通 《城市设计、交通与人群健康》专刊 全球性的健康问题已成为21世纪人类社会的一项…

量化城市︱计算机视觉技术在街道品质量化评估中的应用

一览众山小 SustainableCity & Transportation 编辑团队 原文/ 肖天骏 &#xff08;美国微软必应搜索团队&#xff09; 文献/ 李安岭 校核/ 众山小 编辑/ 众山小 排版/ 王雅桐 译者萌像&导读&#xff1a; 我们 2014年11月28日期的文章《定量城市︱街道设计领域的…

【hadoop学习项目】10. 使用多级MR找出两两用户之间的共同好友

0. 项目结构 数据内容 刘灵薇 孙初丹,孙听兰,李秋翠,李绿春 王访琴 刘忆翠,钱语芙,钱平蝶 赵雅蕊 刘灵薇,刘雅蕊 王含蕾 钱语芙,李书蕾,李忆翠 钱雅蕊 李秋春,李初丹,孙听蓉 王绿春 李含烟,刘谷丝,孙秋春,钱雅蕊,赵语芙,钱南松,钱绿春,王听兰 刘含玉 赵绿春,王幻珊,刘语芙,赵怜…

2022年湖南省高职单招(语文)考试冲刺试题及答案

题库来源&#xff1a;优题宝公众号 2022年湖南省高职单招&#xff08;语文&#xff09;考试冲刺试题及答案&#xff0c;由优题宝公众号根据最新高职单招&#xff08;语文&#xff09;考试大纲与历年高职单招&#xff08;语文&#xff09;考试真题汇总编写&#xff0c;包含高职…

金道科技冲刺科创板:拟募资4.5亿 金言荣家族色彩浓厚

雷递网 雷建平 3月3日报道 浙江金道科技股份有限公司&#xff08;简称&#xff1a;“金道科技”&#xff09;日前递交招股书&#xff0c;准备在创业板上市&#xff0c;计划募资4.5亿元。 其中&#xff0c;3.9亿元用于新能源物流传动机械及液力传动变速箱建设项目&#xff0c;60…

2022年河北省高职单招(语文)考试冲刺试题及答案

题库来源&#xff1a;优题宝公众号 2022年河北省高职单招&#xff08;语文&#xff09;考试冲刺试题及答案&#xff0c;由优题宝公众号根据最新高职单招&#xff08;语文&#xff09;考试大纲与历年高职单招&#xff08;语文&#xff09;考试真题汇总编写&#xff0c;包含高职…

金道科技深交所上市:市值37亿 为金言荣家族企业

雷递网 雷建平 4月13日报道 浙江金道科技股份有限公司&#xff08;简称&#xff1a;“金道科技”&#xff0c;股票代码为&#xff1a;“证券代码&#xff1a;301279”&#xff09;今日在深交所上市。 金道科技此次发行2500万股&#xff0c;发行价为31.20元&#xff0c;募资7.8亿…