告别控制台应用,用Go语言打造你的第一个桌面软件
🎯 痛点场景:为什么需要桌面应用开发技能?
你是否遇到这些问题?
- 学了Go语言,却只会写命令行工具?
- 想学桌面开发,却觉得C++/C#太复杂?
- 面试官对你的"烂大街"项目不感兴趣?
你能学到什么?
- ✅️ Wails 基础使用
- ✅️ 实时事件通信
- ✅️ 系统托盘集成
- ✅️ 多线程编程
- ✅️ 附完整源码

🛠️ 环境准备
步骤1:安装Go语言
bash
# 官网下载安装包:https://golang.org/dl/
go version # 验证安装
步骤2:安装Wails框架
bash
go install github.com/wailsapp/wails/v2/cmd/wails@latest
wails version # 验证安装
步骤3:创建项目骨架
bash
wails init -n Punktime
cd Punktime

🚀 核心功能开发
设计 UI (前端)
-
使用 HTML/CSS/JavaScript实现:
- 时间/倒计时主界面
- 倒计时设置遮罩层
-
示例代码
html
<!-- ============================================================================
* 主界面结构
* 应用程序的主要显示区域
* ============================================================================ -->
<!-- 主容器:显示倒计时/时间 -->
<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后端)
- 核心代码
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()
}
}
- 前后端通信
- 前端负责监听更新事件并渲染
- 后端负责触发更新事件并发送数据
js
// 监听倒计时更新事件
window.runtime.EventsOn("timerUpdate", (data) => {
document.getElementById("countdown").textContent = data;
});
// 监听时间显示更新事件
window.runtime.EventsOn("timeUpdate", (data) => {
document.getElementById("countdown").textContent = data;
});
go
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)
}
📦️ 打包发布
bash
wails build

📖 学习资源
👋你还希望有哪些学习搭子?欢迎留言!
❤️如果对你有帮助,别忘了点赞 + 关注!