<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Tauri-Admin通用后臺管理系統|tauri+vue3+pinia桌面端后臺EXE

      基于tauri+vite4+pinia2跨端后臺管理系統應用實例TauriAdmin

      tauri-admin 基于最新跨端技術 Tauri Rust webview2 整合 Vite4 構建桌面端通用后臺管理解決方案。搭載輕量級ve-plus組件庫、支持多窗口切換管理、vue-i18n多語言包、動態路由權限、常用業務功能模塊、3種布局模板及動態路由緩存等功能。

      使用技術

      • 編碼工具:vscode
      • 框架技術:tauri+vite^4.2.1+vue^3.2.45+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

      目前tauri已經迭代到1.4,如果大家對tauri+vue3創建多窗口項目感興趣,可以去看看之前的這篇分享文章。

      http://www.rzrgm.cn/xiaoyan2017/p/16812092.html

      功能特性

      1. 使用跨端技術tauri1.4
      2. 最新前端技術棧vite4、vue3、pinia、vue-router、vue-i18n
      3. 支持中文/英文/繁體多語言解決方案
      4. 支持動態路由權限驗證
      5. 支持路由緩存功能/tabs控制切換路由頁面
      6. 內置多個模板布局風格
      7. 搭配輕量級vue3組件庫veplus
      8. 高效開發,支持增刪定制化頁面模塊

      項目結構

      使用tauri腳手架搭配vite4構建項目,整體采用vue3 setup語法編碼開發。

      主入口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布局模板

      提供了三種常見的布局模板,大家也可以定制喜歡的模板樣式。

      <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>

      路由/pinia狀態管理

      如上圖:配置router路由信息。

      /**
       * 路由配置
       * @author YXY    Q:282310962
       */
      
      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)',
              onOpen: () => {
                  console.log('開啟loading')
              },
              onClose: () => {
                  console.log('關閉loading')
              }
          })
          
          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進行狀態管理。

      /**
       * 狀態管理 Pinia util
       * @author YXY
       */
      
      import { createPinia } from 'pinia'
      // 引入pinia本地持久化存儲
      import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
      
      const pinia = createPinia()
      pinia.use(piniaPluginPersistedstate)
      
      export default pinia

      自定義路由菜單

      項目中的三種模板提供了不同的路由菜單。均是基于Menu組件封裝的RouteMenu菜單。

      <script setup>
        import { ref, computed, h, watch, nextTick } from 'vue'
        import { useI18n } from 'vue-i18n'
        import { Icon, useLink } from 've-plus'
        import { useRoutes } from '@/hooks/useRoutes'
        import { appStore } from '@/pinia/modules/app'
      
        // 引入路由集合
        import mainRoutes from '@/router/modules/main.js'
      
        const props = defineProps({
          // 菜單模式(vertical|horizontal)
          mode: { type: String, default: 'vertical' },
          // 是否開啟一級路由菜單
          rootRouteEnable: { type: Boolean, default: true },
          // 是否要收縮
          collapsed: { type: Boolean, default: false },
      
          // 菜單背景色
          background: { type: String, default: 'transparent' },
          // 滑過背景色
          backgroundHover: String,
          // 菜單文字顏色
          color: String,
          // 菜單激活顏色
          activeColor: String
        })
      
        const { t } = useI18n()
        const { jumpTo } = useLink()
        const { route, getActiveRoute, getCurrentRootRoute, getTreeRoutes } = useRoutes()
        const store = appStore()
      
        const rootRoute = computed(() => getCurrentRootRoute(route))
        const activeKey = ref(getActiveRoute(route))
        const menuOptions = ref(getTreeRoutes(mainRoutes))
        const menuFilterOptions = computed(() => {
          if(props.rootRouteEnable) {
            return menuOptions.value
          }
          // 過濾掉一級菜單
          return menuOptions.value.find(item => item.path == rootRoute.value && item.children)?.children
        })
      
        watch(() => route.path, () => {
          nextTick(() => {
            activeKey.value = getActiveRoute(route)
          })
        })
      
        // 批量渲染圖標
        const batchRenderIcon = (option) => {
          return h(Icon, {name: option?.meta?.icon ?? 've-icon-verticleleft'})
        }
      
        // 批量渲染標題
        const batchRenderLabel = (option) => {
          return t(option?.meta?.title)
        }
      
        // 路由菜單更新
        const handleUpdate = ({key}) => {
          jumpTo(key)
        }
      </script>
      
      <template>
        <Menu
          class="veadmin__menus"
          v-model="activeKey"
          :options="menuFilterOptions"
          :mode="mode"
          :collapsed="collapsed && store.config.collapse"
          iconSize="18"
          key-field="path"
          :renderIcon="batchRenderIcon"
          :renderLabel="batchRenderLabel"
          :background="background"
          :backgroundHover="backgroundHover"
          :color="color"
          :activeColor="activeColor"
          @change="handleUpdate"
          style="border: 0;"
        />
      </template>

       

      Menu組件支持橫向/豎向排列,調用非常簡單。

      <RouteMenu
          :rootRouteEnable="false"
          backgroundHover="#f1f8fb"
          activeColor="#24c8db"
      />
      
      <RouteMenu
          rootRouteEnable
          collapsed
          background="#193c47"
          backgroundHover="#1a5162"
          color="rgba(235,235,235,.7)"
          activeColor="#24c8db"
          :collapsedIconSize="20"
      />
      
      <RouteMenu
          mode="horizontal"
          background="#193c47"
          backgroundHover="#1a5162"
          color="rgba(235,235,235,.7)"
          activeColor="#24c8db"
          arrowIcon="ve-icon-caretright"
      />

      tauri-admin多語言配置

      tauri-vue3-admin項目使用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)
      }

      路由緩存功能

      項目支持配置路由頁面緩存功能。可以在全局pinia/modules/app.js中配置,也可以在router配置項meta中配置isKeepAlive: true。

      <template>
        <div v-if="app.config.tabsview" class="veadmin__tabsview">
          <Scrollbar ref="scrollbarRef" mousewheel>
            <ul class="tabview__wrap">
              <li
                v-for="(tab,index) in tabOptions" :key="index"
                :class="{'actived': tabKey == tab.path}"
                @click="changeTab(tab)"
                @contextmenu.prevent="openContextMenu(tab, $event)"
              >
                <Icon class="tab-icon" :name="tab.meta?.icon" />
                <span class="tab-title">{{$t(tab.meta?.title)}}</span>
                <Icon v-if="!tab.meta?.isAffix" class="tab-close" name="ve-icon-close" size="12" @click.prevent.stop="closeTab(tab)" />
              </li>
            </ul>
          </Scrollbar>
        </div>
        <!-- 右鍵菜單 -->
        <Dropdown
          ref="contextmenuRef"
          trigger="manual"
          :options="contextmenuOptions"
          fixed="true"
          :render-label="handleRenderLabel"
          @change="changeContextMenu"
          style="height: 0;"
        />
      </template>

      import { ref, nextTick } from 'vue'
      import { useRoute } from 'vue-router'
      import { defineStore } from 'pinia'
      import { appStore } from '@/pinia/modules/app'
      
      export const tabsStore = defineStore('tabs', () => {
              const currentRoute = useRoute()
              const store = appStore()
      
              /*state*/
              const tabViews = ref([]) // 標簽欄列表
              const cacheViews = ref([]) // 緩存列表
              const reload = ref(true) // 刷新標識
      
              // 判斷tabViews某個路由是否存在
              const tabIndex = (route) => {
                  return tabViews.value.findIndex(item => item?.path === route?.path)
              }
      
              /*actions*/
              // 新增標簽
              const addTabs = (route) => {
                  const index = tabIndex(route)
                  if(index > -1) {
                      tabViews.value.map(item => {
                          if(item.path == route.path) {
                              // 當前路由緩存
                              return Object.assign(item, route)
                          }
                      })
                  }else {
                      tabViews.value.push(route)
                  }
      
                  // 更新keep-alive緩存
                  updateCacheViews()
              }
      
              // 移除標簽
              const removeTabs = (route) => {
                  const index = tabIndex(route)
                  if(index > -1) {
                      tabViews.value.splice(index, 1)
                  }
      
                  // 更新keep-alive緩存
                  updateCacheViews()
              }
      
              // 移除左側標簽
              const removeLeftTabs = (route) => {
                  const index = tabIndex(route)
                  if(index > -1) {
                      tabViews.value = tabViews.value.filter((item, i) => item?.meta?.isAffix || i >= index)
                  }
      
                  // 更新keep-alive緩存
                  updateCacheViews()
              }
      
              // 移除右側標簽
              const removeRightTabs = (route) => {
                  const index = tabIndex(route)
                  if(index > -1) {
                      tabViews.value = tabViews.value.filter((item, i) => item?.meta?.isAffix || i <= index)
                  }
      
                  // 更新keep-alive緩存
                  updateCacheViews()
              }
      
              // 移除其它標簽
              const removeOtherTabs = (route) => {
                  tabViews.value = tabViews.value.filter(item => item?.meta?.isAffix || item?.path === route?.path)
      
                  // 更新keep-alive緩存
                  updateCacheViews()
              }
      
              // 移除所有標簽
              const clearTabs = () => {
                  tabViews.value = tabViews.value.filter(item => item?.meta?.isAffix)
      
                  // 更新keep-alive緩存
                  updateCacheViews()
              }
      
              // 更新keep-alive緩存
              const updateCacheViews = () => {
                  cacheViews.value = tabViews.value.filter(item => store.config.keepAlive || item?.meta?.isKeepAlive).map(item => item.name)
                  console.log('cacheViews緩存路由>>:', cacheViews.value)
              }
      
              // 移除keep-alive緩存
              const removeCacheViews = (route) => {
                  cacheViews.value = cacheViews.value.filter(item => item !== route?.name)
              }
      
              // 刷新路由
              const reloadTabs = () => {
                  removeCacheViews(currentRoute)
                  reload.value = false
                  nextTick(() => {
                      updateCacheViews()
                      reload.value = true
                      document.documentElement.scrollTo({ left: 0, top: 0 })
                  })
              }
      
              // 清空緩存
              const clear = () => {
                  tabViews.value = []
                  cacheViews.value = []
              }
      
              return {
                  tabViews,
                  cacheViews,
                  reload,
      
                  addTabs,
                  removeTabs,
                  removeLeftTabs,
                  removeRightTabs,
                  removeOtherTabs,
                  clearTabs,
                  reloadTabs,
                  clear
              }
          },
          // 本地持久化存儲(默認存儲localStorage)
          {
              // persist: true
              persist: {
                  // key: 'tabsState',
                  storage: localStorage,
                  paths: ['tabViews', 'cacheViews']
              }
          }
      )

      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
          }
        }
      }

      Cargo.toml配置

      [package]
      name = "tauri-admin"
      version = "0.0.0"
      description = "基于tauri+vue3+vite4+pinia輕量級桌面端后臺管理Tauri-Admin"
      authors = "andy <282310962@qq.com>"
      license = ""
      repository = ""
      edition = "2023"
      
      # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
      
      [build-dependencies]
      tauri-build = { version = "1.4", features = [] }
      
      [dependencies]
      tauri = { version = "1.4", features = ["api-all", "icon-ico", "icon-png", "system-tray"] }
      serde = { version = "1.0", features = ["derive"] }
      serde_json = "1.0"
      
      [features]
      # this feature is used for production builds or when `devPath` points to the filesystem
      # DO NOT REMOVE!!
      custom-protocol = ["tauri/custom-protocol"]

      OK,基于tauri+vue3跨端后臺管理系統就分享到這里。希望對大家有所幫助哈~~

      最新原創自研tauri2.0+vue3+element-plus客戶端后臺管理系統

      http://www.rzrgm.cn/xiaoyan2017/p/18467237

      最后附上兩個最新開發的Electron和uniapp跨端項目實例

      http://www.rzrgm.cn/xiaoyan2017/p/17468074.html

      http://www.rzrgm.cn/xiaoyan2017/p/17507581.html

       

      posted @ 2023-07-14 00:15  xiaoyan2017  閱讀(2451)  評論(2)    收藏  舉報
      友情鏈接: UP主小店B站
      主站蜘蛛池模板: 中文字幕av无码免费一区| 中国熟妇毛多多裸交视频| 九九热在线精品视频观看| 国产免费午夜福利在线播放 | 一区二区三区精品自拍视频| 国产亚洲无线码一区二区| japanese边做边乳喷| 99热久久这里只有精品| 久热久视频免费在线观看| 国产午夜精品久久久久免费视| 蜜桃一区二区三区在线看| 九九热在线精品视频观看| 大地资源网第二页免费观看| 在线观看人成视频免费| 九九热在线免费观看视频| 无码人妻丰满熟妇奶水区码| 午夜福利理论片高清在线| 国产自拍一区二区三区在线| 成人福利一区二区视频在线| 久热这里只有精品12| 国产精品伦人视频免费看| 人妻一区二区三区三区| 国精偷拍一区二区三区| 久久人妻精品国产| 国产永久免费高清在线| 99re热视频这里只精品| 99精品国产一区二区三区不卡| 亚洲AV午夜电影在线观看 | 国产伦一区二区三区精品| 99er热精品视频| 免费无码AV一区二区波多野结衣 | 国精品91人妻无码一区二区三区| 任我爽精品视频在线播放| 国产高清亚洲一区亚洲二区| 四虎在线成人免费观看| 在线天堂中文新版www| 成人一区二区三区激情视频| 免费国产又色又爽又黄的网站| 骚虎三级在线免费播放| 国产精品99中文字幕| 国产无套粉嫩白浆在线|