electron系列9:调用原生能力,剪贴板、通知、开机自启

一、功能全景图

javascript 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Electron 原生能力调用架构                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         Electron App                                 │   │
│  ├─────────────────────────┬─────────────────────────┬─────────────────┤   │
│  │      剪贴板 Clipboard    │      通知 Notification    │    开机自启     │   │
│  ├─────────────────────────┼─────────────────────────┼─────────────────┤   │
│  │ • 读写纯文本             │ • 系统通知弹窗           │ • 登录项设置     │   │
│  │ • 读写 HTML              │ • 带图标通知             │ • 隐藏启动       │   │
│  │ • 读写图片               │ • 带操作按钮             │ • 多平台兼容     │   │
│  │ • 读写 RTF               │ • 点击回调               │ • 状态查询       │   │
│  │ • 监听剪贴板变化          │ • 关闭回调               │ • 自定义参数     │   │
│  └─────────────────────────┴─────────────────────────┴─────────────────┘   │
│                                    │                                        │
│                                    ▼                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        操作系统底层 API                               │   │
│  │            Windows │ macOS │ Linux                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

二、剪贴板操作

2.1 基础使用

Electron 的 clipboard 模块提供剪贴板读写能力,可在主进程和渲染进程中使用。

javascript 复制代码
// main.ts 或 preload.ts
import { clipboard, nativeImage } from 'electron'

// ==================== 文本操作 ====================

// 写入纯文本
clipboard.writeText('Hello Electron!')

// 读取纯文本
const text = clipboard.readText()
console.log('剪贴板内容:', text)

// ==================== HTML 操作 ====================

// 写入 HTML
clipboard.writeHTML('<h1>标题</h1><p>段落内容</p>')

// 读取 HTML
const html = clipboard.readHTML()
console.log('HTML 内容:', html)

// ==================== 图片操作 ====================

// 写入图片
const image = nativeImage.createFromPath('/path/to/image.png')
clipboard.writeImage(image)

// 读取图片
const clipboardImage = clipboard.readImage()
if (!clipboardImage.isEmpty()) {
  const imagePath = clipboardImage.toDataURL()
  // 使用图片...
}

// ==================== RTF 富文本 ====================

// 写入 RTF
clipboard.writeRTF('{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard This is some {\\b bold} text.\\par}')

// 读取 RTF
const rtf = clipboard.readRTF()

// ==================== 多种格式同时写入 ====================

// 同时写入多种格式
clipboard.write({
  text: 'Plain Text',
  html: '<strong>HTML</strong>',
  rtf: '{\\rtf1\\ansi\\b Bold\\b0}',
  bookmark: 'Bookmark Name',
  image: nativeImage.createFromPath('/path/to/icon.png')
})

// ==================== 读取可用格式 ====================

// 获取剪贴板中可用的格式列表
const availableFormats = clipboard.availableFormats()
console.log('可用格式:', availableFormats)

// 检查是否有特定格式
const hasText = clipboard.has('text/plain')
const hasImage = clipboard.has('image/png')

2.2 监听剪贴板变化

javascript 复制代码
// main.ts
import { clipboard, BrowserWindow } from 'electron'

class ClipboardMonitor {
  private lastText: string = ''
  private intervalId: NodeJS.Timeout | null = null
  private window: BrowserWindow | null = null
  
  constructor(window: BrowserWindow) {
    this.window = window
  }
  
  // 开始监听剪贴板变化
  startMonitoring(interval: number = 500) {
    this.lastText = clipboard.readText()
    
    this.intervalId = setInterval(() => {
      const currentText = clipboard.readText()
      
      if (currentText !== this.lastText) {
        this.lastText = currentText
        this.onClipboardChange(currentText)
      }
    }, interval)
  }
  
  // 剪贴板变化回调
  private onClipboardChange(content: string) {
    console.log('剪贴板内容已变化:', content)
    
    // 通知渲染进程
    this.window?.webContents.send('clipboard-changed', {
      content,
      timestamp: Date.now()
    })
  }
  
