feat(): 添加更新功能

This commit is contained in:
tsl 2026-03-25 10:06:25 +08:00
parent 92055dd6a0
commit 1b6e12e402
23 changed files with 1045 additions and 285 deletions

1
.tauri/signing-key Normal file
View File

@ -0,0 +1 @@
dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5eVJXUG1sdWpVNnBJUHpXSjFZWnczeVpPVjNZMEVvOGFkQlB5UFZDSFNEc0FBQkFBQUFBQUFBQUFBQUlBQUFBQVVmWE1BVXlmNXNqRFRmSEhSU3ltY3hqT1h4aE1hRVNEenJ2RUhkZ0JZaFJWc1owb1hpY3UwaEdqeTNQelRYaVd4M3E4WWR2MTloYWJSZFVTQm5scUhaTjRKOXlqeVlPRVpybWZTY2p2cUQzNkpDZGZGRzBtTEgvdFVBajNzdnBPTXVROFZXQXFUTGc9Cg==

1
.tauri/signing-key.pub Normal file
View File

@ -0,0 +1 @@
dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDY0Q0YxMzNBM0Q5OEU4NEMKUldSTTZKZzlPaFBQWkVXRVdBVHdTdlNvVnZSL2FFQkZtbFpRTzdjNGxHbjJsZ1R5MFRLdkpmYzMK

View File

@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 常用命令 ## 常用命令
项目使用 **npm** 作为包管理器(存在 package-lock.json 项目使用 **pnpm** 作为包管理器(存在 package-lock.json
```bash ```bash
# 启动开发模式(前端 + Tauri # 启动开发模式(前端 + Tauri
@ -18,7 +18,7 @@ pnpm run tauri dev
pnpm run dev pnpm run dev
# 构建生产版本 # 构建生产版本
npm run tauri build pnpm run tauri build
# 前端类型检查 + 构建 # 前端类型检查 + 构建
pnpm run build pnpm run build
@ -60,7 +60,7 @@ cd src-tauri && cargo clippy
- `src-tauri/capabilities/default.json` - Tauri 权限配置HTTP 白名单、插件权限) - `src-tauri/capabilities/default.json` - Tauri 权限配置HTTP 白名单、插件权限)
- `src-tauri/tauri.conf.json` - 应用配置窗口、图标、bundle - `src-tauri/tauri.conf.json` - 应用配置窗口、图标、bundle
- 用户配置文件路径:`%APPDATA%/tauri-app/config.json`Windows - 用户配置文件路径:`%APPDATA%/work-order-monitor/config.json`Windows
## 依赖的 Tauri 插件 ## 依赖的 Tauri 插件

View File

@ -1,137 +1,127 @@
# 系统监测应用程序 # 工单系统监测
这是一个基于 Tauri + Vue 开发的系统监测应用程序,具有以下功能: 基于 **Tauri 2.x + Vue 3** 的工单监测桌面应用,用于监测 CRM 系统的待处理工单并发送系统通知。
## 功能特性 ## 环境要求
1. **用户设置界面** - 配置用户名和密码 - Node.js >= 18
2. **系统托盘集成** - 应用启动后自动最小化到系统托盘 - Rust (stable)
3. **自动登录** - 保存用户凭据并支持自动登录 - Visual Studio Build ToolsWindows含 C++ 工具链)
4. **定时API调用** - 登录后每1分钟调用一次接口
5. **系统通知** - 根据API返回值弹出系统消息 ## 开发
6. **模块化架构** - 代码按功能模块组织,便于维护
```bash
# 安装依赖
pnpm install
# 启动开发模式(前端 + Tauri
pnpm tauri dev
# 仅启动前端
pnpm dev
```
## 构建
签名密钥已包含在项目中(`.tauri/` 目录),构建脚本会自动加载,直接运行:
```bash
pnpm run tauri-build
```
构建产物位于 `src-tauri/target/release/bundle/nsis/`
## 版本号管理
使用自定义脚本统一更新 `package.json`、`tauri.conf.json`、`Cargo.toml` 三个文件的版本号:
```bash
node scripts/bump-version.mjs # 默认 patch: 0.2.0 → 0.2.1
node scripts/bump-version.mjs minor # minor: 0.2.0 → 0.3.0
node scripts/bump-version.mjs major # major: 0.2.0 → 1.0.0
node scripts/bump-version.mjs 1.2.3 # 指定版本
```
## 发布
构建完成后,运行发布脚本将安装包和更新元数据上传到远程服务器:
```bash
node scripts/publish.mjs --notes "更新说明"
```
脚本会自动:
1. 从构建产物中读取 `.nsis.zip``.nsis.zip.sig`
2. 生成 `update.json`(供应用内自动更新使用)
3. 通过 SCP 上传到服务器 `192.168.2.127:/var/www/html/work-order-monitor/`
更新地址:`http://web.nps.yunvip123.cn/work-order-monitor/update.json`
## 完整发版流程
```bash
# 1. 更新版本号
node scripts/bump-version.mjs minor
# 2. 构建
pnpm run tauri-build
# 3. 发布到服务器
node scripts/publish.mjs --notes "v0.3.0 新功能说明"
```
## 签名密钥
项目已内置 updater 签名密钥(`.tauri/signing-key` 和 `.tauri/signing-key.pub`),无密码保护,任何人克隆项目后即可直接构建。
如需重新生成密钥:
```bash
pnpm tauri signer generate -w .tauri/signing-key -p "" --ci -f
```
生成后需将 `.tauri/signing-key.pub` 的内容更新到 `src-tauri/tauri.conf.json``plugins.updater.pubkey` 字段。
## 项目结构 ## 项目结构
``` ```
src-tauri/src/ src/ # 前端Vue 3
├── lib.rs # 主模块,应用程序入口和命令处理 ├── App.vue # 主界面
├── config.rs # 配置管理模块 ├── stores/monitor.ts # Pinia 状态管理(核心业务逻辑)
├── api.rs # API调用模块 ├── components/
├── tray.rs # 系统托盘模块 │ ├── MonitorControl.vue # 监测面板(工单数据卡片)
└── scheduler.rs # 定时任务模块 │ ├── LoginForm.vue # 登录表单
│ ├── SettingsPanel.vue # 配置面板
│ ├── StatusHeader.vue # 顶部状态栏
│ ├── LogViewer.vue # 日志查看器
│ ├── CloseDialog.vue # 关闭确认弹窗
│ └── UpdateDialog.vue # 更新确认弹窗
src/ src-tauri/ # Rust 后端
└── App.vue # Vue设置界面 ├── src/lib.rs # 应用初始化、插件注册
├── src/notification.rs # Windows Toast 通知
├── tauri.conf.json # Tauri 配置
├── capabilities/default.json # 权限配置
scripts/
├── build.mjs # 构建(自动加载签名密钥)
├── bump-version.mjs # 版本号管理
└── publish.mjs # 构建发布
``` ```
## 使用说明 ## 监测的 API
### 1. 启动应用程序 | 接口 | 说明 | 触发通知条件 |
```bash | --- | --- | --- |
# 开发模式 | `SystemUser/Login` | 登录获取 Session | - |
pnpm tauri dev | `DemandManage/QueryIndexCount` | 工单统计 | PendingCount / StaycloseCount / ConfirmCount > 0 |
| `DemandManage/GetWorkOrderListPage` | 待审核工单列表 | DataCount > 0 |
# 构建生产版本 ## Tauri 插件
pnpm tauri build
```
### 2. 配置设置 - `tauri-plugin-http` — HTTP 请求
- 应用启动后会自动最小化到系统托盘 - `tauri-plugin-notification` — 系统通知
- 右键点击托盘图标选择"显示设置"打开设置界面 - `tauri-plugin-autostart` — 开机自启动
- 输入用户名和密码,点击"保存设置" - `tauri-plugin-opener` — 打开外部链接
- `tauri-plugin-updater` — 应用内自动更新
### 3. 登录和监测 - `tauri-plugin-process` — 进程管理(重启)
- 保存设置后点击"登录"按钮
- 登录成功后会自动启动定时任务每1分钟执行一次
- 系统会根据API返回值显示通知消息
### 4. 托盘操作
- **左键点击托盘图标** - 显示设置界面
- **右键点击托盘图标** - 显示菜单(显示设置/退出)
- **关闭窗口** - 应用会隐藏到托盘而不是退出
## 配置说明
### API接口
应用程序已配置为使用以下API接口
- **登录接口**: `https://crm.yunvip123.com/api/SystemUser/Login`
- **检查接口**: `https://crm.yunvip123.com/api/DemandManage/QueryIndexCount`
### 认证机制
- 登录时获取 `ASP.NET_SessionId` Cookie
- 所有后续请求都需要携带此Cookie进行认证
### API响应格式
**登录响应 (LoginResponse):**
```json
{
"success": true,
"code": null,
"msg": "执行成功",
"data": {
"GID": "512860ad-aa4a-4268-a5d9-6c7d2e28680b",
"SU_UserName": "A田森林"
}
}
```
**检查响应 (CheckResponse):**
```json
{
"success": true,
"code": null,
"msg": "执行成功",
"data": {
"PendingCount": 0,
"StaycloseCount": 0,
"ConfirmCount": 0
}
}
```
### 通知逻辑
- 当 `PendingCount`、`StaycloseCount`、`ConfirmCount` 三个值都为0时不发送通知
- 当任意一个值大于0时发送系统通知显示具体的工单数量
## 开发说明
### 添加新功能
1. **新的API端点** - 在 `api.rs` 中添加新的方法
2. **新的配置项** - 在 `config.rs` 中添加到 `AppConfig` 结构体
3. **新的UI组件** - 在 `App.vue` 中添加新的界面元素
4. **新的命令** - 在 `lib.rs` 中添加新的 Tauri 命令
### 错误处理
- 所有模块都使用 `anyhow::Result` 进行错误处理
- 错误信息会显示在UI的消息卡片中
- 日志记录使用 `log` crate可通过环境变量 `RUST_LOG` 控制级别
### 数据持久化
- 用户配置保存在系统配置目录:`%APPDATA%/tauri-app/config.json` (Windows)
- 配置文件使用JSON格式包含用户名、密码等信息
## 注意事项
1. **安全性** - 密码以明文形式存储在配置文件中,生产环境建议加密
2. **网络请求** - API调用失败时会显示错误通知
3. **系统权限** - 需要通知权限来显示系统消息
4. **托盘图标** - 使用应用程序默认图标,可根据登录状态切换不同图标
## TODO 项目
- [x] 配置实际的API地址和端点
- [x] 实现Cookie认证机制
- [x] 适配实际API响应格式
- [x] 添加更多的配置选项(检查间隔等)
- [x] 添加日志系统和日志显示
- [x] 实现自动启动功能
- [x] 添加网络连接状态检测
- [x] 添加测试按钮(手动检查、停止/开始、最小化等)
- [ ] 实现密码加密存储
- [ ] 支持不同登录状态的托盘图标(图标文件)
- [ ] 添加日志文件输出
- [ ] 实现会话过期自动重新登录
- [ ] 解决Windows编译环境问题需要Visual Studio Build Tools

