HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

uni-admin,H5发布后,报错“未找到集合[xxx]对应的schema”的一种问题原因分享

uni-admin

当然,造成该问题的原因,最好还是看一下对应的schema是不是上传到了unicloud,或者写错了名字

我的故障现象是:在本地测试时,完全没问题,但发布后,就会报错未找到集合[xxx]对应的schema,就偶尔几个表,其他大部分也没事。而且检查云数据库里,确实该表是存在的。

我最终发现,问题出在,我建的数据库表超过100个了,至于怎么出来101个表,我也不知道。

解决办法是删没用的表,删到100个以内,问题就解决了。

希望能帮到大家,这问题耽误了好几个小时。

继续阅读 »

当然,造成该问题的原因,最好还是看一下对应的schema是不是上传到了unicloud,或者写错了名字

我的故障现象是:在本地测试时,完全没问题,但发布后,就会报错未找到集合[xxx]对应的schema,就偶尔几个表,其他大部分也没事。而且检查云数据库里,确实该表是存在的。

我最终发现,问题出在,我建的数据库表超过100个了,至于怎么出来101个表,我也不知道。

解决办法是删没用的表,删到100个以内,问题就解决了。

希望能帮到大家,这问题耽误了好几个小时。

收起阅读 »

vue3.5+tauri v2桌面版后台管理系统|vite5+tauri2+element-plus客户端后台模板

经过大半个月高强度实战开发,又一款原创跨平台新作tauri2.0+vue3+pinia2+elementPlus+mockjs电脑端通用权限后台管理系统,正式结束了。实现4种通用布局模板,支持vue-i18n国际化、面包屑导航、tab标签路由等功能。

tauri2.0-vue3admin桌面端管理系统|tauri2+vite5+element-plus后台EXE程序

img

img

使用技术

  • 开发工具:VScode
  • 技术框架:tauri2.0+vite^5.4.8+vue^3.5.11+vue-router^4.4.5
  • 状态管理:pinia^2.2.4
  • 存储服务:pinia-plugin-persistedstate^4.1.1
  • 组件库:element-plus^2.8.5
  • 图表组件:echarts^5.5.1
  • 国际化:vue-i18n^10.0.4
  • 富文本编辑器:@vueup/vue-quill^1.2.0
  • md编辑器:md-editor-v3^4.20.3
  • 模拟数据:mockjs^1.1.0
  • 预处理样式:sass^1.79.4

img

img

目前该项目Tauri2-Vue3Admin已经同步发布到我的原创作品集。

https://gf.bilibili.com/item/detail/1107226011

项目结构

img

img

img

img

tauri2-admin后台布局模板

img

<script setup>  
  import { appState } from '@/pinia/modules/app'  

  import Toolbar from '@/layouts/components/Toolbar.vue'  
  import Sidebar from '@/layouts/components/sidebar/index.vue'  
  import Menus from '@/layouts/components/menus/index.vue'  
  import Breadcrumb from '@/layouts/components/Breadcrumb.vue'  
  import Tabview from '@/layouts/components/Tabview.vue'  
  import Main from '@/layouts/components/Main.vue'  

  const appstate = appState()  
</script>  

<template>  
  <div class="vuadmin__layout flexbox flex-col">  
    <Toolbar />  

    <div class="vuadmin__layout-body flex1 flexbox">  
      <!-- 侧边栏 -->  
      <div class="vuadmin__layout-sidebar">  
        <Sidebar />  
      </div>  

      <!-- 菜单栏 -->  
      <div class="vuadmin__layout-menus" :class="{'hidden': appstate.config.collapsed}">  
        <el-scrollbar>  
          <Menus :rootRouteEnable="false" />  
        </el-scrollbar>  
      </div>  

      <!-- 右侧主内容区 -->  
      <div class="vuadmin__layout-main flex1 flexbox flex-col">  
        <!-- 面包屑导航 -->  
        <Breadcrumb v-if="appstate.config.breadcrumb" />  

        <!-- 标签页 -->  
        <Tabview v-if="appstate.config.tabview" />  

        <!-- 内容区 -->  
        <Main />  
      </div>  
    </div>  
  </div>  
</template>

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

tauri2-admin国际化配置

img

img

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

tauri2-admin实现自定义导航栏

img

<script setup>  
  import { ref, markRaw } from 'vue'  
  import { ElMessageBox } from 'element-plus'  
  import { QuestionFilled, SwitchButton } from '@element-plus/icons-vue'  
  import { getCurrentWindow } from '@tauri-apps/api/window'  
  import { listen } from '@tauri-apps/api/event'  
  import { exit } from '@tauri-apps/plugin-process'  

  import { isTrue } from '@/utils'  
  import { authState } from '@/pinia/modules/auth'  

  const authstate = authState()  

  const props = defineProps({  
    color: String,  
    // 窗口是否可最小化  
    minimizable: {type: [Boolean, String], default: true},  
    // 窗口是否可最大化  
    maximizable: {type: [Boolean, String], default: true},  
    // 窗口是否可关闭  
    closable: {type: [Boolean, String], default: true},  
    // 层级  
    zIndex: {type: [Number, String], default: 2024},  
  })  

  const hasMaximized = ref(false)  
  const isResizable = ref(true)  
  const isMaximizable = ref(true)  

  // 用户是否可以手动调整窗口大小  
  getCurrentWindow().isResizable().then(res => {  
    isResizable.value = res  
  })  
  // 窗口是否可以最大化  
  getCurrentWindow().isMaximizable().then(res => {  
    isMaximizable.value = res  
  })  
  // 初始监听窗口是否最大化  
  getCurrentWindow().isMaximized().then(res => {  
    hasMaximized.value = res  
  })  
  // 实时监听窗口是否最大化  
  listen('tauri://resize', async() => {  
    hasMaximized.value = await getCurrentWindow().isMaximized()  
  })  

  // 最小化  
  const handleWinMin = async() => {  
    await getCurrentWindow().minimize()  
  }  
  // 最大化/还原  
  const handleWinToggle = async() => {  
    await getCurrentWindow().toggleMaximize()  
  }  
  // 关闭  
  const handleWinClose = async() => {  
    const isMajor = getCurrentWindow().label.indexOf('main') > -1  
    if(isMajor) {  
      ElMessageBox.confirm('是否最小化到系统托盘,不退出程序?', '提示', {  
        type: 'warning',  
        icon: markRaw(QuestionFilled),  
        confirmButtonText: '残忍退出',  
        cancelButtonText: '最小化到托盘',  
        customStyle: {'width': '300px'},  
        draggable: true,  
        roundButton: true,  
        center: true,  
        buttonSize: 'small',  
        distinguishCancelAndClose: true,  
      }).then(async() => {  
        authstate.logout()  
        await exit()  
      }).catch(async(action) => {  
        if(action === 'cancel') {  
          await getCurrentWindow().hide()  
        }  
      })  
    }else {  
      await getCurrentWindow().close()  
    }  
  }  
</script>  

<template>  
  <div class="ev__winbtns flexbox flex-alignc vu__drag" :style="{'z-index': zIndex}">  
    <div class="ev__winbtns-actions flexbox flex-alignc vu__undrag" :style="{'color': color}">  
      <a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon iconfont elec-icon-min"></i></a>  
      <a v-if="isTrue(maximizable)" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">  
        <i class="wicon iconfont" :class="hasMaximized ? 'elec-icon-restore' : 'elec-icon-max'"></i>  
      </a>  
      <a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleWinClose"><i class="wicon iconfont elec-icon-quit"></i></a>  
    </div>  
  </div>  
</template>  

<style lang="scss" scoped>  
  @import './index.scss';  
</style>

tauri2-vue3admin自定义标签栏路由

img

img

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

img

tauri2+vue3实现自定义托盘右键菜单功能。

img

End,以上就是tauri2+vue3+pinia2实战开发桌面版后台管理系统模板的一些知识分享。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045381943
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

经过大半个月高强度实战开发,又一款原创跨平台新作tauri2.0+vue3+pinia2+elementPlus+mockjs电脑端通用权限后台管理系统,正式结束了。实现4种通用布局模板,支持vue-i18n国际化、面包屑导航、tab标签路由等功能。

tauri2.0-vue3admin桌面端管理系统|tauri2+vite5+element-plus后台EXE程序

img

img

使用技术

  • 开发工具:VScode
  • 技术框架:tauri2.0+vite^5.4.8+vue^3.5.11+vue-router^4.4.5
  • 状态管理:pinia^2.2.4
  • 存储服务:pinia-plugin-persistedstate^4.1.1
  • 组件库:element-plus^2.8.5
  • 图表组件:echarts^5.5.1
  • 国际化:vue-i18n^10.0.4
  • 富文本编辑器:@vueup/vue-quill^1.2.0
  • md编辑器:md-editor-v3^4.20.3
  • 模拟数据:mockjs^1.1.0
  • 预处理样式:sass^1.79.4

img

img

目前该项目Tauri2-Vue3Admin已经同步发布到我的原创作品集。

https://gf.bilibili.com/item/detail/1107226011

项目结构

img

img

img

img

tauri2-admin后台布局模板

img

<script setup>  
  import { appState } from '@/pinia/modules/app'  

  import Toolbar from '@/layouts/components/Toolbar.vue'  
  import Sidebar from '@/layouts/components/sidebar/index.vue'  
  import Menus from '@/layouts/components/menus/index.vue'  
  import Breadcrumb from '@/layouts/components/Breadcrumb.vue'  
  import Tabview from '@/layouts/components/Tabview.vue'  
  import Main from '@/layouts/components/Main.vue'  

  const appstate = appState()  
</script>  

<template>  
  <div class="vuadmin__layout flexbox flex-col">  
    <Toolbar />  

    <div class="vuadmin__layout-body flex1 flexbox">  
      <!-- 侧边栏 -->  
      <div class="vuadmin__layout-sidebar">  
        <Sidebar />  
      </div>  

      <!-- 菜单栏 -->  
      <div class="vuadmin__layout-menus" :class="{'hidden': appstate.config.collapsed}">  
        <el-scrollbar>  
          <Menus :rootRouteEnable="false" />  
        </el-scrollbar>  
      </div>  

      <!-- 右侧主内容区 -->  
      <div class="vuadmin__layout-main flex1 flexbox flex-col">  
        <!-- 面包屑导航 -->  
        <Breadcrumb v-if="appstate.config.breadcrumb" />  

        <!-- 标签页 -->  
        <Tabview v-if="appstate.config.tabview" />  

        <!-- 内容区 -->  
        <Main />  
      </div>  
    </div>  
  </div>  
</template>

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

tauri2-admin国际化配置

img

img

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

tauri2-admin实现自定义导航栏

img

<script setup>  
  import { ref, markRaw } from 'vue'  
  import { ElMessageBox } from 'element-plus'  
  import { QuestionFilled, SwitchButton } from '@element-plus/icons-vue'  
  import { getCurrentWindow } from '@tauri-apps/api/window'  
  import { listen } from '@tauri-apps/api/event'  
  import { exit } from '@tauri-apps/plugin-process'  

  import { isTrue } from '@/utils'  
  import { authState } from '@/pinia/modules/auth'  

  const authstate = authState()  

  const props = defineProps({  
    color: String,  
    // 窗口是否可最小化  
    minimizable: {type: [Boolean, String], default: true},  
    // 窗口是否可最大化  
    maximizable: {type: [Boolean, String], default: true},  
    // 窗口是否可关闭  
    closable: {type: [Boolean, String], default: true},  
    // 层级  
    zIndex: {type: [Number, String], default: 2024},  
  })  

  const hasMaximized = ref(false)  
  const isResizable = ref(true)  
  const isMaximizable = ref(true)  

  // 用户是否可以手动调整窗口大小  
  getCurrentWindow().isResizable().then(res => {  
    isResizable.value = res  
  })  
  // 窗口是否可以最大化  
  getCurrentWindow().isMaximizable().then(res => {  
    isMaximizable.value = res  
  })  
  // 初始监听窗口是否最大化  
  getCurrentWindow().isMaximized().then(res => {  
    hasMaximized.value = res  
  })  
  // 实时监听窗口是否最大化  
  listen('tauri://resize', async() => {  
    hasMaximized.value = await getCurrentWindow().isMaximized()  
  })  

  // 最小化  
  const handleWinMin = async() => {  
    await getCurrentWindow().minimize()  
  }  
  // 最大化/还原  
  const handleWinToggle = async() => {  
    await getCurrentWindow().toggleMaximize()  
  }  
  // 关闭  
  const handleWinClose = async() => {  
    const isMajor = getCurrentWindow().label.indexOf('main') > -1  
    if(isMajor) {  
      ElMessageBox.confirm('是否最小化到系统托盘,不退出程序?', '提示', {  
        type: 'warning',  
        icon: markRaw(QuestionFilled),  
        confirmButtonText: '残忍退出',  
        cancelButtonText: '最小化到托盘',  
        customStyle: {'width': '300px'},  
        draggable: true,  
        roundButton: true,  
        center: true,  
        buttonSize: 'small',  
        distinguishCancelAndClose: true,  
      }).then(async() => {  
        authstate.logout()  
        await exit()  
      }).catch(async(action) => {  
        if(action === 'cancel') {  
          await getCurrentWindow().hide()  
        }  
      })  
    }else {  
      await getCurrentWindow().close()  
    }  
  }  
