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>
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 156 KiB |
|
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,
|
||||
);
|
||||
}
|
||||