yangg 2 hónapja
szülő
commit
76e79424cf
9 módosított fájl, 504 hozzáadás és 9 törlés
  1. 40 0
      electron-builder-simple.json
  2. 32 0
      electron-builder.json
  3. 199 0
      electron/main.cjs
  4. 39 0
      electron/preload.cjs
  5. 124 0
      electron/test.html
  6. 7 1
      package.json
  7. 16 0
      src/main.js
  8. 38 7
      src/router/index.js
  9. 9 1
      vite.config.js

+ 40 - 0
electron-builder-simple.json

@@ -0,0 +1,40 @@
+{
+  "appId": "com.yourcompany.yourapp",
+  "productName": "Your App Name",
+  "directories": {
+    "output": "electron-build"
+  },
+  "files": [
+    "dist/**/*",
+    "electron/**/*.cjs",
+    "package.json"
+  ],
+  "win": {
+    "icon": "public/icon.ico",
+    "target": [
+      "portable"
+    ],
+    "extraResources": [
+      {
+        "from": "resources/win/hideConsole.vbs",
+        "to": "hideConsole.vbs"
+      }
+    ]
+  },
+  "asar": false,
+  "forceCodeSigning": false,
+  "npmRebuild": false,
+  "buildDependenciesFromSource": true,
+  "win": {
+    "target": ["portable"],
+    "icon": "public/icon.ico"
+  },
+  "linux": {
+    "target": ["AppImage"],
+    "category": "Office"
+  },
+  "mac": {
+    "target": ["dmg"],
+    "category": "public.app-category.productivity"
+  }
+}

+ 32 - 0
electron-builder.json

@@ -0,0 +1,32 @@
+{
+  "appId": "com.yourcompany.yourapp",
+  "productName": "Your App Name",
+  "directories": {
+    "output": "electron-build"
+  },
+  "files": [
+    "dist/**/*",
+    "electron/**/*",
+    "package.json"
+  ],
+  "win": {
+    "icon": "public/icon.ico",
+    "target": [
+      "portable"
+    ],
+    "sign": null
+  },
+  "nsis": {
+    "oneClick": false,
+    "allowToChangeInstallationDirectory": true,
+    "createDesktopShortcut": true
+  },
+  "electronDownload": {
+    "mirror": "https://npmmirror.com/mirrors/electron/"
+  },
+  "extraMetadata": {
+    "type": "commonjs"
+  },
+  "asar": false,
+  "forceCodeSigning": false
+}

+ 199 - 0
electron/main.cjs