</script>  

<template>  
  <div class="ev__winbtns flexbox flex-alignc vu__drag" :style="{'z-index': zIndex}">  
    <div class="ev__winbtns-actions flexbox flex-alignc vu__undrag" :style="{'color': color}">  
      <a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon iconfont elec-icon-min"></i></a>  
      <a v-if="isTrue(maximizable)" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">  
        <i class="wicon iconfont" :class="hasMaximized ? 'elec-icon-restore' : 'elec-icon-max'"></i>  
      </a>  
      <a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleWinClose"><i class="wicon iconfont elec-icon-quit"></i></a>  
    </div>  
  </div>  
</template>  

<style lang="scss" scoped>  
  @import './index.scss';  
</style>

tauri2-vue3admin自定义标签栏路由

img

img

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

img

tauri2+vue3实现自定义托盘右键菜单功能。

img

End,以上就是tauri2+vue3+pinia2实战开发桌面版后台管理系统模板的一些知识分享。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045381943
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

vue3+tauri2.0+element-plus桌面端exe聊天模板|vite5+tauri2仿QQ/微信客户端程序

vue3

趁着国庆假期,我的又一款原创重磅新作Vue3.5+Tauri2.0+Vite5.4+Pinia2+ElementPlus跨平台实战仿QQ/微信电脑端聊天Exe程序Vue3Tauri2Chat,正式的完结了。整体UI采用全新无边框透明圆角阴影窗体

Vue3+Tauri2.0聊天实例|tauri2+vite5+element-plus仿微信|tauri聊天应用

img

img

封装tauri2.x多开窗口管理、换肤壁纸、自定义系统托盘闪烁/右键菜单功能。实现聊天、联系人、收藏、朋友圈、短视频、我的等页面模块。

img

img

运用技术

  • 编码工具:Vscode
  • 技术框架:tauri2.0+vite^5.4+vue^3.5+vue-router^4.4.5
  • 状态管理:pinia^2.2.2
  • 本地存储插件:pinia-plugin-persistedstate^4.0.2
  • 组件库:element-plus^2.8.3
  • 富文本编辑器:@vueup/vue-quill^1.2.0
  • 样式预处理:sass^1.79.3
  • 视频滑动组件:swiper^11.1.14

img

img

img

img

img

img

img

img

img

img

目前tauri2-vue3chat聊天项目已经发布到我的原创作品集,有需要的话可以去瞅瞅~
https://gf.bilibili.com/item/detail/1107133011

tauri2-vue3chat项目布局模板

img

img

<template>  
  <div class="vu__chatbox">  
    <template v-if="!route?.meta?.isNewWin">  
      <div class="vu__container flexbox flex-alignc flex-justifyc">  
        <div class="vu__layout flexbox flex-col">  
          <div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>  
            <!-- 菜单栏 -->  
            <slot v-if="!route?.meta?.hideMenuBar" name="menubar">  
              <MenuBar />  
            </slot>  

            <!-- 侧边栏 -->  
            <div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">  
              <aside class="vu__layout-sidebar__body flexbox flex-col">  
                <slot name="sidebar">  
                  <SideBar />  
                </slot>  
              </aside>  
            </div>  

            <!-- 主内容区 -->  
            <div class="vu__layout-main flex1 flexbox flex-col">  
              <ToolBar v-if="!route?.meta?.hideToolBar" />  
              <router-view v-slot="{ Component, route }">  
                <keep-alive>  
                  <component :is="Component" :key="route.path" />  
                </keep-alive>  
              </router-view>  
            </div>  
          </div>  
        </div>  
      </div>  
    </template>  
    <template v-else>  
      <WinLayout />  
    </template>  
  </div>  
</template>

img

img

img

tauri2多窗口实践

img

// 朋友圈窗口  
const handleFzone = () => {  
  winCreate({  
    label: 'win-fzone',  
    url: '/win/fzone',  
    title: '朋友圈',  
    width: 500,  
    height: 695,  
    minWidth: 350,  
    minHeight: 500,  
    maximizable: false,  
  })  
}  

// 短视频窗口  
const handleFvideo = () => {  
  winCreate({  
    label: 'win-fvideo',  
    url: '/win/fvideo',  
    title: '短视频',  
    width: 575,  
    height: 675,  
    minWidth: 415,  
    minHeight: 545  
  })  
}  

// 换肤壁纸窗口  
const handleSkin = () => {  
  winCreate({  
    label: 'win-skin',  
    url: '/win/skin',  
    title: '壁纸',  
    width: 375,  
    height: 480,  
    resizable: false,  
    maximizable: false,  
    visible: true,  
  })  
}  

// 界面管理器  
const handleManageUI = () => {  
  winCreate({  
    label: 'win-manage',  
    url: '/win/manage',  
    title: '界面管理器',  
    width: 320,  
    height: 360,  
    resizable: false,  
    maximizable: false,  
  })  
}

之前有过一篇关于tauri2创建多窗口应用的分享文章,可以去看看。
基于Tauri2+Vite5搭建桌面端程序|tauri2+vue3多窗口|消息提醒|托盘闪烁

img

img

img

vue3+tauri2自定义透明圆角阴影窗体

img

.vu__chatbox {height: calc(100vh); padding: 5px; overflow: hidden;}  
.vu__layout {  
  background-color: #f5f5f5;  
  overflow: hidden;  
  height: 100%; width: 100%;  
  position: relative; z-index: 100;  
  border-radius: 8px;  
  box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.15),0 1px 5px -1px rgba(0, 0, 0, 0.1),0 2px 5px rgba(0, 0, 0, 0.1);  
}

img

<script setup>  
  /**  
   * tauri2.0自定义系统最大化/最小化/关闭  by andy  Q:282310962  
   */  

  // ...  

  const props = defineProps({  
    color: String,  
    // 窗口是否可最小化  
    minimizable: {type: [Boolean, String], default: true},  
    // 窗口是否可最大化  
    maximizable: {type: [Boolean, String], default: true},  
    // 窗口是否可关闭  
    closable: {type: [Boolean, String], default: true},  
    // 层级  
    zIndex: {type: [Number, String], default: 2024},  

    // 关闭前回调,会暂停实例关闭 function(done),done用于关闭  
        beforeClose: Function  
  })  

  const hasMaximized = ref(false)  
  const isResizable = ref(true)  
  const isMaximizable = ref(true)  

  // 用户是否可以手动调整窗口大小  
  getCurrentWindow().isResizable().then(res => {  
    isResizable.value = res  
  })  
  // 窗口是否可以最大化  
  getCurrentWindow().isMaximizable().then(res => {  
    isMaximizable.value = res  
  })  
  // 初始监听窗口是否最大化  
  getCurrentWindow().isMaximized().then(res => {  
    hasMaximized.value = res  
  })  
  // 实时监听窗口是否最大化  
  listen('tauri://resize', async() => {  
    hasMaximized.value = await getCurrentWindow().isMaximized()  
  })  

  // 最小化  
  const handleWinMin = async() => {  
    // winSet('minimize')  
    await getCurrentWindow().minimize()  
  }  
  // 最大化/还原  
  const handleWinToggle = async() => {  
    // winSet('max2min')  
    await getCurrentWindow().toggleMaximize()  
  }  
  // 关闭  
  const handleClose = async() => {  
    const isMajor = getCurrentWindow().label.indexOf('main') > -1  
    if(isMajor) {  
      let el = layer({  
        type: 'android',  
        content: '是否最小化到托盘,不退出程序?',  
        layerStyle: 'background: #f9f9f9; border-radius: 8px;',  
        closable: false,  
        resize: false,  
        btns: [  
          {  
            text: '最小化托盘',  
            style: 'color: #646cff',  
            click: () => {  
              layer.close(el)  
              // winSet('hide')  
              await getCurrentWindow().hide()  
            }  
          },  
          {  
            text: '退出程序',  
            style: 'color: #fa5151',  
            click: async() => {  
              authstate.logout()  
              await exit()  
            }  
          }  
        ]  
      })  
    }else {  
      // winSet('close')  
      await getCurrentWindow().close()  
    }  
  }  
</script>  

<template>  
  <div class="ev__winbtns vu__drag" :style="{'z-index': zIndex}">  
    <div class="ev__winbtns-actions vu__undrag" :style="{'color': color}">  
      <a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon elec-icon elec-icon-min"></i></a>  
      <a v-if="isTrue(maximizable) && isResizable && isMaximizable" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">  
        <i class="wicon elec-icon iconfont" :class="hasMaximized ? 've-icon-shrink' : 've-icon-arrowsalt'"></i>  
      </a>  
      <a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleClose"><i class="wicon elec-icon elec-icon-quit"></i></a>  
    </div>  
  </div>  
</template>

img

img

img

img

img

综上就是vue3+tauri2.x实战桌面端聊天项目的一些知识分享。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045331960
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

趁着国庆假期,我的又一款原创重磅新作Vue3.5+Tauri2.0+Vite5.4+Pinia2+ElementPlus跨平台实战仿QQ/微信电脑端聊天Exe程序Vue3Tauri2Chat,正式的完结了。整体UI采用全新无边框透明圆角阴影窗体

Vue3+Tauri2.0聊天实例|tauri2+vite5+element-plus仿微信|tauri聊天应用

img

img

封装tauri2.x多开窗口管理、换肤壁纸、自定义系统托盘闪烁/右键菜单功能。实现聊天、联系人、收藏、朋友圈、短视频、我的等页面模块。

img

img

运用技术

  • 编码工具:Vscode
  • 技术框架:tauri2.0+vite^5.4+vue^3.5+vue-router^4.4.5
  • 状态管理:pinia^2.2.2
  • 本地存储插件:pinia-plugin-persistedstate^4.0.2
  • 组件库:element-plus^2.8.3
  • 富文本编辑器:@vueup/vue-quill^1.2.0
  • 样式预处理:sass^1.79.3
  • 视频滑动组件:swiper^11.1.14

img

img

img

img

img

img

img

img

img

img

目前tauri2-vue3chat聊天项目已经发布到我的原创作品集,有需要的话可以去瞅瞅~
https://gf.bilibili.com/item/detail/1107133011

tauri2-vue3chat项目布局模板

img

img

<template>  
  <div class="vu__chatbox">  
    <template v-if="!route?.meta?.isNewWin">  
      <div class="vu__container flexbox flex-alignc flex-justifyc">  
        <div class="vu__layout flexbox flex-col">  
          <div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>  
            <!-- 菜单栏 -->  
            <slot v-if="!route?.meta?.hideMenuBar" name="menubar">  
              <MenuBar />  
            </slot>  

            <!-- 侧边栏 -->  
            <div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">  
              <aside class="vu__layout-sidebar__body flexbox flex-col">  
                <slot name="sidebar">  
                  <SideBar />  
                </slot>  
              </aside>  
            </div>  

            <!-- 主内容区 -->  
            <div class="vu__layout-main flex1 flexbox flex-col">  
              <ToolBar v-if="!route?.meta?.hideToolBar" />  
              <router-view v-slot="{ Component, route }">  
                <keep-alive>  
                  <component :is="Component" :key="route.path" />  
                </keep-alive>  
              </router-view>  
            </div>  
          </div>  
        </div>  
      </div>  
    </template>  
    <template v-else>  
      <WinLayout />  
    </template>  
  </div>  
</template>

img

img

img

tauri2多窗口实践

img

