使用electron来创建属于自己的utools
electron介绍
Electron是跨平台的桌面应用程序框架。 Electron兼容Mac、Windows和Linux,可以构建出三个平台的应用程序。
官网:https://www.electronjs.org/zh
这是官网给出的一些使用该架构的产品 看看有没有你熟悉的呢?
样例展示(以下均为使用快捷键ctrl+k来隐藏和展示窗口)
设置快捷键ctrl+k呼出界面和隐藏界面 密码管理和信息查询需要登录方可使用
是密码管理界面,在这可以对本地新增加密的密码本只有登录上系统方可查看。
这是信息查询读取的excel内容,并加上模糊搜索
从热键呼出开始,进行一系列定制的快捷操作,无论是信息查询还是登记内容还是说各类的上报与跳转 都能在热键的快捷操作下完成,省去页面操作与打开文件操作需要开各种页面或是开各种文件夹的繁琐。
开始编写代码
环境的构建就不多做赘述了,安装nodejs npm 初始化环境 npm install electron
开始我们的代码演示
\\导入我们需要的模块
const { app, BrowserWindow, ipcMain, dialog ,Menu,globalShortcut,shell } = require('electron');
const mysql = require('mysql');
const xlsx = require('xlsx');
\\创建数据库连接
const pool = mysql.createPool({
connectionLimit: 10,
host: '127.0.0.1',
user: 'root',
password: 'Asdfg12345',
database: 'electronsql'
});
main.js剩下的内容我直接贴上带上注释
let isLoggedIn = false; // 标记是否已登录成功
\\创建窗口
function createWindow() {
let mainWindow = new BrowserWindow({
show: false,
width: 800,
height: 600,
title: '密码管理',
frame: true,
// autoHideMenuBar: true,
// icon: '18.ico',
\\处理预加载脚本中调用API
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
});
\\创建我们的菜单栏
let menuTemp = [
{
label: '密码管理',click(){
console.log('操作')
\\如果登录了就可以进行点击
if (isLoggedIn) {
console.log('操作');
\\点击密码管理后将窗口跳转到password页面
const focusedWindow = BrowserWindow.getFocusedWindow();
focusedWindow.loadFile('password.html');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
}
},
{
label: '信息查询',
submenu: [
{label:'交易区VIP查询',
click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
},
},
{type: 'separator' },
{label:'非交易区VIP查询',click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
},},
{type: 'separator' },
{label:'查询物理机上的虚拟机',click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
},},
{type: 'separator' },
{label:'服务器明细查询',click(){
console.log('操作')
if (isLoggedIn) {\\查询服务器信息点击后跳转到新页面
const focusedWindow = BrowserWindow.getFocusedWindow();
focusedWindow.loadFile('servermessage.html');
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
},},
{type: 'separator' },
{label:'万国服务器',click(){
console.log('操作')
if (isLoggedIn) {
const focusedWindow = BrowserWindow.getFocusedWindow();
focusedWindow.loadFile('wanguo.html');
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
},}
]
},
{
label: '上报',click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
},
submenu: [
{label:'发布上报',
click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
}
},
{type: 'separator' },
{label:'演练上报',
click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
}},
{type: 'separator' },
{label:'故障上报',
click(){
console.log('操作')
if (isLoggedIn) {
console.log('操作');
} else {
dialog.showMessageBox({
type: 'warning',
message: '请先登录'
});
}
}}
]
},
{
label: '跳转',click(){
console.log('操作')
},
submenu: [
{label:'交易区workflow跳转',
click(){
console.log('操作')
// if (isLoggedIn) {
console.log('操作');
const url = 'https://xxxxx; \\跳转到浏览器页面
shell.openExternal(url);
// } else {
// dialog.showMessageBox({
// type: 'warning',
// message: '请先登录'
// });
// }
}
},
{type: 'separator' },
{label:'非交易区工作证跳转',
click(){
console.log('操作')
// if (isLoggedIn) {
console.log('操作');
const url = 'https://xxx';
shell.openExternal(url);
// } else {
// dialog.showMessageBox({
// type: 'warning',
// message: '请先登录'
// });
// }
}},
{type: 'separator' },
{label:'交易区cmdb跳转',
click(){
console.log('操作')
// if (isLoggedIn) {
const url = 'https://xxxx';
shell.openExternal(url);
console.log('操作');
// } else {
// dialog.showMessageBox({
// type: 'warning',
// message: '请先登录'
// });
// }
}}
]
}
]
let menu = Menu.buildFromTemplate(menuTemp)
Menu.setApplicationMenu(menu)
mainWindow.loadFile('index.html');
\\热键注册 触发ctrl+k时就对界面隐藏和显示操作
globalShortcut.register('CommandOrControl+k', () => {
if (mainWindow.isVisible()) {
mainWindow.hide()
} else {
mainWindow.show()
}
})
// mainWindow.webContents.openDevTools()
mainWindow.on('ready-to-show',() => {
mainWindow.show()
})
mainWindow.webContents.on('did-finish-load',()=>{
})
mainWindow.webContents.on('dom-ready',()=>{
})
mainWindow.on('closed',()=>{
mainWindow = null
});
}
//初始化完成
app.on('ready', ()=>{
console.log('1111-ready')
createWindow()
\\监听点击登录按钮后的请求,在数据库查找数据 有该用户即为登录成功
ipcMain.on('managepwd', (ev,name,password) => {
console.log(name,password),
pool.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [name, password], (error, results, fields) => {
if (error) {
console.error('Error executing query:', error);
return;
}
if (results.length > 0) {
console.log('Account and password found:', results[0]);
isLoggedIn=true
ev.sender.send('managepwdResult', true);
} else {
console.log('Account and password not found');
ev.sender.send('managepwdResult', false);
}
});
});
ipcMain.on('openSettingWindow', (ev,data) => {
console.log(data)
ev.sender.send('getmsg','getmsg')
let indexMin = new BrowserWindow({
width: 200,
height: 200,
});
indexMin.loadFile('password.html');
indexMin.on('close', () => {
indexMin = null;
});
});
});
接下来是登录界面index.html与他的预加载脚本,很简单的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title></title>
</head>
<body>
<div id="background-container"></div>
<input type="text" id="name" placeholder="账号"><br>
<input type="text" id="password" placeholder="密码"><br>
<button id="btn">登录</button><br>
<script src="./renderer.js"></script>
</body>
<style>body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
input[type="text"], input[type="password"] {
width: 300px;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
#background-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('./dg.webp');
background-size: cover;
background-position: center;
z-index: -1;
}
</style>
</html>
const { ipcRenderer } = require('electron')
const name = document.getElementById('name');
const password = document.getElementById('password');
// name.addEventListener('keydown', (event) => {
// if (event.key === 'Enter') {
// const inputValue = textInput.value.trim();
// if (inputValue === '密码') {
// window.location.href = 'password.html';
// }
// }
// });
window.addEventListener('DOMContentLoaded',() => {
const oBtn = document.getElementById('btn');
oBtn.addEventListener('click', () => {
const nameValue = name.value.trim();
const passwordValue = password.value.trim();
console.log(nameValue,passwordValue)
ipcRenderer.send('managepwd',nameValue,passwordValue);
ipcRenderer.on('managepwdResult',(ev,data) => {
if (!data) {
console.log('无数据')}
})
});
})
下面再展示一下密码管理这块的内容
password.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Password Manager</title>
</head>
<body>
<h1>Password Manager</h1>
<button onclick="showInputFields()">新增密码</button>
<div id="inputFields" style="display: none;">
<input type="text" id="deviceNameInput" placeholder="设备名">
<input type="text" id="usernameInput" placeholder="账号">
<input type="password" id="passwordInput" placeholder="密码">
<button onclick="savePassword()">保存</button>
</div>
<table id="passwordTable" border="1">
<thead>
<tr>
<th>设备名</th>
<th>账号</th>
<th>密码</th>
</tr>
</thead>
<tbody></tbody>
</table>
<style>.password-hidden {
display: none;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
h1 {
text-align: center;
}
#inputFields {
margin-bottom: 20px;
}
#passwordTable {
width: 100%;
border-collapse: collapse;
}
#passwordTable th, #passwordTable td {
padding: 10px;
text-align: center;
}
#passwordTable th {
background-color: #f0f0f0;
}
#passwordTable tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
.password-masked {
display: inline;
}
.password-hidden {
display: none;
}
.show-password-btn {
cursor: pointer;
background-color: #007bff;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 5px;
}
.show-password-btn:hover {
background-color: #0056b3;
}
</style>
<!-- 引入通信脚本 -->
<script src="./test.js"></script>
</body>
</html>
然后是他的test.js脚本
const fs = require('fs');
const path = require('path');
const CryptoJS = require('crypto-js');
const passwordFilePath = path.join('D:', '18tools', 'password.json');\\密码本保存路径
const SECRET_KEY = 'xxxxxxxx';\\ 加密秘钥
function createPasswordFile() {\\创建密码本的函数
if (!fs.existsSync(passwordFilePath)) {
const passwordFolder = path.dirname(passwordFilePath);
if (!fs.existsSync(passwordFolder)) {
fs.mkdirSync(passwordFolder, { recursive: true });
}
fs.writeFileSync(passwordFilePath, '{}');
}
}
function readPasswordFile() {\\读取密码本的函数,并解密密码本中的加密字段
try {
const encryptedData = fs.readFileSync(passwordFilePath, 'utf8');
const decryptedPasswords = JSON.parse(encryptedData);
for (const deviceName in decryptedPasswords) {
const encryptedPassword = decryptedPasswords[deviceName].password;
const decryptedPassword = CryptoJS.AES.decrypt(encryptedPassword, SECRET_KEY).toString(CryptoJS.enc.Utf8);
decryptedPasswords[deviceName].password = decryptedPassword;
}
return decryptedPasswords;
} catch (err) {
console.error('Failed to read password file:', err);
return {};
}
}
function showInputFields() {
const inputFields = document.getElementById('inputFields');
inputFields.style.display = 'block';
}
#点击保存密码执行函数 将密码字段加密存进密码本
function savePassword() {
createPasswordFile();
const deviceName = document.getElementById('deviceNameInput').value.trim();
const username = document.getElementById('usernameInput').value.trim();
const password = document.getElementById('passwordInput').value.trim();
if (!deviceName || !username || !password) {
alert('请填写完整信息!');
return;
}
if (deviceName || username || password) {
try {
const passwords = readPasswordFile();
for (const existingDeviceName in passwords) {
const existingPassword = passwords[existingDeviceName].password;
const encryptedExistingPassword = CryptoJS.AES.encrypt(existingPassword, SECRET_KEY).toString();
passwords[existingDeviceName].password = encryptedExistingPassword;
}
const encryptedPassword = CryptoJS.AES.encrypt(password, SECRET_KEY).toString();
passwords[deviceName] = { username, password: encryptedPassword };
fs.writeFileSync(passwordFilePath, JSON.stringify(passwords, null, 2), 'utf8'); // 使用 UTF-8 编码
const inputFields = document.getElementById('inputFields');
inputFields.style.display = 'none';
document.getElementById('deviceNameInput').value = '';
document.getElementById('usernameInput').value = '';
document.getElementById('passwordInput').value = '';
loadPasswordTable();
return true;
} catch (err) {
console.error('Failed to save password:', err);
return false;
};
}
}
\\加载页面表格
function loadPasswordTable() {
const passwords = readPasswordFile();
const tbody = document.querySelector('#passwordTable tbody');
tbody.innerHTML = '';
for (const deviceName in passwords) {
const tr = document.createElement('tr');
const password = passwords[deviceName].password;
const maskedPassword = '*'.repeat(password.length); // 将密码用 * 替代
tr.innerHTML = `
<td>${deviceName}</td>
<td>${passwords[deviceName].username}</td>
<td>
<span class="password-masked" >${maskedPassword}</span>
<button class="show-password-btn" onclick="togglePasswordVisibility(this)">显示密码</button>
<span class="password-hidden" style.display = 'none' >${password}</span>
</td>
`;
tbody.appendChild(tr);
}
}
#表格中的显示隐藏密码功能
function togglePasswordVisibility(btn) {
const passwordCell = btn.parentNode;
const maskedPassword = passwordCell.querySelector('.password-masked');
const hiddenPassword = passwordCell.querySelector('.password-hidden');
if (maskedPassword.style.display !== 'none') {
maskedPassword.style.display = 'none';
hiddenPassword.style.display = 'inline';
btn.textContent = '隐藏密码';
} else {
maskedPassword.style.display = 'inline';
hiddenPassword.style.display = 'none';
btn.textContent = '显示密码';
}
}
window.addEventListener('DOMContentLoaded', () => {
loadPasswordTable();
});
本次主要是为了分享一下electron的实用性,在添加了热键之后能将很大一部分需要多次点击才能实现的内容操作直接通过快捷键的方式来完成,且实现的代码也并不复杂,可以说是是实用性很强的一次实践。
剩下的表格展示代码我就不演示了需要完整代码内容的可以联系。