39
data/deploy.js Normal file
View File

@ -0,0 +1,39 @@
const { NodeSSH } = require("node-ssh");
const os = require("node:os");
const config = {
host: "192.168.2.250",
username: "admin",
privateKeyPath: `${os.homedir()}\\.ssh\\id_rsa_2048`,
localDirectory: "dist",
remoteDirectory: "/D:/nginx/nginx-1.27.1/erp",
};
async function main() {
const ssh = new NodeSSH();
await ssh.connect({
host: config.host,
username: config.username,
privateKeyPath: config.privateKeyPath,
});
const res = await ssh.putDirectory(
config.localDirectory,
config.remoteDirectory,
{
recursive: true,
}
);
if (res) {
console.log("传输完成", res);
} else {
console.error(res);
}
const connection = ssh.getConnection();
connection.destroy();
// await ssh.dispose();
}
main();

124
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "tauri-app", "name": "work-order-monitor",
"version": "0.1.3", "version": "0.2.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "tauri-app", "name": "work-order-monitor",
"version": "0.1.3", "version": "0.2.1",
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^7.0.1", "@ant-design/icons-vue": "^7.0.1",
"@tauri-apps/api": "^2.10.1", "@tauri-apps/api": "^2.10.1",
@ -14,13 +14,15 @@
"@tauri-apps/plugin-http": "^2.5.7", "@tauri-apps/plugin-http": "^2.5.7",
"@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-opener": "^2.5.3", "@tauri-apps/plugin-opener": "^2.5.3",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.10.0",
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.29" "vue": "^3.5.29"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2", "@tauri-apps/cli": "^2.10.1",
"@vitejs/plugin-vue": "^6.0.4", "@vitejs/plugin-vue": "^6.0.4",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.3.1", "vite": "^7.3.1",
@ -957,9 +959,9 @@
} }
}, },
"node_modules/@tauri-apps/cli": { "node_modules/@tauri-apps/cli": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli/-/cli-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli/-/cli-2.10.1.tgz",
"integrity": "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==", "integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==",
"dev": true, "dev": true,
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"bin": { "bin": {
@ -973,23 +975,23 @@
"url": "https://opencollective.com/tauri" "url": "https://opencollective.com/tauri"
}, },
"optionalDependencies": { "optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "2.10.0", "@tauri-apps/cli-darwin-arm64": "2.10.1",
"@tauri-apps/cli-darwin-x64": "2.10.0", "@tauri-apps/cli-darwin-x64": "2.10.1",
"@tauri-apps/cli-linux-arm-gnueabihf": "2.10.0", "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1",
"@tauri-apps/cli-linux-arm64-gnu": "2.10.0", "@tauri-apps/cli-linux-arm64-gnu": "2.10.1",
"@tauri-apps/cli-linux-arm64-musl": "2.10.0", "@tauri-apps/cli-linux-arm64-musl": "2.10.1",
"@tauri-apps/cli-linux-riscv64-gnu": "2.10.0", "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1",
"@tauri-apps/cli-linux-x64-gnu": "2.10.0", "@tauri-apps/cli-linux-x64-gnu": "2.10.1",
"@tauri-apps/cli-linux-x64-musl": "2.10.0", "@tauri-apps/cli-linux-x64-musl": "2.10.1",
"@tauri-apps/cli-win32-arm64-msvc": "2.10.0", "@tauri-apps/cli-win32-arm64-msvc": "2.10.1",
"@tauri-apps/cli-win32-ia32-msvc": "2.10.0", "@tauri-apps/cli-win32-ia32-msvc": "2.10.1",
"@tauri-apps/cli-win32-x64-msvc": "2.10.0" "@tauri-apps/cli-win32-x64-msvc": "2.10.1"
} }
}, },
"node_modules/@tauri-apps/cli-darwin-arm64": { "node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz",
"integrity": "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==", "integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1004,9 +1006,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-darwin-x64": { "node_modules/@tauri-apps/cli-darwin-x64": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz",
"integrity": "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==", "integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1021,9 +1023,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz",
"integrity": "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==", "integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1038,9 +1040,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm64-gnu": { "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz",
"integrity": "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==", "integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1055,9 +1057,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm64-musl": { "node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz",
"integrity": "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==", "integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1072,9 +1074,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": { "node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz",
"integrity": "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==", "integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -1089,9 +1091,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-x64-gnu": { "node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz",
"integrity": "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==", "integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1106,9 +1108,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-x64-musl": { "node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz",
"integrity": "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==", "integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1123,9 +1125,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-arm64-msvc": { "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz",
"integrity": "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==", "integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1140,9 +1142,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-ia32-msvc": { "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz",
"integrity": "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==", "integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -1157,9 +1159,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-x64-msvc": { "node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "2.10.0", "version": "2.10.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.0.tgz", "resolved": "https://registry.npmmirror.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz",
"integrity": "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==", "integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1209,6 +1211,24 @@
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.8.0"
} }
}, },
"node_modules/@tauri-apps/plugin-process": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz",
"integrity": "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.8.0"
}
},
"node_modules/@tauri-apps/plugin-updater": {
"version": "2.10.0",
"resolved": "https://registry.npmmirror.com/@tauri-apps/plugin-updater/-/plugin-updater-2.10.0.tgz",
"integrity": "sha512-ljN8jPlnT0aSn8ecYhuBib84alxfMx6Hc8vJSKMJyzGbTPFZAC44T2I1QNFZssgWKrAlofvJqCC6Rr472JWfkQ==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.10.1"
}
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",

View File

@ -1,7 +1,7 @@
{ {
"name": "tauri-app", "name": "work-order-monitor",
"private": true, "private": true,
"version": "0.1.3", "version": "0.2.2",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@ -9,7 +9,7 @@
"preview": "vite preview", "preview": "vite preview",
"tauri": "tauri", "tauri": "tauri",
"tauri-dev": "tauri dev", "tauri-dev": "tauri dev",
"tauri-build": "tauri build" "tauri-build": "node scripts/build.mjs"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^7.0.1", "@ant-design/icons-vue": "^7.0.1",
@ -18,13 +18,15 @@
"@tauri-apps/plugin-http": "^2.5.7", "@tauri-apps/plugin-http": "^2.5.7",
"@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-opener": "^2.5.3", "@tauri-apps/plugin-opener": "^2.5.3",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.10.0",
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.29" "vue": "^3.5.29"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2", "@tauri-apps/cli": "^2.10.1",
"@vitejs/plugin-vue": "^6.0.4", "@vitejs/plugin-vue": "^6.0.4",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.3.1", "vite": "^7.3.1",

View File

@ -26,6 +26,12 @@ importers:
'@tauri-apps/plugin-opener': '@tauri-apps/plugin-opener':
specifier: ^2.5.3 specifier: ^2.5.3
version: 2.5.3 version: 2.5.3
'@tauri-apps/plugin-process':
specifier: ^2.3.1
version: 2.3.1
'@tauri-apps/plugin-updater':
specifier: ^2.10.0
version: 2.10.0
ant-design-vue: ant-design-vue:
specifier: ^4.2.6 specifier: ^4.2.6
version: 4.2.6(vue@3.5.29(typescript@5.9.3)) version: 4.2.6(vue@3.5.29(typescript@5.9.3))
@ -40,8 +46,8 @@ importers:
version: 3.5.29(typescript@5.9.3) version: 3.5.29(typescript@5.9.3)
devDependencies: devDependencies:
'@tauri-apps/cli': '@tauri-apps/cli':
specifier: ^2 specifier: ^2.10.1
version: 2.8.4 version: 2.10.1
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^6.0.4 specifier: ^6.0.4
version: 6.0.4(vite@7.3.1)(vue@3.5.29(typescript@5.9.3)) version: 6.0.4(vite@7.3.1)(vue@3.5.29(typescript@5.9.3))
@ -388,79 +394,79 @@ packages:
'@tauri-apps/api@2.10.1': '@tauri-apps/api@2.10.1':
resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==} resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==}
'@tauri-apps/cli-darwin-arm64@2.8.4': '@tauri-apps/cli-darwin-arm64@2.10.1':
resolution: {integrity: sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==} resolution: {integrity: sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@tauri-apps/cli-darwin-x64@2.8.4': '@tauri-apps/cli-darwin-x64@2.10.1':
resolution: {integrity: sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==} resolution: {integrity: sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': '@tauri-apps/cli-linux-arm-gnueabihf@2.10.1':
resolution: {integrity: sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==} resolution: {integrity: sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@tauri-apps/cli-linux-arm64-gnu@2.8.4': '@tauri-apps/cli-linux-arm64-gnu@2.10.1':
resolution: {integrity: sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==} resolution: {integrity: sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.8.4': '@tauri-apps/cli-linux-arm64-musl@2.10.1':
resolution: {integrity: sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==} resolution: {integrity: sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl] libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.8.4': '@tauri-apps/cli-linux-riscv64-gnu@2.10.1':
resolution: {integrity: sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==} resolution: {integrity: sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.8.4': '@tauri-apps/cli-linux-x64-gnu@2.10.1':
resolution: {integrity: sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==} resolution: {integrity: sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.8.4': '@tauri-apps/cli-linux-x64-musl@2.10.1':
resolution: {integrity: sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==} resolution: {integrity: sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl] libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.8.4': '@tauri-apps/cli-win32-arm64-msvc@2.10.1':
resolution: {integrity: sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==} resolution: {integrity: sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@tauri-apps/cli-win32-ia32-msvc@2.8.4': '@tauri-apps/cli-win32-ia32-msvc@2.10.1':
resolution: {integrity: sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==} resolution: {integrity: sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@tauri-apps/cli-win32-x64-msvc@2.8.4': '@tauri-apps/cli-win32-x64-msvc@2.10.1':
resolution: {integrity: sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==} resolution: {integrity: sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@tauri-apps/cli@2.8.4': '@tauri-apps/cli@2.10.1':
resolution: {integrity: sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==} resolution: {integrity: sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
hasBin: true hasBin: true
@ -476,6 +482,12 @@ packages:
'@tauri-apps/plugin-opener@2.5.3': '@tauri-apps/plugin-opener@2.5.3':
resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==} resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==}
'@tauri-apps/plugin-process@2.3.1':
resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==}
'@tauri-apps/plugin-updater@2.10.0':
resolution: {integrity: sha512-ljN8jPlnT0aSn8ecYhuBib84alxfMx6Hc8vJSKMJyzGbTPFZAC44T2I1QNFZssgWKrAlofvJqCC6Rr472JWfkQ==}
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -970,52 +982,52 @@ snapshots:
'@tauri-apps/api@2.10.1': {} '@tauri-apps/api@2.10.1': {}
'@tauri-apps/cli-darwin-arm64@2.8.4': '@tauri-apps/cli-darwin-arm64@2.10.1':
optional: true optional: true
'@tauri-apps/cli-darwin-x64@2.8.4': '@tauri-apps/cli-darwin-x64@2.10.1':
optional: true optional: true
'@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': '@tauri-apps/cli-linux-arm-gnueabihf@2.10.1':
optional: true optional: true
'@tauri-apps/cli-linux-arm64-gnu@2.8.4': '@tauri-apps/cli-linux-arm64-gnu@2.10.1':
optional: true optional: true
'@tauri-apps/cli-linux-arm64-musl@2.8.4': '@tauri-apps/cli-linux-arm64-musl@2.10.1':
optional: true optional: true
'@tauri-apps/cli-linux-riscv64-gnu@2.8.4': '@tauri-apps/cli-linux-riscv64-gnu@2.10.1':
optional: true optional: true
'@tauri-apps/cli-linux-x64-gnu@2.8.4': '@tauri-apps/cli-linux-x64-gnu@2.10.1':
optional: true optional: true
'@tauri-apps/cli-linux-x64-musl@2.8.4': '@tauri-apps/cli-linux-x64-musl@2.10.1':
optional: true optional: true
'@tauri-apps/cli-win32-arm64-msvc@2.8.4': '@tauri-apps/cli-win32-arm64-msvc@2.10.1':
optional: true optional: true
'@tauri-apps/cli-win32-ia32-msvc@2.8.4': '@tauri-apps/cli-win32-ia32-msvc@2.10.1':
optional: true optional: true
'@tauri-apps/cli-win32-x64-msvc@2.8.4': '@tauri-apps/cli-win32-x64-msvc@2.10.1':
optional: true optional: true
'@tauri-apps/cli@2.8.4': '@tauri-apps/cli@2.10.1':
optionalDependencies: optionalDependencies:
'@tauri-apps/cli-darwin-arm64': 2.8.4 '@tauri-apps/cli-darwin-arm64': 2.10.1
'@tauri-apps/cli-darwin-x64': 2.8.4 '@tauri-apps/cli-darwin-x64': 2.10.1
'@tauri-apps/cli-linux-arm-gnueabihf': 2.8.4 '@tauri-apps/cli-linux-arm-gnueabihf': 2.10.1
'@tauri-apps/cli-linux-arm64-gnu': 2.8.4 '@tauri-apps/cli-linux-arm64-gnu': 2.10.1
'@tauri-apps/cli-linux-arm64-musl': 2.8.4 '@tauri-apps/cli-linux-arm64-musl': 2.10.1
'@tauri-apps/cli-linux-riscv64-gnu': 2.8.4 '@tauri-apps/cli-linux-riscv64-gnu': 2.10.1
'@tauri-apps/cli-linux-x64-gnu': 2.8.4 '@tauri-apps/cli-linux-x64-gnu': 2.10.1
'@tauri-apps/cli-linux-x64-musl': 2.8.4 '@tauri-apps/cli-linux-x64-musl': 2.10.1
'@tauri-apps/cli-win32-arm64-msvc': 2.8.4 '@tauri-apps/cli-win32-arm64-msvc': 2.10.1
'@tauri-apps/cli-win32-ia32-msvc': 2.8.4 '@tauri-apps/cli-win32-ia32-msvc': 2.10.1
'@tauri-apps/cli-win32-x64-msvc': 2.8.4 '@tauri-apps/cli-win32-x64-msvc': 2.10.1
'@tauri-apps/plugin-autostart@2.5.1': '@tauri-apps/plugin-autostart@2.5.1':
dependencies: dependencies:
@ -1033,6 +1045,14 @@ snapshots:
dependencies: dependencies:
'@tauri-apps/api': 2.10.1 '@tauri-apps/api': 2.10.1
'@tauri-apps/plugin-process@2.3.1':
dependencies:
'@tauri-apps/api': 2.10.1
'@tauri-apps/plugin-updater@2.10.0':
dependencies:
'@tauri-apps/api': 2.10.1
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@vitejs/plugin-vue@6.0.4(vite@7.3.1)(vue@3.5.29(typescript@5.9.3))': '@vitejs/plugin-vue@6.0.4(vite@7.3.1)(vue@3.5.29(typescript@5.9.3))':

24
scripts/build.mjs Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env node
/**
* 构建脚本 - 自动加载项目内置签名密钥后执行 tauri build
*/
import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { execSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "..");
const keyPath = resolve(root, ".tauri/signing-key");
try {
process.env.TAURI_SIGNING_PRIVATE_KEY = readFileSync(keyPath, "utf-8").trim();
process.env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD = "";
} catch {
console.error(`错误: 未找到签名密钥 ${keyPath}`);
console.error("请运行: pnpm tauri signer generate -w .tauri/signing-key -p \"\" --ci");
process.exit(1);
}
execSync("pnpm tauri build", { stdio: "inherit", cwd: root });

103
scripts/bump-version.mjs Normal file
View File

@ -0,0 +1,103 @@
/*
* @file /scripts/bump-version.mjs
* @description 统一更新 Tauri 项目版本号
* @author tsl (randy1924@163.com)
* @date 2026-03-23 10:23:42
* @lastModified 2026-03-23 10:25:08
* @lastModifiedBy tsl (randy1924@163.com)
* @example node scripts/bump-version.mjs [patch|minor|major|x.y.z]
* @default patch
*/
import { readFileSync, writeFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "..");
// 读取当前版本
function getCurrentVersion() {
const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf-8"));
return pkg.version;
}
// 根据 bump 类型计算新版本
function bumpVersion(current, type) {
const parts = current.replace(/-.*$/, "").split(".").map(Number);
switch (type) {
case "major":
return `${parts[0] + 1}.0.0`;
case "minor":
return `${parts[0]}.${parts[1] + 1}.0`;
case "patch":
return `${parts[0]}.${parts[1]}.${parts[2] + 1}`;
default:
return null;
}
}
const arg = process.argv[2] || "patch";
const BUMP_TYPES = ["patch", "minor", "major"];
const current = getCurrentVersion();
let version;
if (BUMP_TYPES.includes(arg)) {
version = bumpVersion(current, arg);
} else if (/^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/.test(arg)) {
version = arg;
} else {
console.error(`错误: 无效参数 "${arg}",应为 patch / minor / major 或 x.y.z 格式`);
process.exit(1);
}
const files = [
{
path: "package.json",
update(content) {
const json = JSON.parse(content);
const old = json.version;
json.version = version;
return { result: JSON.stringify(json, null, 2) + "\n", old };
},
},
{
path: "src-tauri/tauri.conf.json",
update(content) {
const json = JSON.parse(content);
const old = json.version;
json.version = version;
return { result: JSON.stringify(json, null, 2) + "\n", old };
},
},
{
path: "src-tauri/Cargo.toml",
update(content) {
let old = "";
let replaced = false;
const result = content.replace(
/^version\s*=\s*"([^"]*)"/m,
(match, v) => {
if (replaced) return match;
replaced = true;
old = v;
return `version = "${version}"`;
}
);
return { result, old };
},
},
];
console.log(`\n${current}${version} (${BUMP_TYPES.includes(arg) ? arg : "指定版本"})\n`);
for (const file of files) {
const filePath = resolve(root, file.path);
const content = readFileSync(filePath, "utf-8");
const { result, old } = file.update(content);
writeFileSync(filePath, result, "utf-8");
console.log(`${file.path} ${old}${version}`);
}
console.log("\n全部更新完成!");
console.log("提示: package-lock.json 和 Cargo.lock 会在下次 install/build 时自动同步\n");

120
scripts/publish.mjs Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env node
/**
* @file /scripts/publish.mjs
* @description 构建后生成 update.json 并上传到远程服务器
* @usage node scripts/publish.mjs [--notes "更新说明"]
*
* 前置条件
* 1. 已运行 `pnpm run tauri-build` 生成构建产物
* 2. 远程服务器 SSH 可达192.168.2.127:22
*/
import { readFileSync, writeFileSync, readdirSync, existsSync } from "node:fs";
import { resolve, dirname, basename } from "node:path";
import { fileURLToPath } from "node:url";
import { execSync } from "node:child_process";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "..");
// ===== 配置 =====
const REMOTE_USER = "root_test";
const REMOTE_HOST = "192.168.2.127";
const REMOTE_PORT = 22;
const REMOTE_DIR = "/var/www/html/work-order-monitor";
const BASE_URL = "http://web.nps.yunvip123.cn/work-order-monitor";
// ===== 解析参数 =====
const args = process.argv.slice(2);
let notes = "";
const notesIdx = args.indexOf("--notes");
if (notesIdx !== -1 && args[notesIdx + 1]) {
notes = args[notesIdx + 1];
}
// ===== 读取版本号 =====
const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf-8"));
const version = pkg.version;
// ===== 查找构建产物 =====
const bundleDir = resolve(root, "src-tauri/target/release/bundle/nsis");
if (!existsSync(bundleDir)) {
console.error(`错误: 未找到构建目录 ${bundleDir}`);
console.error("请先运行: pnpm run tauri-build");
process.exit(1);
}
const files = readdirSync(bundleDir);
const setupFile = files.find((f) => f.includes(version) && f.endsWith("-setup.exe"));
const sigFile = files.find((f) => f.includes(version) && f.endsWith("-setup.exe.sig"));
if (!setupFile || !sigFile) {
console.error(`错误: 未找到版本 ${version} 的 -setup.exe 或 -setup.exe.sig 文件`);
console.error(`目录内容: ${files.join(", ")}`);
process.exit(1);
}
const setupPath = resolve(bundleDir, setupFile);
const sigPath = resolve(bundleDir, sigFile);
const signature = readFileSync(sigPath, "utf-8").trim();
console.log(`\n版本: ${version}`);
console.log(`安装包: ${setupFile}`);
console.log(`签名文件: ${sigFile}`);
console.log(`签名: ${signature.substring(0, 40)}...`);
// ===== 生成 update.json =====
const encodedFileName = encodeURIComponent(setupFile);
const updateJson = {
version,
notes: notes || `v${version} 更新`,
pub_date: new Date().toISOString(),
platforms: {
"windows-x86_64": {
url: `${BASE_URL}/${encodedFileName}`,
signature,
},
},
};
const updateJsonPath = resolve(bundleDir, "update.json");
writeFileSync(updateJsonPath, JSON.stringify(updateJson, null, 2), "utf-8");
console.log(`\nupdate.json 已生成`);
console.log(JSON.stringify(updateJson, null, 2));
// ===== 上传到远程服务器 =====
console.log(`\n正在上传到 ${REMOTE_HOST}:${REMOTE_DIR} ...`);
try {
// 确保远程目录存在
execSync(
`ssh -p ${REMOTE_PORT} ${REMOTE_USER}@${REMOTE_HOST} "mkdir -p ${REMOTE_DIR}"`,
{ stdio: "inherit" }
);
// 上传安装包
console.log(` 上传 ${setupFile} ...`);
execSync(
`scp -P ${REMOTE_PORT} "${setupPath}" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/`,
{ stdio: "inherit" }
);
// 上传 update.json
console.log(` 上传 update.json ...`);
execSync(
`scp -P ${REMOTE_PORT} "${updateJsonPath}" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/`,
{ stdio: "inherit" }
);
console.log("\n全部上传完成!");
console.log(`更新地址: ${BASE_URL}/update.json`);
console.log(`下载地址: ${BASE_URL}/${encodedFileName}\n`);
} catch (e) {
console.error("\n上传失败:", e.message);
console.error("请检查 SSH 连接是否正常,或手动上传以下文件:");
console.error(` 1. ${setupPath}`);
console.error(` 2. ${updateJsonPath}`);
console.error(` → 目标: ${REMOTE_HOST}:${REMOTE_DIR}/`);
process.exit(1);
}

267
src-tauri/Cargo.lock generated
View File

@ -47,6 +47,15 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
dependencies = [
"derive_arbitrary",
]
[[package]] [[package]]
name = "async-broadcast" name = "async-broadcast"
version = "0.7.2" version = "0.7.2"
@ -717,6 +726,17 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "derive_arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.20" version = "0.99.20"
@ -1018,6 +1038,17 @@ dependencies = [
"rustc_version", "rustc_version",
] ]
[[package]]
name = "filetime"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
dependencies = [
"cfg-if",
"libc",
"libredox",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.9" version = "0.1.9"
@ -2114,6 +2145,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"libc", "libc",
"redox_syscall 0.7.3",
] ]
[[package]] [[package]]
@ -2225,6 +2257,12 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minisign-verify"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.9" version = "0.8.9"
@ -2286,10 +2324,10 @@ dependencies = [
"libc", "libc",
"log", "log",
"openssl", "openssl",
"openssl-probe", "openssl-probe 0.1.6",
"openssl-sys", "openssl-sys",
"schannel", "schannel",
"security-framework", "security-framework 2.11.1",
"security-framework-sys", "security-framework-sys",
"tempfile", "tempfile",
] ]
@ -2548,6 +2586,18 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
] ]
[[package]]
name = "objc2-osa-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
dependencies = [
"bitflags 2.11.0",
"objc2",
"objc2-app-kit",
"objc2-foundation",
]
[[package]] [[package]]
name = "objc2-quartz-core" name = "objc2-quartz-core"
version = "0.3.2" version = "0.3.2"
@ -2649,6 +2699,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-probe"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.110" version = "0.9.110"
@ -2677,6 +2733,20 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "osakit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
dependencies = [
"objc2",
"objc2-foundation",
"objc2-osa-kit",
"serde",
"serde_json",
"thiserror 2.0.18",
]
[[package]] [[package]]
name = "pango" name = "pango"
version = "0.18.3" version = "0.18.3"
@ -2726,7 +2796,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall 0.5.18",
"smallvec", "smallvec",
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
@ -3305,6 +3375,15 @@ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
] ]
[[package]]
name = "redox_syscall"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
dependencies = [
"bitflags 2.11.0",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.6" version = "0.4.6"
@ -3436,15 +3515,20 @@ dependencies = [
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-rustls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"serde", "serde",
"serde_json", "serde_json",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http", "tower-http",
@ -3512,6 +3596,18 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-native-certs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
dependencies = [
"openssl-probe 0.2.1",
"rustls-pki-types",
"schannel",
"security-framework 3.5.1",
]
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.14.0" version = "1.14.0"
@ -3522,6 +3618,33 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-platform-verifier"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework 3.5.1",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.9" version = "0.103.9"
@ -3633,6 +3756,19 @@ dependencies = [
"security-framework-sys", "security-framework-sys",
] ]
[[package]]
name = "security-framework"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
dependencies = [
"bitflags 2.11.0",
"core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]] [[package]]
name = "security-framework-sys" name = "security-framework-sys"
version = "2.15.0" version = "2.15.0"
@ -3923,7 +4059,7 @@ dependencies = [
"objc2-foundation", "objc2-foundation",
"objc2-quartz-core", "objc2-quartz-core",
"raw-window-handle", "raw-window-handle",
"redox_syscall", "redox_syscall 0.5.18",
"tracing", "tracing",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@ -4137,6 +4273,17 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "tar"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]] [[package]]
name = "target-lexicon" name = "target-lexicon"
version = "0.12.16" version = "0.12.16"
@ -4195,24 +4342,6 @@ dependencies = [
"windows 0.61.3", "windows 0.61.3",
] ]
[[package]]
name = "tauri-app"
version = "0.1.2"
dependencies = [
"env_logger",
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-autostart",
"tauri-plugin-http",
"tauri-plugin-notification",
"tauri-plugin-opener",
"tauri-plugin-store",
"windows 0.58.0",
]
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "2.5.5" version = "2.5.5"
@ -4394,6 +4523,16 @@ dependencies = [
"zbus", "zbus",
] ]
[[package]]
name = "tauri-plugin-process"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a"
dependencies = [
"tauri",
"tauri-plugin",
]
[[package]] [[package]]
name = "tauri-plugin-store" name = "tauri-plugin-store"
version = "2.4.2" version = "2.4.2"
@ -4410,6 +4549,39 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tauri-plugin-updater"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fe8e9bebd88fc222938ffdfbdcfa0307081423bd01e3252fc337d8bde81fc61"
dependencies = [
"base64 0.22.1",
"dirs 6.0.0",
"flate2",
"futures-util",
"http",
"infer",
"log",
"minisign-verify",
"osakit",
"percent-encoding",
"reqwest 0.13.2",
"rustls",
"semver",
"serde",
"serde_json",
"tar",
"tauri",
"tauri-plugin",
"tempfile",
"thiserror 2.0.18",
"time",
"tokio",
"url",
"windows-sys 0.60.2",
"zip",
]
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.10.0" version = "2.10.0"
@ -5305,6 +5477,15 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "webpki-root-certs"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "1.0.6" version = "1.0.6"
@ -5984,6 +6165,26 @@ dependencies = [
"wasmparser", "wasmparser",
] ]
[[package]]
name = "work-order-monitor"
version = "0.2.2"
dependencies = [
"env_logger",
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-autostart",
"tauri-plugin-http",
"tauri-plugin-notification",
"tauri-plugin-opener",
"tauri-plugin-process",
"tauri-plugin-store",
"tauri-plugin-updater",
"windows 0.58.0",
]
[[package]] [[package]]
name = "writeable" name = "writeable"
version = "0.6.2" version = "0.6.2"
@ -6056,6 +6257,16 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
dependencies = [
"libc",
"rustix",
]
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.1" version = "0.8.1"
@ -6220,6 +6431,18 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "zip"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
dependencies = [
"arbitrary",
"crc32fast",
"indexmap 2.13.0",
"memchr",
]
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.21"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-app" name = "work-order-monitor"
version = "0.1.2" version = "0.2.2"
description = "A Tauri App" description = "A Tauri App"
authors = ["you"] authors = ["you"]
edition = "2021" edition = "2021"
@ -11,7 +11,7 @@ edition = "2021"
# The `_lib` suffix may seem redundant but it is necessary # The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name. # to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "tauri_app_lib" name = "work_order_monitor_lib"
crate-type = ["staticlib", "cdylib", "rlib"] crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies] [build-dependencies]
@ -28,6 +28,8 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
log = "0.4" log = "0.4"
env_logger = "0.10" env_logger = "0.10"
tauri-plugin-updater = "2.10.0"
tauri-plugin-process = "2.3.1"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = [ windows = { version = "0.58", features = [

View File

@ -41,6 +41,8 @@
"core:window:allow-set-title", "core:window:allow-set-title",
"core:window:allow-unminimize", "core:window:allow-unminimize",
"core:image:default", "core:image:default",
"core:image:allow-from-path" "core:image:allow-from-path",
"updater:default",
"process:allow-restart"
] ]
} }

