feat: Phase 4 收尾 + Phase 5 打包(首启向导/开机自启/URL scheme 闭环/mac dmg)

Phase 4 剩余三项:
- 首次引导向导(setup/first_run + ui/setup_wizard):服务自检(含 C# 助手共存
  提示)→ 打印机检测与 HiTi 驱动安装指引(官网链接/重新检测,mac 与 win 文案
  分支,允许无打印机完成)→ 开机自启开关 + scheme 注册;侧栏底部常驻设置区
  (自启开关 + 重新运行引导)
- 开机自启(setup/autostart):mac 写 LaunchAgent(RunAtLoad 一次性,不用
  KeepAlive——托盘退出不能被拉回);Windows 走 HKCU Run(reg 命令免 win32 依赖)
- URL scheme 闭环:单实例唤醒端口 38210(setup/single_instance)——zaxprint://
  唤起或重复双击启动第二进程时通知常驻实例弹窗后自退,跨平台一致(弃用
  app_links 的 mac 事件转发不确定性);Windows 启动时幂等注册 HKCU scheme;
  前端 cardPrinter.ts 加 launchAgent()(zaxprint://open),开卡抽屉离线时显示
  "启动打印助手"按钮,点击唤起后 1s×6 轮询自动上线(biz 前端改动待用户提交)

Phase 5:
- tool/package_macos.sh:构建 → ad-hoc 就地签名(--deep,勿加 --options=runtime:
  hardened runtime 库校验会因 ad-hoc 无 Team ID 拒载 framework,实测踩坑)→
  hdiutil dmg;实测产出 22MB dmg + 签名后冒烟通过
- windows/installer.iss:Inno Setup 安装包脚本(快捷方式/桌面图标/开机自启
  Tasks/zaxprint scheme 注册与卸载清理/HiTi SDK DLL 随 Release 目录打包)

依赖 +url_launcher +shared_preferences;13 测试全过、analyze 零问题。
This commit is contained in:
dzc committed 2026-09-21 13:51:42 +08:00
1 parent a7cc69bb92
commit 3ecac9a302
15 files changed
+934 -51

No files matched your search

+80 -1
View File
@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import '../agent.dart';
import '../setup/autostart.dart';
import '../setup/first_run.dart';
import 'routes.dart';
import 'setup_wizard.dart';
/// 应用外壳:左侧插件菜单 + 右侧插件页面(菜单项来自路由注册表)。
class AppShell extends StatefulWidget {
@@ -16,9 +19,50 @@ class AppShell extends StatefulWidget {
class _AppShellState extends State<AppShell> {
int _selectedIndex = 0;
bool _autostart = false;
bool _autostartLoaded = false;
Agent get _agent => widget.agent;
@override
void initState() {
super.initState();
_loadAutostart();
_maybeShowWizard();
}
Future<void> _loadAutostart() async {
final enabled = await Autostart.isEnabled();
if (mounted) {
setState(() {
_autostart = enabled;
_autostartLoaded = true;
});
}
}
Future<void> _toggleAutostart(bool value) async {
setState(() => _autostart = value);
try {
value ? await Autostart.enable() : await Autostart.disable();
} catch (_) {
if (mounted) await _loadAutostart(); // 失败回读真实状态
}
}
Future<void> _maybeShowWizard() async {
if (await FirstRun.isDone()) return;
if (!mounted) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => SetupWizard(agent: _agent, onFinished: _loadAutostart),
);
});
}
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
@@ -100,8 +144,43 @@ class _AppShellState extends State<AppShell> {
),
Divider(height: 1, thickness: 1, color: scheme.outlineVariant),
// 设置区:开机自启 + 重新运行引导
Padding(
padding: const EdgeInsets.fromLTRB(16, 10, 16, 12),
padding: const EdgeInsets.fromLTRB(8, 6, 8, 0),
child: SwitchListTile(
dense: true,
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
value: _autostartLoaded && _autostart,
onChanged: Autostart.isSupported ? _toggleAutostart : null,
title: Text('开机自动启动', style: Theme.of(context).textTheme.bodySmall),
secondary: Icon(Icons.power_settings_new,
size: 18, color: scheme.onSurfaceVariant),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 8, 4),
child: Align(
alignment: Alignment.centerLeft,
child: TextButton.icon(
onPressed: () {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => SetupWizard(agent: _agent, onFinished: _loadAutostart),
);
},
icon: const Icon(Icons.restart_alt, size: 14),
label: const Text('重新运行引导'),
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.bodySmall,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 10),
child: Text(
'v1.0.0',
style: Theme.of(context)