为了账号安全,请及时绑定邮箱和手机立即绑定

Electron+Vue3+TypeScript+Vite桌面应用TodoList UI架构

标签:
CSS3 Vue.js

前言

上一章节[《Electron+Vue3+TypeScript+Vite桌面应用程序项目初始化》]完成后,我们的桌面应用程序如上图,这次我们先美化一下界面,形成初步的UI框架。

从这里开始需要书写css,我们选择SCSS工具,先安装:

npm install sass -save-dev

新建assets/scss/globalVariable.scss

// 这里书写全局的css样式,或者方法等

修改 vite.config.ts

...
css: {
  //css预处理
  preprocessorOptions: {
    scss: {
      /*
      引入.scss全局预定义变量
      */
      additionalData: '@import "./src/assets/scss/globalVariable.scss";@import "./src/assets/scss/iconfont.scss";'
    }
  }
}

窗口优化

由上图可以看到,默认的菜单栏、最小化最大化关闭按钮都不是很美观,所以我们来自定义一下。

掉标题栏和边框

修改electron-main/index.ts

...
mainWindow = new BrowserWindow({
  ...
  frame: false, // 标题栏和边框一并隐藏
});
...

自定义标题栏

新建组件components/c-header.vue,利用 flex布局 两端对齐,左侧程序名称,右侧最小化、最大化和关闭按钮

<template>
  <div class="header flex">
    <div class="header-title flex1 flex-align-center">Todo List</div>
    <div class="header-btns" v-if="showBtn">
      <component :is="components.get(compName)" type="min" title="最小化" content="icon-jianhao"></component>
      <component :is="components.get(compName)" type="max" title="最大化" content="icon-fangxingweixuanzhong"></component>
      <component :is="components.get(compName)" type="close" title="关闭" content="icon-guanbi1"></component>
    </div>
  </div>
</template>

<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue'

// 判断是node还是浏览器环境
// 浏览器环境就不用显示最小化、最大化、关闭按钮
// 根据环境动态加载组件
// 如果不这样处理,浏览器会报错 require undefault
let showBtn = typeof global!='undefined'
const components = ref(new Map<string, any>())
if(showBtn){
  components.value.set(
    'headerBtn',
    defineAsyncComponent(() => import('./c-header-btn.vue'))
  )
}
const compName = ref('headerBtn')

</script>

<style lang="scss" scoped>
.header{
  -webkit-app-region: drag;
  padding: 15px 15px;
  &-title{
    font-weight: 600;
  }
  &-btns{
    &>div{
      &:last-child{
        padding-right: 0;
      }
    }
  }
}
</style>

上面的 flex 默认选择器在全局的scss里面以书写

// assets/scss/globalVariable.scss
.flex {
  display: flex;
  .flex1{
    flex: 1;
  }
}

.flex-align-center {
  @extend .flex;
  align-items: center;
}

.flex-space {
  @extend .flex;
  justify-content: space-between;
}

.flex-space-center {
  @extend .flex;
  align-items: center;
  justify-content: space-between;
}

.flex-center-center {
  @extend .flex;
  align-items: center;
  justify-content: center;
}

.flex-row {
  flex-direction: row;
}

.flex-col {
  flex-direction: column;
}

.flex-wrap {
  flex-wrap: wrap;
}

新建components/c-header-btn.vue

<template>
  <div 
    class="c-header-btn" 
    v-on:click="click">
    <i class="iconfont" :class="content"></i>
  </div>
</template>

<script lang="ts">
import electron from 'electron';

export default {
  name: 'c-header-btn',
  props: {
    type: [String],
    content: [String],
  },
  data(){
  },
  methods: {
    click: function () {
      if(this.type && typeof global!='undefined'){
        // 向主进程发送异步消息,下面在electron-main/index.ts接收
        electron.ipcRenderer.send(this.type);
      }
    }
  }
}
</script>
    
<style lang="scss" scoped>
.c-header-btn {
  position: relative;
  padding-right: 15px;
  -webkit-app-region: no-drag;
  color: #2798f7;
  cursor: pointer;
  display: inline-block;
  &:hover i{
    color: #ED5A5A;
  }
}
</style>

electron-main/index.ts监听按钮事件

// electron-main/index.ts
import { ..., ipcMain } from 'electron';
...
let mainWindow : any;

const createWindow = () => {
  mainWindow = new BrowserWindow({
    ...
  });
}

// 监听自定义按钮事件,并作出相应动作
// 最小化应用程序
ipcMain.on('min', e => mainWindow.minimize())
// 最大化应用程序
ipcMain.on('max', e => {
  if (mainWindow.isMaximized()) {
    mainWindow.unmaximize()
  } else {
    mainWindow.maximize()
  }
})
// 关闭应用程序
ipcMain.on('close', e => mainWindow.close())

App.vue 导入 c-header.vue 组件

<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import cHeader from './components/c-header.vue'
</script>

<template>
  <div class="layout flex flex-col">
    <c-header/>
  </div>
</template>


<style lang="scss" scoped>
.layout {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  height: 100%;
}
</style>

todo list 事项面板UI

新建components/c-main.vue

<script setup lang="ts">

</script>

<template>
  <div class="main">
    <div class="group">待处理</div>
    <div class="group">进行中</div>
    <div class="group">待发布</div>
    <div class="group">已发布</div>
    <div class="group">已完成</div>
    <div class="group">待观察</div>
  </div>
</template>

<style lang="scss" scoped>
.main{
  overflow-x: auto;
  white-space: nowrap;
  padding: 0 15px;
  .group{
    display: inline-block;
    width: 200px;
    height: 100%;
    margin-right: 10px;
    // 随机背景色,用来看清布局
    @for $i from 1 through 30 {
    &:nth-child(#{$i}) {
        background: rgba(random(255), random(255), random(255), 0.8);
      }
    }
  }
  
}
</style>

事项分组group可能有N个,所以这里css样式让group一直向右排列,overflow-x: auto; white-space: nowrap;

App.vue 导入 c-main.vue 组件

<script setup lang="ts">
...
import cMain from './components/c-main.vue'
</script>

<template>
  <div class="layout flex flex-col">
    <c-header/>
    <c-main class="flex1"/>
  </div>
</template>

结语

到这里,项目的UI框架基本结束,下一章节将集成 Vue.Draggable 拖拽任务。

本项目将持续更新,希望你也持续关注。

项目地址

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
1.5万
获赞与收藏
5278

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消