@@ -0,0 +1,199 @@
+const { app, BrowserWindow, ipcMain } = require('electron')
+const path = require('path')
+const fs = require('fs')
+
+// 设置环境变量
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+
+// 命令行参数
+const argv = process.argv.slice(2);
+
+// 保存主窗口引用
+let mainWindow
+
+// 设置文件路径
+const settingsPath = path.join(app.getPath('userData'), 'settings.json');
+
+// 加载设置
+function loadSettings() {
+  try {
+    if (fs.existsSync(settingsPath)) {
+      const data = fs.readFileSync(settingsPath, 'utf8');
+      console.log('读取到设置文件:', data);
+      try {
+        return JSON.parse(data);
+      } catch (parseErr) {
+        console.error('解析设置文件失败:', parseErr);
+      }
+    } else {
+      console.log('设置文件不存在:', settingsPath);
+    }
+  } catch (err) {
+    console.error('加载设置失败:', err);
+  }
+  return { defaultLoad: false };
+}
+
+// 保存设置
+function saveSettings(settings) {
+  try {
+    // 确保目录存在
+    const settingsDir = path.dirname(settingsPath);
+    if (!fs.existsSync(settingsDir)) {
+      fs.mkdirSync(settingsDir, { recursive: true });
+    }
+    
+    const data = JSON.stringify(settings, null, 2);
+    console.log('保存设置:', data, '到路径:', settingsPath);
+    fs.writeFileSync(settingsPath, data, 'utf8');
+    return true;
+  } catch (err) {
+    console.error('保存设置失败:', err);
+    return false;
+  }
+}
+
+function createWindow() {
+  // 加载设置
+  const settings = loadSettings();
+  console.log('当前设置:', settings);
+  
+  // 判断是否直接加载应用
+  // 命令行参数优先,其次是设置,最后是生产环境默认值
+  const shouldDirectLoad = argv.includes('--direct-load') || 
+                          settings.defaultLoad === true || 
+                          process.env.NODE_ENV === 'production' ||
+                          process.env.FORCE_DIRECT_LOAD === 'true';
+  
+  console.log('是否直接加载应用:', shouldDirectLoad, 
+              '(命令行:', argv.includes('--direct-load'),
+              ', 设置:', settings.defaultLoad,
+              ', 环境:', process.env.NODE_ENV, ')');
+  
+  // 创建浏览器窗口
+  mainWindow = new BrowserWindow({
+    width: 1200,
+    height: 800,
+    webPreferences: {
+      nodeIntegration: false,
+      contextIsolation: true,
+      preload: path.join(__dirname, 'preload.cjs'),
+      webSecurity: process.env.NODE_ENV === 'production',
+      devTools: true
+    }
+  });
+
+  // 添加控制台消息监听
+  mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
+    console.log(`[WebContents] ${message}`);
+  });
+
+  // 打开开发者工具
+  mainWindow.webContents.openDevTools();
+
+  if (shouldDirectLoad) {
+    // 直接加载Vue应用并导航到报表页面
+    console.log('直接加载报表页面...');
+    loadVueApp('/report');
+  } else {
+    // 加载测试页面
+    const testPath = path.join(__dirname, 'test.html');
+    console.log('加载测试页面...');
+    mainWindow.loadFile(testPath);
+  }
+
+  // 添加错误处理
+  mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
+    console.error('页面加载失败:', errorCode, errorDescription);
+    const testPath = path.join(__dirname, 'test.html');
+    if (fs.existsSync(testPath)) {
+      mainWindow.loadFile(testPath);
+    }
+  });
+
+  // 在加载页面前添加
+  mainWindow.webContents.on('dom-ready', () => {
+    console.log('DOM已准备就绪');
+  });
+
+  // 在页面加载后执行脚本
+  mainWindow.webContents.on('did-finish-load', () => {
+    console.log('页面加载完成');
+  });
+}
+
+// 当Electron完成初始化并准备创建浏览器窗口时调用此方法
+app.whenReady().then(() => {
+  createWindow()
+  
+  // 设置IPC监听器
+  ipcMain.on('load-app', (event, route) => {
+    console.log('收到加载应用请求, 路由:', route || '默认');
+    loadVueApp(route);
+  });
+  
+  // 添加保存设置的IPC监听器
+  ipcMain.on('save-settings', (event, settings) => {
+    console.log('收到保存设置请求:', settings);
+    const success = saveSettings(settings);
+    console.log('保存设置结果:', success ? '成功' : '失败');
+  });
+  
+  // 添加获取设置的IPC处理器
+  ipcMain.handle('get-settings', () => {
+    return loadSettings();
+  });
+  
+  app.on('activate', () => {
+    if (BrowserWindow.getAllWindows().length === 0) {
+      createWindow()
+    }
+  })
+})
+
+// 加载Vue应用的函数
+function loadVueApp(route) {
+  if (!mainWindow) return;
+  
+  const indexPath = path.join(__dirname, '../dist/index.html');
+  console.log('尝试加载Vue应用:', indexPath);
+  
+  if (fs.existsSync(indexPath)) {
+    console.log('Vue应用文件存在,正在加载...');
+    
+    // 构建URL,如果有指定路由则添加
+    let url = `file://${indexPath}?t=${Date.now()}`;
+    if (route) {
+      url += `#${route}`; // 使用hash模式路由
+    }
+    
+    console.log('加载URL:', url);
+    
+    // 在加载前清除缓存
+    mainWindow.webContents.session.clearCache().then(() => {
+      mainWindow.loadURL(url);
+      
+      // 监听加载完成事件
+      mainWindow.webContents.once('did-finish-load', () => {
+        console.log('Vue应用加载完成');
+      });
+    });
+  } else {
+    console.error('Vue应用文件不存在:', indexPath);
+    mainWindow.webContents.executeJavaScript(`
+      alert('Vue应用文件不存在: ${indexPath}');
+    `).catch(err => console.error('执行脚本失败:', err));
+  }
+}
+
+// 关闭所有窗口时退出应用
+app.on('window-all-closed', () => {
+  if (process.platform !== 'darwin') {
+    app.quit()
+  }
+})
+
+// 添加全局未捕获异常处理
+process.on('uncaughtException', (error) => {
+  console.error('未捕获的异常:', error)
+})

+ 39 - 0
electron/preload.cjs

