Compare commits

..

No commits in common. "fdc008f33cb68dc2211f9630e6b7e3d590ee4620" and "89ee08789dcb8d6293c6af4be2b114a3d337859b" have entirely different histories.

12 changed files with 83 additions and 360 deletions

113
README.md
View File

@ -1,113 +0,0 @@
# EIAC Desktop Application
一个基于 Electron 框架的跨平台桌面应用程序,用于访问和管理企业内部应用系统。
## 概述
EIAC Desktop Application 是一个基于 Electron 框架的桌面应用程序。
主要功能包括:
- 🏢 统一的企业应用访问入口
- 📑 多标签页管理
- 🔍 自定义页面缩放
- 🔐 统一的用户认证
- 🐛 故障上报功能
- 🖥️ 跨平台支持Windows、macOS、Linux
本项目使用 `npx create-electron-app@latest china-telecom-app --template=vite-typescript` 创建。
## 开发
### 环境要求
- Node.js >= 22.11.0
- npm >= 11.3.0
- Git
### 开发环境设置
1. 克隆仓库
```bash
git clone [repository-url]
cd china-telecom-app
```
2. 安装依赖
```bash
npm install
```
3. 启动开发服务器
```bash
npm run start
```
### 开发指南
- 使用 TypeScript 进行开发
- 遵循 ESLint 规范
- 使用 Prettier 进行代码格式化
- 主要开发文件位于 `src` 目录下
- 使用 IPC 通信进行主进程和渲染进程的通信
## 构建
### 构建命令
```bash
npm run package
```
### 构建配置
- 构建配置位于 `forge.config.js`
- 支持自定义应用图标
- 支持自定义应用名称
- 支持自定义构建目标平台
## 发布
### 发布流程
1. 更新版本号
在确认仓库没有任何未提交的更改后,执行以下命令更新版本号:
```bash
npm version [patch|minor|major]
```
如有未更改的提交,执行以上命令会报错:`npm error Git working directory not clean.`
或者手工编辑 `package.json` 文件,将 `version` 字段更新为新版本号。
2. 构建版本
```bash
npm run package
```
3. 打包版本
```bash
npm run make
```
4. 发布到发布服务器
```bash
npm run publish
```
### 发布注意事项
- 确保版本号正确更新
- 确保所有依赖都是最新的稳定版本
- 确保构建配置正确
- 测试发布版本的功能完整性
## 参考文档
- [Electron](https://www.electronjs.org/) - 跨平台桌面应用框架
- [Electron Forge](https://www.electronforge.io/) - Electron 应用打包工具
- [TypeScript](https://www.typescriptlang.org/) - JavaScript 的超集
- [electron-tabs](https://github.com/brrd/electron-tabs) - Electron 标签页管理
- [vite](https://vite.dev/) - 现代前端构建工具

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -13,25 +13,10 @@ const config: ForgeConfig = {
extraResource: [ extraResource: [
'assets/' 'assets/'
], ],
icon: 'assets/icon', icon: 'assets/icon.ico',
}, },
rebuildConfig: {}, rebuildConfig: {},
makers: [ makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],
new MakerSquirrel({
authors: 'Allen Cai',
owners: 'Allen Cai',
exe: 'china-telecom-app.exe',
name: 'china-telecom-app',
version: require('./package.json').version,
description: 'China Telecom App',
copyright: 'Copyright © 2025 Allen Cai',
noMsi: true,
setupIcon: 'assets/icon.ico',
}),
new MakerZIP({}, ['darwin']),
new MakerRpm({}),
new MakerDeb({})
],
plugins: [ plugins: [
new VitePlugin({ new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc. // `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.

View File

@ -19,7 +19,7 @@
</div> </div>
<div class="header-right"> <div class="header-right">
<span id="userInfo"></span> <span id="userInfo"></span>
<button id="btnExit" class="exit-btn">退出</button> <button id="btnLogout" class="logout-btn">退出登录</button>
</div> </div>
</header> </header>
@ -47,12 +47,8 @@
<button id="closeErrorModal" class="close-btn">取消</button> <button id="closeErrorModal" class="close-btn">取消</button>
</div> </div>
</div> </div>
<!-- 帮助图标 --> <script src="node_modules/electron-tabs/dist/electron-tabs.js"></script>
<img id="helpIcon" src="./assets/help.png" alt="帮助图标" class="help-icon">
<script type="module">
import 'electron-tabs';
</script>
<script type="module" src="./src/index.ts"></script> <script type="module" src="./src/index.ts"></script>
</body> </body>

View File

@ -1,8 +1,8 @@
{ {
"name": "china-telecom-app", "name": "china-telecom-app",
"productName": "china-telecom-app", "productName": "china-telecom-app",
"version": "1.1.1", "version": "1.0.0",
"description": "China Telecom App", "description": "My Electron application description",
"main": ".vite/build/main.js", "main": ".vite/build/main.js",
"scripts": { "scripts": {
"start": "cross-env NODE_ENV=development electron-forge start", "start": "cross-env NODE_ENV=development electron-forge start",

View File

@ -1,4 +1,4 @@
import { BrowserWindow, ipcMain, screen, session, app } from "electron"; import { BrowserWindow, ipcMain, screen, session } from "electron";
import http from 'http'; import http from 'http';
import https from 'https'; import https from 'https';
import os from 'os'; import os from 'os';
@ -8,11 +8,6 @@ import { ApiResponse, MenuItem, TagResolutionConfig, EIACDesktopApi, SpecialPUrl
const memoryCache = new Map<string, any>(); const memoryCache = new Map<string, any>();
export function initialize(): void { export function initialize(): void {
// Close app
ipcMain.handle('app:close', (): void => {
app.exit(0); // 使用 exit 而不是 quit确保立即退出
});
// Set cache // Set cache
ipcMain.handle('cache:set', (_event, key: string, value: any): void => { ipcMain.handle('cache:set', (_event, key: string, value: any): void => {
memoryCache.set(key, value); memoryCache.set(key, value);
@ -45,7 +40,7 @@ export function initialize(): void {
return helperDescrip ? helperDescrip.Descrip : null; return helperDescrip ? helperDescrip.Descrip : null;
}); });
// Get zoom factor by url // Get zoom factor
ipcMain.handle('get-zoom-factor-by-url', async (event, url: string): Promise<number> => { ipcMain.handle('get-zoom-factor-by-url', async (event, url: string): Promise<number> => {
const display: Electron.Display = screen.getPrimaryDisplay(); const display: Electron.Display = screen.getPrimaryDisplay();
const physicalSize: Electron.Size = { const physicalSize: Electron.Size = {
@ -118,13 +113,7 @@ export function initialize(): void {
}, },
(res) => { (res) => {
console.log('check-url-available HEAD', url.toString(), res.statusCode, res.statusMessage); console.log('check-url-available HEAD', url.toString(), res.statusCode, res.statusMessage);
/** if (res.statusCode === 403 || res.statusCode === 404) {
* 403: 禁止访问
* 404: 未找到
* 405: 方法不允许
*/
const requiresRetryStatusCodes: number[] = [403, 404, 405];
if (requiresRetryStatusCodes.includes(res.statusCode)) {
headReq.destroy() headReq.destroy()
const getReq = lib.get({ const getReq = lib.get({
method: 'GET', method: 'GET',
@ -164,7 +153,7 @@ export function initialize(): void {
} }
}); });
// Set webview's cookie // Set webviews cookie
ipcMain.handle('set-webview-cookie', async (event, url: string, cookie: string): Promise<boolean> => { ipcMain.handle('set-webview-cookie', async (event, url: string, cookie: string): Promise<boolean> => {
try { try {
const parsedUrl = new URL(url); const parsedUrl = new URL(url);
@ -199,24 +188,12 @@ export function initialize(): void {
} }
const base64 = await captureWindowAsBase64(win); const base64 = await captureWindowAsBase64(win);
console.debug('base64:', base64); console.log('base64:', base64);
const account: string = memoryCache.get('Account');
if (!account) {
throw new Error('Not found account');
}
console.log('account:', account);
const ip: string = getLocalIPAddress();
if (!ip) {
throw new Error('Not found ip');
}
console.log('ip:', ip);
try { try {
const response: ApiResponse<FaultReportingResponse> = await EIACDesktopApi.Help.FaultReportingAsync({ const response: ApiResponse<FaultReportingResponse> = await EIACDesktopApi.Help.FaultReportingAsync({
Account: account, Account: memoryCache.get('Account'),
IP: ip, IP: getLocalIPAddress(),
Url: url, Url: url,
ImgBase64: base64, ImgBase64: base64,
Explain: `message: ${message}, status: ${status}` Explain: `message: ${message}, status: ${status}`

View File

@ -44,7 +44,7 @@ body {
gap: 20px; gap: 20px;
} }
.exit-btn { .logout-btn {
padding: 8px 16px; padding: 8px 16px;
background-color: #f44336; background-color: #f44336;
color: white; color: white;
@ -53,7 +53,7 @@ body {
cursor: pointer; cursor: pointer;
} }
.exit-btn:hover { .logout-btn:hover {
background-color: #d32f2f; background-color: #d32f2f;
} }
@ -126,89 +126,68 @@ body {
/* 故障窗口样式 */ /* 故障窗口样式 */
.modal { .modal {
display: none; display: none;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
z-index: 1000; z-index: 1000;
} }
.modal-content { .modal-content {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background-color: white; background-color: white;
padding: 20px; padding: 20px;
border-radius: 5px; border-radius: 5px;
min-width: 300px; min-width: 300px;
text-align: center; text-align: center;
} }
.modal-content h2 { .modal-content h2 {
color: #e74c3c; color: #e74c3c;
margin-bottom: 15px; margin-bottom: 15px;
} }
.modal-content p { .modal-content p {
margin-bottom: 20px; margin-bottom: 20px;
color: #333; color: #333;
} }
.close-btn { .close-btn {
background-color: #ed7226; background-color: #ed7226;
color: white; color: white;
border: none; border: none;
padding: 8px 20px; padding: 8px 20px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
} }
.close-btn:hover { .close-btn:hover {
background-color: #ce6524; background-color: #ce6524;
} }
.fault-reporting-btn { .fault-reporting-btn {
background-color: #3498db; background-color: #3498db;
color: white; color: white;
border: none; border: none;
padding: 8px 20px; padding: 8px 20px;
border-radius: 4px; border-radius: 4px;
} }
.fault-reporting-btn:hover { .fault-reporting-btn:hover {
background-color: #2980b9; background-color: #2980b9;
} }
/* Tabs styles */ /* Tabs styles */
.tabs-container { .tabs-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
width: 100%; width: 100%;
}
/* Help icon */
.help-icon {
min-width: 32px;
min-height: 32px;
max-width: 64px;
max-height: 64px;
position: fixed;
top: 50%;
right: 20px;
transform: translateY(-50%);
z-index: 9999;
transition: transform 0.6s ease;
cursor: pointer;
opacity: 0.7;
}
.help-icon:hover {
transform: translateY(-50%) scale(1.2) rotate(360deg);
opacity: 1;
} }

View File

@ -116,23 +116,21 @@ function renderMenu(menuList: MenuItem[]): void {
* *
* @param tabGroup * @param tabGroup
* @param menuItem * @param menuItem
* @param allowCloseTab
* @returns * @returns
*/ */
async function addTabAsync(tabGroup: TabGroup, menuItem: MenuItem, allowCloseTab: boolean = true): Promise<Tab | null> { async function addTabAsync(tabGroup: TabGroup, menuItem: MenuItem): Promise<Tab | null> {
const url: string = menuItem.Url.startsWith("http") ? menuItem.Url : `http://${menuItem.Url}`; const url: string = menuItem.Url.startsWith("http") ? menuItem.Url : `http://${menuItem.Url}`;
const result: { ok: boolean; status: number; message?: string } = await window.electronAPI.checkUrlAvailable(url); const result: { ok: boolean; status: number; message?: string } = await window.electronAPI.checkUrlAvailable(url);
if (result.ok && result.status >= 200 && result.status < 400) { if (result.ok && result.status >= 200 && result.status < 400) {
console.log(`✅ URL ${url} 可访问:`, result.status); console.log(`✅ URL ${url} 可访问:`, result.status);
lastInvalidUrlResult = null; lastInvalidUrlResult = null;
const cookies: string = window.electronAPI.getSessionStorage('cookies'); const cookies: string = window.electronAPI.getSessionStorage('cookies');
await window.electronAPI.setWebviewCookie(url, cookies); await window.electronAPI.setWebviewCookie(url, cookies);
} else { } else {
console.warn(`❌ URL ${url} 不可访问:`, result.message ?? `status ${result.status}`); console.warn(`❌ URL ${url} 不可访问:`, result.message ?? `status ${result.status}`);
lastInvalidUrlResult = { url, message: result.message, status: result.status }; lastInvalidUrlResult = { url, message: result.message, status: result.status };
const helpDescrip: string = await window.electronAPI.getHelperDescripAsync(result.status.toString()) ?? `无法访问{URL}\r\n异常原因${result.message ?? `status ${result.status}`}\r\n请联系技术支持。`; const helpDescrip: string = await window.electronAPI.getHelperDescripAsync(result.status.toString()) ?? `无法访问 {URL}\r\n异常原因${result.message ?? `status ${result.status}`}\r\n${helpDescrip ?? ''}`;
showErrorModal(helpDescrip.replace('{URL}', url)); showErrorModal(helpDescrip.replace('{URL}', url));
return null; return null;
} }
@ -141,7 +139,7 @@ async function addTabAsync(tabGroup: TabGroup, menuItem: MenuItem, allowCloseTab
const tab: Tab = tabGroup.addTab({ const tab: Tab = tabGroup.addTab({
active: true, active: true,
closable: allowCloseTab, closable: true,
title: menuItem.ShowName, title: menuItem.ShowName,
src: url, src: url,
iconURL: menuItem.IconConfig._1x.Default, iconURL: menuItem.IconConfig._1x.Default,
@ -155,9 +153,7 @@ async function addTabAsync(tabGroup: TabGroup, menuItem: MenuItem, allowCloseTab
const webview: Electron.WebviewTag = tab.webview as Electron.WebviewTag; const webview: Electron.WebviewTag = tab.webview as Electron.WebviewTag;
listenWebviewTitleChange(webview, tab); listenWebviewTitleChange(webview, tab);
// 监听 webview 的 DOM 加载完成事件
tab.once('webview-dom-ready', () => { tab.once('webview-dom-ready', () => {
// 设置 webview 的缩放比例
const webview: Electron.WebviewTag = tab.webview as Electron.WebviewTag; const webview: Electron.WebviewTag = tab.webview as Electron.WebviewTag;
const defaultZoomFactor: number = webview.getZoomFactor(); const defaultZoomFactor: number = webview.getZoomFactor();
console.log('Default zoom factor:', defaultZoomFactor); console.log('Default zoom factor:', defaultZoomFactor);
@ -168,18 +164,6 @@ async function addTabAsync(tabGroup: TabGroup, menuItem: MenuItem, allowCloseTab
} else { } else {
console.log('Default zoom factor is the same as the zoom factor:', zoomFactor); console.log('Default zoom factor is the same as the zoom factor:', zoomFactor);
} }
// 监听 webview 的关闭事件
webview.addEventListener('destroyed', (_event: Event) => {
console.log('Webview destroyed, closing tab:', tab.title);
tab.close(true);
});
// 监听 webview 的关闭事件(当页面调用 window.close() 时触发)
webview.addEventListener('close', (_event: Event) => {
console.log('Webview close event triggered, closing tab:', tab.title);
tab.close(true);
});
}); });
} }
}); });
@ -193,31 +177,7 @@ async function addTabAsync(tabGroup: TabGroup, menuItem: MenuItem, allowCloseTab
* @param tab * @param tab
*/ */
function listenWebviewTitleChange(webview: Electron.WebviewTag, tab: Tab): void { function listenWebviewTitleChange(webview: Electron.WebviewTag, tab: Tab): void {
// 监听 URL 变化事件 // 在webview加载完成后获取并设置标签页的标题和图标
webview.addEventListener('did-navigate', async (event: Electron.DidNavigateEvent) => {
const url: string = event.url;
const zoomFactor: number = await window.electronAPI.getZoomFactorByUrl(url);
const currentZoomFactor: number = webview.getZoomFactor();
if (currentZoomFactor !== zoomFactor) {
webview.setZoomFactor(zoomFactor);
console.log('URL changed, modify zoom factor:', zoomFactor);
}
});
// 监听 URL 在同一个页面内的变化事件(如 hash 变化)
webview.addEventListener('did-navigate-in-page', async (event: Electron.DidNavigateInPageEvent) => {
const url: string = event.url;
const zoomFactor: number = await window.electronAPI.getZoomFactorByUrl(url);
const currentZoomFactor: number = webview.getZoomFactor();
if (currentZoomFactor !== zoomFactor) {
webview.setZoomFactor(zoomFactor);
console.log('URL in-page changed, modify zoom factor:', zoomFactor);
}
});
// 监听webview的标题变化并更新标签页的标题和图标
webview.addEventListener('did-finish-load', async () => { webview.addEventListener('did-finish-load', async () => {
const title: string = webview.getTitle(); const title: string = webview.getTitle();
tab.setTitle(title); tab.setTitle(title);
@ -267,29 +227,6 @@ async function getFaviconUrl(webview: Electron.WebviewTag): Promise<string> {
return defaultFaviconUrl; return defaultFaviconUrl;
} }
/**
* Help图标点击事件
*/
function bindHelpIconClickEvent(menuItem: MenuItem): void {
const helpIcon: HTMLImageElement = document.getElementById('helpIcon') as HTMLImageElement;
helpIcon.src = menuItem.IconConfig._1x.Default;
helpIcon.addEventListener('mouseenter', (event) => helpIcon.src = menuItem.IconConfig._1x.Selected);
helpIcon.addEventListener('mouseleave', () => helpIcon.src = menuItem.IconConfig._1x.Default);
helpIcon.addEventListener('click', async (event) => {
if (lastInvalidUrlResult) {
const helpDescrip: string = await window.electronAPI.getHelperDescripAsync(lastInvalidUrlResult.status.toString()) ?? `无法访问{URL}\r\n异常原因${lastInvalidUrlResult.message ?? `status ${lastInvalidUrlResult.status}`}\r\n请联系技术支持。`;
showErrorModal(helpDescrip.replace('{URL}', lastInvalidUrlResult.url));
} else {
const tab: Tab | null = tabGroup.tabs.find(tab => (tab.webview as Electron.WebviewTag).getURL() === menuItem.Url);
if (tab) {
tab.activate();
} else {
await addTabAsync(tabGroup, menuItem);
}
}
});
}
/** /**
* logo点击事件 * logo点击事件
*/ */
@ -312,19 +249,18 @@ function bindLogoClickEvent(tabGroup: TabGroup, menuItem: MenuItem): void {
} }
/** /**
* 退 *
*/ */
function bindExitEvent(): void { function bindLogoutEvent(): void {
const exitBtn: HTMLButtonElement = document.getElementById('btnExit') as HTMLButtonElement; const logoutBtn: HTMLButtonElement = document.getElementById('btnLogout') as HTMLButtonElement;
if (exitBtn) { if (logoutBtn) {
exitBtn.addEventListener('click', async () => { logoutBtn.addEventListener('click', () => {
window.electronAPI.removeSessionStorage('cookies'); window.electronAPI.removeSessionStorage('cookies');
await window.electronAPI.closeApp(); window.location.href = 'login.html';
}); });
} }
} }
let closeCount: number = 0;
/** /**
* *
*/ */
@ -333,31 +269,17 @@ function bindErrorModalEvent(): void {
const closeErrorModal: HTMLButtonElement = document.getElementById('closeErrorModal') as HTMLButtonElement; const closeErrorModal: HTMLButtonElement = document.getElementById('closeErrorModal') as HTMLButtonElement;
const faultReporting: HTMLButtonElement = document.getElementById('faultReporting') as HTMLButtonElement; const faultReporting: HTMLButtonElement = document.getElementById('faultReporting') as HTMLButtonElement;
faultReporting.addEventListener('click', async () => await faultReportingAsync()); faultReporting.addEventListener('click', () => faultReportingAsync());
// Close button click event // Close button click event
closeErrorModal.addEventListener('click', () => { closeErrorModal.addEventListener('click', () => {
errorModal.style.display = 'none'; errorModal.style.display = 'none';
closeCount++;
// 如果关闭次数大于等于2则重置计数并且清空lastInvalidUrlResult
if (closeCount >= 2) {
closeCount = 0;
lastInvalidUrlResult = null;
}
}); });
// Click outside to close // Click outside to close
window.addEventListener('click', (event: Event) => { window.addEventListener('click', (event: Event) => {
if (event.target === errorModal) { if (event.target === errorModal) {
errorModal.style.display = 'none'; errorModal.style.display = 'none';
closeCount++;
// 如果关闭次数大于等于2则重置计数并且清空lastInvalidUrlResult
if (closeCount >= 2) {
closeCount = 0;
lastInvalidUrlResult = null;
}
} }
}); });
} }
@ -394,7 +316,6 @@ async function faultReportingAsync(): Promise<void> {
// 显示请求结果 // 显示请求结果
if (result.ok) { if (result.ok) {
lastInvalidUrlResult = null;
faultReportingBtn.textContent = '上报成功'; faultReportingBtn.textContent = '上报成功';
faultReportingBtn.style.backgroundColor = '#4CAF50'; faultReportingBtn.style.backgroundColor = '#4CAF50';
setTimeout(() => { setTimeout(() => {
@ -440,18 +361,14 @@ async function initialize(): Promise<void> {
// Create initial tab // Create initial tab
const firstMenuItem: MenuItem = menuList[0]; const firstMenuItem: MenuItem = menuList[0];
await addTabAsync(tabGroup, firstMenuItem, false); await addTabAsync(tabGroup, firstMenuItem);
// Bind help icon click event
const helpMenuItem: MenuItem = menuList[menuList.length - 2];
bindHelpIconClickEvent(helpMenuItem);
// Bind logo click event // Bind logo click event
const logoMenuItem: MenuItem = menuList[menuList.length - 1]; const logoMenuItem: MenuItem = menuList[menuList.length - 1];
bindLogoClickEvent(tabGroup, logoMenuItem); bindLogoClickEvent(tabGroup, logoMenuItem);
// Bind exit event // Bind logout event
bindExitEvent(); bindLogoutEvent();
// Bind error modal event // Bind error modal event
bindErrorModalEvent(); bindErrorModalEvent();

View File

@ -5,48 +5,43 @@ import { contextBridge, ipcRenderer } from 'electron';
import { ApiResponse, MenuItem, TagResolutionConfig } from './EIAC_Desktop_Api'; import { ApiResponse, MenuItem, TagResolutionConfig } from './EIAC_Desktop_Api';
contextBridge.exposeInMainWorld('electronAPI', { contextBridge.exposeInMainWorld('electronAPI', {
/**
*
*/
closeApp: (): Promise<void> => ipcRenderer.invoke('app:close'),
/** /**
* *
* @param key * @param key
* @returns * @returns
*/ */
getCacheAsync: (key: string): Promise<any> => ipcRenderer.invoke('cache:get', key), getCacheAsync: (key: string) => ipcRenderer.invoke('cache:get', key) as Promise<any>,
/** /**
* *
* @param key * @param key
* @param value * @param value
*/ */
setCacheAsync: (key: string, value: any): Promise<void> => ipcRenderer.invoke('cache:set', key, value), setCacheAsync: (key: string, value: any) => ipcRenderer.invoke('cache:set', key, value) as Promise<void>,
/** /**
* *
* @returns * @returns
*/ */
getMenuCacheAsync: (): Promise<ApiResponse<MenuItem[]>> => ipcRenderer.invoke('get-menu-cache'), getMenuCacheAsync: () => ipcRenderer.invoke('get-menu-cache') as Promise<ApiResponse<MenuItem[]>>,
/** /**
* *
* @param code * @param code
* @returns * @returns
*/ */
getHelperDescripAsync: (code: string): Promise<string | null> => ipcRenderer.invoke('get-helper-descrip', code), getHelperDescripAsync: (code: string) => ipcRenderer.invoke('get-helper-descrip', code) as Promise<string | null>,
/** /**
* URL的缩放比例 * URL的缩放比例
* @param url URL * @param url URL
* @returns * @returns
*/ */
getZoomFactorByUrl: (url: string): Promise<number> => ipcRenderer.invoke('get-zoom-factor-by-url', url), getZoomFactorByUrl: (url: string) => ipcRenderer.invoke('get-zoom-factor-by-url', url) as Promise<number>,
/** /**
* URL * URL
* @param callback webContentId和urlwebContentId是请求打开URL的webview的id * @param callback webContentId和urlwebContentId是请求打开URL的webview的id
*/ */
onOpenTab: (callback: (webContentId: number, url: string) => void): void => { onOpenTab: (callback: (webContentId: number, url: string) => void) => {
ipcRenderer.on('webview-new-window', (_event, webContentId, url) => callback(webContentId, url)); ipcRenderer.on('webview-new-window', (_event, webContentId, url) => callback(webContentId, url));
}, },
@ -55,14 +50,14 @@ contextBridge.exposeInMainWorld('electronAPI', {
* @param url URL * @param url URL
* @returns * @returns
*/ */
checkUrlAvailable: (url: string): Promise<boolean> => ipcRenderer.invoke('check-url-available', url), checkUrlAvailable: (url: string) => ipcRenderer.invoke('check-url-available', url) as Promise<boolean>,
/** /**
* webview的cookie * webview的cookie
* @param url cookie的URL * @param url cookie的URL
* @param cookie cookie字符串 * @param cookie cookie字符串
*/ */
setWebviewCookie: (url: string, cookie: string): Promise<boolean> => ipcRenderer.invoke('set-webview-cookie', url, cookie), setWebviewCookie: (url: string, cookie: string) => ipcRenderer.invoke('set-webview-cookie', url, cookie) as Promise<boolean>,
/** /**
* *
@ -70,14 +65,14 @@ contextBridge.exposeInMainWorld('electronAPI', {
* @param message * @param message
* @param status * @param status
*/ */
faultReporting: (url: string, message: string, status: number): Promise<{ ok: boolean; status: number; message?: string }> => ipcRenderer.invoke('fault-reporting', url, message, status), faultReporting: (url: string, message: string, status: number) => ipcRenderer.invoke('fault-reporting', url, message, status) as Promise<{ ok: boolean; status: number; message?: string }>,
/** /**
* sessionStorage * sessionStorage
* @param key * @param key
* @param value * @param value
*/ */
setSessionStorage: (key: string, value: string): void => { setSessionStorage: (key: string, value: string) => {
window.sessionStorage.setItem(key, value); window.sessionStorage.setItem(key, value);
}, },
@ -86,7 +81,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
* @param key * @param key
* @returns * @returns
*/ */
getSessionStorage: (key: string): string | null => { getSessionStorage: (key: string) => {
return window.sessionStorage.getItem(key); return window.sessionStorage.getItem(key);
}, },
@ -94,14 +89,14 @@ contextBridge.exposeInMainWorld('electronAPI', {
* sessionStorage中删除指定键的值 * sessionStorage中删除指定键的值
* @param key * @param key
*/ */
removeSessionStorage: (key: string): void => { removeSessionStorage: (key: string) => {
window.sessionStorage.removeItem(key); window.sessionStorage.removeItem(key);
}, },
/** /**
* sessionStorage * sessionStorage
*/ */
clearSessionStorage: (): void => { clearSessionStorage: () => {
window.sessionStorage.clear(); window.sessionStorage.clear();
} }
}); });

View File

@ -1,10 +1,6 @@
import { ApiResponse, MenuItem, TagResolutionConfig } from '../EIAC_Desktop_Api'; import { ApiResponse, MenuItem, TagResolutionConfig } from '../EIAC_Desktop_Api';
export interface ElectronAPI { export interface ElectronAPI {
/**
*
*/
closeApp: () => Promise<void>;
/** /**
* *
* @param key * @param key

View File

@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "commonjs",
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,

View File

@ -20,18 +20,9 @@ export default defineConfig({
input: { input: {
login: 'login.html', login: 'login.html',
index: 'index.html' index: 'index.html'
},
output: {
// 确保 electron-tabs.js 被正确打包
manualChunks: {
'electron-tabs': ['electron-tabs']
}
} }
} }
}, },
optimizeDeps: {
include: ['electron-tabs']
},
// 使用 Vite 的 env 配置 // 使用 Vite 的 env 配置
envPrefix: ['EIAC_DESKTOP_API_HOST', 'NODE_ENV'], envPrefix: ['EIAC_DESKTOP_API_HOST', 'NODE_ENV'],
// 定义环境变量 // 定义环境变量