electron + js + vue3自定义多开窗口(窗口可最大、最小化也可隐藏和关闭)

 新开一个文件,写入以下代码

const { app, BrowserWindow, ipcMain } = require('electron')
const { createProtocol} = require('vue-cli-plugin-electron-builder/lib')

const newWindow = {
  id: '',
  title: '',
  width: '',
  height: '',
  minWidth: '',
  minHeight: '',
  route: '', // 页面路由URL '/manage?id=123'
  darkTheme:true,
  resizable: true, //是否支持调整窗口大小
  maximize: false, //是否最大化
  minimizable: true, //是否可以最小化
  backgroundColor:'#eee', //窗口背景色
  data: null, //数据
  isMultiWindow: false, //是否支持多开窗口 (如果为false,当窗体存在,再次创建不会新建一个窗体 只focus显示即可,,如果为true,即使窗体存在,也可以新建一个)
  isMainWin: false, //是否主窗口(当为true时会替代当前主窗口)
  parentId: '', //父窗口id  创建父子窗口 -- 子窗口永远显示在父窗口顶部 【父窗口可以操作】
  modal: false, //模态窗口 -- 模态窗口是禁用父窗口的子窗口,创建模态窗口必须设置 parent 和 modal 选项 【父窗口不能操作】
}

/**
 * 窗口配置
 */
class Window {
  constructor() {
    this.parentWin = null
    this.main = null //当前页
    this.group = {} //窗口组
    // this.tray = null; //托盘
  }

  // 窗口配置
  configWindow(windowSize = []) {
    return {
      width: windowSize[0],
      height: windowSize[1],
      minWidth: null,
      minHeight: null,
      backgroundColor: '#ffff',
      autoHideMenuBar: true, //自动隐藏菜单栏
      titleBarStyle:'default',
      resizable: true,
      minimizable: true,
      maximizable: true,
      closable: true, //窗口是否可以关闭
      frame: false,  //创建一个无边框窗口
      show: false,
      webPreferences: {
        contextIsolation: false, //上下文隔离
        nodeIntegration: true, //启用Node集成(是否完整的支持 node)
        webSecurity: false,
        enableRemoteModule: true, //是否启用远程模块(即在渲染进程页面使用remote)
      }
    }
  }

  // 获取窗口
  getWindow(id) {
    return BrowserWindow.fromId(id)
  }

  // 获取全部窗口
  getAllWindows() {
    return BrowserWindow.getAllWindows()
  }


  // 创建窗口
  createWindows(Invalidwin) {

    console.log('------------开始创建窗口--------------')
    let initialWindow = Object.assign({}, newWindow, Invalidwin)
    // 判断窗口是否存在
    for(let i in this.group) {
      if(this.getWindow(Number(i))
      && this.group[i].route === initialWindow.route
      && !this.group[i].isMultiWindow) {
        this.getWindow(Number(i)).focus()
        return
      }
    }

    let opt = this.configWindow([initialWindow.width || 960, initialWindow.height || 720])
    if(initialWindow.parentId) {
      opt.parent = this.getWindow(initialWindow.parentId)
      console.log('parent id', opt.parent.width)

    } else if(this.main) {
      //console.log('是当前页')
    }
    if(typeof initialWindow.modal === 'boolean') {
      opt.modal = initialWindow.modal
    }
    if(typeof initialWindow.resizable === 'boolean') {
      opt.resizable = initialWindow.resizable
    }
    if(initialWindow.minWidth) {
      opt.minWidth = initialWindow.minWidth
    }
    if(initialWindow.minHeight) {
      opt.minHeight = initialWindow.minHeight
    }
    // console.log('opt', opt)
    const win = new BrowserWindow(opt)

    console.log('创建的窗口id:' + win.id)

    this.group[win.id] = {
      route: initialWindow.route,
      mainWin: initialWindow.isMainWin,
    }
    // 是否最大化
    if(initialWindow.maximize && initialWindow.resizable) {
      win.maximize()
    }
    // 是否是当前窗口
    if(initialWindow.isMainWin) {
      if(this.main) {
        delete this.group[this.main.id]
        this.main.close()
      }
      this.main = win
    }
    initialWindow.id = win.id
    win.on('close', () => win.setOpacity(0))
    // win.loadFile('index.html')
    // 打开网址(加载页面)
    /**
     * 开发环境: http://localhost:8080
     * 正式环境: app://./index.html
     */
    let winURL = ''
    if (process.env.WEBPACK_DEV_SERVER_URL) {
      //打开控制台
      let devtools = null
      devtools = new BrowserWindow()
      win.webContents.setDevToolsWebContents(devtools.webContents)
      win.webContents.openDevTools()
      // Load the url of the dev server if in development mode
      winURL = initialWindow.route ? `http://localhost:1919${initialWindow.route}` : 'http://localhost:1919'

    }else {
      createProtocol('app')
      winURL = initialWindow.route ? `app://./index.html#${initialWindow.route}` : 'app://./index.html'
    }
    win.loadURL(winURL)

    win.once('ready-to-show', () => {
      win.show()
    })

    // 钩住窗口消息(-webkit-app-region: drag)
    win.hookWindowMessage(278, function(e) {
      win.setEnabled(false)
      setTimeout(() => {
        win.setEnabled(true)
      }, 150)

      return true
    })
  }

