前言

上一章节[《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 拖拽任务。

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

项目地址

作者:|向建峰_Javan|,原文链接: http://www.imooc.com/article/325450

文章推荐

「网络知识」FTP主被动模式介绍及抓包分析

Node.js躬行记(28)——Cypress自动化测试实践

linux安装tomcat,mysql

Spring中Bean的实例化详细流程

Disruptor-简单使用

深入理解Linux中的nohup命令

Angular 应用里 ng-package.json 文件的作用是什么?

前端设计模式——过滤器模式

python安装robotframework的一些常见的错误

1亿条数据批量插入 MySQL,哪种方式最快?

【JS 逆向百例】某音 X-Bogus 逆向分析,JSVMP 纯算法还

基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接...