  // 停止监听
  stopMonitoring() {
    if (this.intervalId) {
      clearInterval(this.intervalId)
      this.intervalId = null
    }
  }
}

// 使用示例
app.whenReady().then(() => {
  const win = new BrowserWindow({ width: 800, height: 600 })
  win.loadFile('index.html')
  
  const monitor = new ClipboardMonitor(win)
  monitor.startMonitoring()
})

2.3 封装剪贴板工具类

javascript 复制代码
// utils/clipboard.ts
import { clipboard, nativeImage } from 'electron'

export class ClipboardManager {
  
  /**
   * 复制文本
   */
  static copyText(text: string): void {
    clipboard.writeText(text)
    console.log(`已复制文本: ${text.substring(0, 50)}`)
  }
  
  /**
   * 复制 HTML
   */
  static copyHtml(html: string, text?: string): void {
    clipboard.write({
      html: html,
      text: text || this.stripHtml(html)
    })
  }
  
  /**
   * 复制图片
   */
  static copyImage(imagePath: string): boolean {
    try {
      const image = nativeImage.createFromPath(imagePath)
      if (!image.isEmpty()) {
        clipboard.writeImage(image)
        return true
      }
      return false
    } catch (error) {
      console.error('复制图片失败:', error)
      return false
    }
  }
  
  /**
   * 复制多格式内容(同时支持文本和HTML)
   */
  static copyRichContent(plainText: string, htmlText: string): void {
    clipboard.write({
      text: plainText,
      html: htmlText
    })
  }
  
  /**
   * 获取剪贴板文本
   */
  static pasteText(): string {
    return clipboard.readText()
  }
  
  /**
   * 获取剪贴板图片(如果存在)
   */
  static pasteImage(): string | null {
    const image = clipboard.readImage()
    if (!image.isEmpty()) {
      return image.toDataURL()
    }
    return null
  }
  
  /**
   * 清空剪贴板
   */
  static clear(): void {
    clipboard.clear()
  }
  
  /**
   * HTML 转纯文本
   */
  private static stripHtml(html: string): string {
    return html.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ')
  }
}

三、通知功能

3.1 基础通知

Electron 的 Notification 模块用于发送系统通知。

javascript 复制代码
// main.ts
import { Notification } from 'electron'

// ==================== 基础通知 ====================

function sendBasicNotification() {
  const notification = new Notification({
    title: '任务完成',
    body: '文件导出已完成,点击查看',
    silent: false
  })
  
  notification.show()
}

// ==================== 带图标的通知 ====================

function sendNotificationWithIcon() {
  const notification = new Notification({
    title: '新消息',
    subtitle: '来自张三',  // macOS only
    body: '您收到一条新消息,请及时查看',
    icon: '/path/to/icon.png',  // 图标路径
    silent: false,
    sound: 'default'  // macOS only
  })
  
  notification.show()
}

// ==================== 带操作按钮的通知 (macOS) ====================

function sendNotificationWithActions() {
  const notification = new Notification({
    title: '会议提醒',
    subtitle: '团队会议',
    body: '10分钟后开始,是否参加?',
    actions: [
      { text: '参加', type: 'button' },
      { text: '拒绝', type: 'button' }
    ],
    hasReply: true,
    replyPlaceholder: '输入回复内容'
  })
  
  // 监听按钮点击
  notification.on('action', (event, index) => {
    if (index === 0) {
      console.log('用户点击了参加')
    } else if (index === 1) {
      console.log('用户点击了拒绝')
    }
  })
  
  // 监听回复
  notification.on('reply', (event, reply) => {
    console.log('用户回复:', reply)
  })
  
  notification.show()
}

// ==================== 带超时类型的通知 ====================

function sendPersistentNotification() {
  const notification = new Notification({
    title: '系统更新',
    body: '更新将在重启后生效',
    timeoutType: 'never',  // 'default' | 'never' - Windows/Linux
    urgency: 'critical'     // 'normal' | 'critical' | 'low' - Linux
  })
  
  notification.show()
}

