年轻人的第一个 GO 桌面应用:用 Wails 做个学习搭子计时器

🎯 痛点场景:为什么需要桌面应用开发技能?

你是否遇到这些问题?

  • 学了Go语言,却只会写命令行工具?
  • 想学桌面开发,却觉得C++/C#太复杂?
  • 面试官对你的“烂大街”项目不感兴趣?

你能学到什么?

  • ✅️ Wails 基础使用
  • ✅️ 实时事件通信
  • ✅️ 系统托盘集成
  • ✅️ 多线程编程
  • ✅️ 附完整源码

🛠️ 环境准备

步骤1:安装Go语言


# 官网下载安装包:https://golang.org/dl/ go version # 验证安装

步骤2:安装Wails框架


go install github.com/wailsapp/wails/v2/cmd/wails@latest wails version # 验证安装

步骤3:创建项目骨架


wails init -n Punktime cd Punktime

🚀 核心功能开发

设计 UI (前端)

  • 使用 HTML/CSS/JavaScript实现:

    • 时间/倒计时主界面
    • 倒计时设置遮罩层
  • 示例代码


<!-- ============================================================================ * 主界面结构 * 应用程序的主要显示区域 * ============================================================================ --> <!-- 主容器:显示倒计时/时间 --> <div class="container"> <div class="countdown" id="countdown"></div> </div> <!-- ============================================================================ * 倒计时输入框模态对话框 * 用于设置倒计时时长的弹出窗口 * ============================================================================ --> <!-- 倒计时输入框遮罩层 --> <div id="countdownInputOverlay" class="countdown-input-overlay" style="display: none" > <div class="countdown-input-container"> <div class="countdown-input-title"></div> <div> <!-- 分钟输入框 --> <input type="number" id="minutesInput" class="countdown-input" min="0" max="59" value="25" placeholder="分" /> <span style="color: lightgreen">:</span> <!-- 秒数输入框 --> <input type="number" id="secondsInput" class="countdown-input" min="0" max="59" value="0" placeholder="秒" /> </div> <div class="countdown-input-buttons"> <!-- 确认按钮 --> <button id="confirmCountdown" class="countdown-input-button"> 确定 </button> <!-- 取消按钮 --> <button id="cancelCountdown" class="countdown-input-button"> 取消 </button> </div> </div> </div>

实现计时逻辑(Go后端)

  • 核心代码

/** * @description: 计时器更新循环,每秒执行一次的时间/倒计时更新逻辑 * 函数作为计时器管理器的核心循环,负责: * 1. 每秒更新计时器状态和显示内容 * 2. 根据当前显示模式(倒计时/时间)执行不同的更新逻辑 * 3. 处理倒计时结束事件和状态转换 * 4. 通过Wails事件系统向前端发送更新数据 * * 倒计时模式逻辑: * - 运行中:计算剩余时间,倒计时结束时触发timerEnd事件 * - 暂停中:显示暂停时的剩余时间 * - 未开始:显示设置的倒计时总时长 * * 时间模式逻辑: * - 显示当前系统时间(MM:SS格式) * * @example * // 每秒自动执行,无需手动调用 * // 倒计时模式:显示"25:00" → "24:59" → ... → "00:00"(触发结束事件) * // 时间模式:显示当前时间如"14:30" * * @see runtime.EventsEmit 发送事件到前端 * @see time.Since 计算时间间隔 * * @note 使用互斥锁确保线程安全,避免并发访问计时器状态 * @note 倒计时结束时自动重置状态并发送结束事件 * @note 时间格式统一为两位数(如"05:09"而非"5:9") */ func (tm *TimerManager) updateLoop() { for range tm.ticker.C { tm.mu.Lock() if tm.isTimerRunning { tm.timerElapsed = time.Since(tm.timerStartTime) } switch tm.displayMode { case "countdown": if tm.isTimerRunning { remaining := tm.countdownDuration - tm.timerElapsed if remaining <= 0 { remaining = 0 tm.isTimerRunning = true tm.timerElapsed = tm.countdownDuration tm.isPaused = false runtime.EventsEmit(tm.ctx, "timerEnd") timerText := "00:00" runtime.EventsEmit(tm.ctx, "timerUpdate", timerText) } else { minutes := int(remaining.Minutes()) seconds := int(remaining.Seconds()) % 60 timerText := fmt.Sprintf("%02d:%02d", minutes, seconds) runtime.EventsEmit(tm.ctx, "timerUpdate", timerText) } } else { if tm.isPaused { remaining := tm.countdownDuration - tm.timerElapsed if remaining < 0 { remaining = 0 } minutes := int(remaining.Minutes()) seconds := int(remaining.Seconds()) % 60 timerText := fmt.Sprintf("%02d:%02d", minutes, seconds) runtime.EventsEmit(tm.ctx, "timerUpdate", timerText) } else { minutes := int(tm.countdownDuration.Minutes()) seconds := int(tm.countdownDuration.Seconds()) % 60 timerText := fmt.Sprintf("%02d:%02d", minutes, seconds) runtime.EventsEmit(tm.ctx, "timerUpdate", timerText) } } case "time": currentTime := time.Now().Format("15:04") runtime.EventsEmit(tm.ctx, "timeUpdate", currentTime) } tm.mu.Unlock() } }

  • 前后端通信
    • 前端负责监听更新事件并渲染
    • 后端负责触发更新事件并发送数据

// 监听倒计时更新事件 window.runtime.EventsOn("timerUpdate", (data) => { document.getElementById("countdown").textContent = data; }); // 监听时间显示更新事件 window.runtime.EventsOn("timeUpdate", (data) => { document.getElementById("countdown").textContent = data; });


if tm.isTimerRunning { remaining := tm.countdownDuration - tm.timerElapsed if remaining <= 0 { remaining = 0 tm.isTimerRunning = true tm.timerElapsed = tm.countdownDuration tm.isPaused = false runtime.EventsEmit(tm.ctx, "timerEnd") timerText := "00:00" // 发送事件显示更新事件 runtime.EventsEmit(tm.ctx, "timerUpdate", timerText) } else { minutes := int(remaining.Minutes()) seconds := int(remaining.Seconds()) % 60 timerText := fmt.Sprintf("%02d:%02d", minutes, seconds) // 发送事件显示更新事件 runtime.EventsEmit(tm.ctx, "timerUpdate", timerText) }

📦️ 打包发布


wails build

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值