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()
相关推荐
Mapmost2 小时前
【Mapmost渲染指北】灯光+后处理,一招切出立体感
前端
J船长2 小时前
Kotlin 协程:从入门到深度理解
前端
Hilaku3 小时前
做中后台业务,为什么我不建议你用 Tailwind CSS?
前端·css·代码规范
有意义3 小时前
【面试复盘】前端底层原理与 React 核心机制深度梳理
前端·react.js·面试
二十一_3 小时前
LangChain 教程 05|模型配置:AI 的大脑与推理引擎
前端·langchain
kerli3 小时前
Compose 组件:BoxWithConstraints作用及其原理
android·前端
LovroMance3 小时前
消息总线 + 可插拔的消息插件管理系统
前端
Lee川3 小时前
React Router 实战指南:构建现代化前端路由系统
前端·react.js·架构
薛纪克3 小时前
前端自动化测试的 Spec 模式:用 Kiro Power 实现从需求到脚本的全链路自动化
前端·自动化测试·ai·kiro