3.2 通知事件监听

TypeScript 复制代码
// main.ts
import { Notification } from 'electron'

function createNotificationWithEvents() {
  const notification = new Notification({
    title: '下载完成',
    body: '文件已下载到桌面',
    silent: false
  })
  
  // 点击通知
  notification.on('click', (event) => {
    console.log('通知被点击')
    // 打开文件或聚焦应用
    BrowserWindow.getFocusedWindow()?.show()
  })
  
  // 关闭通知
  notification.on('close', (event) => {
    console.log('通知已关闭')
  })
  
  // 显示通知
  notification.show()
  
  return notification
}

3.3 通知管理器封装

TypeScript 复制代码
// utils/notification.ts
import { Notification } from 'electron'

interface NotificationOptions {
  title: string
  body: string
  subtitle?: string
  icon?: string
  silent?: boolean
  actions?: Array<{ text: string; type: 'button' }>
  onClick?: () => void
  onClose?: () => void
}

export class NotificationManager {
  private static instance: NotificationManager
  private notifications: Map<string, Notification> = new Map()
  
  static getInstance(): NotificationManager {
    if (!NotificationManager.instance) {
      NotificationManager.instance = new NotificationManager()
    }
    return NotificationManager.instance
  }
  
  /**
   * 发送普通通知
   */
  send(options: NotificationOptions): string {
    const id = Date.now().toString()
    
    const notification = new Notification({
      title: options.title,
      subtitle: options.subtitle,
      body: options.body,
      icon: options.icon,
      silent: options.silent ?? false,
      actions: options.actions
    })
    
    // 绑定事件
    if (options.onClick) {
      notification.on('click', options.onClick)
    }
    if (options.onClose) {
      notification.on('close', options.onClose)
    }
    
    notification.show()
    this.notifications.set(id, notification)
    
    // 自动清理
    setTimeout(() => {
      this.notifications.delete(id)
    }, 5000)
    
    return id
  }
  
  /**
   * 发送成功通知
   */
  success(title: string, body: string, onClick?: () => void): void {
    this.send({
      title: `✅ ${title}`,
      body,
      icon: '/path/to/success-icon.png',
      onClick
    })
  }
  
  /**
   * 发送错误通知
   */
  error(title: string, body: string, onClick?: () => void): void {
    this.send({
      title: `❌ ${title}`,
      body,
      icon: '/path/to/error-icon.png',
      onClick
    })
  }
  
  /**
   * 发送警告通知
   */
  warning(title: string, body: string, onClick?: () => void): void {
    this.send({
      title: `⚠️ ${title}`,
      body,
      icon: '/path/to/warning-icon.png',
      onClick
    })
  }
  
  /**
   * 发送信息通知
   */
  info(title: string, body: string, onClick?: () => void): void {
    this.send({
      title: `ℹ️ ${title}`,
      body,
      onClick
    })
  }
}

// 使用示例
const notifier = NotificationManager.getInstance()
notifier.success('操作成功', '文件已保存')
notifier.error('操作失败', '请检查网络连接')

四、开机自启动

4.1 使用 Electron 原生 API

Electron 提供了 app.setLoginItemSettings 方法来设置开机自启动。

javascript 复制代码
// main.ts
import { app } from 'electron'

// ==================== 基础设置 ====================

// 启用开机自启动
function enableAutoStart() {
  app.setLoginItemSettings({
    openAtLogin: true
  })
  console.log('开机自启动已启用')
}

// 禁用开机自启动
function disableAutoStart() {
  app.setLoginItemSettings({
    openAtLogin: false
  })
  console.log('开机自启动已禁用')
}

// 查询当前状态
function getAutoStartStatus() {
  const settings = app.getLoginItemSettings()
  console.log('开机自启动状态:', settings.openAtLogin)
  console.log('是否隐藏启动:', settings.wasOpenedAsHidden)
  return settings
}

// ==================== 隐藏窗口启动 ====================

