electron-vite【实战】自定义标题栏【组件封装】(含异形标题栏,指定区域拖拽,窗口置顶,窗口最小化,窗口最大化,取消最大化,隐藏窗口到托盘等)

效果预览

在这里插入图片描述

技术要点

透明背景

src/main/index.ts 的 new BrowserWindow 中添加

    transparent: true, // 设置窗口背景透明
    frame: false, // 隐藏窗口边框

仅图标和标题部分可拖拽

在这里插入图片描述
仅图标和标题部分添加样式 drag

.drag {
  -webkit-app-region: drag;
}

图标与标题栏的融合

  • 标题栏的背景色采用图标的背景色
bg-[#0baaf5]
  • 标题栏添加顶部的外边距,值为图标的半径
mt-30px

图标的样式如下:

<img :src="icon" alt="logo" class="fixed h-60px top-[0px] drag" />

窗口置顶

在这里插入图片描述

      <Icon
        v-if="ifTop"
        icon="stash:pin-thumbtack-solid"
        :class="iconClassString"
        @click="cancle_top"
      />

      <Icon v-else icon="stash:pin-thumbtack" :class="iconClassString" @click="top" />
const ifTop = ref(false)

// 窗口置顶
function top() {
  window.electron.ipcRenderer.send('top')
  ifTop.value = true
}

function cancle_top() {
  window.electron.ipcRenderer.send('cancle_top')
  ifTop.value = false
}

src/main/index.ts 中

  ipcMain.on('top', () => {
    mainWindow.setAlwaysOnTop(true)
  })

  ipcMain.on('cancle_top', () => {
    mainWindow.setAlwaysOnTop(false)
  })

窗口最小化

在这里插入图片描述

 <Icon icon="qlementine-icons:windows-minimize-16" :class="iconClassString" @click="min" />
function min() {
  window.electron.ipcRenderer.send('min')
}

src/main/index.ts 中

  ipcMain.on('min', () => {
    mainWindow.minimize()
  })

窗口最大化 / 取消最大化

在这里插入图片描述
在这里插入图片描述

      <Icon
        v-if="ifMax"
        icon="qlementine-icons:windows-unmaximize-16"
        :class="iconClassString"
        @click="cancel_max"
      />

      <Icon
        v-else
        icon="qlementine-icons:windows-maximize-16"
        :class="iconClassString"
        @click="max"
      />
const ifMax = ref(false)

function max() {
  window.electron.ipcRenderer.send('max')
}

function cancel_max() {
  window.electron.ipcRenderer.send('cancel_max')
}

window.electron.ipcRenderer.on('unmaximize', () => {
  ifMax.value = false
})

window.electron.ipcRenderer.on('maximize', () => {
  ifMax.value = true
})

src/main/index.ts 中

  ipcMain.on('max', () => {
    mainWindow.maximize()
  })

  ipcMain.on('cancel_max', () => {
    mainWindow.unmaximize()
  })

因将窗口拖拽到屏幕边缘时,会自动触发最大化,调整窗口大小会退出最大化,则需监听窗口最大化和退出最大化事件,由主进程通知渲染进程状态,以便切换图标。[ 不适应于本范例,因本范例隐藏了 frame ]

  // 窗口变为最大化状态
  mainWindow.on('maximize', () => {
    mainWindow.webContents.send('maximize')
  })

  // 窗口从最大化状态退出
  mainWindow.on('unmaximize', () => {
    mainWindow.webContents.send('unmaximize')
  })

隐藏窗口到托盘

在这里插入图片描述

<Icon icon="si:close-fill" :class="iconClassString" @click="hide" />
function hide() {
  window.electron.ipcRenderer.send('hide')
}

src/main/index.ts 中

  ipcMain.on('hide', () => {
    // 使窗口不显示在任务栏中
    mainWindow.setSkipTaskbar(true)
    mainWindow.hide()
  })

代码实现

src/renderer/src/components/TitleBar.vue

<script setup lang="ts">
import icon from '../../../../resources/icon.png'
const props = defineProps({
  title: {
    type: String
  }
})

function hide() {
  window.electron.ipcRenderer.send('hide')
}

const ifTop = ref(false)

// 窗口置顶
function top() {
  window.electron.ipcRenderer.send('top')
  ifTop.value = true
}

function cancle_top() {
  window.electron.ipcRenderer.send('cancle_top')
  ifTop.value = false
}

function min() {
  window.electron.ipcRenderer.send('min')
}

const ifMax = ref(false)

function max() {
  window.electron.ipcRenderer.send('max')
}

function cancel_max() {
  window.electron.ipcRenderer.send('cancel_max')
}

window.electron.ipcRenderer.on('unmaximize', () => {
  ifMax.value = false
})

window.electron.ipcRenderer.on('maximize', () => {
  ifMax.value = true
})

const iconClassString = 'cursor-pointer hover:bg-blue-500 block h-full px-2'
</script>

<template>
  <img :src="icon" alt="logo" class="fixed h-60px top-[0px] drag" />
  <div class="flex items-center bg-[#0baaf5] text-white h-30px mt-30px">
    <div class="drag flex-1 pl-70px font-bold text-white">{{ props.title }}</div>
    <div class="flex h-full">
      <Icon
        v-if="ifTop"
        icon="stash:pin-thumbtack-solid"
        :class="iconClassString"
        @click="cancle_top"
      />

      <Icon v-else icon="stash:pin-thumbtack" :class="iconClassString" @click="top" />

      <Icon icon="qlementine-icons:windows-minimize-16" :class="iconClassString" @click="min" />

      <Icon
        v-if="ifMax"
        icon="qlementine-icons:windows-unmaximize-16"
        :class="iconClassString"
        @click="cancel_max"
      />

      <Icon
        v-else
        icon="qlementine-icons:windows-maximize-16"
        :class="iconClassString"
        @click="max"
      />

      <Icon icon="si:close-fill" :class="iconClassString" @click="hide" />
    </div>
  </div>
</template>

页面使用

<TitleBar title="邀请函" />

src/main/index.ts

import { app, shell, BrowserWindow, ipcMain, Tray, Menu } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'

function createWindow(): void {
  const mainWindow = new BrowserWindow({
    // 自定义图标
    icon: icon,
    // 自定义窗口宽度
    width: 360,
    // 自定义窗口高度
    height: 430,
    //默认隐藏窗口
    show: false,
    // 隐藏窗口标题栏
    titleBarStyle: 'hidden',
    // 隐藏默认菜单
    autoHideMenuBar: true,
    // 不可改变窗口大小
    resizable: false,
    // 不可改变窗口大小
    maximizable: false,

    transparent: true, // 设置窗口背景透明
    frame: false, // 隐藏窗口边框

    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      sandbox: false
    }
  })

  // 托盘
  const tray = new Tray(icon)

  const contextMenu = [
    {
      label: '退出',
      click: function () {
        app.exit()
      }
    }
  ]

  const menu = Menu.buildFromTemplate(contextMenu)

  tray.setToolTip('EC编程俱乐部')

  tray.setContextMenu(menu)

  tray.on('click', () => {
    // 使窗口显示在任务栏中
    mainWindow.setSkipTaskbar(false)
    mainWindow.show()
  })

  // IPC通信
  ipcMain.on('showPage_home', () => {
    // 窗口可调整大小
    mainWindow.setResizable(true)
    mainWindow.setSize(800, 720)
    // 窗口居中
    mainWindow.center()
    // 窗口可最大化
    mainWindow.setMaximizable(true)
  })

  ipcMain.on('top', () => {
    mainWindow.setAlwaysOnTop(true)
  })

  ipcMain.on('cancle_top', () => {
    mainWindow.setAlwaysOnTop(false)
  })

  ipcMain.on('hide', () => {
    // 使窗口不显示在任务栏中
    mainWindow.setSkipTaskbar(true)
    mainWindow.hide()
  })

  ipcMain.on('min', () => {
    mainWindow.minimize()
  })

  ipcMain.on('max', () => {
    mainWindow.maximize()
  })

  ipcMain.on('cancel_max', () => {
    mainWindow.unmaximize()
  })

  mainWindow.on('ready-to-show', () => {
    // 自定义标题
    mainWindow.setTitle('EC编程俱乐部')
    mainWindow.show()
  })

  // 窗口变为最大化状态
  mainWindow.on('maximize', () => {
    mainWindow.webContents.send('maximize')
  })

  // 窗口从最大化状态退出
  mainWindow.on('unmaximize', () => {
    mainWindow.webContents.send('unmaximize')
  })

  mainWindow.webContents.setWindowOpenHandler((details) => {
    shell.openExternal(details.url)
    return { action: 'deny' }
  })

  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
  } else {
    mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
  }
}

