基于tauri+vue3+pinia2客户端管理系统程序|tauri+vite4后台系统

news2024/11/19 14:48:09

TauriAdmin一款跨端通用后台系统模板解决方案

基于 tauri rust webview2 整合 vite4 搭建桌面端 vue3 管理后台模板TauriVue3Admin。支持多窗口切换管理、vue-i18n多语言、动态路由权限、常用业务功能模块及动态路由缓存等功能。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用技术

  • 编码工具:Vscode
  • 框架技术:tauri+vite4+vue3+pinia+vue-router
  • UI组件库:ve-plus (基于vue3轻量级UI组件库)
  • 样式处理:sass^1.63.6
  • 图表组件:echarts^5.4.2
  • 国际化方案:vue-i18n^9.2.2
  • 编辑器组件:wangeditor^4.7.15
  • 本地缓存:pinia-plugin-persistedstate^3.1.0

在这里插入图片描述

特色

  1. 最新跨端技术tauri1.4 rust webview2
  2. 前端技术栈vite4、vue3、pinia、vue-router、vue-i18n
  3. 支持中文/英文/繁体多语言解决方案
  4. 支持动态路由权限验证
  5. 支持路由缓存功能/tabs控制切换路由页面
  6. 内置多个模板布局风格
  7. 搭配轻量级vue3组件库veplus

在这里插入图片描述

项目搭建结构目录

在这里插入图片描述
整个项目基于tauri脚手架创建,全部采用vue3 setup语法糖编码开发。

在这里插入图片描述
tauri-admin支持创建多窗口,如上图从登录窗口切换到主窗口。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

tauri多窗口封装

在src目录下新建一个管理tauri多窗口文件夹。
在这里插入图片描述
创建新窗口配置参数

// 创建窗口参数配置
export const windowConfig = {
    label: null,            // 窗口唯一label
    title: '',              // 窗口标题
    url: '',                // 路由地址url
    width: 1000,            // 窗口宽度
    height: 640,            // 窗口高度
    minWidth: null,         // 窗口最小宽度
    minHeight: null,        // 窗口最小高度
    x: null,                // 窗口相对于屏幕左侧坐标
    y: null,                // 窗口相对于屏幕顶端坐标
    center: true,           // 窗口居中显示
    resizable: true,        // 是否支持缩放
    maximized: false,       // 最大化窗口
    decorations: false,     // 窗口是否装饰边框及导航条
    alwaysOnTop: false,     // 置顶窗口
    fileDropEnabled: false, // 禁止系统拖放
    visible: false          // 隐藏窗口
}

在这里插入图片描述

/**
 * @desc    窗口管理
 * @author: YXY  Q:282310962
 * @time    2023.07
 */

import { WebviewWindow, appWindow, getAll } 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: 1000,            // 窗口宽度
    height: 640,            // 窗口高度
    minWidth: null,         // 窗口最小宽度
    minHeight: null,        // 窗口最小高度
    x: null,                // 窗口相对于屏幕左侧坐标
    y: null,                // 窗口相对于屏幕顶端坐标
    center: true,           // 窗口居中显示
    resizable: true,        // 是否支持缩放
    maximized: false,       // 最大化窗口
    decorations: false,     // 窗口是否装饰边框及导航条
    alwaysOnTop: false,     // 置顶窗口
    fileDropEnabled: false, // 禁止系统拖放
    visible: false          // 隐藏窗口
}

class Windows {
    constructor() {
        // 主窗口
        this.mainWin = null
    }

    // 创建新窗口
    async createWin(options) {
        console.log('-=-=-=-=-=开始创建窗口')

        const args = Object.assign({}, windowConfig, options)

        // 判断窗口是否存在
        const existWin = getAll().find(w => w.label == args.label)
        if(existWin) {
            console.log('窗口已存在>>', existWin)
            if(existWin.label.indexOf('main') == -1) {
                // 自定义处理...
            }
        }

        // 是否主窗口
        if(args.label.indexOf('main') > -1) {
            console.log('该窗口是主窗口')
            // 自定义处理...
        }

        // 创建窗口对象
        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()
        })

        win.once('tauri://error', async() => {
            console.log('window create error!')
        })
    }

    // 获取窗口
    getWin(label) {
        return WebviewWindow.getByLabel(label)
    }

    // 获取全部窗口
    getAllWin() {
        return getAll()
    }

    // 开启主进程监听事件
    async listen() {
        console.log('——+——+——+——+——+开始监听窗口')

        // 创建新窗体
        await listen('win-create', (event) => {
            this.createWin(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-close', async(event) => {
            await appWindow.close()
        })

        // 退出应用
        await listen('win-exit', async(event) => {
            setWin('logout')
            await exit()
        })

        // ...
    }
}

