Vite-Admin后台管理系统|vite4+vue3+pinia前端后台框架实例
Posted web前端项目案例实战
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vite-Admin后台管理系统|vite4+vue3+pinia前端后台框架实例相关的知识,希望对你有一定的参考价值。
基于vite4.x+vue3+pinia前端后台管理系统解决方案ViteAdmin。
前段时间分享了一篇vue3自研pc端UI组件库VEPlus。这次带来最新开发的基于vite4+vue3+pinia技术栈搭配ve-plus组件库构建的中后台权限管理系统框架。支持vue-i18n国际化多语言、动态路由鉴权、4种布局模板及tab页面缓存等功能。
技术栈
- 编码工具:Cursor+Sublime
- 框架技术:vite4+vue3+pinia+vue-router
- UI组件库:ve-plus (基于vue3自研ui组件库)
- 样式处理:sass^1.58.3
- 图表组件:echarts^5.4.2
- 国际化方案:vue-i18n^9.2.2
- 富文本编辑器组件:wangeditor^4.7.15
- markdown编辑器:md-editor-v3^2.11.0
- 数据模拟:mockjs^1.1.0
功能点
- 最新前端技术栈vite4、vue3、pinia、vue-router、vue-i18n、ve-plus。
- 支持中文/英文/繁体多语言模式切换。
- 支持表格单选/多选、边框/隔行换色、横向/纵向虚拟滚动条等功能。
- 搭配高颜值的vue3-plus组件库,风格更加统一。
- 内置多个模板布局样式
- 支持动态路由权限控制
- 支持tabs动态路由缓存
- 高效率开发,整个框架已经搭建完毕,只需定制化相应模块即可。
项目页面结构
整体采用vue3 setup语法糖模式开发,搭配ve-plus轻量级组件库,使得界面清新且运行极速。
效果图
Vue3 UI VEPlus组件
ve-plus:基于vue3开发的pc端组件库,包含了40+常用的功能组件,易于上手。
veplus整合了vue3.js开发的两个独立插件vue3-layer弹窗、vue3-scrollbar虚拟滚动条组件。
快速安装
npm install ve-plus -S cnpm install ve-plus -S yarn add ve-plus
具体的使用方法,大家可以去看看这篇分享文章。
https://www.cnblogs.com/xiaoyan2017/p/17170454.html
Vite-Admin布局模块
公共布局文件在layouts目录,提供了4种经典的布局模板。
<script setup> import computed from \'vue\' import appStore from \'@/store/modules/app\' // 引入布局模板 import Classic from \'./layout/classic/index.vue\' import Columns from \'./layout/columns/index.vue\' import Vertical from \'./layout/vertical/index.vue\' import Transverse from \'./layout/transverse/index.vue\' const store = appStore() const config = computed(() => store.config) const LayoutConfig = classic: Classic, columns: Columns, vertical: Vertical, transverse: Transverse </script> <template> <div class="veadmin__container" :style="\'--themeSkin\': store.config.skin"> <component :is="LayoutConfig[config.layout]" /> </div> </template>
主模板main.vue提供了Permission权限控制及KeepAlive路由缓存。
<!-- 主缓存模板 --> <script setup> import ref from \'vue\' import useRoutes from \'@/hooks/useRoutes\' import tabsStore from \'@/store/modules/tabs\' import Permission from \'@/components/Permission.vue\' import Forbidden from \'@/views/error/forbidden.vue\' const route = useRoutes() const store = tabsStore() </script> <template> <Scrollbar autohide gap="2"> <div class="ve__layout-main__wrapper"> <!-- 路由鉴权 --> <Permission :roles="route?.meta?.roles"> <template #tips> <Forbidden /> </template> <!-- 路由缓存 --> <router-view v-slot=" Component "> <transition name="ve-slide-right" mode="out-in" appear> <KeepAlive :include="store.cacheViews"> <component v-if="store.reload" :is="Component" :key="route.path" /> </KeepAlive> </transition> </router-view> </Permission> </div> </Scrollbar> </template>
自定义路由菜单RouteMenu
如上图:路由菜单组件只需传入配置参数,即可切换不同的模式。
<RouteMenu :rootRouteEnable="false" /> <RouteMenu rootRouteEnable collapsed background="#292d3e" backgroundHover="#353b54" color="rgba(235,235,235,.7)" /> <RouteMenu mode="horizontal" background="#292d3e" backgroundHover="#353b54" color="rgba(235,235,235,.7)" />
RouteMenu.vue模板
<!-- 路由菜单 --> <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 \'@/store/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: String, // 滑过背景色 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 ) console.log(\'根路由地址::>>\', rootRoute.value) console.log(\'过滤后路由地址::>>\', menuFilterOptions.value) watch(() => route.path, () => nextTick(() => activeKey.value = getActiveRoute(route) ) ) // 批量渲染图标 const batchRenderIcon = (option) => return h(Icon, name: option?.meta?.icon) // 批量渲染标题 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>
vue-i18n国际化解决方案
vite-admin支持中英文/繁体三种语言模式,使用 "vue-i18n": "^9.2.2" 组件。
/** * 国际化配置 * @author YXY */ import createI18n from \'vue-i18n\' import appStore from \'@/store/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)
Lang.vue模板
<script setup> import ref from \'vue\' import useI18n from \'vue-i18n\' import appStore from \'@/store/modules/app\' const locale = useI18n() const store = appStore() const langVal = ref(locale.value) const langOptions = ref([ key: "zh-CN", label: "简体中文", key: "zh-TW", label: "繁体字", key: "en", label: "英文", ]) const changeLang = () => // 设置locale语言 locale.value = langVal.value store.lang = locale.value // store.setLang(locale.value) </script> <template> <Dropdown v-model="langVal" :options="langOptions" placement="bottom" @change="changeLang"> <div class="toolbar__item"><Icon name="ve-icon-lang" size="20" cursor /></div> <template #label="item"> <div> item.label <span style="color: #999; font-size: 12px;">item.key</span> </div> </template> </Dropdown> </template>
Vue3动态图表Hooks
vite-admin支持动态图表,使用 "echarts": "^5.4.2" 组件。
/** * 动态图表Hooks * @author YXY */ import onMounted, onBeforeUnmount, ref from \'vue\' import * as echarts from \'echarts\' import useResizeObserver from \'ve-plus\' export function useEcharts(node, options) let chartNode let chartRef = ref(null) const resizeHandle = () => chartNode && chartNode.resize() onMounted(() => if(node.value) chartNode = echarts.init(node.value) chartNode.setOption(options) chartRef.value = chartNode ) onBeforeUnmount(() => chartNode.dispose() ) // 自适应图表 useResizeObserver(node, resizeHandle) return chartRef
通过useResizeObserver函数,支持图表自适应大小。
网站动态标题title
通过监听路由route更改,动态设置网站标题。
/** * 设置网站标题 * @author YXY */ import watch, unref from \'vue\' import useRouter from \'vue-router\' import useI18n from \'vue-i18n\' export function useTitle() const VITE_APP_TITLE = import.meta.env const currentRoute = useRouter() const t, locale = useI18n() watch( () => [currentRoute.value.path, locale.value], () => console.log(\'开始监听标题变化........\') const route = unref(currentRoute) const title = route?.meta?.title ? `$t(route?.meta?.title) - $VITE_APP_TITLE` : VITE_APP_TITLE console.log(\'监听标题\', title) document.title = title , immediate: true )
动态路由缓存
ve-admin支持keepalive路由缓存功能。使用 pinia 替代 vuex 状态管理,使用 pinia-plugin-persistedstate 持久化存储。
https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
TabsView.vue模板
<script setup> import ref, computed, watch, nextTick, h from \'vue\' import useRouter, useRoute from \'vue-router\' import useI18n from \'vue-i18n\' import appStore from \'@/store/modules/app\' import tabsStore from \'@/store/modules/tabs\' const t = useI18n() const router = useRouter() const route = useRoute() const app = appStore() const store = tabsStore() const tabKey = ref(route.path) const tabOptions = computed(() => store.tabViews) // 滚动到当前路由 const scrollToActiveRoute = () => nextTick(() => const activeRef = scrollbarRef.value.scrollbarWrap.querySelector(\'.actived\').offsetLeft scrollbarRef.value.scrollTo(left: activeRef, top: 0, behavior: \'smooth\') ) // 监听路由(增加标签/缓存) watch(() => route.path, () => tabKey.value = route.path const params = path: route.path, name: route.name, meta: ...route.meta store.addTabs(params) scrollToActiveRoute() , immediate: true ) // 右键菜单 const scrollbarRef = ref() const selectedTab = ref() const contextmenuRef = ref() const contextmenuOptions = ref([ key: \'refresh\', icon: \'ve-icon-reload\', label: \'tabview__contextmenu-refresh\' , key: \'close\', icon: \'ve-icon-close\', label: \'tabview__contextmenu-close\' , key: \'closeLeft\', icon: \'ve-icon-logout\', label: \'tabview__contextmenu-closeleft\' , key: \'closeRight\', icon: \'ve-icon-logout1\', label: \'tabview__contextmenu-closeright\' , key: \'closeOther\', icon: \'ve-icon-retweet\', label: \'tabview__contextmenu-closeother\' , key: \'closeAll\', icon: \'ve-icon-close-square\', label: \'tabview__contextmenu-closeall\' , ]) const handleRenderLabel = (option) => return t(option?.label) // 是否第一个标签 const isFirstTab = () => return selectedTab.value.path === store.tabViews[0].path || selectedTab.value.path === \'/home/index\' // 是否最后一个标签 const isLastTab = () => return selectedTab.value.path === store.tabViews[store.tabViews.length - 1].path const openContextMenu = (tab, e) => selectedTab.value = tab contextmenuOptions.value[1].disabled = tab.meta?.isAffix contextmenuOptions.value[2].disabled = isFirstTab() contextmenuOptions.value[3].disabled = isLastTab() // 设置坐标 contextmenuRef.value.setPos(e.clientX, e.clientY) contextmenuRef.value.show() const changeContextMenu = (v) => if(v.key == \'refresh\') if(tabKey.value !== selectedTab.value.path) router.push(selectedTab.value.path) store.reloadTabs() return else if(v.key == \'close\') store.removeTabs(selectedTab.value) else if(v.key == \'closeLeft\') store.removeLeftTabs(selectedTab.value) else if(v.key == \'closeRight\') store.removeRightTabs(selectedTab.value) else if(v.key == \'closeOther\') store.removeOtherTabs(selectedTab.value) else if(v.key == \'closeAll\') store.clearTabs() updateTabRoute() // 跳转更新路由 const updateTabRoute = () => const lastTab = store.tabViews.slice(-1)[0] if(lastTab && lastTab.path) router.push(lastTab.path) else router.push(\'/\') // 切换tab const changeTab = (tab) => router.push(tab.path) // 关闭tab const closeTab = (tab) => store.removeTabs(tab) updateTabRoute() </script> <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" @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>
/** * 状态管理 Pinia */ import createPinia from \'pinia\' // 引入pinia本地持久化存储 import piniaPluginPersistedstate from \'pinia-plugin-persistedstate\' const pinia = createPinia() pinia.use(piniaPluginPersistedstate) export default pinia
/** * 标签栏缓存状态管理 * 在setup store中 * ref() 就是 state 属性 * computed() 就是 getters * function() 就是 actions * @author YXY * Q:282310962 WX:xy190310 */ import ref, nextTick from \'vue\' import useRoute from \'vue-router\' import defineStore from \'pinia\' import appStore from \'@/store/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) updateCacheViews() // 移除左侧标签 const removeLeftTabs = (route) => const index = tabIndex(route) if(index > -1) tabViews.value = tabViews.value.filter((item, i) => item?.meta?.isAffix || i >= index) updateCacheViews() // 移除右侧标签 const removeRightTabs = (route) => const index = tabIndex(route) if(index > -1) tabViews.value = tabViews.value.filter((item, i) => item?.meta?.isAffix || i <= index) updateCacheViews() // 移除其它标签 const removeOtherTabs = (route) => tabViews.value = tabViews.value.filter(item => item?.meta?.isAffix || item?.path === route?.path) updateCacheViews() // 移除所有标签 const clearTabs = () => tabViews.value = tabViews.value.filter(item => item?.meta?.isAffix) 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: storage: localStorage, paths: [\'tabViews\', \'cacheViews\'] )
vite.config.js配置文件
import defineConfig, loadEnv from \'vite\' import vue from \'@vitejs/plugin-vue\' import resolve from \'path\' import wrapEnv from \'./src/utils/env\' // https://vitejs.dev/config/ export default defineConfig(( mode ) => const viteEnv = loadEnv(mode, process.cwd()) const env = wrapEnv(viteEnv) return plugins: [vue()], // base: \'/\', // mode: \'development\', // development|production /*构建选项*/ build: // minify: \'esbuild\', // 打包方式 esbuild(打包快)|terser // chunkSizeWarningLimit: 2000, // 打包大小警告 // rollupOptions: // output: // chunkFileNames: \'assets/js/[name]-[hash].js\', // entryFileNames: \'assets/js/[name]-[hash].js\', // assetFileNames: \'assets/[ext]/[name]-[hash].[ext]\', // // , esbuild: // 打包去除 console.log 和 debugger drop: env.VITE_DROP_CONSOLE ? [\'console\', \'debugger\'] : [] , /*开发服务器选项*/ server: // 端口 port: env.VITE_PORT, // 是否浏览器自动打开 open: env.VITE_OPEN, // 开启https https: env.VITE_HTTPS, // 代理配置 proxy: // ... , resolve: // 设置别名 alias: \'@\': resolve(__dirname, \'src\'), \'@assets\': resolve(__dirname, \'src/assets\'), \'@components\': resolve(__dirname, \'src/components\'), \'@views\': resolve(__dirname, \'src/views\'), // 解决vue-i18n警告提示:You are running the esm-bundler build of vue-i18n. \'vue-i18n\': \'vue-i18n/dist/vue-i18n.cjs.js\' )
Okra,基于 vite4.x+pinia+vePlus 开发后台管理系统模板就分享到这里,希望对大家有些帮助哈~~
Electron-Vite2-MacUI桌面管理框架|electron13+vue3.x仿mac桌面UI
基于vue3.0.11+electron13仿制macOS桌面UI管理系统ElectronVue3MacUI。
前段时间有分享一个vue3结合electron12开发后台管理系统项目。今天要分享的是最新研发的跨平台仿macOS桌面UI管理框架。使用了最新前端技术electron13+vite2.3+vue3搭建开发。支持多窗口、动态壁纸、程序坞DOCK菜单、可拖拽等功能。
一、实现技术
- 编辑器:Vscode
- 框架技术:Vite2.3.4+Vue3.0.11+Vuex4+VueRouter@4
- 跨端框架:Electron13.0.1
- 打包工具:vue-cli-plugin-electron-builder
- UI组件库:Element-Plus^1.0.2 (饿了么vue3组件库)
- 弹窗组件:MacLayer (vue3弹窗v3layer改进版)
- 图表组件:Echarts^5.1.1
- 模拟请求:Mockjs1.1.0
二、功能特性
✅经典的图标+dock菜单模式
✅流畅的操作体验
✅可拖拽桌面+程序坞dock菜单
✅符合macOS big sur操作窗口管理
✅丰富的视觉效果,自定义桌面个性壁纸
✅可视化创建多窗口,支持拖拽/缩放/最大化,可传入自定义组件页面。
三、项目分层结构
项目中的弹窗分为vue3自定义弹窗组件和electron新开弹窗窗口。
◆ Electron桌面公共布局模板
菜单栏位于屏幕顶部。程序坞Dock菜单位于屏幕底部。位于二者之间的称为桌面。
<!-- //Main主模块模板 --> <template> <div class="macui__wrapper" :style="{\'--themeSkin\': store.state.skin}"> <div v-if="!route.meta.isNewin" class="macui__layouts-main flexbox flex-col"> <!-- //顶部导航 --> <div class="layout__topbar"> <TopNav /> </div> <div class="layout__workpanel flex1 flexbox" @contextmenu="handleCtxMenu"> <div class="panel__mainlayer flex1 flexbox" style="margin-bottom: 70px;"> <DeskMenu /> </div> </div> <!-- //底部Dock菜单 --> <Dock /> </div> <router-view v-else class="macui__layouts-main flexbox flex-col macui__filter"></router-view> </div> </template>
◆ Vue3+Electron实现无边框导航条
如上图:顶部导航条均是自定义组件实现功能。同时支持自定义标题、背景及文字颜色等功能。
下拉菜单则是使用的element-plus中的Dropdown组件实现功能。
<template> <WinBar bgcolor="rgba(29,29,32,.7)" color="#fff" zIndex="1000" title="false"> <template #menu> ... <el-dropdown placement="bottom-start" @command="handleMenuClicked"> <a class="menu menu-label">首页</a> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="2-1">首页</el-dropdown-item> <el-dropdown-item command="2-2">控制台</el-dropdown-item> <el-dropdown-item command="2-3">自定义面包屑导航</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> ... </template> <template #wbtn> <MsgMenu /> <a class="menu" title="换肤" @click="handleSkinWin"><i class="iconfont el-icon-magic-stick"></i></a> <a class="menu" :class="{\'on\': isAlwaysOnTop}" :title="isAlwaysOnTop ? \'取消置顶\' : \'置顶\'" @click="handleAlwaysTop"><i class="iconfont icon-ding"></i></a> <a class="menu" title="搜索"><i class="iconfont el-icon-search"></i></a> <Setting /> <a class="menu menu-label">{{currentDate}}</a> <el-divider direction="vertical" /> <Avatar @logout="handleLogout" /> <el-divider direction="vertical" /> </template> </WinBar> </template>
对于导航条组件的一些实现方式,大家如果感兴趣可以去看看之前的分享文章哈~
https://www.cnblogs.com/xiaoyan2017/p/14449570.html
◆ Vite2+Electron实现dock动效菜单
底部dock可自适应布局,支持拖拽排序,采用了毛玻璃模糊背景效果。
<template> <div class="macui__dock"> <div class="macui__dock-wrap macui__filter" ref="dockRef"> <a class="macui__dock-item"><span class="tooltips">appstore</span><img src="/static/mac/appstore.png" /></a> <a class="macui__dock-item active"><span class="tooltips">launchpad</span><img src="/static/mac/launchpad.png" /></a> ... </div> </div> </template>
// 拖拽Dock菜单 const dragDockMenu = () => { Sortable.create(dockRef.value, { handle: \'.macui__dock-item\', filter: \'.macui__dock-filter\', animation: 200, delay: 0, onEnd({ newIndex, oldIndex }) { console.log(\'新索引:\', newIndex) console.log(\'旧索引:\', oldIndex) } }) } // 打开地图 const openMaps = () => { createWin({ title: \'地图\', route: \'/map\', width: 1000, height: 500, }) } // 打开日历 const openCalendar = () => { createWin({ title: \'日历\', route: \'/calendar\', width: 500, height: 500, resize: false, }) }
.macui__dock {display: flex; justify-content: center; height: 60px; transform: translateX(-50%); position: fixed; left: 50%; bottom: 5px; z-index: 10010;} .macui__dock-wrap {background: rgba(255,255,255,.3); box-shadow: 0 1px 1px rgba(29,29,32,.15); border-radius: 15px; display: flex; align-items: center; height: 60px; padding: 0 10px;} .macui__dock-item {color: #fff; cursor: pointer; display: inline-block; position: relative;} .macui__dock-item .tooltips {display: none; background: rgba(0,0,0,.3); border-radius: 5px; padding: 4px 8px; text-align: center; width: 100%; position: absolute;} .macui__dock-item img {height: 50px; width: 50px; object-fit: cover; transition: all .2s;} .macui__dock-item.active:after {content: \'\'; background: rgba(29,29,32,.9); border-radius: 50%; margin-left: -2px; height: 4px; width: 4px; position: absolute; left: 50%; bottom: -2px;} .macui__dock-item:hover .tooltips {display: block; opacity: 1; top: -70px; animation: animTooltips .3s;} .macui__dock-item:hover img {margin: 0 2em; transform: scale(2) translateY(-10px);}
◆ Vue3仿mac弹窗组件
项目中如上图的弹窗组件,都是使用vue3自定义弹窗组件v3layer改进版实现功能。
v3layer支持超过30+种参数自定义配置,支持拖拽、四角缩放、全屏等功能,并且新增了支持动态传入组件页面功能。
import Home from \'@/views/home/index.vue\' import ControlPanel from \'@/views/home/dashboard.vue\' import CustomTpl from \'@/views/home/customTpl.vue\' import Table from \'@/views/component/table/custom.vue\' import Form from \'@/views/component/form/all.vue\' import UserSetting from \'@/views/setting/manage/user/index.vue\' import Ucenter from \'@/views/setting/ucenter.vue\' const deskmenu = [ { type: \'component\', icon: \'el-icon-monitor\', title: \'首页\', component: Home, }, { type: \'component\', icon: \'icon-gonggao\', title: \'控制面板\', component: ControlPanel, }, { type: \'component\', img: \'/static/mac/reminders.png\', title: \'自定义组件模板\', component: CustomTpl, area: [\'600px\', \'360px\'], }, { type: \'iframe\', img: \'/static/vite.png\', title: \'vite.js官方文档\', component: \'https://cn.vitejs.dev/\', }, { type: \'component\', icon: \'el-icon-s-grid\', title: \'表格\', component: Table, }, // ... ]
// 点击菜单 const handleMenuClicked = (menu) => { let icon = menu.icon ? `<i class="iconfont ${menu.icon}"></i>` : menu.img ? `<img src="${menu.img}" />` : \'\' let title = menu.title ? `<div class="macui__customTitle">${icon}${menu.title}</div>` : \'标题\' v3layer({ type: menu.type || null, layerStyle: menu.style || \'\', customClass: \'macui__deskLayer\', title: title, content: menu.component || \'<div style="color:red;margin-top:50px;">嗷嗷!您似乎忘记了填充内容。</div>\', area: menu.area || [\'1000px\', \'550px\'], shade: false, xclose: true, maximize: menu.maximize != false ? true : false, resize: menu.resize != false ? true : false, fullscreen: menu.fullscreen || false, zIndex: 500, topmost: true, }) }
大家如果对v3layer的实现感兴趣的话,可以去看看之前的这篇分享文章。
https://www.cnblogs.com/xiaoyan2017/p/14221729.html
◆ Vite2+Electron项目打包配置
基于vite2和electron搭建的项目,如果需要打包成.exe文件,需要新建一个 electron-builder.json 的配置文件。
{ "productName": "electron-macui", "appId": "cc.xiaoyan.electron-macui", "copyright": "Copyright © 2021-present", "author": "Power By XiaoYan | Q:282310962 WX:xy190310" "compression": "maximum", "asar": false, "extraResources": [ { "from": "./resource", "to": "resource" } ], "nsis": { "oneClick": false, "allowToChangeInstallationDirectory": true, "perMachine": true, "deleteAppDataOnUninstall": true, "createDesktopShortcut": true, "createStartMenuShortcut": true, "shortcutName": "ElectronMacUI" }, "win": { "icon": "./resource/shortcut.ico", "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}", "target": [ { "target": "nsis", "arch": ["ia32"] } ] }, "mac": { "icon": "./resource/shortcut.icns", "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}" }, "linux": { "icon": "./resource", "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}" } }
好了,以上就是基于vite2.x+electron13开发跨端仿制macOS桌面UI后台管理系统,希望对大家有所帮助哈!
最后附上一个vue3+electron跨平台桌面端仿QQ聊天实例
https://www.cnblogs.com/xiaoyan2017/p/14454624.html
以上是关于Vite-Admin后台管理系统|vite4+vue3+pinia前端后台框架实例的主要内容,如果未能解决你的问题,请参考以下文章