文档大纲

如何把网站打包成一个windows应用

最近,使用Wordpress+插件tutor LMS在局域网内的NAS打造了一个内部培训网站。因为是局域网内,外部使用动态域名打开网站时,就需要添加端口,为便于使用,就依托网站,制作了一个windows应用,在应用中不需要输入网址即可打开网站,进行学习。这里总结一下整个流程,希望可以帮助有相似需求的朋友。

一、准备工作(先装好工具)

1. 安装 Node.js(应用运行的基础)

Electron(我们做应用的框架)需要 Node.js 支持,先装它:

  • 打开浏览器,访问 Node.js 官网:https://nodejs.org/
  • 下载「LTS 版本」(长期支持版,更稳定),比如 v20.x.x,选 Windows Installer (.msi) 64 位
  • 双击安装包,全程下一步,重点:安装时一定要勾选「Add Node.js to PATH」(自动添加环境变量,不然命令行用不了)
  • 验证是否装成功:按 Win+R 输入 cmd 打开命令提示符,输入 node -v,如果显示版本号(比如 v20.10.0),就说明装好了

2. 安装代码编辑器(写代码用)

推荐 VS Code(免费、简单):

二、创建项目文件夹(给应用找个家)

  1. 在桌面新建一个文件夹,命名为 PeixunApp(培训应用的意思,名字随便改)
  2. 双击打开这个文件夹,在空白处右键,选择「打开方式」→「VS Code」(如果没有,选「更多应用」里找)

三、初始化项目(告诉电脑这是个 Electron 应用)

  1. 在 VS Code 里,点击顶部「终端」→「新建终端」(底部会出现黑色命令行窗口)
  2. 输入以下命令,按回车(作用:快速创建项目配置文件):
npm init -y
  1. 继续输入以下命令,按回车(安装 Electron 和所需插件,耐心等 1-3 分钟,网络慢的话多等会):
npm install electron electron-updater electron-window-state electron-log --save
  • 出现一堆文字但没有红色报错,就说明安装成功了

四、编写应用代码(核心步骤,直接复制就行)

在 VS Code 的左侧「资源管理器」里,你会看到 package.json 文件,现在新建 3 个文件,全部复制下面的代码:

步骤 1:新建 main.js(应用的核心控制文件)

  1. 点击左侧「新建文件」按钮,输入 main.js,回车
  2. 复制下面的代码,覆盖空白文件:
// 引入Electron的核心功能
const { app, BrowserWindow, Menu, ipcMain, dialog, globalShortcut } = require('electron');
const path = require('path');
const windowStateKeeper = require('electron-window-state'); // 记住窗口大小/位置
const { autoUpdater } = require('electron-updater'); // 自动更新
const log = require('electron-log'); // 日志(方便调试)

// 配置自动更新日志
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
log.info('应用启动,开始检查更新');

// 声明主窗口变量
let mainWindow;

