Electron with Vue 框架搭建及打印功能分享

Electron with Vue 框架搭建及打印功能分享


2019.09.27
文档有改进地方,欢迎提出。

安装


  1. 准备工作:确保本机已经安装了nodejs && npm

  2. 检查本地是否已经安装了vue:npm view vue version;否则先安装vue:npm install vue

  3. 创建一个electron-vue的demo项目:vue create electron-vue-demo

  4. 安装配置:

    ? Please pick a preset:
      default (babel, eslint)
    > Manually select features
    
    ? Check the features needed for your project:
     (*) Babel
     ( ) TypeScript
     ( ) Progressive Web App (PWA) Support
     (*) Router
     (*) Vuex
    >(*) CSS Pre-processors
     (*) Linter / Formatter
     ( ) Unit Testing
     ( ) E2E Testing
    
    Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) y
    
    ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):
      Sass/SCSS (with dart-sass)
      Sass/SCSS (with node-sass)
    > Less
      Stylus
    
    ? Pick a linter / formatter config: (Use arrow keys)
    > ESLint with error prevention only
      ESLint + Airbnb config
      ESLint + Standard config
      ESLint + Prettier
    
    ? Pick a linter / formatter config: (Use arrow keys)
    > ESLint with error prevention only
      ESLint + Airbnb config
      ESLint + Standard config
      ESLint + Prettier
    
    ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
    >(*) Lint on save
     ( ) Lint and fix on commit (requires Git)
    
    ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)
    > In dedicated config files
      In package.json
    
    Save this as a preset for future projects? (y/N) N
    

    此处安装过程需要大约2分钟(视网络决定)

  5. 进入项目目录:cd electron-vue-demo

  6. 启动 Vue.js App 项目:npm run serve,打开浏览器登入:http://localhost:8080/

  7. 安装electron构建包:vue add electron-builder

    使用打印机必须要使用electron 3.0或以下版本,此处先任意选择安装,后续单独更换electron版本。

    npm uninstall electron && npm install [email protected] --save-dev

    ? Choose Electron Version (Use arrow keys)
      ^4.0.0
      ^5.0.0
    > ^6.0.0
    

    electron包文件较大,此处安装过程耗时较长,建议使用淘宝镜像cnpm安装。

  8. 由于electron将版本的问题,./src/background.js中有一处需要更改配置,否则启动不了electron。

    //删除第15行: 
    // protocol.registerSchemesAsPrivileged([{scheme: 'app', privileges: { secure: true, standard: true } }])
    //新增一行:
    protocol.registerStandardSchemes(['app'], { secure: true })
    
  9. 启动electron项目:npm run electron:serve
    Electron with Vue 框架搭建及打印功能分享_第1张图片

  10. 默认配置了热加载,代码保存后,electron窗口会体现最新效果。