// 朋友圈窗口  
const handleFzone = () => {  
  winCreate({  
    label: 'win-fzone',  
    url: '/win/fzone',  
    title: '朋友圈',  
    width: 500,  
    height: 695,  
    minWidth: 350,  
    minHeight: 500,  
    maximizable: false,  
  })  
}  

// 短视频窗口  
const handleFvideo = () => {  
  winCreate({  
    label: 'win-fvideo',  
    url: '/win/fvideo',  
    title: '短视频',  
    width: 575,  
    height: 675,  
    minWidth: 415,  
    minHeight: 545  
  })  
}  

// 换肤壁纸窗口  
const handleSkin = () => {  
  winCreate({  
    label: 'win-skin',  
    url: '/win/skin',  
    title: '壁纸',  
    width: 375,  
    height: 480,  
    resizable: false,  
    maximizable: false,  
    visible: true,  
  })  
}  

// 界面管理器  
const handleManageUI = () => {  
  winCreate({  
    label: 'win-manage',  
    url: '/win/manage',  
    title: '界面管理器',  
    width: 320,  
    height: 360,  
    resizable: false,  
    maximizable: false,  
  })  
}

之前有过一篇关于tauri2创建多窗口应用的分享文章,可以去看看。
基于Tauri2+Vite5搭建桌面端程序|tauri2+vue3多窗口|消息提醒|托盘闪烁

img

img

img

vue3+tauri2自定义透明圆角阴影窗体

img

.vu__chatbox {height: calc(100vh); padding: 5px; overflow: hidden;}  
.vu__layout {  
  background-color: #f5f5f5;  
  overflow: hidden;  
  height: 100%; width: 100%;  
  position: relative; z-index: 100;  
  border-radius: 8px;  
  box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.15),0 1px 5px -1px rgba(0, 0, 0, 0.1),0 2px 5px rgba(0, 0, 0, 0.1);  
}

img

<script setup>  
  /**  
   * tauri2.0自定义系统最大化/最小化/关闭  by andy  Q:282310962  
   */  

  // ...  

  const props = defineProps({  
    color: String,  
    // 窗口是否可最小化  
    minimizable: {type: [Boolean, String], default: true},  
    // 窗口是否可最大化  
    maximizable: {type: [Boolean, String], default: true},  
    // 窗口是否可关闭  
    closable: {type: [Boolean, String], default: true},  
    // 层级  
    zIndex: {type: [Number, String], default: 2024},  

    // 关闭前回调,会暂停实例关闭 function(done),done用于关闭  
        beforeClose: Function  
  })  

  const hasMaximized = ref(false)  
  const isResizable = ref(true)  
  const isMaximizable = ref(true)  

  // 用户是否可以手动调整窗口大小  
  getCurrentWindow().isResizable().then(res => {  
    isResizable.value = res  
  })  
  // 窗口是否可以最大化  
  getCurrentWindow().isMaximizable().then(res => {  
    isMaximizable.value = res  
  })  
  // 初始监听窗口是否最大化  
  getCurrentWindow().isMaximized().then(res => {  
    hasMaximized.value = res  
  })  
  // 实时监听窗口是否最大化  
  listen('tauri://resize', async() => {  
    hasMaximized.value = await getCurrentWindow().isMaximized()  
  })  

  // 最小化  
  const handleWinMin = async() => {  
    // winSet('minimize')  
    await getCurrentWindow().minimize()  
  }  
  // 最大化/还原  
  const handleWinToggle = async() => {  
    // winSet('max2min')  
    await getCurrentWindow().toggleMaximize()  
  }  
  // 关闭  
  const handleClose = async() => {  
    const isMajor = getCurrentWindow().label.indexOf('main') > -1  
    if(isMajor) {  
      let el = layer({  
        type: 'android',  
        content: '是否最小化到托盘,不退出程序?',  
        layerStyle: 'background: #f9f9f9; border-radius: 8px;',  
        closable: false,  
        resize: false,  
        btns: [  
          {  
            text: '最小化托盘',  
            style: 'color: #646cff',  
            click: () => {  
              layer.close(el)  
              // winSet('hide')  
              await getCurrentWindow().hide()  
            }  
          },  
          {  
            text: '退出程序',  
            style: 'color: #fa5151',  
            click: async() => {  
              authstate.logout()  
              await exit()  
            }  
          }  
        ]  
      })  
    }else {  
      // winSet('close')  
      await getCurrentWindow().close()  
    }  
  }  
</script>  

<template>  
  <div class="ev__winbtns vu__drag" :style="{'z-index': zIndex}">  
    <div class="ev__winbtns-actions vu__undrag" :style="{'color': color}">  
      <a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon elec-icon elec-icon-min"></i></a>  
      <a v-if="isTrue(maximizable) && isResizable && isMaximizable" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">  
        <i class="wicon elec-icon iconfont" :class="hasMaximized ? 've-icon-shrink' : 've-icon-arrowsalt'"></i>  
      </a>  
      <a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleClose"><i class="wicon elec-icon elec-icon-quit"></i></a>  
    </div>  
  </div>  
</template>

img

img

img

img

img

综上就是vue3+tauri2.x实战桌面端聊天项目的一些知识分享。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045331960
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

vue3+electron32+arco.design桌面端os模板|vite5+electron32仿mac/win客户端os程序

原创基于electron32.x+vite5+vue3 setup+pinia2+arco-design实战开发全新桌面版os后台管理系统解决方案Vue3ElectronOS。内置了macos和windows两种风格桌面模板,自研可拖拽栅格布局桌面。

自研Electron32+Vite5+ArcoDesign桌面OS管理系统

img

img

运用技术

  • 开发工具:VScode
  • 技术框架:vite^5.4.1+vue^3.4.37+vue-router^4.4.3
  • 跨平台框架:electron^32.0.1
  • UI组件库:@arco-design/web-vue^2.56.0 (字节前端vue3组件库)
  • 状态管理:pinia^2.2.2
  • 拖拽插件:sortablejs^1.15.2
  • 图表组件:echarts^5.5.1
  • markdown编辑器:md-editor-v3^4.19.2
  • 模拟数据:mockjs^1.1.0
  • 打包构建:electron-builder^24.13.3
  • electron+vite插件:vite-plugin-electron^0.28.7

img

img

基于electronjs封装高性能窗口多开器,采用自研栅格化拖拽布局引擎。

img

img

项目结构框架

electron32-winos基于vite.js整合最新跨平台技术electron32.x搭建项目模板。

img

img

目前electron32-vue3-os桌面os已经发布到我的原创作品集。
https://gf.bilibili.com/item/detail/1106958011

img

img

Electron主线程配置

img

/**  
 * electron主线程配置  
 * @author andy  
 */  

import { app, BrowserWindow } from 'electron'  

import { WindowManager } from '../src/windows/index.js'  

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)  
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true  

const createWindow = () => {  
  let win = new WindowManager()  
  win.create({isMajor: true})  
  // 系统托盘管理  
  win.trayManager()  
  // 监听ipcMain事件  
  win.ipcManager()  
}  

app.whenReady().then(() => {  
  createWindow()  

  app.on('activate', () => {  
    if(BrowserWindow.getAllWindows().length === 0) createWindow()  
  })  
})  

app.on('window-all-closed', () => {  
  if(process.platform !== 'darwin') app.quit()  
})

入口文件main.js

import { createApp } from 'vue'  
import './style.scss'  
import App from './App.vue'  

import { launchApp } from '@/windows/actions'  

// 引入路由及状态管理  
import Router from './router'  
import Pinia from './pinia'  

// 引入插件  
import Plugins from './plugins'  