  // 关闭所有窗口
  closeAllWindow() {
    for(let i in this.group) {
      if(this.getWindow(Number(i))) {
        // console.log('所有关闭', i)
        this.getWindow(Number(i)).close()
      } else {
        app.quit()
      }
    }
    // console.log('finally', this.group)
  }

  // 开启监听
  listen() {
    // 关闭
    ipcMain.on('window-closed', (event, winId) => {
      if(winId) {
        // console.log('单个关闭1')
        if(this.group[Number(winId)]) {
          this.getWindow(Number(winId)).close()
          delete this.group[Number(winId)]
        }
      }
      console.log('删除后的', this.group)
    })
    //关闭所有
    ipcMain.on('all-win-closed', (event, winId) => {
      if (winId) {
        // console.log('关闭所有的监听')
        this.closeAllWindow()
      }
    })

    // 隐藏
    ipcMain.on('window-hide', (event, winId) => {
      if (Object.keys(this.group).length === 1) {
        this.closeAllWindow()
      } else {
        if(winId) {
          this.getWindow(Number(winId)).hide()
        }else {
          for(let i in this.group) {
            if (this.group[i]) {
              this.getWindow(Number(i)).hide()
            }
          }
        }
      }


    })
    // 显示
    ipcMain.on('window-show', (event, winId) => {
      if(winId) {
        this.getWindow(Number(winId)).show()
      } else {
        for(let i in this.group) {
          if (this.group[i]) {
            this.getWindow(Number(i)).show()
          }
        }
      }
    })

    // 最小化
    ipcMain.on('window-mini', (event, winId) => {
      // console.log('resiable', this.getWindow(Number(winId)).resizable)
      if(winId) {
        this.getWindow(Number(winId)).minimize()
      } else {
        for(let i in this.group) {
          if(this.group[i]) {
            this.getWindow(Number(i)).minimize()
          }
        }
      }
    })

    // 最大化
    ipcMain.on('window-max', (event, winId) => {
      if(winId) {
        this.getWindow(Number(winId)).maximize()
      } else {
        for(let i in this.group) {
          if(this.group[i]) {
            this.getWindow(Number(i)).maximize()
          }
        }
      }
    })

    // 最大化/还原
    ipcMain.on('window-max-restore', (event, winId) => {
      // console.log('最大化', this.getWindow(Number(winId)))
      if(winId) {
        if(this.getWindow(Number(winId)).isMaximized()) {
          this.getWindow(Number(winId)).restore()
        }else {
          this.getWindow(Number(winId)).maximize()
        }
      }
    })

    // 重新加载
    ipcMain.on('window-reload', (event, winId) => {
      if(winId) {
        this.getWindow(Number(winId)).reload()
      } else {
        for(let i in this.group) {
          if(this.group[i]) {
            this.getWindow(Number(i)).reload()
          }
        }
      }
    })

    // 创建窗口
    ipcMain.on('window-new', (event, initialWindow) => {
      this.createWindows(initialWindow)
    })

  }

}