View File

@ -15,10 +15,10 @@ val tauriProperties = Properties().apply {
android { android {
compileSdk = 36 compileSdk = 36
namespace = "com.administrator.tauri_app" namespace = "com.yunpu.workordermonitor"
defaultConfig { defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false" manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "com.administrator.tauri_app" applicationId = "com.yunpu.workordermonitor"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()

View File

@ -1,4 +1,4 @@
package com.administrator.tauri_app package com.yunpu.workordermonitor
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge

View File

@ -1,4 +1,4 @@
<resources> <resources>
<string name="app_name">tauri-app</string> <string name="app_name">work-order-monitor</string>
<string name="main_activity_title">tauri-app</string> <string name="main_activity_title">work-order-monitor</string>
</resources> </resources>

View File

@ -91,6 +91,8 @@ pub fn run() {
MacosLauncher::LaunchAgent, MacosLauncher::LaunchAgent,
Some(vec![]), Some(vec![]),
)) ))
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_process::init())
.setup(|app| { .setup(|app| {
start_notification_callback_server(app.handle().clone()); start_notification_callback_server(app.handle().clone());
Ok(()) Ok(())

View File

@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() { fn main() {
tauri_app_lib::run() work_order_monitor_lib::run()
} }

View File

@ -1,8 +1,8 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "工单系统监测", "productName": "工单系统监测",
"version": "0.1.3", "version": "0.2.2",
"identifier": "com.administrator.tauri-app", "identifier": "com.yunpu.workordermonitor",
"build": { "build": {
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420", "devUrl": "http://localhost:1420",
@ -23,7 +23,10 @@
}, },
"bundle": { "bundle": {
"active": true, "active": true,
"targets": ["nsis"], "targets": [
"nsis"
],
"createUpdaterArtifacts": true,
"icon": [ "icon": [
"icons/32x32.png", "icons/32x32.png",
"icons/128x128.png", "icons/128x128.png",
@ -35,5 +38,17 @@
"icons/32x32.png", "icons/32x32.png",
"icons/icon.ico" "icons/icon.ico"
] ]
},
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDY0Q0YxMzNBM0Q5OEU4NEMKUldSTTZKZzlPaFBQWkVXRVdBVHdTdlNvVnZSL2FFQkZtbFpRTzdjNGxHbjJsZ1R5MFRLdkpmYzMK",
"endpoints": [
"http://web.nps.yunvip123.cn/work-order-monitor/update.json"
],
"dangerousInsecureTransportProtocol": true,
"windows": {
"installMode": "passive"
}
}
} }
} }