// 创建应用窗口的函数
function createWindow() {
  // 加载窗口状态(记住上次关闭时的大小/位置)
  const mainWindowState = windowStateKeeper({
    defaultWidth: 1200, // 默认宽度
    defaultHeight: 800  // 默认高度
  });

  // 创建窗口
  mainWindow = new BrowserWindow({
    x: mainWindowState.x, // 窗口位置x轴
    y: mainWindowState.y, // 窗口位置y轴
    width: mainWindowState.width, // 窗口宽度
    height: mainWindowState.height, // 窗口高度
    webPreferences: {
      nodeIntegration: true, // 允许使用Node.js功能(小白不用管)
      contextIsolation: false, // 简化代码操作
      webviewTag: true // 支持嵌入外部网页(笔记用)
    }
  });

  // 加载你的培训网站(核心!改成你的地址)
  mainWindow.loadURL('http://winerking.picp.net:8081');

  // 把导航栏注入到网站中(后面会写index.html)
  mainWindow.webContents.on('did-finish-load', () => {
    mainWindow.webContents.executeJavaScript(`
      // 注入导航栏HTML
      const navBar = document.createElement('div');
      navBar.style.cssText = 'height: 40px; background: #f5f5f5; padding: 0 10px; display: flex; align-items: center; gap: 10px; position: sticky; top: 0; z-index: 9999;';
      navBar.innerHTML = \`
        <button id="backBtn" style="padding: 5px 10px; cursor: pointer;">← 后退</button>
        <button id="forwardBtn" style="padding: 5px 10px; cursor: pointer;">前进 →</button>
        <button id="refreshBtn" style="padding: 5px 10px; cursor: pointer;">刷新</button>
        <button id="homeBtn" style="padding: 5px 10px; cursor: pointer;">返回首页</button>
        <span style="flex: 1; text-align: center; font-size: 14px; color: #666;">培训学习系统</span>
      \`;
      document.body.prepend(navBar);

      // 导航按钮事件
      document.getElementById('backBtn').onclick = () => window.history.back();
      document.getElementById('forwardBtn').onclick = () => window.history.forward();
      document.getElementById('refreshBtn').onclick = () => window.location.reload();
      document.getElementById('homeBtn').onclick = () => window.location.href = 'http://winerking.picp.net:8081';

      // 动态禁用/启用按钮(没后退记录就灰显)
      setInterval(() => {
        document.getElementById('backBtn').disabled = window.history.length <= 1;
        document.getElementById('forwardBtn').disabled = !window.history.forward();
      }, 500);

      // 离线检测(每10秒检查一次网站是否能访问)
      setInterval(() => {
        fetch('http://winerking.picp.net:8081', { method: 'HEAD' })
          .catch(() => {
            const tip = document.createElement('div');
            tip.style.cssText = 'position: fixed; top: 50px; left: 50%; transform: translateX(-50%); background: #ff4444; color: white; padding: 10px; z-index: 9999; border-radius: 4px;';
            tip.innerText = '⚠️ 已断开培训网站连接,请检查NAS/局域网!';
            document.body.appendChild(tip);
            setTimeout(() => tip.remove(), 5000);
          });
      }, 10000);
    `);
  });

  // 自定义右键菜单(刷新、后退、复制等)
  mainWindow.webContents.on('context-menu', (e, params) => {
    const contextMenu = Menu.buildFromTemplate([
      { label: '后退', click: () => mainWindow.webContents.goBack(), enabled: mainWindow.webContents.canGoBack() },
      { label: '前进', click: () => mainWindow.webContents.goForward(), enabled: mainWindow.webContents.canGoForward() },
      { label: '刷新', click: () => mainWindow.webContents.reload() },
      { type: 'separator' }, // 分隔线
      { label: '复制', click: () => mainWindow.webContents.copy(), enabled: params.editFlags.canCopy },
      { label: '粘贴', click: () => mainWindow.webContents.paste(), enabled: params.editFlags.canPaste }
    ]);
    contextMenu.popup({ window: mainWindow });
  });

  // 拦截外部链接(笔记链接在应用内打开,不脱离)
  mainWindow.webContents.setWindowOpenHandler(({ url }) => {
    // 不是你的培训网站,就用子窗口打开
    if (!url.includes('winerking.picp.net')) {
      const noteWindow = new BrowserWindow({
        width: 1200,
        height: 800,
        parent: mainWindow, // 子窗口,关闭后返回主窗口
        title: '课程笔记',
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false
        }
      });
      noteWindow.loadURL(url);
      // 给笔记窗口加「返回课程」按钮
      noteWindow.webContents.on('did-finish-load', () => {
        noteWindow.webContents.executeJavaScript(`
          const backBtn = document.createElement('button');
          backBtn.style.cssText = 'position: fixed; top: 10px; left: 10px; padding: 5px 10px; background: #4285f4; color: white; border: none; border-radius: 4px; cursor: pointer; z-index: 9999;';
          backBtn.innerText = '← 返回课程页面';
          backBtn.onclick = () => window.close();
          document.body.prepend(backBtn);
        `);
      });
      return { action: 'deny' }; // 阻止默认跳转
    }
    return { action: 'allow' }; // 站内链接正常跳转
  });

  // 记住窗口状态(关闭时保存大小/位置)
  mainWindowState.manage(mainWindow);

  // 窗口关闭时清空变量
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

// 注册快捷键(F5刷新、Alt+←后退、Alt+→前进)
function registerShortcuts() {
  globalShortcut.register('F5', () => {
    if (mainWindow) mainWindow.webContents.reload();
  });
  globalShortcut.register('Alt+Left', () => {
    if (mainWindow && mainWindow.webContents.canGoBack()) mainWindow.webContents.goBack();
  });
  globalShortcut.register('Alt+Right', () => {
    if (mainWindow && mainWindow.webContents.canGoForward()) mainWindow.webContents.goForward();
  });
}

// 应用准备好后创建窗口+注册快捷键
app.whenReady().then(() => {
  createWindow();
  registerShortcuts();
  // 检查自动更新
  autoUpdater.checkForUpdatesAndNotify();
});

// 自动更新完成后提示重启
autoUpdater.on('update-downloaded', (info) => {
  dialog.showMessageBox({
    type: 'info',
    title: '更新完成',
    message: '新版本已下载完成,是否立即重启应用?',
    buttons: ['是', '稍后']
  }).then(({ response }) => {
    if (response === 0) autoUpdater.quitAndInstall(); // 重启并安装
  });
});

// 所有窗口关闭时退出应用(Mac系统除外,小白不用管)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
    globalShortcut.unregisterAll(); // 注销快捷键
  }
});

// Mac系统点击dock图标时重新创建窗口
app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