// 设置开机隐藏启动(启动后窗口不显示)
function enableAutoStartHidden() {
  app.setLoginItemSettings({
    openAtLogin: true,
    openAsHidden: true  // macOS only,启动时隐藏窗口
  })
}

// ==================== 自定义参数启动 ====================

// 带参数启动,用于区分启动方式
function enableAutoStartWithArgs() {
  app.setLoginItemSettings({
    openAtLogin: true,
    args: ['--openAsHidden', '--silent-start']  // 自定义启动参数
  })
}

// 在主进程中检测启动参数
function checkStartupArgs() {
  const args = process.argv
  
  if (args.includes('--openAsHidden')) {
    console.log('应用通过开机自启动启动,将隐藏窗口')
    // 不显示主窗口
    return true
  }
  
  console.log('应用通过用户手动启动')
  return false
}

// ==================== Windows 注册表名称自定义 ====================

// Windows 下可自定义注册表项名称
function enableAutoStartWithCustomName() {
  app.setLoginItemSettings({
    openAtLogin: true,
    name: 'MyAppName',  // 自定义注册表键名
    enabled: true
  })
}

4.2 完整开机自启动管理类

javascript 复制代码
// utils/autoLaunch.ts
import { app } from 'electron'

export interface AutoStartConfig {
  enabled: boolean
  hidden?: boolean
  args?: string[]
}

export class AutoLaunchManager {
  private static instance: AutoLaunchManager
  
  static getInstance(): AutoLaunchManager {
    if (!AutoLaunchManager.instance) {
      AutoLaunchManager.instance = new AutoLaunchManager()
    }
    return AutoLaunchManager.instance
  }
  
  /**
   * 启用开机自启动
   */
  enable(hidden: boolean = false, customArgs?: string[]): void {
    try {
      app.setLoginItemSettings({
        openAtLogin: true,
        openAsHidden: hidden,
        args: customArgs || this.getDefaultArgs(hidden)
      })
      
      console.log('✅ 开机自启动已启用', hidden ? '(隐藏启动)' : '')
    } catch (error) {
      console.error('启用开机自启动失败:', error)
    }
  }
  
  /**
   * 禁用开机自启动
   */
  disable(): void {
    try {
      app.setLoginItemSettings({
        openAtLogin: false
      })
      console.log('❌ 开机自启动已禁用')
    } catch (error) {
      console.error('禁用开机自启动失败:', error)
    }
  }
  
  /**
   * 获取当前状态
   */
  getStatus(): {
    enabled: boolean
    wasOpenedAsHidden: boolean
    isHiddenStart: boolean
  } {
    const settings = app.getLoginItemSettings()
    const args = process.argv
    
    return {
      enabled: settings.openAtLogin,
      wasOpenedAsHidden: settings.wasOpenedAsHidden,
      isHiddenStart: args.includes('--openAsHidden')
    }
  }
  
  /**
   * 切换状态
   */
  toggle(hidden: boolean = false): boolean {
    const current = this.getStatus()
    
    if (current.enabled) {
      this.disable()
      return false
    } else {
      this.enable(hidden)
      return true
    }
  }
  
  /**
   * 检查是否通过开机自启动启动
   */
  isLaunchedByAutoStart(): boolean {
    const settings = app.getLoginItemSettings()
    const args = process.argv
    
    // macOS 使用 wasOpenedAsHidden
    if (process.platform === 'darwin') {
      return settings.wasOpenedAsHidden
    }
    
    // Windows/Linux 使用自定义参数
    return args.includes('--openAsHidden')
  }
  
  /**
   * 处理开机启动时的行为
   */
  handleAutoStartLaunch(mainWindow: Electron.BrowserWindow | null): void {
    if (this.isLaunchedByAutoStart()) {
      console.log('应用通过开机自启动启动,窗口将保持隐藏')
      // 可以选择不显示窗口,或显示到系统托盘
      // mainWindow?.hide()
    } else {
      console.log('应用通过用户手动启动,正常显示窗口')
      mainWindow?.show()
    }
  }
  
