chore: release v1.0.18
Deploy / build-linux-web (push) Successful in 53s
Deploy / build-windows (push) Successful in 1m48s
Deploy / build-macos (push) Successful in 1m17s
Deploy / build-android (push) Successful in 4m13s
Deploy / build-ios (push) Successful in 9s
Deploy / release-deploy (push) Successful in 1m37s

移动端响应式适配(抽屉导航/列表卡片/弹窗自适应)、Android 正式签名与 APK 发布、
iOS(TestFlight) 工程与 CI、多平台构建流水线、相关文档同步。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-07 07:55:33 +08:00
parent 94f0fb2095
commit 480ce836bb
81 changed files with 3096 additions and 366 deletions
+147 -53
View File
@@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
import 'dart:async';
import '../../core/auth/auth_state.dart';
import '../../core/config/app_config.dart';
import '../../core/responsive/responsive.dart';
import '../../core/theme/app_theme.dart';
import '../../providers/connectivity_provider.dart';
import '../../providers/shop_provider.dart';
@@ -24,6 +25,7 @@ class _AppShellState extends ConsumerState<AppShell> {
bool _sidebarExpanded = true;
final String _loginTime = DateFormat('HH:mm:ss').format(DateTime.now());
bool _forceDialogShown = false;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
void _showForceUpdateDialog(
BuildContext context, AppUpdateInfo info) {
@@ -69,11 +71,86 @@ class _AppShellState extends ConsumerState<AppShell> {
_NavItem(icon: Icons.info_outline, label: '关于我们', path: '/about'),
];
/// 窄屏(手机)侧滑抽屉导航,容纳全部菜单项。
Widget _buildDrawer(
BuildContext context, AuthUser? user, String location) {
final shopAsync = ref.watch(shopInfoProvider);
final shopName = shopAsync.valueOrNull?.name ?? user?.shopNo ?? '';
final logoUrl = shopAsync.valueOrNull?.logoUrl ?? '';
return Drawer(
child: Container(
color: AppTheme.primaryDark,
child: SafeArea(
child: Column(
children: [
// 抽屉头部:门店 Logo + 名称 + 编号
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16),
color: AppTheme.primary,
child: Row(
children: [
_ShopLogo(
logoUrl: logoUrl, shopName: shopName, size: 40),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
shopName.isEmpty ? '' : shopName,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
if (user != null) ...[
const SizedBox(height: 2),
Text(
'${user.shopNo} · ${user.username}',
style: const TextStyle(
color: Colors.white70, fontSize: 12),
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 8),
children: _navItems.map((item) {
final isActive = location.startsWith(item.path);
return _SidebarItem(
item: item,
isActive: isActive,
expanded: true,
onTap: () {
Navigator.pop(context); // 关闭抽屉
context.go(item.path);
},
);
}).toList(),
),
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
final user = ref.watch(authStateProvider).user;
final isOnline = ref.watch(connectivityProvider);
final location = GoRouterState.of(context).matchedLocation;
final isMobile = context.isMobile;
final sidebarWidth = _sidebarExpanded ? 200.0 : 56.0;
final updateNotifier = ref.watch(updateProvider.notifier);
final updateInfo = ref.watch(updateProvider).valueOrNull;
@@ -82,6 +159,8 @@ class _AppShellState extends ConsumerState<AppShell> {
return SelectionArea(
child: Scaffold(
key: _scaffoldKey,
drawer: isMobile ? _buildDrawer(context, user, location) : null,
body: Column(
children: [
// Top Bar
@@ -93,41 +172,54 @@ class _AppShellState extends ConsumerState<AppShell> {
children: [
IconButton(
icon: Icon(
_sidebarExpanded ? Icons.menu_open : Icons.menu,
isMobile
? Icons.menu
: (_sidebarExpanded ? Icons.menu_open : Icons.menu),
color: Colors.white),
onPressed: () =>
setState(() => _sidebarExpanded = !_sidebarExpanded),
tooltip: _sidebarExpanded ? '收起侧边栏' : '展开侧边栏',
onPressed: () {
if (isMobile) {
_scaffoldKey.currentState?.openDrawer();
} else {
setState(() => _sidebarExpanded = !_sidebarExpanded);
}
},
tooltip: isMobile
? '菜单'
: (_sidebarExpanded ? '收起侧边栏' : '展开侧边栏'),
),
const SizedBox(width: 4),
_ShopButton(user: user, version: appVersion),
const Spacer(),
if (user != null) ...[
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => _showShopPanel(context, user, version: appVersion),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.business,
color: Colors.white70, size: 14),
const SizedBox(width: 4),
Text(user.shopNo,
style: const TextStyle(
color: Colors.white70, fontSize: 13)),
],
// 窄屏隐藏门店号/用户名文字块,仅保留用户菜单,避免顶栏拥挤
if (!isMobile) ...[
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () =>
_showShopPanel(context, user, version: appVersion),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.business,
color: Colors.white70, size: 14),
const SizedBox(width: 4),
Text(user.shopNo,
style: const TextStyle(
color: Colors.white70, fontSize: 13)),
],
),
),
),
),
const SizedBox(width: 20),
const Icon(Icons.person_outline,
color: Colors.white70, size: 14),
const SizedBox(width: 4),
Text(user.username,
style: const TextStyle(
color: Colors.white70, fontSize: 13)),
const SizedBox(width: 8),
const SizedBox(width: 20),
const Icon(Icons.person_outline,
color: Colors.white70, size: 14),
const SizedBox(width: 4),
Text(user.username,
style: const TextStyle(
color: Colors.white70, fontSize: 13)),
const SizedBox(width: 8),
],
PopupMenuButton<String>(
icon: const Icon(Icons.keyboard_arrow_down,
color: Colors.white70),
@@ -172,32 +264,33 @@ class _AppShellState extends ConsumerState<AppShell> {
Expanded(
child: Row(
children: [
// Sidebar
AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
width: sidebarWidth,
color: AppTheme.primaryDark,
child: Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 8),
children: _navItems.map((item) {
final isActive =
location.startsWith(item.path);
return _SidebarItem(
item: item,
isActive: isActive,
expanded: _sidebarExpanded,
onTap: () => context.go(item.path),
);
}).toList(),
// Sidebar(仅宽屏;窄屏改用 Drawer
if (!isMobile)
AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
width: sidebarWidth,
color: AppTheme.primaryDark,
child: Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 8),
children: _navItems.map((item) {
final isActive =
location.startsWith(item.path);
return _SidebarItem(
item: item,
isActive: isActive,
expanded: _sidebarExpanded,
onTap: () => context.go(item.path),
);
}).toList(),
),
),
),
],
],
),
),
),
// Content area
Expanded(
child: Column(
@@ -278,7 +371,8 @@ class _AppShellState extends ConsumerState<AppShell> {
),
),
Expanded(child: widget.child),
// Status bar
// Status bar(仅宽屏;窄屏隐藏,离线已有顶部横幅提示)
if (!isMobile)
LayoutBuilder(
builder: (context, constraints) {
final w = constraints.maxWidth;
@@ -515,7 +609,7 @@ void _showShopPanel(BuildContext context, AuthUser u, {String version = 'v1.0.0'
builder: (ctx) => Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: SizedBox(
width: 360,
width: ctx.dialogWidth(360),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,