electron31-380玩彩网官网入口
:原创electron31 vite5 element-plus
实战桌面端后台管理系统。
- 框架技术:vite^5.3.4 vue^3.4.31 vue-router^4.4.0
- 跨端框架:electron^31.3.0
- ui组件库:element-plus^2.7.8
- 状态管理:pinia^2.2.0
- 多语言方案:vue-i18n@9
- 图表组件:echarts^5.5.1
- markdown编辑器:md-editor-v3^4.18.0
- 模拟数据:mockjs^1.1.0
- 打包工具:electron-builder^24.13.3
- electron vite桥接插件:vite-plugin-electron^0.28.7
使用vite5.x搭建前端模板,整合最新跨平台技术electron31。采用vue3 setup语法开发。
- 基于最新前端技术栈vite5、vue3、electron31.x、elementplus、vue-i18n、echarts
- 支持中英文/繁体三种语言
- 支持动态权限路由、多页签缓存路由
- 封装多窗口管理器,内置4种通用布局模板、自由切换风格
- 整合通用的表格、表单、列表、图表、编辑器、错误处理等模块
- 高颜值ui界面、轻量级模块化、高定制性
目前electron31-viteadmin已经同步到我的原创作品集。有需要的话可以去下载使用。
提供了4种不同风格的布局模板。
/**
* 通用布局模板
* @author andy q:282310962
*/
<script setup>
import { appstate } from '@/pinia/modules/app'
// 引入布局模板
import classic from './template/classic/index.vue'
import columns from './template/columns/index.vue'
import vertical from './template/vertical/index.vue'
import horizontal from './template/horizontal/index.vue'
const appstate = appstate()
const layoutmap = {
'classic': classic,
'columns': columns,
'vertical': vertical,
'horizontal': horizontal
}
</script>
<template>
<div class="vuadmin__container" :style="{'--themeskin': appstate.config.skin}">
<component :is="layoutmap[appstate.config.layout]" />
</div>
</template>
/**
* 多语言配置
* @author yxy
*/
import { createi18n } from 'vue-i18n'
import { appstate } 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 appstate = appstate()
const lang = appstate.lang || langval
appstate.setlang(lang)
const i18n = createi18n({
legacy: false,
locale: lang,
messages: {
'en': enus,
'zh-cn': zhcn,
'zh-tw': zhtw
}
})
app.use(i18n)
}
/**
* 动态图表hook
*/
import { onmounted, onbeforeunmount, ref } from 'vue'
import * as echarts from 'echarts'
import elementresizedetectormaker from 'element-resize-detector'
export function useecharts(el, options) {
let chartel
let chartref = ref(null)
let erd = elementresizedetectormaker()
const resizehandle = () => {
chartel && chartel.resize()
}
onmounted(() => {
if(el?.value) {
chartel = echarts.init(el.value)
chartel.setoption(options)
chartref.value = chartel
}
erd.listento(el.value, resizehandle)
})
onbeforeunmount(() => {
chartel.dispose()
erd.removelistener(el.value, resizehandle)
})
return chartref
}
<script setup>
import { ref, computed } from 'vue'
import { isobject, isarray, isimg } from '@/utils'
import { appstate } from '@/pinia/modules/app'
import { useroutes } from '@/hooks/useroutes'
const props = defineprops({
// 菜单模式(vertical|horizontal)
mode: { type: string, default: 'vertical' },
// 是否开启一级路由菜单
rootrouteenable: { type: boolean, default: true },
// 是否暗黑模式
dark: { type: boolean }
})
import submenu from './submenu.vue'
// 引入主路由表
import routes from '@/router/modules/main.js'
const appstate = appstate()
const { route, getactiveroute, getcurrentrootroute, gettreeroutes } = useroutes()
const activeroute = computed(() => getactiveroute(route))
const rootroute = computed(() => getcurrentrootroute(route))
const treeroutes = computed(() => gettreeroutes(routes))
const filterroutes = computed(() => {
if(props.rootrouteenable) {
return treeroutes.value
}
// 过滤一级路由菜单
return treeroutes.value.find(item => item.path === rootroute.value && item.children)?.children
})
</script>
<template>
<div class="vu__menubar" :class="{'is-dark': dark, 'is-collapsed': mode == 'vertical' && appstate.config.collapsed}">
<el-menu class="vu__menus" :default-active="activeroute" :mode="mode" :collapse="appstate.config.collapsed">
<submenu
v-for="route in filterroutes"
:key="route.path"
:item="route"
:rootroute="rootroute"
:rootrouteenable="rootrouteenable"
/>
</el-menu>
</div>
</template>
调用非常简单。
<menus :rootrouteenable="false" />
<menus rootrouteenable :dark="true" />
<menus mode="horizontal" :dark="true" />
<template>
<div class="vu__tabview">
<el-tabs
v-model="activetab"
class="vu__tabview-tabs"
@tab-change="changetabs"
@tab-remove="removetab"
>
<el-tab-pane
v-for="(item, index) in tablist"
:key="index"
:name="item.path"
:closable="!item?.meta?.isaffix"
>
<template #label>
<el-dropdown ref="dropdownref" trigger="contextmenu" :id="item.path" @visible-change="handledropdownchange($event, item.path)" @command="handledropdowncommand($event, item)">
<span class="vu__tabview-tabs__label">
<span>{{$t(item?.meta?.title)}}</span>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="refresh" :icon="refresh">{{$t('tabview__contextmenu-refresh')}}</el-dropdown-item>
<el-dropdown-item command="close" :icon="close" :disabled="item.meta.isaffix">{{$t('tabview__contextmenu-close')}}</el-dropdown-item>
<el-dropdown-item command="closeother" :icon="switch">{{$t('tabview__contextmenu-closeother')}}</el-dropdown-item>
<el-dropdown-item command="closeleft" :icon="darrowleft">{{$t('tabview__contextmenu-closeleft')}}</el-dropdown-item>
<el-dropdown-item command="closeright" :icon="darrowright">{{$t('tabview__contextmenu-closeright')}}</el-dropdown-item>
<el-dropdown-item command="closeall" :icon="circleclosefilled">{{$t('tabview__contextmenu-closeall')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-tab-pane>
</el-tabs>
</div>
</template>
el-dropdown
组件控制每次只显示一个右键下拉菜单。
<script setup>
import { onmounted, ref, computed, watch, nexttick } from 'vue'
import { userouter, useroute } from 'vue-router'
import { usei18n } from 'vue-i18n'
import { refresh, close, switch, darrowleft, darrowright, circleclosefilled } from '@element-plus/icons-vue'
import { isobject, isimg } from '@/utils'
import { uselink } from '@/hooks/uselink'
import { appstate } from '@/pinia/modules/app'
const router = userouter()
const route = useroute()
const { jump } = uselink()
const { locale } = usei18n()
let { config: { keepalive, tabroutes, cacheroutes }, updateconfig } = appstate()
const dropdownref = ref()
const activetab = ref(route.path)
const tablist = ref(tabroutes)
// 新增选项卡
const addtab = () => {
const index = tablist.value.findindex(item => item?.path === activetab.value)
if(index == -1) {
tablist.value.push({
path: route?.path,
name: route?.name,
meta: {
...route?.meta,
}
})
}
updateconfig('tabroutes', tablist.value)
updatecacheroutes()
}
// 删除选项卡
const removetab = (path) => {
const index = tablist.value.findindex(item => item?.path === path)
if(index > -1) {
tablist.value.splice(index, 1)
updatetabs(tablist.value)
}
}
// 删除左侧选项卡
const removelefttab = (path) => {
const index = tablist.value.findindex(item => item?.path === path)
if(index > -1) {
tablist.value = tablist.value.filter((item, i) => item?.meta?.isaffix || i >= index)
updatetabs(tablist.value)
}
}
// 删除右侧选项卡
const removerighttab = (path) => {
const index = tablist.value.findindex(item => item?.path === path)
if(index > -1) {
tablist.value = tablist.value.filter((item, i) => item?.meta?.isaffix || i <= index)
updatetabs(tablist.value)
}
}
// 删除其它选项卡
const removeothertab = (path) => {
tablist.value = tablist.value.filter(item => item?.meta?.isaffix || item?.path === path)
updatetabs(tablist.value)
}
// 删除全部
const removealltab = (path) => {
tablist.value = tablist.value.filter(item => item?.meta?.isaffix)
updatetabs(tablist.value)
}
// 更新选项卡
const updatetabs = (tabs) => {
updateconfig('tabroutes', tabs)
updatecacheroutes()
const nexttab = tabs[tabs.length 1] || tabs[tabs.length - 1]
if(!nexttab) return
jump(nexttab?.path)
}
// 更新keep-alive缓存
const updatecacheroutes = () => {
let caches = tablist.value.filter(item => keepalive || item?.meta?.iskeepalive).map(item => item.name)
updateconfig('cacheroutes', caches)
}
// 清空keep-alive缓存
const clearcacheroutes = () => {
updateconfig('cacheroutes', [])
}
// 点击选项卡
const changetabs = (path) => {
jump(path)
}
// 右键菜单更新
const handledropdownchange = (visible, name) => {
// 控制每次只显示一个右键菜单
if(!visible) return
dropdownref.value.foreach(item => {
if(item.id === name) return
item.handleclose()
})
}
// 右键菜单命令
const handledropdowncommand = (cmd, item) => {
const path = item?.path
switch(cmd) {
case 'refresh':
router.go(0)
break
case 'close':
removetab(path)
break
case 'closeleft':
removelefttab(path)
break
case 'closeright':
removerighttab(path)
break
case 'closeother':
removeothertab(path)
break
case 'closeall':
removealltab()
break
}
}
watch(() => route.path, () => {
activetab.value = route.path
addtab()
}, {
immediate: true
})
</script>
博客:electron31-vite5-chat:原创vue3 electron31 pinia2客户端聊天exe应...
博客:vite-vue3os:基于vue3 pinia2 arco-design仿macos桌面os系统后台模...
本作品采用《cc 协议》,转载必须注明作者和本文链接
本文为原创文章,未经作者允许不得转载,欢迎大家一起交流 qq(282310962) wx(xy190310)