276 lines
6.7 KiB
JavaScript
276 lines
6.7 KiB
JavaScript
import path from 'node:path'
|
|
import os from 'node:os'
|
|
import { fileURLToPath } from 'node:url'
|
|
import {
|
|
app,
|
|
BrowserWindow,
|
|
ipcMain,
|
|
Menu,
|
|
Tray,
|
|
nativeImage,
|
|
shell
|
|
} from 'electron'
|
|
|
|
import { createConfigStore } from './config/storage.js'
|
|
import {
|
|
loadDriverResources,
|
|
resolveDriverDownloadUrl
|
|
} from './services/driverManifest.js'
|
|
import { createPrintAgentClient } from './services/apiClient.js'
|
|
import { createJobProcessor } from './services/jobProcessor.js'
|
|
import { createAgentRuntime } from './services/agentRuntime.js'
|
|
import {
|
|
buildAssetLabelPrintHtml,
|
|
buildTestLabelPrintHtml
|
|
} from './services/labelDocument.js'
|
|
import { printHtmlSilently } from './services/electronPrinter.js'
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = path.dirname(__filename)
|
|
const agentRoot = path.resolve(__dirname, '..')
|
|
|
|
let mainWindow = null
|
|
let tray = null
|
|
let runtime = null
|
|
let store = null
|
|
let isQuitting = false
|
|
let shouldShowMainWindow = false
|
|
|
|
function createTrayIcon() {
|
|
return nativeImage.createFromDataURL(
|
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAK0lEQVR4AWP4//8/AyUYTFhYWPjPwMBgkJGR4T8GBgYmhjEwMDAwQAEA8A4Qbw6X8UIAAAAASUVORK5CYII='
|
|
)
|
|
}
|
|
|
|
function sendStateToRenderer(state) {
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('agent:state', state)
|
|
}
|
|
}
|
|
|
|
async function getPrinters() {
|
|
if (!mainWindow || mainWindow.isDestroyed()) {
|
|
return []
|
|
}
|
|
|
|
try {
|
|
const printers = await mainWindow.webContents.getPrintersAsync()
|
|
return printers.map((printer) => ({
|
|
name: printer.name,
|
|
description: printer.description || '',
|
|
status: printer.status ?? 0,
|
|
isDefault: Boolean(printer.isDefault)
|
|
}))
|
|
} catch (error) {
|
|
return [
|
|
{
|
|
name: '',
|
|
description: `打印机列表读取失败:${error.message}`,
|
|
status: -1,
|
|
isDefault: false
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1080,
|
|
height: 760,
|
|
minWidth: 960,
|
|
minHeight: 680,
|
|
show: false,
|
|
autoHideMenuBar: true,
|
|
backgroundColor: '#0b0d12',
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
contextIsolation: true,
|
|
sandbox: false
|
|
}
|
|
})
|
|
|
|
mainWindow.loadFile(path.join(__dirname, 'index.html'))
|
|
|
|
mainWindow.on('ready-to-show', () => {
|
|
if (shouldShowMainWindow) {
|
|
mainWindow.show()
|
|
}
|
|
})
|
|
|
|
mainWindow.on('close', (event) => {
|
|
if (!isQuitting) {
|
|
event.preventDefault()
|
|
mainWindow.hide()
|
|
}
|
|
})
|
|
}
|
|
|
|
function createTray() {
|
|
tray = new Tray(createTrayIcon())
|
|
tray.setToolTip('固定资产打印助手')
|
|
tray.setContextMenu(
|
|
Menu.buildFromTemplate([
|
|
{
|
|
label: '打开打印助手',
|
|
click: () => mainWindow?.show()
|
|
},
|
|
{
|
|
label: '退出',
|
|
click: () => {
|
|
isQuitting = true
|
|
runtime?.stop()
|
|
app.quit()
|
|
}
|
|
}
|
|
])
|
|
)
|
|
tray.on('double-click', () => mainWindow?.show())
|
|
}
|
|
|
|
async function getDriverResourcesForUi(config) {
|
|
const resources = await loadDriverResources(agentRoot)
|
|
return {
|
|
...resources,
|
|
companyArchive: {
|
|
...resources.companyArchive,
|
|
downloadUrl: resolveDriverDownloadUrl(
|
|
config?.serverUrl || 'https://www.jsgdzc.cn',
|
|
resources.companyArchive
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function registerIpcHandlers() {
|
|
ipcMain.handle('agent:bootstrap', async () => {
|
|
const config = await runtime.loadConfig()
|
|
const printers = await runtime.refreshPrinters()
|
|
const driverResources = await getDriverResourcesForUi(config)
|
|
return {
|
|
config,
|
|
printers,
|
|
driverResources,
|
|
runtimeState: runtime.getState()
|
|
}
|
|
})
|
|
|
|
ipcMain.handle('agent:save-config', async (_event, patch) => {
|
|
const config = await runtime.updateConfig(patch)
|
|
const driverResources = await getDriverResourcesForUi(config)
|
|
return { config, driverResources }
|
|
})
|
|
|
|
ipcMain.handle('agent:list-printers', async () => runtime.refreshPrinters())
|
|
ipcMain.handle('agent:heartbeat-now', async () => runtime.heartbeatOnce())
|
|
ipcMain.handle('agent:claim-now', async () => runtime.pollOnce())
|
|
ipcMain.handle('agent:test-print', async () => runtime.runTestPrint())
|
|
ipcMain.handle('agent:open-external', async (_event, url) => shell.openExternal(url))
|
|
|
|
ipcMain.handle('agent:pair', async (_event, payload) => {
|
|
const currentConfig = await runtime.loadConfig()
|
|
const client = createPrintAgentClient({
|
|
serverUrl: payload.serverUrl || currentConfig.serverUrl
|
|
})
|
|
|
|
const result = await client.pair({
|
|
pairCode: payload.pairCode,
|
|
terminalName: payload.terminalName,
|
|
machineName: payload.machineName || os.hostname(),
|
|
platform: payload.platform || process.platform
|
|
})
|
|
|
|
const config = await runtime.updateConfig({
|
|
serverUrl: payload.serverUrl || currentConfig.serverUrl,
|
|
terminalName: payload.terminalName || currentConfig.terminalName,
|
|
pairingCode: '',
|
|
pairResult: result
|
|
})
|
|
|
|
return {
|
|
config,
|
|
pairResult: result
|
|
}
|
|
})
|
|
}
|
|
|
|
async function createRuntime() {
|
|
store = createConfigStore(path.join(app.getPath('userData'), 'data'))
|
|
const jobProcessor = createJobProcessor({
|
|
async printAssetLabels({ items, printerName }) {
|
|
const html = await buildAssetLabelPrintHtml({
|
|
items
|
|
})
|
|
|
|
await printHtmlSilently({
|
|
html,
|
|
printerName,
|
|
jobName: '资产标签打印'
|
|
})
|
|
|
|
return {
|
|
printed_count: items.length
|
|
}
|
|
},
|
|
async printTestLabel({ printerName }) {
|
|
const html = await buildTestLabelPrintHtml({
|
|
printerName
|
|
})
|
|
|
|
await printHtmlSilently({
|
|
html,
|
|
printerName,
|
|
jobName: '测试标签打印'
|
|
})
|
|
}
|
|
})
|
|
|
|
runtime = createAgentRuntime({
|
|
store,
|
|
clientFactory: (options) => createPrintAgentClient(options),
|
|
getPrinters,
|
|
jobProcessor,
|
|
appVersion: app.getVersion(),
|
|
onStateChange: sendStateToRenderer
|
|
})
|
|
|
|
await runtime.start()
|
|
|
|
const config = runtime.getState().config
|
|
shouldShowMainWindow = !config?.deviceToken
|
|
if (shouldShowMainWindow) {
|
|
mainWindow?.show()
|
|
} else {
|
|
mainWindow?.hide()
|
|
}
|
|
}
|
|
|
|
app.setAppUserModelId('com.jsgdzc.printagent')
|
|
if (process.platform === 'win32') {
|
|
app.setLoginItemSettings({
|
|
openAtLogin: true,
|
|
openAsHidden: true
|
|
})
|
|
}
|
|
app.on('before-quit', () => {
|
|
isQuitting = true
|
|
})
|
|
|
|
app.whenReady().then(async () => {
|
|
createWindow()
|
|
createTray()
|
|
registerIpcHandlers()
|
|
await createRuntime()
|
|
})
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
createWindow()
|
|
} else {
|
|
mainWindow?.show()
|
|
}
|
|
})
|
|
|
|
app.on('window-all-closed', (event) => {
|
|
event.preventDefault()
|
|
})
|