搭建框架


  1. 安装axios:npm install axios --save-dev

  2. 封装数据请求方法:

    新建文件夹:./src/axios/

    新建文件并粘贴代码:

    api.js

    import requestAll from "./request"; //引用fetch,js
    
    import apiHeader from './url'; //引用url.js
    
    // 数据请求
    
    export function Axios(method, api, params = {}) { 
      let header = '';
      header = apiHeader.server;
      // 浏览器访问
      if( !window.require ) {
        header = '/api';
      }
      // electron访问
      return requestAll.Request(header+api, params, method)
    }
    

    request.js

    import axios from 'axios';//引入axios
    
    // post请求
    function Request(url, data = {}, method = 'post') {
      return new Promise((resolve, reject) => {
        axios({
          url: url,
          method: method,
          headers: {
            'Content-Type': 'application/json'
          },
          timeout: 30 * 1000, // 30秒超时
          data: data
        })
          .then(res => {
            // 统一错误状态码
            let errorMessage = '操作失败!';
            switch (res.data.code) {
              case 200: errorMessage = '请求成功'; break;
              case 501: errorMessage = '参数异常'; break;
            }
            res.data.errorMessage = errorMessage;
            //成功
            resolve(res.data)
          })
          .catch(res => {
            //失败
            reject(res)
          })
      })
    }
    
    export default {
      Request: Request
    }
    

    url.js

    export default {
    
      server: 'http://127.0.0.1:12306'
    
    }
    

    使用方法:

    // 引入封装好的axios方法
    import { Axios } from "../axios/api";
    // 使用例子
    Axios("post", url, params)
        .then(res => {
            if (res.code === 200) {
                resolve(res.data);
            } else {
                _this.$alert(res.errorMessage, "提示", {
                    confirmButtonText: "知道了"
                });
            }
        })
        .catch(err => {
            reject();
            _this.$alert(`发生错误!\n${err}`, "提示", {
                confirmButtonText: "知道了"
        });
    });
    

    浏览器访问项目时,会遇到请求跨域问题,解决方法(./vue.config.js文件内容):

    // vue.config.js相关代码
    module.exports = {
      devServer: {
        proxy: {
          '/api': {
            target: 'http://127.0.0.1:8080',
            changeOrigin: true,
            pathRewrite: {
              '^/api': '/'
            }
          }
        }
      }
    }
    
  3. 路由设置:

    ./src/router.js

    import Vue from 'vue'
    import Router from 'vue-router'
    Vue.use(Router)
    export default new Router({
      base: process.env.BASE_URL,
      routes: [
        {
          path: '/',
          name: 'home',
          component: () => import('./views/Home.vue'),
          children: [
            {
              path: '/',
              redirect: 'test'
            },
            {
              path: '/test',
              name: 'test',
              component: () => import('./components/Test.vue') // 主窗口里面的子组件
            }
          ]
        },
        {
          path: '/setUp',
          name: 'setUp',
          component: () => import('./components/SetUp') // 其他窗口的页面文件
        }
      ]
    })
    
    

窗口设置


  1. 对这个文件进行编写配置:./src/background.js

  2. 引入electron中的ipcMain:

    import { ipcMain } from 'electron'
    
  3. 主窗口在项目初始化已经设置好,基本配置如下:

    function createWindow() {
      // Create the browser window.
      win = new BrowserWindow({
        width: 1336, // 窗体宽度
        height: 890, // 窗体高度
        frame: false, // 窗体是否需要标题栏
        resizable: false, // 窗口是否可以改变尺寸
        webPreferences: {
          nodeIntegration: true, // 是否集成Node
          webSecurity: false  // 解决跨域问题
        }
      })
    }
    

    具体更多窗口配置:https://electronjs.org/docs/api/browser-window

  4. electron启动后执行createWindow方法:

    // This method will be called when Electron has finished
    // initialization and is ready to create browser windows.
    // Some APIs can only be used after this event occurs.
    app.on('ready', async () => {
      if (isDevelopment && !process.env.IS_TEST) {
        // Install Vue Devtools
        try {
          await installVueDevtools()
        } catch (e) {
          console.error('Vue Devtools failed to install:', e.toString())
        }
      }
      createWindow()
    })
    
  5. 设置窗口访问的页面(写在createWindow方法内)

    if (process.env.WEBPACK_DEV_SERVER_URL) { // 如果是开发模式
        // Load the url of the dev server if in development mode
        win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
        testWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL + "/#/test")
        // 如果是开发模式,自动打开开发者调试工具
        if (!process.env.IS_TEST) win.webContents.openDevTools()
        if (!process.env.IS_TEST) testWindow.webContents.openDevTools()
    } else {	// 如果是打包模式
        createProtocol('app')
        // Load the index.html when not in development
        win.loadURL('app://./index.html')
        testWindow.loadURL('app://./index.html#/test')
    }
    
  6. 设置窗口,开启关闭功能(在createWindow方法内)

    win.on('closed', () => {
        win = null
    })
    
    testWindow.on('close', (e) => {
        e.preventDefault()
        testWindow.hide();
    })
    