  private getDefaultArgs(hidden: boolean): string[] {
    return hidden ? ['--openAsHidden'] : []
  }
}

4.3 使用 auto-launch 第三方库

除了原生 API,也可以使用 auto-launch 库。

javascript 复制代码
npm install auto-launch --save
javascript 复制代码
// 使用 auto-launch 库
import AutoLaunch from 'auto-launch'

const appLauncher = new AutoLaunch({
  name: 'MyElectronApp',
  path: app.getPath('exe'),  // 可选,指定可执行文件路径
  isHidden: false            // 是否隐藏启动
})

// 启用
async function enableAutoLaunch() {
  try {
    await appLauncher.enable()
    console.log('开机自启动已启用')
  } catch (err) {
    console.error('启用失败:', err)
  }
}

// 禁用
async function disableAutoLaunch() {
  try {
    await appLauncher.disable()
    console.log('开机自启动已禁用')
  } catch (err) {
    console.error('禁用失败:', err)
  }
}

// 检查状态
async function isAutoLaunchEnabled() {
  try {
    const enabled = await appLauncher.isEnabled()
    console.log('自启动状态:', enabled)
    return enabled
  } catch (err) {
    console.error('检查失败:', err)
    return false
  }
}

4.4 Windows Store 应用特殊处理

对于 Windows Store 应用,需要使用专门的模块。

javascript 复制代码
npm install electron-winstore-auto-launch
TypeScript 复制代码
// 需要在 appxmanifest.xml 中配置 Extension
// <desktop:Extension Category="windows.startupTask" ...>

import { WindowsStoreAutoLaunch } from 'electron-winstore-auto-launch'

// 启用
await WindowsStoreAutoLaunch.enable()

// 禁用
await WindowsStoreAutoLaunch.disable()

// 获取状态
const status = await WindowsStoreAutoLaunch.getStatus()
// 0: disabled, 1: disabledByUser, 2: enabled

五、渲染进程调用封装

5.1 Preload 脚本

javascript 复制代码
// preload.ts
import { contextBridge, ipcRenderer, clipboard } from 'electron'

// 暴露安全的 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  // ==================== 剪贴板 ====================
  clipboard: {
    writeText: (text: string) => clipboard.writeText(text),
    readText: () => clipboard.readText(),
    writeHtml: (html: string) => clipboard.writeHTML(html),
    readHtml: () => clipboard.readHTML(),
    copyImage: (imagePath: string) => {
      const { nativeImage } = require('electron')
      const image = nativeImage.createFromPath(imagePath)
      clipboard.writeImage(image)
      return !image.isEmpty()
    }
  },
  
  // ==================== 通知 ====================
  notification: {
    send: (options: { title: string; body: string; icon?: string }) => {
      ipcRenderer.send('send-notification', options)
    }
  },
  
  // ==================== 开机自启 ====================
  autoLaunch: {
    enable: (hidden?: boolean) => ipcRenderer.invoke('auto-launch-enable', hidden),
    disable: () => ipcRenderer.invoke('auto-launch-disable'),
    getStatus: () => ipcRenderer.invoke('auto-launch-status'),
    toggle: (hidden?: boolean) => ipcRenderer.invoke('auto-launch-toggle', hidden)
  },
  
  // ==================== 事件监听 ====================
  onClipboardChange: (callback: (content: string) => void) => {
    ipcRenderer.on('clipboard-changed', (_, data) => callback(data))
  }
})

5.2 主进程 IPC 处理

javascript 复制代码
// main.ts
import { ipcMain, Notification, app, BrowserWindow } from 'electron'
import { AutoLaunchManager } from './utils/autoLaunch'
import { NotificationManager } from './utils/notification'

// 通知 IPC
ipcMain.on('send-notification', (event, options) => {
  const notifier = NotificationManager.getInstance()
  notifier.send(options)
})

