快速入门Tauri开发桌面端应用
最近发现自己工作状况愈发杂乱无章,时而奋笔疾书口干舌燥,时而思考半天苦于没有思路,时而发现有趣的文章一看再看不可自拔导致时间挥霍无度,总之就是时间观念日渐模糊,急需一款时间工具改善自身状况。
一、需求分析
首先解决自己的痛点,根据工作状态分析,初版需要的功能如下:
- 倒计时提醒功能、自定义倒计时时间、包括开始、暂停、重置
- 放置桌面顶层、能够时刻关注时间进度
- 半透明模式、至于顶层尽量不遮挡其他应用
二、技术调研
作为一名前端工程师,尽量使用学习成本低的语言入手,桌面端的开发适合前端开发的框架有Electron和新出的Tauri,鉴于开发的是款小工具,所以选择的了打包更轻量的Tauri框架,事实证明确实很小,只有几M大小,为电脑磁盘并不富裕(原神占了很大部分)的我节省下了很大的空间
三、项目准备
预先准备
快速开始
npm create tauri-app
根据需要选择用vite作为前端构建工具,然后根据命令提示输入项目名称,选择包管理工具(npm,pnpm,yarn...),选择前端框架(vue,react....),我这边选择的是vue+ts,最后生成目录结构如下
如果你的应用用不到系统API的话,就跟开发WEB应用类似,打包的时候会编译成桌面应用
四、项目开发
项目配置
为了方便项目开发,我在项目中集成了包括ElementPlus、Windicss、Iconify等方便组建库,快速编写样式和引入图标,具体vite.config.ts配置如下
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import WindiCSS from 'vite-plugin-windicss'
// 图标
import Icons from 'unplugin-icons/vite';
import IconsResolver from 'unplugin-icons/resolver';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
// elementUI组件自动导入
AutoImport({
dts:'./auto-imports.d.ts',
resolvers: [ElementPlusResolver()],
}),
Components({
dts:'./components.d.ts',
resolvers: [ElementPlusResolver(),IconsResolver()],
}),
// 样式封装方便使用
WindiCSS(),
Icons({
compiler: 'vue3',
autoInstall: true,
}),
],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
// prevent vite from obscuring rust errors
clearScreen: false,
// tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
},
// to make use of `TAURI_DEBUG` and other env variables
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
envPrefix: ["VITE_", "TAURI_"],
build: {
// Tauri supports es2021
target: process.env.TAURI_PLATFORM == "windows" ? "chrome105" : "safari13",
// don't minify for debug builds
minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
// produce sourcemaps for debug builds
sourcemap: !!process.env.TAURI_DEBUG,
},
});
代码编写
核心功能组件编写clock.vue
<template>
<div class="w-250px h-250px m-auto h-300" :class="{'bg-white':!state.isDark}" @contextmenu.prevent
>
<div class="m-auto flex justify-between items-center pl-2 pt-2 pr-2" >
<div class="flex items-center">
<div class="flex items-center">
<Rank class="cursor-pointer" style="width: 1.4em; height: 1.4em;color:#4194f8;" data-tauri-drag-region />
</div>
<div class="flex items-center">
<el-switch
v-model="state.isDark"
class="ml-2"
inline-prompt
:active-icon="Sunny"
:inactive-icon="Moon"
@change="changeTop"
/>
</div>
</div>
<div class="flex items-center cursor-pointer">
<Close style="width: 1.4em; height: 1.4em;color:#4194f8" @click="closeWindow" />
</div>
</div>
<div class="flex flex-col">
<div class="mx-auto cursor-pointer select-none" @click="miniStop">
<el-progress type="circle" :width="110" :stroke-width="12" :percentage="state.progress" :color="state.colors">
<div>
<div v-if="state.isDark && state.status!=='running'">
<el-icon size="30" color="#3772d3">
<i-carbon-continue-filled />
</el-icon>
</div>
<div v-else class="text-blue-500 text-xl font-bold flex justify-center items-center m-auto" >
<span>
{{ state.down_minute>9?state.down_minute:'0'+state.down_minute }}
</span>
<div class="spec-circle-box">
<span class="spec-circle"></span>
<span class="spec-circle"></span>
</div>
<span>
{{ endTime }}
</span>
</div>
</div>
</el-progress>
</div>
<transition name="el-fade-in">
<div class="flex flex-col mx-auto mt-2" v-if="!state.isDark">
<div class="mx-auto flex justify-center">
<el-button-group>
<el-tooltip
effect="dark"
content="开始/继续"
placement="bottom"
>
<el-button type="primary" @click="confirmRun">
<i-carbon-continue-filled />
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
content="暂停"
placement="top"
>
<el-button type="primary" @click="ClockPause">
<i-clarity-pause-solid />
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
content="结束"
placement="bottom"
>
<el-button type="primary" @click="ClockStop">
<i-carbon-stop-filled-alt />
</el-button>
</el-tooltip>
</el-button-group>
</div>
<div class="w-30 mt-3 mx-auto flex justify-center">
<el-input-number :disabled="state.status !== 'default'" placeholder="分钟数" v-model="state.number" />
</div>
</div>
</transition>
</div>
<audio class="hidden " ref="audio" src="./audio.mp3"></audio>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from "vue"
import { ElMessageBox } from 'element-plus'
import { appWindow } from '@tauri-apps/api/window';
import { Rank, Sunny, Moon, Close } from "@element-plus/icons-vue"
import type { Action } from 'element-plus'
import { exit } from '@tauri-apps/api/process'
interface State{
progress:number
number:number
time:string
colors:Color[]
timer:any
progress_time:number
sum_time:number
down_minute:number,
down_second:number,
isDark: boolean
// 状态 默认、运行中、暂停
status: 'default' | 'running' | 'pause'
}
interface Color{
color:string
percentage:number
}
const audio = ref<HTMLAudioElement | null>(null)
const state = reactive<State>({
isDark:false,
progress:0,
number:25,
time:'',
colors:[
{ color: '#6f7ad3', percentage: 20 },
{ color: '#1989fa', percentage: 40 },
{ color: '#5cb87a', percentage: 60 },
{ color:'#e6a23c' , percentage: 80 },
{ color:'#f56c6c' , percentage: 100 },
],
timer:null,
progress_time:0,//进行多少时间
sum_time:0,
down_minute:25,
down_second:0,
status: 'default'
})
const endTime = computed(() => {
return state.down_second>9?state.down_second:'0'+state.down_second
})
// 改变窗口置顶
const changeTop = async ()=>{
if(state.isDark){
await appWindow.setAlwaysOnTop(true);
}else{
await appWindow.setAlwaysOnTop(false);
}
}
const miniStop = ()=>{
if (state.isDark) {
if (state.status === 'default' || state.status === 'pause') {
ClockRun()
return
}
if (state.status === 'running') {
ClockPause()
return
}
}
}
const closeWindow = ()=>{
ElMessageBox.confirm('确认退出应用程序么?', '提示', {
confirmButtonText: '退出',
cancelButtonText: '最小化托盘',
distinguishCancelAndClose:true,
callback: (action: Action) => {
if(action === 'confirm'){
exit()
}
if(action === 'cancel'){
appWindow.hide()
}
},
})
}
// 倒计时停止
const ClockStop = () => {
if (state.status === 'pause' || state.status == 'running') {
state.status = 'default'
clearInterval(state.timer)
state.timer = null
state.number = 25
state.progress_time = 0//进行多少时间
state.progress = 0
state.sum_time = 0
state.down_minute = 25
state.down_second = 0
}
}
// 倒计时暂停
const ClockPause = () => {
if (state.status === 'running') {
state.status = 'pause'
clearInterval(state.timer)
state.timer = null
}
}
// 倒计时开始
const ClockRun = () => {
if (state.status === 'default' || state.status === 'pause') {
// 开始运行
state.status = 'running'
// 总共多少秒
state.sum_time = state.number*60
state.timer = setInterval(()=>{
// 当前进行多少时间
state.progress_time++
state.down_second = (state.sum_time - state.progress_time)%60
state.down_minute = Math.floor((state.sum_time - state.progress_time)/60)
// 计算百分比
state.progress = (state.progress_time/state.sum_time)*100
if(state.progress >= 100 || state.progress_time >= state.sum_time){
ElMessageBox.alert('休息一下吧', '提示', {
confirmButtonText: '确认',
callback: (action: Action) => {
},
})
playMusic()
ClockStop()
}
},1000)
}
}
// 判断是否可以开始
const confirmRun = ()=>{
if(state.number<=0){
ElMessageBox.alert('请输入正确的分钟数', '提示', {
// if you want to disable its autofocus
// autofocus: false,
confirmButtonText: '确认',
callback: (action: Action) => {
// ElMessage({
// type: 'info',
// message: `action: ${action}`,
// })
},
})
return
}
ClockRun()
}
const playMusic = ()=>{
audio.value?.play()
}
onMounted(()=>{
})
</script>
<style scoped>
.spec-circle-box{
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
width: 8px;
height: 10px;
}
.spec-circle{
width:4px;
height: 4px;
border-radius: 50%;
background: #3b82f6;
}
</style>
桌面端配置
- 桌面端配置文件tauri.conf.json
- 必须要修改的配置有identifier,打包发布需要包名
- 修改icon桌面图标,Tauri提供了工具生成多类型图标,根目录下放置
./app-icon.png,运行npm run tauri icon即可生成多种适配图标 工具链接 - 其他修改,如默认窗口大小、标题、是否居中等配置,参考配置链接
代码打包
npm run tauri build
由于国内环境原因,打包很可能会遇到卡住不动的问题,如下图
解决办法如下
根据提示Downloading的链接下载该压缩包,下载完后,在本机 C:\Users\xxxxxxxx\AppData\Loca 中,创建 tauri/WixTools 文件夹,然后把内容解压到里面就可以了,解压后如下图,然后重新运行打包命令即可
项目截图
项目地址
项目地址如下,欢迎大家star
https://gitee.com/q-_-p/focus-clock
欢迎关注我的公众号“总有BUG想害朕”,原创技术文章第一时间推送。