前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >tauri聊天应用篇|vue3+tauri桌面聊天实例

tauri聊天应用篇|vue3+tauri桌面聊天实例

原创
作者头像
andy2018
发布2022-10-29 20:46:46
4K1
发布2022-10-29 20:46:46
举报
文章被收录于专栏:h5

前几天有分享一篇tauri整合vue3.js创建多窗口应用,tauri封装多开窗体。

/developer/article/2137811

今天分享的是使用tauri+vue3开发桌面端实战聊天实例TauriChat

tauri-vue3-chat 实现了发送消息、图片/视频/网址预览、拖拽聊天区发送图片、朋友圈等功能。支持tauri打开多个窗体、更换主题皮肤等功能。

技术栈

  • 使用技术:tauri1.1+vue^3.2.37+vite^3.0.2+vuex4+vue-router@4
  • UI组件库:element-plus^2.2.17
  • 弹窗组件:v3layer(vue3自定义pc端弹窗组件)
  • 滚动条组件:v3scroll(vue3模拟滚动条组件)
  • 矢量图标:阿里iconfont图标库

tauri-chat 还支持新窗口打开朋友圈功能。

tauri+vue3登录模板

代码语言:javascript
复制
<!-- 登录模板 -->
<template>
    <div>
        <div class="ntMain__cont flex1 flexbox flex-col">
            <div class="nt__lgregWrapper flex1 flexbox flex-col">
                <NavBar transparent />

                <div class="nt__lgregBox flex1">
                    <div class="banner">
                        <h2 class="tit flexbox flex-alignc"><img src="@assets/logo.png" />TAURI-VUE3-CAHT</h2>
                        <img class="bg" src="/static/skin/bg-banner.jpg" />
                    </div>
                    <div class="forms">
                        <form @submit.prevent="handleSubmit">
                            <ul class="clearfix">
                                <li><input class="iptxt flex1" type="text" v-model="formObj.tel" placeholder="请输入手机号" /></li>
                                <li><input class="iptxt flex1" type="password" v-model="formObj.pwd" placeholder="请输入密码"/></li>
                            </ul>
                            <div class="btns">
                                <el-button type="primary" native-type="submit" size="default" auto-insert-space>登录</el-button>
                            </div>
                            <div class="lgregLink align-c clearfix">
                                <router-link class="navigator" to="#">忘记密码</router-link>
                                <router-link class="navigator" to="/register">注册账号</router-link>
                            </div>
                            <!-- ... -->
                        </form>
                    </div>
                </div>
                <!-- ... -->
            </div>
        </div>
    </div>
</template>

<script setup>
    import { ref, reactive, inject } from 'vue'
    import { useStore } from 'vuex'

    import { mainWin } from '@/windows/actions'

    const store = useStore()

    const v3layer = inject('v3layer')

    const utils = inject('utils')

    const formObj = reactive({})

    const VMsg = (content) => {
        v3layer.message({ content, icon: 'error', shade: true })
    }

    const handleSubmit = () => {
        if(!formObj.tel){
            VMsg('手机号不能为空')
        }else if(!utils.checkTel(formObj.tel)){
            VMsg('手机号格式不正确')
        }else if(!formObj.pwd){
            VMsg('密码不能为空')
        }else{
            // 一些逻辑处理...
            
            v3layer({
                type: 'toast',
                icon: 'success',
                content: '登录成功',
                time: 2,
                onEnd() {
                    // 跳转主窗口(会关闭登录窗口)
                    mainWin()
                }
            })
        }
    }
</script>

tauri封装多窗口

代码语言:javascript
复制
// 创建新窗口
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+vue3实现自定义拖拽窗口

tauri-chat没有采用原生顶部导航,改用了自定义导航。配置decorations: false即可实现无边框窗口。

代码语言:javascript
复制
<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>{}</template>
            </div>
        </div>
        <WinTool :minimizable="minimizable" :maximizable="maximizable" :closable="closable">
            <slot name="wbtn" />
        </WinTool>
    </div>
</template>
代码语言:javascript
复制
<!-- 右上角操作按钮 -->
<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>
/**
 * tauri自定义拖拽
 * Q:282310962
*/
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托盘图标

代码语言:javascript
复制
/**
 * 创建系统托盘图标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();
            }
        });
    }
}

OK,以上就是基于tauri+vue3创建聊天实例的一些分享,希望对大家有所帮助。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 技术栈
  • tauri+vue3登录模板
  • tauri封装多窗口
  • tauri+vue3实现自定义拖拽窗口
  • tauri托盘图标
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档

http://www.vxiaotou.com