// 开机自启 IPC
ipcMain.handle('auto-launch-enable', async (event, hidden) => {
  const manager = AutoLaunchManager.getInstance()
  manager.enable(hidden)
  return manager.getStatus()
})

ipcMain.handle('auto-launch-disable', async () => {
  const manager = AutoLaunchManager.getInstance()
  manager.disable()
  return manager.getStatus()
})

ipcMain.handle('auto-launch-status', async () => {
  const manager = AutoLaunchManager.getInstance()
  return manager.getStatus()
})

ipcMain.handle('auto-launch-toggle', async (event, hidden) => {
  const manager = AutoLaunchManager.getInstance()
  const newStatus = manager.toggle(hidden)
  return newStatus
})

5.3 Vue3 组合式函数

javascript 复制代码
// composables/useElectron.ts
import { ref } from 'vue'

declare global {
  interface Window {
    electronAPI: {
      clipboard: {
        writeText: (text: string) => void
        readText: () => string
        writeHtml: (html: string) => void
        readHtml: () => string
        copyImage: (path: string) => boolean
      }
      notification: {
        send: (options: { title: string; body: string; icon?: string }) => void
      }
      autoLaunch: {
        enable: (hidden?: boolean) => Promise<{ enabled: boolean }>
        disable: () => Promise<{ enabled: boolean }>
        getStatus: () => Promise<{ enabled: boolean; isHiddenStart: boolean }>
        toggle: (hidden?: boolean) => Promise<boolean>
      }
      onClipboardChange: (callback: (content: string) => void) => void
    }
  }
}

export function useElectron() {
  const isElectron = ref(!!window.electronAPI)
  
  /**
   * 复制文本
   */
  const copyToClipboard = (text: string): boolean => {
    if (!isElectron.value) {
      // 降级到 Web API
      navigator.clipboard.writeText(text)
      return true
    }
    window.electronAPI.clipboard.writeText(text)
    return true
  }
  
  /**
   * 粘贴文本
   */
  const pasteFromClipboard = (): string => {
    if (!isElectron.value) {
      return ''
    }
    return window.electronAPI.clipboard.readText()
  }
  
  /**
   * 发送通知
   */
  const sendNotification = (title: string, body: string, icon?: string) => {
    if (!isElectron.value) {
      // 降级到 Web Notification
      if ('Notification' in window) {
        new Notification(title, { body, icon })
      }
      return
    }
    window.electronAPI.notification.send({ title, body, icon })
  }
  
  /**
   * 启用开机自启
   */
  const enableAutoStart = async (hidden: boolean = false) => {
    if (!isElectron.value) return false
    const result = await window.electronAPI.autoLaunch.enable(hidden)
    return result.enabled
  }
  
  /**
   * 禁用开机自启
   */
  const disableAutoStart = async () => {
    if (!isElectron.value) return false
    const result = await window.electronAPI.autoLaunch.disable()
    return !result.enabled
  }
  
  /**
   * 获取开机自启状态
   */
  const getAutoStartStatus = async () => {
    if (!isElectron.value) return { enabled: false, isHiddenStart: false }
    return await window.electronAPI.autoLaunch.getStatus()
  }
  
  /**
   * 切换开机自启
   */
  const toggleAutoStart = async (hidden: boolean = false) => {
    if (!isElectron.value) return false
    return await window.electronAPI.autoLaunch.toggle(hidden)
  }
  
  /**
   * 监听剪贴板变化
   */
  const onClipboardChange = (callback: (content: string) => void) => {
    if (!isElectron.value) return
    window.electronAPI.onClipboardChange(callback)
  }
  
  return {
    isElectron,
    copyToClipboard,
    pasteFromClipboard,
    sendNotification,
    enableAutoStart,
    disableAutoStart,
    getAutoStartStatus,
    toggleAutoStart,
    onClipboardChange
  }
}

5.4 Vue3 组件使用示例

