• vbenadmin 改造为 electron 版本


    vben-admin 改造为 electron 版本

    vben-admin 简介

    https://vvbin.cn/doc-next/

    目标

    目标:将 vben-admin 改造为 electron 版本
    electron 版本源码地址:https://gitee.com/Artisan-k/vben-admin-electron

    下载 vben-admin 源码

    完整版:

    https://github.com/vbenjs/vue-vben-admin

    目前最新发布版本:v2.8.0

    https://github.com/vbenjs/vue-vben-admin/releases/tag/v2.8.0

    轻量版:

    https://github.com/vbenjs/vben-admin-thin-next

    目前最新发布版本:v2.7.2

    使用 pnpm 安装依赖

    下载后运行:

    E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm install
     ERR_PNPM_INVALID_OVERRIDE_SELECTOR  Cannot parse the "//" selector in the overrides
    

    出现错误,安装失败。

    package.json 里面的

      "resolutions": {
        "//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
        "bin-wrapper": "npm:bin-wrapper-china",
        "rollup": "^2.56.3"
      },
    

    "//": 一行去掉,

    pnpm-lock.yaml 里面的

    overrides:
      //: Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it
      bin-wrapper: npm:bin-wrapper-china
      rollup: ^2.56.3
    

    "//": 一行去掉,

    再次运行如下命令进行安装:

    E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm install
    

    下载完,有一个提示错误:

    > vben-admin@2.7.2 prepare E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0
    > husky install
    
    fatal: not a git repository (or any of the parent directories): .git
    

    运行如下命令:

    E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>git init
    Initialized empty Git repository in E:/artisan/labs/vben-admin-electron/src/vue-vben-admin-2.8.0/.git/
    

    再次运行如下命令进行安装:

    E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm install
    

    运行

    运行如下命令进行启动运行

    E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm serve
    

    查看运行结果:

    > npm run dev
    
    
    > vben-admin@2.7.2 dev
    > vite
    
    Pre-bundling dependencies:
      vue
      pinia
      vue-router
      vue-i18n
      ant-design-vue
      (...and 72 more)
    (this will be run only when your dependencies or config have changed)
    
      vite v2.6.13 dev server running at:
    
      > Network:  http://192.168.0.102:3100/
      > Local:    http://localhost:3100/    
      > Network:  http://172.25.32.1:3100/  
      > Network:  http://172.23.144.1:3100/ 
      > Network:  http://172.27.112.1:3100/ 
      > Network:  http://172.28.96.1:3100/  
    

    访问:http://localhost:3100

    账号密码是预先好的,直接点击登录按钮。

    electron 版本

    使用 pnpm 安装依赖

    推荐使用 yarn,但是为了亲自体验使用 pnpm 出现的问题,这里还是使用 pnpm

    devDependencies

    pnpm install electron --save-dev
    pnpm install electron-builder --save-dev
    pnpm install electron-connect --save-dev
    pnpm install electron-contextmenu-middleware --save-dev
    pnpm install electron-input-menu --save-dev
    pnpm install wait-on --save-dev
    

    下面依次安装,先安装 electron

    > pnpm install  electron --save-dev
    
    ## devDependencies
    + electron 19.0.6
    
     WARN  Issues with peer dependencies found
    .
    ├─┬ rollup-plugin-visualizer
    │ └── ✕ missing peer rollup@^2.0.0
    └─┬ vite-plugin-mock
      └─┬ @rollup/plugin-node-resolve
        ├── ✕ missing peer rollup@^2.42.0
        └─┬ @rollup/pluginutils
          └── ✕ missing peer rollup@^1.20.0||^2.0.0
    Peer dependencies that should be installed:
      rollup@">=2.42.0 <3.0.0">
    

    有警告,安装 rollup ,运行如下命令进行安装:

    > pnpm install rollup
    

    安装完成后,提示安装了如下依赖:

    dependencies:
    + rollup 2.59.0 (2.75.7 is available)
    
    devDependencies:
    - rollup-plugin-visualizer 5.5.2
    + rollup-plugin-visualizer 5.5.2
    - vite-plugin-mock 2.9.6
    + vite-plugin-mock 2.9.6
    

    安装 electron-builder

    > pnpm install electron-builder --save-dev
    ...
    devDependencies:
    + electron-builder 23.1.0
    ...
    

    安装 electron-connect

    > pnpm install electron-connect --save-dev
    devDependencies:
    + electron-connect 0.6.3
    

    安装 electron-contextmenu-middleware

    > pnpm install electron-contextmenu-middleware --save-dev
    devDependencies:
    + electron-contextmenu-middleware 1.0.3
    

    安装 electron-input-menu

    > pnpm install electron-input-menu --save-dev
    devDependencies:
    + electron-input-menu 2.1.0
    

    安装 electron-input-menu

    > pnpm install wait-on --save-dev
    evDependencies:
    + wait-on 6.0.1
    

    dependencies

    pnpm install electron-is-dev
    
    pnpm install rollup
    pnpm install rollup-plugin-esbuild
    
    pnpm install @rollup/plugin-alias
    pnpm install @rollup/plugin-commonjs
    pnpm install @rollup/plugin-json
    pnpm install @rollup/plugin-node-resolve
    
    pnpm install esbuild
    pnpm install chalk
    pnpm install install ora
    

    下面依次安装:

    > pnpm install electron-is-dev
    dependencies:
    + electron-is-dev 2.0.0
    
    > pnpm install rollup
    dependencies:
    + rollup 2.59.0 (2.75.7 is available)
    
    devDependencies:
    - rollup-plugin-visualizer 5.5.2
    + rollup-plugin-visualizer 5.5.2
    - vite-plugin-mock 2.9.6
    + vite-plugin-mock 2.9.6
    
    > pnpm install rollup-plugin-esbuild
    dependencies:
    + rollup-plugin-esbuild 4.9.1
    
     WARN  Issues with peer dependencies found
    .
    └─┬ @rollup/plugin-commonjs
      └── ✕ unmet peer rollup@^2.68.0: found 2.59.0
      
    > pnpm install @rollup/plugin-alias
    dependencies:
    + @rollup/plugin-alias 3.1.9
    
    > pnpm install @rollup/plugin-commonjs
    dependencies:
    + @rollup/plugin-commonjs 22.0.0
     WARN  Issues with peer dependencies found
    .
    └─┬ @rollup/plugin-commonjs
      └── ✕ unmet peer rollup@^2.68.0: found 2.59.0
    
    > pnpm install @rollup/plugin-json
    dependencies:
    + @rollup/plugin-json 4.1.0
    
     WARN  Issues with peer dependencies found
    .
    └─┬ @rollup/plugin-commonjs
      └── ✕ unmet peer rollup@^2.68.0: found 2.59.0
    
    > pnpm install @rollup/plugin-node-resolve
    dependencies:
    + @rollup/plugin-node-resolve 13.3.0
    
     WARN  Issues with peer dependencies found
    .
    └─┬ @rollup/plugin-commonjs
      └── ✕ unmet peer rollup@^2.68.0: found 2.59.0
    
    ----------------------------------------
    ## 上面的安装几个依赖包都提示:
    ## ✕ unmet peer rollup@^2.68.0: found 2.59.0
    ## 这里重新更新下 rollup的版本
    > pnpm install rollup@^2.68.0
    
    ## 更新了各个版本,输出结果如下:
    dependencies:
    - @rollup/plugin-alias 3.1.9
    + @rollup/plugin-alias 3.1.9
    - @rollup/plugin-commonjs 22.0.0
    + @rollup/plugin-commonjs 22.0.0
    - @rollup/plugin-json 4.1.0
    + @rollup/plugin-json 4.1.0
    - @rollup/plugin-node-resolve 13.3.0
    + @rollup/plugin-node-resolve 13.3.0
    - rollup 2.59.0
    + rollup 2.68.0 (2.75.7 is available)
    - rollup-plugin-esbuild 4.9.1
    + rollup-plugin-esbuild 4.9.1
    
    devDependencies:
    - rollup-plugin-visualizer 5.5.2
    + rollup-plugin-visualizer 5.5.2
    - vite-plugin-mock 2.9.6
    + vite-plugin-mock 2.9.6
    
    ----------------------------------------
    > pnpm install esbuild
    dependencies:
    + esbuild 0.14.47
    
    > pnpm install chalk
    dependencies:
    + chalk 5.0.1
    
    > pnpm install install ora
    dependencies:
    + install 0.13.0
    + ora 6.1.0
    
    

    使用 yarn 安装

    devDependencies

    yarn add electron electron-builder electron-connect electron-contextmenu-middleware electron-input-menu wait-on -D
    

    dependencies

    yarn add electron-is-dev rollup rollup-plugin-esbuild @rollup/plugin-alias @rollup/plugin-commonjs @rollup/plugin-json @rollup/plugin-json @rollup/plugin-node-resolve esbuild chalk ora -S
    

    集成electron代码

    index.ts

    新建 electron-main 文件夹,新建 index.ts 文件

    注意:
    是项目根目录下,即:vue-vben-admin-2.8.0/electron-main

    代码清单: electron-main/index.ts

    import { app, BrowserWindow, screen } from 'electron';
    import is_dev from 'electron-is-dev';
    import { join } from 'path';
    
    let mainWindow: BrowserWindow | null = null;
    
    class createWin {
      constructor() {
        const displayWorkAreaSize = screen.getAllDisplays()[0].workArea;
        mainWindow = new BrowserWindow({
           parseInt(`${displayWorkAreaSize.width * 0.85}`, 10),
          height: parseInt(`${displayWorkAreaSize.height * 0.85}`, 10),
          movable: true,
          // frame: false,
          show: false,
          center: true,
          resizable: true,
          // transparent: true,
          titleBarStyle: 'default',
          webPreferences: {
            devTools: true,
            contextIsolation: false,
            nodeIntegration: true,
            //enableRemoteModule: true,
            webSecurity: false, //解决:打包后出现跨域问题
          },
          backgroundColor: '#fff',
        });
        const URL = is_dev
          ? `http://localhost:${process.env.PORT}` // vite 启动的服务器地址
          : `file://${join(__dirname, '../index.html')}`; // vite 构建后的静态文件地址
    
        mainWindow.loadURL(URL);
    
        mainWindow.on('ready-to-show', () => {
          mainWindow.show();
        });
      }
    }
    
    app.whenReady().then(() => new createWin());
    
    const isFirstInstance = app.requestSingleInstanceLock();
    
    if (!isFirstInstance) {
      app.quit();
    } else {
      app.on('second-instance', () => {
        if (mainWindow) {
          mainWindow.focus();
        }
      });
    }
    
    app.on('window-all-closed', () => {
      if (process.platform !== 'darwin') {
        app.quit();
      }
    });
    
    app.on('activate', () => {
      if (BrowserWindow.getAllWindows().length === 0) {
        new createWin();
      }
    });
    

    rollupElectronConfig.ts

    在目录 build/config 目录下新建 rollupElectronConfig.ts

    代码清单:build/config/rollupElectronConfig.ts

    import path from 'path';
    import { RollupOptions } from 'rollup';
    import { nodeResolve } from '@rollup/plugin-node-resolve';
    import commonjs from '@rollup/plugin-commonjs';
    import esbuild from 'rollup-plugin-esbuild';
    import alias from '@rollup/plugin-alias';
    import json from '@rollup/plugin-json';
    
    export function getRollupOptions(): RollupOptions {
      return {
        input: path.join(__dirname, '../../electron-main/index.ts'),
        output: {
          file: path.join(__dirname, '../../dist/main/build.js'),
          format: 'cjs',
          name: 'ElectronMainBundle',
          sourcemap: true,
        },
        plugins: [
          nodeResolve({ preferBuiltins: true, browser: true }), // 消除碰到 node.js 模块时⚠警告
          commonjs(),
          json(),
          esbuild({
            // All options are optional
            include: /\.[jt]sx?$/, // default, inferred from `loaders` option
            exclude: /node_modules/, // default
            // watch: process.argv.includes('--watch'), // rollup 中有配置
            sourceMap: false, // default
            minify: process.env.NODE_ENV === 'production',
            target: 'es2017', // default, or 'es20XX', 'esnext'
            jsxFactory: 'React.createElement',
            jsxFragment: 'React.Fragment',
            // Like @rollup/plugin-replace
            define: {
              __VERSION__: '"x.y.z"',
            },
            // Add extra loaders
            loaders: {
              // Add .json files support
              // require @rollup/plugin-commonjs
              '.json': 'json',
              // Enable JSX in .js files too
              '.js': 'jsx',
            },
          }),
          alias({
            entries: [{ find: '/@main/', replacement: path.join(__dirname, '../../electron-main') }],
          }),
        ],
        external: [
          'crypto',
          'assert',
          'fs',
          'util',
          'os',
          'events',
          'child_process',
          'http',
          'https',
          'path',
          'electron',
        ],
      };
    }
    
    

    compilerElectron.ts

    在 build/script 目录下新建 compilerElectron.ts

    代码清单: build/script/compilerElectron.ts

    import rollup, { OutputOptions } from 'rollup';
    import chalk from 'chalk';
    import ora from 'ora';
    import waitOn from 'wait-on';
    import net from 'net';
    import { URL } from 'url';
    import minimist from 'minimist';
    import electronConnect from 'electron-connect';
    
    import { getRollupOptions } from '../config/rollupElectronConfig';
    
    const argv = minimist(process.argv.slice(2));
    const TAG = '[compiler-electron]';
    
    export function startCompilerElectron(port = 80) {
      // 因为 vite 不会重定向到 index.html,所以直接写 index.html 路由。
      const ELECTRON_URL = `http://localhost:${port}/index.html`;
    
      const spinner = ora(`${TAG} Electron build...`);
      const electron = electronConnect.server.create({ stopOnClose: true });
      const rollupOptions = getRollupOptions();
    
      function watchFunc() {
        // once here, all resources are available
        const watcher = rollup.watch(rollupOptions);
        watcher.on('change', (filename) => {
          const log = chalk.green(`change -- ${filename}`);
          console.log(TAG, log);
        });
        watcher.on('event', (ev) => {
          if (ev.code === 'END') {
            // init-未启动、started-第一次启动、restarted-重新启动
            electron.electronState === 'init' ? electron.start() : electron.restart();
          } else if (ev.code === 'ERROR') {
            console.log(ev.error);
          }
        });
      }
    
      if (argv.watch) {
        waitOn(
          {
            resources: [ELECTRON_URL],
            timeout: 5000,
          },
          (err) => {
            if (err) {
              const { hostname } = new URL(ELECTRON_URL);
              const serverSocket = net.connect(port, hostname, () => {
                watchFunc();
              });
              serverSocket.on('error', (e) => {
                console.log(err);
                console.log(e);
                process.exit(1);
              });
            } else {
              watchFunc();
            }
          },
        );
      } else {
        spinner.start();
        rollup
          .rollup(rollupOptions)
          .then((build) => {
            spinner.stop();
            console.log(TAG, chalk.green('Electron build successed.'));
            build.write(rollupOptions.output as OutputOptions);
          })
          .catch((error) => {
            spinner.stop();
            console.log(`\n${TAG} ${chalk.red('构建报错')}\n`, error, '\n');
          });
      }
    }
    
    

    startElectron.ts

    在 build/script 目录下新建 startElectron.ts
    代码清单: build/script/startElectron.ts

    import { createServer } from 'vite';
    import path from 'path';
    import { startCompilerElectron } from './compilerElectron';
    import minimist from 'minimist';
    
    (async () => {
      const argv = minimist(process.argv.slice(2));
      console.log(argv);
      const isDev = argv.env === 'development';
      let port: number | undefined = undefined;
      if (isDev) {
        const server = await createServer({
          root: path.resolve(__dirname, '../../'),
        });
    
        const app = await server.listen();
        port = app.config.server.port;
        process.env.PORT = `${port}`;
      }
    
      startCompilerElectron(port);
    })();
    
    

    命令

    在 package.json 添加如下命令

    {
        ...
        "main": "dist/main/build.js",
        "build": {
        "appId": "xxx@gmail.com",
        "electronDownload": {
          "mirror": "https://npm.taobao.org/mirrors/electron/"
        },
        "files": [
          "!node_modules",
          "dist/**",
        ],
        "asar": false,
        "mac": {
          "artifactName": "${productName}_setup_${version}.${ext}",
          "target": [
            "dmg"
          ]
        },
        "linux": {
          "icon": "build/icons/512x512.png",
          "target": [
            "deb"
          ]
        },
        "win": {
          "target": [
            {
              "target": "nsis",
              "arch": [
                "x64"
              ]
            }
          ],
          "artifactName": "${productName}_setup_${version}.${ext}"
        },
        "nsis": {
          "oneClick": false,
          "perMachine": false,
          "allowToChangeInstallationDirectory": true,
          "deleteAppDataOnUninstall": false
        }
      },
      "scripts": {
         ...
        "start": "node ./build/script/electron/dev",
        "dev:app": "esno ./build/script/startElectron.ts --env=development --watch",
        "build:app": "npm run build && esno ./build/script/startElectron.ts --env=production && electron-builder ",
        }
       ...
    } 
    
    • main:electron程序入口
    • build:打包
    • scripts:electron 相关命令,
      • dev:app:开发环境运行
      • build:app:项目打包

    运行 electron

    > pnpm dev:app
    

    点击登录按钮:

    打包

    > pnpm build:app
    

    执行打包命令后,在项目的目录下,自动生成一个 dist 目录

    出现错误 : 找不到文件

    打开绿色版,会出现如下错误:

    图片、js 的地址错误,找不到文件。

    打开 .env.production 文件

    找到

    # public path
    VITE_PUBLIC_PATH = /
    

    将其修改为:

    # public path 
    ## 注意:生产环境发布为 Electron 客户端时得修改为 ./ ,否则Electron 客户端找不到文件
    VITE_PUBLIC_PATH = ./
    

    重新打包:

    > pnpm build:app
    

    打开绿色版,运行一切正常

    点击登录按钮:

    electron 示例

    进程间通信

    渲染进程

    代码清单: src/views/electron/process/ipc.vue

    <template>
      <PageWrapper
        title="示例:进程间通信"
        contentBackground
        contentClass="p-4"
        content="在这里将演示如何在主进程与渲染进程间进行通信"
      >
        <Divider>向主进程发送消息</Divider>
        <Alert
          class="mt-4"
          type="info"
          message="点击按钮后请查看编译器(Visual Code)控制台消息"
          show-icon
        />
        <div class="mt-4">
          <a-button type="primary" size="small" @click="hanleSendMessageToMain">
            向主进程发送消息
          </a-button>
        </div>
    
        <Divider>向主进程发送消息并接收主进程的消息</Divider>
        <div class="mt-4">
          <a-button type="primary" size="small" @click="hanleSendMessageToMainNeedReply">
            向主进程发送消息并接收主进程的消息
          </a-button>
          <p> 主进程返回的消息: {{ dataFromMain }} </p>
        </div>
      </PageWrapper>
    </template>
    
    <script lang="ts">
      import { defineComponent, ref, onMounted } from 'vue';
      import { Alert, Divider } from 'ant-design-vue';
      import { PageWrapper } from '/@/components/Page';
      const { ipcRenderer } = require('electron');
      //import { ipcRenderer } from 'electron'; // 这样引用,打包后报错,请使用:require('electron')
    
      export default defineComponent({
        components: { PageWrapper, Alert, Divider },
        setup() {
          const dataFromMain = ref('');
    
          const hanleSendMessageToMain = () => {
            ipcRenderer.send('event_from_renderer', { param: 123 });
          };
    
          const hanleSendMessageToMainNeedReply = () => {
            ipcRenderer.send('event_from_renderer_need_replay', { param: 'abc' });
          };
    
          const registerEvents = () => {
            ipcRenderer.on('event_from_main_replay', (_, data) => {
              console.log('In renderer:event_from_main_replay-->data:', data);
              dataFromMain.value += data;
            });
          };
    
          onMounted(() => {
            registerEvents();
          });
    
          return {
            hanleSendMessageToMain,
            hanleSendMessageToMainNeedReply,
            dataFromMain,
          };
        },
      });
    </script>
    
    <style lang="less" scoped></style>
    
    

    其中,

    • 渲染进程发送消息到主进程

        const hanleSendMessageToMainNeedReply = () => {
              ipcRenderer.send('event_from_renderer_need_replay', { param: 'abc' });
            };
      
    • 监听来自主进程的响应消息

            const registerEvents = () => {
              ipcRenderer.on('event_from_main_replay', (_, data) => {
                console.log('In renderer:event_from_main_replay-->data:', data);
                dataFromMain.value += data;
              });
            };
      
            onMounted(() => {
              registerEvents();
            });
      

    主进程

    主进程的代码写在 electron-baisc.ts 文件中

    代码清单: electron-main/main/electron-baisc.ts

    import { ipcMain } from 'electron';
    
    const registerEvents = () => {
      ipcMain.on('event_from_renderer', (event, data) => {
        console.log('event_from_renderer->data:', data);
      });
    
      ipcMain.on('event_from_renderer_need_replay', (event, data) => {
        console.log('event_from_renderer_need_replay->data:', data);
        event.reply('event_from_main_replay', '【渲染进程,你的消息我已收到】');
      });
    };
    
    export default {
      registerEvents,
    };
    
    

    然后在主进程 index.ts 中使用

    代码清单: electron-main/index.ts

    ...
    import electronBaisc from './main/electron-baisc';
    
    class createWin {
        ...
        mainWindow.on('ready-to-show', () => {
          mainWindow.show();
          registerEvents();
        });
      }
    }
    
    //-----------------------------------------------------------------------------------
    // 在这个文件中,你可以包含应用程序剩余的所有部分的代码,
    // 也可以拆分成几个文件,然后用 require 导入。
    //-----------------------------------------------------------------------------------
    function registerEvents() {
      electronBaisc.registerEvents();
    }
    

    添加路由菜单

    新建 electron.ts,系统会根据路由自动生成菜单项

    代码清单:src/routes/modules/electron.ts

    import type { AppRouteModule } from '/@/router/types';
    
    import { getParentLayout, LAYOUT } from '/@/router/constant';
    import { t } from '/@/hooks/web/useI18n';
    
    const electron: AppRouteModule = {
      path: '/electron',
      name: 'Electron',
      component: LAYOUT,
      redirect: '/electron/process/ipc',
      meta: {
        orderNo: 1999,
        icon: 'ant-design:share-alt-outlined',
        title: t('routes.electron.RouteName'),
      },
      children: [
        {
          path: 'Process',
          name: 'ElectronProcess',
          component: getParentLayout('ElectronProcess'),
          meta: {
            title: t('routes.electron.Process'),
          },
          redirect: '/electron/process/ipc',
          children: [
            {
              path: 'ipc',
              name: 'ElectronIPC',
              meta: {
                title: t('routes.electron.InterProcessCommunication'),
              },
              component: () => import('/@/views/electron/process/Ipc.vue'),
            },
          ],
        },
      ],
    };
    
    export default electron;
    
    

    其中:

    • routes.electron.xxx: 是支持国际化的字符串

    国际化

    路由菜单支持国际化,需要新建两个多语言资源,新建如下两个文件,新建后系统会自动加载,可以直接使用。

    • 英文

    代码清单:src/locales/lang/en/routes/electron.ts

    export default {
      RouteName: 'Electron demo',
      Process: 'Process',
      InterProcessCommunication: 'IPC',
    };
    
    • 简体中文

    代码清单:src/locales/lang/zh-CN/routes/electron.ts

    export default {
      RouteName: 'Electron示例',
      Process: '进程',
      InterProcessCommunication: '进程间通信',
    };
    

    测试

    运行开发环境

    > pnpm dev:app
    

    点击【 向主进程发送消息 】按钮,向主进程发送消息,在 Visual Code 的控制台中可以看到输出日志:

    event_from_renderer->data: { param: 123 }
    

    点击【 向主进程发送消息并接收主进程的消息 】按钮, 向主进程发送消息并接收主进程的消息,如下图所示:

    集成串口

    Github: https://github.com/serialport/node-serialport

    官网:https://serialport.io/

    文档:https://serialport.io/docs/

    https://serialport.io/docs/guide-electron

    其它资料:

    使用SerialPort库进行Node物联网项目开发:https://zhuanlan.zhihu.com/p/98050314

    安装

    https://serialport.io/docs/guide-installation

    运行如下命令进行安装

    > pnpm install serialport
    dependencies:
    + serialport 10.4.0 #目前最新版本
    

    可能出现的问题

    10.x.x 版本已经允许编译了各个平台的lib,并支持 Typescript,一般不会出现以下的问题。

    10.x.x 版本以下可能会出现 serialport 不能使用的问题

    若 serialport 不能使用,重新编译 serialport 的模块,这个看情况而定!

    安装:node-v16.14.0-x64.msi

    安装:python-3.10.2-amd64.exe

    pnpm install global node-gyp 
    pnpm install serialport@9.2.8 
    pnpm install electron-rebuild
    .\node_modules\.bin\electron-rebuild.cmd 
    

    serialport@9.x 支持 windows 7、8、10+

    serialport@10.x 支持 windows 10+

    国际化

    路由菜单支持国际化,需要新建两个多语言资源,新建如下两个文件,新建后系统会自动加载,可以直接使用。

    • 英文

    代码清单:src/locales/lang/en/routes/electron.ts

    export default {
      ...
      SerialPort: 'SerialPort',
      SerialPortAssitant: 'SP-Assitant',
    };
    
    • 简体中文

    代码清单:src/locales/lang/zh-CN/routes/electron.ts

    export default {
      ...
      SerialPort: '串口',
      SerialPortAssitant: '串口助手',
    };
    

    添加路由菜单

    在 src/routes/modules/electron.ts,添加菜单

    代码清单:src/routes/modules/electron.ts

    import type { AppRouteModule } from '/@/router/types';
    
    import { getParentLayout, LAYOUT } from '/@/router/constant';
    import { t } from '/@/hooks/web/useI18n';
    
    const electron: AppRouteModule = {
      path: '/electron',
      name: 'Electron',
      component: LAYOUT,
      redirect: '/electron/process/ipc',
      meta: {
        orderNo: 1999,
        icon: 'ant-design:share-alt-outlined',
        title: t('routes.electron.RouteName'),
      },
      children: [
        {
            // 进程间通信路由
            ...
        },
        {
          path: 'SerialPort',
          name: 'SerialPort',
          component: getParentLayout('SerialPort'),
          meta: {
            title: t('routes.electron.SerialPort'),
          },
          redirect: '/electron/serialport/assistant',
          children: [
            {
              path: 'ipc',
              name: 'SPAssistant',
              meta: {
                title: t('routes.electron.SerialPortAssitant'),
              },
              component: () => import('/@/views/electron/serialport/Assistant.vue'),
            },
          ],
        },
      ],
    };
    
    export default electron;
    
    

    串口的使用示例

    代码清单: src/views/electron/serialport/Assistant.vue

    <template>
      <PageWrapper
        title="示例:串口助手"
        contentBackground
        contentClass="p-4"
        content="在这里将演示如何在Electron中使用 Node-SerialPort, 官网:https://serialport.io/"
      >
        <div>
          <a-alert type="error" v-if="errorMsg" :message="errorMsg" banner closable />
          <a-alert type="success" v-if="message" :message="message" show-icon closable />
          <div style="margin-top: 10px">
            <a-row>
              <a-col :span="18">
                <div>
                  <h1>接收缓存区</h1>
                  <a-row>
                    <textarea cols="80" rows="10" v-model="recievedData"></textarea>
                  </a-row>
                  <a-row>
                    <a-space>
                      <a-button class="" @click="handleClearRecievedData"> 清空 </a-button>
                    </a-space>
                  </a-row>
                </div>
                <div>
                  <h1>发送缓存区</h1>
                  <a-row>
                    <textarea cols="80" rows="10" v-model="toSendData"></textarea>
                  </a-row>
                  <a-row>
                    <a-space>
                      <a-button @click="handleSendData"> 发送 </a-button>
                      <a-button @click="handleClearSendData"> 清空 </a-button>
                    </a-space>
                  </a-row>
                </div>
              </a-col>
              <a-col :span="6">
                <div>
                  <a-form :label-col="labelCol" :wrapper-col="wrapperCol">
                    <a-form-item label="串口" v-bind="validateInfos.path">
                      <a-input v-model:value="portOptionsRef.path" />
                    </a-form-item>
                    <a-form-item label="波特率" v-bind="validateInfos.baudRate">
                      <a-input v-model:value="portOptionsRef.baudRate" />
                    </a-form-item>
                    <a-form-item :wrapper-col="{ span: 14, offset: 10 }">
                      <a-button type="primary" size="small" @click.prevent="handleOpenSerilPort">
                        打开串口
                      </a-button>
                      <a-button type="primary" size="small" danger @click="handleClosSerilPort"
                        >关闭串口</a-button
                      >
                    </a-form-item>
                  </a-form>
                </div>
              </a-col>
            </a-row>
          </div>
        </div>
      </PageWrapper>
    </template>
    
    <script lang="ts">
      import { defineComponent, reactive, toRaw, onMounted, toRefs, onUnmounted } from 'vue';
      import { PageWrapper } from '/@/components/Page';
      import { Form, Alert, Row, Col, message } from 'ant-design-vue';
      const { SerialPort } = require('serialport');
      // import { SerialPort } from 'serialport';
    
      const useForm = Form.useForm;
    
      export default defineComponent({
        components: {
          PageWrapper,
          'a-row': Row,
          'a-col': Col,
          'a-form': Form,
          'a-form-item': Form.Item,
          'a-alert': Alert,
        },
        setup() {
          let port;
    
          const state = reactive({
            errorMsg: '',
            message: '',
            toSendData: '',
            recievedData: '',
          });
    
          const portOptionsRef = reactive({
            path: 'COM1',
            baudRate: 9600, //波特率
            dataBits: 8, //数据位
            parity: 'none', //奇偶校验
            stopBits: 1, //停止位
            flowControl: false,
            autoOpen: false, //不自动打开
          });
    
          const { validate, validateInfos } = useForm(
            portOptionsRef,
            reactive({
              path: [
                {
                  required: true,
                  message: '请输入串口',
                },
              ],
              baudRate: [
                {
                  required: true,
                  message: '请输入波特率',
                },
              ],
            }),
          );
    
          onMounted(() => {
            getSerialPortList();
            createeSerialPort();
          });
    
          const getSerialPortList = () => {
            SerialPort.list().then(
              (ports) => {
                if (!ports || ports.length == 0) {
                  state.errorMsg = '未监测到串口信息';
                } else {
                  // state.message = `监测到可用串口:${ports.map((e) => e.path)}`;
                  ports.forEach(console.log);
                }
              },
              (err) => console.error(err),
            );
          };
    
          const createeSerialPort = () => {
            if (!port) {
              port = new SerialPort(toRaw(portOptionsRef));
              registerEvents();
            }
          };
    
          const handleOpenSerilPort = () => {
            validate()
              .then(() => {
                openSerialPort();
              })
              .catch((err) => {
                console.log('handleOpenSerilPort validate error', err);
              });
          };
    
          const openSerialPort = () => {
            const result = openPort();
            if (!result.success) {
              message.error(result.message);
            } else {
              message.success(result.message);
            }
          };
    
          const openPort = () => {
            const result = {
              success: true,
              message: '',
            };
    
            console.log('---------1-------------');
            console.log('openPort-port:', port);
            console.log('openPort-por1t.isOpen:', port?.isOpen);
    
            if (port == null) {
              console.log('---------2-------------');
              createeSerialPort();
            }
            console.log('---------3-------------');
            console.log('openPort-port:', port);
            console.log('openPort-port.isOpen:', port.isOpen);
    
            if (port.isOpen) {
              console.log('---------4-------------');
              result.message = `串口 ${portOptionsRef.path} 已打开成功`;
              return result;
            } else {
              port.open((err) => {
                console.assert('dddddd');
                console.log('---------5-------------');
    
                if (err) {
                  console.log('---------7-------------');
    
                  result.success = false;
                  result.message = `串口 ${portOptionsRef.path} 打开失败:${err}`;
                  return result;
                } else {
                  console.log('---------8-------------');
                  result.message = `串口 ${portOptionsRef.path} 打开成功`;
                  return result;
                }
              });
            }
            console.log('---------9-------------');
            return result;
          };
    
          const registerEvents = () => {
            // 监听接收串口数据
            // Switches the port into "flowing mode"
            port.on('data', function (data) {
              console.log('RecievedData:', data);
              handleRecievedData(data);
            });
          };
    
          const handleRecievedData = (data) => {
            const str = Buffer.from(data).toString().toString();
            state.recievedData += str;
          };
    
          const handleClearRecievedData = () => {
            state.recievedData = '';
          };
    
          const handleClearSendData = () => {
            state.toSendData = '';
          };
    
          const handleSendData = () => {
            sendDateToPort(state.toSendData);
          };
    
          const sendDateToPort = (data) => {
            port.write(data);
            //port.write(Buffer.from(data));
          };
    
          const handleClosSerilPort = () => {
            const result = closeSerialPort();
            if (!result.success) {
              message.error(result.message);
            } else {
              message.success(result.message);
            }
          };
    
          const closeSerialPort = () => {
            const result = {
              success: true,
              message: '',
            };
    
            if (port && port.isOpen) {
              port.close((err) => {
                if (err) {
                  result.success = false;
                  result.message = `串口 ${portOptionsRef.path} 关闭失败:${err}`;
                  return result;
                }
              });
            }
            port = null;
            result.message = `串口 ${portOptionsRef.path} 已关闭`;
            return result;
          };
    
          onUnmounted(() => {
            closeSerialPort();
          });
    
          return {
            labelCol: {
              span: 10,
            },
            wrapperCol: {
              span: 14,
            },
            validateInfos,
            ...toRefs(state),
            portOptionsRef,
            handleOpenSerilPort,
            handleClosSerilPort,
            handleSendData,
            handleClearRecievedData,
            handleClearSendData,
          };
        },
      });
    </script>
    
    <style lang="less" scoped></style>
    
    

    注意:这里使用的是

      const { SerialPort } = require('serialport');
    

    而不是

     import { SerialPort } from 'serialport';
    

    测试

    运行开发环境

    > pnpm dev:app
    

    打开菜单:

    Tips:
    1.电脑没有串口的,可以安装虚拟串口软件:Virtual Serial Port Driver
    2.可以自行查找串口调试助手,比如 STC公司的:stc-isp

    下面进行测试:

    先使用 Virtual Serial Port Driver 添加了两个虚拟串口

    然后使用串口通讯助手测试数据通信,如下图所示:

    打包出现的问题

    打包后运行程序,出现了错误,如下图所示:

    Tips:

    按组合键 【ctrl + shift + i】打开开发者工具(Developer Tools)

    错误信息如下:

    vendor.b7ee0cad.js:147 Error: Cannot find module '@serialport/parser-byte-length'
    Require stack:
    - E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\dist\win-unpacked\resources\app\node_modules\serialport\dist\index.js
    ...
    
    index.ca01a1fc.js:1 Error: Cannot find module '@serialport/parser-byte-length'
    Require stack:
    - E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\dist\win-unpacked\resources\app\node_modules\serialport\dist\index.js
    - E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\dist\win-unpacked\resources\app\dist\index.html
     ...
    

    serialport 打包问题最终解决方法

    最终的解决方案是:

    package.json 文件中的如下配置:

    ... 
    "build": {
        ...
        "files": [
          "!node_modules",
          "dist/**"
        ],
        .... 
     }
     ...
    

    中的 "!node_modules", 删除掉,即最终的配置为:

    ... 
    "build": {
        ...
        "files": [
          "dist/**"
        ],
        .... 
     }
     ...
    

    使用 pnpm 进行打包

    pnpm build:app
    

    不幸的是,使用 pnpm 会出现如下错误:

    > pnpm build:app
    [compiler-electron] Electron build successed.
      • electron-builder  version=23.1.0 os=10.0.19044
      • loaded configuration  file=package.json ("build" field)
      • description is missed in the package.json  appPackageFile=E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\package.json
      • writing effective config  file=dist\builder-effective-config.yaml
      • rebuilding native dependencies  dependencies=@serialport/bindings-cpp@10.7.0 platform=win32 arch=x64
      ⨯ cannot execute  cause=fork/exec C:\Users\wei\AppData\Roaming\npm\node_modules\pnpm\bin\pnpm.cjs: %1 is not a valid Win32 application.
                        command='C:\Users\wei\AppData\Roaming\npm\node_modules\pnpm\bin\pnpm.cjs' rebuild @serialport/bindings-cpp@10.7.0
                        workingDir=
     ELIFECYCLE  Command failed with exit code 1.
    

    更换 pnpmyarn

    • 删除掉 node_modules 文件夹

    • 删除文件:pnpm-lock.yaml

    • 安装依赖

      yarn install
      
    • 打包

      yarn build:app
      

    这样执行打包后,打包文件把 node_modules也打包到如下文件夹:

    dist\win-unpacked\resources\app\node_modules
    

    如下图所示:


    点击安装文件【vben-admin_setup_2.8.0.exe】,进行安装,安装后的如下目录中,可以看到被打包的 node_modules 文件夹

    安装文件也变大了,但是这是目前能找到的唯一解决方案。

    TODO:看看怎么优化下,只导入 serialprot及其依赖

    集成 SQLite

    https://blog.csdn.net/jiyulonely/article/details/77089701

    菜鸟教程:https://www.runoob.com/sqlite/sqlite-where-clause.html

    安装 sqlite3

    yarn add sqlite3
    

    不要使用 pnpm

    使用 pnpm 安装 sqlite3

    pnpm install sqlite3 --save
    pnpm install electron-rebuild --save
    .\node_modules\.bin\electron-rebuild.cmd
    

    或者

    > npm install sqlite3 --build-from-source --runtime=electron --target=17.1.0 --disturl=https://atom.io/download/electron   
    
    • --target=17.1.0: 是项目中使用的 electron 版本

    使用 pnpm 安装 sqlite3,会出现各种各样的问题,比如如下错误:

    > pnpm install
    ⠙ Building module: sqlite3, Completed: 1在此解决方案中一次生成一个项目。若要启用并行生成,请添加“-m”开关。
    ⠇ Building module: sqlite3, Completed: 1C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(989,5): error MSB3191: 无法创建目录“E:\artisan\labs\antdv-pr
    o\src\electron\node_modules\.pnpm\registry.npmmirror.com+sqlite3@5.0.2\registry.npmmirror.com+node-addon-api@3.2.1\node_modules\node-addon-api\Release\obj\nothing\reg 
    istry.npmmirror.com+node-addon-api@3.2.1\node_modules\node-addon-api”。指定的路径或文件名太长,或者两者都太长。完全限定文件名必须少于 260 个字符,并且目录名必须少于 248 个字符。 [E:\artisan\labs\antdv-pro\src\electr
    ......
    

    npmyarn 命令安装,不用 electron-rebuild

    npm install sqlite3 --build-from-source --runtime=electron --target=13.0.0 --dist-url=https://atom.io/download/electron
    或
    yarn add sqlite3 --build-from-source --runtime=electron --target=13.0.0 --dist-url=https://atom.io/download/electron
    

    在vben-admin中,直接使用如下命令安装即可:

    yarn add sqlite3
    

    electron-builder 会自动帮我们编译出对应 electron 版本的 sqlite3

    kenx.js

    https://knexjs.org/

    https://github.com/knex/knex

    knex js 是一个sql 指令构建器,可以操作 sqlite、mysql等

    安装 kenx.js

    yarn add knex -S
    

    knex.js 链接 sqlite3时,还得安装 @vscode/sqlite3

    yarn add @vscode/sqlite3 
    

    相关代码

    创建数据库

    代码清单:electron-main/common/database-sqlite.ts

    require('sqlite3'); //确保knex使用sqlite3时它已加载
    console.log('------1--------');
    
    const knex = require('knex');
    
    console.log('------2--------');
    const database = knex({
      client: 'sqlite3',
      connection: {
        filename: './vben-admin-db.sqlite',
      },
      useNullAsDefault: true,
    });
    console.log('------3--------');
    
    // 检查数据库是否存在数据库表,不存在则创建
    database.schema.hasTable('users').then((exists) => {
      console.log('------4--------', `!exists: ${!exists}`);
      if (!exists) {
        console.log('------5--------');
        return database.schema.createTable('users', (t) => {
          t.increments('id').primary(); //创建自增的id列,并设置为主键
          t.string('name', 100);
          t.boolean('isActived');
        });
      }
    });
    console.log('------6--------');
    
    export default database;
    
    

    渲染进程中使用 kenx

    代码清单:src/views/electron/sqlite/SqliteKenx.vue

    <template>
      <PageWrapper
        title="示例:SQLite & Knex.js"
        contentBackground
        contentClass="p-4"
        content="在这里将演示如何在Electron中使用 SQLite & Knex.js, 官网:https://knexjs.org"
      >
        <div>
          <div style="margin-top: 10px">
            <h2>Users</h2>
            <a-button @click="hanldeAddUser">添加</a-button>
          </div>
          <div>序号| Id | name | isActived</div>
          <div v-for="item in users" :key="item.id">
            {{ item.id }} | {{ item.name }} | {{ item.isActived }} |
            <a-button @click="hanldeDelUser(item.id)">删除</a-button>
          </div>
        </div>
      </PageWrapper>
    </template>
    
    <script lang="ts">
      import { defineComponent, reactive, toRefs, onMounted } from 'vue';
      import { PageWrapper } from '/@/components/Page';
      import { User } from './types';
      import database from '../../../../electron-main/common/database-sqlite';
    
      export default defineComponent({
        components: {
          PageWrapper,
        },
        setup() {
          const state = reactive({
            users: new Array<User>(),
          });
          onMounted(() => {
            getUserList();
          });
    
          const getUserList = () => {
            database('users')
              .select()
              .orderBy('id', 'desc')
              .then((items) => {
                console.log('users:', items);
                state.users = items;
              })
              .catch(console.error);
          };
    
          const hanldeAddUser = () => {
            const user = {
              name: 'users' + Math.random() * 10000,
              isActived: false,
            };
    
            database('users')
              .insert(user)
              .then(() => {
                getUserList();
              })
              .catch(console.error);
          };
    
          const hanldeDelUser = (id) => {
            console.log('id:', id);
            database('users')
              .where('id', id)
              .delete()
              .then(() => {
                getUserList();
              })
              .catch(console.error);
          };
    
          return {
            ...toRefs(state),
            hanldeAddUser,
            hanldeDelUser,
          };
        },
      });
    </script>
    
    <style lang="less" scoped></style>
    
    

    代码清单:src/views/electron/sqlite/types.ts

    export interface User {
      id: number;
      name: string;
      isActived: boolean;
    }
    

    添加路由

    代码清单:src/router/routes/modules/elctron.ts

    const electron: AppRouteModule = {
      path: '/electron',
      name: 'Electron',
        ...
      },
      children: [
        ...
        {
          path: 'Sqlite',
          name: 'Sqlite',
          component: getParentLayout('Sqlite'),
          meta: {
            title: t('routes.electron.Sqlite'),
          },
          redirect: '/electron/Sqlite/sqlite-kenx',
          children: [
            {
              path: 'sqlite-kenx',
              name: 'SqliteKenx',
              meta: {
                title: t('routes.electron.SqliteKenx'),
              },
              component: () => import('/@/views/electron/sqlite/SqliteKenx.vue'),
            },
          ],
        },
      ]
    }
    

    国际化

    路由菜单支持国际化,需要新建两个多语言资源,新建如下两个文件,新建后系统会自动加载,可以直接使用。

    • 英文

    代码清单:src/locales/lang/en/routes/electron.ts

    export default {
      ...
       Sqlite: 'Sqlite',
      SqliteKenx: 'Sqlite-Kenx',
    };
    
    • 简体中文

    代码清单:src/locales/lang/zh-CN/routes/electron.ts

    export default {
      ...
       Sqlite: 'Sqlite',
      SqliteKenx: 'Sqlite-Kenx',
    };
    

    开发环境运行

    yarn dev:app
    

    程序启动后,点击菜单【Sqlite | Sqlite-Kenx】,如下图所示:

    第一次运行点击菜单时,数据库还没有生成,这时会在项目的根目录下生成一个名为【vben-admin-db.sqlite】的sqlite数据库,如下图所示:

    380433-20220630213920448-2022320060

    点击【添加】按钮,添加一个 User,

    点击 【删除】删除 User

    出现的错误提示

    出现如下错误提示,但是程序还是可以运行,

    X [ERROR] Could not resolve "mock-aws-s3"
    
        node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28:
          43 │     const AWSMock = require('mock-aws-s3');
             ╵                             ~~~~~~~~~~~~~
    
      You can mark the path "mock-aws-s3" as external to exclude it from the bundle, which will remove
      this error. You can also surround this "require" call with a try/catch block to handle this
      failure at run-time instead of bundle-time.
    
    X [ERROR] Could not resolve "aws-sdk"
    
        node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22:
          76 │   const AWS = require('aws-sdk');
             ╵                       ~~~~~~~~~
    
      You can mark the path "aws-sdk" as external to exclude it from the bundle, which will remove this
      error. You can also surround this "require" call with a try/catch block to handle this failure at
      run-time instead of bundle-time.
    
    X [ERROR] Could not resolve "nock"
    
        node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23:
          112 │   const nock = require('nock');
              ╵                        ~~~~~~
    
      You can mark the path "nock" as external to exclude it from the bundle, which will remove this
      error. You can also surround this "require" call with a try/catch block to handle this failure at
      run-time instead of bundle-time.
      
    20:17:55 [vite] error while updating dependencies:
    Error: Build failed with 3 errors:
    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: ERROR: Could not resolve "mock-aws-s3"
    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22: ERROR: Could not resolve "aws-sdk"
    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: ERROR: Could not resolve "nock"
        at failureErrorWithLog (E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\node_modules\esbuild\lib\main.js:1605:15)
        ...
    Vite Error, /node_modules/.vite/deps/vue.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/pinia.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/vue-router.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/ant-design-vue.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/vue-i18n.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/vue.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/ant-design-vue.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/lodash-es.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/vite-plugin-theme_es_client.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/axios.js?v=db3ca739 optimized info should be defined
    Vite Error, /node_modules/.vite/deps/vite-plugin-theme_es_client.js?v=db3ca739 optimized info should be defin
    
    ....
    

    根据报错提示,

     Could not resolve "mock-aws-s3"...
      You can mark the path "mock-aws-s3" as external
     ...
     Could not resolve "aws-sdk...
      You can mark the path "aws-sdk" as external
     ... 
     Could not resolve "nock" ...
      You can mark the path "nock" as external
    

    安装 Vite 插件: vite-plugin-resolve-externals

    yarn add vite-plugin-resolve-externals -D
    

    然后创建插件:

    代码清单: build/vite/plugin/externals.ts

    import resolveExternalsPlugin from 'vite-plugin-resolve-externals';
    
    export function configResolveExternalsPlugin() {
      return resolveExternalsPlugin({
        'mock-aws-s3': 'mock-aws-s3',
        'aws-sdk': 'aws-sdk',
        nock: 'nock',
      });
    }
    

    把无法 resolve 的模块配置进去,最后把生成的插件对象添加到 vite.config.ts 的插件配置节点,

    代码清单:vite.config.ts

    import { createVitePlugins } from './build/vite/plugin';
    ...
    export default ({ command, mode }: ConfigEnv): UserConfig => {
        ...
        // The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
        plugins: createVitePlugins(viteEnv, isBuild),
    }
    

    代码清单:build/vite/plugin/index.ts

    import { configResolveExternalsPlugin } from './externals';
    ...
    export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
       const vitePlugins: (Plugin | Plugin[])[] = [...];
       ...
       vitePlugins.push(configResolveExternalsPlugin());
    
       return vitePlugins;
    }
    

    其中, vitePlugins.push(configResolveExternalsPlugin()); 把刚才创建的插件添加到 vite.config.ts 中。

    打包

    yarn build:app
    

    也是需要把 package.json 文件

      "build": {
        "appId": "xxx@gmail.com",
        ...
        "files": [
          "!node_modules",
          "dist/**"
        ],
        ...
    

    中的 "!node_modules" 删掉,即最终配置为:

      "build": {
        "appId": "xxx@gmail.com",
        ...
        "files": [
          "dist/**"
        ],
        ...
    

    否则,报错,如下图所示:

    index.4583ee62.js:117 Error: Cannot find module 'sqlite3'
    

    打包成功后,打开程序,第一次运行点击菜单时,数据库还没有生成,这时会在程序的根目录下生成一个名为【vben-admin-db.sqlite】的sqlite数据库,如下图所示:

    点击【添加】按钮,添加一个 User,

    点击 【删除】删除 User

  • 相关阅读:
    node--ubuntu 安装
    vue+node 全栈开发 --- 同时运行vue和node
    vue-$nextTick() 没有获取到DOM
    Window Terminal
    解析NaN
    HTML页面预览表格文件内容
    python爬虫-爬坑之路
    VSCode-VUE模板文件
    markdown-sample.md
    待继续博文
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/16408366.html
Copyright © 2020-2023  润新知