调整界面

调整脚本
增加功能:备份、恢复、清空
This commit is contained in:
2026-04-03 14:05:18 +08:00
parent 4d0ce274e0
commit 51d607d970
15 changed files with 1494 additions and 679 deletions

View File

@@ -1,13 +1,16 @@
import {activateRecordService} from "../service/database/activateRecord";
import path from 'path';
import { app as electronApp, dialog } from 'electron';
import { activateRecordService } from "../service/database/activateRecord";
class ActivateRecordController {
async list(args: { macAddress: string, modules: string[] }): Promise<any> {
const {macAddress, modules} = args
return activateRecordService.list(macAddress, modules)
const { macAddress, modules } = args;
return activateRecordService.list(macAddress, modules);
}
async save(args: {
applicant: string,
macAddress: string,
applicationCode: string,
modules: string[],
@@ -15,8 +18,76 @@ class ActivateRecordController {
createTime: string,
remark: string
}): Promise<any> {
const { modules} = args
return activateRecordService.save({...args, module : modules.join(',')})
const { modules } = args;
return activateRecordService.save({ ...args, module: modules.join(',') });
}
async removeById(args: { id: number }): Promise<any> {
const { id } = args;
return activateRecordService.removeById(id);
}
async clear(): Promise<any> {
return activateRecordService.clear();
}
async backup(): Promise<any> {
const defaultFileName = `activate-record-backup-${this.getTimestamp()}.json`;
const filePath = dialog.showSaveDialogSync({
title: '导出备份',
defaultPath: path.join(electronApp.getPath('documents'), defaultFileName),
filters: [
{ name: 'JSON', extensions: ['json'] }
]
});
if (!filePath) {
return {
canceled: true
};
}
const count = await activateRecordService.backup(filePath);
return {
canceled: false,
count,
filePath
};
}
async importBackup(): Promise<any> {
const filePaths = dialog.showOpenDialogSync({
title: '导入备份',
properties: ['openFile'],
filters: [
{ name: 'JSON', extensions: ['json'] }
]
});
if (!filePaths || filePaths.length === 0) {
return {
canceled: true
};
}
const filePath = filePaths[0];
const count = await activateRecordService.importBackup(filePath);
return {
canceled: false,
count,
filePath
};
}
private getTimestamp(): string {
const now = new Date();
const year = now.getFullYear();
const month = `${now.getMonth() + 1}`.padStart(2, '0');
const day = `${now.getDate()}`.padStart(2, '0');
const hour = `${now.getHours()}`.padStart(2, '0');
const minute = `${now.getMinutes()}`.padStart(2, '0');
const second = `${now.getSeconds()}`.padStart(2, '0');
return `${year}${month}${day}-${hour}${minute}${second}`;
}
}

View File

@@ -1,6 +1,30 @@
import fs from 'fs';
import path from 'path';
import { ElectronEgg } from 'ee-core';
import { Lifecycle } from './preload/lifecycle';
import { preload } from './preload';
import { app as electronApp } from 'electron';
function writeRuntimeLog(message: string): void {
try {
const baseDir = electronApp.isPackaged ? path.dirname(process.execPath) : process.cwd();
const logFile = path.join(baseDir, 'runtime.log');
const line = `[${new Date().toISOString()}] ${message}\n`;
fs.appendFileSync(logFile, line, 'utf8');
} catch (error) {
console.error('[runtime-log] write failed:', error);
}
}
process.on('uncaughtException', (error) => {
writeRuntimeLog(`uncaughtException: ${error?.stack || error}`);
});
process.on('unhandledRejection', (reason) => {
writeRuntimeLog(`unhandledRejection: ${String(reason)}`);
});
writeRuntimeLog('app bootstrap start');
// New app
const app = new ElectronEgg();
@@ -16,4 +40,5 @@ app.register("before-close", life.beforeClose);
app.register("preload", preload);
// Run
app.run();
app.run();
writeRuntimeLog('app bootstrap end');

View File

@@ -2,6 +2,19 @@ import { app as electronApp, screen } from 'electron';
import { logger } from 'ee-core/log';
import { getConfig } from 'ee-core/config';
import { getMainWindow } from 'ee-core/electron';
import fs from 'fs';
import path from 'path';
function writeRuntimeLog(message: string): void {
try {
const baseDir = electronApp.isPackaged ? path.dirname(process.execPath) : process.cwd();
const logFile = path.join(baseDir, 'runtime.log');
const line = `[${new Date().toISOString()}] ${message}\n`;
fs.appendFileSync(logFile, line, 'utf8');
} catch (error) {
console.error('[runtime-log] write failed:', error);
}
}
class Lifecycle {
/**
@@ -36,6 +49,16 @@ class Lifecycle {
const win = getMainWindow();
win.webContents.on('did-finish-load', () => {
logger.info('[window] did-finish-load');
writeRuntimeLog('window did-finish-load');
});
win.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => {
logger.error('[window] did-fail-load errorCode:', errorCode, 'errorDescription:', errorDescription, 'url:', validatedURL);
writeRuntimeLog(`window did-fail-load errorCode=${errorCode} errorDescription=${errorDescription} url=${validatedURL}`);
});
// The window is centered and scaled proportionally
// Obtain the size information of the main screen, calculate the width and height of the window as a percentage of the screen,
// and calculate the coordinates of the upper left corner when the window is centered
@@ -67,4 +90,4 @@ class Lifecycle {
}
Lifecycle.toString = () => '[class Lifecycle]';
export { Lifecycle };
export { Lifecycle };

View File

@@ -1,8 +1,18 @@
import {BasedbService} from './basedb';
import fs from 'fs';
import { BasedbService } from './basedb';
interface ActivateRecordItem {
applicant: string;
macAddress: string;
applicationCode: string;
module: string;
activationCode: string;
createTime: string;
remark: string;
}
/**
* sqlite数据存储
* @class
* sqlite data storage
*/
class ActivateRecordService extends BasedbService {
tableName: string;
@@ -10,75 +20,89 @@ class ActivateRecordService extends BasedbService {
constructor() {
const options = {
dbname: 'pqs9100-tool.db',
}
};
super(options);
this.tableName = 'activate_record';
}
/*
* 初始化表
/**
* Initialize table and perform lightweight schema migration.
*/
init(): void {
this._init();
// 检查表是否存在
const masterStmt = this.db.prepare('SELECT * FROM sqlite_master WHERE type=? AND name = ?');
let tableExists = masterStmt.get('table', this.tableName);
const tableExists = masterStmt.get('table', this.tableName);
if (!tableExists) {
// 创建表
const create_table_sql =
const createTableSql =
`CREATE TABLE ${this.tableName}
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
applicant CHAR(100) NULL,
macAddress CHAR(50) NOT NULL,
applicationCode CHAR(2000) NOT NULL,
module CHAR(200) NOT NULL,
activationCode CHAR(2000) NOT NULL,
createTime CHAR(32) NOT NULL,
remark CHAR(120) NULL
);`
this.db.exec(create_table_sql);
remark CHAR(120) NULL
);`;
this.db.exec(createTableSql);
return;
}
const columns = this.db.prepare(`PRAGMA table_info(${this.tableName})`).all() as Array<{ name: string }>;
const hasApplicant = columns.some((column) => column.name === 'applicant');
if (!hasApplicant) {
this.db.exec(`ALTER TABLE ${this.tableName} ADD COLUMN applicant CHAR(100) NULL`);
}
}
/*
* 增 data (sqlite)
/**
* Insert one record.
*/
async save(data: {
macAddress: string;
applicationCode: string;
module: string;
activationCode: string;
createTime: string;
remark: string;
}) {
const insert = this.db.prepare(`INSERT INTO ${this.tableName} (macAddress, applicationCode, module, activationCode, createTime, remark)
VALUES (@macAddress, @applicationCode, @module, @activationCode, @createTime, @remark)`);
insert.run(data);
async save(data: ActivateRecordItem) {
const insert = this.db.prepare(
`INSERT INTO ${this.tableName} (applicant, macAddress, applicationCode, module, activationCode, createTime, remark)
VALUES (@applicant, @macAddress, @applicationCode, @module, @activationCode, @createTime, @remark)`
);
insert.run({
...data,
applicant: data.applicant || '',
remark: data.remark || ''
});
return true;
}
/*
* 删 data
/**
* Delete one record by id.
*/
async removeById(name: string = ''): Promise<boolean> {
async removeById(id: number): Promise<boolean> {
const remove = this.db.prepare(`DELETE
FROM ${this.tableName}
WHERE id = ?`);
remove.run(name);
remove.run(id);
return true;
}
/*
* 查list data (sqlite)
/**
* Clear all records.
*/
async clear(): Promise<boolean> {
const clearStmt = this.db.prepare(`DELETE FROM ${this.tableName}`);
clearStmt.run();
return true;
}
/**
* Query record list.
*/
async list(macAddress: string = '', modules: string[] = []): Promise<any[]> {
let condition = ''
let condition = '';
if (macAddress) {
condition += ` AND macAddress = '${macAddress}'`
condition += ` AND macAddress = '${macAddress}'`;
}
if (modules.length > 0) {
const moduleConditions = modules.map(module => `module LIKE '%${module}%'`).join(' OR ');
const moduleConditions = modules.map((module) => `module LIKE '%${module}%'`).join(' OR ');
condition += ` AND (${moduleConditions})`;
}
const select = this.db.prepare(`SELECT *
@@ -88,35 +112,88 @@ class ActivateRecordService extends BasedbService {
return select.all();
}
/*
* all Test data (sqlite)
/**
* Export all records to a JSON backup file.
*/
async backup(filePath: string): Promise<number> {
const records = await this.getAllTestDataSqlite();
const payload = {
version: 1,
tableName: this.tableName,
exportedAt: new Date().toISOString(),
records
};
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf8');
return records.length;
}
/**
* Import backup and replace current records.
*/
async importBackup(filePath: string): Promise<number> {
const raw = fs.readFileSync(filePath, 'utf8');
const parsed = JSON.parse(raw);
const inputRecords = Array.isArray(parsed) ? parsed : parsed.records;
if (!Array.isArray(inputRecords)) {
throw new Error('Invalid backup file');
}
const records = inputRecords.map((item: any) => this.normalizeRecord(item));
const clearStmt = this.db.prepare(`DELETE FROM ${this.tableName}`);
const insertStmt = this.db.prepare(
`INSERT INTO ${this.tableName} (applicant, macAddress, applicationCode, module, activationCode, createTime, remark)
VALUES (@applicant, @macAddress, @applicationCode, @module, @activationCode, @createTime, @remark)`
);
const transaction = this.db.transaction((rows: ActivateRecordItem[]) => {
clearStmt.run();
for (const row of rows) {
insertStmt.run(row);
}
});
transaction(records);
return records.length;
}
/**
* Read all records.
*/
async getAllTestDataSqlite(): Promise<any[]> {
const selectAllUser = this.db.prepare(`SELECT *
FROM ${this.tableName} `);
const allUser = selectAllUser.all();
return allUser;
FROM ${this.tableName}
ORDER BY id DESC`);
return selectAllUser.all();
}
/*
* get data dir (sqlite)
/**
* Get data directory.
*/
async getDataDir(): Promise<string> {
const dir = this.storage.getDbDir();
return dir;
return this.storage.getDbDir();
}
/*
* set custom data dir (sqlite)
/**
* Set custom data directory.
*/
async setCustomDataDir(dir: string): Promise<void> {
if (dir.length == 0) {
if (dir.length === 0) {
return;
}
this.changeDataDir(dir);
this.init();
return;
}
private normalizeRecord(item: any): ActivateRecordItem {
return {
applicant: typeof item?.applicant === 'string' ? item.applicant : '',
macAddress: typeof item?.macAddress === 'string' ? item.macAddress : '',
applicationCode: typeof item?.applicationCode === 'string' ? item.applicationCode : '',
module: typeof item?.module === 'string' ? item.module : Array.isArray(item?.modules) ? item.modules.join(',') : '',
activationCode: typeof item?.activationCode === 'string' ? item.activationCode : '',
createTime: typeof item?.createTime === 'string' ? item.createTime : '',
remark: typeof item?.remark === 'string' ? item.remark : '',
};
}
}