actions.js则是处理一些渲染进程/主进程通讯。

/**
 * 处理渲染器进程到主进程的异步通信
 */

import { WebviewWindow } from '@tauri-apps/api/window'
import { emit } from '@tauri-apps/api/event'

/**
 * @desc 创建新窗口
 * @param args {object} {label: 'new', url: '/new', width: 500, height: 300, ...}
 */
export async function createWin(args) {
    console.log(args)
    await emit('win-create', args)
}

/**
 * @desc 获取窗口
 * @param args {string} 'main'|'main_login' ...
 */
export async function getWin(label) {
    return await WebviewWindow.getByLabel(label)
}

/**
 * @desc 设置窗口
 * @param type {string} 'show'|'hide'|'close'|'min'|'max'|'max2min'|'exit'|'relaunch'
 * @param id {number}
 */
export async function setWin(type) {
    await emit('win-' + type)
}

/**
 * @desc 主|渲染进程数据传递
 * @param args {object} {type: 'MSG_TYPE_XXX', value: 123}
 */
export async function setWinData(args) {
    await emit('win-setdata', args)
}

/**
 * @desc 屏蔽系统右键菜单
 */
export function disableWindowMenu() {
    document.addEventListener('contextmenu', e => e.preventDefault())
}


/**
 * @desc 登录窗口
 */
export async function loginWin() {
    await createWin({
        label: 'main_login',
        title: '登录',
        url: '/login',
        width: 520,
        height: 420,
        resizable: false,
        alwaysOnTop: true
    })
}

/**
 * @desc 主窗口
 */
export async function mainWin() {
    await createWin({
        label: 'main',
        title: 'TAURI-ADMIN',
        url: '/',
        width: 1000,
        height: 640,
        minWidth: 750,
        minHeight: 500
    })
}

在这里插入图片描述

主入口main.js

import { createApp } from "vue"
import "./styles.scss"
import App from "./App.vue"

// 引入路由及状态管理
import Router from './router'
import Pinia from './pinia'

// 引入插件配置
import Libs from './libs'

const app = createApp(App)

app
.use(Router)
.use(Pinia)
.use(Libs)
.mount("#app")

布局模板

tauri-admin内置了3种常用的模板。

在这里插入图片描述

<script setup>
    import { computed } from 'vue'
    import { appStore } from '@/pinia/modules/app'

    // 引入布局模板
    import Columns from './template/columns/index.vue'
    import Vertical from './template/vertical/index.vue'
    import Transverse from './template/transverse/index.vue'

    const store = appStore()
    const config = computed(() => store.config)

    const LayoutConfig = {
        columns: Columns,
        vertical: Vertical,
        transverse: Transverse
    }
</script>

<template>
    <div class="veadmin__container" :style="{'--themeSkin': store.config.skin}">
        <component :is="LayoutConfig[config.layout]" />
    </div>
</template>

路由vue-router配置

在这里插入图片描述

/**
 * 路由配置
 * @author YXY
 */

import { appWindow } from '@tauri-apps/api/window'
import { createRouter, createWebHistory } from 'vue-router'
import { appStore } from '@/pinia/modules/app'
import { hasPermission } from '@/hooks/usePermission'
import { loginWin } from '@/multiwins/actions'

// 批量导入modules路由
const modules = import.meta.glob('./modules/*.js', { eager: true })
const patchRoutes = Object.keys(modules).map(key => modules[key].default).flat()