View File

@ -15,6 +15,7 @@ import SettingsPanel from "./components/SettingsPanel.vue";
import LogViewer from "./components/LogViewer.vue"; import LogViewer from "./components/LogViewer.vue";
import AppStatusBar, { type StatusBarItem } from "./components/AppStatusBar.vue"; import AppStatusBar, { type StatusBarItem } from "./components/AppStatusBar.vue";
import CloseDialog from "./components/CloseDialog.vue"; import CloseDialog from "./components/CloseDialog.vue";
import UpdateDialog from "./components/UpdateDialog.vue";
import { useMonitorStore } from "./stores/monitor"; import { useMonitorStore } from "./stores/monitor";
const activeTab = ref("monitor"); const activeTab = ref("monitor");
@ -81,6 +82,7 @@ onUnmounted(() => store.cleanup());
</a-layout> </a-layout>
<CloseDialog /> <CloseDialog />
<UpdateDialog />
</template> </template>
<style> <style>

View File

@ -0,0 +1,99 @@
<script setup lang="ts">
import { computed } from "vue";
import { useMonitorStore } from "../stores/monitor";
const store = useMonitorStore();
const progressPercent = computed(() => {
if (!store.updateProgress || !store.updateProgress.total) return 0;
return Math.round((store.updateProgress.downloaded / store.updateProgress.total) * 100);
});
const downloadedMB = computed(() => {
if (!store.updateProgress) return "0";
return (store.updateProgress.downloaded / 1024 / 1024).toFixed(1);
});
const totalMB = computed(() => {
if (!store.updateProgress || !store.updateProgress.total) return "0";
return (store.updateProgress.total / 1024 / 1024).toFixed(1);
});
</script>
<template>
<a-modal
v-model:open="store.showUpdateDialog"
title="发现新版本"
:width="400"
centered
:closable="!store.isUpdating"
:maskClosable="!store.isUpdating"
>
<div class="update-body">
<div v-if="!store.isUpdating">
<p class="version-info">
新版本 <span class="version-tag">v{{ store.updateInfo?.version }}</span> 已发布
</p>
<div v-if="store.updateInfo?.notes" class="update-notes">
<p class="notes-title">更新说明</p>
<p class="notes-content">{{ store.updateInfo.notes }}</p>
</div>
</div>
<div v-else class="update-progress">
<p>正在下载更新...</p>
<a-progress :percent="progressPercent" :status="progressPercent >= 100 ? 'success' : 'active'" />
<p class="progress-detail">{{ downloadedMB }} / {{ totalMB }} MB</p>
</div>
</div>
<template #footer>
<template v-if="!store.isUpdating">
<a-button @click="store.dismissUpdate()">稍后提醒</a-button>
<a-button type="primary" @click="store.confirmUpdate()">立即更新</a-button>
</template>
<template v-else>
<a-button disabled>更新中请稍候...</a-button>
</template>
</template>
</a-modal>
</template>
<style scoped>
.update-body {
padding: 4px 0 8px;
}
.version-info {
margin: 0 0 12px;
font-size: 15px;
color: #333;
}
.version-tag {
font-weight: 600;
color: #1677ff;
}
.notes-title {
margin: 0 0 4px;
font-size: 13px;
color: #888;
}
.notes-content {
margin: 0;
padding: 8px 12px;
background: #f5f5f5;
border-radius: 6px;
font-size: 13px;
color: #555;
white-space: pre-wrap;
}
.update-progress {
text-align: center;
}
.update-progress p {
margin: 0 0 8px;
}
.progress-detail {
font-size: 12px;
color: #999;
margin-top: 4px !important;
}
</style>

