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语法开发。

  1. 基于最新前端技术栈vite5、vue3、electron31.x、elementplus、vue-i18n、echarts
  2. 支持中英文/繁体三种语言
  3. 支持动态权限路由、多页签缓存路由
  4. 封装多窗口管理器,内置4种通用布局模板、自由切换风格
  5. 整合通用的表格、表单、列表、图表、编辑器、错误处理等模块
  6. 高颜值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)
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
网站地图