javascript 复制代码
<template>
  <div class="electron-demo">
    <h2>Electron 原生能力演示</h2>
    
    <!-- 剪贴板 -->
    <div class="section">
      <h3>📋 剪贴板</h3>
      <textarea v-model="clipboardText" placeholder="输入要复制的内容"></textarea>
      <button @click="handleCopy">复制到剪贴板</button>
      <button @click="handlePaste">粘贴</button>
      <div v-if="pastedText" class="result">
        粘贴内容: {{ pastedText }}
      </div>
    </div>
    
    <!-- 通知 -->
    <div class="section">
      <h3>🔔 系统通知</h3>
      <input v-model="notificationTitle" placeholder="通知标题" />
      <input v-model="notificationBody" placeholder="通知内容" />
      <button @click="handleNotify">发送通知</button>
    </div>
    
    <!-- 开机自启 -->
    <div class="section">
      <h3>🚀 开机自启动</h3>
      <div class="status">
        当前状态: 
        <span :class="autoStartStatus ? 'enabled' : 'disabled'">
          {{ autoStartStatus ? '已启用' : '已禁用' }}
        </span>
      </div>
      <button @click="handleToggleAutoStart">
        {{ autoStartStatus ? '禁用' : '启用' }}
      </button>
      <label v-if="autoStartStatus">
        <input type="checkbox" v-model="autoStartHidden" />
        隐藏窗口启动
      </label>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useElectron } from '@/composables/useElectron'

const {
  copyToClipboard,
  pasteFromClipboard,
  sendNotification,
  getAutoStartStatus,
  toggleAutoStart,
  onClipboardChange
} = useElectron()

// 剪贴板
const clipboardText = ref('')
const pastedText = ref('')

const handleCopy = () => {
  copyToClipboard(clipboardText.value)
  sendNotification('复制成功', '内容已复制到剪贴板')
}

const handlePaste = () => {
  pastedText.value = pasteFromClipboard()
}

// 通知
const notificationTitle = ref('系统提示')
const notificationBody = ref('这是一条测试通知')

const handleNotify = () => {
  sendNotification(notificationTitle.value, notificationBody.value)
}

// 开机自启
const autoStartStatus = ref(false)
const autoStartHidden = ref(false)

const loadAutoStartStatus = async () => {
  const status = await getAutoStartStatus()
  autoStartStatus.value = status.enabled
}

const handleToggleAutoStart = async () => {
  const newStatus = await toggleAutoStart(autoStartHidden.value)
  autoStartStatus.value = newStatus
}

// 监听剪贴板变化
onMounted(() => {
  loadAutoStartStatus()
  
  onClipboardChange((content) => {
    console.log('剪贴板内容变化:', content)
    sendNotification('剪贴板更新', '检测到新的剪贴板内容')
  })
})
</script>

<style scoped>
.electron-demo {
  padding: 20px;
  font-family: sans-serif;
}

.section {
  margin-bottom: 24px;
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

button {
  margin: 8px 8px 8px 0;
  padding: 8px 16px;
  background: #42b883;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.enabled {
  color: #42b883;
}

.disabled {
  color: #ff6b6b;
}
</style>

六、注意事项与最佳实践

6.1 跨平台差异

功能 Windows macOS Linux
openAsHidden 不支持 支持 不支持
sound 属性 部分支持 支持 部分支持
actions 按钮 不支持 支持 不支持
urgency 级别 不支持 不支持 支持

6.2 权限说明

javascript 复制代码
// macOS 通知需要请求权限
if (process.platform === 'darwin') {
  app.requestSingleInstanceLock()
  // 通知权限在首次使用时自动请求
}

// Windows 开机自启可能需要管理员权限
// 建议在安装包中配置

6.3 内存管理

javascript 复制代码
// 及时清理不需要的通知
const notification = new Notification({...})
notification.show()

// 组件销毁时移除事件监听
notification.removeAllListeners()
相关推荐
卸任5 小时前
Vibe coding工具使用心得
前端·ai编程
brien96 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance6 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M6 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写6 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李6 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易6 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding7 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马7 小时前
前沿视界
前端
kyriewen7 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员