打开和关闭窗口


  1. 文件./src/background.js添加窗口的打开和关闭监听:

    // 打开窗口 
    ipcMain.on('open-test', (event, arg) => {
      testWindow.show();
      testWindow.webContents.send('param', arg)	// 触发这个事件时,传递给将要打开的窗口一个数据
    })
    // 关闭窗口
    ipcMain.on('close-test', function () {
      testWindow.hide();
    })
    
  2. .vue文件里触发窗口的打开或关闭事件:

    const { ipcRenderer } = require("electron");	// 文件头部新建electron控制窗口的上下文对象
    
    ipcRenderer.send( "open-testEdit", { data: this.data } );	// 打开窗口命令,"open-testEdit"是"background.js"文件中添加的事件监听,send的第二个参数是传递给窗口的数据对象
    
    ipcRenderer.send("close-testEdit");	// 关闭窗口
    

窗口之间的通讯


  1. 可以利用electron中的ipcRenderer.send方法给窗口传递数据;当然,在触发打开或关闭窗口的时候也可以传递数据,因为他们都在使用ipcRenderer.send方法。

  2. 如果是单纯传递数据(非窗口打开或关闭),可以这样做:

    // 首先在文件"./src/background.js"中定义监听方法
    ipcMain.on('testChange', (event, message) => {
      win.webContents.send('getTestData', message);
    });	// 当任何页面触发'testChange'方法时,win窗口可以接收到message这份数据
    
    // 注意:接收数据的窗口(.vue页面),同时需要做好接收数据的工作
    mounted() {
        // 需要使用electron的ipcRenderer.on方法,参数1:接收数据事件名称,参数2:接收数据或event的方法体
        ipcRenderer.on("getTestData", (e, message) => {
            console.log( message );
        });
    },
    