app.whenReady().then(() => {
  // Set app user model id for windows
  electronApp.setAppUserModelId('com.electron')

  app.on('browser-window-created', (_, window) => {
    optimizer.watchWindowShortcuts(window)
  })

  createWindow()

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

ipcMain.on('quit', () => {
  app.quit()
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[react] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就行了! ,然后点一下看看.弹出提示了 当然你还可以用ant内置的G…

springboot中Controller内文件上传到本地以及阿里云

上传文件的基本操作 <form action"/upload" method"post" enctype"multipart/form-data"> <h1>登录</h1> 姓名&#xff1a;<input type"text" name"username" required><br> 年龄&#xf…

python 曲线拟合,曲线拟合交点

目录 效果图: 源代码: 效果图: 源代码: import json import os import shutilimport cv2 import numpy as npfrom numpy.polynomial.polynomial import Polynomialdef calculate_distance(x1, y1, x2, y2):return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)def get_new_g…

java 集合对象

Java 基础之集合_java集合继承关系图-CSDN博客 集合可以有序或无序&#xff0c;重复或不能重复&#xff0c;空或不能空。 List<> 集合&#xff0c;是有序&#xff0c;允许重复元素&#xff0c;允许空元素 1、ArrayList<> 非线程安全 2、LInkedList<> 非线…

【零基础学习UDS诊断测试】——0x27测试用例设计

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者,时光不负有心人。 目录 1.概述 2.响应情况 3.测试点解析 4.0x27诊断调查表 5.详细用例展示 1.概述 UDS(统一诊断服务)中的0x27服务,即安全访问服务(Security Access Service),其主要作用…

【BUG记录】Apifox 参数传入 + 号变成空格的 BUG

文章目录 1. 问题描述2. 原因2.1 编码2.2 解码 3. 解决方法 1. 问题描述 之前写了一个接口&#xff0c;用 Apifox 请求&#xff0c;参数传入一个 86 的电话&#xff0c;结果到服务器 就变成空格了。 Java 接收请求的接口&#xff1a; 2. 原因 2.1 编码 进行 URL 请求的…

视频直播点播平台EasyDSS推拉流技术结合无人机推流在道路交通巡检场景中的应用

随着城市化进程的加速&#xff0c;交通网络日益复杂&#xff0c;交通巡检工作面临着前所未有的挑战。传统的巡检方式往往依赖于人工巡查或地面监控设备&#xff0c;但这些方法存在巡检范围有限、效率低下等缺点。 无人机凭借其高空视野、灵活机动、实时监控等优势&#xff0c;…

git仓库的基本概念和流程以及一些基本命令

什么是版本库&#xff1f;版本库又名仓库&#xff0c;英文名repository,你可以简单的理解一个目录&#xff0c;这个目录里面的所有文件都可以被Git管理起来&#xff0c;每个文件的修改&#xff0c;删除&#xff0c;Git都能跟踪&#xff0c;以便任何时刻都可以追踪历史&#xff…

2025erp系统开源免费进销存系统搭建教程/功能介绍/上线即可运营软件平台源码

系统介绍 基于ThinkPHP与LayUI构建的全方位进销存解决方案 本系统集成了采购、销售、零售、多仓库管理、财务管理等核心功能模块&#xff0c;旨在为企业提供一站式进销存管理体验。借助详尽的报表分析和灵活的设置选项&#xff0c;企业可实现精细化管理&#xff0c;提升运营效…

2024技能大赛Vue流程复现

1. 关于版本的控制 vue/cli 5.0.8vscode 最新下载版本 2. 创建vuecli项目 若没有安装vuecli则可以先安装 npm install -g vue/cli # 默认下载最新版本。vue --version vue -V # 查看版本&#xff0c;两个选一 使用vuecli来创建一个新的vue项目&#xff0c;vs code打开…

Qwen2.5-7B-Instruct FastApi部署与调用

Qwen2.5-7B-Instruct FastApi 部署调用 环境准备 本文基础环境如下&#xff1a; ---------------- ubuntu 22.04 python 3.12 cuda 12.1 pytorch 2.3.0 ----------------本文默认学习者已安装好以上 Pytorch(cuda) 环境&#xff0c;如未安装请自行安装。 首先 pip 换源加速下…

【Vue3学习】ref,reactive,toRef,toRefs的功能与用法区别

在 Vue 3 的组合式 API&#xff08;Composition API&#xff09;中&#xff0c;ref、reactive、toRef 和 toRefs 是四个非常重要的工具函数&#xff0c;用于创建和管理响应式数据。 一、ref 用ref()包裹数据,返回的响应式引用对象&#xff0c;包含一个 .value 属性&#xff0…

【面试 - 遇到的问题】Vue 里 router-view 使用 key + 关闭页面后重新打开页面-获取的数据赋值到旧组件问题(钩子执行顺序)

目录 【1】问题描述【2】问题排查前 - 页面渲染、tag 页签渲染 逻辑梳理页面渲染【借用别人的描述】<router-view :key"key" />1. 不设置key 属性2. 设置 key 属性值为 $route.path/page/1 > /page/2/page?id1 > /page?id2, 3. 设置 key 属性值为 $rou…

电商商品详情API接口(item get)数据分析上货

电商商品详情API接口&#xff08;item get&#xff09;在数据分析与商品上货方面发挥着重要作用。以下是对这两个方面的详细探讨&#xff1a; 一、数据分析 数据源获取&#xff1a; 商品详情API接口提供了丰富的数据源&#xff0c;包括商品的标题、价格、库存、描述、图片、用…

【计算机网络】期末考试预习复习|中

作业讲解 转发器、网桥、路由器和网关(4-6) 作为中间设备&#xff0c;转发器、网桥、路由器和网关有何区别&#xff1f; (1) 物理层使用的中间设备叫做转发器(repeater)。 (2) 数据链路层使用的中间设备叫做网桥或桥接器(bridge)。 (3) 网络层使用的中间设备叫做路…

大数据与AI:从分析到预测的跃迁

引言&#xff1a;数据时代的新纪元 从每天的社交分享到企业的运营决策&#xff0c;数据早已成为现代社会不可或缺的资源。我们正置身于一个数据爆炸的时代&#xff0c;数以亿计的信息流实时生成&#xff0c;为人类带来了前所未有的洞察能力。然而&#xff0c;数据的价值并不仅限…

CSDN数据大屏可视化【开源】

项目简介 本次基于版本3 开源 版本3开源地址&#xff1a;https://github.com/nangongchengfeng/CsdnBlogBoard.git 版本1开源地址&#xff1a;https://github.com/nangongchengfeng/CSDash.git 这是一个基于 Python 的 CSDN 博客数据可视化看板项目&#xff0c;通过爬虫采…

产品品牌战略升级!原WorkPlus正式改名为BeeWorks,版本重要更新

尊敬的各位合作伙伴、用户朋友&#xff1a; 感谢大家一直来对恒拓高科的大力支持&#xff01; 为了配合公司战略业务的实施和后续品牌规划的发展&#xff0c;自2024年12月18日起&#xff0c;恒拓高科旗下安全专属的移动数字化平台“WorkPlus”正式更名为“BeeWorks”&#xf…

jvm栈帧中的动态链接

“-Xss”这一名称并没有一个特定的“为什么”来解释其命名&#xff0c;它更多是JVM&#xff08;Java虚拟机&#xff09;配置参数中的一个约定俗成的标识。在JVM中&#xff0c;有多个配置参数用于调整和优化Java应用程序的性能&#xff0c;这些参数通常以一个短横线“-”开头&am…

R语言混合模型回归GBTM群组轨迹模型绘图可视化研究

全文链接&#xff1a;https://tecdat.cn/?p38581 在回归分析的广袤领域中&#xff0c;面对具有多条未知函数线的复杂数据时&#xff0c;传统方法常常捉襟见肘。混合模型作为一种强有力的分析手段应运而生&#xff0c;其在处理此类复杂情境时展现出独特的优势与潜力&#xff08…