/**
 * @description 动态路由参数配置
 * @param path ==> 菜单路径
 * @param redirect ==> 重定向地址
 * @param component ==> 视图文件路径
 * 菜单信息(meta)
 * @param meta.icon ==> 菜单图标
 * @param meta.title ==> 菜单标题
 * @param meta.activeRoute ==> 路由选中(默认空 route.path)
 * @param meta.rootRoute ==> 所属根路由选中(默认空)
 * @param meta.roles ==> 页面权限 ['admin', 'dev', 'test']
 * @param meta.breadcrumb ==> 自定义面包屑导航 [{meta:{...}, path: '...'}]
 * @param meta.isAuth ==> 是否需要验证
 * @param meta.isHidden ==> 是否隐藏页面
 * @param meta.isFull ==> 是否全屏页面
 * @param meta.isKeepAlive ==> 是否缓存页面
 * @param meta.isAffix ==> 是否固定标签(tabs标签栏不能关闭)
 * */
const routes = [
    // 首页
    {
        path: '/',
        redirect: '/home'
    },
    // 错误模块
    {
        path: '/:pathMatch(.*)*',
        component: () => import('@views/error/404.vue'),
        meta: {
            title: 'page__error-notfound'
        }
    },
    ...patchRoutes
]

const router = createRouter({
    history: createWebHistory(),
    routes
})

// 全局钩子拦截
router.beforeEach((to, from, next) => {
    // 开启加载提示
    loading({
        text: 'Loading...',
        background: 'rgba(70, 255, 170, .1)'
    })
    
    const store = appStore()
    if(to?.meta?.isAuth && !store.isLogged) {
        loginWin()
        loading.close()
    }else if(!hasPermission(store.roles, to?.meta?.roles)) {
        // 路由鉴权
        appWindow?.show()
        next('/error/forbidden')
        loading.close()
        Notify({
            title: '访问限制!',
            description: `<span style="color: #999;">当前登录角色 ${store.roles} 没有操作权限,请联系管理员授权后再操作。</div>`,
            type: 'danger',
            icon: 've-icon-unlock',
            time: 10
        })
    }else {
        appWindow?.show()
        next()
    }
})

router.afterEach(() => {
    loading.close()
})

router.onError(error => {
    loading.close()
    console.warn('Router Error》》', error.message);
})

export default router

在这里插入图片描述
在这里插入图片描述

vue3状态管理pinia配置

项目中使用的状态管理由之前的vuex替换为pinia了。
在这里插入图片描述

/**
 * 状态管理 Pinia util
 * @author YXY
 */

import { createPinia } from 'pinia'
// 引入pinia本地持久化存储
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

export default pinia

tauri vue3 国际化解决方案

tauri-admin国际化多语言采用vue-i18n插件实现功能。

在这里插入图片描述

# 安装插件
npm i vue-i18n
yarn add vue-i18n

在这里插入图片描述

import { createI18n } from 'vue-i18n'
import { appStore } from '@/pinia/modules/app'

// 引入语言配置
import enUS from './en-US'
import zhCN from './zh-CN'
import zhTW from './zh-TW'

// 默认语言
export const langVal = 'zh-CN'

export default async (app) => {
    const store = appStore()
    const lang = store.lang || langVal

    const i18n = createI18n({
        legacy: false,
        locale: lang,
        messages: {
            'en': enUS,
            'zh-CN': zhCN,
            'zh-TW': zhTW
        }
    })
    
    app.use(i18n)
}

在这里插入图片描述
支持中英文/繁体切换,可自定义多语言包配置文件。

tauri.conf.json配置

配置一些打包参数、窗口启动配置参数及托盘参数。

{
  "build": {
    "beforeDevCommand": "yarn dev",
    "beforeBuildCommand": "yarn build",
    "devPath": "http://localhost:1420",
    "distDir": "../dist",
    "withGlobalTauri": false
  },
  "package": {
    "productName": "tauri-admin",
    "version": "0.0.0"
  },
  "tauri": {
    "allowlist": {
      "all": true,
      "shell": {
        "all": false,
        "open": true
      }
    },
    "bundle": {
      "active": true,
      "targets": "all",
      "identifier": "com.tauri.admin",
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/128x128@2x.png",
        "icons/icon.icns",
        "icons/icon.ico"
      ]
    },
    "security": {
      "csp": null
    },
    "windows": [
      {
        "fullscreen": false,
        "resizable": true,
        "title": "tauri-admin",
        "width": 1000,
        "height": 640,
        "center": true,
        "decorations": false,
        "fileDropEnabled": false,
        "visible": false
      }
    ],
    "systemTray": {
      "iconPath": "icons/icon.ico",
      "iconAsTemplate": true,
      "menuOnLeftClick": false
    }
  }
}