module.exports = {Window}

如果想在electron的主进程中,也就是background.js中引用:

require('@electron/remote/main').initialize()
const window = new Window()

/**创建首页 */
async function createWindow() {
  window.listen()
  window.createWindows({ route: '/welCome', width: 350, height: 254, isMainWin: false, modal: false })
//此处就是引用了上面文件的创建函数,可自定义各种参数。
//route一定要与你在router文件中所创建的页面路由一致。
}

 如果需要在.vue文件中对窗口进行操作,则需要重新创建一个文件,去做electron和vue之间的‘中间人’。主进程中会监听每个函数的调用,从而对窗口进行操作,而渲染进程只需要调用以下函数即可,例如,我在index.vue文件中想调用关闭窗口,如下

//关闭窗口
const closed = () => {
  // console.log(currentWindow.closable, currentWindow.id)
  if (winRoute.value === '/mainBoardmonitoring') {
    winClose.value.warningVisibled = true
  }else if (winRoute.value === '/deviceHome') {
    windowHide(2)//传入你所想要关闭的窗口id
  } else{

    if (currentWindow.closable) {
      return windowClose(currentWindow.id)
    }
    winClose.value.warningVisibled = true
    mainWindow.value = false
  }
}

这是窗口的中间人文件pluginUtil.js

import { ipcRenderer, app } from'electron'
export function windowCreate(args) {
  ipcRenderer.send('window-new', args)
}
/**
 * 关闭窗口
 */
export function windowClose(id) {
  ipcRenderer.send('window-closed', id)
}

// 显示窗口
export function windowShow(id) {
  ipcRenderer.send('window-show', id)
}
//隐藏窗口
export function windowHide(id) {
  ipcRenderer.send('window-hide', id)
}
//关闭所有窗口
export async function windowCloseAll() {
  ipcRenderer.send('all-win-closed', -1)
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现自定义菜单栏,可以使用 Electron 提供的 Menu 模块。以下是一个使用 Electron Vite Vue 实现自定义菜单栏的步骤: 1. 在 Vue 组件中引入 Electron 的 remote 模块,用于获取主进程的 Menu 对象。 ```javascript import { remote } from 'electron' const Menu = remote.Menu ``` 2. 在 Vue 组件的生命周期钩子函数中创建菜单项,可以使用 Menu.buildFromTemplate 方法创建菜单项的数组。 ```javascript created() { const template = [ { label: '文件', submenu: [ { label: '新建', accelerator: 'CmdOrCtrl+N', click: this.handleNew }, { label: '打开', accelerator: 'CmdOrCtrl+O', click: this.handleOpen }, { type: 'separator' }, { label: '保存', accelerator: 'CmdOrCtrl+S', click: this.handleSave }, { label: '另存为', accelerator: 'Shift+CmdOrCtrl+S', click: this.handleSaveAs }, { type: 'separator' }, { label: '退出', accelerator: 'CmdOrCtrl+Q', click: this.handleQuit } ] }, { label: '编辑', submenu: [ { label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { label: '重做', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, { label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' }, { label: '复制', accelerator: 'CmdOrCtrl+C', role: 'copy' }, { label: '粘贴', accelerator: 'CmdOrCtrl+V', role: 'paste' }, { label: '全选', accelerator: 'CmdOrCtrl+A', role: 'selectAll' } ] } ] const menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu) } ``` 3. 在 Vue 组件中实现菜单项的点击事件。 ```javascript methods: { handleNew() { // 新建文件 }, handleOpen() { // 打开文件 }, handleSave() { // 保存文件 }, handleSaveAs() { // 另存为文件 }, handleQuit() { // 退出应用程序 } } ``` 这样就可以在 Electron Vite Vue 应用程序中实现自定义菜单栏了。需要注意的是,菜单项的点击事件可以调用主进程中的方法,例如使用 ipcRenderer 发送消息给主进程,让主进程执行相应的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值