Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ec609255c | |||
| 5ad1289419 |
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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});
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user