紹介
tauri+vue3+pinia2+veplus をベースにしたクロスプラットフォーム、デスクトップ向け汎用バックエンド管理システムアプリケーションテンプレート TauriAdmin。



tauri-admin はクロスプラットフォーム技術 Tauri Rust と vite4.x を統合し、PC版バックエンドシステムプログラム exe テンプレートを構築します。

マルチウィンドウ管理、国際化多言語対応、ルート認証検証/ルートキャッシュ、よく使われる業務機能モジュールをサポート。

技術スタック
- エディタ:VScode
- 技術フレームワーク:tauri+vite4+vue3+pinia2+vue-router
- UI コンポーネントライブラリ:ve-plus(vue3 ベースのPC向けUIコンポーネントライブラリ)
- スタイル処理:sass^1.63.6
- チャートコンポーネント:echarts^5.4.2
- 国際化ソリューション:vue-i18n^9.2.2
- エディタコンポーネント:wangeditor^4.7.15
- ローカルキャッシュ:pinia-plugin-persistedstate^3.1.0

プロジェクトには3つのよく使われるレイアウトテンプレートスタイルが組み込まれており、好みに応じて自由に切り替えられます。お好みのテンプレートをカスタマイズすることも可能です。
プロジェクト構成ディレクトリ

プロジェクト全体は tauri-cli スキャフォールディングを使用して vue3 プロジェクトを構築しています。プロジェクトテンプレートの作成やマルチウィンドウに興味がある方は、以前の共有記事をご覧ください。
https://www.cnblogs.com/xiaoyan2017/p/16812092.html




tauri vue3 によるマルチウィンドウのカプセル化
multiwins ディレクトリに新しい index.js ファイルを作成し、tauri のマルチウィンドウをカプセル化して管理します。

// ウィンドウ作成設定
export const windowConfig = {
label: null, // ウィンドウのユニークなラベル
title: "", // ウィンドウタイトル
url: "", // ルートアドレスURL
width: 1000, // ウィンドウ幅
height: 640, // ウィンドウ高さ
minWidth: null, // ウィンドウ最小幅
minHeight: null, // ウィンドウ最小高さ
x: null, // 画面左端からのX座標
y: null, // 画面最上部からのY座標
center: true, // ウィンドウ中央配置
resizable: true, // リサイズ可否
maximized: false, // ウィンドウ最大化
decorations: false, // ウィンドウの装飾枠・ナビゲーションバー表示
alwaysOnTop: false, // ウィンドウ常に最前面
fileDropEnabled: false, // ファイルドラッグ&ドロップ無効
visible: false, // ウィンドウ非表示
};
tauri が提供する WebviewWindow メソッドを使用して新しいウィンドウインスタンスを作成します。label はウィンドウのユニークな識別子であり、メインウィンドウに切り替える必要がある場合は、label に "main" キーワードを含める必要があります。後で "main" キーワードがあるかどうかを検索して、メインウィンドウかどうかを判断します。
/**
* @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, // ウィンドウのユニークなラベル
title: "", // ウィンドウタイトル
url: "", // ルートアドレスURL
width: 1000, // ウィンドウ幅
height: 640, // ウィンドウ高さ
minWidth: null, // ウィンドウ最小幅
minHeight: null, // ウィンドウ最小高さ
x: null, // 画面左端からのX座標
y: null, // 画面最上部からのY座標
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();
});
// ...
}
}





メインテンプレートレイアウト
プロジェクトには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">
<component :is="LayoutConfig[config.layout]" />
</div>
</template>




ルート設定
tauri プロジェクトでは vue-router を使用してルート遷移管理を行います。

/**
* ルート設定 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";
// モジュールルートを一括インポート
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 ==> タブを固定するか(タブバーで閉じられない)
* */
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;
状態管理 pinia
現在、vue3 プロジェクトでは pinia を使用した状態管理が推奨されていますが、vuex も引き続き使用可能です。

/**
* 状態管理設定 Pinia
* @author YXY
*/
import { createPinia } from "pinia";
// pinia ローカル永続化ストレージをインポート
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;
ローカル永続化ストレージは、pinia プラグイン pinia-plugin-persistedstate を使用して機能を実現しています。





vue-i18n 国際化ソリューション
tauri-admin は中国語・英語・繁体字の3言語設定をサポートしています。

/**
* 国際化設定 VueI18n
* @author YXY
*/
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+pinia を使用してクライアントバックエンド管理システムテンプレートのインスタンスを実装する方法についての共有でした。