launchApp().then(config => {  
  if(config) {  
    // 全局窗口配置  
    window.config = config  
  }  

  // 初始化app实例  
  createApp(App)  
  .use(Router)  
  .use(Pinia)  
  .use(Plugins)  
  .mount('#app')  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

electron32-os桌面布局

img

<script setup>  
  import { appState } from '@/pinia/modules/app'  

  // 引入布局模板  
  import MacosLayout from './template/macos.vue'  
  import WindowsLayout from './template/windows.vue'  

  const appstate = appState()  

  const DeskLayout = {  
    macos: MacosLayout,  
    windows: WindowsLayout  
  }  
</script>  

<template>  
  <div class="vu__container" :style="{'--themeSkin': appstate.config.skin}">  
    <component :is="DeskLayout[appstate.config.layout]" />  
  </div>  
</template>

img

<script setup>  
  import Wintool from '@/layouts/components/wintool/index.vue'  
  import Desk from '@/layouts/components/mac/desk.vue'  
  import Dock from '@/layouts/components/mac/dock.vue'  
</script>  

<template>  
  <div class="vu__layout flexbox flex-col">  
    <div class="vu__layout-header">  
      <Wintool />  
    </div>  
    <div class="vu__layout-body flex1 flexbox">  
      <Desk />  
    </div>  
    <div class="vu__layout-footer">  
      <Dock />  
    </div>  
  </div>  
</template>

img

img

img

img

img

img

electron32-os桌面栅格模板

img

img

桌面json菜单配置项

/**  
  * label 图标标签  
  * imgico 图标(本地或网络图片) 支持Arco Design内置图标或自定义iconfont字体图标  
  * path 跳转路由地址  
  * link 跳转外部链接  
  * hideLabel 是否隐藏图标标签  
  * background 自定义图标背景色  
  * color 自定义图标颜色  
  * size 栅格布局(16种) 1x1 1x2 1x3 1x4、2x1 2x2 2x3 2x4、3x1 3x2 3x3 3x4、4x1 4x2 4x3 4x4  
  * onClick 点击图标回调函数  
  * children 二级菜单配置  * isNewin 新窗口打开路由页面  
  */

img

img

桌面菜单示例代码

const deskMenu = [  
  {  
    uid: 'd137f210-507e-7e8e-1950-9deefac27e48',  
    list: [  
      {imgico: markRaw(Today), size: '2x2'},  
      {label: '日历', imgico: markRaw(Calendar3x3), size: '3x3'},  
      {label: 'Electron32', imgico: '/electron.svg', link: 'https://www.electronjs.org/'},  
      // ...  
    ]  
  },  
  {  
    uid: 'g270f210-207e-6e8e-2650-9deefac27e48',  
    list: [  
      {label: 'Appstore', imgico: '/static/mac/appstore.png'},  
      // ...  
    ]  
  },  
  {  
    uid: 't165f210-607e-4e8e-9950-9deefac27e48',  
    list: [  
      {label: 'Vue.js', imgico: '/vue.svg', link: 'https://vuejs.org/',},  
      {label: 'Vite.js官方文档', imgico: '/vite.svg', link: 'https://vitejs.dev/',},  
      // ...  
    ]  
  },  
  {  
    uid: 'u327f210-207e-1e8e-9950-9deefac27e48',  
    list: [  
      {label: 'Electron32', imgico: '/electron.svg', link: 'https://www.electronjs.org/'},  
      {label: '首页', imgico: markRaw(IconHome), path: '/home', color: '#fff', isNewin: true},  
      {label: '工作台', imgico: 'elec-icon-dotchart', path: '/home/dashboard', color: '#fff'},  
      // ...  
      {  
        label: '用户中心',  
        children: [  
          {label: '主页', imgico: '/static/svg/ucenter.svg', path: '/setting'},  
          {label: '用户管理', imgico: markRaw(IconUserGroup), path: '/user', color: '#fff'},  
          // ...  
        ]  
      },  
      {  
        label: '设置',  
        children: [  
          // ...  
        ]  
      },  
      {  
        label: '收藏网址',  
        children: [  
          {label: 'Electron32', imgico: '/electron.svg', link: 'https://www.electronjs.org/'},  
          {label: 'Vite.js', imgico: '/vite.svg',},  
          // ...  
        ]  
      },  
      {  
        label: '公众号', imgico: '/static/qrimg.png', color: '#07c160',  
        onClick: () => {  
          Modal.info({  
            // ...  
          })  
        }  
      },  
    ]  
  }  
]

Okay,综上就是electron32+vue3开发桌面端os系统的一些知识分享。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045245775
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

原创基于electron32.x+vite5+vue3 setup+pinia2+arco-design实战开发全新桌面版os后台管理系统解决方案Vue3ElectronOS。内置了macos和windows两种风格桌面模板,自研可拖拽栅格布局桌面。

自研Electron32+Vite5+ArcoDesign桌面OS管理系统

img

img

运用技术

  • 开发工具:VScode
  • 技术框架:vite^5.4.1+vue^3.4.37+vue-router^4.4.3
  • 跨平台框架:electron^32.0.1
  • UI组件库:@arco-design/web-vue^2.56.0 (字节前端vue3组件库)
  • 状态管理:pinia^2.2.2
  • 拖拽插件:sortablejs^1.15.2
  • 图表组件:echarts^5.5.1
  • markdown编辑器:md-editor-v3^4.19.2
  • 模拟数据:mockjs^1.1.0
  • 打包构建:electron-builder^24.13.3
  • electron+vite插件:vite-plugin-electron^0.28.7

img

img

基于electronjs封装高性能窗口多开器,采用自研栅格化拖拽布局引擎。

img

img

项目结构框架

electron32-winos基于vite.js整合最新跨平台技术electron32.x搭建项目模板。

img

img

目前electron32-vue3-os桌面os已经发布到我的原创作品集。
https://gf.bilibili.com/item/detail/1106958011

img

img

Electron主线程配置

img

/**  
 * electron主线程配置  
 * @author andy  
 */  

import { app, BrowserWindow } from 'electron'  

import { WindowManager } from '../src/windows/index.js'  

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)  
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true  

const createWindow = () => {  
  let win = new WindowManager()  
  win.create({isMajor: true})  
  // 系统托盘管理  
  win.trayManager()  
  // 监听ipcMain事件  
  win.ipcManager()  
}  

app.whenReady().then(() => {  
  createWindow()  

  app.on('activate', () => {  
    if(BrowserWindow.getAllWindows().length === 0) createWindow()  
  })  
})  

app.on('window-all-closed', () => {  
  if(process.platform !== 'darwin') app.quit()  
})

入口文件main.js

import { createApp } from 'vue'  
import './style.scss'  
import App from './App.vue'  

import { launchApp } from '@/windows/actions'  

// 引入路由及状态管理  
import Router from './router'  
import Pinia from './pinia'  

// 引入插件  
import Plugins from './plugins'  

launchApp().then(config => {  
  if(config) {  
    // 全局窗口配置  
    window.config = config  
  }  

  // 初始化app实例  
  createApp(App)  
  .use(Router)  
  .use(Pinia)  
  .use(Plugins)  
  .mount('#app')  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

electron32-os桌面布局

img

<script setup>  
  import { appState } from '@/pinia/modules/app'  

  // 引入布局模板  
  import MacosLayout from './template/macos.vue'  
  import WindowsLayout from './template/windows.vue'  

  const appstate = appState()  

  const DeskLayout = {  
    macos: MacosLayout,  
    windows: WindowsLayout  
  }  
</script>  

<template>  
  <div class="vu__container" :style="{'--themeSkin': appstate.config.skin}">  
    <component :is="DeskLayout[appstate.config.layout]" />  
  </div>  
</template>

img

<script setup>  
  import Wintool from '@/layouts/components/wintool/index.vue'  
  import Desk from '@/layouts/components/mac/desk.vue'  
  import Dock from '@/layouts/components/mac/dock.vue'  
</script>  

<template>  
  <div class="vu__layout flexbox flex-col">  
    <div class="vu__layout-header">  
      <Wintool />  
    </div>  
    <div class="vu__layout-body flex1 flexbox">  
      <Desk />  
    </div>  
    <div class="vu__layout-footer">  
      <Dock />  
    </div>  
  </div>  
</template>

img

img

img

img

img

img

electron32-os桌面栅格模板

img

img

桌面json菜单配置项

/**  
  * label 图标标签  
  * imgico 图标(本地或网络图片) 支持Arco Design内置图标或自定义iconfont字体图标  
  * path 跳转路由地址  
  * link 跳转外部链接  
  * hideLabel 是否隐藏图标标签  
  * background 自定义图标背景色  
  * color 自定义图标颜色  
  * size 栅格布局(16种) 1x1 1x2 1x3 1x4、2x1 2x2 2x3 2x4、3x1 3x2 3x3 3x4、4x1 4x2 4x3 4x4  
  * onClick 点击图标回调函数  
  * children 二级菜单配置  * isNewin 新窗口打开路由页面  
  */

img

img

桌面菜单示例代码

const deskMenu = [  
  {  
    uid: 'd137f210-507e-7e8e-1950-9deefac27e48',  
    list: [  
      {imgico: markRaw(Today), size: '2x2'},  
      {label: '日历', imgico: markRaw(Calendar3x3), size: '3x3'},  
      {label: 'Electron32', imgico: '/electron.svg', link: 'https://www.electronjs.org/'},  
      // ...  
    ]  
  },  
  {  
    uid: 'g270f210-207e-6e8e-2650-9deefac27e48',  
    list: [  
      {label: 'Appstore', imgico: '/static/mac/appstore.png'},  
      // ...  
    ]  
  },  
  {  
    uid: 't165f210-607e-4e8e-9950-9deefac27e48',  
    list: [  
      {label: 'Vue.js', imgico: '/vue.svg', link: 'https://vuejs.org/',},  
      {label: 'Vite.js官方文档', imgico: '/vite.svg', link: 'https://vitejs.dev/',},  
      // ...  
    ]  
  },  
  {  
    uid: 'u327f210-207e-1e8e-9950-9deefac27e48',  
    list: [  
      {label: 'Electron32', imgico: '/electron.svg', link: 'https://www.electronjs.org/'},  
      {label: '首页', imgico: markRaw(IconHome), path: '/home', color: '#fff', isNewin: true},  
      {label: '工作台', imgico: 'elec-icon-dotchart', path: '/home/dashboard', color: '#fff'},  
      // ...  
      {  
        label: '用户中心',  
        children: [  
          {label: '主页', imgico: '/static/svg/ucenter.svg', path: '/setting'},  
          {label: '用户管理', imgico: markRaw(IconUserGroup), path: '/user', color: '#fff'},  
          // ...  
        ]  
      },  
      {  
        label: '设置',  
        children: [  
          // ...  
        ]  
      },  
      {  
        label: '收藏网址',  
        children: [  
          {label: 'Electron32', imgico: '/electron.svg', link: 'https://www.electronjs.org/'},  
          {label: 'Vite.js', imgico: '/vite.svg',},  
          // ...  
        ]  
      },  
      {  
        label: '公众号', imgico: '/static/qrimg.png', color: '#07c160',  
        onClick: () => {  
          Modal.info({  
            // ...  
          })  
        }  
      },  
    ]  
  }  
]

Okay,综上就是electron32+vue3开发桌面端os系统的一些知识分享。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045245775
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

vue3+electron31+element-plus后台系统管理应用程序

整合vite5.x+electron31+pinia2+vue-i18n+echarts全新后台管理方案Vue3ElectronAdmin。提供了4种通用布局模板,支持中英文/繁体国际化解决方案、动态路由权限,实现了表格、表单、列表、图表、编辑器、错误处理等模块。

原创Electron31+Vue3+ElementPlus桌面端后台管理系统

img

img

技术栈

  • 编辑器:vscode
  • 框架技术:vite^5.3.4+vue^3.4.31+vue-router^4.4.0
  • 跨端框架:electron^31.3.0
  • 组件库: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

img

img

img

功能性

1、最新前端技术栈Vite5.x、Vue3、Electron31、ElementPlus、Vue-I18n、Echarts
2、支持中英文/繁体国际化解决方案
3、支持动态权限路由、多页签缓存路由
4、封装多窗口管理器
5、内置4种通用布局模板、自由切换风格
6、整合通用的表格、表单、列表、图表、编辑器、错误处理等模块
7、高颜值UI界面、轻量级模块化、高定制性

img

项目结构

整个项目整合vite.js+electronjs跨平台技术,采用vue3 setup语法编码。

img

img

electronjs主线程配置

/**  
 * electron主线程配置  
 * @author andy  
 */  

import { app, BrowserWindow } from 'electron'  

import { WindowManager } from '../src/windows/index.js'  

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)  
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true  

const createWindow = () => {  
  let win = new WindowManager()  
  win.create({isMajor: true})  
  // 系统托盘管理  
  win.trayManager()  
  // 监听ipcMain事件  
  win.ipcManager()  
}  

app.whenReady().then(() => {  
  createWindow()  

  app.on('activate', () => {  
    if(BrowserWindow.getAllWindows().length === 0) createWindow()  
  })  
})  

app.on('window-all-closed', () => {  
  if(process.platform !== 'darwin') app.quit()  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

electron31-vue3-admin布局模板

img

img

/**  
 * 通用布局模板  
 * @author Andy  
*/  

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

electron31-admin国际化配置

img

img

/**  
 * 国际化配置  
 * @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)  
}

vue3封装echarts图表hook

img

img

/**  
 * 动态图表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  
}

OK,以上就是Electron31+Vue3+ElementPlus跨平台实战开发后台管理系统的一些分享。整个项目涉及到的知识点还是蛮多的,限于篇幅就暂时分享到这里。希望对大家有所帮助哈~

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045186093
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

整合vite5.x+electron31+pinia2+vue-i18n+echarts全新后台管理方案Vue3ElectronAdmin。提供了4种通用布局模板,支持中英文/繁体国际化解决方案、动态路由权限,实现了表格、表单、列表、图表、编辑器、错误处理等模块。

原创Electron31+Vue3+ElementPlus桌面端后台管理系统

img

img

技术栈

  • 编辑器:vscode
  • 框架技术:vite^5.3.4+vue^3.4.31+vue-router^4.4.0
  • 跨端框架:electron^31.3.0
  • 组件库: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

img

img

img

功能性

1、最新前端技术栈Vite5.x、Vue3、Electron31、ElementPlus、Vue-I18n、Echarts
2、支持中英文/繁体国际化解决方案
3、支持动态权限路由、多页签缓存路由
4、封装多窗口管理器
5、内置4种通用布局模板、自由切换风格
6、整合通用的表格、表单、列表、图表、编辑器、错误处理等模块
7、高颜值UI界面、轻量级模块化、高定制性

img

项目结构

整个项目整合vite.js+electronjs跨平台技术,采用vue3 setup语法编码。

img

img

electronjs主线程配置

/**  
 * electron主线程配置  
 * @author andy  
 */  

import { app, BrowserWindow } from 'electron'  

import { WindowManager } from '../src/windows/index.js'  

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)  
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true  

const createWindow = () => {  
  let win = new WindowManager()  
  win.create({isMajor: true})  
  // 系统托盘管理  
  win.trayManager()  
  // 监听ipcMain事件  
  win.ipcManager()  
}  

app.whenReady().then(() => {  
  createWindow()  

  app.on('activate', () => {  
    if(BrowserWindow.getAllWindows().length === 0) createWindow()  
  })  
})  

app.on('window-all-closed', () => {  
  if(process.platform !== 'darwin') app.quit()  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

electron31-vue3-admin布局模板

img

img

/**  
 * 通用布局模板  
 * @author Andy  
*/  

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

electron31-admin国际化配置

img

img

/**  
 * 国际化配置  
 * @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)  
}

vue3封装echarts图表hook

img

img

/**  
 * 动态图表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  
}

OK,以上就是Electron31+Vue3+ElementPlus跨平台实战开发后台管理系统的一些分享。整个项目涉及到的知识点还是蛮多的,限于篇幅就暂时分享到这里。希望对大家有所帮助哈~

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045186093
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

如何在GitHub免费搭建个人博客网站?

  如何在GitHub免费搭建个人博客网站?当你想要开始自己的博客之旅,但又不想花费金钱购买服务器和域名时,还有一些免费的平台可供你选择。以下是一种无需服务器和域名的方法,利用GitHub Pages和Jekyll搭建个人博客网站(http://m.bokequ.com/list/22-0.html)

  步骤一:准备 GitHub 账户

  如果你还没有 GitHub 账户,首先需要注册一个。GitHub 提供免费的代码托管服务,同时也支持通过 GitHub Pages 托管静态网站。

  步骤二:创建 GitHub 仓库

  登录你的 GitHub 账户,点击右上角的加号按钮,选择 "New repository"。在 "Repository name" 栏中输入你的用户名(或者你希望的博客地址),比如 yourusername.github.io(注意替换成你的用户名)。勾选 "Initialize this repository with a README" 选项,并点击 "Create repository"。

  步骤三:下载 Jekyll 主题

  Jekyll 是一个简单易用的静态网站生成器,GitHub Pages 支持使用 Jekyll 搭建个人网站。你可以在 Jekyll 官方网站(https://jekyllrb.com/)或 GitHub 上找到各种免费的 Jekyll 主题。选择一个你喜欢的主题,将其下载并解压缩到本地。

  步骤四:上传文件到 GitHub 仓库

  将 Jekyll 主题文件夹中的所有文件上传到你在步骤二中创建的 GitHub 仓库中。你可以使用 GitHub Desktop、Git 命令行或者直接通过 GitHub 网站上传文件。

  步骤五:访问你的博客网站

  等待一段时间,GitHub 会自动构建你的网站,并将其托管在bokequ.github.io这个地址上。你可以在浏览器中输入这个地址,访问你的个人博客网站。

  步骤六:定制你的博客

  编辑 Jekyll 主题文件夹中的配置文件和内容文件,定制你的个人博客。你可以修改页面布局、添加新的页面和文章,以及调整样式和颜色。

  通过 GitHub Pages 和 Jekyll,你可以免费搭建个人博客网站,无需购买服务器和域名。这是一个简单且经济高效的方式,让你能够开始你的博客之旅,并与世界分享你的想法和创作。

继续阅读 »

  如何在GitHub免费搭建个人博客网站?当你想要开始自己的博客之旅,但又不想花费金钱购买服务器和域名时,还有一些免费的平台可供你选择。以下是一种无需服务器和域名的方法,利用GitHub Pages和Jekyll搭建个人博客网站(http://m.bokequ.com/list/22-0.html)

  步骤一:准备 GitHub 账户

  如果你还没有 GitHub 账户,首先需要注册一个。GitHub 提供免费的代码托管服务,同时也支持通过 GitHub Pages 托管静态网站。

  步骤二:创建 GitHub 仓库

  登录你的 GitHub 账户,点击右上角的加号按钮,选择 "New repository"。在 "Repository name" 栏中输入你的用户名(或者你希望的博客地址),比如 yourusername.github.io(注意替换成你的用户名)。勾选 "Initialize this repository with a README" 选项,并点击 "Create repository"。

  步骤三:下载 Jekyll 主题

  Jekyll 是一个简单易用的静态网站生成器,GitHub Pages 支持使用 Jekyll 搭建个人网站。你可以在 Jekyll 官方网站(https://jekyllrb.com/)或 GitHub 上找到各种免费的 Jekyll 主题。选择一个你喜欢的主题,将其下载并解压缩到本地。

  步骤四:上传文件到 GitHub 仓库

  将 Jekyll 主题文件夹中的所有文件上传到你在步骤二中创建的 GitHub 仓库中。你可以使用 GitHub Desktop、Git 命令行或者直接通过 GitHub 网站上传文件。

  步骤五:访问你的博客网站

  等待一段时间,GitHub 会自动构建你的网站,并将其托管在bokequ.github.io这个地址上。你可以在浏览器中输入这个地址,访问你的个人博客网站。

  步骤六:定制你的博客

  编辑 Jekyll 主题文件夹中的配置文件和内容文件,定制你的个人博客。你可以修改页面布局、添加新的页面和文章,以及调整样式和颜色。

  通过 GitHub Pages 和 Jekyll,你可以免费搭建个人博客网站,无需购买服务器和域名。这是一个简单且经济高效的方式,让你能够开始你的博客之旅,并与世界分享你的想法和创作。

收起阅读 »

PHP框架的性能比较

  基准测试结果表明,在页面加载时间方面,codeigniter 最快,其次是 laravel、symfony 和 zend framework。在数据库操作方面,codeigniter 最快,其次是 laravel、symfony 和 zend framework。在内存使用方面,codeigniter 最少,其次是 laravel、symfony 和 zend framework。实战案例表明,codeigniter 非常适合小型项目,而 laravel 和 symfony 适用于中型到大型项目,zend framework 适用于企业级应用,但性能较差。

  PHP框架性能比较

  在选择PHP框架时,性能是一个关键考虑因素。本文将比较常见的 PHP 框架的性能,并提供实战案例来展示其差异http://m.bokequ.com/moban/1252.html

  比较的框架

  Laravel: 一个流行的 MVC 框架,以其易用性和强大的特性而闻名。

  Symfony: 一个广泛的框架,提供丰富的组件库和灵活的配置选项。

  Zend Framework: 一个老牌的企业级框架,以其稳定性和安全性而闻名。

  CodeIgniter: 一个轻量级的 MVC 框架,非常适合小型到中型的项目。

  基准测试

  我们使用以下基准测试来比较这些框架:

  页面加载时间:加载一个简单页面的时间

  数据库操作:执行数据库查询的速度

  内存使用:框架运行时所需的内存量

  实战案例

  我们创建一个简单的博客应用程序,使用每个框架来显示一页包含 10 篇文章的帖子。我们使用 Apache Bench 进行基准测试,每秒发送 100 个请求持续 10 分钟。

  结果

  页面加载时间:Laravel

  数据库操作:CodeIgniter

  内存使用:CodeIgniter

  结论

  从基准测试和实战案例中可以看出,CodeIgniter 在性能方面表现出色,非常适合小型项目。 Laravel 和 Symfony 在性能和功能方面实现了良好的平衡,适用于中型到大型项目。 Zend Framework 是企业级应用程序的首选,提供卓越的稳定性和安全性,但它的性能也受到影响。

继续阅读 »

  基准测试结果表明,在页面加载时间方面,codeigniter 最快,其次是 laravel、symfony 和 zend framework。在数据库操作方面,codeigniter 最快,其次是 laravel、symfony 和 zend framework。在内存使用方面,codeigniter 最少,其次是 laravel、symfony 和 zend framework。实战案例表明,codeigniter 非常适合小型项目,而 laravel 和 symfony 适用于中型到大型项目,zend framework 适用于企业级应用,但性能较差。

  PHP框架性能比较

  在选择PHP框架时,性能是一个关键考虑因素。本文将比较常见的 PHP 框架的性能,并提供实战案例来展示其差异http://m.bokequ.com/moban/1252.html

  比较的框架

  Laravel: 一个流行的 MVC 框架,以其易用性和强大的特性而闻名。

  Symfony: 一个广泛的框架,提供丰富的组件库和灵活的配置选项。

  Zend Framework: 一个老牌的企业级框架,以其稳定性和安全性而闻名。

  CodeIgniter: 一个轻量级的 MVC 框架,非常适合小型到中型的项目。

  基准测试

  我们使用以下基准测试来比较这些框架:

  页面加载时间:加载一个简单页面的时间

  数据库操作:执行数据库查询的速度

  内存使用:框架运行时所需的内存量

  实战案例

  我们创建一个简单的博客应用程序,使用每个框架来显示一页包含 10 篇文章的帖子。我们使用 Apache Bench 进行基准测试,每秒发送 100 个请求持续 10 分钟。

  结果

  页面加载时间:Laravel

  数据库操作:CodeIgniter

  内存使用:CodeIgniter

  结论

  从基准测试和实战案例中可以看出,CodeIgniter 在性能方面表现出色,非常适合小型项目。 Laravel 和 Symfony 在性能和功能方面实现了良好的平衡,适用于中型到大型项目。 Zend Framework 是企业级应用程序的首选,提供卓越的稳定性和安全性,但它的性能也受到影响。

收起阅读 »

网站如何实现https重定向(301)到http

  对于个人网站站注册比较少的,服务器配置不是很好的,没必要https,https跳转到http是要时间的,会影响网站打开的速度。免费的https每年都要更换。个人博客网站https有一段时间了,而且很多页面都有收录排名,现在已去掉https了,用户搜索从https进网站无法打开页面。去掉后https对面网站有一定的影响,所有这里就要实现访问https自动跳转到http

  网站设置http跳转到https比较容易,如相反方向将https跳转到http,不是专搞程序的,有点难设置。网上文章也有很多"HTTPS重定向到HTTP的解决方法"。但很多都是没用的,都是转载别人没测试过,搞的网站打不开。

将以下代码放在你网站根目录下的.htaccess文件,没有.htaccess可以新建一个。域名换成你自己的域名,经测试有效

RewriteEngine On  
RewriteBase /  

RewriteCond %{HTTP_HOST} !^www\. [NC]  
RewriteRule ^ http://www.bokequ.com%{REQUEST_URI} [L,R=301,NE]  

RewriteCond %{HTTPS} on [OR]  
RewriteCond %{HTTP:X-Forwarded-Proto} https [OR]  
RewriteCond %{SERVER_PORT} ^443$  
RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]  

RewriteCond %{HTTP_REFERER} !^$  
RewriteCond %{HTTP_REFERER} !^http://(www\.)?bokequ.com/.*$ [NC]  
RewriteRule \.(gif|jpg|png|tif|js|css|xls|xlsx|zip|rar|pdf|ods|ots)$ - [F,NC]  

RewriteCond %{REQUEST_FILENAME} !-f  
RewriteCond %{REQUEST_FILENAME} !-d  
RewriteCond %{REQUEST_FILENAME} !-l  
RewriteRule ^(.+)$ index.php/$1 [L]
继续阅读 »

  对于个人网站站注册比较少的,服务器配置不是很好的,没必要https,https跳转到http是要时间的,会影响网站打开的速度。免费的https每年都要更换。个人博客网站https有一段时间了,而且很多页面都有收录排名,现在已去掉https了,用户搜索从https进网站无法打开页面。去掉后https对面网站有一定的影响,所有这里就要实现访问https自动跳转到http

  网站设置http跳转到https比较容易,如相反方向将https跳转到http,不是专搞程序的,有点难设置。网上文章也有很多"HTTPS重定向到HTTP的解决方法"。但很多都是没用的,都是转载别人没测试过,搞的网站打不开。

将以下代码放在你网站根目录下的.htaccess文件,没有.htaccess可以新建一个。域名换成你自己的域名,经测试有效

RewriteEngine On  
RewriteBase /  

RewriteCond %{HTTP_HOST} !^www\. [NC]  
RewriteRule ^ http://www.bokequ.com%{REQUEST_URI} [L,R=301,NE]  

RewriteCond %{HTTPS} on [OR]  
RewriteCond %{HTTP:X-Forwarded-Proto} https [OR]  
RewriteCond %{SERVER_PORT} ^443$  
RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]  

RewriteCond %{HTTP_REFERER} !^$  
RewriteCond %{HTTP_REFERER} !^http://(www\.)?bokequ.com/.*$ [NC]  
RewriteRule \.(gif|jpg|png|tif|js|css|xls|xlsx|zip|rar|pdf|ods|ots)$ - [F,NC]  

RewriteCond %{REQUEST_FILENAME} !-f  
RewriteCond %{REQUEST_FILENAME} !-d  
RewriteCond %{REQUEST_FILENAME} !-l  
RewriteRule ^(.+)$ index.php/$1 [L]
收起阅读 »

vue3+arco网页版webos系统|vite5+pinia2仿mac/windows桌面os

vite vue3

vue3-webos一款基于最新前端技术vite5.x+vue3+pinia2+arco-design+sortablejs搭建的网页版os管理系统。内置macos和windows两种桌面、支持自定义桌面栅格布局引擎、可拖拽式桌面菜单/程序坞菜单等功能。

原创自研vite5+vue3+arco-design仿macOS网页版os桌面管理系统

img

img

vue3-webos使用vite.js搭建项目模板,采用arco.design组件库。

img

运用技术

  • 编辑器:Vscode
  • 技术框架:vite5.3.3+vue3.4.31+vue-router4.4+pinia2
  • UI组件库:arco-design^2.55.3 (字节桌面版vue3组件库)
  • 状态管理:pinia^2.1.7
  • 图表插件:echarts^5.5.1
  • 拖拽组件:sortablejs^1.15.2
  • 富文本编辑器:wangeditor^4.7.15
  • 模拟数据:mockjs^1.1.0
  • 样式编译:sass^1.77.8
  • 构建工具:vite^5.3.3

img

img

前段时间有分享一款vite5+vue3原创网页版聊天实例。感兴趣的可以去看看。
https://ask.dcloud.net.cn/article/41153

项目结构目录

img

img

img

目前整个vue3-macos项目已经托管到我的原创作品集,有需要的可以去瞅瞅。
https://gf.bilibili.com/item/detail/1106413011

main.js配置

import { createApp } from 'vue'  
import './style.scss'  
import App from './App.vue'  

// 引入arco.design组件库  
import ArcoDesign from '@arco-design/web-vue'  
import '@arco-design/web-vue/dist/arco.css'  
// 额外引入图标库  
import ArcoIcon from '@arco-design/web-vue/es/icon'  
import VEPlus from 've-plus'  
import 've-plus/dist/ve-plus.css'  

// 引入路由及状态管理  
import Router from './router'  
import Pinia from './pinia'  

const app = createApp(App)  

app  
.use(ArcoDesign)  
.use(ArcoIcon)  
.use(VEPlus)  
.use(Router)  
.use(Pinia)  
.mount('#app')

vue3批量路由配置

使用vite.js提供的import.meta.glob批量导入路由配置。

/**  
 * 路由管理Router  
 * @author andy  
 */  

import { createRouter, createWebHashHistory } from 'vue-router'  
import { authState } from '@/pinia/modules/auth'  

import Layout from '@/layouts/index.vue'  

// 批量导入路由  
const modules = import.meta.glob('./modules/*.js', { eager: true })  
console.log(modules)  
const patchRouters = Object.keys(modules).map(key => modules[key].default).flat()  
console.log(patchRouters)  

/**  
 * meta配置  
 * @param meta.requireAuth 需登录验证页面  
 */  
const routes = [  
  {  
    path: '/',  
    redirect: '/desktop',  
  },  
  ...patchRouters,  
  // 错误模块  
  {  
    path: '/:pathMatch(.*)*',  
    redirect: '/404',  
    component: Layout,  
    meta: {  
      title: '404error',  
    },  
    children: [  
      {  
        path: '404',  
        component: () => import('@/views/error/404.vue'),  
      }  
    ]  
  },  
]  

const router = createRouter({  
  history: createWebHashHistory(),  
  routes,  
})  

// 全局路由钩子拦截  
router.beforeEach((to, from) => {  
  const authstate = authState()  
  // 登录验证  
  if(to?.meta?.requireAuth && !authstate.authorization) {  
    console.log('你还未登录!')  
    return {  
      path: '/login'  
    }  
  }  
})  

router.afterEach(() => {  
  // ...  
})  

router.onError(error => {  
  console.warn('[Router Error]', error)  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

桌面公共布局模板

vite-osx内置了macos和windows两种桌面。

<script setup>  
  import { appState } from '@/pinia/modules/app'  

  // 引入布局模板  
  import MacosLayout from './template/macos.vue'  
  import WindowsLayout from './template/windows.vue'  

  const appstate = appState()  

  const DeskLayout = {  
    macos: MacosLayout,  
    windows: WindowsLayout  
  }  
</script>  

<template>  
  <div  
    class="vu__container desktop flexbox flex-alignc flex-justifyc"  
    :style="{'--themeSkin': appstate.config.skin}"  
    @contextmenu.prevent  
  >  
    <component :is="DeskLayout[appstate.config.layout]" />  
  </div>  
</template>

img

<template>  
  <div class="vu__layout flexbox flex-col">  
    <div class="vu__layout-header">  
      <Toolbar />  
    </div>  
    <div class="vu__layout-body flex1 flexbox">  
      <Desk />  
    </div>  
    <div class="vu__layout-footer">  
      <Dock />  
    </div>  
    <!-- 悬浮球(辅助触控) -->  
    <Touch />  
  </div>  
</template>

vue3栅格布局桌面

img

img

img

// 自定义变量(桌面图标)  
const deskVariable = ref({  
  '--icon-radius': '8px', // 圆角  
  '--icon-size': '60px', // 图标尺寸(设置rpx自定义手机设备)  
  '--icon-gap-col': '30px', // 水平间距  
  '--icon-gap-row': '30px', // 垂直间距  
  '--icon-labelSize': '12px', // 标签文字大小  
  '--icon-labelColor': '#fff', // 标签颜色  
  '--icon-fit': 'contain', // 图标自适应模式  
})

img

img

/*  
 * 桌面菜单json配置项 by Andy  Q:282310962  
 */  

const deskMenu = [  
  {  
    pid: 20240507001,  
    list: [  
      {imgico: markRaw(Today), size: '2x2'},  
      {label: '便签', imgico: markRaw(NoteBook), size: '4x2'},  
      ...  
    ]  
  },  
  {  
    pid: 20240509002,  
    list: [  
      {label: 'Appstore', imgico: '/static/mac/appstore.png'},  
      {label: '地图', imgico: '/static/mac/maps.png'},  
      ...  
    ]  
  },  
  {  
    pid: 20240510001,  
    list: [  
      {label: 'Github', imgico: '/static/svg/github.svg', link: 'https://github.com/', background: '#607d8b',},  
      ...  
    ]  
  },  
  {  
    uid: 'd141f210-207e-1e8e-9950-9deefac27e48',  
    list: [  
      {label: 'Vite^5.3.3', imgico: 'https://vitejs.dev/logo.svg', link: 'https://vitejs.dev/'},  
      ...  
      {  
        label: '组件',  
        children: [  
          {label: '表格', imgico: '/static/svg/table.svg', path: '/components/table/all'},  
          {label: '自定义表格', imgico: '/static/svg/table.svg', path: '/components/table/custom'},  
          ...  
        ]  
      },  
      {label: 'ChatGPT', imgico: '/static/svg/chatgpt.svg', link: 'https://openai.com/chatgpt/', background: '#15A17F',},  
      {label: 'Bilibili', imgico: '/static/svg/bilibili.svg', link: 'https://www.bilibili.com/', background: '#ff6899',},  
      {  
        label: '个人中心',  
        children: [  
          {label: '主页', imgico: '/static/svg/my.svg', path: '/setting'},  
          ...  
        ]  
      },  
      {  
        label: '设置',  
        children: [  
          {label: '网站设置', imgico: '/static/svg/settings.svg', path: '/setting/system/website'},  
          {label: '邮件服务', imgico: '/static/mac/mail.png', path: '/setting/system/mail'},  
        ]  
      },  
      {  
        label: '公众号', imgico: markRaw(IconWechat), color: '#07c160',  
        onClick: () => {  
          ...  
        }  
      },  
    ]  
  }  
]

以上就是vue3+pinia2+arco.design实战仿macos和windows桌面os系统的一些分享知识。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045102738
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

vue3-webos一款基于最新前端技术vite5.x+vue3+pinia2+arco-design+sortablejs搭建的网页版os管理系统。内置macos和windows两种桌面、支持自定义桌面栅格布局引擎、可拖拽式桌面菜单/程序坞菜单等功能。

原创自研vite5+vue3+arco-design仿macOS网页版os桌面管理系统

img

img

vue3-webos使用vite.js搭建项目模板,采用arco.design组件库。

img

运用技术

  • 编辑器:Vscode
  • 技术框架:vite5.3.3+vue3.4.31+vue-router4.4+pinia2
  • UI组件库:arco-design^2.55.3 (字节桌面版vue3组件库)
  • 状态管理:pinia^2.1.7
  • 图表插件:echarts^5.5.1
  • 拖拽组件:sortablejs^1.15.2
  • 富文本编辑器:wangeditor^4.7.15
  • 模拟数据:mockjs^1.1.0
  • 样式编译:sass^1.77.8
  • 构建工具:vite^5.3.3

img

img

前段时间有分享一款vite5+vue3原创网页版聊天实例。感兴趣的可以去看看。
https://ask.dcloud.net.cn/article/41153

项目结构目录

img

img

img

目前整个vue3-macos项目已经托管到我的原创作品集,有需要的可以去瞅瞅。
https://gf.bilibili.com/item/detail/1106413011

main.js配置

import { createApp } from 'vue'  
import './style.scss'  
import App from './App.vue'  

// 引入arco.design组件库  
import ArcoDesign from '@arco-design/web-vue'  
import '@arco-design/web-vue/dist/arco.css'  
// 额外引入图标库  
import ArcoIcon from '@arco-design/web-vue/es/icon'  
import VEPlus from 've-plus'  
import 've-plus/dist/ve-plus.css'  

// 引入路由及状态管理  
import Router from './router'  
import Pinia from './pinia'  

const app = createApp(App)  

app  
.use(ArcoDesign)  
.use(ArcoIcon)  
.use(VEPlus)  
.use(Router)  
.use(Pinia)  
.mount('#app')

vue3批量路由配置

使用vite.js提供的import.meta.glob批量导入路由配置。

/**  
 * 路由管理Router  
 * @author andy  
 */  

import { createRouter, createWebHashHistory } from 'vue-router'  
import { authState } from '@/pinia/modules/auth'  

import Layout from '@/layouts/index.vue'  

// 批量导入路由  
const modules = import.meta.glob('./modules/*.js', { eager: true })  
console.log(modules)  
const patchRouters = Object.keys(modules).map(key => modules[key].default).flat()  
console.log(patchRouters)  

/**  
 * meta配置  
 * @param meta.requireAuth 需登录验证页面  
 */  
const routes = [  
  {  
    path: '/',  
    redirect: '/desktop',  
  },  
  ...patchRouters,  
  // 错误模块  
  {  
    path: '/:pathMatch(.*)*',  
    redirect: '/404',  
    component: Layout,  
    meta: {  
      title: '404error',  
    },  
    children: [  
      {  
        path: '404',  
        component: () => import('@/views/error/404.vue'),  
      }  
    ]  
  },  
]  

const router = createRouter({  
  history: createWebHashHistory(),  
  routes,  
})  

// 全局路由钩子拦截  
router.beforeEach((to, from) => {  
  const authstate = authState()  
  // 登录验证  
  if(to?.meta?.requireAuth && !authstate.authorization) {  
    console.log('你还未登录!')  
    return {  
      path: '/login'  
    }  
  }  
})  

router.afterEach(() => {  
  // ...  
})  

router.onError(error => {  
  console.warn('[Router Error]', error)  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

桌面公共布局模板

vite-osx内置了macos和windows两种桌面。

<script setup>  
  import { appState } from '@/pinia/modules/app'  

  // 引入布局模板  
  import MacosLayout from './template/macos.vue'  
  import WindowsLayout from './template/windows.vue'  

  const appstate = appState()  

  const DeskLayout = {  
    macos: MacosLayout,  
    windows: WindowsLayout  
  }  
</script>  

<template>  
  <div  
    class="vu__container desktop flexbox flex-alignc flex-justifyc"  
    :style="{'--themeSkin': appstate.config.skin}"  
    @contextmenu.prevent  
  >  
    <component :is="DeskLayout[appstate.config.layout]" />  
  </div>  
</template>

img

<template>  
  <div class="vu__layout flexbox flex-col">  
    <div class="vu__layout-header">  
      <Toolbar />  
    </div>  
    <div class="vu__layout-body flex1 flexbox">  
      <Desk />  
    </div>  
    <div class="vu__layout-footer">  
      <Dock />  
    </div>  
    <!-- 悬浮球(辅助触控) -->  
    <Touch />  
  </div>  
</template>

vue3栅格布局桌面

img

img

img

// 自定义变量(桌面图标)  
const deskVariable = ref({  
  '--icon-radius': '8px', // 圆角  
  '--icon-size': '60px', // 图标尺寸(设置rpx自定义手机设备)  
  '--icon-gap-col': '30px', // 水平间距  
  '--icon-gap-row': '30px', // 垂直间距  
  '--icon-labelSize': '12px', // 标签文字大小  
  '--icon-labelColor': '#fff', // 标签颜色  
  '--icon-fit': 'contain', // 图标自适应模式  
})

img

img

/*  
 * 桌面菜单json配置项 by Andy  Q:282310962  
 */  

const deskMenu = [  
  {  
    pid: 20240507001,  
    list: [  
      {imgico: markRaw(Today), size: '2x2'},  
      {label: '便签', imgico: markRaw(NoteBook), size: '4x2'},  
      ...  
    ]  
  },  
  {  
    pid: 20240509002,  
    list: [  
      {label: 'Appstore', imgico: '/static/mac/appstore.png'},  
      {label: '地图', imgico: '/static/mac/maps.png'},  
      ...  
    ]  
  },  
  {  
    pid: 20240510001,  
    list: [  
      {label: 'Github', imgico: '/static/svg/github.svg', link: 'https://github.com/', background: '#607d8b',},  
      ...  
    ]  
  },  
  {  
    uid: 'd141f210-207e-1e8e-9950-9deefac27e48',  
    list: [  
      {label: 'Vite^5.3.3', imgico: 'https://vitejs.dev/logo.svg', link: 'https://vitejs.dev/'},  
      ...  
      {  
        label: '组件',  
        children: [  
          {label: '表格', imgico: '/static/svg/table.svg', path: '/components/table/all'},  
          {label: '自定义表格', imgico: '/static/svg/table.svg', path: '/components/table/custom'},  
          ...  
        ]  
      },  
      {label: 'ChatGPT', imgico: '/static/svg/chatgpt.svg', link: 'https://openai.com/chatgpt/', background: '#15A17F',},  
      {label: 'Bilibili', imgico: '/static/svg/bilibili.svg', link: 'https://www.bilibili.com/', background: '#ff6899',},  
      {  
        label: '个人中心',  
        children: [  
          {label: '主页', imgico: '/static/svg/my.svg', path: '/setting'},  
          ...  
        ]  
      },  
      {  
        label: '设置',  
        children: [  
          {label: '网站设置', imgico: '/static/svg/settings.svg', path: '/setting/system/website'},  
          {label: '邮件服务', imgico: '/static/mac/mail.png', path: '/setting/system/mail'},  
        ]  
      },  
      {  
        label: '公众号', imgico: markRaw(IconWechat), color: '#07c160',  
        onClick: () => {  
          ...  
        }  
      },  
    ]  
  }  
]

以上就是vue3+pinia2+arco.design实战仿macos和windows桌面os系统的一些分享知识。

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045102738
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

通过包名获取软件的基本信息

代码:

// 通过包名获取应用基本信息  形参:包名  返回信息{code: true/fals, information: {"appName":"NP管理器","packageName":"player.normal.np","versionName":"3.0.54","versionCode":20221004}}  
function getAppInformation(package_name, information) {  
    function isEmptyObject(obj) {  
        return Object.keys(obj).length === 0;  
    }  
    plus.android.importClass('java.util.ArrayList');  
    plus.android.importClass('android.content.pm.PackageInfo');  
    plus.android.importClass('android.content.pm.PackageManager');  
    var ApplicationInfo = plus.android.importClass('android.content.pm.ApplicationInfo');  
    var MainActivity = plus.android.runtimeMainActivity();  
    var PackageManager = MainActivity.getPackageManager();  
    var pinfo = plus.android.invoke(PackageManager, 'getInstalledPackages', 0)  
    if (pinfo != null) {  
        var apkdist = {}  
        for (var i = 0; i < pinfo.size(); i++) {  
            //PackageInfo{4b45699f9d  com.tencent.mobileqq}      
            var pkginfo = pinfo.get(i);  
            var issysapk = ((pkginfo.plusGetAttribute("applicationInfo").plusGetAttribute("flags") & ApplicationInfo.FLAG_SYSTEM) != 0) ? true : false  
            if (issysapk == false && pkginfo.plusGetAttribute("packageName") === package_name) {  
                apkdist.appName = pkginfo.plusGetAttribute("applicationInfo").loadLabel(PackageManager).toString();  
                apkdist.packageName = pkginfo.plusGetAttribute("packageName");  
                apkdist.versionName = pkginfo.plusGetAttribute("versionName");  
                apkdist.versionCode = pkginfo.plusGetAttribute("versionCode");  
            }  
        }  
        if (typeof information === 'function' && isEmptyObject(apkdist) === false) {  
            information(null, {code: true, information: apkdist})  
        }else {  
            if (typeof information === 'function') {  
                information(null, {code: false, information: {}})  
            }  
        }  
    }  
}

使用方法:

getAppInformation('你要获取的应用包名', function(err, data) {  
       console.log(JSON.stringify(data))  
})
继续阅读 »

代码:

// 通过包名获取应用基本信息  形参:包名  返回信息{code: true/fals, information: {"appName":"NP管理器","packageName":"player.normal.np","versionName":"3.0.54","versionCode":20221004}}  
function getAppInformation(package_name, information) {  
    function isEmptyObject(obj) {  
        return Object.keys(obj).length === 0;  
    }  
    plus.android.importClass('java.util.ArrayList');  
    plus.android.importClass('android.content.pm.PackageInfo');  
    plus.android.importClass('android.content.pm.PackageManager');  
    var ApplicationInfo = plus.android.importClass('android.content.pm.ApplicationInfo');  
    var MainActivity = plus.android.runtimeMainActivity();  
    var PackageManager = MainActivity.getPackageManager();  
    var pinfo = plus.android.invoke(PackageManager, 'getInstalledPackages', 0)  
    if (pinfo != null) {  
        var apkdist = {}  
        for (var i = 0; i < pinfo.size(); i++) {  
            //PackageInfo{4b45699f9d  com.tencent.mobileqq}      
            var pkginfo = pinfo.get(i);  
            var issysapk = ((pkginfo.plusGetAttribute("applicationInfo").plusGetAttribute("flags") & ApplicationInfo.FLAG_SYSTEM) != 0) ? true : false  
            if (issysapk == false && pkginfo.plusGetAttribute("packageName") === package_name) {  
                apkdist.appName = pkginfo.plusGetAttribute("applicationInfo").loadLabel(PackageManager).toString();  
                apkdist.packageName = pkginfo.plusGetAttribute("packageName");  
                apkdist.versionName = pkginfo.plusGetAttribute("versionName");  
                apkdist.versionCode = pkginfo.plusGetAttribute("versionCode");  
            }  
        }  
        if (typeof information === 'function' && isEmptyObject(apkdist) === false) {  
            information(null, {code: true, information: apkdist})  
        }else {  
            if (typeof information === 'function') {  
                information(null, {code: false, information: {}})  
            }  
        }  
    }  
}

使用方法:

getAppInformation('你要获取的应用包名', function(err, data) {  
       console.log(JSON.stringify(data))  
})
收起阅读 »

vue3+electron31+vite5客户端聊天应用

vue3

基于最新跨平台技术原创研发vue3+electron31.x+vite5+pinia2+element-plus仿微信电脑版聊天室Exe程序。整个聊天程序界面清爽简约,支持展示/收缩侧边栏、electron新开多窗口、换肤等功能。

electron31+vite5+vue3+elementPlus仿微信客户端聊天exe程序ElectronViteChat

img

img

vue3-electron-wechat使用vite5.x构建工具搭建项目模板,采用vue3 setup语法开发,融合electron跨平台技术。

img

实现技术

  • 编码工具:Vscode
  • 技术框架:electron31+vite5+vue3.4.29+vue-router4.4.0
  • 跨端框架:electron^31.1.0
  • 组件库:element-plus^2.7.6 (饿了么vue3组件库)
  • 状态管理:pinia^2.1.7
  • 存储服务:pinia-plugin-persistedstate^3.2.1
  • electron打包工具:electron-builder^24.13.3
  • electron整合vite插件:vite-plugin-electron^0.28.7

img

img

前段时间有分享一篇vue3+vite5网页版聊天室,感兴趣的可以去看看。
https://ask.dcloud.net.cn/article/41153

项目结构

img

vite-electronChat聊天已经同步到我的最新原创作品集,有需要的可以去看看。
https://gf.bilibili.com/item/detail/1106312011

img

入口配置main.js

import { createApp } from 'vue'  
import './style.scss'  
import App from './App.vue'  

// 引入组件库  
import ElementPlus from 'element-plus'  
import 'element-plus/dist/index.css'  

// 引入路由/状态管理  
import Router from './router'  
import Pinia from './pinia'  

import { launchApp } from '@/windows/actions'  

launchApp().then(config => {  
  if(config) {  
    console.log('窗口参数:', config)  
    console.log('窗口id:', config?.id)  

    // 全局存储窗口配置  
    window.config = config  
  }  

  // 创建app应用实例  
  createApp(App)  
  .use(ElementPlus)  
  .use(Router)  
  .use(Pinia)  
  .mount('#app')  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

electron主进程

img

/**  
 * electron主进程入口配置  
 * @author andy  
 */  

import { app, BrowserWindow } from 'electron'  

import { WindowManager } from '../src/windows/index.js'  

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)  
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true  

const createWindow = () => {  
  let win = new WindowManager()  
  win.create({isMajor: true})  
  // 系统托盘管理  
  win.trayManager()  
  // 监听ipcMain事件  
  win.ipcManager()  
}  

app.whenReady().then(() => {  
  createWindow()  

  app.on('activate', () => {  
    if(BrowserWindow.getAllWindows().length === 0) createWindow()  
  })  
})  

app.on('window-all-closed', () => {  
  if(process.platform !== 'darwin') app.quit()  
})

项目布局模板

img

<template>  
  <template v-if="!route?.meta?.isNewWin">  
    <div  
      class="vu__container flexbox flex-alignc flex-justifyc"  
      :style="{'--themeSkin': appstate.config.skin}"  
    >  
      <div class="vu__layout flexbox flex-col">  
        <div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>  
          <!-- 菜单栏 -->  
          <slot v-if="!route?.meta?.hideMenuBar" name="menubar">  
            <MenuBar />  
          </slot>  

          <!-- 侧边栏 -->  
          <div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">  
            <aside class="vu__layout-sidebar__body flexbox flex-col">  
              <slot name="sidebar">  
                <SideBar />  
              </slot>  
            </aside>  
          </div>  

          <!-- 主内容区 -->  
          <div class="vu__layout-main flex1 flexbox flex-col">  
            <ToolBar v-if="!route?.meta?.hideToolBar" />  
            <router-view v-slot="{ Component, route }">  
              <keep-alive>  
                <component :is="Component" :key="route.path" />  
              </keep-alive>  
            </router-view>  
          </div>  
        </div>  
      </div>  
    </div>  
  </template>  
  <template v-else>  
    <WinLayout />  
  </template>  
</template>

electron多窗口|新开窗口

img

/**  
 * 创建新窗口  
 * @param {object} args 窗口配置参数 {url: '/chat', width: 850, height: 600, ...}  
 */  
export function winCreate(args) {  
  window.electron.send('win-create', args)  
}
// 登录窗口  
export function loginWindow() {  
  winCreate({  
    url: '/login',  
    title: '登录',  
    width: 320,  
    height: 380,  
    isMajor: true,  
    resizable: false,  
    maximizable: false,  
    alwaysOnTop: true  
  })  
}  

// 关于窗口  
export function aboutWindow() {  
  winCreate({  
    url: '/win/about',  
    title: '关于',  
    width: 375,  
    height: 300,  
    minWidth: 375,  
    minHeight: 300,  
    maximizable: false,  
    alwaysOnTop: true,  
  })  
}  

// 设置窗口  
export function settingWindow() {  
  winCreate({  
    url: '/win/setting',  
    title: '设置',  
    width: 550,  
    height: 470,  
    resizable: false,  
    maximizable: false,  
  })  
}

新建窗口支持如下参数配置:

// 自定义窗口参数  
const windowOptions = {  
  // 窗口唯一标识id  
  id: null,  
  // 窗口标题  
  title: 'Electron-ViteChat',  
  // 窗口路由地址  
  url: '',  
  // 窗口数据传参  
  data: null,  
  // 是否是主窗口(为true则会关闭所有窗口并创建一个新窗口)  
  isMajor: false,  
  // 是否支持多开窗口(为true则支持创建多个窗口)  
  isMultiple: false,  
  // 窗口是否最大化  
  maximize: false,  
}  

// 系统窗口参数(与electron的new BrowserWindow()参数一致)  
const windowBaseOptions = {  
  // 窗口图标  
  icon: join(__root, 'resources/shortcut.ico'),  
  // 是否自动隐藏菜单栏(按下Alt键显示)  
  autoHideMenuBar: true,  
  // 窗口标题栏样式  
  titleBarStyle: 'hidden',  
  // 窗口背景色  
  backgroundColor: '#fff',  
  // 宽度  
  width: 840,  
  // 高度  
  height: 610,  
  // 最小宽度  
  minWidth: '',  
  // 最小高度  
  minHeight: '',  
  // 窗口x坐标  
  x: '',  
  // 窗口y坐标  
  y: '',  
  // 是否可缩放  
  resizable: true,  
  // 是否可最小化  
  minimizable: true,  
  // 是否可最大化  
  maximizable: true,  
  // 是否可关闭  
  closable: true,  
  // 父窗口  
  parent: null,  
  // 是否模态窗口  
  modal: false,  
  // 窗口是否置顶  
  alwaysOnTop: false,  
  // 是否显示窗口边框(要创建无边框窗口,将frame参数设置为false)  
  frame: false,  
  // 是否透明窗口(仅frame: false有效)  
  transparent: false,  
  // 创建时是否显示窗口  
  show: false,  
}

electron-builder打包配置

{  
  "productName": "Electron-ViteChat",  
  "appId": "com.andy.electron-vite-wechat",  
  "copyright": "Copyright © 2024-present Andy  Q:282310962",  
  "compression": "maximum",  
  "asar": true,  
  "directories": {  
    "output": "release/${version}"  
  },  
  "nsis": {  
    "oneClick": false,  
    "allowToChangeInstallationDirectory": true,  
    "perMachine": true,  
    "deleteAppDataOnUninstall": true,  
    "createDesktopShortcut": true,  
    "createStartMenuShortcut": true,  
    "shortcutName": "ElectronViteChat"  
  },  
  "win": {  
    "icon": "./resources/shortcut.ico",  
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",  
    "target": [  
      {  
        "target": "nsis",  
        "arch": ["ia32"]  
      }  
    ]  
  },  
  "mac": {  
    "icon": "./resources/shortcut.icns",  
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"  
  },  
  "linux": {  
    "icon": "./resources",  
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"  
  }  
}

综上就是vue3+vite5+electron31实战开发仿微信客户端聊天的一些知识分享,希望对大家有些帮助~

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045042968
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

基于最新跨平台技术原创研发vue3+electron31.x+vite5+pinia2+element-plus仿微信电脑版聊天室Exe程序。整个聊天程序界面清爽简约,支持展示/收缩侧边栏、electron新开多窗口、换肤等功能。

electron31+vite5+vue3+elementPlus仿微信客户端聊天exe程序ElectronViteChat

img

img

vue3-electron-wechat使用vite5.x构建工具搭建项目模板,采用vue3 setup语法开发,融合electron跨平台技术。

img

实现技术

  • 编码工具:Vscode
  • 技术框架:electron31+vite5+vue3.4.29+vue-router4.4.0
  • 跨端框架:electron^31.1.0
  • 组件库:element-plus^2.7.6 (饿了么vue3组件库)
  • 状态管理:pinia^2.1.7
  • 存储服务:pinia-plugin-persistedstate^3.2.1
  • electron打包工具:electron-builder^24.13.3
  • electron整合vite插件:vite-plugin-electron^0.28.7

img

img

前段时间有分享一篇vue3+vite5网页版聊天室,感兴趣的可以去看看。
https://ask.dcloud.net.cn/article/41153

项目结构

img

vite-electronChat聊天已经同步到我的最新原创作品集,有需要的可以去看看。
https://gf.bilibili.com/item/detail/1106312011

img

入口配置main.js

import { createApp } from 'vue'  
import './style.scss'  
import App from './App.vue'  

// 引入组件库  
import ElementPlus from 'element-plus'  
import 'element-plus/dist/index.css'  

// 引入路由/状态管理  
import Router from './router'  
import Pinia from './pinia'  

import { launchApp } from '@/windows/actions'  

launchApp().then(config => {  
  if(config) {  
    console.log('窗口参数:', config)  
    console.log('窗口id:', config?.id)  

    // 全局存储窗口配置  
    window.config = config  
  }  

  // 创建app应用实例  
  createApp(App)  
  .use(ElementPlus)  
  .use(Router)  
  .use(Pinia)  
  .mount('#app')  
})

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

electron主进程

img

/**  
 * electron主进程入口配置  
 * @author andy  
 */  

import { app, BrowserWindow } from 'electron'  

import { WindowManager } from '../src/windows/index.js'  

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)  
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true  

const createWindow = () => {  
  let win = new WindowManager()  
  win.create({isMajor: true})  
  // 系统托盘管理  
  win.trayManager()  
  // 监听ipcMain事件  
  win.ipcManager()  
}  

app.whenReady().then(() => {  
  createWindow()  

  app.on('activate', () => {  
    if(BrowserWindow.getAllWindows().length === 0) createWindow()  
  })  
})  

app.on('window-all-closed', () => {  
  if(process.platform !== 'darwin') app.quit()  
})

项目布局模板

img

<template>  
  <template v-if="!route?.meta?.isNewWin">  
    <div  
      class="vu__container flexbox flex-alignc flex-justifyc"  
      :style="{'--themeSkin': appstate.config.skin}"  
    >  
      <div class="vu__layout flexbox flex-col">  
        <div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>  
          <!-- 菜单栏 -->  
          <slot v-if="!route?.meta?.hideMenuBar" name="menubar">  
            <MenuBar />  
          </slot>  

          <!-- 侧边栏 -->  
          <div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">  
            <aside class="vu__layout-sidebar__body flexbox flex-col">  
              <slot name="sidebar">  
                <SideBar />  
              </slot>  
            </aside>  
          </div>  

          <!-- 主内容区 -->  
          <div class="vu__layout-main flex1 flexbox flex-col">  
            <ToolBar v-if="!route?.meta?.hideToolBar" />  
            <router-view v-slot="{ Component, route }">  
              <keep-alive>  
                <component :is="Component" :key="route.path" />  
              </keep-alive>  
            </router-view>  
          </div>  
        </div>  
      </div>  
    </div>  
  </template>  
  <template v-else>  
    <WinLayout />  
  </template>  
</template>

electron多窗口|新开窗口

img

/**  
 * 创建新窗口  
 * @param {object} args 窗口配置参数 {url: '/chat', width: 850, height: 600, ...}  
 */  
export function winCreate(args) {  
  window.electron.send('win-create', args)  
}
// 登录窗口  
export function loginWindow() {  
  winCreate({  
    url: '/login',  
    title: '登录',  
    width: 320,  
    height: 380,  
    isMajor: true,  
    resizable: false,  
    maximizable: false,  
    alwaysOnTop: true  
  })  
}  

// 关于窗口  
export function aboutWindow() {  
  winCreate({  
    url: '/win/about',  
    title: '关于',  
    width: 375,  
    height: 300,  
    minWidth: 375,  
    minHeight: 300,  
    maximizable: false,  
    alwaysOnTop: true,  
  })  
}  

// 设置窗口  
export function settingWindow() {  
  winCreate({  
    url: '/win/setting',  
    title: '设置',  
    width: 550,  
    height: 470,  
    resizable: false,  
    maximizable: false,  
  })  
}

新建窗口支持如下参数配置:

// 自定义窗口参数  
const windowOptions = {  
  // 窗口唯一标识id  
  id: null,  
  // 窗口标题  
  title: 'Electron-ViteChat',  
  // 窗口路由地址  
  url: '',  
  // 窗口数据传参  
  data: null,  
  // 是否是主窗口(为true则会关闭所有窗口并创建一个新窗口)  
  isMajor: false,  
  // 是否支持多开窗口(为true则支持创建多个窗口)  
  isMultiple: false,  
  // 窗口是否最大化  
  maximize: false,  
}  

// 系统窗口参数(与electron的new BrowserWindow()参数一致)  
const windowBaseOptions = {  
  // 窗口图标  
  icon: join(__root, 'resources/shortcut.ico'),  
  // 是否自动隐藏菜单栏(按下Alt键显示)  
  autoHideMenuBar: true,  
  // 窗口标题栏样式  
  titleBarStyle: 'hidden',  
  // 窗口背景色  
  backgroundColor: '#fff',  
  // 宽度  
  width: 840,  
  // 高度  
  height: 610,  
  // 最小宽度  
  minWidth: '',  
  // 最小高度  
  minHeight: '',  
  // 窗口x坐标  
  x: '',  
  // 窗口y坐标  
  y: '',  
  // 是否可缩放  
  resizable: true,  
  // 是否可最小化  
  minimizable: true,  
  // 是否可最大化  
  maximizable: true,  
  // 是否可关闭  
  closable: true,  
  // 父窗口  
  parent: null,  
  // 是否模态窗口  
  modal: false,  
  // 窗口是否置顶  
  alwaysOnTop: false,  
  // 是否显示窗口边框(要创建无边框窗口,将frame参数设置为false)  
  frame: false,  
  // 是否透明窗口(仅frame: false有效)  
  transparent: false,  
  // 创建时是否显示窗口  
  show: false,  
}

electron-builder打包配置

{  
  "productName": "Electron-ViteChat",  
  "appId": "com.andy.electron-vite-wechat",  
  "copyright": "Copyright © 2024-present Andy  Q:282310962",  
  "compression": "maximum",  
  "asar": true,  
  "directories": {  
    "output": "release/${version}"  
  },  
  "nsis": {  
    "oneClick": false,  
    "allowToChangeInstallationDirectory": true,  
    "perMachine": true,  
    "deleteAppDataOnUninstall": true,  
    "createDesktopShortcut": true,  
    "createStartMenuShortcut": true,  
    "shortcutName": "ElectronViteChat"  
  },  
  "win": {  
    "icon": "./resources/shortcut.ico",  
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",  
    "target": [  
      {  
        "target": "nsis",  
        "arch": ["ia32"]  
      }  
    ]  
  },  
  "mac": {  
    "icon": "./resources/shortcut.icns",  
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"  
  },  
  "linux": {  
    "icon": "./resources",  
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"  
  }  
}

综上就是vue3+vite5+electron31实战开发仿微信客户端聊天的一些知识分享,希望对大家有些帮助~

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045042968
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

监听实时定位plus.geolocation.watchPosition方法无法关闭,调用 clearWatch()方法也没用

Geolocation
                                         var _this = this  
                     _this.watchId = plus.geolocation.watchPosition(function(position) {  
                        let coords = position.coords;  
                        let address = position.addresses  
                        console.log("获取经纬度信息:", coords.longitude, coords.latitude)  
                        console.log("获取地址信息:", address)  
                        // console.log("返回id" ,plus.geolocation.watchPosition());  
                        _this.checkAdress(coords.latitude, coords.longitude)  
                        _this.longitude = coords.longitude;  
                        _this.latitude = coords.latitude;  
                        _this.dataForm.punchSite = coords.longitude + ',' + coords.latitude  
                        //标记点赋值  
                        _this.markers[0].longitude = coords.longitude;  
                        _this.markers[0].latitude = coords.latitude;  
                        //获取当前当前位置的名称  
                        _this.locationcourier = address;  
                        _this.dataForm.punchSiteName = _this.locationcourier  
                    }, function(e) {  
                        console.log("监听位置变化信息失败:" + e.message);  
                    }, {  
                        'enableHighAccuracy': true,  
                        'geocode': true,  
                        'provider': 'amap',  
                        // 'maximumAge': 3000  
                    });

获取的watchId 不是number 而是个类似于'timer13213123'的字符串,无法,怎么关都关不掉,哪怕退出后台都还在获取地址,打印plus.geolocation.clearWatch() 发现是个undefined ,我人都傻了,这个h5+到底是啥意思,不能用的东西放上来干啥 ,全是bug,有没有大佬遇到过的,给点建议

继续阅读 »
                                         var _this = this  
                     _this.watchId = plus.geolocation.watchPosition(function(position) {  
                        let coords = position.coords;  
                        let address = position.addresses  
                        console.log("获取经纬度信息:", coords.longitude, coords.latitude)  
                        console.log("获取地址信息:", address)  
                        // console.log("返回id" ,plus.geolocation.watchPosition());  
                        _this.checkAdress(coords.latitude, coords.longitude)  
                        _this.longitude = coords.longitude;  
                        _this.latitude = coords.latitude;  
                        _this.dataForm.punchSite = coords.longitude + ',' + coords.latitude  
                        //标记点赋值  
                        _this.markers[0].longitude = coords.longitude;  
                        _this.markers[0].latitude = coords.latitude;  
                        //获取当前当前位置的名称  
                        _this.locationcourier = address;  
                        _this.dataForm.punchSiteName = _this.locationcourier  
                    }, function(e) {  
                        console.log("监听位置变化信息失败:" + e.message);  
                    }, {  
                        'enableHighAccuracy': true,  
                        'geocode': true,  
                        'provider': 'amap',  
                        // 'maximumAge': 3000  
                    });

获取的watchId 不是number 而是个类似于'timer13213123'的字符串,无法,怎么关都关不掉,哪怕退出后台都还在获取地址,打印plus.geolocation.clearWatch() 发现是个undefined ,我人都傻了,这个h5+到底是啥意思,不能用的东西放上来干啥 ,全是bug,有没有大佬遇到过的,给点建议

收起阅读 »