From c49ee3b1dfcc97b91e6bffa8cdc38f310b2fe3f8 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Sat, 4 Jul 2026 23:57:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(client):=20=E5=BA=93=E5=AD=98=E6=8A=BD?= =?UTF-8?q?=E5=B1=89=E6=89=93=E5=8D=B0=E6=A0=87=E7=AD=BE=20+=20=E4=BF=AE?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E6=9E=84=E5=BB=BA=E7=89=88=E6=9C=AC=E5=8F=B7?= =?UTF-8?q?=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 商品编辑抽屉头部「预览」旁加「打印标签」(同入库管理:QR+品牌型号版本+ 门店信息 → LabelPreviewDialog;移动端无打印不渲染) - 修版本号 bug:appVersionProvider/更新检查改为优先 APP_VERSION dart-define (本地与 iOS 手工构建不改 pubspec,PackageInfo 恒为旧值 1.0.52,App 内 显示错且误弹更新横幅);版本比较容忍 -dev/+build 后缀(每段取前导数字) - local_test.sh:版本标记 -dev(如 v1.1.4-dev)明确区分本地包;新增 --ios 模式(开发签名构建 + devicectl 装 USB iPhone),双端同一版本注入链路 Co-Authored-By: Claude Fable 5 --- client/lib/providers/update_provider.dart | 26 +++++++-- client/lib/widgets/product_editor_drawer.dart | 37 ++++++++++++ scripts/local_test.sh | 57 +++++++++++++++---- 3 files changed, 104 insertions(+), 16 deletions(-) diff --git a/client/lib/providers/update_provider.dart b/client/lib/providers/update_provider.dart index 34d258a..bf0f4e8 100644 --- a/client/lib/providers/update_provider.dart +++ b/client/lib/providers/update_provider.dart @@ -95,8 +95,8 @@ class UpdateNotifier extends AsyncNotifier { final downloadUrls = rawUrls.map((k, v) => MapEntry(k, v?.toString() ?? '')); - final info = await PackageInfo.fromPlatform(); - final hasUpdate = _isNewer(latestVersion, info.version); + final current = (await currentAppVersion()).substring(1); // 去 v 前缀 + final hasUpdate = _isNewer(latestVersion, current); return AppUpdateInfo( latestVersion: latestVersion, @@ -123,16 +123,32 @@ class UpdateNotifier extends AsyncNotifier { } List _parse(String v) { - final parts = v.split('.').map((s) => int.tryParse(s) ?? 0).toList(); + // 每段取前导数字(容忍 1.1.4-dev / 1.1.4+7 等本地构建后缀) + final parts = v.split('.').map((s) { + final m = RegExp(r'^\d+').firstMatch(s); + return m == null ? 0 : int.parse(m.group(0)!); + }).toList(); while (parts.length < 3) parts.add(0); return parts; } } -// ── 版本号 Provider(供状态栏 / 门店信息面板使用)────────── -final appVersionProvider = FutureProvider((ref) async { +// ── 当前版本(单一真相)───────────────────────────────────── +// 优先编译期 APP_VERSION dart-define(CI 与本地脚本都传;本地构建不改 +// pubspec,PackageInfo 会是旧值);未定义时回退 PackageInfo。 +const _definedVersion = String.fromEnvironment('APP_VERSION'); + +Future currentAppVersion() async { + if (_definedVersion.isNotEmpty) { + return _definedVersion.startsWith('v') ? _definedVersion : 'v$_definedVersion'; + } final info = await PackageInfo.fromPlatform(); return 'v${info.version}'; +} + +// ── 版本号 Provider(供状态栏 / 门店信息面板使用)────────── +final appVersionProvider = FutureProvider((ref) async { + return currentAppVersion(); }); // ── 打开下载链接工具函数 ──────────────────────────────────── diff --git a/client/lib/widgets/product_editor_drawer.dart b/client/lib/widgets/product_editor_drawer.dart index b06de6a..0d11fbd 100644 --- a/client/lib/widgets/product_editor_drawer.dart +++ b/client/lib/widgets/product_editor_drawer.dart @@ -12,6 +12,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../core/config/app_config.dart'; +import '../core/utils/dialog_util.dart'; import '../core/responsive/responsive.dart'; import '../core/theme/app_chrome.g.dart'; import '../core/theme/app_dims.g.dart'; @@ -20,11 +21,14 @@ import '../models/product.dart'; import '../models/product_image.dart'; import '../providers/product_option_provider.dart'; import '../providers/product_provider.dart'; +import '../providers/shop_provider.dart' show shopInfoProvider; import 'ds/ds_atoms.dart'; import 'ds/grid_combo_cell.dart'; import 'ds/m_sheet.dart'; import 'searchable_option_field.dart'; import 'fullscreen_image_viewer.dart'; +import '../core/utils/label_data.dart'; +import 'label_preview_dialog.dart'; import 'write_guard.dart'; import 'ds/ds_toast.dart'; @@ -184,6 +188,14 @@ class _ProductEditorDrawerState extends ConsumerState<_ProductEditorDrawer> { onPressed: p == null || p.publicId.isEmpty ? null : () => context.push('/product/${p.publicId}')), + // 打印标签(同入库管理打印标签;移动端无打印——用户拍板) + if (!context.isMobile) ...[ + const SizedBox(width: 10), + DsButton('打印标签', + icon: LucideIcons.printer, + small: true, + onPressed: p == null ? null : () => _printLabel(p)), + ], const SizedBox(width: 10), InkWell( onTap: () => Navigator.of(context).pop(), @@ -615,6 +627,31 @@ class _ProductEditorDrawerState extends ConsumerState<_ProductEditorDrawer> { } // ── 保存:介绍正文 +(选了的话)介绍库文档 ── + /// 打印标签(同入库管理打印标签:QR + 品牌/型号/版本 + 门店信息)。 + Future _printLabel(Product p) async { + final qrBytes = + await ref.read(productRepositoryProvider).getQRCodeBytes(p.id); + final shopInfo = ref.read(shopInfoProvider).valueOrNull; + if (!mounted) return; + final label = LabelData( + productId: p.id, + qrBytes: qrBytes, + name: p.name, + code: p.code, + series: (p.series ?? '').isEmpty ? null : p.series, + spec: (p.spec ?? '').isEmpty ? null : p.spec, + remark: (p.remark ?? '').isEmpty ? null : p.remark, + shopName: shopInfo?.name ?? '', + shopAddress: shopInfo?.address ?? '', + shopPhone: shopInfo?.phone ?? '', + ); + if (!mounted) return; + showAppDialog( + context: context, + builder: (_) => LabelPreviewDialog(labels: [label]), + ); + } + Future _save() async { setState(() => _saving = true); try { diff --git a/scripts/local_test.sh b/scripts/local_test.sh index 22c857b..641c471 100755 --- a/scripts/local_test.sh +++ b/scripts/local_test.sh @@ -1,17 +1,19 @@ #!/usr/bin/env bash -# local_test.sh — 本地编译一份「指向线上后端」的 macOS app 并启动,用于发版前自测。 +# local_test.sh — 本地编译一份「指向线上后端」的 app 用于发版前自测。 # # 用法: -# sh scripts/local_test.sh # 编译 + 启动 -# sh scripts/local_test.sh --no-open # 只编译不启动 +# sh scripts/local_test.sh # 编译 macOS + 启动 +# sh scripts/local_test.sh --no-open # 只编译 macOS 不启动 +# sh scripts/local_test.sh --ios # 编译 iOS 并装到 USB iPhone(开发签名) # -# 后端固定指向生产 https://jiu.51yanmei.com(与 CI compile-macos.sh 一致; -# 2026-07-03 备案通过已回切 https 域名)。 +# 后端固定指向生产 https://jiu.51yanmei.com(与 CI compile 脚本一致)。 +# 版本号 = 最新 client-v* tag 的 patch+1 加 -dev 后缀(如 v1.1.4-dev), +# App 内显示走 APP_VERSION dart-define(update_provider.currentAppVersion), +# 与 CI 同一读取链路——本地构建不再显示 pubspec 旧版本(2026-07-04 修复)。 set -euo pipefail export PATH="/opt/homebrew/bin:$PATH" -# 版本号:取最新 client-v* tag 的 patch+1,纯展示用,取不到则回退 0.0.0-dev。 REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$REPO_ROOT" @@ -20,19 +22,52 @@ if [ -n "$LATEST_TAG" ]; then BASE_VER="${LATEST_TAG#client-v}" MAJOR_MINOR="${BASE_VER%.*}" PATCH="${BASE_VER##*.}" - VER="${MAJOR_MINOR}.$((PATCH + 1))" + VER="${MAJOR_MINOR}.$((PATCH + 1))-dev" else VER="0.0.0-dev" fi +DEFINES=( + "--dart-define=BASE_URL=https://jiu.51yanmei.com" + "--dart-define=PUBLIC_URL=https://jiu.51yanmei.com" + "--dart-define=APP_VERSION=v${VER}" +) + +if [ "${1:-}" = "--ios" ]; then + # ── iOS:开发签名构建 + devicectl 装到 USB iPhone ── + # Release 配置是 CI 的 Manual+App Store 签名,本机没有分发证书, + # 故 xcodebuild 用 Automatic + Apple Development 覆盖(不改工程文件)。 + echo "==> 编译 iOS(线上后端,版本 v${VER})" + cd client + flutter build ios --release --config-only "${DEFINES[@]}" + cd ios + CD_ID="$(xcrun devicectl list devices 2>/dev/null | grep 'available (paired)' | grep 'iPhone' | grep -oE '[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}' | head -1 || true)" + if [ -z "$CD_ID" ]; then + echo "!! 未检测到已配对 iPhone(插线并解锁后重试)" >&2 + exit 1 + fi + xcodebuild -workspace Runner.xcworkspace -scheme Runner -configuration Release \ + -destination "generic/platform=iOS" \ + CODE_SIGN_STYLE=Automatic DEVELOPMENT_TEAM=BYL4KQHMTN \ + CODE_SIGN_IDENTITY="Apple Development" PROVISIONING_PROFILE_SPECIFIER= \ + -allowProvisioningUpdates -allowProvisioningDeviceRegistration build \ + | grep -E "BUILD (SUCCEEDED|FAILED)" + APP_PATH="$(find "$HOME/Library/Developer/Xcode/DerivedData" -path '*Release-iphoneos/Runner.app' -maxdepth 6 2>/dev/null | head -1)" + if [ -z "$APP_PATH" ]; then + echo "!! 找不到构建产物 Runner.app" >&2 + exit 1 + fi + echo "==> 安装到 iPhone($CD_ID)" + xcrun devicectl device install app --device "$CD_ID" "$APP_PATH" | grep -m1 databaseSequenceNumber || true + echo "==> 完成:解锁手机打开 岩美酒库(版本 v${VER})" + exit 0 +fi + APP="client/build/macos/Build/Products/Release/jiu_client.app" echo "==> 编译 macOS app(线上后端,版本 v${VER})" cd client -flutter build macos --release \ - "--dart-define=BASE_URL=https://jiu.51yanmei.com" \ - "--dart-define=PUBLIC_URL=https://jiu.51yanmei.com" \ - "--dart-define=APP_VERSION=v${VER}" +flutter build macos --release "${DEFINES[@]}" cd "$REPO_ROOT" echo "==> 产物:$APP"