以上就是tauri+vue3创建跨端桌面管理后台应用的一些分享,希望大家喜欢~~
如果大家觉得有些帮助,也可以关注公众号,不定期分享一些项目实例。
在这里插入图片描述

最后附上两个最新实例项目

Electron25+vue3桌面端仿制ChatGPT会话模板实例

Vue3+Tauri跨端聊天实例|tauri仿微信聊天室

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/756082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【chap4-链表】用Python3刷《代码随想录》

通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域data&#xff0c;另一个是指针域next&#xff08;存放指向下一个节点的指针&#xff09;&#xff0c;最后一个节点的指针域指向null&#xff08;空指针&#xff09; 链接的入口点称为…

耳夹式骨传导耳机有哪些比较好用?这三个款式不容错过!

骨传导耳机由于不入耳&#xff0c;不用担心耳道健康问题&#xff0c;越来越受到广大网友的喜欢&#xff0c;而传统的入耳式耳机&#xff0c;则因为长时间佩戴会耳朵痛&#xff0c;容易掉落等问题逐渐的被网友抛弃&#xff0c;那么在骨传导耳机市场种类这么多的情况下&#xff0…

Apache Kudu 在**医疗科技的生产实践

目录 说明 医疗场景下数据特点 KUDU 的介绍 kudu 架构 kudu 文件组织形式 kudu的生产实践 技术选型 整体的架构 项目遇到的问题 参考资料 说明 本文主要介绍APACHE KUDU 在**医疗科技数据实时分析场景下的实践&#xff0c;内容包括&#xff1a; 医疗场景下数据特点 …

mysql什么情况下行锁(表锁)(锁的概念)

1&#xff1a;数据表aa的设计结构 2&#xff1a; 使用navicat编写手动控制事务 3&#xff1a;先选择开启事务和执行更新操作&#xff0c;where b1&#xff08;表锁&#xff09;b不是索引&#xff0c;不提交事务&#xff0c;&#xff08;如果where b1&#xff0c;b是索引就行锁&…

本地Nginx部署React前端项目浅尝