步骤 2:修改 package.json(项目配置文件)

  1. 点击左侧的 package.json 文件
  2. 把原有代码全部替换成下面的内容(重点:改了启动和打包配置):
{
  "name": "peixunapp",
  "version": "1.0.0",
  "description": "培训学习系统桌面应用",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "pack": "electron-builder --dir",
    "dist": "electron-builder"
  },
  "keywords": ["培训", "Electron"],
  "author": "你自己的名字",
  "license": "MIT",
  "dependencies": {
    "electron-log": "^5.0.0",
    "electron-updater": "^6.1.7",
    "electron-window-state": "^5.0.3",
    "electron": "^28.2.0"
  },
  "build": {
    "appId": "com.peixun.app",
    "productName": "培训学习系统",
    "copyright": "Copyright © 2026 ${author}",
    "directories": {
      "output": "dist"
    },
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": ["x64"]
        }
      ],
      "icon": "icon.ico" // 可选:放一个ico图标在项目文件夹,改名字对应
    },
    "publish": [
      {
        "provider": "generic",
        "url": "http://你的NAS地址/更新包目录/" // 替换成你的NAS更新地址,比如http://192.168.1.100/peixun_update/
      }
    ]
  }
}
  • 重点修改处:
    1. "author":改成你的名字(比如 "张三")
    2. "url":改成你的 NAS 文件夹地址(用来放更新包,暂时不改也能打包,后面讲)
    3. "icon":可选,如果你有 ico 格式的图标,放在项目文件夹,名字对应就行,没有就删掉这行

五、测试运行应用(先看看好不好用)

  1. 在 VS Code 的终端里,输入以下命令,按回车:
npm start
  1. 等待几秒,会弹出一个窗口,里面就是你的培训网站!
  2. 测试功能:
    • 右键点击页面,会出现「后退、前进、刷新」等菜单
    • 页面顶部有导航栏,点击「后退」「刷新」能正常用
    • 点击课程笔记链接,会弹出新窗口,里面是笔记,还有「返回课程」按钮
    • 断开 NAS 后,会弹出「已断开连接」的提示
    • 按 F5 能刷新,Alt+←能后退,符合 Windows 习惯

六、打包成 Windows 安装文件(发给其他人用)

步骤 1:安装打包工具

在终端输入以下命令,按回车:

npm install electron-builder --save-dev

步骤 2:开始打包

  1. 终端输入以下命令,按回车(打包需要 1-5 分钟,耐心等):
npm run dist
  1. 打包完成后,在你的 PeixunApp 文件夹里,会多出一个 dist 文件夹
  2. 打开 dist 文件夹,里面有:
    • 培训学习系统 Setup 1.0.0.exe:Windows 安装文件(双击就能安装)
    • win-unpacked 文件夹:免安装版(直接打开里面的 exe 就能用)

步骤 3:给其他人用

把 培训学习系统 Setup 1.0.0.exe 发给同事 / 学员,双击安装,就能在桌面看到应用图标,打开直接用,不用输网址!

七、配置自动更新(可选,让应用能自动更)

  1. 在NAS 上新建一个文件夹,比如 peixun_update,把打包后的 dist 文件夹里的 latest.yml、培训学习系统 Setup 1.0.0.exe 等文件复制进去
  2. 把 package.json 里的 "url" 改成这个 NAS 文件夹的网络地址(比如 http://192.168.1.100/peixun_update/)
  3. 重新打包(npm run dist),把新的安装文件发给用户,以后更新时,只需要把新的打包文件放到 NAS 的 peixun_update 文件夹,用户打开应用就会提示更新!

八、常见问题解决(小白必看)

  1. 终端报错「npm 不是内部命令」:重新安装 Node.js,一定要勾选「Add to PATH」,安装后重启 VS Code。
  2. 打包慢 / 失败:换国内 npm 镜像,终端输入 npm config set registry https://registry.npmmirror.com,然后重新安装依赖。
  3. 应用打不开网站:检查 NAS 是否开机、局域网是否通、网址 http://winerking.picp.net:8081 是否能在浏览器打开。
  4. 右键菜单不显示:测试运行时(npm start)看终端有没有红色报错,一般是依赖没装全,重新执行 npm install。

总结

  1. 核心步骤:装 Node.js → 建文件夹 → 初始化项目 → 写代码 → 测试 → 打包。
  2. 关键功能:右键菜单、顶部导航栏、外部链接拦截、自动更新、离线提示、快捷键,全部集成在代码里,复制即用。
  3. 打包后的 exe 文件可以直接发给其他人,不用装任何环境,双击就能安装使用。

跟着这个教程走,你就能做出一个功能完整、交互友好的培训网站桌面应用啦!如果哪一步卡壳了,回头检查命令有没有输错、代码有没有复制全就行。

阅读量: 351