electron仿百度云桌面版,Vue端基础配置和electronAPI封装

在上一篇什么是electron,vue如何应用electron中我们已经了解了electron的一些基本信息和在Vue配置electron的一些基本操作,现在由我们来进入正式的项目阶段。

我们拿上一篇创建好的Vue架子。

具体内容预览:

  1. 页面布局
  2. electron项目用到的功能封装
  3. 路由配置
  4. 请求拦截器封装

emm.. 由于项目比较简单,我就不画流程图、逻辑图之类的了,本猴最近995的厉害,见谅见谅。

界面布局以及一些简单逻辑我就略过了,后续整理好会放GitHub连接供大家clone

快速搭建Vue前端页面

页面结构:
image.png

部分界面如下:
image.png

electron常用功能

在electron目录下新增lib目录,作为常用的函数存放地址,按照百度云的功能,我们重点关注的功能有:

  1. 下载
  2. 设置下载路径
  3. 下载中途暂停
  4. 取消下载
  5. 断点恢复下载
  6. 应用基础功能

我们需要了解到如下API:

  1. ipcMain: 主进程,可以接收Vue端发送过来的消息
  2. ipcRenderer:可以通过本身发送消息给主进程,当然也可以收到主进程回复的消息(主要用于Vue端分发事件)
  3. reply: 主进程用来回复视图端份发过来的事件异步处理结果。

根据这三个API,就可以实现主进程和视图逻辑层之间的通信

下载

我们翻看electron的文档,发现其下载功能Session中,首先我们先将其引入, 并且写成一个函数封起来,方便复用。

const { session } = require('electron')

const downfile = (url) => {
    session.defaultSession.downloadURL(url)
  }
复制代码

利用session.defaultSession.on 去监听下载,并且获取下载时的参数和状态,用于更新视图

session.defaultSession.on('will-download', (e, item) => {
    try {
      const url = item.getURL()
      let cacheItem = cacheDownItem[url] || { // 获取当前下载参数(后续会解释这个参数从哪里来)
            notSend: true
      }
      // 获取文件的总大小
      const totalBytes = item.getTotalBytes()
      // 设置下载路径
      const filePath = path.join(app.getPath('downloads'), item.getFilename())
      item.setSavePath(filePath) 

      // 更新状态并且缓存downitem
      cacheItem._downFileItem = item
      cacheItem.path = item.getSavePath() // 文件路径
      cacheItem.eTag = item.getETag() // eTag
      cacheItem.urlChain = item.getURLChain() // 项目的完整URL链,包括任何重定向
      cacheItem.length = totalBytes
      cacheItem.lastModified = item.getLastModifiedTime() // 剩余时间
      cacheItem.startTime = item.getStartTime() // 下载总耗时

      let lastBytes = 0

      // 监听下载过程,计算并设置进度条进度
      item.on('updated', (event, state) => {
        if (state === 'interrupted') { // 是否下载中断
          cacheItem.state = 'interrupted' // 更新下载数据缓存状态
        } else if (state === 'progressing') { // 下载进度
          if (item.isPaused()) { // 是否被暂停了
            cacheItem.state = 'paused'
          } else { // 保存当前下载的行为参数
            const offset = item.getReceivedBytes() // 已经下了多少
            cacheItem.state = 'downing' // 正在下载
            cacheItem.speedBytes = offset - lastBytes // 剩余多少
            cacheItem.progress = parseInt((offset / totalBytes) * 100)
            cacheItem.offset = offset
            lastBytes = offset
          }
        }
        !cacheItem.notSend && win.webContents.send('update-down-state', JSON.parse(JSON.stringify(cacheItem)))
      })

      // 下载完成
      item.once('done', (event, state) => { // 更新当前下载对象状态,用于后续视图更新
        cacheItem.done = 'end'
        switch (state) {
          case 'interrupted':
            cacheItem.state = 'interrupted-err'
            break
          case 'cancelle':
            cacheItem.state = 'cancelle'
            break
          default:
            cacheItem.state = 'completed'
            notification(cacheItem.path)
            break
        }

        !cacheItem.notSend && win.webContents.send('update-down-state', JSON.parse(JSON.stringify(cacheItem)))

        // 请求后端改变文件状态
        axios.post('http://1localhost:3000/file/dow_file',
          { file_obj: cacheDownItem[url] },
          {
            headers: {
              'Content-Type': 'application/json;charset=UTF-8'
            }
          })

        // 删除缓存
        delete cacheDownItem[url]
        cacheItem = null
        item = null
      })

      // 恢复
      if (item.canResume) {
        item.resume()
      }
    } catch (error) {
      console.log(error)
    }
  })
