feat(client): 库存列表收尾——副标/toolbar 间距/移动端 golden
- 副标(还原原型 .head .sub):KPI 上方「共 N 个商品 · 数据实时」(千分位) - toolbar 间距 token 化:搜索框对齐原型 240,间隔走 AppDims.sp2/sp3 - 移动端 golden:窄屏(390)卡片流路径 ×三主题入回归闸 - 顺带修 DataTableCard 分页条窄屏横向溢出(mobile 隐藏每页选择器, golden 闸抓出的预存 bug) analyze 0 error · flutter test +175(库存桌面/移动各 3 golden 全过)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSKEiHsvauyxYUW2itzUXX
@@ -4,6 +4,7 @@ import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../core/config/app_config.dart';
|
||||
import '../../core/config/app_constants.dart';
|
||||
import '../../core/responsive/responsive.dart';
|
||||
@@ -528,6 +529,18 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// 副标(还原原型 .head .sub):商品总数 + 实时提示
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: context.tokens.bg,
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppDims.sp3, AppDims.sp3, AppDims.sp3, 0),
|
||||
child: Text(
|
||||
'共 ${NumberFormat.decimalPattern().format(result.total)} 个商品 · 数据实时',
|
||||
style:
|
||||
TextStyle(fontSize: AppDims.fsSm, color: context.tokens.muted),
|
||||
),
|
||||
),
|
||||
// KPI 卡(还原原型 .kpi):宽屏 4 等分,窄屏横向滚动
|
||||
Builder(builder: (context) {
|
||||
final inStockQty =
|
||||
@@ -659,7 +672,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
searchField,
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: AppDims.sp2),
|
||||
Row(
|
||||
children: [
|
||||
if (canCheck)
|
||||
@@ -716,11 +729,11 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
// 桌面:搜索置于最前,按钮成组靠左
|
||||
// 桌面:搜索置于最前(原型 searchbox 240),按钮成组靠左
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(width: 220, child: searchField),
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(width: 240, child: searchField),
|
||||
const SizedBox(width: AppDims.sp3),
|
||||
if (canCheck) ...[
|
||||
WriteGuard(
|
||||
child: OutlinedButton.icon(
|
||||
@@ -729,14 +742,14 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
label: const Text('发起盘点'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: AppDims.sp2),
|
||||
],
|
||||
OutlinedButton.icon(
|
||||
onPressed: doExport,
|
||||
icon: const Icon(Icons.download, size: 16),
|
||||
label: const Text('导出'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: AppDims.sp2),
|
||||
ColumnToggleButton(
|
||||
columns: _colDefs,
|
||||
hidden: hidden,
|
||||
@@ -745,7 +758,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
ColumnPrefs.save(_screenId, v);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: AppDims.sp2),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () =>
|
||||
ref.read(inventoryListProvider.notifier).reload(),
|
||||
|
||||
@@ -83,6 +83,8 @@ class _DataTableCardState extends State<DataTableCard> {
|
||||
pageSize: widget.pageSize,
|
||||
onPageChanged: widget.onPageChanged,
|
||||
onPageSizeChanged: widget.onPageSizeChanged,
|
||||
// 窄屏隐藏「每页 N 条」选择器,给翻页让位,避免横向溢出
|
||||
compact: context.isMobile,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -207,6 +209,7 @@ class _PaginationBar extends StatelessWidget {
|
||||
final int pageSize;
|
||||
final ValueChanged<int>? onPageChanged;
|
||||
final ValueChanged<int>? onPageSizeChanged;
|
||||
final bool compact;
|
||||
|
||||
static const _pageSizeOptions = [10, 20, 50, 100];
|
||||
|
||||
@@ -216,6 +219,7 @@ class _PaginationBar extends StatelessWidget {
|
||||
required this.pageSize,
|
||||
this.onPageChanged,
|
||||
this.onPageSizeChanged,
|
||||
this.compact = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -225,8 +229,8 @@ class _PaginationBar extends StatelessWidget {
|
||||
final effectiveSize = _pageSizeOptions.contains(pageSize) ? pageSize : _pageSizeOptions.first;
|
||||
return Row(
|
||||
children: [
|
||||
// 每页条数选择器
|
||||
if (onPageSizeChanged != null) ...[
|
||||
// 每页条数选择器(窄屏隐藏,避免溢出)
|
||||
if (onPageSizeChanged != null && !compact) ...[
|
||||
Text('每页', style: TextStyle(fontSize: 13, color: context.tokens.muted)),
|
||||
const SizedBox(width: 4),
|
||||
DropdownButtonHideUnderline(
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 41 KiB |
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:jiu_client/core/models/page_result.dart';
|
||||
@@ -70,20 +71,32 @@ class _FakeSpecNotifier extends ProductSpecListNotifier {
|
||||
Future<List<ProductSpecOption>> build() async => const [];
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
goldenAcrossThemes(
|
||||
'inventory list 整屏',
|
||||
goldenPrefix: 'inventory_list',
|
||||
child: () => const Scaffold(body: InventoryListScreen()),
|
||||
overrides: () => [
|
||||
List<Override> _overrides() => [
|
||||
inventoryListProvider.overrideWith(() => _FakeInvNotifier()),
|
||||
productSeriesListProvider.overrideWith(() => _FakeSeriesNotifier()),
|
||||
productSpecListProvider.overrideWith(() => _FakeSpecNotifier()),
|
||||
isReadonlyProvider.overrideWithValue(false),
|
||||
],
|
||||
];
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
// 桌面:宽屏表格路径
|
||||
goldenAcrossThemes(
|
||||
'inventory list 整屏(桌面)',
|
||||
goldenPrefix: 'inventory_list',
|
||||
child: () => const Scaffold(body: InventoryListScreen()),
|
||||
overrides: _overrides,
|
||||
logical: const Size(1280, 900),
|
||||
);
|
||||
|
||||
// 移动:窄屏(<600)→ context.isMobile → MobileListCard 卡片流路径
|
||||
goldenAcrossThemes(
|
||||
'inventory list 整屏(移动)',
|
||||
goldenPrefix: 'inventory_list_mobile',
|
||||
child: () => const Scaffold(body: InventoryListScreen()),
|
||||
overrides: _overrides,
|
||||
logical: const Size(390, 1400),
|
||||
);
|
||||
}
|
||||
|
||||