构建软件包


  1. ./package.json中给打包操作设置参数

    {
        "name": "WondfoDMS-Pet",		// 项目名称(主要应用在软件运行中的名称)
    	"productName": "WondfoDMS-Pet",	// 软件包名称(安装包上的名称)
    	"version": "6.6.6",			   // 软件版本号
    	"scripts": {
                "electron:build": "vue-cli-service electron:build --windows --dir --ia32",
                /*
                 *参数意义:
                 *@ --windows 打包成window软件包
                 *@ --dir      
                 *@ --ia32    软件包为32位
                 */
      	},
        "win": {
          "icon": "icon.ico",			   // 软件包图标,图标放置于项目根目录,尺寸为256*256
          "extraResources": "./public"	    // 页面项目静态文件入口放置的位置
        }
    }
    
  2. 执行命令npm run electron:build即可打包,软件位置:./dist_electron/win-ia32-unpacked/*.exe

Electron如何实现打印(未完善)


  1. 新建打印中转页面printContent(有关页面路由router配置,此处不讲述),内容:

    <template>
      <div class="component">
        <webview id="printWebview" ref="printWebview" :src="fullPath" nodeintegration />
      </div>
    </template>
    <script>
    const { ipcRenderer } = require("electron");
    import path from "path";
    
    export default {
      name: "printContent",
      data() {
        return {
          printList: [],
          printDeviceName: "",
          fullPath: path.join(__static, "print.html"),
          messageBox: null,
          htmlData: {},
          isAllowPrint: false
        };
      },
      mounted() {
        const webview = this.$refs.printWebview;
        webview.addEventListener("ipc-message", event => {
          if (!this.isAllowPrint) return;
          if (event.channel === "webview-print-do") {
            webview.print(
              {
                silent: true,
                printBackground: true,
                deviceName: this.printDeviceName
              },
              data => {
                if (data) {
                  this.$message({
                    message: "打印成功!",
                    type: "success"
                  });
                } else {
                  this.$alert("打印失败!", "提示", {
                    confirmButtonText: "知道了"
                  });
                }
              }
            );
          }
        });
      },
      methods: {
        print(data, isAllowPrint) {
          this.htmlData = data;
          // 设置打印机名称
          this.printDeviceName = this.htmlData.printDeviceName;
          this.isAllowPrint = isAllowPrint;
          this.printRender();
        },
        printRender() {
          // 获取节点
          const webview = this.$refs.printWebview;
          // 发送信息到里的页面
          webview.send("webview-print-render", {
            printName: this.htmlData.printDeviceName,
            data: this.htmlData
          });
        }
      }
    };
    </script>
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    webview {
      width: 100%;
      height: 100%;
    }
    </style>
    
  2. 文件 ./src/background.js中需要新建获取打印列表事件的监听:

    // 获取打印列表
    ipcMain.on('getPrinterList', (event) => {
      //主线程获取打印机列表
      const list = printWindow.webContents.getPrinters();
      //通过webContents发送事件到渲染线程,同时将打印机列表也传过去
      printWindow.webContents.send('printerList', list);
    });
    
  3. 触发打印的页面(即打印按钮的页面):

    <template>
      <div class="component">
        <div class="content">
          <el-form :inline="true" size="small" label-width="108px">
            <el-form-item class="form_item_btn">
    		 <!-- 打印按钮 -->
              <el-button type="primary" @click="doPrint()">打印</el-button>
            </el-form-item>
          </el-form>
          <el-row>
            <el-col :span="16" style="height:540px;">   
    		 <!-- 打印预览 -->
              <pinter ref="print" :html-data="HtmlData" class="print_preview"></pinter>
            </el-col>
          </el-row>
        </div>
      </div>
    </template>
    <script>
    const { ipcRenderer } = require("electron");
    import Pinter from "./PrintContent.vue"; // 引用上面新建的组件
    
    export default {
      name: "test",
      components: {
        Pinter
      },
      data() {
        return {
          HtmlData: "",
          selectedPrinter: "",
        };
      },
      mounted() {
        // 获取打印列表
        ipcRenderer.send("getPrinterList");
        ipcRenderer.on("printerList", (e, message) => {
          // 设置打印机名字
          this.selectedPrinter = message[0].name; // 可在页面给予选择打印机功能,此处不讲述
        });
      },
      methods: {
        // 触发打印事件
        doPrint() {
            this.HtmlData = '打印数据';
            this.$refs.print.print(this.HtmlData);
        }
      }
    };
    </script>
    
  4. 新建预览文件(同时也是打印模版文件,关键!):./public/print.html

    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <!-- 如果需要使用第三方组件,需要额外放置组件文件在public中,并引用 -->
      <link rel="stylesheet" href="./element-ui.css" type="text/css" />
      <script src="./vue.js"></script>
      <script src="./element-ui.js"></script>
      <!-- 引用本页面css -->
      <link rel="stylesheet" href="./print_preview.css" type="text/css" />
    </head>
    <body id='bd'>
        <div id="app">
            打印内容
        </div>
    	<script>
        // 具体数据渲染,根据实际业务开发
        new Vue({
          el: '#app',
          data: function () {
            return { data: '' // 本页需要的数据 }
          },
          mounted() {
            const { ipcRenderer } = require('electron')
            // 接收打印数据 参数2:第1步中给webview发送的数据{ printName, data }
            ipcRenderer.on('webview-print-render', (event, data) => {
              this.data = data.data;
              ipcRenderer.sendToHost('webview-print-do');
            });
          }
        })
      </script>
    </body>
    </html>
    

打印功能参考:https://juejin.im/post/5bf8b580e51d4522143b7b03

附录


框架搭建参考视频:https://www.youtube.com/watch?v=Fl7—SEORQ

electron构建的第三方包源码:https://github.com/nklayman/vue-cli-plugin-electron-builder

你可能感兴趣的:(Electron,with,Vue)