feat(client): 往来/基础数据/商品详情窄屏对齐移动原型(卡片流/图标徽章/sheet 表单)

- 往来单位:窄屏隐藏页内大标题(标题在壳顶栏);MSearchRow 搜索行;
  类型 pills 全部/供应商/客户/两者(两者=客户端过滤,服务端 FIND_IN_SET 无独立值);
  卡片流类型+状态徽章换图标变体、mc-sub mono 编码
- 基础数据:窄屏隐藏大标题;各 tab 搜索行换 MSearchRow(清空 × 并入,重置钮撤);
  档案/字典卡片副行 mono 编码;系列/规格/仓库 新增编辑窄屏走 showMSheet 底部 sheet 轻表单
- 商品详情:窄屏 padding 收紧 sp4;信息卡 Wrap→单列;图片缩略图行窄屏横滚防溢出;
  二维码弹窗宽度走 context.dialogWidth
- golden:新增 m_partners / m_products / m_products_series(390×844 @2x 三主题);
  重生成既有 partners_mobile / products_archive_mobile 基准(桌面 golden 零漂移)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-04 12:13:38 +08:00
parent be9afcf68d
commit 4287f5a876
20 changed files with 665 additions and 260 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 156 KiB

@@ -0,0 +1,109 @@
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';
import 'package:jiu_client/core/auth/auth_state.dart';
import 'package:jiu_client/models/partner.dart';
import 'package:jiu_client/providers/partner_provider.dart';
import 'package:jiu_client/screens/partners/partners_screen.dart';
import '../support/golden_harness.dart';
/// 往来单位窄屏 golden × 三主题(390×844 @2x,对齐移动原型 m-partners.html):
/// 页内大标题隐藏(标题在壳顶栏)+ MSearchRow 搜索行 + 类型 pills
/// (全部/供应商/客户/两者)+ 卡片流(类型/状态图标徽章、mono 编码副行)。
/// 更新基准:flutter test --update-goldens test/golden/partners_mobile_golden_test.dart
// 行数据对齐 m-partners.html 的 PARTNERS(供应商/客户/两者、启用/停用齐备)。
const _partners = [
Partner(
id: 1,
code: 'GYS001',
name: '鼎晟供应链',
type: 'supplier',
contact: '张伟',
phone: '138-0011-2233'),
Partner(
id: 2,
code: 'KH002',
name: '城东烟酒行',
type: 'customer',
contact: '李建国',
phone: '139-2200-7788'),
Partner(
id: 3,
code: 'GYS003',
name: '贵州茅台经销',
type: 'supplier',
contact: '王芳',
phone: '137-6655-4321'),
Partner(
id: 4,
code: 'KH004',
name: '金樽会所',
type: 'customer',
contact: '陈静',
phone: '136-2211-9900'),
Partner(
id: 5,
code: 'KH005',
name: '喜宴婚庆',
type: 'customer',
contact: '周丽',
phone: '139-3344-5566',
status: 'disabled'),
Partner(
id: 6,
code: 'GYS006',
name: '川酒集团批发',
type: 'supplier,customer',
contact: '赵明',
phone: '138-7766-5544'),
];
// 原型 bal(应收/应付)
const _balances = <int, ({double recv, double pay})>{
1: (recv: 0, pay: 52800),
2: (recv: 86200, pay: 0),
3: (recv: 0, pay: 0),
4: (recv: 41000, pay: 0),
5: (recv: 0, pay: 0),
6: (recv: 0, pay: 0),
};
class _FakePartnersNotifier extends PartnerListNotifier {
@override
Future<PageResult<Partner>> build() async =>
const PageResult(data: _partners, total: 6, page: 1, pageSize: 1000);
@override
void setPage(int page) {}
@override
void setPageSize(int pageSize) {}
@override
void setKeyword(String keyword) {}
@override
void setType(String? t) {}
@override
void reload() {}
}
List<Override> _overrides() => [
partnersScreenProvider.overrideWith(() => _FakePartnersNotifier()),
partnerBalancesProvider.overrideWith((ref) async => _balances),
isReadonlyProvider.overrideWithValue(false),
];
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
SharedPreferences.setMockInitialValues({});
goldenAcrossThemes(
'partners 窄屏(手机视口)',
goldenPrefix: 'm_partners',
child: () => const Scaffold(body: PartnersScreen()),
overrides: _overrides,
logical: const Size(390, 844),
dpr: 2,
);
}
@@ -0,0 +1,146 @@
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';
import 'package:jiu_client/core/auth/auth_state.dart';
import 'package:jiu_client/models/product.dart';
import 'package:jiu_client/models/product_option.dart';
import 'package:jiu_client/models/shop.dart';
import 'package:jiu_client/providers/product_option_provider.dart';
import 'package:jiu_client/providers/product_provider.dart';
import 'package:jiu_client/providers/shop_provider.dart';
import 'package:jiu_client/screens/products/products_screen.dart';
import '../support/golden_harness.dart';
/// 基础数据窄屏 golden × 三主题(390×844 @2x,对齐移动原型 m-products.html):
/// 页内大标题隐藏 + 分段 tab 横滚(商品档案/介绍/系列/规格/仓库)+
/// MSearchRow 搜索行 + 卡片流(mono 编码副行)。两组:商品档案 tab / 系列字典 tab。
/// 更新基准:flutter test --update-goldens test/golden/products_mobile_golden_test.dart
// 行数据照抄 m-products.html 的 PRODUCTS(前 6 行足够填满手机视口)。
const _products = [
Product(
id: 1,
code: 'MT-FT-500',
name: '茅台 飞天',
unit: '',
series: '飞天',
spec: '500ml×6 · 53°',
purchasePrice: 2680),
Product(
id: 2,
code: 'MT-FT-100',
name: '茅台 飞天',
unit: '',
series: '飞天',
spec: '100ml×24 · 53°',
purchasePrice: 620),
Product(
id: 3,
code: 'WLY-PW-500',
name: '五粮液 普五',
unit: '',
series: '第八代',
spec: '500ml×6 · 52°',
purchasePrice: 1050),
Product(
id: 4,
code: 'GJ-1573-500',
name: '国窖 1573',
unit: '',
series: '经典装',
spec: '500ml×6 · 52°',
purchasePrice: 980),
Product(
id: 5,
code: 'JNC-SJ-500',
name: '剑南春 水晶剑',
unit: '',
series: '水晶剑',
spec: '500ml×6 · 52°',
purchasePrice: 438),
Product(
id: 6,
code: 'YH-M6-500',
name: '洋河 梦之蓝',
unit: '',
series: 'M6+',
spec: '550ml×6 · 45°',
purchasePrice: 620),
];
// 系列字典(m-products DICTS.series)。
const _series = [
ProductSeriesOption(id: 1, code: 'XL001', name: '飞天', remark: '茅台主力系列'),
ProductSeriesOption(id: 2, code: 'XL002', name: '第八代', remark: '五粮液当前主力'),
ProductSeriesOption(id: 3, code: 'XL003', name: '经典装', remark: '国窖 1573 经典'),
ProductSeriesOption(id: 4, code: 'XL004', name: '水晶剑', remark: '剑南春核心'),
ProductSeriesOption(id: 5, code: 'XL005', name: 'M6+', remark: '梦之蓝高端'),
ProductSeriesOption(id: 6, code: 'XL006', name: '青花20', remark: '汾酒青花系列'),
];
const _shop = ShopInfo(
id: 1,
code: 'DSJH-001',
name: '鼎晟酒行',
address: '浙江省杭州市',
phone: '0571-88886666',
managerName: '王经理',
customFields: {'default_series_id': 1});
class _FakeProductList extends ProductListNotifier {
@override
Future<PageResult<Product>> build() async =>
const PageResult(data: _products, total: 6, page: 1, pageSize: 20);
@override
void setPage(int page) {}
@override
void setPageSize(int pageSize) {}
@override
void setKeyword(String keyword) {}
@override
void setCategoryId(int? categoryId) {}
@override
void reload() {}
}
class _FakeSeriesList extends ProductSeriesListNotifier {
@override
Future<List<ProductSeriesOption>> build() async => _series;
@override
void reload() {}
}
List<Override> _overrides() => [
productListProvider.overrideWith(() => _FakeProductList()),
productSeriesListProvider.overrideWith(() => _FakeSeriesList()),
shopInfoProvider.overrideWith((ref) => _shop),
isReadonlyProvider.overrideWithValue(false),
];
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
SharedPreferences.setMockInitialValues({});
// 商品档案 tab:搜索行 + 卡片流(mono 编码副行)
goldenAcrossThemes(
'products 商品档案 窄屏(手机视口)',
goldenPrefix: 'm_products',
child: () => const Scaffold(body: ProductsScreen()),
overrides: _overrides,
logical: const Size(390, 844),
dpr: 2,
);
// 系列字典 tab:搜索行 + 新增按钮 + 字典卡片流(默认徽章)
goldenAcrossThemes(
'products 系列字典 窄屏(手机视口)',
goldenPrefix: 'm_products_series',
child: () => const Scaffold(body: ProductsScreen(initialTab: 2)),
overrides: _overrides,
logical: const Size(390, 844),
dpr: 2,
);
}