复制代码

设置下载路径

const { app, ipcMain, shell, dialog } = require('electron') // 引入所需要的API


  ipcMain.on('set_path', (e, data = {}) => { // 主进程监听vue派发的事件
    const { path } = data
    if (path) {
      if (path !== 'not') app.setPath('downloads', path)
      e.reply('set_path', app.getPath('downloads'))
    } else {
      dialog.showOpenDialog({ // 打开文件窗口
        title: '选择下载目录',
        defaultPath: app.getPath('downloads'),
        properties: ['openDirectory']
      }).then((files) => {
        if (!files.canceled) { // 如果有选中
          app.setPath('downloads', files.filePaths[0])
        }
        e.reply('set_path', files) // 设置完成 回复vue端,方便vue端做处理
      })
    }
  })

复制代码

下载中途暂停

ipcMain.on('down-file-pause', function(e, data) {  // 主进程监听vue派发的事件
    const { url } = data
    const t = cacheDownItem[url] // 拿到缓存上的当前下载的参数
    if (t) {
      t._downFileItem.pause() // 更新其状态
    }
    e.reply('down-file-pause-' + url, '已暂停') // 通知派发端
  })

复制代码

取消下载

逻辑如上

ipcMain.on('down-file-cancel', function(e, data) {
    const { url } = data
    const t = cacheDownItem[url]
    if (t) {
      t._downFileItem.cancel()
    } else {
      // 删除未下在完成文件
    }
    e.reply('down-file-cancel-' + url, '已取消下载')
  })
复制代码

恢复下载

ipcMain.on('resume-download', function(e, data) {
    const { url } = data
    const t = cacheDownItem[url]
    if (t) {
      t._downFileItem.resume()
    } else {
      cacheDownItem[url] = { ...data }
      resumeDownload(data)
    }
    e.reply('down-file-resume-' + url, '已恢复下载')
  })

复制代码

应用基础功能

 // 打开调试
  ipcMain.on('toggle_dev_tools', function(event, arg) {
    win.webContents.toggleDevTools()
  })

  // 重启
  ipcMain.on('restart', function() {
    app.relaunch()
    app.exit(0)
  })

  // 最小化
  ipcMain.on('min', function() {
    win.minimize()
  })

  // 最大化
  ipcMain.on('max', function() {
    if (win.isMaximized()) {
      win.unmaximize()
    } else {
      win.maximize()
    }
  })

  // 关闭程序
  ipcMain.on('close', function() {
    cacheDownItemClose()
    win.close()
  })
复制代码

以上就是本项目所用到的部分electron功能,后续还会再加

路由配置

路由采用的是哈希路由和路由懒加载

image.png

开发环境:
image.png

生产环境:

image.png

配置:

image.png

请求拦截器封装

import axios from 'axios'
import store from '@/store'
import { Message } from 'element-ui'
import { getToken } from './tokne'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 1000000
})

service.interceptors.request.use(
  config => {
    config.headers['Content-Type'] = 'application/json;charset=UTF-8'
    store.getters.token && (config.headers['Authorization'] = `Bearer ${getToken()}`)
    return config
  },
  error => {
    Message({
      message: error || '网络错误',
      duration: 1000,
      type: 'error'
    })
    return Promise.reject(error)
  }
)

service.interceptors.response.use(
  response => {
    const res = response.data || response
    const errMsg = res.msg || '请求失败!'
    if (res.code && res.code !== 200) {
      Message({
        message: errMsg || '请求失败',
        duration: 1000,
        type: 'error'
      })
      return Promise.reject('error')
    } else {
      return Promise.resolve(res)
    }
  },
  error => {
    if (error.request.status === 401) { store.dispatch('resetToken') }
    Message({
      message: error || '请求失败',
      duration: 1000,
      type: 'error'
    })
    return Promise.reject(error)
  }
)

export default service

复制代码

项目的基础配置就讲到这里啦,下篇预告:node+express+mysql 基本搭建,如果对此感兴趣的话,请关注本专栏或者关注本猴,能追踪到后续篇章哦!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享