目录 nginx [下载](http://nginx.org/en/download.html)nginx命令react打包文件放置nginx 配置 运行效果nginx踩坑根目录配置 nginx 下载 根据上面的版本找到适合自己的 nginx版本&#xff0c;我目前是环境是 windows&#xff0c;所以下载 稳定版本。 nginx命令 在下载的ngin…

数学建模-判断数据是否服从正态分布

大样本用qq图 >1000 皮尔逊相关系数需要正态性检验&#xff0c;利用上面三种方法其中一种 斯皮尔曼相关系数不用正态性检验

Claude2轻松解决代码Bug的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

GPT-4最新细节曝光:从架构、基础设施、训练数据集、成本、视觉到MoE

OpenAI保持GPT-4架构封闭&#xff0c;不是因为对人类的某种存在风险&#xff0c;而是因为他们所构建的内容是可复制的。实际上&#xff0c;我们预计Google、Meta、Anthropic、Inflection、Character、Tencent、ByteDance、Baidu等公司在短期内将拥有与GPT-4一样甚至更强大的模型…

T100简易的查询作业功能开发

一、自定义作业维护 首先打开作业【azzi310】,进行作业的新增。 这是一个空白的作业界面,我们需要填写的地方也就那么几个。 查询单id:注册一个查询单 【四个字母三个数字】比如这里我们cxmq101.查询单名称:给这个查询起一个名字最大查询笔数:可以自己定义报表是否自己录…

数据库,数据仓库,数据湖

数据仓库四层分层 ODS——原始数据层&#xff1a;存放原始数据 ODS层即操作数据存储&#xff0c;是最接近数据源中数据的一层&#xff0c;数据源中的数据&#xff0c;经过抽取、洗净、传输&#xff0c;也就说传说中的ETL之后&#xff0c;装入本层&#xff1b;一般来说ODS层的数…

JavaScript初识

ECMAScript和JavaScript到底是什么关系&#xff1f; 简单来说&#xff0c;ECMAScript是JavaScript语言的国际标准&#xff0c;JavaScript是ECMAScript的实现。 一、第一个程序&#xff1a;hello word 二、JavaScript的几种常见写法&#xff1a; 1、将JavaScript写在标签上 2、…

应用上下文能否获取-spring13

我们能否通过web层通过spring容器去获得serive&#xff0c;然后serive内部Dao再去注入 这句话的意思是去加载xml配置文件&#xff0c;去加载spring容器&#xff0c;每次都要创建&#xff0c;太麻烦了&#xff0c;影响性能 最好的方法&#xff1a;应用上下文或者spring容器创建一…

DCL与延迟初始化(单例)

前言 在Java多线程程序中&#xff0c;有时候需要采用延迟初始化来降低初始化类和创建对象的开销。 第一种&#xff08;存在问题&#xff09; public class UnsafeLazyInitialization {private static Instance instance;public static Instance getInstance() {if (instance …

零基础如何自学成为网络安全工程师

前言 一份网络攻防渗透测试的学习路线&#xff0c;不藏私了&#xff01; &#x1f449; 【一帮助安全学习一】&#x1f448;这里自取256G网络安全自学资料 1、学习编程语言(phpmysqljshtml) 原因&#xff1a; phpmysql可以帮助你快速的理解B/S架构是怎样运行的&#xff0c…

【AI底层逻辑】——篇章5(上):机器学习算法之回归分类

目录 引入 一、何为机器学习 1、定规则和学规则 2、算法的定义 二、机器学习算法 1、常见学习方法 2、回归 3、分类 续下篇... 往期精彩&#xff1a; 引入 计算机发明初&#xff0c;专家通过将专业知识和经验梳理成规则输入计算机程序&#xff0c;但是这样跟不上知识…

IT技术培训班:搭乘学习快车的抉择

引言&#xff1a; 在IT技术学习的道路上&#xff0c;我们常常会被推荐各种五花八门的技术培训班。它们通过各种宣传手段向我们展示着美好的未来和无限的机会。然而&#xff0c;我们又应该如何看待这些培训班呢&#xff1f;在培训班里学技术真的有用吗&#xff1f;本文将从不同角…

【Java进阶之路】HashMap源码分析(JDK1.8)

概述 JDK 1.8 对 HashMap 进行了比较大的优化&#xff0c;底层实现由之前的 “数组链表” 改为 “数组链表红黑树”&#xff0c;本文就 HashMap 的几个常用的重要方法和 JDK 1.8 之前的死循环问题展开学习讨论。 JDK 1.8 的 HashMap 的数据结构如下图所示&#xff0c;当链表节…

Docker 替代方案:适用于 SaaS 应用程序的 10 种 Docker 替代方案

Docker技术已经在基础设施管理领域引起了革命性的变化&#xff0c;以至于Docker现在已经成为容器的代名词。重要的是要理解&#xff0c;所有的Docker都是容器&#xff0c;但并非所有的容器都是Docker。虽然Docker是最常用的容器技术&#xff0c;但还有其他几种替代Docker的选择…

积分兑换小程序项目总结

1. 项目概述 背景&#xff1a;中标项目&#xff0c;第三方公司做会员福利&#xff0c;以积分的形式发放。目标&#xff1a;给固定的钱&#xff0c;积分兑完&#xff0c;周期两个月。需求&#xff1a;固定会员能及时线上兑换积分。解决方案&#xff1a;开发微信小程序在线兑换。…

Nexpose v6.6.203 for Linux Windows - 漏洞扫描

Nexpose v6.6.203 for Linux & Windows - 漏洞扫描 Rapid7 Vulnerability Management, Release Jul 05, 2023 请访问原文链接&#xff1a;https://sysin.org/blog/nexpose-6/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.o…