@@ -0,0 +1,39 @@
+const { contextBridge, ipcRenderer } = require('electron');
+
+// 在window加载完成时执行
+window.addEventListener('DOMContentLoaded', () => {
+  console.log('预加载脚本已执行');
+  
+  try {
+    // 使用更安全的方式添加调试信息
+    const debugDiv = document.createElement('div');
+    debugDiv.id = 'electron-debug';
+    debugDiv.textContent = 'Electron已加载';
+    debugDiv.style.position = 'fixed';
+    debugDiv.style.bottom = '10px';
+    debugDiv.style.right = '10px';
+    debugDiv.style.padding = '5px';
+    debugDiv.style.background = 'rgba(0,0,0,0.7)';
+    debugDiv.style.color = 'white';
+    debugDiv.style.zIndex = '9999';
+    
+    // 等待DOM完全加载
+    setTimeout(() => {
+      if (document.body) {
+        document.body.appendChild(debugDiv);
+      }
+    }, 1000);
+  } catch (e) {
+    console.error('预加载脚本错误:', e);
+  }
+});
+
+// 使用contextBridge安全地暴露API
+contextBridge.exposeInMainWorld('electronAPI', {
+  versions: process.versions,
+  isElectron: true,
+  loadApp: (route) => ipcRenderer.send('load-app', route),
+  platform: process.platform,
+  saveSettings: (settings) => ipcRenderer.send('save-settings', settings),
+  getSettings: () => ipcRenderer.invoke('get-settings')
+});

+ 124 - 0
electron/test.html

@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Electron测试</title>
+  <style>
+    body { font-family: Arial, sans-serif; padding: 20px; }
+    button { padding: 10px 15px; margin-top: 20px; cursor: pointer; }
+    #electron-info { margin-top: 20px; padding: 10px; background: #f0f0f0; }
+  </style>
+</head>
+<body>
+  <h1>Electron测试页面</h1>
+  <p>如果您看到这个页面,说明Electron可以正常加载HTML。</p>
+  <div id="electron-info"></div>
+  
+  <button id="loadApp">加载Vue应用</button>
+  <button id="debugInfo">显示调试信息</button>
+  
+  <div id="debug-output" style="margin-top: 20px; white-space: pre; background: #eee; padding: 10px; display: none;"></div>
+  
+  <div style="margin-top: 20px;">
+    <h3>直接访问路由</h3>
+    <button id="loadReport">加载报表页面</button>
+    <button id="loadTest">加载测试页面</button>
+  </div>
+  
+  <div style="margin-top: 20px;">
+    <h3>启动选项</h3>
+    <label>
+      <input type="checkbox" id="defaultLoad" checked> 
+      下次启动时直接加载报表页面
+    </label>
+    <button id="saveSettings" style="margin-left: 10px;">保存设置</button>
+    <div id="settingsStatus" style="margin-top: 5px; color: green;"></div>
+  </div>
+  
+  <script>
+    // 显示Electron版本信息
+    document.getElementById('electron-info').textContent = 
+      window.electronAPI ? `Electron版本: ${window.electronAPI.versions.electron}` : '未检测到Electron API';
+    
+    // 加载Vue应用按钮
+    document.getElementById('loadApp').addEventListener('click', function() {
+      if (window.electronAPI && window.electronAPI.loadApp) {
+        window.electronAPI.loadApp();
+      } else {
+        alert('无法加载应用,electronAPI未定义');
+      }
+    });
+    
+    // 显示调试信息按钮
+    document.getElementById('debugInfo').addEventListener('click', function() {
+      const debugOutput = document.getElementById('debug-output');
+      debugOutput.style.display = debugOutput.style.display === 'none' ? 'block' : 'none';
+      
+      if (debugOutput.style.display === 'block') {
+        let info = '';
+        // 收集环境信息
+        info += `User Agent: ${navigator.userAgent}\n`;
+        info += `Platform: ${navigator.platform}\n`;
+        info += `Screen: ${window.screen.width}x${window.screen.height}\n`;
+        
+        if (window.electronAPI) {
+          info += `Electron: ${window.electronAPI.versions.electron}\n`;
+          info += `Chrome: ${window.electronAPI.versions.chrome}\n`;
+          info += `Node: ${window.electronAPI.versions.node}\n`;
+        }
+        
+        debugOutput.textContent = info;
+      }
+    });
+    
+    // 添加直接访问路由的按钮事件
+    document.getElementById('loadReport').addEventListener('click', function() {
+      if (window.electronAPI && window.electronAPI.loadApp) {
+        window.electronAPI.loadApp('/report');
+      }
+    });
+    
+    document.getElementById('loadTest').addEventListener('click', function() {
+      if (window.electronAPI && window.electronAPI.loadApp) {
+        window.electronAPI.loadApp('/test');
+      }
+    });
+    
+    // 添加保存设置的功能
+    document.getElementById('saveSettings').addEventListener('click', function() {
+      const defaultLoad = document.getElementById('defaultLoad').checked;
+      const statusEl = document.getElementById('settingsStatus');
+      
+      if (window.electronAPI && window.electronAPI.saveSettings) {
+        window.electronAPI.saveSettings({ defaultLoad: defaultLoad });
+        statusEl.textContent = '设置已保存: ' + (defaultLoad ? '直接加载报表页面' : '显示测试页面');
+        statusEl.style.display = 'block';
+        
+        // 3秒后隐藏状态信息
+        setTimeout(() => {
+          statusEl.style.display = 'none';
+        }, 3000);
+      } else {
+        alert('无法保存设置,electronAPI未定义');
+      }
+    });
+    
+    // 在页面加载时获取当前设置
+    window.addEventListener('DOMContentLoaded', async function() {
+      if (window.electronAPI && window.electronAPI.getSettings) {
+        try {
+          const settings = await window.electronAPI.getSettings();
+          console.log('当前设置:', settings);
+          
+          // 更新复选框状态
+          if (settings && typeof settings.defaultLoad === 'boolean') {
+            document.getElementById('defaultLoad').checked = settings.defaultLoad;
+          }
+        } catch (err) {
+          console.error('获取设置失败:', err);
+        }
+      }
+    });
+  </script>
+</body>
+</html>

