feat: 更新鸿蒙应用配置与功能优化

- 添加鸿蒙分层图标配置和生成脚本
- 修复数据导出JSON解析问题
- 优化关于页面和团队信息展示
- 更新应用版本至1.4.1
- 清理代码警告和冗余文件
- 添加字体和二维码测试脚本
- 完善鸿蒙适配文档和指南
This commit is contained in:
Developer
2026-04-25 09:52:06 +08:00
parent 3c90407bb5
commit 4ec348b28e
63 changed files with 4538 additions and 1818 deletions

View File

@@ -4,6 +4,7 @@
* 作用: 交友软件风格左右滑动浏览菜品,支持分类筛选/搜索/收藏/全屏查看/分享
* 创建: 2026-04-14
* 更新: 2026-04-14 引入flutter_card_swiper重构滑动系统移除自定义手势/动画控制器
* 更新: 2026-04-25 清理5个unnecessary_underscores警告(双下划线→单下划线)
*/
import 'dart:async';
@@ -845,7 +846,7 @@ class _MiniCardPageState extends State<MiniCardPage> {
horizontal: DesignTokens.space4,
),
itemCount: _searchResults.length,
separatorBuilder: (_, __) => const SizedBox(height: 4),
separatorBuilder: (_, _) => const SizedBox(height: 4),
itemBuilder: (context, index) {
final recipe = _searchResults[index];
final catInfo = _meta?.categories[recipe.category];
@@ -877,14 +878,14 @@ class _MiniCardPageState extends State<MiniCardPage> {
memCacheHeight: 96,
maxWidthDiskCache: 200,
maxHeightDiskCache: 200,
errorWidget: (_, __, _) => Container(
errorWidget: (_, _, _) => Container(
color: DesignTokens.background,
child: const Icon(
CupertinoIcons.photo,
size: 20,
),
),
progressIndicatorBuilder: (_, __, _) =>
progressIndicatorBuilder: (_, _, _) =>
Container(
color: DesignTokens.background,
child: const CupertinoActivityIndicator(
@@ -947,7 +948,7 @@ class _MiniCardPageState extends State<MiniCardPage> {
maxHeightDiskCache: 800,
fadeInDuration: const Duration(milliseconds: 300),
fadeInCurve: Curves.easeOut,
errorWidget: (_, __, _) => Container(
errorWidget: (_, _, _) => Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
@@ -966,7 +967,7 @@ class _MiniCardPageState extends State<MiniCardPage> {
),
),
),
progressIndicatorBuilder: (_, __, _) => Container(
progressIndicatorBuilder: (_, _, _) => Container(
color: DesignTokens.background,
child: const Center(child: CupertinoActivityIndicator()),
),

View File

@@ -281,7 +281,7 @@ class _StatsDashboardPageState extends State<StatsDashboardPage> {
_formatNumber(nutritionRecords),
DesignTokens.teal,
isDark,
subtitle: '$nutritionTypes 项日志',
subtitle: '$nutritionTypes 项日志(归档)',
),
),
const SizedBox(width: DesignTokens.space2),

View File

@@ -20,6 +20,7 @@ import 'package:cute_kitchen/src/pages/profile/info/learn_us_page.dart';
import 'package:cute_kitchen/src/pages/profile/info/privacy_policy_page.dart';
import 'package:cute_kitchen/src/pages/profile/tools/permission_page.dart';
import 'package:cute_kitchen/src/pages/profile/info/references_page.dart';
import 'package:cute_kitchen/src/utils/platform_utils.dart';
import 'package:cute_kitchen/src/services/core/app_info_service.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -251,13 +252,7 @@ class AboutPage extends StatelessWidget {
title: '评价应用',
subtitle: '在应用商店给我们评分',
isDark: isDark,
onTap: () {
Get.snackbar(
'提示',
'未找到应用商店链接',
snackPosition: SnackPosition.BOTTOM,
);
},
onTap: () => _onRateApp(context, isDark),
),
_buildDivider(isDark),
_buildActionTile(
@@ -448,6 +443,67 @@ class AboutPage extends StatelessWidget {
);
}
void _onRateApp(BuildContext context, bool isDark) {
if (PlatformUtils().isHarmonyOS) {
showCupertinoDialog(
context: context,
builder: (ctx) => CupertinoAlertDialog(
title: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('', style: TextStyle(fontSize: 24)),
SizedBox(width: 8),
Text('给个五星好评吧'),
],
),
content: const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('如果您喜欢小妈厨房,请给我们一个好评!'),
Text(
'您的支持是我们前进的动力 💪',
style: TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
),
),
SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('⭐⭐⭐⭐⭐', style: TextStyle(fontSize: 28))],
),
],
),
),
actions: [
CupertinoDialogAction(
onPressed: () => Navigator.pop(ctx),
isDestructiveAction: true,
child: const Text('下次再说'),
),
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Navigator.pop(ctx);
launchUrl(
Uri.parse(
'https://appgallery.huawei.com/app/detail?id=cute.major.kitchen',
),
mode: LaunchMode.externalApplication,
);
},
child: const Text('去评价 ⭐'),
),
],
),
);
} else {
Get.snackbar('提示', '未找到应用商店链接', snackPosition: SnackPosition.BOTTOM);
}
}
void _showEmailSheet(BuildContext context, bool isDark) {
final emails = [
_ContactEmail(
@@ -471,6 +527,11 @@ class AboutPage extends StatelessWidget {
icon: '📬',
),
_ContactEmail(address: '5147662@qq.com', label: '任意邮箱均可联系', icon: '📬'),
_ContactEmail(
address: 'lzy20010304@gmail.com',
label: '任意邮箱均可联系',
icon: '📧',
),
];
showCupertinoModalPopup(

View File

@@ -391,7 +391,8 @@ class _AppInfoPageState extends State<AppInfoPage> {
String buildSdk = 'Unknown';
try {
final platform = PlatformUtils();
if (platform.isHarmonyOS || defaultTargetPlatform == TargetPlatform.ohos) {
if (platform.isHarmonyOS ||
defaultTargetPlatform == TargetPlatform.ohos) {
buildSdk = 'Deveco API 23';
} else if (platform.isAndroid) {
buildSdk = 'Android Target 36';
@@ -629,13 +630,7 @@ class _AppInfoPageState extends State<AppInfoPage> {
_buildDivider(isDark),
_buildInfoItem('Web 服务器', 'Nginx', CupertinoIcons.globe, isDark),
_buildDivider(isDark),
_buildCopyableItem(
'App 在线版Beta测试',
'https://www.wktyl.com/app',
CupertinoIcons.link,
isDark,
primaryColor,
),
_buildWebAppItem(isDark, primaryColor),
],
),
);
@@ -795,16 +790,21 @@ class _AppInfoPageState extends State<AppInfoPage> {
children: [
_buildUpdateItem(
'版本 ${AppConfig.appVersion}',
'2026-04-23',
['升级鸿蒙api 23', '优化关于页面布局与交互体验'],
'2026-04-25',
[
'修复鸿蒙端数据导入问题',
'现在你从APP导出的备份数据在小妈厨房 androidHarmonyWebWindows 可直接导入了',
'现已支持将菜品导出为PDFWord (实验)',
],
isDark,
primaryColor,
),
const SizedBox(height: DesignTokens.space3),
_buildUpdateItem(
'版本 0.99.6',
'2026-04-18',
['工具中心', '工具下拉页面使用new UI 旧版ui停止维护'],
'版本 1.0.1',
'2026-04-20',
['优化工具中心下拉动画', '工具中心暂时归档,不再增加功能'],
isDark,
primaryColor,
),
@@ -1232,6 +1232,107 @@ class _AppInfoPageState extends State<AppInfoPage> {
);
}
Widget _buildWebAppItem(bool isDark, Color primaryColor) {
return GestureDetector(
onTap: () => _showWebAppDialog(isDark),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
vertical: DesignTokens.space3,
),
child: Row(
children: [
Icon(
CupertinoIcons.link,
size: 20,
color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3,
),
const SizedBox(width: DesignTokens.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'App 在线版Beta测试',
style: TextStyle(
fontSize: DesignTokens.fontMd,
fontWeight: FontWeight.w500,
color: isDark
? DarkDesignTokens.text1
: DesignTokens.text1,
),
),
const SizedBox(height: 2),
Text(
'https://www.wktyl.com/app',
style: TextStyle(
fontSize: DesignTokens.fontSm,
color: isDark
? DarkDesignTokens.text3
: DesignTokens.text3,
),
),
],
),
),
Icon(
CupertinoIcons.arrow_up_right_circle,
size: 16,
color: primaryColor.withValues(alpha: 0.6),
),
],
),
),
);
}
void _showWebAppDialog(bool isDark) {
showCupertinoDialog(
context: Get.context!,
builder: (ctx) => CupertinoAlertDialog(
title: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('🌐', style: TextStyle(fontSize: 22)),
SizedBox(width: 8),
Text('打开在线版'),
],
),
content: const Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 8),
Text('首次打开需要加载资源,预计等待'),
Text(
'30~60 秒',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
Text('请耐心等待,后续访问会明显加快 🚀'),
],
),
actions: [
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () => Navigator.pop(ctx),
child: const Text('取消'),
),
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Navigator.pop(ctx);
launchUrl(
Uri.parse('https://www.wktyl.com/app'),
mode: LaunchMode.externalApplication,
);
},
child: const Text('确认打开'),
),
],
),
);
}
Widget _buildCopyableItem(
String title,
String value,

View File

@@ -394,7 +394,7 @@ class LearnUsPage extends StatelessWidget {
children: [
_buildTeamMember('💻', '程序设计', '纯情小妈', '喜欢发呆', isDark),
_buildDivider(isDark),
_buildTeamMember('🎨', 'UI/UX/Testing', 'Freetime', '关于你的风景。', isDark),
_buildTeamMember('🎨', 'UI/UX/Tools', 'Freetime', '关于你的风景。', isDark),
_buildDivider(isDark),
_buildTeamMember('⚙️', '后端开发', '伯乐不相马', '还是做不到吗?', isDark),
_buildDivider(isDark),
@@ -830,6 +830,11 @@ class LearnUsPage extends StatelessWidget {
icon: '📬',
),
_ContactEmail(address: '5147662@qq.com', label: '任意邮箱均可联系', icon: '📬'),
_ContactEmail(
address: 'lzy20010304@gmail.com',
label: '任意邮箱均可联系',
icon: '📧',
),
];
showCupertinoModalPopup(

View File

@@ -0,0 +1,290 @@
/*
* 文件: known_issues_sheet.dart
* 名称: 已知问题底部弹出面板
* 作用: 展示应用已知问题与说明的独立组件,从 profile_settings.dart 分流提取
* 创建: 2026-04-25
* 更新: 2026-04-25 从 profile_settings.dart 提取,降低主文件体积
*/
import 'package:flutter/cupertino.dart';
import 'package:cute_kitchen/src/config/design_tokens.dart';
enum IssueLevel { important, warning, info }
class KnownIssueItem {
final String icon;
final String title;
final IssueLevel level;
final String description;
const KnownIssueItem({
required this.icon,
required this.title,
required this.level,
required this.description,
});
}
class KnownIssuesSheet {
static List<KnownIssueItem> get issues => [
KnownIssueItem(
icon: '🌐',
title: 'VPN/代理环境部分功能异常',
level: IssueLevel.warning,
description: '使用代理或 VPN 时,部分接口请求可能超时或失败,建议在直连环境或者国内网络环境下使用以获得最佳体验。',
),
KnownIssueItem(
icon: '🧊',
title: 'Win10/11端 页面返回问题',
level: IssueLevel.warning,
description: '部分页面未设置返回逻辑,需使用快捷键 同时按下 ALT+方向左键/Escape(Esc键) 返回上一页。',
),
KnownIssueItem(
icon: '📂',
title: '分类列表默认显示前20项',
level: IssueLevel.info,
description:
'为节省服务器资源大部分分类菜系、标签等默认只展示前20条。低于20条的分类会显示全部数量。后续将支持"加载更多"。',
),
KnownIssueItem(
icon: '🔑',
title: 'Key 码获取方式',
level: IssueLevel.important,
description: '当前暂未开放注册登录,需通过邀请获取 Key 码激活。后续版本将开放付费购买渠道。',
),
KnownIssueItem(
icon: '🧊',
title: '液态玻璃效果 GPU 占用较高',
level: IssueLevel.warning,
description:
'液态玻璃Liquid Glass效果对 GPU 要求较高,长时间使用可能出现轻微发热。低性能设备建议在设置中关闭毛玻璃效果。',
),
KnownIssueItem(
icon: '🚧',
title: '开发中功能免费,完成后可能收费',
level: IssueLevel.important,
description: '当前处于开发阶段的功能均可免费使用。功能开发完成并稳定后,部分高级功能可能转为付费订阅制。',
),
KnownIssueItem(
icon: '📱',
title: '大部分页面横屏适配未完成',
level: IssueLevel.info,
description: '当前主要针对竖屏优化,横屏或折叠屏展开状态下部分页面布局可能不够理想,后续版本将逐步适配。',
),
KnownIssueItem(
icon: '🖼️',
title: '图片加载偶现延迟',
level: IssueLevel.info,
description:
'服务器带宽6MB/s换算后加载资源每秒约500Kb首次加载可能较慢。高并发时段服务器会触发自我保护机制限制单连接速率带宽达到上限时也会出现加载缓慢或失败。已访问过的图片会自动缓存命中缓存加载将显著加快。后续会增加无图模式。',
),
KnownIssueItem(
icon: '📧',
title: '邮箱发送受限说明',
level: IssueLevel.warning,
description:
'邮箱发送功能仅支持国内网络环境。使用代理、VPN或在海外网络下无法发送即使显示发送成功也会失败。路由器端代理同样会导致发送失败。部分WiFi环境下可能因服务商拦截而无法发送可切换移动数据重试。建议在稳定的国内网络下使用此功能。',
),
KnownIssueItem(
icon: '🔍',
title: 'web端 部分功能未开放',
level: IssueLevel.info,
description: '由于服务器占用较大,部分功能在线版暂未开放,无法加载。',
),
KnownIssueItem(
icon: '💾',
title: '离线模式功能有限',
level: IssueLevel.warning,
description: '无网络时仅可查看已缓存的菜谱和分类数据,筛选、推荐等需要联网的功能暂不可用。后续将增强离线体验。',
),
KnownIssueItem(
icon: '📄',
title: '部分平台导出文件后缀重复',
level: IssueLevel.warning,
description:
'已知鸿蒙端导出文件后缀会出现重复(如 xxx.pdf.pdf、xxx.json.json ....),此为系统文件选择器自动追加后缀导致。当前问题排查中,暂无修复计划。用户可在导出页面手动删除多余后缀。',
),
];
static void show(BuildContext context, bool isDark) {
showCupertinoModalPopup(
context: context,
builder: (ctx) => Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(ctx).size.height * 0.75,
),
decoration: BoxDecoration(
color: isDark ? DarkDesignTokens.card : DesignTokens.card,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(DesignTokens.radiusXl),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: DesignTokens.space2),
Container(
width: 36,
height: 4,
decoration: BoxDecoration(
color: (isDark ? DarkDesignTokens.text3 : DesignTokens.text3)
.withValues(alpha: 0.3),
borderRadius: DesignTokens.borderRadiusSm,
),
),
const SizedBox(height: DesignTokens.space3),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
),
child: Row(
children: [
const Text('⚠️', style: TextStyle(fontSize: 20)),
const SizedBox(width: DesignTokens.space2),
Text(
'已知问题与说明',
style: TextStyle(
fontSize: DesignTokens.fontLg,
fontWeight: FontWeight.w700,
color: isDark
? DarkDesignTokens.text1
: DesignTokens.text1,
),
),
const Spacer(),
GestureDetector(
onTap: () => Navigator.pop(ctx),
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color:
(isDark
? DarkDesignTokens.text3
: DesignTokens.text3)
.withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.xmark,
size: 14,
color: isDark
? DarkDesignTokens.text2
: DesignTokens.text2,
),
),
),
],
),
),
const SizedBox(height: DesignTokens.space3),
Flexible(
child: ListView.separated(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
),
itemCount: issues.length,
separatorBuilder: (_, __) =>
const SizedBox(height: DesignTokens.space2),
itemBuilder: (_, index) =>
_buildIssueCard(issues[index], isDark),
),
),
const SizedBox(height: DesignTokens.space4),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
),
child: SizedBox(
width: double.infinity,
child: CupertinoButton(
borderRadius: DesignTokens.borderRadiusLg,
color: DesignTokens.primaryLight,
onPressed: () => Navigator.pop(ctx),
child: Text(
'我知道了',
style: TextStyle(
color: DesignTokens.dynamicPrimary,
fontWeight: FontWeight.w600,
),
),
),
),
),
SizedBox(
height: MediaQuery.of(ctx).padding.bottom + DesignTokens.space3,
),
],
),
),
);
}
static Widget _buildIssueCard(KnownIssueItem issue, bool isDark) {
final levelColor = switch (issue.level) {
IssueLevel.important => CupertinoColors.destructiveRed,
IssueLevel.warning => CupertinoColors.activeOrange,
IssueLevel.info => DesignTokens.dynamicPrimary,
};
return Container(
width: double.infinity,
padding: const EdgeInsets.all(DesignTokens.space3),
decoration: BoxDecoration(
color: levelColor.withValues(alpha: 0.06),
borderRadius: DesignTokens.borderRadiusLg,
border: Border.all(color: levelColor.withValues(alpha: 0.15)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(issue.icon, style: const TextStyle(fontSize: 16)),
const SizedBox(width: DesignTokens.space2),
Expanded(
child: Text(
issue.title,
style: TextStyle(
fontSize: DesignTokens.fontMd,
fontWeight: FontWeight.w600,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: levelColor.withValues(alpha: 0.12),
borderRadius: DesignTokens.borderRadiusSm,
),
child: Text(
switch (issue.level) {
IssueLevel.important => '重要',
IssueLevel.warning => '注意',
IssueLevel.info => '提示',
},
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: levelColor,
),
),
),
],
),
const SizedBox(height: DesignTokens.space2),
Text(
issue.description,
style: TextStyle(
fontSize: DesignTokens.fontXs,
height: 1.5,
color: isDark ? DarkDesignTokens.text2 : DesignTokens.text2,
),
),
],
),
);
}
}