View File

@ -18,6 +18,8 @@ import {
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event"; import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import { openUrl } from "@tauri-apps/plugin-opener"; import { openUrl } from "@tauri-apps/plugin-opener";
import { check, type Update } from "@tauri-apps/plugin-updater";
import { relaunch } from "@tauri-apps/plugin-process";
export const APP_NAME = "工单系统监测"; export const APP_NAME = "工单系统监测";
const API_BASE = "https://crm.yunvip123.com/api"; const API_BASE = "https://crm.yunvip123.com/api";
@ -76,6 +78,10 @@ export const useMonitorStore = defineStore("monitor", () => {
}); });
const showCloseDialog = ref(false); const showCloseDialog = ref(false);
const postponeResumeTime = ref<number | null>(null); const postponeResumeTime = ref<number | null>(null);
const showUpdateDialog = ref(false);
const updateInfo = ref<{ version: string; notes: string; date: string } | null>(null);
const updateProgress = ref<{ total: number; downloaded: number } | null>(null);
const isUpdating = ref(false);
// ===== 内部状态 ===== // ===== 内部状态 =====
let sessionId = ""; let sessionId = "";
@ -85,7 +91,9 @@ export const useMonitorStore = defineStore("monitor", () => {
let postponeTimer: ReturnType<typeof setTimeout> | null = null; let postponeTimer: ReturnType<typeof setTimeout> | null = null;
let unlistenNotification: UnlistenFn | null = null; let unlistenNotification: UnlistenFn | null = null;
let unlistenClose: UnlistenFn | null = null; let unlistenClose: UnlistenFn | null = null;
let pendingUpdate: Update | null = null;
let trayIconInstance: TrayIcon | null = null; let trayIconInstance: TrayIcon | null = null;
let cachedSvgImg: HTMLImageElement | null = null;
// ===== 工具函数 ===== // ===== 工具函数 =====
function formatTime(date: Date): string { function formatTime(date: Date): string {
@ -541,6 +549,60 @@ export const useMonitorStore = defineStore("monitor", () => {
} }
// ===== 配置更新 ===== // ===== 配置更新 =====
async function checkForUpdates() {
try {
addLog("INFO", "正在检查应用更新...", "UPDATER");
const update = await check();
if (update) {
pendingUpdate = update;
updateInfo.value = {
version: update.version,
notes: update.body || "",
date: update.date || "",
};
showUpdateDialog.value = true;
addLog("INFO", `发现新版本: ${update.version}`, "UPDATER");
} else {
addLog("INFO", "当前已是最新版本", "UPDATER");
}
} catch (e: any) {
addLog("WARN", `检查更新失败: ${e.message || e}`, "UPDATER");
}
}
async function confirmUpdate() {
if (!pendingUpdate) return;
isUpdating.value = true;
updateProgress.value = { total: 0, downloaded: 0 };
try {
addLog("INFO", "开始下载更新...", "UPDATER");
await pendingUpdate.downloadAndInstall((event) => {
if (event.event === "Started" && event.data.contentLength) {
updateProgress.value = { total: event.data.contentLength, downloaded: 0 };
} else if (event.event === "Progress") {
if (updateProgress.value) {
updateProgress.value.downloaded += event.data.chunkLength;
}
} else if (event.event === "Finished") {
addLog("INFO", "更新下载完成,即将重启...", "UPDATER");
}
});
await relaunch();
} catch (e: any) {
addLog("ERROR", `更新安装失败: ${e.message || e}`, "UPDATER");
message.value = "更新安装失败";
isUpdating.value = false;
updateProgress.value = null;
}
}
function dismissUpdate() {
showUpdateDialog.value = false;
pendingUpdate = null;
updateInfo.value = null;
}
async function updateConfig() { async function updateConfig() {
isLoading.value = true; isLoading.value = true;
try { try {
@ -563,29 +625,59 @@ export const useMonitorStore = defineStore("monitor", () => {
// ===== 系统托盘 ===== // ===== 系统托盘 =====
/** 用 Canvas 渲染彩色圆形背景 + 白色"工"字,返回 RGBA 像素数据 */ /** 加载 SVG 图片(带缓存) */
function makeTrayIconRGBA(r: number, g: number, b: number, size = 32): Uint8Array { async function loadSvgImage(): Promise<HTMLImageElement> {
if (cachedSvgImg) return cachedSvgImg;
return new Promise((resolve, reject) => {
const img = new window.Image();
img.onload = () => { cachedSvgImg = img; resolve(img); };
img.onerror = reject;
img.src = "/gong.svg";
});
}
/** 加载 SVG → Canvas 渲染 → 像素替色 → 返回 RGBA 数据 */
async function makeTrayIconRGBA(r: number, g: number, b: number, size = 32): Promise<Uint8Array> {
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.width = size; canvas.width = size;
canvas.height = size; canvas.height = size;
const ctx = canvas.getContext("2d")!; const ctx = canvas.getContext("2d")!;
// 圆形背景 try {
ctx.clearRect(0, 0, size, size); const svgImg = await loadSvgImage();
ctx.beginPath();
ctx.arc(size / 2, size / 2, size / 2 - 1, 0, Math.PI * 2);
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fill();
// 白色"工"字 ctx.drawImage(svgImg, 0, 0, size, size);
const imageData = ctx.getImageData(0, 0, size, size);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// 根据亮度判断:暗像素(文字) → 白色,亮像素(背景) → 状态色
const brightness = (data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114);
if (brightness < 128) {
data[i] = data[i + 1] = data[i + 2] = 255;
} else {
data[i] = r;
data[i + 1] = g;
data[i + 2] = b;
}
data[i + 3] = 255;
}
ctx.putImageData(imageData, 0, 0);
} catch {
// SVG 加载失败,回退到纯 Canvas 绘制
addLog("WARN", "SVG 图标加载失败,使用回退方案", "SYSTEM");
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = "#ffffff"; ctx.fillStyle = "#ffffff";
ctx.font = `bold ${Math.round(size * 0.62)}px "Microsoft YaHei","SimHei",sans-serif`; ctx.font = `bold ${Math.round(size * 0.62)}px "Microsoft YaHei","SimHei",sans-serif`;
ctx.textAlign = "center"; ctx.textAlign = "center";
ctx.textBaseline = "middle"; ctx.textBaseline = "middle";
ctx.fillText("工", size / 2, size / 2 + 1); ctx.fillText("工", size / 2, size / 2 + 1);
}
const imageData = ctx.getImageData(0, 0, size, size); const finalData = ctx.getImageData(0, 0, size, size);
return new Uint8Array(imageData.data.buffer); return new Uint8Array(finalData.data.buffer);
} }
/** 根据当前应用状态更新托盘图标和提示文字 */ /** 根据当前应用状态更新托盘图标和提示文字 */
@ -620,7 +712,7 @@ export const useMonitorStore = defineStore("monitor", () => {
tooltip = `${APP_NAME} - 监测中`; tooltip = `${APP_NAME} - 监测中`;
} }
const icon = await Image.new(makeTrayIconRGBA(...color), 32, 32); const icon = await Image.new(await makeTrayIconRGBA(...color), 32, 32);
await trayIconInstance.setIcon(icon); await trayIconInstance.setIcon(icon);
await trayIconInstance.setTooltip(tooltip); await trayIconInstance.setTooltip(tooltip);
} catch (e: any) { } catch (e: any) {
@ -686,7 +778,7 @@ export const useMonitorStore = defineStore("monitor", () => {
const menu = await buildTrayMenu(); const menu = await buildTrayMenu();
// 初始状态:灰色(未开始) // 初始状态:灰色(未开始)
const icon = await Image.new(makeTrayIconRGBA(158, 158, 158), 32, 32); const icon = await Image.new(await makeTrayIconRGBA(158, 158, 158), 32, 32);
trayIconInstance = await TrayIcon.new({ trayIconInstance = await TrayIcon.new({
icon, icon,
@ -763,6 +855,7 @@ export const useMonitorStore = defineStore("monitor", () => {
} }
checkNetworkStatus(); checkNetworkStatus();
checkForUpdates();
if (config.value.auto_monitor && rememberPassword.value) { if (config.value.auto_monitor && rememberPassword.value) {
const canAutoStart = loginMode.value === "token" const canAutoStart = loginMode.value === "token"
@ -795,12 +888,14 @@ export const useMonitorStore = defineStore("monitor", () => {
isLoggedIn, isMonitoring, isPostponed, postponeResumeTime, autoStartEnabled, isLoggedIn, isMonitoring, isPostponed, postponeResumeTime, autoStartEnabled,
logs, networkStatus, config, ticketCounts, logs, networkStatus, config, ticketCounts,
showCloseDialog, showCloseDialog,
showUpdateDialog, updateInfo, updateProgress, isUpdating,
// 方法 // 方法
initialize, cleanup, initialize, cleanup,
addLog, clearLogs, clearMessage, saveSettings, exportLogs, addLog, clearLogs, clearMessage, saveSettings, exportLogs,
startMonitoring, stopMonitoring, resumeMonitoring, manualCheck, startMonitoring, stopMonitoring, resumeMonitoring, manualCheck,
testNotification, checkNetworkStatus, updateConfig, testNotification, checkNetworkStatus, updateConfig,
minimizeToTray, handleCloseAction, minimizeToTray, handleCloseAction,
checkForUpdates, confirmUpdate, dismissUpdate,
}; };
}); });