Compare commits
17
Commits
7c69197396
...
dev2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94138ba7f4 | ||
|
|
33b54fe454 | ||
|
|
901e42a41b | ||
|
|
af8934fe7f | ||
|
|
048c5a9ff4 | ||
|
|
1d4028f486 | ||
|
|
944493a39b | ||
|
|
6d08db54a5 | ||
|
|
3f9256491d | ||
|
|
95e40f31cc | ||
|
|
639d2033f0 | ||
|
|
4da7fb32de | ||
|
|
147ab866e1 | ||
|
|
0526baa3c8 | ||
|
|
b49fb59db4 | ||
|
|
9608e8b9fb | ||
|
|
5ccc06b0f7 |
@@ -1,7 +1,12 @@
|
||||
# TODO — Flutter Wisetronic Dart 3 空安全迁移(剩余工作)
|
||||
# TODO — Flutter Wisetronic Dart 3 空安全迁移
|
||||
|
||||
> **当前进度**:错误从峰值 **2374** 降到 **118(lib)/ 112(web 构建)**,0 语法损坏。
|
||||
> 分支 `dev2`,最新提交已 clean。Web 构建 (`flutter build web`) 目标:0 error → 可运行。
|
||||
> ## ✅ Web 构建已成功!(2026-08-01)
|
||||
> **`flutter build web --no-tree-shake-icons` → ✓ Built build/web/(main.dart.js 4.5M)**
|
||||
> - `dart analyze lib`: 仅剩 **6 error**,全部在 `utils/util_io.dart`(原生路径,Web 已排除,见 §5 延后)。
|
||||
> - Web 可达错误: **0**。
|
||||
> - 峰值 2374 → 6(全部 util_io 原生)。0 语法损坏。分支 `dev2`。
|
||||
> - 构建脚本: `./build_web.sh`(构建)/ `./build_web.sh run`(Chrome 运行)。
|
||||
> - **关键修复**: `cross_file` override 到 `0.3.4+2`(旧版 0.3.1+4 用了 Dart3 移除的 `UnmodifiableUint8ListView`;0.3.5+ 需 Dart 3.10)。
|
||||
|
||||
---
|
||||
|
||||
@@ -10,10 +15,11 @@
|
||||
```bash
|
||||
cd /Users/peima/StudioProjects/flutter_wisetronic
|
||||
git log --oneline -1 # 确认在最新提交
|
||||
dart analyze lib | tail -5 # 看当前错误(应 118 error)
|
||||
flutter build web 2>&1 | grep -c '^Error:' # web 错误(应 112)
|
||||
dart analyze lib | tail -5 # 当前: 仅 6 error(util_io.dart)
|
||||
./build_web.sh # 构建: ✓ Built build/web/
|
||||
./build_web.sh run # Chrome 运行调试
|
||||
|
||||
# 重跑自动修复脚本(安全,带损坏自检,可随时跑)
|
||||
# 重跑自动修复脚本(安全,带损坏自检,可随时跑)—— 已无 web 可达错误,通常无需再跑
|
||||
python3 tools/nullfix.py --max-rounds 12
|
||||
```
|
||||
|
||||
@@ -219,3 +225,26 @@ f8a90ad 基线
|
||||
flutter build web # 目标:无 error,生成 build/web
|
||||
flutter run -d chrome # 目标:能打开、首页/商品/购物车/结账流程跑通
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 运行时问题(应用已能运行,以下为后续优化,非阻塞)
|
||||
|
||||
应用 `flutter run -d chrome` 已成功启动并加载首页/API 数据/数据库。控制台有以下运行时警告/异常:
|
||||
|
||||
### 9.1 Scrollbar 缺 ScrollPosition(真实,影响滚动条显示)
|
||||
4 个 `Scrollbar(` 未配 `controller`,新版 Flutter 的 Scrollbar 找不到 ScrollPosition:
|
||||
- `lib/pages/home.dart:101` — ✅ 已修:给 `SingleChildScrollView` 加 `primary: true`
|
||||
- `lib/pages/blog.dart:24` — 子是 `DesktopBlog`(自定义组件)
|
||||
- `lib/pages/download.dart:63` — 子是 `DesktopDownloadApps`(自定义组件)
|
||||
- `lib/widgets/desktop/shop.dart:137` — 包裹 `NotificationListener`
|
||||
|
||||
后 3 处的子是自定义组件,需让内部 ListView/ScrollView 暴露 controller(设 `primary: true`)或 Scrollbar 改用 `NotificationListener` 关联。**仅影响滚动条渲染,不影响内容加载/显示。**
|
||||
|
||||
### 9.2 CustomersLogo setState-after-dispose(小 bug)
|
||||
`lib/widgets/desktop/desktop_customers_logo.dart` 的 `Timer.periodic`(每 10s 刷新 logo)未在 `dispose()` 里 `cancel()`,组件卸载后 timer 仍触发 `setState`。建议在 `dispose` 里 `timer?.cancel()`。**热重载时易触发,干净启动偶发。**
|
||||
|
||||
### 9.3 `_business` LateInitializationError(热重载假象)
|
||||
`_business` 是店铺页面组件(`shop_promote.dart`/`shop.dart` 等)的 `late Business` 字段。热重载重建 widget 树时字段状态丢失触发,**干净启动(浏览器刷新 Cmd+R)不会出现**。
|
||||
|
||||
> **验证方式**:在浏览器里 Cmd+R 刷新页面(而非热重载),9.2/9.3 类异常应消失,仅剩 9.1 的滚动条警告。
|
||||
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# Flutter Wisetronic — Web 构建脚本
|
||||
# 用法:
|
||||
# ./build_web.sh # 构建 release 版本到 build/web/
|
||||
# ./build_web.sh run # 构建并在 Chrome 中运行调试版
|
||||
#
|
||||
# 说明:
|
||||
# - 构建(release)必须 --no-tree-shake-icons: 项目中有 24 处 IconData(codePoint)
|
||||
# 使用来自 JSON/API 的动态 code point(非 const), icon tree-shaking 无法处理。
|
||||
# 注意: --no-tree-shake-icons 仅 build 支持; flutter run(debug)不做 tree-shaking,
|
||||
# 因此 run 分支不带该 flag。
|
||||
# - Dart SDK: 本地 Flutter 3.29.3 / Dart 3.7.2(未升级全局 SDK)。
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [ "$1" = "run" ]; then
|
||||
echo ">>> flutter run -d chrome"
|
||||
flutter run -d chrome
|
||||
else
|
||||
echo ">>> flutter build web --no-tree-shake-icons"
|
||||
flutter build web --no-tree-shake-icons
|
||||
echo ""
|
||||
echo "✓ 构建完成: build/web/ (main.dart.js: $(du -h build/web/main.dart.js | cut -f1))"
|
||||
echo " 本地预览: python3 -m http.server --directory build/web 8080"
|
||||
echo " 然后访问 http://localhost:8080/"
|
||||
fi
|
||||
+9
-2
@@ -33,10 +33,17 @@ import 'utils/utils.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
FlutterError.onError = (details) {
|
||||
FlutterError.presentError(details);
|
||||
print('CK_ERROR: ${details.exceptionAsString()}');
|
||||
print('CK_CTX: ${details.context}');
|
||||
print('CK_STACK: ${details.stack}');
|
||||
};
|
||||
// Enable configureApp will cause route problem.
|
||||
// configureApp();
|
||||
setPathUrlStrategy();
|
||||
runApp(MyApp());
|
||||
Future.delayed(Duration(seconds: 3), () => Util.devAutologin()); // DEV e2e: 延迟 3s 等 Flutter/Hive ready
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@@ -165,12 +172,12 @@ class MyApp extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (pathElements[1] == 'renew-minioffice') {
|
||||
if (pathElements[1] == 'renew-minioffice' && pathElements.length >= 3) {
|
||||
return MaterialPageRoute(builder: (BuildContext context) =>
|
||||
RenewMiniOffice(int.parse(pathElements[2]))
|
||||
);
|
||||
}
|
||||
if (pathElements[1] == 'buy-service') {
|
||||
if (pathElements[1] == 'buy-service' && pathElements.length >= 4) {
|
||||
return MaterialPageRoute(builder: (BuildContext context) =>
|
||||
BuyService(int.parse(pathElements[2]), pathElements[3])
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ class BuyService extends StatefulWidget {
|
||||
}
|
||||
|
||||
class BuyServiceState extends State<BuyService> {
|
||||
late Map<String, dynamic> data;
|
||||
Map<String, dynamic>? data;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -51,9 +51,9 @@ class BuyServiceState extends State<BuyService> {
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInformation) =>
|
||||
ScreenTypeLayout(
|
||||
mobile: MobileBuyService(data),
|
||||
tablet: DesktopBuyService(data),
|
||||
desktop: DesktopBuyService(data),
|
||||
mobile: MobileBuyService(data!),
|
||||
tablet: DesktopBuyService(data!),
|
||||
desktop: DesktopBuyService(data!),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -72,12 +72,16 @@ class BuyServiceState extends State<BuyService> {
|
||||
'store_id': '${widget.sid}',
|
||||
}
|
||||
).then((value) {
|
||||
data = value;
|
||||
data['domain'] = widget.domain;
|
||||
print('data: $data');
|
||||
if (!mounted) return;
|
||||
if (value == null || value is! Map || value['service_selections'] == null) {
|
||||
Utils.showMessageDialog(context, 'Service info unavailable');
|
||||
return;
|
||||
}
|
||||
data = Map<String, dynamic>.from(value);
|
||||
data!['domain'] = widget.domain;
|
||||
setState(() {});
|
||||
}).onError((error, stackTrace) {
|
||||
Utils.showMessageDialog(context, error);
|
||||
if (mounted) Utils.showMessageDialog(context, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class ContactUs extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ContactUsState extends State<ContactUs> {
|
||||
late Business business;
|
||||
Business? business;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -43,9 +43,9 @@ class ContactUsState extends State<ContactUs> {
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInformation) =>
|
||||
ScreenTypeLayout(
|
||||
mobile: MobileContactUs(business),
|
||||
tablet: DesktopContactUs(business),
|
||||
desktop: DesktopContactUs(business),
|
||||
mobile: MobileContactUs(business!),
|
||||
tablet: DesktopContactUs(business!),
|
||||
desktop: DesktopContactUs(business!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Download extends StatefulWidget {
|
||||
|
||||
class DownloadState extends State<Download> {
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
late Map<String, dynamic> data;
|
||||
Map<String, dynamic>? data;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -58,11 +58,11 @@ class DownloadState extends State<Download> {
|
||||
),
|
||||
drawer: sizingInformation.deviceScreenType == DeviceScreenType.mobile ? MobileNavigationDrawer() : null,
|
||||
body: ScreenTypeLayout(
|
||||
mobile: MobileDownloadApps(data),
|
||||
tablet: DesktopDownloadApps(data),
|
||||
mobile: MobileDownloadApps(data!),
|
||||
tablet: DesktopDownloadApps(data!),
|
||||
desktop: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
child: DesktopDownloadApps(data),
|
||||
child: DesktopDownloadApps(data!),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: ScreenTypeLayout(
|
||||
|
||||
@@ -101,6 +101,7 @@ class HomeState extends State<Home> {
|
||||
desktop: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
primary: true,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
@@ -26,7 +26,7 @@ class IGoShowLearnMore extends StatefulWidget {
|
||||
|
||||
class IGoShowLearnMoreState extends State<IGoShowLearnMore> {
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
late Map<String, dynamic> data;
|
||||
Map<String, dynamic>? data;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -56,9 +56,9 @@ class IGoShowLearnMoreState extends State<IGoShowLearnMore> {
|
||||
),
|
||||
drawer: null,
|
||||
body: ScreenTypeLayout(
|
||||
mobile: MobileiGoShowLearnMore(data),
|
||||
tablet: DesktopiGoShowLearnMore(data),
|
||||
desktop: DesktopiGoShowLearnMore(data),
|
||||
mobile: MobileiGoShowLearnMore(data!),
|
||||
tablet: DesktopiGoShowLearnMore(data!),
|
||||
desktop: DesktopiGoShowLearnMore(data!),
|
||||
),
|
||||
bottomNavigationBar: ScreenTypeLayout(
|
||||
mobile: MobileBottomNav(currentIndex: 0,),
|
||||
|
||||
@@ -26,7 +26,7 @@ class MiniPosLearnMore extends StatefulWidget {
|
||||
|
||||
class MiniPosLearnMoreState extends State<MiniPosLearnMore> {
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
late Map<String, dynamic> data;
|
||||
Map<String, dynamic>? data;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -57,9 +57,9 @@ class MiniPosLearnMoreState extends State<MiniPosLearnMore> {
|
||||
),
|
||||
drawer: null,
|
||||
body: ScreenTypeLayout(
|
||||
mobile: MobileMiniPosLearnMore(data),
|
||||
tablet: DesktopMiniPosLearnMore(data),
|
||||
desktop: DesktopMiniPosLearnMore(data),
|
||||
mobile: MobileMiniPosLearnMore(data!),
|
||||
tablet: DesktopMiniPosLearnMore(data!),
|
||||
desktop: DesktopMiniPosLearnMore(data!),
|
||||
),
|
||||
bottomNavigationBar: ScreenTypeLayout(
|
||||
mobile: MobileBottomNav(currentIndex: 0,),
|
||||
|
||||
@@ -23,7 +23,7 @@ class PlainPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class PlainPageState extends State<PlainPage> {
|
||||
late Blog blog;
|
||||
Blog? blog;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -44,9 +44,9 @@ class PlainPageState extends State<PlainPage> {
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInformation) =>
|
||||
ScreenTypeLayout(
|
||||
mobile: MobilePlainPage(blog),
|
||||
tablet: DesktopPlainPage(blog),
|
||||
desktop: DesktopPlainPage(blog),
|
||||
mobile: MobilePlainPage(blog!),
|
||||
tablet: DesktopPlainPage(blog!),
|
||||
desktop: DesktopPlainPage(blog!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class RenewMiniOffice extends StatefulWidget {
|
||||
}
|
||||
|
||||
class RenewMiniOfficeState extends State<RenewMiniOffice> {
|
||||
late Map<String, dynamic> data;
|
||||
Map<String, dynamic>? data;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -46,9 +46,9 @@ class RenewMiniOfficeState extends State<RenewMiniOffice> {
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInformation) =>
|
||||
ScreenTypeLayout(
|
||||
mobile: MobileRenewMiniOffice(data),
|
||||
tablet: DesktopRenewMiniOffice(data),
|
||||
desktop: DesktopRenewMiniOffice(data),
|
||||
mobile: MobileRenewMiniOffice(data!),
|
||||
tablet: DesktopRenewMiniOffice(data!),
|
||||
desktop: DesktopRenewMiniOffice(data!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class DoubleBackToCloseApp extends StatefulWidget {
|
||||
|
||||
class _DoubleBackToCloseAppState extends State<DoubleBackToCloseApp> {
|
||||
/// The last time the user tapped Android's back-button.
|
||||
late DateTime _lastTimeBackButtonWasTapped;
|
||||
DateTime? _lastTimeBackButtonWasTapped;
|
||||
|
||||
/// Returns whether the current platform is Android.
|
||||
bool get _isAndroid => Theme.of(context).platform == TargetPlatform.android;
|
||||
@@ -50,7 +50,7 @@ class _DoubleBackToCloseAppState extends State<DoubleBackToCloseApp> {
|
||||
bool get _isSnackBarVisible =>
|
||||
(_lastTimeBackButtonWasTapped != null) &&
|
||||
(widget.snackBar.duration >
|
||||
DateTime.now().difference(_lastTimeBackButtonWasTapped));
|
||||
DateTime.now().difference(_lastTimeBackButtonWasTapped!));
|
||||
|
||||
/// Returns whether the next back navigation of this route will be handled
|
||||
/// internally.
|
||||
|
||||
@@ -199,6 +199,9 @@ class Util {
|
||||
return box;
|
||||
}
|
||||
|
||||
/// DEV ONLY stub (原生不自动登录)
|
||||
static void devAutologin() {}
|
||||
|
||||
static Widget showImage(String imageUrl, {double? width, double? height,
|
||||
BoxFit? fit, Widget Function(BuildContext, String, dynamic)? errorWidget}) {
|
||||
if (imageUrl != null && imageUrl.isNotEmpty && imageUrl.startsWith('https:')) {
|
||||
|
||||
@@ -41,6 +41,28 @@ class Util {
|
||||
return box;
|
||||
}
|
||||
|
||||
/// DEV ONLY (e2e tests): 读 localStorage['_test_token'] = 'username|password',
|
||||
/// 调登录 API 写 Hive + dispatch currentUser。测试后移除。
|
||||
static Future<void> devAutologin() async {
|
||||
try {
|
||||
final t = window.localStorage['_test_token'];
|
||||
if (t == null || t.isEmpty) return;
|
||||
final parts = t.split('|');
|
||||
if (parts.length < 2) return;
|
||||
print('CK_AUTOLOGIN_CALL');
|
||||
await HttpUtil.httpPost('v1/oauth-wisetronic/access_token', (response) async {
|
||||
final box = await getBox();
|
||||
await box.put(Constants.KEY_ACCESS_TOKEN, response.data['access_token']);
|
||||
await box.put(Constants.KEY_USER_ID, response.data['user_id']);
|
||||
store.dispatch(UpdateCurrentUser(User.fromJson(response.data['user'])));
|
||||
print('CK_AUTOLOGIN_OK');
|
||||
},
|
||||
queryParameters: {'client_id': Utils.getPlatformName(), 'grant_type': 'password'},
|
||||
body: {'username': parts[0], 'password': parts[1], 'fcm_token': ''},
|
||||
isFormData: true, businessId: Constants.BUSINESS_ID);
|
||||
} catch (e) { print('CK_AUTOLOGIN_EX: ${e.toString().substring(0, 150)}'); }
|
||||
}
|
||||
|
||||
static Widget showImage(String imageUrl, {double? width, double? height,
|
||||
BoxFit? fit, Widget Function(BuildContext, String?, dynamic)? errorWidget}) {
|
||||
return Image.network(imageUrl,
|
||||
|
||||
@@ -35,7 +35,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
bool canSubmit = false;
|
||||
|
||||
List<dynamic> stores = [];
|
||||
late Map<String, dynamic> service;
|
||||
Map<String, dynamic>? service;
|
||||
dynamic selectedStore;
|
||||
|
||||
late Group group;
|
||||
@@ -322,7 +322,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
service['description'],
|
||||
service!['description'],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -330,7 +330,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
service['options'][0]['name'],
|
||||
service!['options'][0]['name'],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -338,7 +338,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'\$${service['options'][0]['price']}',
|
||||
'\$${service!['options'][0]['price']}',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -383,13 +383,14 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
'service': Constants.WEB_MINISTORE_SERVICE
|
||||
}
|
||||
).then((value) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
stores = value['stores'];
|
||||
service = value['service'];
|
||||
group = Group.fromJson(value['group']);
|
||||
print('stores: ${value}');
|
||||
});
|
||||
}).onError((error, stackTrace) {
|
||||
if (!mounted) return;
|
||||
Utils.showMessageDialog(context, error, onOk: () {
|
||||
Routes.router.navigateTo(context, '/');
|
||||
});
|
||||
@@ -411,7 +412,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
print('value: $value');
|
||||
if (value['status'] == 'OK') {
|
||||
domainResult = (value['domain_check_result'] as List);
|
||||
setState(() {});
|
||||
if (mounted) setState(() {});
|
||||
} else if (value['status'] == 'ERROR') {
|
||||
String errors = '';
|
||||
for (int i = 0; i < (value['errors'] as List).length; i++) {
|
||||
|
||||
@@ -292,8 +292,7 @@ class DesktopCheckoutState extends State<DesktopCheckout> with SingleTickerProvi
|
||||
itemCount: 6,
|
||||
addAutomaticKeepAlives: true,
|
||||
itemBuilder: (BuildContext context, int position) {
|
||||
var deliveryTimeInSeconds = cartInfo!.businessInfo!.shippingTime! * 60 + (durationInTraffic != null ? durationInTraffic!.value ?? 0 : 0);
|
||||
print('aaa: $deliveryTimeInSeconds');
|
||||
var deliveryTimeInSeconds = (cartInfo!.businessInfo!.shippingTime ?? 0) * 60 + (durationInTraffic != null ? durationInTraffic!.value ?? 0 : 0);
|
||||
DateTime now = DateTime.now();
|
||||
var formatter = DateFormat('H:mm');
|
||||
String deliveryAt = formatter.format(now.add(Duration(seconds: deliveryTimeInSeconds)));
|
||||
@@ -306,7 +305,7 @@ class DesktopCheckoutState extends State<DesktopCheckout> with SingleTickerProvi
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).table_token(store.state.tableNumber!),
|
||||
S.of(context).table_token(store.state.tableNumber ?? ''),
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@@ -55,6 +55,7 @@ class DesktopDownloadAppsState extends State<DesktopDownloadApps> {
|
||||
}
|
||||
col.children.add(wrap);
|
||||
Widget view = SingleChildScrollView(
|
||||
primary: true,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
|
||||
@@ -16,9 +16,9 @@ import '../../utils/util_web.dart'
|
||||
if (dart.library.io) '../../utils/util_io.dart';
|
||||
import '../../utils/utils.dart';
|
||||
|
||||
late MediaQueryData mediaQuery;
|
||||
late double statusBarHeight;
|
||||
late double screenHeight;
|
||||
MediaQueryData? mediaQuery;
|
||||
double? statusBarHeight;
|
||||
double? screenHeight;
|
||||
|
||||
class DesktopMe extends StatefulWidget {
|
||||
final Key? key;
|
||||
@@ -67,8 +67,8 @@ class DesktopMeState extends State<DesktopMe> {
|
||||
}
|
||||
|
||||
mediaQuery ??= MediaQuery.of(context);
|
||||
screenHeight ??= mediaQuery.size.height;
|
||||
statusBarHeight ??= mediaQuery.padding.top;
|
||||
screenHeight ??= mediaQuery!.size.height;
|
||||
statusBarHeight ??= mediaQuery!.padding.top;
|
||||
|
||||
Widget userInfo = GestureDetector(
|
||||
child: Container(
|
||||
|
||||
@@ -34,7 +34,7 @@ class DesktopNewComment extends StatefulWidget {
|
||||
}
|
||||
|
||||
class DesktopNewCommentState extends State<DesktopNewComment> {
|
||||
late Comment comment;
|
||||
Comment? comment;
|
||||
|
||||
late bool _showProgress;
|
||||
|
||||
@@ -203,8 +203,8 @@ class DesktopNewCommentState extends State<DesktopNewComment> {
|
||||
children: <Widget>[],
|
||||
);
|
||||
|
||||
if (comment != null && comment.images!.length > 0) {
|
||||
for (ProductImage image in comment.images!) {
|
||||
if (comment != null && comment!.images!.length > 0) {
|
||||
for (ProductImage image in comment!.images!) {
|
||||
row.children.add(
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10.0),
|
||||
@@ -275,7 +275,7 @@ class DesktopNewCommentState extends State<DesktopNewComment> {
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
size: 60.0,
|
||||
color: comment == null || comment.images!.length < 3 ? Colors.lightBlue : Colors.black12,
|
||||
color: comment == null || comment!.images!.length < 3 ? Colors.lightBlue : Colors.black12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white70,
|
||||
@@ -300,13 +300,13 @@ class DesktopNewCommentState extends State<DesktopNewComment> {
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (comment == null || comment.images!.length < 3) {
|
||||
if (comment == null || comment!.images!.length < 3) {
|
||||
showDialog(
|
||||
context: mainContext,
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return Util().getPicture(mainContext, store.state.user!,
|
||||
commentId: comment != null ? comment.id! : 0,
|
||||
commentId: comment != null ? comment!.id! : 0,
|
||||
orderId: widget.orderId);
|
||||
}
|
||||
);
|
||||
@@ -365,7 +365,7 @@ class DesktopNewCommentState extends State<DesktopNewComment> {
|
||||
}
|
||||
},
|
||||
queryParameters: {
|
||||
'comment_id': comment != null ? comment.id! : 0,
|
||||
'comment_id': comment != null ? comment!.id! : 0,
|
||||
},
|
||||
).catchError((error) {
|
||||
Utils.showMessageDialog(context, error);
|
||||
@@ -392,7 +392,7 @@ class DesktopNewCommentState extends State<DesktopNewComment> {
|
||||
isFormData: true,
|
||||
body: {
|
||||
'order_id': widget.orderId,
|
||||
'comment_id': comment != null ? comment.id! : 0,
|
||||
'comment_id': comment != null ? comment!.id! : 0,
|
||||
'content': commentController.text,
|
||||
'rating': rating.round(),
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ class DesktopPayNow extends StatefulWidget {
|
||||
}
|
||||
|
||||
class DesktopPayNowState extends State<DesktopPayNow> {
|
||||
late Order order;
|
||||
Order? order;
|
||||
late List<PaymentPlatform> paymentPlatforms;
|
||||
late User _user;
|
||||
|
||||
@@ -86,7 +86,7 @@ class DesktopPayNowState extends State<DesktopPayNow> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'\$${order.totalPrice!.toStringAsFixed(2)}',
|
||||
'\$${order!.totalPrice!.toStringAsFixed(2)}',
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -206,7 +206,7 @@ class DesktopPayNowState extends State<DesktopPayNow> {
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Util.goPayment(context, order, paymentPlatform);
|
||||
Util.goPayment(context, order!, paymentPlatform);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
|
||||
final double _tabBarHeight = 50;
|
||||
|
||||
late ProductDetail productDetail;
|
||||
ProductDetail? productDetail;
|
||||
|
||||
late bool refresh;
|
||||
|
||||
@@ -83,7 +83,7 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
title: S.of(context).shop,
|
||||
back: true,
|
||||
breadCrumbs: [
|
||||
BreadCrumb(productDetail.name!, null),
|
||||
BreadCrumb(productDetail!.name!, null),
|
||||
],
|
||||
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
|
||||
// shoppingCart: DesktopShoppingCartWidget(business: widget.business,),
|
||||
@@ -114,7 +114,7 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
'SKU: ${productDetail.sku}',
|
||||
'SKU: ${productDetail!.sku}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: TextStyle(
|
||||
@@ -125,7 +125,7 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
productDetail.name!,
|
||||
productDetail!.name!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: TextStyle(
|
||||
@@ -137,7 +137,7 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 20),
|
||||
child: Text(
|
||||
'${productDetail.description}',
|
||||
'${productDetail!.description}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 3,
|
||||
style: TextStyle(
|
||||
@@ -163,12 +163,12 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ShowPrice(
|
||||
productDetail.price!,
|
||||
productDetail!.price!,
|
||||
currencySign: '\$',
|
||||
fontWeight: FontWeight.bold,
|
||||
smallFontSize: 24,
|
||||
largeFontSize: 40,
|
||||
regularPrice: productDetail.regularPrice,
|
||||
regularPrice: productDetail!.regularPrice,
|
||||
),
|
||||
AddRemoveButton(
|
||||
product: widget.product,
|
||||
@@ -207,35 +207,35 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
(productDetail.subproducts!.length > 0) ?
|
||||
subProducts(productDetail.subproducts!) :
|
||||
(productDetail!.subproducts!.length > 0) ?
|
||||
subProducts(productDetail!.subproducts!) :
|
||||
SizedBox.shrink(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 10.0, left: 10.0, right: 10.0, bottom: 16.0),
|
||||
child: Text(
|
||||
productDetail.description!,
|
||||
productDetail!.description!,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0, color: Colors.black54),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: (productDetail.description2 != null &&
|
||||
!productDetail.description2!.isEmpty)
|
||||
child: (productDetail!.description2 != null &&
|
||||
!productDetail!.description2!.isEmpty)
|
||||
? Text(
|
||||
'${productDetail.description2}',
|
||||
'${productDetail!.description2}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0, color: Colors.black54),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
productDetail.detailDescription != null
|
||||
productDetail!.detailDescription != null
|
||||
? Container(
|
||||
padding:
|
||||
EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: MarkdownBody(
|
||||
data: '${productDetail.detailDescription}',
|
||||
data: '${productDetail!.detailDescription}',
|
||||
shrinkWrap: true,
|
||||
),
|
||||
)
|
||||
@@ -253,7 +253,7 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text(
|
||||
S.of(context).weight_token(productDetail.weight!),
|
||||
S.of(context).weight_token(productDetail!.weight!),
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.black54,
|
||||
@@ -263,9 +263,9 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
Container(
|
||||
child: Text(
|
||||
S.of(context).dimentions_token(
|
||||
productDetail.dimentionsLength!,
|
||||
productDetail.dimentionsWidth!,
|
||||
productDetail.dimentionsHeight!),
|
||||
productDetail!.dimentionsLength!,
|
||||
productDetail!.dimentionsWidth!,
|
||||
productDetail!.dimentionsHeight!),
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.black54,
|
||||
@@ -299,9 +299,9 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
}
|
||||
|
||||
Widget getImage(double width) {
|
||||
if (productDetail.images!.length <= 0) {
|
||||
if (productDetail!.images!.length <= 0) {
|
||||
return Util.showImage(
|
||||
productDetail.image!,
|
||||
productDetail!.image!,
|
||||
width: width,
|
||||
height: width,
|
||||
);
|
||||
@@ -452,10 +452,10 @@ class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
|
||||
List<Widget> _getProductPictures(BuildContext context) {
|
||||
var pages = <Widget>[];
|
||||
List<String> images = [];
|
||||
images.add(productDetail.image!);
|
||||
for (var i = 0; i < productDetail.images!.length; i++) {
|
||||
images.add(productDetail!.image!);
|
||||
for (var i = 0; i < productDetail!.images!.length; i++) {
|
||||
// print('>>https:' + productDetail.images[i].image);
|
||||
images.add(productDetail.images![i].image!);
|
||||
images.add(productDetail!.images![i].image!);
|
||||
}
|
||||
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
|
||||
@@ -18,15 +18,15 @@ class DesktopShoppingCartWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class DesktopShoppingCartWidgetState extends State<DesktopShoppingCartWidget> {
|
||||
late CartInfo cartInfo;
|
||||
CartInfo? cartInfo;
|
||||
double totalPrice = 0.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
totalPrice = 0.0;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business)!;
|
||||
if (cartInfo != null && cartInfo.businessInfo!.id == widget.business.id) {
|
||||
totalPrice = cartInfo.getTotalPrice();
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business);
|
||||
if (cartInfo != null && cartInfo!.businessInfo!.id == widget.business.id) {
|
||||
totalPrice = cartInfo!.getTotalPrice();
|
||||
}
|
||||
Row row = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
|
||||
@@ -28,7 +28,7 @@ class DesktopViewBlog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class DesktopViewBlogState extends State<DesktopViewBlog> {
|
||||
late Blog blog;
|
||||
Blog? blog;
|
||||
|
||||
double sideSpace = 0;
|
||||
double mainSpace = 1200;
|
||||
@@ -97,7 +97,7 @@ class DesktopViewBlogState extends State<DesktopViewBlog> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${blog.title}',
|
||||
'${blog!.title}',
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.black
|
||||
@@ -105,7 +105,7 @@ class DesktopViewBlogState extends State<DesktopViewBlog> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, blog.createdAt!),
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, blog!.createdAt!),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black38,
|
||||
@@ -119,7 +119,7 @@ class DesktopViewBlogState extends State<DesktopViewBlog> {
|
||||
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0),
|
||||
child: Container(
|
||||
child: Text(
|
||||
'${blog.body}',
|
||||
'${blog!.body}',
|
||||
style: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: 17.0,
|
||||
@@ -169,12 +169,12 @@ class DesktopViewBlogState extends State<DesktopViewBlog> {
|
||||
width: mainSpace / 2,
|
||||
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: (blog.imageUrl == null) ?
|
||||
child: (blog!.imageUrl == null) ?
|
||||
SizedBox.shrink()
|
||||
: Container(
|
||||
width: mainSpace / 2 - 100.0,
|
||||
height: mainSpace / 2 - 100.0,
|
||||
child: Util.showImage('https:${blog.imageUrl}'),
|
||||
child: Util.showImage('https:${blog!.imageUrl}'),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -33,7 +33,7 @@ class DesktopViewTicket extends StatefulWidget {
|
||||
class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
late Ticket ticket;
|
||||
Ticket? ticket;
|
||||
|
||||
final issueMsgController = TextEditingController();
|
||||
|
||||
@@ -220,14 +220,14 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).ticket_number_token(ticket.id!),
|
||||
S.of(context).ticket_number_token(ticket!.id!),
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.black
|
||||
),
|
||||
),
|
||||
Text(
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, ticket.createdAt!),
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, ticket!.createdAt!),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black38,
|
||||
@@ -240,7 +240,7 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
width: double.maxFinite,
|
||||
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0),
|
||||
child: Text(
|
||||
'${ticket.issue!.msg}',
|
||||
'${ticket!.issue!.msg}',
|
||||
style: TextStyle(
|
||||
color: Colors.black54,
|
||||
fontSize: 14.0,
|
||||
@@ -260,7 +260,7 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
padding: EdgeInsets.only(top: 16.0, bottom: 16.0, left: 16.0, right: 16.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: showGalleryImages(mainContext, ticket.issue!.files!),
|
||||
child: showGalleryImages(mainContext, ticket!.issue!.files!),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
@@ -291,7 +291,7 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
],
|
||||
);
|
||||
|
||||
if (ticket.followUps!.length > 0) {
|
||||
if (ticket!.followUps!.length > 0) {
|
||||
ticketCol.children.add(
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
@@ -305,8 +305,8 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
),
|
||||
),
|
||||
);
|
||||
for (int i = 0; i < ticket.followUps!.length; i++) {
|
||||
FollowUp followUp = ticket.followUps![i];
|
||||
for (int i = 0; i < ticket!.followUps!.length; i++) {
|
||||
FollowUp followUp = ticket!.followUps![i];
|
||||
ticketCol.children.add(
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
@@ -406,7 +406,7 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
width: mainSpace / 2,
|
||||
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: (ticket.isClosed) ?
|
||||
child: (ticket!.isClosed == true) ?
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
@@ -423,7 +423,7 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 0.0, bottom: 30.0),
|
||||
child: TextLink(
|
||||
S.of(context).new_ticket,
|
||||
'/new-ticket/${ticket.store!.id}',
|
||||
'/new-ticket/${ticket!.store!.id}',
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -692,7 +692,7 @@ class DesktopViewTicketState extends State<DesktopViewTicket> {
|
||||
child: Text(S.of(context).ok),
|
||||
onPressed: () {
|
||||
Routes.router.navigateTo(context,
|
||||
'/my-support/${ticket.store!.id}',
|
||||
'/my-support/${ticket!.store!.id}',
|
||||
replace: true,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ class Shop extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ShopState extends State<Shop> {
|
||||
late Business _business;
|
||||
Business? _business;
|
||||
|
||||
PanelController panelController = PanelController();
|
||||
late SlidingUpPanel _slidUpShoppingCart;
|
||||
@@ -71,7 +71,7 @@ class ShopState extends State<Shop> {
|
||||
backdropEnabled: true,
|
||||
slideDirection: SlideDirection.DOWN,
|
||||
panel: ShoppingCartBar(
|
||||
business: _business,
|
||||
business: _business!,
|
||||
endKey: endKey,
|
||||
barAtBottom: true,
|
||||
hasPicture: true,
|
||||
@@ -119,14 +119,14 @@ class ShopState extends State<Shop> {
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return ProductSearch(_business);
|
||||
return ProductSearch(_business!);
|
||||
}));
|
||||
},
|
||||
),
|
||||
],
|
||||
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
|
||||
shoppingCart: ShoppingCartWidget(
|
||||
business: _business,
|
||||
business: _business!,
|
||||
onTap: () {
|
||||
if (panelController.isPanelClosed) {
|
||||
panelController.open();
|
||||
@@ -150,7 +150,7 @@ class ShopState extends State<Shop> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ShopPromote(data),
|
||||
ShopBulletin(_business),
|
||||
ShopBulletin(_business!),
|
||||
ShopProducts(data),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -114,7 +114,7 @@ class ShopProductsState extends State<ShopProducts> {
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
CategoryProducts cp = _categoryProducts[i];
|
||||
int qtyInCategory = 0;
|
||||
CartInfo cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, _business)!;
|
||||
CartInfo? cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, _business);
|
||||
if (cartInfo != null &&
|
||||
cartInfo.businessInfo!.id == _business.id &&
|
||||
cartInfo.productList != null) {
|
||||
|
||||
@@ -19,15 +19,15 @@ class ShoppingCartWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ShoppingCartWidgetState extends State<ShoppingCartWidget> {
|
||||
late CartInfo cartInfo;
|
||||
CartInfo? cartInfo;
|
||||
double totalPrice = 0.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
totalPrice = 0.0;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business)!;
|
||||
if (cartInfo != null && cartInfo.businessInfo!.id == widget.business.id) {
|
||||
totalPrice = cartInfo.getTotalPrice();
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business);
|
||||
if (cartInfo != null && cartInfo!.businessInfo!.id == widget.business.id) {
|
||||
totalPrice = cartInfo!.getTotalPrice();
|
||||
}
|
||||
Row row = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
|
||||
@@ -48,7 +48,7 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
|
||||
var d = 1;
|
||||
|
||||
late CartInfo cartInfo;
|
||||
CartInfo? cartInfo;
|
||||
|
||||
GlobalKey startKey = GlobalKey();
|
||||
|
||||
@@ -61,12 +61,12 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.product!.leftNum == null) {
|
||||
_qty = 0;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business!)!;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business!);
|
||||
if (cartInfo != null) {
|
||||
for (var i = 0; i < cartInfo.productList!.length; i++) {
|
||||
if (cartInfo.productList![i].product!.id == widget.product!.id
|
||||
&& cartInfo.productList![i].unitPrice == 0.0) {
|
||||
_qty = cartInfo.productList![i].quantity!.round();
|
||||
for (var i = 0; i < cartInfo!.productList!.length; i++) {
|
||||
if (cartInfo!.productList![i].product!.id == widget.product!.id
|
||||
&& cartInfo!.productList![i].unitPrice == 0.0) {
|
||||
_qty = cartInfo!.productList![i].quantity!.round();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -102,11 +102,11 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
}
|
||||
if (widget.addToBasket) {
|
||||
_qty = 0;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business!)!;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business!);
|
||||
if (cartInfo != null) {
|
||||
for (var i = 0; i < cartInfo.productList!.length; i++) {
|
||||
if (cartInfo.productList![i].product!.id == widget.product!.id) {
|
||||
_qty = cartInfo.productList![i].quantity!.round();
|
||||
for (var i = 0; i < cartInfo!.productList!.length; i++) {
|
||||
if (cartInfo!.productList![i].product!.id == widget.product!.id) {
|
||||
_qty = cartInfo!.productList![i].quantity!.round();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -194,11 +194,11 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
);
|
||||
} else {
|
||||
_qty = 0;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business!)!;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business!);
|
||||
if (cartInfo != null) {
|
||||
for (var i = 0; i < cartInfo.productList!.length; i++) {
|
||||
if (cartInfo.productList![i].product!.id == widget.product!.id) {
|
||||
_qty = cartInfo.productList![i].quantity!.round();
|
||||
for (var i = 0; i < cartInfo!.productList!.length; i++) {
|
||||
if (cartInfo!.productList![i].product!.id == widget.product!.id) {
|
||||
_qty = cartInfo!.productList![i].quantity!.round();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -311,7 +311,7 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
|
||||
void _addToCart(BuildContext context) {
|
||||
if (widget.cartLineItemIndex != -1) {
|
||||
if (cartInfo.productList![widget.cartLineItemIndex].quantity! + 1.0 > widget.product!.leftNum!) {
|
||||
if (cartInfo!.productList![widget.cartLineItemIndex].quantity! + 1.0 > widget.product!.leftNum!) {
|
||||
Fluttertoast.showToast(
|
||||
msg: S.of(context).product_insufficient,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
@@ -320,10 +320,10 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
textColor: Colors.white
|
||||
);
|
||||
} else {
|
||||
cartInfo.productList![widget.cartLineItemIndex].quantity += 1.0;
|
||||
Utils.addSubproductQty(cartInfo, cartInfo.productList![widget.cartLineItemIndex]);
|
||||
cartInfo!.productList![widget.cartLineItemIndex].quantity = (cartInfo!.productList![widget.cartLineItemIndex].quantity ?? 0) + 1.0;
|
||||
Utils.addSubproductQty(cartInfo!, cartInfo!.productList![widget.cartLineItemIndex]);
|
||||
store.dispatch(UpdateCartInfo(
|
||||
Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
|
||||
Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo!)));
|
||||
eventBus.fire(new OnCartInfoUpdated());
|
||||
}
|
||||
} else {
|
||||
@@ -344,7 +344,7 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
|
||||
void _removeFromCart(BuildContext context) {
|
||||
if (widget.cartLineItemIndex != -1) {
|
||||
if (cartInfo.productList![widget.cartLineItemIndex].quantity! <= 1) {
|
||||
if (cartInfo!.productList![widget.cartLineItemIndex].quantity! <= 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
@@ -379,18 +379,18 @@ class AddRemoveButtonState extends State<AddRemoveButton> {
|
||||
}
|
||||
|
||||
void _removeCartLineItem() {
|
||||
if (cartInfo.productList![widget.cartLineItemIndex].quantity! <= 1) {
|
||||
String uuid = cartInfo.productList![widget.cartLineItemIndex].uuid!;
|
||||
cartInfo.productList!.removeAt(widget.cartLineItemIndex);
|
||||
Utils.removeSubproduct(cartInfo, uuid);
|
||||
if (cartInfo!.productList![widget.cartLineItemIndex].quantity! <= 1) {
|
||||
String uuid = cartInfo!.productList![widget.cartLineItemIndex].uuid!;
|
||||
cartInfo!.productList!.removeAt(widget.cartLineItemIndex);
|
||||
Utils.removeSubproduct(cartInfo!, uuid);
|
||||
} else {
|
||||
cartInfo.productList![widget.cartLineItemIndex].quantity -= 1;
|
||||
Utils.addSubproductQty(cartInfo, cartInfo.productList![widget.cartLineItemIndex], remove: true);
|
||||
cartInfo!.productList![widget.cartLineItemIndex].quantity = (cartInfo!.productList![widget.cartLineItemIndex].quantity ?? 0) - 1;
|
||||
Utils.addSubproductQty(cartInfo!, cartInfo!.productList![widget.cartLineItemIndex], remove: true);
|
||||
}
|
||||
if (cartInfo.productList!.length <= 0) {
|
||||
if (cartInfo!.productList!.length <= 0) {
|
||||
store.dispatch(new UpdateCartInfo(Utils.removeCartInfoFromCartInfoList(store.state.cartInfos, cartInfo)));
|
||||
} else {
|
||||
store.dispatch(new UpdateCartInfo(Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
|
||||
store.dispatch(new UpdateCartInfo(Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo!)));
|
||||
}
|
||||
eventBus.fire(new OnCartInfoUpdated());
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Carousel extends StatefulWidget {
|
||||
class CarouselState extends State<Carousel> {
|
||||
final _pageController = new PageController();
|
||||
|
||||
late Timer _timer;
|
||||
Timer? _timer;
|
||||
int _currentPage = 0;
|
||||
bool reverse = false;
|
||||
GlobalKey<IndicatorState> _indicatorStateKey = new GlobalKey();
|
||||
@@ -63,7 +63,7 @@ class CarouselState extends State<Carousel> {
|
||||
void dispose() {
|
||||
_pageController?.dispose();
|
||||
if (_timer != null) {
|
||||
_timer.cancel();
|
||||
_timer!.cancel();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class ParabolicAnimationWidget extends AnimatedWidget {
|
||||
this.endAdjustOffset = Offset.zero,
|
||||
}) : super(listenable: animation);
|
||||
|
||||
late Offset _startOffset;
|
||||
Offset? _startOffset;
|
||||
late Offset _endOffset;
|
||||
|
||||
@override
|
||||
@@ -36,13 +36,13 @@ class ParabolicAnimationWidget extends AnimatedWidget {
|
||||
// double x(double time) => _x + _v * time + 0.5 * _a * time * time;
|
||||
final double initV = -400; //纵坐标初速度, 负值为向上抛
|
||||
final double acceleration =
|
||||
(_endOffset.dy - _startOffset.dy - initV) / 0.5; //求纵坐标加速度
|
||||
(_endOffset.dy - _startOffset!.dy - initV) / 0.5; //求纵坐标加速度
|
||||
|
||||
final GravitySimulation spy = GravitySimulation(
|
||||
acceleration, _startOffset.dy, _endOffset.dy, initV); //y轴加速度运动 模拟
|
||||
acceleration, _startOffset!.dy, _endOffset.dy, initV); //y轴加速度运动 模拟
|
||||
|
||||
final GravitySimulation spx = GravitySimulation(0, _startOffset.dx,
|
||||
_endOffset.dx, _endOffset.dx - _startOffset.dx); //x轴匀速模拟 加速度为0
|
||||
final GravitySimulation spx = GravitySimulation(0, _startOffset!.dx,
|
||||
_endOffset.dx, _endOffset.dx - _startOffset!.dx); //x轴匀速模拟 加速度为0
|
||||
|
||||
final Animation<double> opacity = Tween<double>(begin: 1, end: 0).animate(
|
||||
CurvedAnimation(
|
||||
@@ -69,13 +69,13 @@ class ParabolicAnimationWidget extends AnimatedWidget {
|
||||
RenderBox stackBox = stackKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
Offset stackBoxOffset = stackBox.globalToLocal(Offset.zero);
|
||||
|
||||
EdgeInsets startMargin = _margin(startKey)!;
|
||||
EdgeInsets startMargin = _margin(startKey);
|
||||
RenderBox startBox = startKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
_startOffset = startBox.localToGlobal(Offset(
|
||||
startMargin.left + startAdjustOffset.dx,
|
||||
stackBoxOffset.dy + startMargin.top + startAdjustOffset.dy));
|
||||
|
||||
EdgeInsets endMargin = _margin(endKey)!;
|
||||
EdgeInsets endMargin = _margin(endKey);
|
||||
RenderBox endBox = endKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
_endOffset = endBox.localToGlobal(Offset(
|
||||
endMargin.left + endAdjustOffset.dx,
|
||||
@@ -83,9 +83,9 @@ class ParabolicAnimationWidget extends AnimatedWidget {
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsetsGeometry _margin(GlobalKey key) {
|
||||
EdgeInsets _margin(GlobalKey key) {
|
||||
final Widget widget = key.currentContext!.widget;
|
||||
EdgeInsetsGeometry margin = (widget is Container) ? (widget.margin ?? EdgeInsets.zero) : EdgeInsets.zero;
|
||||
return margin ?? EdgeInsets.zero;
|
||||
final mg = (widget is Container) ? widget.margin : null;
|
||||
return mg is EdgeInsets ? mg : EdgeInsets.zero;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class PaymentVerificationCodeDialogState extends State<PaymentVerificationCodeDi
|
||||
String getCodeText = '';
|
||||
String paymentCodeEncrypt = '';
|
||||
|
||||
late String verifyMethod;
|
||||
String? verifyMethod;
|
||||
late String verifyName;
|
||||
|
||||
final TextEditingController _pinPutController = TextEditingController();
|
||||
|
||||
@@ -18,7 +18,7 @@ class PopupAnimationWidget extends AnimatedWidget {
|
||||
this.popupOffset = Offset.zero,
|
||||
}) : super(listenable: animation);
|
||||
|
||||
late Offset _startOffset;
|
||||
Offset? _startOffset;
|
||||
Offset _offset = Offset.zero;
|
||||
|
||||
@override
|
||||
@@ -29,7 +29,7 @@ class PopupAnimationWidget extends AnimatedWidget {
|
||||
.animate(CurvedAnimation(
|
||||
parent: animation, curve: Interval(0, 0.4, curve: Curves.ease)));
|
||||
_offset =
|
||||
Offset(_startOffset.dx, _startOffset.dy - opacityAnimation.value * 80);
|
||||
Offset(_startOffset!.dx, _startOffset!.dy - opacityAnimation.value * 80);
|
||||
|
||||
return Positioned(
|
||||
left: _offset.dx,
|
||||
@@ -52,7 +52,7 @@ class PopupAnimationWidget extends AnimatedWidget {
|
||||
final RenderBox stackBox = stackKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
final Offset stackBoxOffset = stackBox.globalToLocal(Offset.zero);
|
||||
|
||||
final EdgeInsets startMargin = _margin(startKey)!;
|
||||
final EdgeInsets startMargin = _margin(startKey);
|
||||
final RenderBox startBox = startKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
|
||||
_startOffset = startBox.localToGlobal(Offset(
|
||||
@@ -61,10 +61,9 @@ class PopupAnimationWidget extends AnimatedWidget {
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsetsGeometry _margin(GlobalKey key) {
|
||||
EdgeInsets _margin(GlobalKey key) {
|
||||
final Widget widget = key.currentContext!.widget;
|
||||
final EdgeInsetsGeometry margin =
|
||||
(widget is Container) ? widget.margin : EdgeInsets.zero;
|
||||
return margin ?? EdgeInsets.zero;
|
||||
final mg = (widget is Container) ? widget.margin : null;
|
||||
return mg is EdgeInsets ? mg : EdgeInsets.zero;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class ReadMoreTextState extends State<ReadMoreText> {
|
||||
widget.locale ?? Localizations.localeOf(context);
|
||||
|
||||
final colorClickableText =
|
||||
widget.colorClickableText ?? Theme.of(context).accentColor;
|
||||
widget.colorClickableText ?? Theme.of(context).colorScheme.secondary;
|
||||
|
||||
TextSpan link = TextSpan(
|
||||
text: _readMore ? widget.trimCollapsedText : widget.trimExpandedText,
|
||||
|
||||
@@ -602,7 +602,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
|
||||
|
||||
|
||||
class PanelController{
|
||||
late _SlidingUpPanelState _panelState;
|
||||
_SlidingUpPanelState? _panelState;
|
||||
|
||||
void _addState(_SlidingUpPanelState panelState){
|
||||
this._panelState = panelState;
|
||||
@@ -616,27 +616,27 @@ class PanelController{
|
||||
/// Closes the sliding panel to its collapsed state (i.e. to the minHeight)
|
||||
Future<void> close(){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._close();
|
||||
return _panelState!._close();
|
||||
}
|
||||
|
||||
/// Opens the sliding panel fully
|
||||
/// (i.e. to the maxHeight)
|
||||
Future<void> open(){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._open();
|
||||
return _panelState!._open();
|
||||
}
|
||||
|
||||
/// Hides the sliding panel (i.e. is invisible)
|
||||
Future<void> hide(){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._hide();
|
||||
return _panelState!._hide();
|
||||
}
|
||||
|
||||
/// Shows the sliding panel in its collapsed state
|
||||
/// (i.e. "un-hide" the sliding panel)
|
||||
Future<void> show(){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._show();
|
||||
return _panelState!._show();
|
||||
}
|
||||
|
||||
/// Animates the panel position to the value.
|
||||
@@ -647,7 +647,7 @@ class PanelController{
|
||||
Future<void> animatePanelToPosition(double value, {Duration? duration, Curve curve = Curves.linear}){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
assert(0.0 <= value && value <= 1.0);
|
||||
return _panelState._animatePanelToPosition(value, duration: duration, curve: curve);
|
||||
return _panelState!._animatePanelToPosition(value, duration: duration, curve: curve);
|
||||
}
|
||||
|
||||
/// Animates the panel position to the snap point
|
||||
@@ -656,8 +656,8 @@ class PanelController{
|
||||
/// (optional) curve specifies the easing behavior of the animation.
|
||||
Future<void> animatePanelToSnapPoint({Duration? duration, Curve curve = Curves.linear}){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
assert(_panelState.widget.snapPoint != null, "SlidingUpPanel snapPoint property must not be null");
|
||||
return _panelState._animatePanelToSnapPoint(duration: duration, curve: curve);
|
||||
assert(_panelState!.widget.snapPoint != null, "SlidingUpPanel snapPoint property must not be null");
|
||||
return _panelState!._animatePanelToSnapPoint(duration: duration, curve: curve);
|
||||
}
|
||||
|
||||
/// Sets the panel position (without animation).
|
||||
@@ -666,7 +666,7 @@ class PanelController{
|
||||
set panelPosition(double value){
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
assert(0.0 <= value && value <= 1.0);
|
||||
_panelState._panelPosition = value;
|
||||
_panelState!._panelPosition = value;
|
||||
}
|
||||
|
||||
/// Gets the current panel position.
|
||||
@@ -677,35 +677,35 @@ class PanelController{
|
||||
/// 1.0 is full open.
|
||||
double get panelPosition{
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._panelPosition;
|
||||
return _panelState!._panelPosition;
|
||||
}
|
||||
|
||||
/// Returns whether or not the panel is
|
||||
/// currently animating.
|
||||
bool get isPanelAnimating{
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._isPanelAnimating;
|
||||
return _panelState!._isPanelAnimating;
|
||||
}
|
||||
|
||||
/// Returns whether or not the
|
||||
/// panel is open.
|
||||
bool get isPanelOpen{
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._isPanelOpen;
|
||||
return _panelState!._isPanelOpen;
|
||||
}
|
||||
|
||||
/// Returns whether or not the
|
||||
/// panel is closed.
|
||||
bool get isPanelClosed{
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._isPanelClosed;
|
||||
return _panelState!._isPanelClosed;
|
||||
}
|
||||
|
||||
/// Returns whether or not the
|
||||
/// panel is shown/hidden.
|
||||
bool get isPanelShown{
|
||||
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
|
||||
return _panelState._isPanelShown;
|
||||
return _panelState!._isPanelShown;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
bool canSubmit = false;
|
||||
|
||||
List<dynamic> stores = [];
|
||||
late Map<String, dynamic> service;
|
||||
Map<String, dynamic>? service;
|
||||
dynamic selectedStore;
|
||||
|
||||
late Group group;
|
||||
@@ -269,7 +269,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
service['description'],
|
||||
service!['description'],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -277,7 +277,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
service['options'][0]['name'],
|
||||
service!['options'][0]['name'],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -285,7 +285,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'\$${service['options'][0]['price']}',
|
||||
'\$${service!['options'][0]['price']}',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@@ -45,7 +45,7 @@ class MobileCheckout extends StatefulWidget {
|
||||
}
|
||||
|
||||
class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||
late CartInfo cartInfo;
|
||||
CartInfo? cartInfo;
|
||||
Address? shipAddress;
|
||||
late bool canSubmit;
|
||||
late List<ErrorMessage> errorMessages;
|
||||
@@ -53,7 +53,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
late List<BookingDateTime> bookingDateTimeList;
|
||||
late List<PaymentPlatform> paymentPlatforms;
|
||||
TextValue? durationInTraffic;
|
||||
late int selectedCoupon;
|
||||
int? selectedCoupon;
|
||||
double couponDiscountAmount = 0;
|
||||
late List<Coupon> coupons;
|
||||
|
||||
@@ -79,7 +79,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
GlobalKey slidingUpPanelKey = GlobalKey();
|
||||
late SlidingUpPanel slidingUpPanel;
|
||||
PanelController panelController = PanelController();
|
||||
late Widget panel;
|
||||
Widget? panel;
|
||||
|
||||
late double subtotal;
|
||||
|
||||
@@ -102,7 +102,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
);
|
||||
}
|
||||
|
||||
if (cartInfo.businessInfo!.deliveryPickup == false && cartInfo.businessInfo!.deliveryCanadaPost == false && cartInfo.businessInfo!.deliveryStoreDelivery == false) {
|
||||
if (cartInfo!.businessInfo!.deliveryPickup == false && cartInfo!.businessInfo!.deliveryCanadaPost == false && cartInfo!.businessInfo!.deliveryStoreDelivery == false) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
child: Center(
|
||||
@@ -197,13 +197,13 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'\$${(cartInfo.totalPrice! - couponDiscountAmount).toStringAsFixed(2)}',
|
||||
'\$${(cartInfo!.totalPrice! - couponDiscountAmount).toStringAsFixed(2)}',
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
cartInfo.businessInfo!.isPublic == true ? SizedBox.shrink() : Container(
|
||||
cartInfo!.businessInfo!.isPublic == true ? SizedBox.shrink() : Container(
|
||||
padding: EdgeInsets.only(top: 2.0, bottom: 2.0, left: 5.0, right: 5.0),
|
||||
width: 100.0,
|
||||
color: Colors.red,
|
||||
@@ -267,8 +267,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
itemCount: 6,
|
||||
addAutomaticKeepAlives: true,
|
||||
itemBuilder: (BuildContext context, int position) {
|
||||
var deliveryTimeInSeconds = cartInfo.businessInfo!.shippingTime! * 60 + (durationInTraffic != null ? durationInTraffic!.value ?? 0 : 0);
|
||||
print('aaa: $deliveryTimeInSeconds');
|
||||
var deliveryTimeInSeconds = cartInfo!.businessInfo!.shippingTime! * 60 + (durationInTraffic != null ? durationInTraffic!.value ?? 0 : 0);
|
||||
DateTime now = DateTime.now();
|
||||
var formatter = DateFormat('H:mm');
|
||||
String deliveryAt = formatter.format(now.add(Duration(seconds: deliveryTimeInSeconds)));
|
||||
@@ -281,7 +280,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).table_token(store.state.tableNumber!),
|
||||
S.of(context).table_token(store.state.tableNumber ?? ''),
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -341,7 +340,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
);
|
||||
switch (position) {
|
||||
case 0:
|
||||
if (cartInfo.businessInfo!.deliveryPickup == true) {
|
||||
if (cartInfo!.businessInfo!.deliveryPickup == true) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 16.0, bottom: 16.0, left: 16.0, right: 16.0),
|
||||
@@ -389,7 +388,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
child: Text(
|
||||
cartInfo.businessInfo!.name!,
|
||||
cartInfo!.businessInfo!.name!,
|
||||
style: TextStyle(
|
||||
fontSize: 17.0,
|
||||
),
|
||||
@@ -398,7 +397,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 5.0),
|
||||
child: Text(
|
||||
cartInfo.businessInfo!.address!.addressLine1!,
|
||||
cartInfo!.businessInfo!.address!.addressLine1!,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black45,
|
||||
@@ -406,9 +405,9 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: cartInfo.businessInfo!.address!.addressLine2 != null
|
||||
&& cartInfo.businessInfo!.address!.addressLine2!.length > 0 ?
|
||||
Text(cartInfo.businessInfo!.address!.addressLine2!,
|
||||
child: cartInfo!.businessInfo!.address!.addressLine2 != null
|
||||
&& cartInfo!.businessInfo!.address!.addressLine2!.length > 0 ?
|
||||
Text(cartInfo!.businessInfo!.address!.addressLine2!,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black45,
|
||||
@@ -417,7 +416,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
'${cartInfo.businessInfo!.address!.city}, ${cartInfo.businessInfo!.address!.state}, ${cartInfo.businessInfo!.address!.zip}',
|
||||
'${cartInfo!.businessInfo!.address!.city}, ${cartInfo!.businessInfo!.address!.state}, ${cartInfo!.businessInfo!.address!.zip}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black45,
|
||||
@@ -427,7 +426,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 5.0),
|
||||
child: Text(
|
||||
'Tel: ${cartInfo.businessInfo!.phone}',
|
||||
'Tel: ${cartInfo!.businessInfo!.phone}',
|
||||
style: TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Colors.black54,
|
||||
@@ -504,7 +503,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Routes.router.navigateTo(context, '/my-addresses/${cartInfo.businessInfo!.id}', replace: true);
|
||||
Routes.router.navigateTo(context, '/my-addresses/${cartInfo!.businessInfo!.id}', replace: true);
|
||||
},
|
||||
);
|
||||
break;
|
||||
@@ -583,7 +582,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
},
|
||||
);
|
||||
}
|
||||
if (cartInfo.businessInfo!.instanceDelivery != true) {
|
||||
if (cartInfo!.businessInfo!.instanceDelivery != true) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 0.0, bottom: 16.0),
|
||||
child: Text(S.of(context).no_instance_delivery_desc),
|
||||
@@ -712,7 +711,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(bottom: 10.0),
|
||||
child: Text(
|
||||
cartInfo.businessInfo!.name!,
|
||||
cartInfo!.businessInfo!.name!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
@@ -731,9 +730,9 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
|
||||
subtotal = 0.0;
|
||||
|
||||
for (var i = 0; i < cartInfo.productList!.length; i++) {
|
||||
subtotal += cartInfo.productList![i].totalPrice!;
|
||||
column.children.add(lineItem(cartInfo.productList![i]));
|
||||
for (var i = 0; i < cartInfo!.productList!.length; i++) {
|
||||
subtotal += cartInfo!.productList![i].totalPrice!;
|
||||
column.children.add(lineItem(cartInfo!.productList![i]));
|
||||
}
|
||||
column.children.add(GestureDetector(
|
||||
child: Container(
|
||||
@@ -826,8 +825,8 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
),
|
||||
));
|
||||
|
||||
if (cartInfo.extraFeeList!.length > 0) {
|
||||
for (var i = 0; i < cartInfo.extraFeeList!.length; i++) {
|
||||
if (cartInfo!.extraFeeList!.length > 0) {
|
||||
for (var i = 0; i < cartInfo!.extraFeeList!.length; i++) {
|
||||
column.children.add(Container(
|
||||
padding: EdgeInsets.only(bottom: 16.0),
|
||||
alignment: Alignment.centerRight,
|
||||
@@ -838,7 +837,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
Container(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
S.of(context).extra_fee_token(cartInfo.extraFeeList![i].name!, cartInfo.extraFeeList![i].rate!),
|
||||
S.of(context).extra_fee_token(cartInfo!.extraFeeList![i].name!, cartInfo!.extraFeeList![i].rate!),
|
||||
style: TextStyle(
|
||||
color: Colors.grey,
|
||||
),
|
||||
@@ -848,7 +847,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
width: 100.0,
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'${cartInfo.extraFeeList![i].price!.toStringAsFixed(2)}'
|
||||
'${cartInfo!.extraFeeList![i].price!.toStringAsFixed(2)}'
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -876,7 +875,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
width: 100.0,
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'${(cartInfo.totalPrice! - couponDiscountAmount).toStringAsFixed(2)}',
|
||||
'${(cartInfo!.totalPrice! - couponDiscountAmount).toStringAsFixed(2)}',
|
||||
style: TextStyle(
|
||||
fontSize: 19.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -1074,7 +1073,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
shippingRates = (response.data['shipping_rates'] as List).map((e) => ShippingRate.fromJson(e)).toList();
|
||||
selectedShippingRate = (response.data['selected_shipping_rate'] as String).length > 0 ? ShippingRate.fromJson(json.decode(response.data['selected_shipping_rate'])) : null;
|
||||
int i = 0;
|
||||
if (cartInfo.businessInfo!.deliveryStoreDelivery == true) {
|
||||
if (cartInfo!.businessInfo!.deliveryStoreDelivery == true) {
|
||||
shippingMethodLabels.add(S.of(context).delivery);
|
||||
shippingMethodIcons.add(Icons.directions_car);
|
||||
if (deliveryMethod == 'store-delivery') {
|
||||
@@ -1082,7 +1081,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (cartInfo.businessInfo!.deliveryCanadaPost == true) {
|
||||
if (cartInfo!.businessInfo!.deliveryCanadaPost == true) {
|
||||
shippingMethodLabels.add(S.of(context).canada_post);
|
||||
shippingMethodIcons.add(Icons.local_shipping);
|
||||
if (deliveryMethod == 'canada-post') {
|
||||
@@ -1090,7 +1089,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (cartInfo.businessInfo!.deliveryPickup == true) {
|
||||
if (cartInfo!.businessInfo!.deliveryPickup == true) {
|
||||
shippingMethodLabels.add(S.of(context).pickup);
|
||||
shippingMethodIcons.add(Icons.store);
|
||||
if (deliveryMethod == 'pickup') {
|
||||
@@ -1681,7 +1680,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
decoration: selectedCoupon == 0 ? BoxDecoration(
|
||||
color: subtotal > cartInfo.businessInfo!.minPrice! ? Colors
|
||||
color: subtotal > cartInfo!.businessInfo!.minPrice! ? Colors
|
||||
.transparent : Colors.black38,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
@@ -1702,7 +1701,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
),
|
||||
),
|
||||
) : BoxDecoration(
|
||||
color: subtotal > cartInfo.businessInfo!.minPrice! ? Colors
|
||||
color: subtotal > cartInfo!.businessInfo!.minPrice! ? Colors
|
||||
.transparent : Colors.black38,
|
||||
),
|
||||
child: Row(
|
||||
@@ -1743,7 +1742,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
decoration: selectedCoupon == coupon.id ? BoxDecoration(
|
||||
color: subtotal > cartInfo.businessInfo!.minPrice! ? Colors
|
||||
color: subtotal > cartInfo!.businessInfo!.minPrice! ? Colors
|
||||
.transparent : Colors.black38,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
@@ -1917,7 +1916,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [],
|
||||
);
|
||||
if (cartInfo.businessInfo!.quickInputs!.length > 0) {
|
||||
if (cartInfo!.businessInfo!.quickInputs!.length > 0) {
|
||||
Wrap w = Wrap(
|
||||
children: [],
|
||||
);
|
||||
@@ -1931,8 +1930,8 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
),
|
||||
),
|
||||
));
|
||||
for (int i = 0; i < cartInfo.businessInfo!.quickInputs!.length; i++) {
|
||||
String qi = cartInfo.businessInfo!.quickInputs![i].value!;
|
||||
for (int i = 0; i < cartInfo!.businessInfo!.quickInputs!.length; i++) {
|
||||
String qi = cartInfo!.businessInfo!.quickInputs![i].value!;
|
||||
w.children.add(TextButton(
|
||||
child: Text(
|
||||
qi,
|
||||
@@ -2196,7 +2195,7 @@ class MobileCheckoutState extends State<MobileCheckout> with SingleTickerProvide
|
||||
},
|
||||
businessId: widget.businessId,
|
||||
body: {
|
||||
'cart_id': cartInfo.id,
|
||||
'cart_id': cartInfo!.id,
|
||||
'remark': orderRemark,
|
||||
'booked_at': bookingTimeList.length > 0
|
||||
? bookingTimeList[bookingTimeIndex].unixTime
|
||||
|
||||
@@ -114,7 +114,7 @@ class MobileCouponsState extends State<MobileCoupons> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
coupon.store != null ? coupon.store!.name : S.of(context).general_coupon,
|
||||
coupon.store != null ? coupon.store!.name! : S.of(context).general_coupon,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -146,7 +146,7 @@ class MobileCouponsState extends State<MobileCoupons> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: !coupon.isPercentage ?
|
||||
child: coupon.isPercentage != true ?
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -20,9 +20,9 @@ import '../../utils/util_web.dart'
|
||||
if (dart.library.io) '../../utils/util_io.dart';
|
||||
import '../../utils/utils.dart';
|
||||
|
||||
late MediaQueryData mediaQuery;
|
||||
late double statusBarHeight;
|
||||
late double screenHeight;
|
||||
MediaQueryData? mediaQuery;
|
||||
double? statusBarHeight;
|
||||
double? screenHeight;
|
||||
|
||||
class MobileMe extends StatefulWidget {
|
||||
final Key? key;
|
||||
@@ -43,7 +43,7 @@ class MobileMeState extends State<MobileMe> {
|
||||
User? _user;
|
||||
|
||||
late ShopScrollCoordinator _shopCoordinator;
|
||||
late ShopScrollController _pageScrollController;
|
||||
ShopScrollController? _pageScrollController;
|
||||
final double _sliverAppBarInitHeight = 165.0;
|
||||
final double _appBarHeight = 85.0;
|
||||
|
||||
@@ -65,13 +65,13 @@ class MobileMeState extends State<MobileMe> {
|
||||
}
|
||||
|
||||
mediaQuery ??= MediaQuery.of(context);
|
||||
screenHeight ??= mediaQuery.size.height;
|
||||
statusBarHeight ??= mediaQuery.padding.top;
|
||||
screenHeight ??= mediaQuery!.size.height;
|
||||
statusBarHeight ??= mediaQuery!.padding.top;
|
||||
|
||||
_pageScrollController ??=
|
||||
_shopCoordinator.pageScrollController(_sliverAppBarInitHeight * -1.0);
|
||||
_shopCoordinator.pinnedHeaderSliverHeightBuilder ??= () {
|
||||
return statusBarHeight + kToolbarHeight + _appBarHeight;
|
||||
return statusBarHeight! + kToolbarHeight + _appBarHeight;
|
||||
};
|
||||
|
||||
_listScrollController1 = _shopCoordinator.newChildScrollController();
|
||||
|
||||
@@ -31,7 +31,7 @@ class MobileNewComment extends StatefulWidget {
|
||||
}
|
||||
|
||||
class MobileNewCommentState extends State<MobileNewComment> {
|
||||
late Comment comment;
|
||||
Comment? comment;
|
||||
|
||||
late bool _showProgress;
|
||||
|
||||
@@ -167,8 +167,8 @@ class MobileNewCommentState extends State<MobileNewComment> {
|
||||
children: <Widget>[],
|
||||
);
|
||||
|
||||
if (comment != null && comment.images!.length > 0) {
|
||||
for (ProductImage image in comment.images!) {
|
||||
if (comment != null && comment!.images!.length > 0) {
|
||||
for (ProductImage image in comment!.images!) {
|
||||
row.children.add(
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10.0),
|
||||
@@ -239,7 +239,7 @@ class MobileNewCommentState extends State<MobileNewComment> {
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
size: 60.0,
|
||||
color: comment == null || comment.images!.length < 3 ? Colors.lightBlue : Colors.black12,
|
||||
color: comment == null || comment!.images!.length < 3 ? Colors.lightBlue : Colors.black12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white70,
|
||||
@@ -264,13 +264,13 @@ class MobileNewCommentState extends State<MobileNewComment> {
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (comment == null || comment.images!.length < 3) {
|
||||
if (comment == null || comment!.images!.length < 3) {
|
||||
showDialog(
|
||||
context: mainContext,
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return Util().getPicture(mainContext, store.state.user!,
|
||||
commentId: comment != null ? comment.id! : 0,
|
||||
commentId: comment != null ? comment!.id! : 0,
|
||||
orderId: widget.orderId);
|
||||
}
|
||||
);
|
||||
@@ -329,7 +329,7 @@ class MobileNewCommentState extends State<MobileNewComment> {
|
||||
}
|
||||
},
|
||||
queryParameters: {
|
||||
'comment_id': comment != null ? comment.id! : 0,
|
||||
'comment_id': comment != null ? comment!.id! : 0,
|
||||
},
|
||||
).catchError((error) {
|
||||
Utils.showMessageDialog(context, error);
|
||||
@@ -356,7 +356,7 @@ class MobileNewCommentState extends State<MobileNewComment> {
|
||||
isFormData: true,
|
||||
body: {
|
||||
'order_id': widget.orderId,
|
||||
'comment_id': comment != null ? comment.id! : 0,
|
||||
'comment_id': comment != null ? comment!.id! : 0,
|
||||
'content': commentController.text,
|
||||
'rating': rating.round(),
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@ class MobilePayNow extends StatefulWidget {
|
||||
}
|
||||
|
||||
class MobilePayNowState extends State<MobilePayNow> {
|
||||
late Order order;
|
||||
Order? order;
|
||||
late List<PaymentPlatform> paymentPlatforms;
|
||||
late User _user;
|
||||
|
||||
@@ -74,7 +74,7 @@ class MobilePayNowState extends State<MobilePayNow> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'\$${order.totalPrice!.toStringAsFixed(2)}',
|
||||
'\$${order!.totalPrice!.toStringAsFixed(2)}',
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -194,7 +194,7 @@ class MobilePayNowState extends State<MobilePayNow> {
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Util.goPayment(context, order, paymentPlatform);
|
||||
Util.goPayment(context, order!, paymentPlatform);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ import '../../widgets/general/add_remove_button.dart';
|
||||
import '../../widgets/general/carousel.dart';
|
||||
import '../../widgets/general/show_price.dart';
|
||||
|
||||
late MediaQueryData mediaQuery;
|
||||
late double statusBarHeight;
|
||||
late double screenHeight;
|
||||
late double screenWidth;
|
||||
MediaQueryData? mediaQuery;
|
||||
double? statusBarHeight;
|
||||
double? screenHeight;
|
||||
double? screenWidth;
|
||||
|
||||
class MobileProductDetailPage extends StatefulWidget {
|
||||
final Business business;
|
||||
@@ -45,15 +45,15 @@ class MobileProductDetailPage extends StatefulWidget {
|
||||
class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late ShopScrollCoordinator _shopCoordinator;
|
||||
late ShopScrollController _pageScrollController;
|
||||
ShopScrollController? _pageScrollController;
|
||||
|
||||
late TabController _tabController;
|
||||
|
||||
late double _sliverAppBarInitHeight;
|
||||
late double _sliverAppBarMaxHeight;
|
||||
double? _sliverAppBarInitHeight;
|
||||
double? _sliverAppBarMaxHeight;
|
||||
final double _tabBarHeight = 50;
|
||||
|
||||
late ProductDetail productDetail;
|
||||
ProductDetail? productDetail;
|
||||
|
||||
late bool refresh;
|
||||
|
||||
@@ -75,18 +75,18 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
refresh = false;
|
||||
|
||||
mediaQuery ??= MediaQuery.of(context);
|
||||
screenHeight ??= mediaQuery.size.height;
|
||||
screenWidth ??= mediaQuery.size.width;
|
||||
statusBarHeight ??= mediaQuery.padding.top;
|
||||
screenHeight ??= mediaQuery!.size.height;
|
||||
screenWidth ??= mediaQuery!.size.width;
|
||||
statusBarHeight ??= mediaQuery!.padding.top;
|
||||
|
||||
_sliverAppBarInitHeight ??= screenWidth;
|
||||
_sliverAppBarMaxHeight ??= screenWidth;
|
||||
|
||||
_pageScrollController ??= _shopCoordinator
|
||||
.pageScrollController(_sliverAppBarMaxHeight - _sliverAppBarInitHeight);
|
||||
.pageScrollController(_sliverAppBarMaxHeight! - _sliverAppBarInitHeight!);
|
||||
|
||||
_shopCoordinator.pinnedHeaderSliverHeightBuilder ??= () {
|
||||
return statusBarHeight + kToolbarHeight + _tabBarHeight;
|
||||
return statusBarHeight! + kToolbarHeight + _tabBarHeight;
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
@@ -110,10 +110,10 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
},
|
||||
),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
expandedHeight: _sliverAppBarMaxHeight - 30.0,
|
||||
expandedHeight: _sliverAppBarMaxHeight! - 30.0,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: Carousel(
|
||||
height: _sliverAppBarInitHeight + 0.0,
|
||||
height: _sliverAppBarInitHeight! + 0.0,
|
||||
pages: _getProductPictures(context),
|
||||
autoPlay: false,
|
||||
),
|
||||
@@ -133,7 +133,7 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
padding:
|
||||
EdgeInsets.only(left: 10.0, top: 5.0, right: 10.0),
|
||||
child: Text(
|
||||
productDetail.name!,
|
||||
productDetail!.name!,
|
||||
style: TextStyle(
|
||||
fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
maxLines: 1,
|
||||
@@ -144,12 +144,12 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
ShowPrice(
|
||||
productDetail.price!,
|
||||
productDetail!.price!,
|
||||
currencySign: '\$',
|
||||
fontWeight: FontWeight.bold,
|
||||
smallFontSize: 14,
|
||||
largeFontSize: 18,
|
||||
regularPrice: productDetail.regularPrice,
|
||||
regularPrice: productDetail!.regularPrice,
|
||||
),
|
||||
Container(
|
||||
child: AddRemoveButton(
|
||||
@@ -204,35 +204,35 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
(productDetail.subproducts!.length > 0) ?
|
||||
subProducts(productDetail.subproducts!) :
|
||||
(productDetail!.subproducts!.length > 0) ?
|
||||
subProducts(productDetail!.subproducts!) :
|
||||
SizedBox.shrink(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 10.0, left: 10.0, right: 10.0),
|
||||
child: Text(
|
||||
productDetail.description!,
|
||||
productDetail!.description!,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0, color: Colors.black54),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: (productDetail.description2 != null &&
|
||||
!productDetail.description2!.isEmpty)
|
||||
child: (productDetail!.description2 != null &&
|
||||
!productDetail!.description2!.isEmpty)
|
||||
? Text(
|
||||
'${productDetail.description2}',
|
||||
'${productDetail!.description2}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0, color: Colors.black54),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
productDetail.detailDescription != null
|
||||
productDetail!.detailDescription != null
|
||||
? Container(
|
||||
padding:
|
||||
EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: MarkdownBody(
|
||||
data: '${productDetail.detailDescription}',
|
||||
data: '${productDetail!.detailDescription}',
|
||||
shrinkWrap: true,
|
||||
),
|
||||
)
|
||||
@@ -250,7 +250,7 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text(
|
||||
S.of(context).weight_token(productDetail.weight!),
|
||||
S.of(context).weight_token(productDetail!.weight!),
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.black54,
|
||||
@@ -260,9 +260,9 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
Container(
|
||||
child: Text(
|
||||
S.of(context).dimentions_token(
|
||||
productDetail.dimentionsLength!,
|
||||
productDetail.dimentionsWidth!,
|
||||
productDetail.dimentionsHeight!),
|
||||
productDetail!.dimentionsLength!,
|
||||
productDetail!.dimentionsWidth!,
|
||||
productDetail!.dimentionsHeight!),
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.black54,
|
||||
@@ -418,10 +418,10 @@ class MobileProductDetailPageState extends State<MobileProductDetailPage>
|
||||
List<Widget> _getProductPictures(BuildContext context) {
|
||||
var pages = <Widget>[];
|
||||
List<String> images = [];
|
||||
images.add(productDetail.image!);
|
||||
for (var i = 0; i < productDetail.images!.length; i++) {
|
||||
images.add(productDetail!.image!);
|
||||
for (var i = 0; i < productDetail!.images!.length; i++) {
|
||||
// print('>>https:' + productDetail.images[i].image);
|
||||
images.add(productDetail.images![i].image!);
|
||||
images.add(productDetail!.images![i].image!);
|
||||
}
|
||||
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
|
||||
@@ -28,7 +28,7 @@ class MobileViewBlog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class MobileViewBlogState extends State<MobileViewBlog> {
|
||||
late Blog blog;
|
||||
Blog? blog;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -85,7 +85,7 @@ class MobileViewBlogState extends State<MobileViewBlog> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${blog.title}',
|
||||
'${blog!.title}',
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.black
|
||||
@@ -93,7 +93,7 @@ class MobileViewBlogState extends State<MobileViewBlog> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, blog.createdAt!),
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, blog!.createdAt!),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black38,
|
||||
@@ -106,7 +106,7 @@ class MobileViewBlogState extends State<MobileViewBlog> {
|
||||
width: double.maxFinite,
|
||||
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0),
|
||||
child: Text(
|
||||
'${blog.body}',
|
||||
'${blog!.body}',
|
||||
style: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: 17.0,
|
||||
@@ -124,11 +124,11 @@ class MobileViewBlogState extends State<MobileViewBlog> {
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
padding: EdgeInsets.only(top: 16.0, bottom: 16.0, left: 16.0, right: 16.0),
|
||||
child: (blog.imageUrl != null) ?
|
||||
child: (blog!.imageUrl != null) ?
|
||||
Container(
|
||||
width: min(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height) - 100.0,
|
||||
height: min(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height) - 100.0,
|
||||
child: Util.showImage('https:${blog.imageUrl}'),
|
||||
child: Util.showImage('https:${blog!.imageUrl}'),
|
||||
) : SizedBox.shrink(),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
|
||||
@@ -34,7 +34,7 @@ class MobileViewTicket extends StatefulWidget {
|
||||
class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
late Ticket ticket;
|
||||
Ticket? ticket;
|
||||
|
||||
final issueMsgController = TextEditingController();
|
||||
|
||||
@@ -103,14 +103,14 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).ticket_number_token(ticket.id!),
|
||||
S.of(context).ticket_number_token(ticket!.id!),
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.black
|
||||
),
|
||||
),
|
||||
Text(
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, ticket.createdAt!),
|
||||
Utils.utcDatetimeStringToLocalDatetimeString(context, ticket!.createdAt!),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black38,
|
||||
@@ -123,7 +123,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
width: double.maxFinite,
|
||||
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0),
|
||||
child: Text(
|
||||
'${ticket.issue!.msg}',
|
||||
'${ticket!.issue!.msg}',
|
||||
style: TextStyle(
|
||||
color: Colors.black54,
|
||||
fontSize: 14.0,
|
||||
@@ -141,7 +141,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
padding: EdgeInsets.only(top: 16.0, bottom: 16.0, left: 16.0, right: 16.0),
|
||||
child: showGalleryImages(mainContext, ticket.issue!.files!),
|
||||
child: showGalleryImages(mainContext, ticket!.issue!.files!),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
@@ -171,7 +171,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
],
|
||||
);
|
||||
|
||||
if (ticket.followUps!.length > 0) {
|
||||
if (ticket!.followUps!.length > 0) {
|
||||
view.children.add(
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
@@ -185,8 +185,8 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
),
|
||||
),
|
||||
);
|
||||
for (int i = 0; i < ticket.followUps!.length; i++) {
|
||||
FollowUp followUp = ticket.followUps![i];
|
||||
for (int i = 0; i < ticket!.followUps!.length; i++) {
|
||||
FollowUp followUp = ticket!.followUps![i];
|
||||
view.children.add(
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
@@ -353,7 +353,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
),
|
||||
);
|
||||
|
||||
if (ticket.followUps!.length > 0) {
|
||||
if (ticket!.followUps!.length > 0) {
|
||||
view.children.add(
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
@@ -375,7 +375,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
);
|
||||
}
|
||||
|
||||
if (ticket.isClosed == true) {
|
||||
if (ticket!.isClosed == true) {
|
||||
view.children.add(
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
|
||||
@@ -393,7 +393,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 0.0, bottom: 30.0),
|
||||
child: TextLink(
|
||||
S.of(context).new_ticket,
|
||||
'/new-ticket/${ticket.store!.id}',
|
||||
'/new-ticket/${ticket!.store!.id}',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -689,7 +689,7 @@ class MobileViewTicketState extends State<MobileViewTicket> {
|
||||
child: Text(S.of(context).ok),
|
||||
onPressed: () {
|
||||
Routes.router.navigateTo(context,
|
||||
'/my-support/${ticket.store!.id}',
|
||||
'/my-support/${ticket!.store!.id}',
|
||||
replace: true,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -37,10 +37,10 @@ import 'product_item.dart';
|
||||
import 'product_search.dart';
|
||||
import 'shopping_cart_bar.dart';
|
||||
|
||||
late MediaQueryData mediaQuery;
|
||||
late double statusBarHeight;
|
||||
late double screenWidth;
|
||||
late double screenHeight;
|
||||
MediaQueryData? mediaQuery;
|
||||
double? statusBarHeight;
|
||||
double? screenWidth;
|
||||
double? screenHeight;
|
||||
|
||||
class Shop extends StatefulWidget {
|
||||
final int? businessId;
|
||||
@@ -56,8 +56,8 @@ class ShopState extends State<Shop>
|
||||
|
||||
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
late Business _business;
|
||||
late List<CategoryProducts> _categoryProducts;
|
||||
Business? _business;
|
||||
List<CategoryProducts>? _categoryProducts;
|
||||
late List<Product> _featuredProducts;
|
||||
late List<Product> _hotSaleProducts;
|
||||
|
||||
@@ -80,13 +80,13 @@ class ShopState extends State<Shop>
|
||||
bool refresh = false;
|
||||
|
||||
late ShopScrollCoordinator _shopCoordinator;
|
||||
late ShopScrollController _pageScrollController;
|
||||
ShopScrollController? _pageScrollController;
|
||||
late TabController _tabController;
|
||||
final double _sliverAppBarInitHeight = 150.0;
|
||||
final double _tabBarHeight = 50.0;
|
||||
late double _sliverAppBarMaxHeight;
|
||||
double? _sliverAppBarMaxHeight;
|
||||
|
||||
late ShopScrollController _listScrollController1;
|
||||
ShopScrollController? _listScrollController1;
|
||||
late ShopScrollController _listScrollController2;
|
||||
late ShopScrollController _listScrollController3;
|
||||
|
||||
@@ -94,7 +94,7 @@ class ShopState extends State<Shop>
|
||||
GlobalKey stackKey = GlobalKey();
|
||||
GlobalKey endKey = GlobalKey();
|
||||
|
||||
late List<Comment> comments;
|
||||
List<Comment>? comments;
|
||||
int _commentPage = 1;
|
||||
int _commentPageCount = 1;
|
||||
bool _commentLoadingFinish = false;
|
||||
@@ -140,7 +140,7 @@ class ShopState extends State<Shop>
|
||||
'page': _commentPage.toString(),
|
||||
'size': Constants.ORDERS_PER_PAGE.toString(),
|
||||
},
|
||||
businessId: _business.id!,
|
||||
businessId: _business!.id!,
|
||||
).then((data) {
|
||||
if (isRefresh) {
|
||||
_commentRefreshController.refreshCompleted();
|
||||
@@ -204,17 +204,17 @@ class ShopState extends State<Shop>
|
||||
}
|
||||
|
||||
mediaQuery ??= MediaQuery.of(context);
|
||||
screenWidth ??= mediaQuery.size.width;
|
||||
screenHeight ??= mediaQuery.size.height;
|
||||
statusBarHeight ??= mediaQuery.padding.top;
|
||||
screenWidth ??= mediaQuery!.size.width;
|
||||
screenHeight ??= mediaQuery!.size.height;
|
||||
statusBarHeight ??= mediaQuery!.padding.top;
|
||||
|
||||
_sliverAppBarMaxHeight ??= screenHeight - 150.0;
|
||||
_sliverAppBarMaxHeight ??= screenHeight! - 150.0;
|
||||
|
||||
_pageScrollController ??= _shopCoordinator
|
||||
.pageScrollController(_sliverAppBarMaxHeight - _sliverAppBarInitHeight);
|
||||
.pageScrollController(_sliverAppBarMaxHeight! - _sliverAppBarInitHeight);
|
||||
|
||||
_shopCoordinator.pinnedHeaderSliverHeightBuilder ??= () {
|
||||
return statusBarHeight + kToolbarHeight + _tabBarHeight;
|
||||
return statusBarHeight! + kToolbarHeight + _tabBarHeight;
|
||||
};
|
||||
|
||||
refresh = false;
|
||||
@@ -226,7 +226,7 @@ class ShopState extends State<Shop>
|
||||
isDraggable: true,
|
||||
backdropEnabled: true,
|
||||
panel: ShoppingCartBar(
|
||||
business: _business,
|
||||
business: _business!,
|
||||
endKey: endKey,
|
||||
onEmptyCartListener: () {
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
@@ -247,7 +247,7 @@ class ShopState extends State<Shop>
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[],
|
||||
);
|
||||
for (var i = 0; i < _business.promoProducts!.length; i++) {
|
||||
for (var i = 0; i < _business!.promoProducts!.length; i++) {
|
||||
promotRow.children.add(Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 10.0,
|
||||
@@ -286,17 +286,17 @@ class ShopState extends State<Shop>
|
||||
GestureDetector(
|
||||
child: Container(
|
||||
child: Util.showImage(
|
||||
_business.promoProducts![i].imagePath!,
|
||||
_business!.promoProducts![i].imagePath!,
|
||||
width: 110.0,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
_showProductDetail(_business.promoProducts![i]);
|
||||
_showProductDetail(_business!.promoProducts![i]);
|
||||
},
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
_business.promoProducts![i].name!,
|
||||
_business!.promoProducts![i].name!,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 14.0),
|
||||
@@ -306,13 +306,13 @@ class ShopState extends State<Shop>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
ShowPrice(
|
||||
_business.promoProducts![i].price!,
|
||||
_business!.promoProducts![i].price!,
|
||||
currencySign: '\$',
|
||||
regularPrice: _business.promoProducts![i].regularPrice,
|
||||
regularPrice: _business!.promoProducts![i].regularPrice,
|
||||
),
|
||||
Container(
|
||||
child: AddRemoveButton(
|
||||
product: _business.promoProducts![i],
|
||||
product: _business!.promoProducts![i],
|
||||
business: _business,
|
||||
addOnly: true,
|
||||
),
|
||||
@@ -392,7 +392,7 @@ class ShopState extends State<Shop>
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return ProductSearch(_business);
|
||||
return ProductSearch(_business!);
|
||||
}));
|
||||
},
|
||||
),
|
||||
@@ -403,13 +403,13 @@ class ShopState extends State<Shop>
|
||||
child: new Icon(Icons.phone),
|
||||
onTap: () {
|
||||
Utils.launchURL(
|
||||
'tel:${Utils.getFirstNumberFromString(_business.phone!)}');
|
||||
'tel:${Utils.getFirstNumberFromString(_business!.phone!)}');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
title: Text(
|
||||
_business != null ? _business.name! : '',
|
||||
_business != null ? _business!.name! : '',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
@@ -446,7 +446,7 @@ class ShopState extends State<Shop>
|
||||
Tab(
|
||||
child: Badge(
|
||||
badgeContent: Text(
|
||||
'${_business.commentsCount}',
|
||||
'${_business!.commentsCount}',
|
||||
style: TextStyle(color: Colors.white, fontSize: 11.0),
|
||||
),
|
||||
badgeStyle: BadgeStyle(badgeColor: Colors.lightBlueAccent),
|
||||
@@ -508,13 +508,13 @@ class ShopState extends State<Shop>
|
||||
|
||||
stack.children.addAll(children);
|
||||
|
||||
if (_business.isPublic != true) {
|
||||
if (_business!.isPublic != true) {
|
||||
stack.children.add(
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: screenWidth - 100,
|
||||
bottom: screenHeight - 100,
|
||||
left: screenWidth! - 100,
|
||||
bottom: screenHeight! - 100,
|
||||
child: Image.asset(
|
||||
'assets/images/under_renovation.png',
|
||||
width: 200.0,
|
||||
@@ -571,12 +571,12 @@ class ShopState extends State<Shop>
|
||||
Widget commentWidget = Center(
|
||||
child: Text(S.of(context).no_comments_yet),
|
||||
);
|
||||
if (comments != null && comments.length > 0) {
|
||||
if (comments != null && comments!.length > 0) {
|
||||
commentWidget = ListView.builder(
|
||||
controller: _listScrollController3,
|
||||
itemCount: comments.length,
|
||||
itemCount: comments!.length,
|
||||
itemBuilder: (BuildContext context, int position) {
|
||||
Comment comment = comments[position];
|
||||
Comment comment = comments![position];
|
||||
Row imageRow = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[],
|
||||
@@ -772,18 +772,18 @@ class ShopState extends State<Shop>
|
||||
children: <Widget>[],
|
||||
);
|
||||
addressColumn.children.add(new Text(
|
||||
_business.address!.addressLine1! +
|
||||
(_business.address!.addressLine2!.isNotEmpty
|
||||
? ' ' + _business.address!.addressLine2!
|
||||
_business!.address!.addressLine1! +
|
||||
(_business!.address!.addressLine2!.isNotEmpty
|
||||
? ' ' + _business!.address!.addressLine2!
|
||||
: ''),
|
||||
style: new TextStyle(fontSize: 13.0, color: const Color(0xFFEEEEEE)),
|
||||
));
|
||||
addressColumn.children.add(new Text(
|
||||
_business.address!.city! +
|
||||
_business!.address!.city! +
|
||||
', ' +
|
||||
_business.address!.state! +
|
||||
_business!.address!.state! +
|
||||
', ' +
|
||||
_business.address!.zip!,
|
||||
_business!.address!.zip!,
|
||||
style: new TextStyle(fontSize: 13.0, color: const Color(0xFFEEEEEE)),
|
||||
));
|
||||
|
||||
@@ -793,23 +793,23 @@ class ShopState extends State<Shop>
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[],
|
||||
);
|
||||
if (_business.distanceInfo != null) {
|
||||
if (_business!.distanceInfo != null) {
|
||||
distanceRow.children.add(new Icon(
|
||||
Icons.directions_car,
|
||||
size: 14.0,
|
||||
color: Colors.white,
|
||||
));
|
||||
distanceRow.children.add(new Text(
|
||||
_business.distanceInfo!.distance != null
|
||||
? _business.distanceInfo!.distance!.text!
|
||||
_business!.distanceInfo!.distance != null
|
||||
? _business!.distanceInfo!.distance!.text!
|
||||
: '***' + ' / ',
|
||||
style: new TextStyle(color: const Color(0xFFEEEEEE), fontSize: 13.0),
|
||||
));
|
||||
var duration = Duration(
|
||||
seconds: (_business.distanceInfo!.duration != null
|
||||
? _business.distanceInfo!.duration!.value
|
||||
seconds: (_business!.distanceInfo!.duration != null
|
||||
? _business!.distanceInfo!.duration!.value
|
||||
: 30)! +
|
||||
_business.shippingTime! * 60);
|
||||
_business!.shippingTime! * 60);
|
||||
var hours = duration.inHours.remainder(60);
|
||||
var minutes = duration.inMinutes.remainder(60);
|
||||
distanceRow.children.add(new Text(
|
||||
@@ -825,7 +825,7 @@ class ShopState extends State<Shop>
|
||||
distanceRow.children.add(
|
||||
new Text(
|
||||
' / ' +
|
||||
S.of(context).min_order_amount_token(_business.minPrice!) +
|
||||
S.of(context).min_order_amount_token(_business!.minPrice!) +
|
||||
' ',
|
||||
style: new TextStyle(fontSize: 13.0, color: const Color(0xFFEEEEEE)),
|
||||
),
|
||||
@@ -853,7 +853,7 @@ class ShopState extends State<Shop>
|
||||
bottom: 0.0,
|
||||
child: GestureDetector(
|
||||
child: Util.showImage(
|
||||
'${_business.picUrl}',
|
||||
'${_business!.picUrl}',
|
||||
width: 72.0,
|
||||
height: 72.0,
|
||||
fit: BoxFit.fill,
|
||||
@@ -895,7 +895,7 @@ class ShopState extends State<Shop>
|
||||
color: Colors.white,
|
||||
),
|
||||
new Text(
|
||||
_business.phone!,
|
||||
_business!.phone!,
|
||||
style: new TextStyle(
|
||||
fontSize: 13.0, color: const Color(0xFFEEEEEE)),
|
||||
)
|
||||
@@ -910,9 +910,9 @@ class ShopState extends State<Shop>
|
||||
color: Colors.white,
|
||||
),
|
||||
new Text(
|
||||
_business.openingTime![0].openTime! +
|
||||
_business!.openingTime![0].openTime! +
|
||||
':00 - ' +
|
||||
_business.openingTime![0].closeTime! +
|
||||
_business!.openingTime![0].closeTime! +
|
||||
':00',
|
||||
style: new TextStyle(
|
||||
fontSize: 13.0, color: const Color(0xFFEEEEEE)),
|
||||
@@ -941,7 +941,7 @@ class ShopState extends State<Shop>
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_business.bulletin!.isNotEmpty ? Row(
|
||||
_business!.bulletin!.isNotEmpty ? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
@@ -953,10 +953,10 @@ class ShopState extends State<Shop>
|
||||
color: Colors.white,
|
||||
)),
|
||||
Container(
|
||||
width: mediaQuery.size.width - 30.0,
|
||||
width: mediaQuery!.size.width - 30.0,
|
||||
padding: EdgeInsets.only(right: 10.0, bottom: 10.0),
|
||||
child: new Text(
|
||||
_business.bulletin!.isEmpty ? '' : _business.bulletin!,
|
||||
_business!.bulletin!.isEmpty ? '' : _business!.bulletin!,
|
||||
softWrap: true,
|
||||
style: new TextStyle(
|
||||
fontSize: 12.0, color: const Color(0xFFDDDDDD)),
|
||||
@@ -965,7 +965,7 @@ class ShopState extends State<Shop>
|
||||
),
|
||||
],
|
||||
) : SizedBox.shrink(),
|
||||
_business.description!.isNotEmpty ? Column(
|
||||
_business!.description!.isNotEmpty ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -996,7 +996,7 @@ class ShopState extends State<Shop>
|
||||
padding:
|
||||
EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
|
||||
child: Text(
|
||||
_business.description!,
|
||||
_business!.description!,
|
||||
style: TextStyle(
|
||||
color: Colors.lightGreen,
|
||||
fontSize: 12.0,
|
||||
@@ -1009,7 +1009,7 @@ class ShopState extends State<Shop>
|
||||
padding: EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
||||
child: slidingGellery,
|
||||
) : SizedBox.shrink(),
|
||||
_business.policy!.isNotEmpty ? Column(
|
||||
_business!.policy!.isNotEmpty ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -1040,7 +1040,7 @@ class ShopState extends State<Shop>
|
||||
padding: EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
_business.policy!,
|
||||
_business!.policy!,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: 10.0,
|
||||
@@ -1062,7 +1062,7 @@ class ShopState extends State<Shop>
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Util.showImage(
|
||||
_business.bannerImageUrl!,
|
||||
_business!.bannerImageUrl!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
@@ -1098,11 +1098,11 @@ class ShopState extends State<Shop>
|
||||
|
||||
List<Widget> _buildBanners(BuildContext context) {
|
||||
var pages = <Widget>[];
|
||||
for (var i = 0; i < _business.slideImages!.length; i++) {
|
||||
for (var i = 0; i < _business!.slideImages!.length; i++) {
|
||||
pages.add(new GestureDetector(
|
||||
child: new Container(
|
||||
child: Util.showImage(
|
||||
_business.slideImages![i].imageUrl!,
|
||||
_business!.slideImages![i].imageUrl!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
@@ -1132,14 +1132,14 @@ class ShopState extends State<Shop>
|
||||
child: new ListView.builder(
|
||||
physics: ClampingScrollPhysics(),
|
||||
controller: _listScrollController2,
|
||||
itemCount: _categoryProducts == null ? 0 : _categoryProducts.length,
|
||||
itemCount: _categoryProducts == null ? 0 : _categoryProducts!.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
CategoryProducts cp = _categoryProducts[i];
|
||||
CategoryProducts cp = _categoryProducts![i];
|
||||
int qtyInCategory = 0;
|
||||
CartInfo cartInfo =
|
||||
Utils.getCartInfoByBusiness(store.state.cartInfos, _business)!;
|
||||
CartInfo? cartInfo =
|
||||
Utils.getCartInfoByBusiness(store.state.cartInfos, _business!);
|
||||
if (cartInfo != null &&
|
||||
cartInfo.businessInfo!.id == _business.id &&
|
||||
cartInfo.businessInfo!.id == _business!.id &&
|
||||
cartInfo.productList != null) {
|
||||
for (var i = 0; i < cartInfo.productList!.length; i++) {
|
||||
if (cartInfo.productList![i].product!.categoryId == cp.id) {
|
||||
@@ -1213,21 +1213,21 @@ class ShopState extends State<Shop>
|
||||
}
|
||||
|
||||
void _selectCategory(int index) {
|
||||
if (displayProductByCategoryClick && _categoryProducts[index].id! > 0) {
|
||||
categoryId = _categoryProducts[index].id!;
|
||||
if (displayProductByCategoryClick && _categoryProducts![index].id! > 0) {
|
||||
categoryId = _categoryProducts![index].id!;
|
||||
loadProducts();
|
||||
return;
|
||||
}
|
||||
double height = 0.0;
|
||||
for (int i = 0; i < index; ++i) {
|
||||
height += _categoryDescHeight +
|
||||
_categoryProducts[i].products!.length * _productHeight;
|
||||
_categoryProducts![i].products!.length * _productHeight;
|
||||
}
|
||||
if (height > _listScrollController1.position.maxScrollExtent) {
|
||||
height = _listScrollController1.position.maxScrollExtent;
|
||||
if (height > _listScrollController1!.position.maxScrollExtent) {
|
||||
height = _listScrollController1!.position.maxScrollExtent;
|
||||
}
|
||||
_categoryIndexChange = true;
|
||||
_listScrollController1
|
||||
_listScrollController1!
|
||||
.animateTo(height,
|
||||
duration: new Duration(
|
||||
microseconds: 200,
|
||||
@@ -1237,7 +1237,7 @@ class ShopState extends State<Shop>
|
||||
_categoryIndexChange = false;
|
||||
});
|
||||
print(
|
||||
'height: $height, index: $index, ${_categoryProducts[0].products!.length}');
|
||||
'height: $height, index: $index, ${_categoryProducts![0].products!.length}');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_categoryIndex = index;
|
||||
@@ -1246,7 +1246,7 @@ class ShopState extends State<Shop>
|
||||
}
|
||||
|
||||
CategoryProducts? getCategoryProductByCategoryId(int cid) {
|
||||
for (CategoryProducts cp in _categoryProducts) {
|
||||
for (CategoryProducts cp in _categoryProducts!) {
|
||||
if (cp.id == cid) {
|
||||
return cp;
|
||||
}
|
||||
@@ -1256,8 +1256,8 @@ class ShopState extends State<Shop>
|
||||
|
||||
void _resetProductListScroll() {
|
||||
if (_listScrollController1 != null &&
|
||||
_listScrollController1.positions.isNotEmpty) {
|
||||
_listScrollController1.animateTo(
|
||||
_listScrollController1!.positions.isNotEmpty) {
|
||||
_listScrollController1!.animateTo(
|
||||
0,
|
||||
duration: new Duration(
|
||||
microseconds: 200,
|
||||
@@ -1270,9 +1270,9 @@ class ShopState extends State<Shop>
|
||||
int _getCategoryIndexByRightScrollHeight(double height) {
|
||||
double cHeight = 0.0;
|
||||
if (height > 0) {
|
||||
for (int i = 0; i < _categoryProducts.length; ++i) {
|
||||
for (int i = 0; i < _categoryProducts!.length; ++i) {
|
||||
double categoryHeight = _categoryDescHeight +
|
||||
_categoryProducts[i].products!.length * _productHeight;
|
||||
_categoryProducts![i].products!.length * _productHeight;
|
||||
if (height >= cHeight && height < cHeight + categoryHeight) {
|
||||
return i;
|
||||
}
|
||||
@@ -1280,7 +1280,7 @@ class ShopState extends State<Shop>
|
||||
}
|
||||
}
|
||||
if (height > cHeight) {
|
||||
return _categoryProducts.length - 1;
|
||||
return _categoryProducts!.length - 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1310,7 +1310,7 @@ class ShopState extends State<Shop>
|
||||
|
||||
int numCategoriesHasProducts() {
|
||||
int num = 0;
|
||||
for (CategoryProducts cp in _categoryProducts) {
|
||||
for (CategoryProducts cp in _categoryProducts!) {
|
||||
if (cp.products!.length > 0) {
|
||||
num += 1;
|
||||
}
|
||||
@@ -1340,21 +1340,21 @@ class ShopState extends State<Shop>
|
||||
_categoryProducts == null ? 0 : numCategoriesHasProducts(),
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
CategoryProducts cp;
|
||||
cp = _categoryProducts[i];
|
||||
cp = _categoryProducts![i];
|
||||
|
||||
int index = -1;
|
||||
if (displayProductByCategoryClick) {
|
||||
if (categoryId > 0) {
|
||||
for (var j = 0; j < _categoryProducts.length; j++) {
|
||||
if (_categoryProducts[j].id == categoryId) {
|
||||
cp = _categoryProducts[j];
|
||||
for (var j = 0; j < _categoryProducts!.length; j++) {
|
||||
if (_categoryProducts![j].id == categoryId) {
|
||||
cp = _categoryProducts![j];
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cp == null) {
|
||||
index = 0;
|
||||
cp = _categoryProducts[0];
|
||||
cp = _categoryProducts![0];
|
||||
}
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
@@ -1424,7 +1424,7 @@ class ShopState extends State<Shop>
|
||||
pStack.children.add(Container(
|
||||
// width: MediaQuery.of(context).size.width - 100.0,
|
||||
child: ProductItem(
|
||||
p, _business,
|
||||
p, _business!,
|
||||
horizontal: true,
|
||||
imageWidth: 80.0,
|
||||
),
|
||||
@@ -1498,9 +1498,9 @@ class ShopState extends State<Shop>
|
||||
_listScrollController2 = _shopCoordinator.newChildScrollController();
|
||||
_listScrollController3 = _shopCoordinator.newChildScrollController();
|
||||
|
||||
_listScrollController1.addListener(() {
|
||||
if (_listScrollController1.position.atEdge) {
|
||||
if (_listScrollController1.position.pixels == 0) {
|
||||
_listScrollController1!.addListener(() {
|
||||
if (_listScrollController1!.position.atEdge) {
|
||||
if (_listScrollController1!.position.pixels == 0) {
|
||||
print('product list at top');
|
||||
} else {
|
||||
print('product list at bottom');
|
||||
@@ -1567,7 +1567,7 @@ class ShopState extends State<Shop>
|
||||
'',
|
||||
'',
|
||||
_hotSaleProducts);
|
||||
_categoryProducts.insert(0, hs);
|
||||
_categoryProducts!.insert(0, hs);
|
||||
}
|
||||
|
||||
if (_featuredProducts.length > 0) {
|
||||
@@ -1578,7 +1578,7 @@ class ShopState extends State<Shop>
|
||||
'',
|
||||
'',
|
||||
_featuredProducts);
|
||||
_categoryProducts.insert(0, fe);
|
||||
_categoryProducts!.insert(0, fe);
|
||||
}
|
||||
|
||||
checkActionAndClose(context);
|
||||
|
||||
@@ -35,21 +35,21 @@ class ShoppingCartBar extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ShoppingCartBarState extends State<ShoppingCartBar> {
|
||||
late CartInfo cartInfo;
|
||||
CartInfo? cartInfo;
|
||||
double totalPrice = 0.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
totalPrice = 0.0;
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business)!;
|
||||
if (cartInfo != null && cartInfo.businessInfo!.id == widget.business.id) {
|
||||
totalPrice = cartInfo.getTotalPrice();
|
||||
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business);
|
||||
if (cartInfo != null && cartInfo!.businessInfo!.id == widget.business.id) {
|
||||
totalPrice = cartInfo!.getTotalPrice();
|
||||
}
|
||||
|
||||
Widget cartContent;
|
||||
|
||||
if (cartInfo == null || (cartInfo.businessInfo!.id != widget.business.id)
|
||||
|| (totalPrice == 0.0 && cartInfo.productList!.length == 0)) {
|
||||
if (cartInfo == null || (cartInfo!.businessInfo!.id != widget.business.id)
|
||||
|| (totalPrice == 0.0 && cartInfo!.productList!.length == 0)) {
|
||||
cartContent = Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
@@ -92,8 +92,8 @@ class ShoppingCartBarState extends State<ShoppingCartBar> {
|
||||
},
|
||||
),
|
||||
);
|
||||
for (var i = 0; i < cartInfo.productList!.length; i++) {
|
||||
(cartContent as Column).children.add(cartLineItem(cartInfo.productList![i], i));
|
||||
for (var i = 0; i < cartInfo!.productList!.length; i++) {
|
||||
(cartContent as Column).children.add(cartLineItem(cartInfo!.productList![i], i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -130,13 +130,13 @@ packages:
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "437b2b8da454fe1e01131f4410274fd607347325e9330f0b469ed5a204991068"
|
||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1+4"
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -73,6 +73,11 @@ dependencies:
|
||||
hive_flutter: ^1.1.0
|
||||
catcher: ^0.8.0
|
||||
|
||||
# cross_file 0.3.1+4 (transitive via image_picker) references UnmodifiableUint8ListView,
|
||||
# which was removed in Dart 3 — override to a Dart 3-safe release for the web build.
|
||||
dependency_overrides:
|
||||
cross_file: 0.3.4+2 # pinned: 0.3.5+ needs Dart 3.10; 0.3.1+4 uses removed UnmodifiableUint8ListView
|
||||
|
||||
# Removed in Phase 2 (no null-safe release and/or zero references):
|
||||
# splashscreen, flappy_search_bar, searchable_dropdown, share, platform_detect,
|
||||
# flutter_inappwebview, countdown, gender_selection, flutter_dash,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*.png
|
||||
@@ -0,0 +1,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext()).newPage();
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load' });
|
||||
await page.waitForTimeout(3000);
|
||||
const data = await page.evaluate(async () => {
|
||||
const r = await fetch('https://api.minipos.us/v1/get-buy-service-info/pos/1?store_id=0');
|
||||
return JSON.stringify(await r.json());
|
||||
}).catch(e => 'ERR: ' + e.message);
|
||||
console.log('buy-service API:', data.slice(0, 600));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,22 @@
|
||||
// 拦截 buy-service 的 API 请求/响应,看实际数据结构
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
page.on('response', async (resp) => {
|
||||
const url = resp.url();
|
||||
if (url.includes('get-buy-service-info')) {
|
||||
console.log('=== buy-service API 响应 ===');
|
||||
console.log('状态:', resp.status());
|
||||
try {
|
||||
const body = await resp.text();
|
||||
console.log('body:', body.slice(0, 700));
|
||||
} catch(e) { console.log('读 body 失败'); }
|
||||
}
|
||||
});
|
||||
page.on('pageerror', e => console.log('PAGEERR:', e.message.slice(0,150)));
|
||||
page.on('console', m => { if (m.text().startsWith('data:') || m.text().includes('RangeError')) console.log('CONSOLE:', m.text().slice(0,200)); });
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(8000);
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,12 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
const dataLog = logs.filter(l => l.startsWith('data: {') || l.startsWith('data:{'));
|
||||
console.log('应用 data:', dataLog[0] ? dataLog[0].slice(0, 500) : '(未捕获)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,21 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('pageerror', e => logs.push('PAGEERR: ' + e.message));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
// 找 data 打印 + RangeError
|
||||
const dataLog = logs.find(l => l.startsWith('data:'));
|
||||
console.log('=== data ===', dataLog ? dataLog.slice(0,400) : '(无)');
|
||||
// 看 service_selections / options 部分
|
||||
if (dataLog) {
|
||||
const m = dataLog.match(/service_selections:[^,}]+/);
|
||||
if (m) console.log('service_selections:', m[0].slice(0,200));
|
||||
const o = dataLog.match(/options:[^]]*\]/);
|
||||
if (o) console.log('options:', o[0].slice(0,200));
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,33 @@
|
||||
// 诊断:对失败路由打印完整错误 + CK_STACK
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
const ROUTES = ['/contact-stores', '/download', '/buy-service/1/pos'];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const route of ROUTES) {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const allLogs = [];
|
||||
page.on('pageerror', e => allLogs.push(`PAGEERROR: ${e.message}`));
|
||||
page.on('console', m => allLogs.push(m.text()));
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 }).catch(e => allLogs.push('GOTO: ' + e.message.slice(0,80)));
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
console.log(`\n########## ${route} ##########`);
|
||||
// 只打印 CK_ERROR + CK_STACK + PAGEERROR
|
||||
for (let i = 0; i < allLogs.length; i++) {
|
||||
const l = allLogs[i];
|
||||
if (l.startsWith('CK_ERROR:') || l.startsWith('PAGEERROR')) {
|
||||
console.log(l.slice(0, 300));
|
||||
// 找紧跟的 CK_STACK
|
||||
if (allLogs[i+1] && allLogs[i+1].startsWith('CK_STACK:')) {
|
||||
const proj = allLogs[i+1].match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
console.log(' → 项目位置:', proj ? [...new Set(proj)].slice(0,4) : '(无项目帧)');
|
||||
}
|
||||
}
|
||||
}
|
||||
await ctx.close();
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,32 @@
|
||||
// 诊断 v2:打印完整 CK_STACK(定位 framework 抛的错误的源头)
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
const ROUTES = ['/buy-service/1/pos', '/download'];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const route of ROUTES) {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const allLogs = [];
|
||||
page.on('pageerror', e => allLogs.push(`PAGEERROR: ${e.stack || e.message}`));
|
||||
page.on('console', m => allLogs.push(m.text()));
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 }).catch(()=>{});
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
console.log(`\n########## ${route} ##########`);
|
||||
for (let i = 0; i < allLogs.length; i++) {
|
||||
const l = allLogs[i];
|
||||
if (l.startsWith('CK_ERROR:') || l.startsWith('PAGEERROR')) {
|
||||
console.log('--- ' + l.slice(0, 150));
|
||||
if (allLogs[i+1] && allLogs[i+1].startsWith('CK_STACK:')) {
|
||||
// 打印堆栈里所有 flutter_wisetronic 帧
|
||||
const proj = allLogs[i+1].match(/flutter_wisetronic\/[^\s]+/g) || [];
|
||||
console.log(' 项目帧: ' + (proj.length ? [...new Set(proj)].join('\n ') : '(无)'));
|
||||
}
|
||||
}
|
||||
}
|
||||
await ctx.close();
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('pageerror', e => logs.push('PAGEERR: ' + e.message.slice(0,150)));
|
||||
page.on('response', async r => { if (r.url().includes('get-buy-service')) console.log('API:', r.status()); });
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(9000);
|
||||
console.log('--- RangeError 相关 console ---');
|
||||
logs.filter(l => l.includes('RangeError') || l.includes('CK_ERROR') || l.startsWith('PAGEERR')).slice(0,5).forEach(l => console.log(l.slice(0,180)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(8000);
|
||||
for (let i=0;i<logs.length;i++){
|
||||
if(logs[i].startsWith('CK_ERROR')){console.log(logs[i].slice(0,120));
|
||||
const ctx=logs.slice(i+1,i+4).find(l=>l.startsWith('CK_CTX'));
|
||||
if(ctx) console.log(' CTX:', ctx.slice(0,150));
|
||||
}
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const ctx = logs.filter(l => l.startsWith('CK_CTX'));
|
||||
if (ctx.length) console.log(ctx[0].slice(0, 600));
|
||||
else console.log('无 CK_CTX');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const range = logs.filter(l => l.includes('RangeError'));
|
||||
const err = logs.filter(l => l.includes('CK_BUY_ERR'));
|
||||
console.log('RangeError 数:', range.length, err.length ? '| CK_BUY_ERR:'+err[0].slice(0,100) : '');
|
||||
if (range.length) console.log(range[0].slice(0,120));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,21 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 375, height: 812 } });
|
||||
await ctx.addInitScript(([u,p]) => { localStorage['_test_token'] = u+'|'+p; }, ['6477108200','8818']);
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load' });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(8000);
|
||||
await page.goto('http://127.0.0.1:8099/me', { waitUntil: 'load' });
|
||||
await page.waitForTimeout(6000);
|
||||
const stacks = logs.filter(l => l.startsWith('CK_STACK:'));
|
||||
for (const s of stacks) {
|
||||
const proj = s.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) console.log('RenderFlex 项目位置:', [...new Set(proj)].slice(0,3));
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,15 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
console.log('=== path elements 打印(看是否有短路径)===');
|
||||
logs.filter(l => l.includes('path elements')).forEach(l => console.log(' ' + l.slice(0,80)));
|
||||
console.log('=== RangeError 之前的最后几条 console ===');
|
||||
const idx = logs.findIndex(l => l.includes('RangeError'));
|
||||
if (idx >= 0) logs.slice(Math.max(0,idx-4), idx).forEach(l => console.log(' ' + l.slice(0,90)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const route of ['/download', '/contact-stores', '/me', '/orders', '/buy-service/1/ministore']) {
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
let range = 0;
|
||||
page.on('console', m => { if (m.text().includes('RangeError')) range++; });
|
||||
await page.goto('http://127.0.0.1:8099'+route, { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
console.log(`${range>0?'✗ '+range+' RangeError':'✓ OK'} ${route}`);
|
||||
await page.close();
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const seq = logs.filter(l => l.startsWith('CK_BS_') || l.includes('RangeError'));
|
||||
console.log('时序:');
|
||||
seq.slice(0,10).forEach(l => console.log(' ' + l.slice(0,90)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,19 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const stack = logs.filter(l => l.startsWith('CK_STACK'));
|
||||
if (stack.length) {
|
||||
// 找 .dart 源文件帧(非 main.dart.js)
|
||||
const dartFrames = stack[0].split('\n').filter(l => l.includes('.dart') && !l.includes('main.dart.js'));
|
||||
console.log('源文件帧:', dartFrames.length ? dartFrames.slice(0,5).join('\n ') : '(无,全是 main.dart.js)');
|
||||
// 也看前 3 个 main.dart.js 帧
|
||||
const jsFrames = stack[0].split('\n').filter(l => l.includes('main.dart.js')).slice(0,4);
|
||||
console.log('main.dart.js 帧:', jsFrames.join('\n '));
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,11 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(8000);
|
||||
logs.filter(l => l.includes('CK_BUY_THEN') || l.includes('RangeError') || l.includes('CK_ERROR')).slice(0,5).forEach(l => console.log(l.slice(0,160)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,47 @@
|
||||
// dump IndexedDB 结构 + Hive entry 格式
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext()).newPage();
|
||||
await page.goto(BASE + '/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(7000);
|
||||
|
||||
const result = await page.evaluate(async () => {
|
||||
const out = {};
|
||||
const dbs = await indexedDB.databases();
|
||||
out.databases = dbs.map(d => d.name);
|
||||
for (const db of dbs) {
|
||||
out[db.name] = [];
|
||||
await new Promise((resolve) => {
|
||||
const req = indexedDB.open(db.name);
|
||||
req.onsuccess = () => {
|
||||
const d = req.result;
|
||||
const stores = [...d.objectStoreNames];
|
||||
out[db.name + '_stores'] = stores;
|
||||
if (stores.length === 0) { d.close(); resolve(); return; }
|
||||
const tx = d.transaction(stores[0], 'readonly');
|
||||
const store = tx.objectStore(stores[0]);
|
||||
const allReq = store.getAll();
|
||||
const keysReq = store.getAllKeys();
|
||||
allReq.onsuccess = () => {
|
||||
out[db.name] = allReq.result.map((v, i) => {
|
||||
let hex = '';
|
||||
if (v instanceof ArrayBuffer) {
|
||||
const u = new Uint8Array(v);
|
||||
hex = Array.from(u.slice(0, 40)).map(b => b.toString(16).padStart(2,'0')).join(' ');
|
||||
return { key: keysReq.result[i], valueBytes: v.byteLength, hexFirst40: hex };
|
||||
}
|
||||
return { key: keysReq.result[i], value: typeof v === 'string' ? v.slice(0,80) : v };
|
||||
});
|
||||
};
|
||||
tx.oncomplete = () => { d.close(); resolve(); };
|
||||
};
|
||||
req.onerror = () => resolve();
|
||||
});
|
||||
}
|
||||
return out;
|
||||
});
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 813 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
|
||||
// A map containing the URLs for the bootstrap scripts in debug.
|
||||
let _scriptUrls = {
|
||||
"mapper": "stack_trace_mapper.js",
|
||||
"requireJs": "require.js"
|
||||
};
|
||||
|
||||
// Create a TrustedTypes policy so we can attach Scripts...
|
||||
let _ttPolicy;
|
||||
if (window.trustedTypes) {
|
||||
_ttPolicy = trustedTypes.createPolicy("flutter-tools-bootstrap", {
|
||||
createScriptURL: (url) => {
|
||||
let scriptUrl = _scriptUrls[url];
|
||||
if (!scriptUrl) {
|
||||
console.error("Unknown Flutter Web bootstrap resource!", url);
|
||||
}
|
||||
return scriptUrl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Creates a TrustedScriptURL for a given `scriptName`.
|
||||
// See `_scriptUrls` and `_ttPolicy` above.
|
||||
function getTTScriptUrl(scriptName) {
|
||||
let defaultUrl = _scriptUrls[scriptName];
|
||||
return _ttPolicy ? _ttPolicy.createScriptURL(scriptName) : defaultUrl;
|
||||
}
|
||||
|
||||
// Attach source mapping.
|
||||
var mapperEl = document.createElement("script");
|
||||
mapperEl.defer = true;
|
||||
mapperEl.async = false;
|
||||
mapperEl.src = getTTScriptUrl("mapper");
|
||||
document.head.appendChild(mapperEl);
|
||||
|
||||
// Attach require JS.
|
||||
var requireEl = document.createElement("script");
|
||||
requireEl.defer = true;
|
||||
requireEl.async = false;
|
||||
requireEl.src = getTTScriptUrl("requireJs");
|
||||
// This attribute tells require JS what to load as main (defined below).
|
||||
requireEl.setAttribute("data-main", "main_module.bootstrap");
|
||||
document.head.appendChild(requireEl);
|
||||
Generated
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"playwright": "^1.62.1",
|
||||
"source-map": "^0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
|
||||
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.62.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
|
||||
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz",
|
||||
"integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"playwright": "^1.62.1",
|
||||
"source-map": "^0.8.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
const fs = require('fs');
|
||||
const { SourceMapConsumer } = require('source-map');
|
||||
(async () => {
|
||||
const mapJson = await (await fetch('http://127.0.0.1:8099/main.dart.js.map')).text().catch(()=>null);
|
||||
if (!mapJson) { console.log('无 sourcemap'); return; }
|
||||
const smc = await new SourceMapConsumer(mapJson);
|
||||
// 解析 299:254 (line:col)
|
||||
const pos = smc.originalPositionFor({ line: 299, column: 254 });
|
||||
console.log('299:254 →', pos.source, pos.line + ':' + pos.column, '|', pos.name);
|
||||
// 也试 299 的前后几行
|
||||
for (const c of [200, 100, 50]) {
|
||||
const p = smc.originalPositionFor({ line: 299, column: c });
|
||||
if (p.source) console.log(`299:${c} →`, p.source.split('/').slice(-2).join('/') + ':' + p.line);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,68 @@
|
||||
// 探测脚本:检查 Flutter renderer + 登录页 DOM + Hive 存储后端
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext();
|
||||
const page = await ctx.newPage();
|
||||
|
||||
// 收集 console
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(`[${m.type()}] ${m.text()}`));
|
||||
page.on('pageerror', e => logs.push(`[PAGEERROR] ${e.message}`));
|
||||
|
||||
console.log('=== 1. 访问首页,检查 renderer ===');
|
||||
await page.goto(BASE + '/', { waitUntil: 'networkidle', timeout: 60000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// 检查 renderer:canvaskit 有 <canvas> + flt-glass-pane;html 有 flutter-view 含 div
|
||||
const canvasCount = await page.locator('canvas').count();
|
||||
const fltGlass = await page.locator('flt-glass-pane').count();
|
||||
const htmlView = await page.locator('flutter-view, .flutter-view').count();
|
||||
console.log(`canvas 元素数: ${canvasCount}`);
|
||||
console.log(`flt-glass-pane (canvaskit 标志): ${fltGlass}`);
|
||||
console.log(`flutter-view: ${htmlView}`);
|
||||
|
||||
// body HTML 片段(判断 renderer)
|
||||
const bodySnippet = await page.evaluate(() => document.body.innerHTML.slice(0, 400));
|
||||
console.log(`body 片段: ${bodySnippet.slice(0, 200)}`);
|
||||
|
||||
console.log('\n=== 2. 访问登录页,检查输入框 DOM ===');
|
||||
await page.goto(BASE + '/login', { waitUntil: 'networkidle', timeout: 60000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Flutter web 输入框:canvaskit 会在聚焦时创建 <input>;检查有无 input
|
||||
const inputCount = await page.locator('input').count();
|
||||
console.log(`input 元素数: ${inputCount}`);
|
||||
const inputs = await page.locator('input').evaluateAll(els =>
|
||||
els.map(e => ({ type: e.type, placeholder: e.placeholder, ariaLabel: e.getAttribute('aria-label'), cls: e.className.slice(0, 40) }))
|
||||
);
|
||||
console.log('input 详情:', JSON.stringify(inputs).slice(0, 300));
|
||||
|
||||
// 检查 semantics(辅助功能树)
|
||||
const semanticsRoles = await page.locator('[role="textbox"], [role="button"]').count();
|
||||
console.log(`semantics (role=textbox/button): ${semanticsRoles}`);
|
||||
|
||||
console.log('\n=== 3. 检查存储(localStorage + IndexedDB)===');
|
||||
const ls = await page.evaluate(() => {
|
||||
return JSON.stringify(Object.keys(localStorage).reduce((o, k) => { o[k] = localStorage.getItem(k).slice(0, 100); return o; }, {}));
|
||||
});
|
||||
console.log(`localStorage: ${ls}`);
|
||||
|
||||
const idb = await page.evaluate(async () => {
|
||||
const dbs = indexedDB.databases ? await indexedDB.databases() : [];
|
||||
const result = [];
|
||||
for (const db of dbs) {
|
||||
result.push({ name: db.name, version: db.version });
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
});
|
||||
console.log(`IndexedDB databases: ${idb}`);
|
||||
|
||||
console.log('\n=== 4. console 日志(前 10 条)===');
|
||||
console.log(logs.slice(0, 10).join('\n'));
|
||||
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('探测失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,51 @@
|
||||
// 探测 v2:正确加载 + 截图 + 检测 canvaskit 隐藏 input
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('pageerror', e => logs.push(`[PAGEERROR] ${e.message.slice(0, 120)}`));
|
||||
|
||||
console.log('=== 访问 /login (load 模式) ===');
|
||||
await page.goto(BASE + '/login', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(6000); // 等 Flutter 渲染
|
||||
|
||||
await page.screenshot({ path: 'login.png', fullPage: true });
|
||||
console.log('截图: login.png');
|
||||
|
||||
// 检查所有 input(含 display:none)
|
||||
const inputs = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('input')].map(e => ({
|
||||
type: e.type,
|
||||
hidden: e.hidden || e.style.display === 'none' || e.offsetParent === null,
|
||||
cls: e.className.slice(0, 50),
|
||||
pos: e.getBoundingClientRect ? (() => { const r = e.getBoundingClientRect(); return {x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height)}; })() : null,
|
||||
}));
|
||||
});
|
||||
console.log(`所有 input (${inputs.length}):`, JSON.stringify(inputs));
|
||||
|
||||
// 检查 flt-glass-pane 内的 semantics
|
||||
const semTextboxes = await page.locator('[role="textbox"]').count();
|
||||
console.log(`role=textbox: ${semTextboxes}`);
|
||||
|
||||
console.log('\n=== 尝试点击用户名输入框区域,看是否触发 input ===');
|
||||
// canvaskit 点击中间偏上区域(假设用户名框)
|
||||
await page.mouse.click(640, 400);
|
||||
await page.waitForTimeout(1500);
|
||||
const inputsAfterClick = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('input')].map(e => ({
|
||||
hidden: e.hidden || e.style.display === 'none',
|
||||
pos: (() => { const r = e.getBoundingClientRect(); return {x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width)}; })(),
|
||||
}));
|
||||
});
|
||||
console.log('点击后 input:', JSON.stringify(inputsAfterClick));
|
||||
console.log('是否有 activeElement 是 input:', await page.evaluate(() => document.activeElement && document.activeElement.tagName));
|
||||
|
||||
console.log('\n=== pageerror 日志 ===');
|
||||
console.log(logs.slice(0, 5).join('\n'));
|
||||
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,40 @@
|
||||
// probe3:开启 Flutter web semantics(Enable accessibility),看能否得到 DOM 输入框
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({ viewport: { width: 1280, height: 900 } })).newPage();
|
||||
await page.goto(BASE + '/login', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// 方法1:点击 flt-semantics-placeholder("Enable accessibility")
|
||||
const ph = await page.locator('flt-semantics-placeholder').count();
|
||||
console.log('placeholder 数:', ph);
|
||||
if (ph > 0) {
|
||||
await page.locator('flt-semantics-placeholder').click({ force: true }).catch(e => console.log('点击 placeholder 失败:', e.message.slice(0,60)));
|
||||
await page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
// 方法2:尝试 JS 开启(多种已知方式)
|
||||
await page.evaluate(() => {
|
||||
// 部分版本支持
|
||||
if (window._flutter) { try { window._flutter.semanticsEnabled = true; } catch(e){} }
|
||||
});
|
||||
|
||||
const textbox = await page.locator('[role="textbox"]').count();
|
||||
const btn = await page.locator('[role="button"]').count();
|
||||
const input = await page.locator('input').count();
|
||||
console.log(`开启后: role=textbox=${textbox}, role=button=${btn}, input=${input}`);
|
||||
|
||||
// 列出 semantics 元素的 label(找用户名/密码/登录按钮)
|
||||
const labels = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('[role="textbox"], [role="button"], [aria-label]')]
|
||||
.map(e => ({ role: e.getAttribute('role'), label: e.getAttribute('aria-label'), name: e.tagName }))
|
||||
.filter(x => x.label && x.label.length < 40)
|
||||
.slice(0, 25);
|
||||
});
|
||||
console.log('semantics labels:', JSON.stringify(labels));
|
||||
|
||||
await page.screenshot({ path: 'login_semantics.png' });
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,27 @@
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({ viewport: { width: 1280, height: 900 } })).newPage();
|
||||
await page.goto(BASE + '/login', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// JS 直接触发 placeholder click(绕过 viewport 限制)
|
||||
await page.evaluate(() => {
|
||||
const el = document.querySelector('flt-semantics-placeholder');
|
||||
if (el) el.click();
|
||||
});
|
||||
await page.waitForTimeout(4000);
|
||||
|
||||
const textbox = await page.locator('[role="textbox"]').count();
|
||||
const input = await page.locator('input').count();
|
||||
console.log(`JS click 后: role=textbox=${textbox}, input=${input}`);
|
||||
|
||||
const labels = await page.evaluate(() =>
|
||||
[...document.querySelectorAll('[role="textbox"],[role="button"]')]
|
||||
.map(e => ({ role: e.getAttribute('role'), label: e.getAttribute('aria-label') }))
|
||||
.filter(x => x.label && x.label.length < 50).slice(0, 30)
|
||||
);
|
||||
console.log('labels:', JSON.stringify(labels));
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,88 @@
|
||||
// e2e 测试:遍历公开路由,捕获 Flutter 错误(print→console 的 CK_ERROR/CK_STACK)
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
// 公开路由(不需登录)。动态参数用合理默认值
|
||||
const ROUTES = [
|
||||
'/',
|
||||
'/shop',
|
||||
'/shop/310',
|
||||
'/about-us',
|
||||
'/contact-us',
|
||||
'/contact-stores',
|
||||
'/download',
|
||||
'/blog/310',
|
||||
'/view-blog/52',
|
||||
'/igoshow-learn-more',
|
||||
'/minipos-learn-more',
|
||||
'/buy-service/1/pos',
|
||||
'/forgot-password',
|
||||
'/new-user',
|
||||
'/privacy-policy',
|
||||
'/return-policy',
|
||||
'/service-policy',
|
||||
'/end-user-license-agreement',
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
async function testRoute(browser, route) {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const errors = [];
|
||||
const ckStacks = [];
|
||||
|
||||
page.on('pageerror', e => errors.push(`PAGEERROR: ${e.message.slice(0, 200)}`));
|
||||
page.on('console', m => {
|
||||
const t = m.text();
|
||||
if (t.startsWith('CK_ERROR:')) errors.push(t);
|
||||
if (t.startsWith('CK_STACK:')) {
|
||||
// 提取项目文件:行号
|
||||
const proj = t.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) ckStacks.push([...new Set(proj)].slice(0, 3));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 });
|
||||
await page.waitForTimeout(5000); // 等 Flutter 渲染 + API
|
||||
} catch (e) {
|
||||
errors.push(`GOTO_ERR: ${e.message.slice(0, 100)}`);
|
||||
}
|
||||
|
||||
const status = errors.length === 0 ? '✓ OK' : `✗ ${errors.length} 错误`;
|
||||
console.log(`${status.padEnd(12)} ${route}`);
|
||||
if (errors.length > 0) {
|
||||
console.log(' 错误: ' + errors.slice(0, 3).join(' | ').slice(0, 250));
|
||||
if (ckStacks.length > 0) console.log(' 位置: ' + JSON.stringify(ckStacks[0]));
|
||||
}
|
||||
results.push({ route, errors, ckStacks });
|
||||
await ctx.close();
|
||||
}
|
||||
|
||||
(async () => {
|
||||
console.log(`Flutter e2e 测试:${ROUTES.length} 个公开路由\n`);
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const r of ROUTES) {
|
||||
await testRoute(browser, r);
|
||||
}
|
||||
await browser.close();
|
||||
|
||||
// 汇总
|
||||
const failed = results.filter(r => r.errors.length > 0);
|
||||
console.log(`\n========== 汇总 ==========`);
|
||||
console.log(`总计 ${results.length} 路由,${failed.length} 个有错误`);
|
||||
if (failed.length > 0) {
|
||||
console.log('\n失败路由 + 崩溃位置:');
|
||||
const locMap = {};
|
||||
for (const f of failed) {
|
||||
const loc = f.ckStacks[0] ? f.ckStacks[0][0] : '(无堆栈)';
|
||||
locMap[loc] = locMap[loc] || [];
|
||||
locMap[loc].push(f.route);
|
||||
}
|
||||
for (const [loc, routes] of Object.entries(locMap)) {
|
||||
console.log(` ${loc}`);
|
||||
console.log(` 影响: ${routes.join(', ')}`);
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,94 @@
|
||||
// 登录态路由测试:注入 _test_token,autologin 后遍历需登录路由
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
const USER = '6477108200';
|
||||
const PASS = '8818';
|
||||
|
||||
// 需登录路由(动态参数用合理默认)
|
||||
const AUTH_ROUTES = [
|
||||
'/me',
|
||||
'/orders',
|
||||
'/user-profile',
|
||||
'/my-cards',
|
||||
'/my-addresses/310',
|
||||
'/coupons/310',
|
||||
'/my-support/310',
|
||||
'/new-ticket/310',
|
||||
'/change-password',
|
||||
'/checkout/310',
|
||||
// 带动态 id(无效 id 也应优雅处理不崩)
|
||||
'/orderdetail/1',
|
||||
'/paynow/1',
|
||||
'/view-ticket/1',
|
||||
'/new-comment/1',
|
||||
'/ocr-scan',
|
||||
'/renew-license',
|
||||
'/renew-minioffice/1',
|
||||
];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(([u, p]) => { localStorage['_test_token'] = u + '|' + p; }, [USER, PASS]);
|
||||
|
||||
// 先访问首页触发 autologin
|
||||
const page = await ctx.newPage();
|
||||
let autologinOk = false;
|
||||
page.on('console', m => { if (m.text().includes('CK_AUTOLOGIN_OK')) autologinOk = true; });
|
||||
console.log('触发 autologin...');
|
||||
await page.goto(BASE + '/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(8000);
|
||||
console.log('autologin:', autologinOk ? '✓ 成功' : '✗ 未确认\n');
|
||||
|
||||
const results = [];
|
||||
for (const route of AUTH_ROUTES) {
|
||||
const errors = [];
|
||||
const stacks = [];
|
||||
const pageErrs = [];
|
||||
page.removeAllListeners('console');
|
||||
page.removeAllListeners('pageerror');
|
||||
page.on('console', m => {
|
||||
const t = m.text();
|
||||
if (t.startsWith('CK_ERROR:')) errors.push(t.slice(0, 120));
|
||||
if (t.startsWith('CK_STACK:')) {
|
||||
const proj = t.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) stacks.push([...new Set(proj)].slice(0, 2));
|
||||
}
|
||||
});
|
||||
page.on('pageerror', e => pageErrs.push(e.message.slice(0, 120)));
|
||||
|
||||
try {
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 });
|
||||
await page.waitForTimeout(6000);
|
||||
} catch (e) {
|
||||
errors.push('GOTO: ' + e.message.slice(0, 80));
|
||||
}
|
||||
|
||||
const allErr = [...errors, ...pageErrs];
|
||||
const status = allErr.length === 0 ? '✓ OK' : `✗ ${allErr.length}`;
|
||||
console.log(`${status.padEnd(10)} ${route}`);
|
||||
if (allErr.length) {
|
||||
console.log(` ${allErr[0].slice(0, 120)}`);
|
||||
if (stacks.length) console.log(` 位置: ${JSON.stringify(stacks[0])}`);
|
||||
}
|
||||
results.push({ route, errors: allErr, stacks });
|
||||
}
|
||||
|
||||
const failed = results.filter(r => r.errors.length > 0);
|
||||
console.log(`\n========== 汇总 ==========`);
|
||||
console.log(`${results.length} 个登录态路由,${failed.length} 个有错误`);
|
||||
if (failed.length) {
|
||||
const locMap = {};
|
||||
for (const f of failed) {
|
||||
const loc = f.stacks[0] ? f.stacks[0][0] : f.errors[0].slice(0, 40);
|
||||
locMap[loc] = locMap[loc] || [];
|
||||
locMap[loc].push(f.route);
|
||||
}
|
||||
for (const [loc, routes] of Object.entries(locMap)) {
|
||||
console.log(` ${loc}\n → ${routes.join(', ')}`);
|
||||
}
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,71 @@
|
||||
// Mobile 视口测试(width 375,触发 ScreenTypeLayout mobile 分支)
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
const ROUTES = [
|
||||
'/',
|
||||
'/shop',
|
||||
'/shop/310',
|
||||
'/me',
|
||||
'/orders',
|
||||
'/checkout/310',
|
||||
'/blog/310',
|
||||
'/view-blog/52',
|
||||
'/download',
|
||||
'/about-us',
|
||||
'/contact-us',
|
||||
'/user-profile',
|
||||
'/my-addresses/310',
|
||||
'/new-user',
|
||||
];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 375, height: 812 } }); // iPhone 尺寸
|
||||
await ctx.addInitScript(([u, p]) => { localStorage['_test_token'] = u + '|' + p; }, ['6477108200', '8818']);
|
||||
|
||||
const page = await ctx.newPage();
|
||||
// 先 autologin
|
||||
page.on('console', () => {});
|
||||
await page.goto(BASE + '/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(8000);
|
||||
console.log('Mobile 视口 (375x812) 测试:\n');
|
||||
|
||||
const results = [];
|
||||
for (const route of ROUTES) {
|
||||
const errors = [];
|
||||
const stacks = [];
|
||||
const pageErrs = [];
|
||||
page.removeAllListeners('console');
|
||||
page.removeAllListeners('pageerror');
|
||||
page.on('console', m => {
|
||||
const t = m.text();
|
||||
if (t.startsWith('CK_ERROR:')) errors.push(t.slice(0, 100));
|
||||
if (t.startsWith('CK_STACK:')) {
|
||||
const proj = t.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) stacks.push([...new Set(proj)].slice(0, 2));
|
||||
}
|
||||
});
|
||||
page.on('pageerror', e => pageErrs.push(e.message.slice(0, 100)));
|
||||
try {
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 });
|
||||
await page.waitForTimeout(5000);
|
||||
} catch (e) { errors.push('GOTO: ' + e.message.slice(0, 70)); }
|
||||
|
||||
const allErr = [...errors, ...pageErrs];
|
||||
const status = allErr.length === 0 ? '✓ OK' : `✗ ${allErr.length}`;
|
||||
console.log(`${status.padEnd(10)} ${route}`);
|
||||
if (allErr.length) {
|
||||
console.log(` ${allErr[0].slice(0, 100)}`);
|
||||
if (stacks.length) console.log(` 位置: ${JSON.stringify(stacks[0])}`);
|
||||
}
|
||||
results.push({ route, errors: allErr, stacks });
|
||||
}
|
||||
|
||||
const failed = results.filter(r => r.errors.length > 0);
|
||||
console.log(`\n========== 汇总 ==========`);
|
||||
console.log(`${results.length} 路由 (mobile),${failed.length} 个有错误`);
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,22 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
// 注入 _test_token(addInitScript 在每个页面加载前执行)
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(8000); // 等 autologin
|
||||
const ok = logs.filter(l => l.includes('CK_AUTOLOGIN_OK'));
|
||||
const fail = logs.filter(l => l.includes('CK_AUTOLOGIN_FAIL'));
|
||||
console.log('autologin:', ok.length ? '✓ OK' : (fail.length ? '✗ FAIL '+fail[0].slice(0,100) : '? 未触发'));
|
||||
// 访问 /me(登录态)
|
||||
await page.goto('http://127.0.0.1:8099/me', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
const errs = logs.filter(l => l.startsWith('CK_ERROR') || l.includes('PAGEERROR'));
|
||||
console.log('/me 错误数:', errs.length);
|
||||
if (errs.length) console.log(' ', errs[0].slice(0,150));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(10000);
|
||||
// 确认 localStorage 注入 + autologin 日志
|
||||
const ls = await page.evaluate(() => localStorage['_test_token']);
|
||||
console.log('localStorage 注入:', ls ? '✓' : '✗');
|
||||
console.log('autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,20 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
// 第一次访问触发编译
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 60000 });
|
||||
await page.waitForTimeout(3000);
|
||||
console.log('编译完成 canvas:', await page.locator('canvas').count());
|
||||
// reload 让 main() 重跑(devAutologin)
|
||||
logs.length = 0;
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(10000);
|
||||
console.log('reload 后 autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,17 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('response', r => { if (r.url().includes('access_token')) console.log('登录API:', r.status()); });
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 60000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(18000);
|
||||
console.log('所有 CK_AUTOLOGIN 日志:');
|
||||
logs.filter(l=>l.includes('AUTOLOGIN')).forEach(l => console.log(' ', l.slice(0,120)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('response', r => { if (r.url().includes('access_token')) console.log(' 登录API:', r.status()); });
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 60000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(25000);
|
||||
console.log('autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,18 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('request', r => { if (r.url().includes('access_token')) console.log(' →请求发出:', r.url().slice(-40)); });
|
||||
page.on('response', r => { if (r.url().includes('access_token')) console.log(' ←响应:', r.status()); });
|
||||
page.on('requestfailed', r => { if (r.url().includes('access_token') || r.url().includes('api.minipos')) console.log(' ✗请求失败:', r.url().slice(-50), r.failure()?.errorText); });
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(15000); // 等 3s 延迟 + autologin
|
||||
console.log('autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const errs = [];
|
||||
page.on('console', m => { if (m.text().includes('RangeError')||m.text().startsWith('CK_ERROR')) errs.push(m.text().slice(0,120)); });
|
||||
page.on('response', r => { if (r.url().includes('get-buy-service')) console.log(' API:', r.url().split('get-buy-service')[1], '→', r.status()); });
|
||||
// 试几个 service 参数
|
||||
for (const p of ['/buy-service/1/ministore', '/buy-service/1/igoshow', '/buy-service/310/ministore']) {
|
||||
errs.length = 0;
|
||||
await page.goto('http://127.0.0.1:8099'+p, { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
console.log(p, errs.length ? '✗ '+errs[0] : '✓ OK');
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
Reference in New Issue
Block a user