Introduction
A cross-platform, desktop-side general admin system application template TauriAdmin based on tauri+vue3+pinia2+veplus.



tauri-admin uses cross-end technology Tauri Rust integrated with vite4.x to build a computer-side background system exe template.

Supports multi-window management, internationalization and multi-language, route permission verification/route caching, commonly used business function modules.

Tech Stack
- Editor: VScode
- Framework: tauri+vite4+vue3+pinia2+vue-router
- UI Component Library: ve-plus (vue3-based desktop UI component library)
- Styling: sass^1.63.6
- Chart Component: echarts^5.4.2
- Internationalization: vue-i18n^9.2.2
- Editor Component: wangeditor^4.7.15
- Local Cache: pinia-plugin-persistedstate^3.1.0

The project includes three commonly used layout template styles, which you can switch freely based on your preference, or customize your own template.
Project Directory Structure

The entire project is built using the tauri-cli scaffold to create a vue3 project. If you are interested in creating project templates and multi-window features, feel free to check out previous sharing articles.
https://www.cnblogs.com/xiaoyan2017/p/16812092.html




tauri vue3 Encapsulate Multi-Window
Create an index.js file in the multiwins directory for tauri multi-window encapsulation management.

// Window creation configuration
export const windowConfig = {
label: null, // Unique window label
title: "", // Window title
url: "", // Route URL
width: 1000, // Window width
height: 640, // Window height
minWidth: null, // Minimum window width
minHeight: null, // Minimum window height
x: null, // X coordinate relative to screen left
y: null, // Y coordinate relative to screen top
center: true, // Center window on screen
resizable: true, // Allow resizing
maximized: false, // Maximize window
decorations: false, // Window frame and nav bar
alwaysOnTop: false, // Keep window on top
fileDropEnabled: false, // Disable system drag-and-drop
visible: false, // Hide window
};
Create a new window instance using the WebviewWindow method provided by tauri. Since the label is the unique identifier for a window, when you need to set it as the main window, ensure the label contains the 'main' keyword. Later, it will check for the 'main' keyword to determine if it's the main window.
/**
* @desc Window management
* @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";
// Window creation parameter configuration
export const windowConfig = {
label: null, // Unique window label
title: "", // Window title
url: "", // Route URL
width: 1000, // Window width
height: 640, // Window height
minWidth: null, // Minimum window width
minHeight: null, // Minimum window height
x: null, // X coordinate relative to screen left
y: null, // Y coordinate relative to screen top
center: true, // Center window on screen
resizable: true, // Allow resizing
maximized: false, // Maximize window
decorations: false, // Window frame and nav bar
alwaysOnTop: false, // Keep window on top
fileDropEnabled: false, // Disable system drag-and-drop
visible: false, // Hide window
};
class Windows {
constructor() {
// Main window
this.mainWin = null;
}
// Create a new window
async createWin(options) {
console.log("-=-=-=-=-=Start creating window");
const args = Object.assign({}, windowConfig, options);
// Check if window exists
const existWin = getAll().find((w) => w.label == args.label);
if (existWin) {
console.log("Window already exists>>", existWin);
if (existWin.label.indexOf("main") == -1) {
// Custom handling...
}
}
// Is it the main window
if (args.label.indexOf("main") > -1) {
console.log("This window is the main window");
// Custom handling...
}
// Create window object
let win = new WebviewWindow(args.label, args);
// Maximize if needed
if (args.maximized && args.resizable) {
win.maximize();
}
// Window created/failed
win.once("tauri://created", async () => {
console.log("window create success!");
await win?.show();
});
win.once("tauri://error", async () => {
console.log("window create error!");
});
}
// Get a window
getWin(label) {
return WebviewWindow.getByLabel(label);
}
// Get all windows
getAllWin() {
return getAll();
}
// Start listening to main process events
async listen() {
console.log("——+——+——+——+——+Start listening to windows");
// Create new window
await listen("win-create", (event) => {
this.createWin(event.payload);
});
// Show window
await listen("win-show", async (event) => {
if (appWindow.label.indexOf("main") == -1) return;
await appWindow.show();
await appWindow.unminimize();
await appWindow.setFocus();
});
// Hide window
await listen("win-hide", async (event) => {
if (appWindow.label.indexOf("main") == -1) return;
await appWindow.hide();
});
// Close window
await listen("win-close", async (event) => {
await appWindow.close();
});
// Exit application
await listen("win-exit", async (event) => {
setWin("logout");
await exit();
});
// ...
}
}





Main Template Layout
The project includes three commonly used layout template styles: traditional columns, vertical menu, and horizontal menu.


<script setup>
import { computed } from 'vue'
import { appStore } from '@/pinia/modules/app'
// Import layout templates
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">
<component :is="LayoutConfig[config.layout]" />
</div>
</template>




Route Configuration
Use vue-router for route navigation management in the tauri project.

/**
* Route configuration 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";
// Batch import modules routes
const modules = import.meta.glob("./modules/*.js", { eager: true });
const patchRoutes = Object.keys(modules)
.map((key) => modules[key].default)
.flat();
/**
* @description Dynamic route parameter configuration
* @param path ==> menu path
* @param redirect ==> redirect address
* @param component ==> view file path
* Menu info (meta)
* @param meta.icon ==> menu icon
* @param meta.title ==> menu title
* @param meta.activeRoute ==> active route (default empty route.path)
* @param meta.rootRoute ==> root route to highlight (default empty)
* @param meta.roles ==> page permissions ['admin', 'dev', 'test']
* @param meta.breadcrumb ==> custom breadcrumb navigation [{meta:{...}, path: '...'}]
* @param meta.isAuth ==> requires authentication
* @param meta.isHidden ==> hide page
* @param meta.isFull ==> full screen page
* @param meta.isKeepAlive ==> cache page
* @param meta.isAffix ==> affix tab (tab cannot be closed)
* */
const routes = [
// Home page
{
path: "/",
redirect: "/home",
},
// Error module
{
path: "/:pathMatch(.*)*",
component: () => import("@views/error/404.vue"),
meta: {
title: "page__error-notfound",
},
},
...patchRoutes,
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// Global hook interception
router.beforeEach((to, from, next) => {
// Show loading indicator
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)) {
// Route permission check
appWindow?.show();
next("/error/forbidden");
loading.close();
Notify({
title: "Access denied!",
description: `<span style="color: #999;">Current role ${store.roles} does not have permission to operate. Please contact administrator to authorize.</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;
State Management with Pinia
For vue3 projects, Pinia is recommended for state management, though Vuex can still be used.

/**
* State management configuration Pinia
* @author YXY
*/
import { createPinia } from "pinia";
// Import pinia local persistent storage
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;
Local persistent storage is implemented using the pinia plugin pinia-plugin-persistedstate.





vue-i18n Internationalization Solution
tauri-admin supports Chinese, English, and Traditional Chinese language configurations.

/**
* Internationalization configuration VueI18n
* @author YXY
*/
import { createI18n } from "vue-i18n";
import { appStore } from "@/pinia/modules/app";
// Import language configurations
import enUS from "./en-US";
import zhCN from "./zh-CN";
import zhTW from "./zh-TW";
// Default language
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 configuration
{
"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
}
}
}
OK, that concludes the sharing of examples for implementing a client-side admin system template using tauri+vue3+pinia.
