feat:回退版本报错的问题

This commit is contained in:
dzc committed 2026-09-22 16:08:42 +08:00
1 parent 383ea2ad4b
commit 2d6603dfd5
10 files changed
+424 -241

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ flutter pub run build_runner build --delete-conflicting-outputs~
## flutter打包
```
flutter build apk --build-name 1.5.0 --build-number 150
flutter build apk --build-name 1.5.1 --build-number 151
--build-name为版本号(展示)
--build-number为版本号(内部比较)
```
@@ -413,9 +413,15 @@ abstract class DatabaseMigration {
/// 版本14为打印任务补充后端指令流列,指令打印模式可离线重试且不改变出纸内容。
///
/// 列可空且不回填:旧任务与旧后端无指令,指令模式下回退 invoiceUrl 图片打印。
/// 外部存储旧库可能已带该列(中间态构建建库、降级重建),重复 ALTER 会中断启动,
/// 因此先查列再添加,保证跨版本安装可重入。
static Migration migration13To14 = Migration(13, 14, (database) async {
await database
.execute('ALTER TABLE print_history ADD COLUMN printCmd TEXT');
final columns = await database.rawQuery('PRAGMA table_info(print_history)');
final hasPrintCmd = columns.any((column) => column['name'] == 'printCmd');
if (!hasPrintCmd) {
await database
.execute('ALTER TABLE print_history ADD COLUMN printCmd TEXT');
}
});
/// 按起始版本升序返回全部迁移,Floor 打开旧数据库时据此连续升级到当前版本。
+2 -2
View File
@@ -15,8 +15,8 @@ class Constants {
static const pwdEncKey = 'pigxpigxpigxpigx';
/// 服务 url 线上
static const apiUrl = 'https://dzc-test-1.fy.takin.cc';
// static const apiUrl = 'https://www.zianxin.com/api';
// static const apiUrl = 'https://dzc-test-1.fy.takin.cc';
static const apiUrl = 'https://www.zianxin.com/api';
// static const apiUrl = 'https://dzc-test-1.fy.takin.cc';
@@ -1686,7 +1686,7 @@ class _CheckoutCartItemSummary extends StatelessWidget {
/// 套餐展开/收起回调;其热区与右侧数量步进器相互独立。
final VoidCallback? onComboToggle;
/// 构建固定图片、商品信息和右对齐小计金额。
/// 构建整行名称、固定图片、商品信息和右对齐小计金额。
@override
Widget build(BuildContext context) {
final theme = FTheme.of(context);
@@ -1697,6 +1697,30 @@ class _CheckoutCartItemSummary extends StatelessWidget {
: 'x${item.productNumber}';
final stockText = item.stock == null ? '库存未知' : '库存 ${item.stock}';
final stockUnavailable = item.stock == 0;
// 名称+类型标签独占卡片一整行(左缘到右缘),不再与图片、步进器同行
// 抢宽度;超长名称等比缩小兜底(与食堂商品卡一致),保证永远单行。
final nameRow = <Widget>[
LayoutBuilder(
builder: (context, constraints) => FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: SizedBox(
width: constraints.maxWidth,
child: Text(
item.productName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.typography.base.copyWith(
color: XfjDesignTokens.ink,
fontWeight: FontWeight.w700,
height: 1.3,
),
),
),
),
).expanded(),
ProductTypeTag(isCombo: combo),
].toRowSpace(space: 8.w);
final productDetails = <Widget>[
_CheckoutProductImage(
key: ValueKey(
@@ -1708,20 +1732,6 @@ class _CheckoutCartItemSummary extends StatelessWidget {
height: 64.h,
),
<Widget>[
<Widget>[
Text(
item.productName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: theme.typography.base.copyWith(
color: XfjDesignTokens.ink,
fontWeight: FontWeight.w700,
height: 1.3,
),
).expanded(),
ProductTypeTag(isCombo: combo),
].toRowSpace(space: 8.w),
SizedBox(height: 7.h),
<Widget>[
Text(
'单价 ${_paymentMoney(item.productPrice)}',
@@ -1822,16 +1832,43 @@ class _CheckoutCartItemSummary extends StatelessWidget {
),
);
return <Widget>[
productDetailsAction.expanded(),
if (editable)
<Widget>[
_CheckoutQuantityStepper(
controller: controller,
item: item,
updating: updating,
onChanged: (value) => onQuantityChanged!(item, value),
pendingQuantity: pendingQuantity,
),
nameRow,
SizedBox(height: 7.h),
<Widget>[
productDetailsAction.expanded(),
if (editable)
<Widget>[
_CheckoutQuantityStepper(
controller: controller,
item: item,
updating: updating,
onChanged: (value) => onQuantityChanged!(item, value),
pendingQuantity: pendingQuantity,
),
<Widget>[
Text(
_paymentMoney(item.totalPrice),
key: ValueKey(
'checkout-item-total-${item.productServiceUniqueCode}',
),
textAlign: TextAlign.right,
style: theme.typography.base.copyWith(
color: XfjDesignTokens.ink,
fontWeight: FontWeight.w800,
fontFeatures: const [FontFeature.tabularFigures()],
),
),
Text(
'商品小计',
style: theme.typography.xs.copyWith(
color: XfjDesignTokens.muted,
),
),
].toColumn(crossAxisAlignment: CrossAxisAlignment.end).width(
88.w < 88 ? 88 : 88.w,
),
].toRowSpace(space: 8.w).width(240.w < 240 ? 240 : 240.w)
else
<Widget>[
Text(
_paymentMoney(item.totalPrice),
@@ -1847,35 +1884,12 @@ class _CheckoutCartItemSummary extends StatelessWidget {
),
Text(
'商品小计',
style: theme.typography.xs.copyWith(
color: XfjDesignTokens.muted,
),
style: theme.typography.xs.copyWith(color: XfjDesignTokens.muted),
),
].toColumn(crossAxisAlignment: CrossAxisAlignment.end).width(
88.w < 88 ? 88 : 88.w,
),
].toRowSpace(space: 8.w).width(240.w < 240 ? 240 : 240.w)
else
<Widget>[
Text(
_paymentMoney(item.totalPrice),
key: ValueKey(
'checkout-item-total-${item.productServiceUniqueCode}',
),
textAlign: TextAlign.right,
style: theme.typography.base.copyWith(
color: XfjDesignTokens.ink,
fontWeight: FontWeight.w800,
fontFeatures: const [FontFeature.tabularFigures()],
),
),
Text(
'商品小计',
style: theme.typography.xs.copyWith(color: XfjDesignTokens.muted),
),
].toColumn(crossAxisAlignment: CrossAxisAlignment.end).width(104.w),
].toColumn(crossAxisAlignment: CrossAxisAlignment.end).width(104.w),
].toRowSpace(space: 12.w, crossAxisAlignment: CrossAxisAlignment.center),
]
.toRowSpace(space: 12.w, crossAxisAlignment: CrossAxisAlignment.center)
.toColumn(crossAxisAlignment: CrossAxisAlignment.stretch)
.padding(horizontal: 8.w, vertical: 12.h);
}
}
@@ -163,7 +163,7 @@ mixin ReservationMenuQueryShortcutActions {
return;
}
unawaited(
shortcutSpeaker('已切换为${target.title},再次点击开始${target.title}'),
shortcutSpeaker('已切换为${target.title}再次点击开始${target.title}识别'),
);
unawaited(selectMethod(target));
}
+182 -182
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,7 +1,7 @@
name: pj_supermarket_cloud
description: "资安信智慧后勤-测试"
publish_to: "none"
version: 1.5.0+150
version: 1.5.1+151
environment:
sdk: ^3.6.0
+30
View File
@@ -143,4 +143,34 @@ void main() {
throwsA(isA<DatabaseException>()),
);
});
test('13到14迁移补指令列且列已存在时可重入', () async {
final database =
await databaseFactoryFfi.openDatabase(inMemoryDatabasePath);
addTearDown(database.close);
await database.execute('''
CREATE TABLE print_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
orderId TEXT NOT NULL,
serviceOrderId TEXT NOT NULL,
servicePrintId TEXT NOT NULL,
invoiceUrl TEXT NOT NULL,
status TEXT NOT NULL,
printTime TEXT,
syncTime TEXT,
createTime TEXT NOT NULL
)
''');
await DatabaseMigration.migration13To14.migrate(database);
final columns = await database.rawQuery('PRAGMA table_info(print_history)');
expect(
columns.map((column) => column['name']),
contains('printCmd'),
);
// 外部存储旧库可能已带该列(中间态构建建库、降级重建),重复迁移不得抛 duplicate column。
await DatabaseMigration.migration13To14.migrate(database);
});
}
@@ -0,0 +1,133 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show FontLoader;
import 'package:flutter_test/flutter_test.dart';
import 'package:forui/forui.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
Future<void> _loadCjkFont() async {
final file = File('/System/Library/Fonts/Supplemental/Arial Unicode.ttf');
final bytes = await file.readAsBytes();
final loader = FontLoader('PreviewCJK')
..addFont(Future.value(ByteData.view(bytes.buffer)));
await loader.load();
}
/// 以结算弹框购物车列的真实宽度预算复刻商品卡两行结构:
/// 名称+类型标签独占整卡一行,图片/单价/步进器/小计在第二行,
/// 固化"名称行不被步进器挤压"的布局契约。
void main() {
setUpAll(_loadCjkFont);
testWidgets('结算条目名称行单行缩放布局', (tester) async {
tester.view.physicalSize = const Size(1920, 1080);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.reset);
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(1920, 1080),
minTextAdapt: true,
builder: (_, __) {
final theme = FThemeData.inherit(
colorScheme: FThemes.zinc.light.colorScheme,
typography: const FTypography(),
);
return FTheme(
data: theme,
child: MaterialApp(
theme: ThemeData(fontFamily: 'PreviewCJK'),
home: Scaffold(
backgroundColor: XfjDesignTokens.surface,
body: Center(
child: Container(
// 结算弹框 42% 购物车列下的单条卡片内容宽
width: 640,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: XfjDesignTokens.primarySoft,
border: Border.all(
color: XfjDesignTokens.primary.withValues(alpha: 0.45),
),
borderRadius: BorderRadius.circular(12),
),
child: <Widget>[
// 名称行:整卡宽,左名称右标签
<Widget>[
LayoutBuilder(
builder: (context, constraints) => FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: SizedBox(
width: constraints.maxWidth,
child: Text(
'D套餐-葱油鸡腿饭套餐',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.typography.base.copyWith(
color: XfjDesignTokens.ink,
fontWeight: FontWeight.w700,
height: 1.3,
),
),
),
),
).expanded(),
const ProductTypeTag(isCombo: true),
].toRowSpace(space: 8.w),
SizedBox(height: 7.h),
// 详情行:图片 + 单价信息 + 步进器/小计
Row(
children: [
Container(
width: 64.w,
height: 64.w,
color: XfjDesignTokens.control,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'单价 ¥9.20 库存 999+ 套餐 4 样',
style: theme.typography.xs.copyWith(
color: XfjDesignTokens.muted,
),
),
],
),
),
SizedBox(width: 12.w),
Container(
width: 240.w,
height: 64.w,
alignment: Alignment.center,
color: XfjDesignTokens.canvas,
child: Text(
'步进器+小计区 240.w',
style: theme.typography.xs,
),
),
],
),
].toColumn(crossAxisAlignment: CrossAxisAlignment.stretch),
),
),
),
),
);
},
),
);
await expectLater(
find.byType(Scaffold),
matchesGoldenFile('goldens/checkout_name_row.png'),
);
});
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB