TauriAdmin is a cross-end universal backend system template solution
Based on tauri rust webview2 and integrating vite4 , we build the desktop vue3 management backend template TauriVue3Admin. Supports multi-window switching management, vue-i18n multi-language, dynamic routing permissions, common business function modules and dynamic routing cache and other functions.
Use technology
- Coding tool: Vscode
- Frame technology: tauri+vite4+vue3+pinia+vue-router
- UI component library: ve-plus (based on vue3 lightweight UI component library)
- Style processing: sass^1.63.6
- Chart component: echarts^5.4.2
- Internationalization solution: vue-i18n^9.2.2
- Editor component: wangeditor^4.7.15
- Local cache: pinia-plugin-persistedstate^3.1.0
feature
- The latest cross-end technology tauri1.4 rust webview2
- Front-end technology stack vite4, vue3, pinia, vue-router, vue-i18n
- Support Chinese/English/Traditional multi-language solutions
- Support dynamic routing authority verification
- Support route caching function/tabs control to switch routing pages
- Built-in multiple template layout styles
- Paired with lightweight vue3 component library veplus
Project construction structure directory
The entire project is created based on tauri scaffolding and is entirely vue3 setup
developed using syntax sugar coding.
tauri-admin supports the creation of multiple windows, switching from the login window to the main window as shown above.
tauri multi-window package
Create a new management tauri multi-window folder in the src directory.
Create new window configuration parameters
// 创建窗口参数配置
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 handles some rendering process/main process communication.
/**
* 处理渲染器进程到主进程的异步通信
*/
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 entrance 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")
layout template
tauri-admin has built-in 3 commonly used templates.
<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>
Routing vue-router configuration
/**
* 路由配置
* @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 state management pinia configuration
The state management used in the project has been replaced by pinia from the previous vuex.
/**
* 状态管理 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 internationalization solution
tauri-admin internationalization and multi-language use vue-i18n
plug-ins to implement functions.
# 安装插件
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)
}
Supports Chinese-English/Traditional Chinese switching, and can customize multi-language package configuration files.
tauri.conf.json configuration
Configure some packaging parameters, window startup configuration parameters and tray parameters.
{
"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/[email protected]",
"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
}
}
}
The above is some sharing of tauri+vue3's creation of cross-end desktop management background applications. I hope you like it~~
If you find it helpful, you can also follow the official account and share some project examples from time to time.
Finally, two latest example projects are attached
Electron25+vue3 desktop imitation ChatGPT session template example
Vue3+Tauri cross-end chat example | tauri imitates WeChat chat room