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
特色
- 最新跨端技术tauri1.4 rust webview2
- 前端技术栈vite4、vue3、pinia、vue-router、vue-i18n
- 支持中文/英文/繁体多语言解决方案
- 支持动态路由权限验证
- 支持路由缓存功能/tabs控制切换路由页面
- 内置多个模板布局风格
- 搭配轻量级vue3组件库veplus
项目搭建结构目录
整个项目基于tauri脚手架创建,全部采用vue3 setup
语法糖编码开发。
tauri-admin支持创建多窗口,如上图从登录窗口切换到主窗口。
tauri多窗口封装
在src目录下新建一个管理tauri多窗口文件夹。
创建新窗口配置参数
// 创建窗口参数配置
export const windowConfig = {label: null, // 窗口唯一labeltitle: '', // 窗口标题url: '', // 路由地址urlwidth: 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, // 窗口唯一labeltitle: '', // 窗口标题url: '', // 路由地址urlwidth: 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) returnawait appWindow.show()await appWindow.unminimize()await appWindow.setFocus()})// 隐藏窗体await listen('win-hide', async(event) => {if(appWindow.label.indexOf('main') == -1) returnawait 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 || langValconst 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仿微信聊天室