Tauri-Chat 一款根据
tauri+rust+element-plus
建立的桌面端谈天实例项目。
/post/715751…
tauri 创立的项目供给了 vue/react/svelte 等多种前端模板。支撑构建多窗口运用。
技术栈
- 开发工具:vscode+sublime
- 运用技术:tauri+vue^3.2.37+vite^3.0.2+vuex4+vue-router@4
- UI组件库:element-plus^2.2.17
- 弹窗组件:v3layer(根据vue3自定义弹窗)
- 滚动条组件:v3scroll(根据vue3模拟滚动条)
- 矢量图标:阿里iconfont字体图标库
tauri封装多窗口
// 创立新窗口
async createWin(options) {
const args = Object.assign({}, windowConfig, options)
// 是否主窗口
if(args.label.indexOf('main') > -1) {
console.log('该窗口是主窗口')
this.mainWin = getAll().find(w => w.label.indexOf('main') > -1 && w.label != args.label)
await this.mainWin?.hide()
}
// 创立窗口对象
let win = new WebviewWindow(args.label, args)
// 是否最大化
if(args.maximized && args.resizable) {
win.maximize()
}
// 窗口创立结束/失利
win.once('tauri://created', async() => {
console.log('window create success!')
await win?.show()
await this.mainWin?.close()
})
win.once('tauri://error', async() => {
console.log('window create error!')
})
}
tauri创立无边框窗口
创立窗口的时分设置decorations:false
属性,则创立的窗口没有边框及导航栏。
<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>
<WinTool :minimizable="minimizable" :maximizable="maximizable" :closable="closable">
<slot name="wbtn" />
</WinTool>
</div>
</template>
<!-- 右上角操作按钮 -->
<template>
<div class="taui__winbtn">
<div class="taui__winbtn-groups">
<slot />
<a v-if="minimizable" class="wbtn" title="最小化" @click="handleWinMin"><i class="iconfont icon-min"></i></a>
<a v-if="maximizable && isResizable" class="wbtn" :title="isMaximized ? '向下复原' : '最大化'" @click="handleWinMax2Min">
<i class="iconfont" :class="isMaximized ? 'icon-restore' : 'icon-max'"></i>
</a>
<a v-if="closable" class="wbtn close" title="封闭" @click="handleWinClose"><i class="iconfont icon-quit"></i></a>
</div>
</div>
</template>
<script setup>
import { onMounted, reactive, inject, toRefs } from 'vue'
import { useStore } from 'vuex'
import { appWindow } from '@tauri-apps/api/window'
import { listen } from '@tauri-apps/api/event'
import { exit } from '@tauri-apps/api/process'
// ...
const store = useStore()
const v3layer = inject('v3layer')
const data = reactive({
isMaximized: false,
isResizable: true
})
onMounted(async() => {
data.isMaximized = await appWindow.isMaximized()
data.isResizable = await appWindow.isResizable()
listen('tauri://resize', async() => {
data.isMaximized = await appWindow.isMaximized()
})
})
// 最小化
const handleWinMin = async() => {
await appWindow.minimize()
}
// 最大化/复原
const handleWinMax2Min = async() => {
const resizable = await appWindow.isResizable()
if(!resizable) return
await appWindow.toggleMaximize()
}
// 封闭
const handleWinClose = async() => {
if(appWindow.label.indexOf('main') > -1) {
let $el = v3layer({
type: 'android',
content: '承认退出运用程序吗?',
btns: [
{
text: '最小化托盘',
style: 'color:#24c8db',
click: () => {
await appWindow.hide()
}
},
{
text: '退出程序',
style: 'color:#ff5438',
click: async() => {
store.commit('LOGOUT')
await exit()
}
}
]
})
}else {
await appWindow.close()
}
}
</script>
tauri自定义托盘
/**
* 创立系统托盘图标Tray
*/
use tauri::{
AppHandle, Manager,
CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu
};
// 托盘菜单
pub fn menu() -> SystemTray {
let exit = CustomMenuItem::new("exit".to_string(), "退出");
let relaunch = CustomMenuItem::new("relaunch".to_string(), "重启运用");
let show = CustomMenuItem::new("show".to_string(), "显现窗口");
let hide = CustomMenuItem::new("hide".to_string(), "躲藏窗口");
let change_ico = CustomMenuItem::new("change_ico".to_string(), "更换托盘图标");
let tray_menu = SystemTrayMenu::new()
.add_submenu(SystemTraySubmenu::new(
"国际化", // 言语菜单
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(relaunch)
.add_item(exit);
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!("双击");
app.emit_all("win-show", {}).unwrap();
}
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
// 更新托盘图标
"change_ico" => {
app.tray_handle()
.set_icon(tauri::Icon::Raw(
include_bytes!("../icons/tray-empty.ico").to_vec()
))
.unwrap();
}
// 选择言语,匹配 id 前缀包含 `lang_` 的事情
lang if lang.contains("lang_") => {
Lang::new(
app,
id,
vec![
Lang {
name: "English",
id: "lang_english",
},
Lang {
name: "繁体中文",
id: "lang_zh_HK",
},
Lang {
name: "简体中文",
id: "lang_zh_CN",
},
],
);
}
"hide" => {
// println!("点击躲藏");
app.emit_all("win-hide", {}).unwrap();
}
"show" => {
// println!("点击显现");
app.emit_all("win-show", {}).unwrap();
}
"relaunch" => {
// println!("点击重启");
app.emit_all("win-relaunch", {}).unwrap();
}
"exit" => {
// println!("点击退出");
app.emit_all("win-exit", {}).unwrap();
}
_ => {}
},
_ => {}
}
}
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();
handle.set_selected(true).unwrap();
} else {
handle.set_title(lang.name).unwrap();
handle.set_selected(false).unwrap();
}
});
}
}
运用tauri-vue3-vite3
桌面谈天实例开发就分享到这儿。感谢各位的支撑哈~~
/post/701019…
/post/697729…