+ 7 - 1
package.json

@@ -3,10 +3,16 @@
   "private": true,
   "version": "0.0.0",
   "type": "module",
+  "main": "electron/main.cjs",
   "scripts": {
     "dev": "vite --mode development",
     "build": "vite build --mode production",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "electron:dev": "vite build && electron .",
+    "electron:dev:direct": "vite build && electron . --direct-load",
+    "electron:dev:force": "vite build && cross-env FORCE_DIRECT_LOAD=true electron .",
+    "electron:dev:noconsole": "vite build && electron . --hide-console",
+    "electron:build": "rimraf electron-build && vite build && electron-builder --config electron-builder-simple.json"
   },
   "dependencies": {
     "@ant-design/icons-vue": "^7.0.1",

+ 16 - 0
src/main.js

@@ -23,3 +23,19 @@ app.use(store)
 // 挂载dom
 app.mount('#app')
 
+console.log('Vue应用正在初始化...');
+
+// 添加错误处理
+window.addEventListener('error', (event) => {
+  console.error('捕获到错误:', event.error);
+  // 防止无限递归
+  if (event.error && event.error.message && event.error.message.includes('Maximum call stack size exceeded')) {
+    console.error('检测到栈溢出错误,可能存在循环引用');
+  }
+});
+
+// 检测是否在Electron环境中
+if (window.electronAPI) {
+  console.log('在Electron环境中运行');
+}
+

+ 38 - 7
src/router/index.js

@@ -1,10 +1,41 @@
-import { createRouter, createWebHistory } from 'vue-router'
+import { createRouter, createWebHashHistory } from 'vue-router'
 import { ROUTE } from './menu.js'
 
-const route = createRouter({
-    history: createWebHistory(import.meta.env.BASE_URL),
-    scrollBehavior: () => ({ y: 0 }),
-    routes: ROUTE
-})
+// 使用命名的异步组件,便于调试
+const XVueComponent = () => import(/* webpackChunkName: "xvue" */ '@views/xvue.vue');
 
-export default route
+const routes = [
+  {
+    path: '/',
+    name: 'main',
+    redirect: '/report',
+    children: [
+      {
+        path: '/report',
+        name: 'report',
+        title: '报表中心',
+        component: XVueComponent
+      },
+    ]
+  },
+  // 添加一个简单的测试路由,用于验证路由系统是否正常工作
+  {
+    path: '/test',
+    name: 'test',
+    component: { 
+      template: '<div>测试路由页面</div>' 
+    }
+  }
+];
+
+// 创建路由器时添加错误处理
+const router = createRouter({
+  history: createWebHashHistory(), // 在Electron中使用hash模式更可靠
+  routes
+});
+
+router.onError((error) => {
+  console.error('路由错误:', error);
+});
+
+export default router

+ 9 - 1
vite.config.js

@@ -53,6 +53,14 @@ export default defineConfig(({ command, mode }) => {
         }
       }
     },
-    base: '/',
+    base: './',
+    build: {
+      sourcemap: true,
+      rollupOptions: {
+        output: {
+          manualChunks: undefined
+        }
+      }
+    }
   }
 })