tauri-vue3-admin backend management exe program template

tauri-vue3-admin backend management exe program template

Based on tauri+vue3+pinia2+veplus, a cross-desktop general backend management system application template TauriAdmin.

Last updated 7/27/2023 10:14 PM
前端加油栈
9 min read
Category
Frontend
Tags
Vue Tauri

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.

Keep Exploring

Related Reading

More Articles
Same tag 11/6/2024

Why My Blog Website Returned to Blazor

The development of the blog website has gone through many hardships, with nearly 10 versions including MVC, Vue, Go, etc. Now it has returned to Blazor and adopted static SSR, resulting in a significant speed increase and successful launch.

Continue Reading
Same tag 10/16/2023

.NET Toolbox: Open-source, free pure frontend tool website, exploring 10 major tool categories and 73 real-time online gadgets

Dotnet Toolbox is a pure frontend, open-source, and free tool website. Over the weekend, I referred to the open-source project it-tools, localized the website interface text into Chinese, and redeployed the site. The website has 10 major tool categories and provides 73 real-time online gadgets. Developed with Vue3, the Dotnet Toolbox has unique features. This article details some of these featured tools and briefly shares how to deploy your own tool website. If you are interested in tool websites, why not come and learn about Dotnet Toolbox!

Continue Reading