Tauri是什么?
Tauri - 用 Web 前端构建更小/快/安全的桌面应用程序框架。提供了许多前端初始化框架模板。
如果你了解 Electron,其实和 electron 性质差不多,只不过它是 基于 rust和webview2来呈现内容。
今天就来分享一些tauri结合vue3创建多窗口应用,自定义拖拽区域及托盘功能。
开始之前
准备之前,需要先安装 Rust 及其依赖。
- "C 生成工具" 和 Windows 10 SDK。
- Tauri 必须安装 WebView2 才能在 Windows 上呈现网页内容。
- Rust环境
https://tauri.app/zh/v1/guides/getting-started/prerequisites
- 创建tauri项目
npm create tauri-app
- 开发/打包
tauri dev
tauri build
Tauri封装多窗口
代码语言:javascript复制/**
* @desc 窗口容器
* @author: YXY Q:282310962
* @time 2022.10
*/
import { WebviewWindow, appWindow, getAll, getCurrent } from '@tauri-apps/api/window'
import { relaunch, exit } from '@tauri-apps/api/process'
import { emit, listen } from '@tauri-apps/api/event'
import { setWin } from './actions'
// 系统参数配置
export const windowConfig = {
label: null, // 窗口唯一label
title: '', // 窗口标题
url: '', // 路由地址url
width: 900, // 窗口宽度
height: 640, // 窗口高度
minWidth: null, // 窗口最小宽度
minHeight: null, // 窗口最小高度
x: null, // 窗口相对于屏幕左侧坐标
y: null, // 窗口相对于屏幕顶端坐标
center: true, // 窗口居中显示
resizable: true, // 是否支持缩放
maximized: false, // 最大化窗口
decorations: false, // 窗口是否无边框及导航条
alwaysOnTop: false, // 置顶窗口
}
class Windows {
constructor() {
this.mainWin = null
}
// 获取窗口
getWin(label) {
return WebviewWindow.getByLabel(label)
}
// 获取全部窗口
getAllWin() {
return getAll()
}
// 创建新窗口
async createWin(options) {
const args = Object.assign({}, windowConfig, options)
// 判断窗口是否存在
const existWin = getAll().find(w => w.label == args.label)
if(existWin) {
if(existWin.label.indexOf('main') == -1) {
await existWin?.unminimize()
await existWin?.setFocus()
return
}
await existWin?.close()
}
// 创建窗口对象
let win = new WebviewWindow(args.label, args)
// 是否最大化
if(args.maximized && args.resizable) {
win.maximize()
}
// 窗口创建完毕/失败
win.once('tauri://created', async() => {
console.log('window create success!')
...
})
win.once('tauri://error', async() => {
console.log('window create error!')
})
}
// 开启主进程监听事件
async listen() {
// 创建新窗体
await listen('win-create', (event) => {
console.log(event)
this.createWin(JSON.parse(event.payload))
})
// 显示窗体
await listen('win-show', async(event) => {
if(appWindow.label.indexOf('main') == -1) return
await appWindow.show()
await appWindow.unminimize()
await appWindow.setFocus()
})
// 隐藏窗体
await listen('win-hide', async(event) => {
if(appWindow.label.indexOf('main') == -1) return
await appWindow.hide()
})
// 退出应用
await listen('win-exit', async(event) => {
setWin('logout')
await exit()
})
// 重启应用
await listen('win-relaunch', async(event) => {
await relaunch()
})
// 主/渲染进程传参
await listen('win-setdata', async(event) => {
await emit('win-postdata', JSON.parse(event.payload))
})
}
}
export default Windows
actions.js进行一些调用处理。
代码语言:javascript复制/**
* 处理渲染器进程到主进程的异步通信
*/
import { WebviewWindow } from '@tauri-apps/api/window'
import { emit } from '@tauri-apps/api/event'
/**
* @desc 创建新窗口
*/
export async function createWin(args) {
await emit('win-create', args)
}
/**
* @desc 获取窗口
* @param args {string}
*/
export async function getWin(label) {
return await WebviewWindow.getByLabel(label)
}
/**
* @desc 设置窗口
* @param type {string} 'show'|'hide'|'close'|'min'|'max'|'max2min'|'exit'|'relaunch'
*/
export async function setWin(type) {
await emit('win-' type)
}
/**
* @desc 登录窗口
*/
export async function loginWin() {
await createWin({
label: 'Login',
title: '登录',
url: '/login',
width: 320,
height: 420,
resizable: false,
alwaysOnTop: true,
})
}
// ...
通过下面的方式,传入参数即可快速生成一个新窗口。
代码语言:javascript复制const createManageWin = async() => {
createWin({
label: 'Manage',
title: '管理页面',
url: '/manage',
width: 600,
height: 450,
minWidth: 300,
minHeight: 200
})
}
const createAboutWin = async() => {
createWin({
label: 'About',
title: '关于页面',
url: '/about',
width: 500,
height: 500,
resizable: false,
alwaysOnTop: true
})
}
Tauri Vue3实现无边框拖拽窗体
设置decorations: false
窗口会无导航栏及边框。这时就需要自定义拖拽区域和最小/大化及关闭按钮。
新建 winTool.vue
模板。
<template>
<div class="nt__navbar" :class="{'fixed': fixed || transparent}">
<div data-tauri-drag-region class="nt__navbar-wrap flexbox flex-alignc">
<div class="nt__navbar-title" :class="{'center': center}">
<template v-if="$slots.title"><slot name="title" /></template>
<template v-else>{{title}}</template>
</div>
</div>
<WinBar :minimizable="minimizable" :maximizable="maximizable" :closable="closable">
<slot name="wbtn" />
</WinBar>
</div>
</template>
tauri提供了自定义拖拽属性data-tauri-drag-region
支持主题和多彩皮肤。
Tauri托盘图标
代码语言:javascript复制use tauri::{
AppHandle, Manager,
CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu
};
// 托盘菜单
pub fn menu() -> SystemTray {
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
let show = CustomMenuItem::new("show".to_string(), "Show");
let hide = CustomMenuItem::new("hide".to_string(), "Hide");
let change_ico = CustomMenuItem::new("change_ico".to_string(), "Change Icon");
let tray_menu = SystemTrayMenu::new()
.add_submenu(SystemTraySubmenu::new(
"Language", // 语言菜单
SystemTrayMenu::new()
.add_item(CustomMenuItem::new("lang_english".to_string(), "English"))
.add_item(CustomMenuItem::new("lang_zh_CN".to_string(), "简体中文"))
.add_item(CustomMenuItem::new("lang_zh_HK".to_string(), "繁体中文")),
))
.add_native_item(SystemTrayMenuItem::Separator) // 分割线
.add_item(change_ico)
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(hide)
.add_item(show)
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(quit);
SystemTray::new().with_menu(tray_menu)
}
// 托盘事件
pub fn handler(app: &AppHandle, event: SystemTrayEvent) {
match event {
SystemTrayEvent::LeftClick {
position: _,
size: _,
..
} => {
println!("点击左键");
}
SystemTrayEvent::RightClick {
position: _,
size: _,
..
} => {
println!("点击右键");
}
SystemTrayEvent::DoubleClick {
position: _,
size: _,
..
} => {
println!("双击");
}
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
"change_ico" => { // 更新托盘图标
app.tray_handle()
.set_icon(tauri::Icon::Raw(
include_bytes!("../icons/new.png").to_vec()
))
.unwrap();
}
lang if lang.contains("lang_") => { // 选择语言,匹配 id 前缀包含 `lang_` 的事件
Lang::new(
app,
id, // 点击菜单的 id
vec![
Lang {
name: "English",
id: "lang_english",
},
Lang {
name: "繁体中文",
id: "lang_zh_HK",
},
Lang {
name: "简体中文",
id: "lang_zh_CN",
},
],
);
}
"hide" => {
// let window = app.get_window("main").unwrap();
// window.show().unwrap();
println!("点击隐藏");
}
"show" => {
println!("点击显示");
}
"quit" => {
println!("点击退出");
std::process::exit(0);
}
_ => {}
},
_ => {}
}
}
struct Lang<'a> {
name: &'a str,
id: &'a str,
}
impl Lang<'static> {
fn new(app: &AppHandle, id: String, langs: Vec<Lang>) {
// 获取点击的菜单项
langs.iter().for_each(|lang| {
let handle = app.tray_handle().get_item(lang.id);
if lang.id.to_string() == id.as_str() {
// 设置菜单名称
handle.set_title(format!(" {}", lang.name)).unwrap();
// 还可以使用 `set_selected`、`set_enabled` 和 `set_native_image`(仅限 macOS)
handle.set_selected(true).unwrap();
} else {
handle.set_title(lang.name).unwrap();
handle.set_selected(false).unwrap();
}
});
}
}
好了,以上就是tauri vue3开发桌面端窗口应用的一些实践分享。
后续还是分享一个实例项目,希望大家能喜欢。