View File

@@ -1,4 +1,4 @@
/*
/*
* 文件: profile_settings.dart
* 名称: 个人中心设置标签
* 作用: iOS 26 风格的设置选项,使用 DesignTokens 和 GlassSettingsTile
@@ -19,6 +19,7 @@ import 'package:cute_kitchen/src/pages/tools/cooking/cooking_note_page.dart';
import 'package:cute_kitchen/src/pages/profile/social/footprints_page.dart';
import 'package:cute_kitchen/src/pages/profile/data/cache_manage_page.dart';
import 'package:cute_kitchen/src/pages/profile/tools/data_export_page.dart';
import 'package:cute_kitchen/src/pages/profile/known_issues_sheet.dart';
class ProfileSettingsTab extends StatelessWidget {
const ProfileSettingsTab({super.key});
@@ -94,7 +95,7 @@ class ProfileSettingsTab extends StatelessWidget {
icon: CupertinoIcons.exclamationmark_triangle,
title: '已知问题 ⚠️',
isDark: isDark,
onTap: () => _showKnownIssuesSheet(context, isDark),
onTap: () => KnownIssuesSheet.show(context, isDark),
),
// _buildTile(
// icon: CupertinoIcons.flame,
@@ -481,277 +482,6 @@ class ProfileSettingsTab extends StatelessWidget {
),
);
}
void _showKnownIssuesSheet(BuildContext context, bool isDark) {
final issues = [
_KnownIssue(
icon: '🌐',
title: 'VPN/代理环境部分功能异常',
level: _IssueLevel.warning,
description: '使用代理或 VPN 时,部分接口请求可能超时或失败,建议在直连环境或者国内网络环境下使用以获得最佳体验。',
),
_KnownIssue(
icon: '🧊',
title: 'Win10/11端 页面返回问题',
level: _IssueLevel.warning,
description: '部分页面未设置返回逻辑,需使用快捷键 同时按下 ALT+方向左键/Escape(Esc键) 返回上一页。',
),
_KnownIssue(
icon: '📂',
title: '分类列表默认显示前20项',
level: _IssueLevel.info,
description:
'为节省服务器资源大部分分类菜系、标签等默认只展示前20条。低于20条的分类会显示全部数量。后续将支持"加载更多"。',
),
_KnownIssue(
icon: '🔑',
title: 'Key 码获取方式',
level: _IssueLevel.important,
description: '当前暂未开放注册登录,需通过邀请获取 Key 码激活。后续版本将开放付费购买渠道。',
),
_KnownIssue(
icon: '🧊',
title: '液态玻璃效果 GPU 占用较高',
level: _IssueLevel.warning,
description:
'液态玻璃Liquid Glass效果对 GPU 要求较高,长时间使用可能出现轻微发热。低性能设备建议在设置中关闭毛玻璃效果。',
),
_KnownIssue(
icon: '🚧',
title: '开发中功能免费,完成后可能收费',
level: _IssueLevel.important,
description: '当前处于开发阶段的功能均可免费使用。功能开发完成并稳定后,部分高级功能可能转为付费订阅制。',
),
_KnownIssue(
icon: '📱',
title: '大部分页面横屏适配未完成',
level: _IssueLevel.info,
description: '当前主要针对竖屏优化,横屏或折叠屏展开状态下部分页面布局可能不够理想,后续版本将逐步适配。',
),
_KnownIssue(
icon: '🖼️',
title: '图片加载偶现延迟',
level: _IssueLevel.info,
description:
'服务器带宽6MB/s换算后加载资源每秒约500Kb首次加载可能较慢。高并发时段服务器会触发自我保护机制限制单连接速率带宽达到上限时也会出现加载缓慢或失败。已访问过的图片会自动缓存命中缓存加载将显著加快。后续会增加无图模式。',
),
_KnownIssue(
icon: '📧',
title: '邮箱发送受限说明',
level: _IssueLevel.warning,
description:
'邮箱发送功能仅支持国内网络环境。使用代理、VPN或在海外网络下无法发送即使显示发送成功也会失败。路由器端代理同样会导致发送失败。部分WiFi环境下可能因服务商拦截而无法发送可切换移动数据重试。建议在稳定的国内网络下使用此功能。',
),
_KnownIssue(
icon: '🔍',
title: 'web端 部分功能未开放',
level: _IssueLevel.info,
description: '由于服务器占用较大,部分功能在线版暂未开放,无法加载。',
),
_KnownIssue(
icon: '💾',
title: '离线模式功能有限',
level: _IssueLevel.warning,
description: '无网络时仅可查看已缓存的菜谱和分类数据,筛选、推荐等需要联网的功能暂不可用。后续将增强离线体验。',
),
];
showCupertinoModalPopup(
context: context,
builder: (ctx) => Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(ctx).size.height * 0.75,
),
decoration: BoxDecoration(
color: isDark ? DarkDesignTokens.card : DesignTokens.card,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(DesignTokens.radiusXl),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: DesignTokens.space2),
Container(
width: 36,
height: 4,
decoration: BoxDecoration(
color: (isDark ? DarkDesignTokens.text3 : DesignTokens.text3)
.withValues(alpha: 0.3),
borderRadius: DesignTokens.borderRadiusSm,
),
),
const SizedBox(height: DesignTokens.space3),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
),
child: Row(
children: [
const Text('⚠️', style: TextStyle(fontSize: 20)),
const SizedBox(width: DesignTokens.space2),
Text(
'已知问题与说明',
style: TextStyle(
fontSize: DesignTokens.fontLg,
fontWeight: FontWeight.w700,
color: isDark
? DarkDesignTokens.text1
: DesignTokens.text1,
),
),
const Spacer(),
GestureDetector(
onTap: () => Navigator.pop(ctx),
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color:
(isDark
? DarkDesignTokens.text3
: DesignTokens.text3)
.withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.xmark,
size: 14,
color: isDark
? DarkDesignTokens.text2
: DesignTokens.text2,
),
),
),
],
),
),
const SizedBox(height: DesignTokens.space3),
Flexible(
child: ListView.separated(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
),
itemCount: issues.length,
separatorBuilder: (_, __) =>
const SizedBox(height: DesignTokens.space2),
itemBuilder: (_, index) =>
_buildIssueCard(issues[index], isDark),
),
),
const SizedBox(height: DesignTokens.space4),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
),
child: SizedBox(
width: double.infinity,
child: CupertinoButton(
borderRadius: DesignTokens.borderRadiusLg,
color: DesignTokens.primaryLight,
onPressed: () => Navigator.pop(ctx),
child: Text(
'我知道了',
style: TextStyle(
color: DesignTokens.dynamicPrimary,
fontWeight: FontWeight.w600,
),
),
),
),
),
SizedBox(
height: MediaQuery.of(ctx).padding.bottom + DesignTokens.space3,
),
],
),
),
);
}
Widget _buildIssueCard(_KnownIssue issue, bool isDark) {
final levelColor = switch (issue.level) {
_IssueLevel.important => CupertinoColors.destructiveRed,
_IssueLevel.warning => CupertinoColors.activeOrange,
_IssueLevel.info => DesignTokens.dynamicPrimary,
};
return Container(
width: double.infinity,
padding: const EdgeInsets.all(DesignTokens.space3),
decoration: BoxDecoration(
color: levelColor.withValues(alpha: 0.06),
borderRadius: DesignTokens.borderRadiusLg,
border: Border.all(color: levelColor.withValues(alpha: 0.15)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(issue.icon, style: const TextStyle(fontSize: 16)),
const SizedBox(width: DesignTokens.space2),
Expanded(
child: Text(
issue.title,
style: TextStyle(
fontSize: DesignTokens.fontMd,
fontWeight: FontWeight.w600,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: levelColor.withValues(alpha: 0.12),
borderRadius: DesignTokens.borderRadiusSm,
),
child: Text(
switch (issue.level) {
_IssueLevel.important => '重要',
_IssueLevel.warning => '注意',
_IssueLevel.info => '提示',
},
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: levelColor,
),
),
),
],
),
const SizedBox(height: DesignTokens.space2),
Text(
issue.description,
style: TextStyle(
fontSize: DesignTokens.fontXs,
height: 1.5,
color: isDark ? DarkDesignTokens.text2 : DesignTokens.text2,
),
),
],
),
);
}
}
enum _IssueLevel { important, warning, info }
class _KnownIssue {
final String icon;
final String title;
final _IssueLevel level;
final String description;
const _KnownIssue({
required this.icon,
required this.title,
required this.level,
required this.description,
});
}
enum _PlanStatus { coming, dev, plan }

View File

@@ -388,7 +388,10 @@ class _DataExportPageState extends State<DataExportPage> {
final lastBracket = content.lastIndexOf(']');
if (lastBracket >= 0 && lastBracket < content.length - 1) {
final trailing = content.substring(lastBracket + 1).trim();
if (trailing.isNotEmpty) {
if (trailing.isNotEmpty &&
!trailing.startsWith(',') &&
!trailing.startsWith('}') &&
!trailing.startsWith(']')) {
debugPrint('🧹 检测到尾部脏数据(${trailing.length}字符)已截取有效JSON');
content = content.substring(0, lastBracket + 1);
}

View File

@@ -317,7 +317,10 @@ class DataExportService extends GetxService {
final lastBracket = content.lastIndexOf(']');
if (lastBracket >= 0 && lastBracket < content.length - 1) {
final trailing = content.substring(lastBracket + 1).trim();
if (trailing.isNotEmpty) {
if (trailing.isNotEmpty &&
!trailing.startsWith(',') &&
!trailing.startsWith('}') &&
!trailing.startsWith(']')) {
debugPrint(
'DataExportService: 检测到尾部脏数据(${trailing.length}字符)已截取有效JSON',
);