简介
这是一个开源软件包提醒工具,可以查询有哪些软件包有更新版本。
自己平时会关注Maven、NPM和GitHub上的一些开源软件包,在收藏夹里收藏了一堆,不定时的去查看,感觉有些麻烦,所以做了这个软件。
通过软件每天定时更新,就会知道是否有更新,有的话就点击版本链接去看看有啥更新。“懒人”改变世界乎。
技术栈
vue 2.6
electron 8.2
element-ui 2.13
axios 0.19
cheerio 1.0
sqlite3 4.2
vuex 3.3
cron 1.8
开发
视图层
引入依赖
# Element插件
vue add element
-
考虑软件功能简单,所以使用按需导入
-
选择语言
- 运行看下效果
yarn electron:serve
绘制页面
-
引入需要的element组件
element.js
-
绘制列表
Package.vue
-
绘制新增
AddPackage.vue
数据层
爬取远程数据
- 引入依赖
# http请求
yarn add axios
# 解析html
yarn add cheerio
- 设置跨域访问
background.js
function createWindow () {
win = new BrowserWindow({
...
webPreferences: {
webSecurity: false,
...
}
})
...
}
- 爬取NPM包信息
const getNpmPackage = (baseUrl, packageName) => {
return new Promise((resolve, reject) => {
http.get(`${baseUrl}/package/${packageName}`).then(res => {
const $ = cheerio.load(res.data)
const packageInfo = $('#top').children().last().children('div')
const latestVersion = packageInfo.find('h3:contains(Version)').siblings().text()
const publishTime = packageInfo.find('h3:contains(Last publish)').siblings().children().first().attr('datetime')
if (latestVersion) {
resolve({ latestVersion, publishTime: util.dateFormat(publishTime) })
} else {
resolve({})
}
}).catch(err => {
reject(err)
})
})
}
本地数据库
- 引入依赖
# 存储一些全局状态
yarn add vuex
yarn add sqlite3
- 程序启动时初始化数据库
App.vue
async beforeCreate() {
await db.init().catch(() => {})
...
// 更新初始化完成状态
this.setAppInitFinished(true)
}
db/index.js
async init() {
...
// 同步执行初始化SQL
await this.run(`create table t_package
(
category_code text default '' not null,
group_name text default '' not null,
name text default '' not null,
current_version text,
latest_version text,
publish_time text,
create_time text,
constraint t_package_pk
primary key (category_code, group_name, name)
);`).catch(() => {})
...
}
Package.vue
watch: {
// 初始化完成查询数据
getAppInitFinished(val) {
if (val) {
this.searchCategory()
}
}
...
}
- 新增数据
dao/package.js
add ({ categoryCode, name, groupName, currentVersion, latestVersion, publishTime }) {
return new Promise((resolve, reject) => {
const sql = `insert into t_package
(category_code, name, group_name, current_version, latest_version, publish_time, create_time)
values
(?, ?, ?, ?, ?, ?, ?)`
db.run(sql, [categoryCode, name, groupName || '', currentVersion, latestVersion, publishTime || '', util.dateFormat(new Date())]).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
- 查询数据
dao/package.js
getListByCategoryCode(categoryCode) {
return new Promise((resolve, reject) => {
const sql = `select category_code categoryCode, name, group_name groupName,
current_version currentVersion,
latest_version latestVersion,
publish_time publishTime
from t_package
where category_code = ?
order by name`
db.all(sql, categoryCode).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
导入导出
导入
- 主进程
background.js
// 弹窗选择文件返回给渲染进程
ipcMain.on(consts.IMPORT_CHANNEL, (ipcMainEvent) => {
dialog.showOpenDialog(win, {
title: '选择CSV文件',
filters: [
{ name: 'Custom File Type', extensions: ['csv'] }
],
properties: ['openFile']
}).then(res => {
ipcMainEvent.sender.send(consts.IMPORT_SELECTED_CHANNEL, res)
}).catch(() => {})
})
- 渲染进程
Backup.vue
// 读取文件存入数据库
async importData(file) {
const data = fs.readFileSync(file)
...
for (const [index, item] of packages.entries()) {
...
try {
this.setPercentage(Math.floor((index + 1) / packages.length * 100))
await packageDao.add(item)
successCount += 1
} catch (e) {
errs.push(index + 1)
console.log(e)
}
}
...
// 刷新页面
this.setRefreshPackageFlag(true)
}
导出
- 主进程
ipcMain.on(consts.EXPORT_CHANNEL, (ipcMainEvent, path) => {
// 监听下载进度
win.webContents.session.once('will-download', (event, item) => {
item.on('updated', (event, state) => {
if (state === 'progressing') {
ipcMainEvent.sender.send(consts.EXPORT_PROGRESS_CHANNEL, Math.floor(item.getReceivedBytes() / item.getTotalBytes() * 100))
}
})
item.once('done', (event, state) => {
ipcMainEvent.sender.send(consts.EXPORT_STATE_CHANNEL, state)
})
})
// 下载文件
ipcMainEvent.sender.downloadURL('file://' + path)
})
- 渲染进程
async exportData() {
// 查询数据
const packageList = await packageDao.getAll()
...
// 写入临时文件
fs.writeFileSync(filePath, Buffer.from(data.join('\n')))
// 文件传递给主进程
this.$electron.ipcRenderer.send(consts.EXPORT_CHANNEL, filePath)
// 获取下载进度
this.$electron.ipcRenderer.on(consts.EXPORT_PROGRESS_CHANNEL, (event, percentage) => {
if (percentage) {
this.setPercentage(percentage)
}
})
this.$electron.ipcRenderer.once(consts.EXPORT_STATE_CHANNEL, (event, state) => {
if (state === consts.ELECTRON_DOWNLOAD_STATE_COMPLETED) {
// eslint-disable-next-line no-new
new Notification('', {
body: '导出数据完成'
})
}
// 删除临时文件
fs.unlink(filePath, function () {})
this.setPercentage(0)
})
}
任务调度,每日定时查询
- 添加依赖
yarn add cron
yarn add moment
yarn add moment-timezone -D
- 程序启动时,创建任务
async beforeCreate() {
...
await settingDao.getOne(consts.DB_SETTING_KEY_REMIND_PERIOD).then(res => {
if (res && res.value) {
// 创建任务
const vm = this
const job = new CronJob(util.getRemindCron(res.value), function() {
vm.$refs.main.check()
}, null, false)
this.setJob(job)
}
console.log('create-job')
}).catch(err => {
console.log(err)
})
await settingDao.getOne(consts.DB_SETTING_KEY_REMIND_ENABLED).then(res => {
// 启用提醒
if (res && res.value && res.value === '1') {
this.getJob.start()
}
console.log('start-job')
}).catch(err => {
console.log(err)
})
this.setAppInitFinished(true)
},
发布
制作图标
- 制作一张Logo图片,
1024*1024
底色透明
png格式
,命名为icon.png
放置在public
文件夹下 - 使用electron-icon-maker制作mac图标
icns
和windows格式图标ico
yarn add electron-icon-maker -D
./node_modules/.bin/electron-icon-maker --input=./public/icon.png --output=./public
安装包配置
vue.config.js
module.exports = {
pluginOptions: {
electronBuilder: {
builderOptions: {
appId: 'com.electron.demo',
// 程序名
productName: 'ElectronDemo',
// 安装包名
artifactName: '${name}-${version}.${ext}',
win: {
icon: './public/icon.ico',
target: 'nsis'
},
nsis: {
// 是否一键安装
oneClick: false,
// 允许权限提升
allowElevation: true,
// 每个用户安装
perMachine: true,
// 允许用户更改安装目录
allowToChangeInstallationDirectory: true,
// 创建桌面快捷方式
createDesktopShortcut: true,
// 创建开始菜单快捷方式
createStartMenuShortcut: true
},
mac: {
icon: './public/icon.icns',
target: 'dmg'
}
}
}
}
}
- 构建
yarn electron:build --win --mac
问题
element-ui按需引入Message组件,弹出空消息问题
-
Vue.use(Message)
改为Vue.component(Message.name, Message)
moment-timezone Cannot read property 'split' of undefined
- moment-timezone issues:838
- 修改moment版本
2.24.0
,增加resolutions
,重新安装yarn install
"dependencies": {
...
"moment": "2.24.0",
...
},
"resolutions": {
"moment": "2.24.0"
},
安装electron-icon-maker包卡住
- 使用npm安装,增加
-d
参数查看详细下载信息,查看卡在下载哪个包上
npm install electron-icon-maker -D -d
- 我的是卡在phantomjs的下载上了
点击下载链接https://github.com/Medium/phantomjs/releases/download/v2.1.1/phantomjs-2.1.1-macosx.zip下载,将下载后的文件放置在/private/var/folders/cm/t2dctpkn0h74c53l72hsd1xh0000gn/T/phantomjs
,重新执行安装yarn add electron-icon-maker -D
😢Maybe下一版本
- 远程包查询功能
- 开机自启动
- 自动更新
- 优化UI