Compare commits

...

2 Commits

Author SHA1 Message Date
wangjia 0ec609255c chore: release v1.0.29
Deploy / build-linux-web (push) Failing after 10m44s
Deploy / build-windows (push) Failing after 10m45s
Deploy / build-macos (push) Has been skipped
Deploy / build-android (push) Has been skipped
Deploy / build-ios (push) Has been skipped
Deploy / release-deploy (push) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:12:26 +08:00
wangjia 5ad1289419 chore: release v1.0.28
Deploy / build-windows (push) Successful in 1m54s
Deploy / build-linux-web (push) Failing after 13m1s
Deploy / build-macos (push) Has been skipped
Deploy / build-android (push) Has been skipped
Deploy / build-ios (push) Has been skipped
Deploy / release-deploy (push) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 09:35:01 +08:00
6 changed files with 108 additions and 31 deletions
+10
View File
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.29] - 2026-06-09
### 新功能
- Windows / macOS 桌面客户端现在限制只能运行一个实例;重复打开时弹窗提示「程序已在运行中」并自动退出
## [1.0.28] - 2026-06-09
### 修复
- macOS 客户端打包脚本路径错误导致 CI 打包失败,修正后 macOS 安装包可正常构建并发布
## [1.0.27] - 2026-06-09
### 改进
@@ -0,0 +1,27 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
// Keep the RAF open so the OS lock stays alive for the lifetime of the process.
// ignore: unused_element
RandomAccessFile? _raf;
/// Returns true if this process successfully acquired the single-instance lock.
/// Returns false if another instance is already running.
/// On Web / mobile / Linux the function always returns true (no restriction).
Future<bool> acquire() async {
if (kIsWeb) return true;
if (!Platform.isWindows && !Platform.isMacOS) return true;
try {
final dir = await getApplicationSupportDirectory();
final lockFile = File('${dir.path}/jiu.lock');
final raf = await lockFile.open(mode: FileMode.write);
await raf.lock(FileLock.exclusive);
_raf = raf;
return true;
} catch (_) {
return false;
}
}
+40
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -6,6 +7,7 @@ import 'package:flutter_web_plugins/url_strategy.dart';
import 'core/auth/auth_state.dart';
import 'core/config/app_info.dart';
import 'core/errors/error_reporter.dart';
import 'core/platform/single_instance.dart' as single_instance;
import 'core/router/app_router.dart';
import 'core/theme/app_theme.dart';
import 'providers/connectivity_provider.dart';
@@ -27,6 +29,12 @@ void main() {
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
if (!await single_instance.acquire()) {
runApp(const _AlreadyRunningApp());
return;
}
await AppInfo.load(); // 从配置文件加载「关于我们」品牌信息
runApp(const ProviderScope(child: JiuApp()));
},
@@ -43,6 +51,38 @@ void main() {
);
}
class _AlreadyRunningApp extends StatelessWidget {
const _AlreadyRunningApp();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Builder(
builder: (context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: const Text('岩美酒库已在运行'),
content: const Text('请勿重复打开,程序已在运行中。\n请在任务栏或 Dock 中找到已打开的窗口。'),
actions: [
TextButton(
onPressed: () => exit(0),
child: const Text('确定'),
),
],
),
);
});
return const Scaffold();
},
),
);
}
}
class JiuApp extends ConsumerStatefulWidget {
const JiuApp({super.key});
+1 -1
View File
@@ -25,7 +25,7 @@ cd ..
# ditto preserves symlinks, Unix permissions, and extended attributes (code-signing).
# Python's zipfile follows symlinks and drops permissions — do NOT use it for .app bundles.
mkdir -p dist
APP_SRC="client/build/macos/Build/Products/Release/jiu.app"
APP_SRC="client/build/macos/Build/Products/Release/jiu_client.app"
ditto -c -k --keepParent "${APP_SRC}" dist/jiu-macos-x64.zip
echo "Packaged dist/jiu-macos-x64.zip ($(du -sh dist/jiu-macos-x64.zip | cut -f1))"
+28 -28
View File
@@ -169,9 +169,9 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
<div class="header-meta">生成于 2026-06-09 · 真相源 todo/todo.json</div>
<div class="stats">
<div class="stat-pill"><strong>24</strong>全部</div>
<div class="stat-pill"><strong>7</strong>待开始</div>
<div class="stat-pill"><strong>6</strong>待开始</div>
<div class="stat-pill"><strong>0</strong>开发中</div>
<div class="stat-pill"><strong>3</strong>待验收</div>
<div class="stat-pill"><strong>4</strong>待验收</div>
<div class="stat-pill"><strong>14</strong>已验收</div>
</div>
</div>
@@ -206,7 +206,7 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
<div class="section-block" id="section-open">
<div class="section-title st-open" data-toggle="open">
📋 待开始 <span class="s-count">7</span>
📋 待开始 <span class="s-count">6</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-open">
@@ -284,30 +284,6 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
</div>
</li>
<li class="todo-card s-open"
data-id="27"
data-level="mid"
data-status="open"
data-tags="Windows,mac">
<div class="card-header">
<span class="item-title">桌面客户端单实例限制,禁止同时运行多个进程</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">Windows 和 macOS 客户端启动时检测是否已有实例运行;若有,则激活已有窗口并退出新进程。Flutter desktop 可通过 window_single_instance 或 dart:io 锁文件实现。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Windows">Windows</span> <span class="tag t-tag" data-tag="mac">mac</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="22"
data-level="low"
@@ -395,7 +371,7 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
</div>
<div class="section-block" id="section-done">
<div class="section-title st-done" data-toggle="done">
🔍 待验收 <span class="s-count">3</span>
🔍 待验收 <span class="s-count">4</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-done">
@@ -425,6 +401,30 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
</div>
</li>
<li class="todo-card s-done"
data-id="27"
data-level="mid"
data-status="done"
data-tags="Windows,mac">
<div class="card-header">
<span class="item-title">桌面客户端单实例限制,禁止同时运行多个进程</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<button class="reject-btn" data-id="27" data-title="桌面客户端单实例限制,禁止同时运行多个进程">拒绝验收</button>
</div>
</div>
<div class="item-desc">Windows 和 macOS 客户端启动时检测是否已有实例运行;若有,则激活已有窗口并退出新进程。Flutter desktop 可通过 window_single_instance 或 dart:io 锁文件实现。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Windows">Windows</span> <span class="tag t-tag" data-tag="mac">mac</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="25"
data-level="low"
+2 -2
View File
@@ -1,7 +1,7 @@
{
"meta": {
"title": "酒库管理系统 — 项目 TODO",
"updated_at": "2026-06-08T16:06:56.875Z"
"updated_at": "2026-06-09T04:11:47.912Z"
},
"seq": 27,
"items": [
@@ -377,7 +377,7 @@
"Windows",
"mac"
],
"status": "open",
"status": "done",
"created_at": "2026-06-08T15:58:11.759Z",
"done": false,
"completed_at": null,