diff --git a/.gitignore b/.gitignore index 2c63242c0a568ab8f115176cd3e844c2d78251a2..0869f955bdc090874267f74311b9b54973e1c128 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .atom/ .buildlog/ .history +.qoder/ .svn/ .fvm/ .vscode/ diff --git a/discover.md b/discover.md new file mode 100644 index 0000000000000000000000000000000000000000..73f692ce51936dc2daeeb474454c5cd1761a19ea --- /dev/null +++ b/discover.md @@ -0,0 +1,108 @@ +
+ +两个项目都在电脑本地,让 Trae Solo 分析 `oiocns-react` 项目中的 `plaza` 组件和 `src/ts` 业务逻辑,然后指导它在 `oiocns-flutter` 中实现发现页面。 + +以下是详细的实现方案,以及一套可以直接在 **Trae Solo** 中使用的、分步骤的提示词。这些提示词会引导 AI 分析 React 项目代码,并自动生成 Flutter 端的完整页面。 + +### 📱 实现策略:让 AI 分析→翻译→生成 + +由于 `oiocns-react` 的 `src/ts` 目录无法通过链接访问,但存在于你的本地,我们可以利用 Trae Solo 的**代码分析能力**,让它自动完成以下工作: + +1. **分析** React 项目中 `plaza` 组件的数据获取、状态管理逻辑。 +2. **翻译** 这些逻辑为 Flutter 的 Dart 代码(网络请求、数据模型、状态管理)。 +3. **生成** 完整的 Flutter 发现页面,包括微信风格的 UI 组件、栏目配置、数据接入和路由替换。 + +### 📂 本地目录结构(供 AI 理解) + +为了让 AI 准确找到文件,请确保 Trae Solo 的工作区包含以下路径(或能访问到): + +- **Flutter 项目根目录**:`oiocns-flutter/` +- **React 项目关键目录**: + - `oiocns-react/src/ts/` (业务逻辑层) + - `oiocns-react/src/pages/` (找到 `plaza` 组件所在位置) + +### 💬 在 Trae Solo 中使用的分步提示词 + +请在 Trae Solo 中按顺序执行以下提示词。每一步完成后,确认生成的代码无误,再继续下一步。 + +*** + +#### **步骤 1:分析 React 项目业务逻辑** + +**提示词:** + +> 请分析我本地 `oiocns-react` 项目中与 `plaza` 组件相关的代码,重点: +> +> 1. 在 `oiocns-react/src/ts` 目录下,找到所有与服务端通信、获取“新闻公告类、数据分享类、市场交易类、群分享类、视频类、直播类”这六种类型数据的 API 函数或服务。 +> 2. 找到 `plaza` 组件的位置,分析它如何使用这些 API,以及如何管理数据状态(例如使用 React state 或 React Query)。 +> 3. 将这些 API 的请求路径、参数、返回的数据结构详细总结出来。 +> +> 完成后,请向我展示分析结果,不要修改任何文件。 + +#### **步骤 2:设计 Flutter 端的数据服务层** + +**提示词:** + +> 根据上一步分析出的 API 定义,在 `oiocns-flutter` 项目的 `lib/pages/discover/` 目录下创建一个 `discover_service.dart` 文件。 +> 要求: +> +> - 创建对应的数据模型类(Dart class),能够序列化/反序列化 API 返回的 JSON 数据。 +> - 创建一个 `DiscoverService` 类,将每个 API 封装成一个静态的 `Future` 方法。 +> - 使用 `http` 或 `dio` 库(如果项目已配置,请检查 `pubspec.yaml`,否则用 `http`)。 +> - 请先只写代码,不要直接修改文件,等我确认后再写入。 + +#### **步骤 3:创建发现页面的配置与 UI 组件** + +**提示词:** + +> 现在开始在 `oiocns-flutter/lib/pages/discover/` 目录下生成发现页面的 UI 代码,严格模仿微信发现页的列表风格。 +> 请依次完成以下文件的创建: +> +> 1. **`discover_config.dart`**: +> - 定义 `PlazaType` 枚举(六个值)。 +> - 定义 `DiscoverItemConfig` 类,包含 `key`, `label`, `icon`, `plazaType`, `badge`。 +> - 生成一个 `discoverItems` 列表,按以下映射配置:动态→群分享,视频→视频,直播→直播,交易→市场交易,分享→数据分享,公告→新闻公告。图标使用 `Icons` 类中合适的图标。 +> 2. **`discover_item.dart`**: +> - 实现一个 `DiscoverItem` 无状态组件,接收 `DiscoverItemConfig`,渲染一个 `ListTile`,左侧图标,右侧箭头和可选的红色角标。 +> 3. **`discover_page.dart`**: +> - 实现 `DiscoverPage` 有状态组件,顶部有灰色背景的 `AppBar` 标题“发现”。 +> - 在 `initState` 中调用 `DiscoverService` 获取数据(暂用模拟数据,后续替换)。 +> - `body` 为 `ListView.builder`,使用 `discoverItems` 生成列表。 +> 请直接生成这些文件的完整代码,并写入到项目中。 + +#### **步骤 4:接入真实数据与状态管理** + +**提示词:** + +> 现在将 `discover_page.dart` 中的模拟数据替换为步骤 2 中创建的 `DiscoverService` 的真实网络请求。 +> 要求: +> +> - 在 `_DiscoverPageState` 中增加状态变量,存储 API 返回的未读计数等信息。 +> - 在 `initState` 中并发调用所有需要的 API(如果 API 是分开的),或调用一个聚合接口。 +> - 将返回的数据映射到每个 `DiscoverItemConfig` 的 `badge` 字段上。 +> - 添加下拉刷新功能(`RefreshIndicator`)来重新请求数据。 +> - 如果 API 尚未准备就绪,请保留模拟逻辑作为 fallback,并添加 TODO 注释。 + +#### **步骤 5:替换旧“数据”页面并更新导航** + +**提示词:** + +> 最后,在 Flutter 项目中找到原有的“数据”页面(可能在 `lib/pages/store/` 或底部导航栏配置中),执行以下操作: +> +> 1. 将指向旧页面的所有引用改为 `DiscoverPage`。 +> 2. 更新底部导航栏的图标和文字,将“数据”改为“发现”。 +> 3. 确保路由正确,运行项目后点击“发现”Tab 能显示新页面。 +> +> 请修改所有相关文件,并确保删除或注释掉旧页面的导入。 + +*** + +### 🔄 后续迭代 + +当你在 Trae Solo 中完成以上步骤后,你就得到了一个可用的“发现”页面。之后可以根据需要: + +- 完善每个栏目的点击跳转子页面(如视频列表、公告列表)。 +- 优化数据缓存策略(如使用 `provider` 或 `riverpod`)。 +- 完全替换模拟数据,确保后台接口就绪。 + +如果在执行过程中遇到任何报错或需要调整,可以直接将错误信息发给 Trae Solo 继续修正。 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 7c56964006274498b0edaa77763cdd72c6d42b6a..391a902b2bebe1a3df1b090312fd85029dd99308 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 12.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 647464e5a0904e6f9c680cc2cbf796f013cd82da..84a55a5cfc92ce223a6e16c7564161b684978d36 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -224,14 +224,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; @@ -245,14 +241,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -370,7 +362,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -558,7 +550,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -607,7 +599,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8e3ca5dfe1936519e96475be8d0b5ff5faa43727..e3773d42e24c8bb3b9070fc9d10d62032787035e 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/lib/components/ActivityWidget/ActivityListWidget/ActivityListWidget.dart b/lib/components/ActivityWidget/ActivityListWidget/ActivityListWidget.dart index f17a8224cf98b2d7ebccb8865e6e71356e561f64..9d246935e7143aa2477caaff5a3beb2027b02513 100644 --- a/lib/components/ActivityWidget/ActivityListWidget/ActivityListWidget.dart +++ b/lib/components/ActivityWidget/ActivityListWidget/ActivityListWidget.dart @@ -4,10 +4,8 @@ import 'package:orginone/components/TabContainerWidget/types.dart'; import 'package:orginone/components/EmptyWidget/EmptyActivity.dart'; import 'package:orginone/components/XButton/XButton.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import 'package:orginone/dart/base/common/commands.dart'; import 'package:orginone/dart/core/chat/activity.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../XButtonBar/XButtonBar.dart'; import '../../XImage/XImage.dart'; import '../../XLazy/XLazy.dart'; diff --git a/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/TargetActivityListWidget.dart b/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/TargetActivityListWidget.dart index 708b38b5b46236d0b287943ffcd38cff75492d76..258b74e1745cd5cf4a742ef6e17b1c3ac0eec5d8 100644 --- a/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/TargetActivityListWidget.dart +++ b/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/TargetActivityListWidget.dart @@ -34,9 +34,16 @@ class TargetActivityListWidget extends XStatelessWidget { } else if (params is IActivity) { activity = params; activityCount = activity.activityList.length; + } else { + // 参数类型不匹配,返回空容器 + return const Scaffold( + body: Center( + child: Text('无效的参数'), + ), + ); } - int initIndex = activity.currIndex ?? 0; + int initIndex = activity.currIndex; PageController pageController = PageController(initialPage: initIndex); pageCurrentIndex = initIndex; return Container( @@ -48,12 +55,12 @@ class TargetActivityListWidget extends XStatelessWidget { }, onPageChanged: (index) async { pageCurrentIndex = index; - if (index >= (activity.activityList.length ?? 1) - 3) { + if (index >= (activity.activityList.length - 3)) { await activity.load(); activity.changeCallback(); } }, - itemCount: activity.activityList.length ?? 1, + itemCount: activity.activityList.length, scrollDirection: Axis.vertical, controller: pageController), ); @@ -61,6 +68,14 @@ class TargetActivityListWidget extends XStatelessWidget { Widget _buildItem(PageController pageController, int indexItem) { double startOffset = 0; + + // 检查索引是否越界 + if (indexItem < 0 || indexItem >= activity.activityList.length) { + return const Center( + child: Text('数据索引错误'), + ); + } + Widget detail = ActivityCommentBoxWidget( body: NotificationListener( onNotification: (notification) { @@ -119,12 +134,10 @@ class TargetActivityListWidget extends XStatelessWidget { itemBuilder: (BuildContext context, int index) { return Container( padding: const EdgeInsets.only(bottom: 10.0), - child: null != activity - ? ActivityMessageWidget( - item: activity.activityList[indexItem], - activity: activity.activityList[indexItem].activity, - ) - : null); + child: ActivityMessageWidget( + item: activity.activityList[indexItem], + activity: activity.activityList[indexItem].activity, + )); }, )), ); diff --git a/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/components/ActivityCommentBoxWidget.dart b/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/components/ActivityCommentBoxWidget.dart index 545bc96814648049a9ed8a0c88c2f4e1b21cd7e1..83ed70bf9f13601b8fdf9113c5ee815a2a0f0ab4 100644 --- a/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/components/ActivityCommentBoxWidget.dart +++ b/lib/components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/components/ActivityCommentBoxWidget.dart @@ -20,13 +20,12 @@ import 'package:orginone/components/XImage/ImageWidget.dart'; import 'package:orginone/config/theme/UIConfig.dart'; import 'package:orginone/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/AtTextFiled.dart'; import 'package:orginone/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/RichTextInputFormatter.dart'; -import 'package:orginone/dart/base/model.dart' hide Column; +import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/core/chat/message.dart'; import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/utils/system/PermissionUtil.dart'; import 'package:path_provider/path_provider.dart'; diff --git a/lib/components/ActivityWidget/ActivityListWidget/components/ActivityReleaseWidget.dart b/lib/components/ActivityWidget/ActivityListWidget/components/ActivityReleaseWidget.dart index aa1a2c6b2d659314a71ca0a224bb66ca2b591092..4d2a5a3704df35a34c4442f311eed9048a56aed7 100644 --- a/lib/components/ActivityWidget/ActivityListWidget/components/ActivityReleaseWidget.dart +++ b/lib/components/ActivityWidget/ActivityListWidget/components/ActivityReleaseWidget.dart @@ -1,4 +1,3 @@ -import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:orginone/components/XStatefulWidget/XStatefulWidget.dart'; @@ -8,7 +7,6 @@ import 'package:orginone/dart/core/chat/activity.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/dart/core/thing/systemfile.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../FileWidget/FileUploadWidget/FileUploadWidget.dart'; import 'ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/components/ActivityCommentBoxWidget.dart'; diff --git a/lib/components/AppUpdate/AppUpdate.dart b/lib/components/AppUpdate/AppUpdate.dart index 73c19cae4bf3307f75e9d2696c9cf5ea48e6af32..a6fdb438b6c25c8042081be93f85f4210a894dae 100644 --- a/lib/components/AppUpdate/AppUpdate.dart +++ b/lib/components/AppUpdate/AppUpdate.dart @@ -2,15 +2,13 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_xupdate/flutter_xupdate.dart'; -import 'package:get/get.dart'; import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/config/constant.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/api/http_util.dart'; -import 'package:orginone/dart/base/model.dart' hide Column; +import 'package:orginone/dart/base/model.dart'; import 'package:orginone/env.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:orginone/utils/string_util.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/components/ChatSessionWidget/ChatSessionWidget.dart b/lib/components/ChatSessionWidget/ChatSessionWidget.dart index d5ae9b9418a49e07d4c40c9b9ffb6e50f571d8d1..507a02b2dba1b134c7fa13e7b644abed4b76a7a0 100644 --- a/lib/components/ChatSessionWidget/ChatSessionWidget.dart +++ b/lib/components/ChatSessionWidget/ChatSessionWidget.dart @@ -7,7 +7,6 @@ import 'package:orginone/components/ChatSessionWidget/components/MessageListWidg import 'package:orginone/components/EmptyWidget/EmptyChat.dart'; import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/target/base/target.dart'; -import 'package:orginone/utils/log/log_util.dart'; class ChatSessionWidget extends XStatefulWidget { ChatSessionWidget({super.key, super.data}); diff --git a/lib/components/ChatSessionWidget/components/ChatBoxWidget/ChatBoxWidget.dart b/lib/components/ChatSessionWidget/components/ChatBoxWidget/ChatBoxWidget.dart index a29dc5aa58ffeb226682928f3fd8083d972ec229..b7da3962a69ae11a03828823295f72b7d0d2e6d0 100644 --- a/lib/components/ChatSessionWidget/components/ChatBoxWidget/ChatBoxWidget.dart +++ b/lib/components/ChatSessionWidget/components/ChatBoxWidget/ChatBoxWidget.dart @@ -29,7 +29,6 @@ import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/utils/system/PermissionUtil.dart'; import 'package:path_provider/path_provider.dart'; diff --git a/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/AtPersonDialog.dart b/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/AtPersonDialog.dart index b26246a7d5f46991feb316dd87f065558bd41fa7..dc7e1772d63d1e674b24f869d6662967b8447d90 100644 --- a/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/AtPersonDialog.dart +++ b/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/AtPersonDialog.dart @@ -1,7 +1,6 @@ import 'package:azlistview_plus/azlistview_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; import 'package:orginone/components/XButton/XButton.dart'; import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/components/XTextField/XTextField.dart'; @@ -11,7 +10,6 @@ import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:pinyin/pinyin.dart'; class AtPersonDialog { diff --git a/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/RichTextInputFormatter.dart b/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/RichTextInputFormatter.dart index 12a1399145ed090301459b5d738c672c833bfb04..637820364a6d57f0ade4fe1f0d030dd02abb5a41 100755 --- a/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/RichTextInputFormatter.dart +++ b/lib/components/ChatSessionWidget/components/ChatBoxWidget/components/AtTextFiled/components/RichTextInputFormatter.dart @@ -31,11 +31,11 @@ typedef ValueChangedCallback = void Function(List rules, String value); /// it can trigger callback when you input special symbol like @person class RichTextInputFormatter extends TextInputFormatter { - TriggerAtCallback _triggerAtCallback; - ValueChangedCallback? _valueChangedCallback; + final TriggerAtCallback _triggerAtCallback; + final ValueChangedCallback? _valueChangedCallback; TextEditingController controller; - List _rules; + final List _rules; /// trigger symbol,when input this ,the [_triggerAtCallback] will be called final String triggerSymbol; @@ -50,8 +50,7 @@ class RichTextInputFormatter extends TextInputFormatter { ValueChangedCallback? valueChangedCallback, required this.controller, this.triggerSymbol = "@", - }) : assert(triggerAtCallback != null && controller != null), - _rules = [], + }) : _rules = [], _triggerAtCallback = triggerAtCallback, _valueChangedCallback = valueChangedCallback; @@ -121,10 +120,10 @@ class RichTextInputFormatter extends TextInputFormatter { if (selStart < currentText.length) { /// 如果光标是在原来字符的中间 newString = - "${currentText.substring(0, startIndex + 1)}${atNameStr} ${currentText.substring(startIndex + 1, currentText.length)}"; + "${currentText.substring(0, startIndex + 1)}$atNameStr ${currentText.substring(startIndex + 1, currentText.length)}"; } else { /// 如果光标是在字符串最后面 - newString = "$currentText${atNameStr} "; + newString = "$currentText$atNameStr "; } int endIndex = startIndex + (newString.length - currentText.length); controller.text = newString; diff --git a/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/DetailItemWidget.dart b/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/DetailItemWidget.dart index 1efb76ac8c1d31b6418c008e748ed75f7a36810f..863e27689dfe1cbfa4efbc02e0359563fd1d6ccd 100644 --- a/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/DetailItemWidget.dart +++ b/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/DetailItemWidget.dart @@ -12,7 +12,6 @@ import 'package:loading_indicator/loading_indicator.dart'; import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/components/XText/target_text.dart'; import 'package:orginone/components/ChatSessionWidget/components/ChatBoxWidget/ChatBoxWidget.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../../../../dart/base/common/commands.dart'; import './components/detail/BusinessCardDetailWidget.dart'; import './components/detail/FileDetailWidget.dart'; @@ -22,7 +21,7 @@ import './components/detail/TaskCardDetailWidget.dart'; import './components/detail/TextDetailWidget.dart'; import './components/detail/UploadingDetailWidget.dart'; import './components/detail/VoiceDetailWidget.dart'; -import 'package:orginone/dart/base/model.dart' hide Column; +import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/chat/message.dart'; import 'package:orginone/dart/core/chat/session.dart'; diff --git a/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/FileDetailWidget.dart b/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/FileDetailWidget.dart index da7aaef7928f3f3d5ee8ff87085dd9ab3e82257f..4d514fb850a89c3bbc4e43ebfc817479ec1f4a97 100644 --- a/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/FileDetailWidget.dart +++ b/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/FileDetailWidget.dart @@ -101,7 +101,7 @@ class FileDetailWidget extends BaseDetailWidget { _onTap(context); }, style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( const EdgeInsets.all(0))), child: Text(FileUtils.isWord(extension) ? "查看文档" @@ -127,7 +127,7 @@ class FileDetailWidget extends BaseDetailWidget { return TextButton( onPressed: onPressed, style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( const EdgeInsets.all(0))), child: Text(btnTxt)); }), @@ -137,7 +137,7 @@ class FileDetailWidget extends BaseDetailWidget { ToastUtils.showMsg(msg: "敬请期待!!!"); }, style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( const EdgeInsets.all(0))), child: const Text("在线编辑")), ].addDivisions(const VerticalDivider())), diff --git a/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/GroupDynamicsDetailWidget.dart b/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/GroupDynamicsDetailWidget.dart index c98aba3eb5f42f511ba36309e9f8ebde33bba2f3..8bce5f3367ad9dac4ba2b4eade314155fe6aaa4b 100644 --- a/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/GroupDynamicsDetailWidget.dart +++ b/lib/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/components/detail/GroupDynamicsDetailWidget.dart @@ -8,13 +8,11 @@ import 'package:orginone/components/XImage/ImageWidget.dart'; import 'package:orginone/config/theme/UIConfig.dart'; import 'package:orginone/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/DetailItemWidget.dart'; import 'package:orginone/dart/base/model.dart'; -import 'package:orginone/dart/base/regex/regex_utils.dart'; import 'package:orginone/dart/core/work/rules/lib/tools.dart'; import 'package:orginone/main.dart'; import 'package:orginone/utils/file_utils.dart'; import 'package:orginone/utils/string_util.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import '../../../../../../../../dart/core/chat/activity.dart'; import '../../../../../../../../dart/core/chat/session.dart'; import '../../../../../../../../dart/core/provider/session.dart'; import '../../../../../../../../routers/pages.dart'; @@ -42,9 +40,7 @@ class GroupDynamicsDetailWidget extends BaseDetailWidget { @override Widget body(BuildContext context) { - dynamic entity = null != relationCtrl.user - ? relationCtrl.user!.findMetadata(msgBody.createUser!) - : null; + dynamic entity = relationCtrl.user?.findMetadata(msgBody.createUser!); BoxConstraints boxConstraints = BoxConstraints(minWidth: 280.w, maxWidth: UIConfig.screenWidth - 110); @@ -65,7 +61,9 @@ class GroupDynamicsDetailWidget extends BaseDetailWidget { children: [ Expanded( child: Container( - padding: const EdgeInsets.only(right: 8.0,), + padding: const EdgeInsets.only( + right: 8.0, + ), child: Text( msgBody.content.trim().isEmpty ? "我发布了一条动态" @@ -100,11 +98,11 @@ class GroupDynamicsDetailWidget extends BaseDetailWidget { ), ), Container( - alignment: Alignment.centerLeft, - padding: EdgeInsets.only(top: 10.h, bottom: 12.h), - // child: RegexUtils.isURL(msgBody.linkInfo) - // ? renderLinkPreview() - // : Container() + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(top: 10.h, bottom: 12.h), + // child: RegexUtils.isURL(msgBody.linkInfo) + // ? renderLinkPreview() + // : Container() ), const Divider(), Row( @@ -197,12 +195,11 @@ class GroupDynamicsDetailWidget extends BaseDetailWidget { if (chatProvider?.chats != null) { chatsAll.addAll(chatProvider!.chats); } - ISession? iss = chatsAll.firstWhereOrNull((element) => element.id == msgBody.shareId); + ISession? iss = + chatsAll.firstWhereOrNull((element) => element.id == msgBody.shareId); if (iss != null) { RoutePages.to( - context: context, - path: Routers.targetActivity, - data: iss.activity); + context: context, path: Routers.targetActivity, data: iss.activity); } else { Navigator.push(context, MaterialPageRoute(builder: (context) { return ActivityMessageFromChatWidget( @@ -219,7 +216,6 @@ class GroupDynamicsDetailWidget extends BaseDetailWidget { // })); } - @override Widget imageWidget(url) { // TODO: implement imageWidget throw UnimplementedError(); diff --git a/lib/components/CommandWidget/index.dart b/lib/components/CommandWidget/index.dart index ace82a244d372d1572e04a835d079c8795c681e5..f41a96b4911a0da8ac9de5592085b1abc2768702 100644 --- a/lib/components/CommandWidget/index.dart +++ b/lib/components/CommandWidget/index.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:orginone/dart/base/common/commands.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:provider/provider.dart'; ///命令动态组件 diff --git a/lib/components/EmptyWidget/EmptyWidget.dart b/lib/components/EmptyWidget/EmptyWidget.dart index b8d52f45c0c2fc9f464b236858704ce55eb97ab8..3ad432070ee31283ed62366757803b47f726454c 100644 --- a/lib/components/EmptyWidget/EmptyWidget.dart +++ b/lib/components/EmptyWidget/EmptyWidget.dart @@ -1,10 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; -import 'package:orginone/components/EmptyWidget/EmptyWork.dart'; import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import 'package:orginone/dart/core/public/entity.dart'; import '../../dart/core/chat/activity.dart'; import '../../dart/core/chat/session.dart'; diff --git a/lib/components/EntityWidget/StandardEntityInfoWidget/StandardEntityInfoWidget.dart b/lib/components/EntityWidget/StandardEntityInfoWidget/StandardEntityInfoWidget.dart index c02aeccc8d754d9cafcbe569bbbc341965fb528f..2efd662ba68e77f9f491a979e82c7eacf25e0fc1 100644 --- a/lib/components/EntityWidget/StandardEntityInfoWidget/StandardEntityInfoWidget.dart +++ b/lib/components/EntityWidget/StandardEntityInfoWidget/StandardEntityInfoWidget.dart @@ -4,7 +4,6 @@ import 'package:orginone/components/XStatefulWidget/XStatefulWidget.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/thing/standard/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../XText/XText.dart'; import 'components/StandardEntityBaseInfoWidget.dart'; diff --git a/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardEntityBaseInfoWidget.dart b/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardEntityBaseInfoWidget.dart index af2ace361148a3d20088596ec7a4b05a6ef76ab6..438c55f2ff7f085860a64c961d30141b8276f337 100644 --- a/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardEntityBaseInfoWidget.dart +++ b/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardEntityBaseInfoWidget.dart @@ -8,7 +8,6 @@ import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/public/entity.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../CopyButtonWidget/CopyButtonWidget.dart'; import '../../../XText/XText.dart'; diff --git a/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardSpeciesListWidget.dart b/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardSpeciesListWidget.dart index a3bc75366c160434d1e0f0f7a8907ddcd2b4938f..fc63498fe5745f11943b2777cc155f3edbebb16f 100644 --- a/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardSpeciesListWidget.dart +++ b/lib/components/EntityWidget/StandardEntityInfoWidget/components/StandardSpeciesListWidget.dart @@ -8,7 +8,6 @@ import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/core/public/consts.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../XText/XText.dart'; diff --git a/lib/components/ExpandTabBar/ExpandTabBar.dart b/lib/components/ExpandTabBar/ExpandTabBar.dart index 3b163a02e0af833ad6b0272328dec797096e191a..2caa3d5dbe30d4be0efb669a22fd1d6bd1b54d38 100644 --- a/lib/components/ExpandTabBar/ExpandTabBar.dart +++ b/lib/components/ExpandTabBar/ExpandTabBar.dart @@ -1228,7 +1228,7 @@ class _ExpandTabBarState extends State { child: Padding( padding: adjustedPadding ?? widget.labelPadding ?? - tabBarTheme?.labelPadding ?? + tabBarTheme.labelPadding ?? kTabLabelPadding, child: KeyedSubtree( key: _tabKeys[index], diff --git a/lib/components/FileWidget/FileDownloadWidget/FileDownloadWidget.dart b/lib/components/FileWidget/FileDownloadWidget/FileDownloadWidget.dart index 8278c172c8000c80fd4a7405022d849ba988d048..43c1e7a68225b70ba348a2cc28bb43eb48e8aa46 100644 --- a/lib/components/FileWidget/FileDownloadWidget/FileDownloadWidget.dart +++ b/lib/components/FileWidget/FileDownloadWidget/FileDownloadWidget.dart @@ -8,9 +8,8 @@ import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/components/ChatSessionWidget/components/MessageListWidget/components/DetailItemWidget/DetailItemWidget.dart'; import 'package:orginone/config/constant.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import 'package:orginone/dart/base/model.dart' hide Column; +import 'package:orginone/dart/base/model.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../XButton/XButton.dart'; @@ -39,7 +38,7 @@ class _MessageFilePageState extends State { try { if (records.isNotEmpty) { task = records - .firstWhere((element) => element.task.filename == fileShare.name!) + .firstWhere((element) => element.task.filename == fileShare.name) .task as DownloadTask; downloadStatus.value = task != null ? DownloadStatus.downloadCompleted @@ -179,7 +178,7 @@ class _MessageFilePageState extends State { task = DownloadTask( url: '${Constant.host}${fileShare.shareLink}', baseDirectory: BaseDirectory.applicationSupport, - filename: fileShare.name!); + filename: fileShare.name); ToastUtils.showMsg(msg: "开始下载"); downloadStatus.value = DownloadStatus.downloading; await FileDownloader().download(task!, onProgress: (prpgress) { diff --git a/lib/components/FileWidget/FileListWidget/FileListWidget.dart b/lib/components/FileWidget/FileListWidget/FileListWidget.dart index d0292e34207fa8bcc14ada2987f408ada93041d2..50bc0960a3ea8c58f6ee18e9edd26c30379bde55 100644 --- a/lib/components/FileWidget/FileListWidget/FileListWidget.dart +++ b/lib/components/FileWidget/FileListWidget/FileListWidget.dart @@ -28,7 +28,6 @@ import 'package:orginone/dart/core/thing/standard/property.dart'; import 'package:orginone/dart/core/thing/systemfile.dart'; import 'package:orginone/routers/pages.dart'; import 'package:orginone/utils/file_utils.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../dart/base/common/commands.dart'; import '../../../routers/app_route.dart'; diff --git a/lib/components/FileWidget/FileListWidget/components/FileTaskListWidget/FileTaskListWidget.dart b/lib/components/FileWidget/FileListWidget/components/FileTaskListWidget/FileTaskListWidget.dart index 619d1ccb4eb55bef434a701bb93df3330a77ed05..9e9f1435826a5c072204b9c9fe71029479bf3918 100644 --- a/lib/components/FileWidget/FileListWidget/components/FileTaskListWidget/FileTaskListWidget.dart +++ b/lib/components/FileWidget/FileListWidget/components/FileTaskListWidget/FileTaskListWidget.dart @@ -79,7 +79,7 @@ class _FileTaskListState extends State { listTask.isEmpty ? const Expanded(child: Center(child: Text(''))) : Obx(() => Column( - children: listTask.value.reversed + children: listTask.reversed .map((item) => Container( padding: EdgeInsets.symmetric(vertical: 16.h), diff --git a/lib/components/FileWidget/FilePreview/BrowerWidget/BrowerWidget.dart b/lib/components/FileWidget/FilePreview/BrowerWidget/BrowerWidget.dart index c7d1795c8c5e73b1429dde2e11f7634bd4e3c347..fbfb76b8e867aacb20bfd3e9af7a269b6ba1c52a 100644 --- a/lib/components/FileWidget/FilePreview/BrowerWidget/BrowerWidget.dart +++ b/lib/components/FileWidget/FilePreview/BrowerWidget/BrowerWidget.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:orginone/components/XScaffold/XScaffold.dart'; import 'package:orginone/routers/pages.dart'; import 'package:webview_flutter/webview_flutter.dart' hide WebViewController; -import 'dart:convert'; import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter/webview_flutter.dart' as wb; diff --git a/lib/components/FileWidget/FilePreview/md/mark_down_preview.dart b/lib/components/FileWidget/FilePreview/md/mark_down_preview.dart index 4dec90b82aa58fcaeae1bc6794d38ad36589883b..229bf5f931ca5124cbe407118b9fc5dcb5e4baff 100644 --- a/lib/components/FileWidget/FilePreview/md/mark_down_preview.dart +++ b/lib/components/FileWidget/FilePreview/md/mark_down_preview.dart @@ -11,8 +11,8 @@ class MarkDownPreview extends XStatelessWidget { @override Widget buildWidget(BuildContext context, FileItemShare data) { - return FutureBuilder( - builder: (BuildContext context, AsyncSnapshot snapshot) { + return FutureBuilder( + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { return Container( margin: const EdgeInsets.only(top: 1), @@ -36,10 +36,10 @@ class MarkDownPreview extends XStatelessWidget { ); } - Future? loadMarkDownFile(FileItemShare data) async { + Future loadMarkDownFile(FileItemShare data) async { if (null != data.shareLink && data.shareLink!.indexOf("assets") == 0) { return await rootBundle.loadString(data.shareLink!); } - return Future.value(null); + return null; } } diff --git a/lib/components/FileWidget/FileUploadWidget/components/ResourceContainerWidget/ResourceContainerWidget.dart b/lib/components/FileWidget/FileUploadWidget/components/ResourceContainerWidget/ResourceContainerWidget.dart index bc4399ba7f9fd363d8a45ba3a3e5e96b3df8656d..29a976db4bf94d995acb4ece7e1d6e1d0071f064 100644 --- a/lib/components/FileWidget/FileUploadWidget/components/ResourceContainerWidget/ResourceContainerWidget.dart +++ b/lib/components/FileWidget/FileUploadWidget/components/ResourceContainerWidget/ResourceContainerWidget.dart @@ -12,8 +12,6 @@ import 'package:orginone/dart/core/work/rules/lib/tools.dart'; import 'package:orginone/routers/pages.dart'; import 'package:orginone/utils/file_utils.dart'; -import '../../../../../config/constant.dart'; -import '../../../../../routers/router_const.dart'; import '../../../../XImage/ImageWidget.dart'; import '../../../../XImage/components/icons.dart'; import 'components/PhotoWidget.dart'; diff --git a/lib/components/ListWidget/ListWidget.dart b/lib/components/ListWidget/ListWidget.dart index 5f36b5a3031fc72c23f8793ae6130aa03ab474a1..14ad832faf734adfb16e1d66bc580d34d6b362c9 100644 --- a/lib/components/ListWidget/ListWidget.dart +++ b/lib/components/ListWidget/ListWidget.dart @@ -67,17 +67,17 @@ class ListWidget extends StatefulWidget { this.onInitLoad, this.onLoad, this.scrollController}) { - this.scrollController = scrollController; - this.onInitLoad = onInitLoad; - this.onLoad = onLoad; - this.getTitle ??= (dynamic data) => + scrollController = scrollController; + onInitLoad = onInitLoad; + onLoad = onLoad; + getTitle ??= (dynamic data) => Text(data is IEntity || data is XEntity ? data.name : "暂无信息"); - this.getDesc ??= (dynamic data) => + getDesc ??= (dynamic data) => Text(data is IEntity || data is XEntity ? data.remark : "暂无信息"); - this.getAvatar ??= (dynamic data) { + getAvatar ??= (dynamic data) { return XImage.entityIcon(data, width: 40); }; - this.getLabels ??= + getLabels ??= (dynamic data) => data is IEntity ? data.groupTags : null; } diff --git a/lib/components/ListWidget/components/ListItemWidget.dart b/lib/components/ListWidget/components/ListItemWidget.dart index 3e23cd69f9760c5f9d189edcf5702581980e8719..e808ad1051358ed32a773c147124f3c015b43915 100644 --- a/lib/components/ListWidget/components/ListItemWidget.dart +++ b/lib/components/ListWidget/components/ListItemWidget.dart @@ -89,8 +89,8 @@ class ListItemWidget extends StatelessWidget { final ListTileThemeData defaults = theme.useMaterial3 ? _LisTileDefaultsM3(context) : _LisTileDefaultsM2(context, listTileStyle); - final Set states = { - if (!enabled) MaterialState.disabled, + final Set states = { + if (!enabled) WidgetState.disabled, }; Color? resolveColor( @@ -185,15 +185,15 @@ class ListItemWidget extends StatelessWidget { defaults.contentPadding!.resolve(textDirection); // Show basic cursor when ListTile isn't enabled or gesture callbacks are null. - final Set mouseStates = { + final Set mouseStates = { if (!enabled || (onTap == null && onLongPress == null)) - MaterialState.disabled, + WidgetState.disabled, }; final MouseCursor effectiveMouseCursor = - MaterialStateProperty.resolveAs( + WidgetStateProperty.resolveAs( mouseCursor, mouseStates) ?? tileTheme.mouseCursor?.resolve(mouseStates) ?? - MaterialStateMouseCursor.clickable.resolve(mouseStates); + WidgetStateMouseCursor.clickable.resolve(mouseStates); final ListTileTitleAlignment effectiveTitleAlignment = tileTheme.titleAlignment ?? @@ -286,7 +286,7 @@ class ListItemWidget extends StatelessWidget { } } -class _IndividualOverrides extends MaterialStateProperty { +class _IndividualOverrides extends WidgetStateProperty { _IndividualOverrides({ this.explicitColor, this.enabledColor, @@ -300,14 +300,14 @@ class _IndividualOverrides extends MaterialStateProperty { final Color? disabledColor; @override - Color? resolve(Set states) { - if (explicitColor is MaterialStateColor) { - return MaterialStateProperty.resolveAs(explicitColor, states); + Color? resolve(Set states) { + if (explicitColor is WidgetStateColor) { + return WidgetStateProperty.resolveAs(explicitColor, states); } - if (states.contains(MaterialState.disabled)) { + if (states.contains(WidgetState.disabled)) { return disabledColor; } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return selectedColor; } return enabledColor; diff --git a/lib/components/MemberListWidget/MemberListWidget.dart b/lib/components/MemberListWidget/MemberListWidget.dart index 9092ca9475b431f799325e8f3b36b7c2427a8e50..0c1dbeb1dfbeef74a79dd66249a14a90df6a54f1 100644 --- a/lib/components/MemberListWidget/MemberListWidget.dart +++ b/lib/components/MemberListWidget/MemberListWidget.dart @@ -20,7 +20,6 @@ import 'package:orginone/dart/core/target/person.dart'; import 'package:orginone/dart/core/target/team/company.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../ListSearchWidget/ListSearchWidget.dart'; import '../XButtonBar/XButtonBar.dart'; diff --git a/lib/components/MySettingWidget/components/AboutWidget/AboutWidget.dart b/lib/components/MySettingWidget/components/AboutWidget/AboutWidget.dart index d5e486a87c7c23ffbc124ed034f073a432a36f13..ce4ce7e01d16b917a80e95ab5b4f5104ebca917c 100644 --- a/lib/components/MySettingWidget/components/AboutWidget/AboutWidget.dart +++ b/lib/components/MySettingWidget/components/AboutWidget/AboutWidget.dart @@ -9,7 +9,6 @@ import 'package:package_info_plus/package_info_plus.dart'; import '../../../../config/theme/unified_style.dart'; import '../../../BeautifulBGWidget/BeautifulBGWidget.dart'; import '../../../TextKeyWidget/TextKeyWidget.dart'; -import '../../../XScaffold/XScaffold.dart'; import 'components/AboutOrginoneWidget.dart'; import 'components/VersionListWidget/VersionListWidget.dart'; diff --git a/lib/components/MySettingWidget/components/AboutWidget/components/AboutOrginoneWidget.dart b/lib/components/MySettingWidget/components/AboutWidget/components/AboutOrginoneWidget.dart index 81f743fa9ad2c2a9e4187738ff380e3ed8782ccd..e6464c9c624be18d79a226fb4d29f4459fa99c89 100644 --- a/lib/components/MySettingWidget/components/AboutWidget/components/AboutOrginoneWidget.dart +++ b/lib/components/MySettingWidget/components/AboutWidget/components/AboutOrginoneWidget.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/components/XScaffold/XScaffold.dart'; class AboutOrginoneWidget extends StatefulWidget { diff --git a/lib/components/MySettingWidget/components/AboutWidget/components/VersionListWidget/VersionListWidget.dart b/lib/components/MySettingWidget/components/AboutWidget/components/VersionListWidget/VersionListWidget.dart index f35662855feb5e07119991913202a8ddbc1f264d..77d5ddf0ed71de7ec65297043e7ed54db7edee13 100644 --- a/lib/components/MySettingWidget/components/AboutWidget/components/VersionListWidget/VersionListWidget.dart +++ b/lib/components/MySettingWidget/components/AboutWidget/components/VersionListWidget/VersionListWidget.dart @@ -73,12 +73,12 @@ class _VersionListWidgetState extends State { final remoteData = await HttpUtil().post(jsonUrl); if (remoteData['status'] != 2000) return false; - UpdateModel _updateModel = UpdateModel.fromJson((remoteData['data'])); + UpdateModel updateModel0 = UpdateModel.fromJson((remoteData['data'])); loadHistoryVersionInfo.clear(); - loadHistoryVersionInfo.add(_updateModel); - if(_updateModel.records!.isNotEmpty) { - for(UpdateRecordModel updateRecordModel in _updateModel.records!) { - UpdateModel updateModel = new UpdateModel(url: updateRecordModel.url, version: updateRecordModel.version); + loadHistoryVersionInfo.add(updateModel0); + if(updateModel0.records!.isNotEmpty) { + for(UpdateRecordModel updateRecordModel in updateModel0.records!) { + UpdateModel updateModel = UpdateModel(url: updateRecordModel.url, version: updateRecordModel.version); updateModel.title = updateRecordModel.title; updateModel.content = updateRecordModel.content; updateModel.version = updateRecordModel.version; diff --git a/lib/components/MySettingWidget/components/ErrorWidget/ErrorListWidget.dart b/lib/components/MySettingWidget/components/ErrorWidget/ErrorListWidget.dart index ba7fcbff7b485ada8c1ea2b4d03a5511d18e0011..5377a1d9acc5cbff781edb7dcc5364e57b1d472f 100644 --- a/lib/components/MySettingWidget/components/ErrorWidget/ErrorListWidget.dart +++ b/lib/components/MySettingWidget/components/ErrorWidget/ErrorListWidget.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:orginone/components/CommandWidget/index.dart'; import 'package:orginone/dart/extension/index.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/system/system_utils.dart'; import '../../../../dart/base/common/commands.dart'; import '../../../../dart/base/common/systemError.dart'; import '../../../../main.dart'; @@ -95,7 +94,7 @@ class ErrorSubPage extends StatelessWidget { return Scaffold( appBar: AppBar( title: Text(title ?? ''), - actions: [ + actions: const [ // XButton.iconText( // text: '复制', // onPressed: () => SystemUtils.copyToClipboard(errInfo), diff --git a/lib/components/MySettingWidget/components/MyCollectionWidget.dart b/lib/components/MySettingWidget/components/MyCollectionWidget.dart index fcd1e7fca4030027c6e2f216bd014ca7687d6d8a..a54c2fec9a3ed1d009ba1cc0f3b12b0510371d6c 100644 --- a/lib/components/MySettingWidget/components/MyCollectionWidget.dart +++ b/lib/components/MySettingWidget/components/MyCollectionWidget.dart @@ -1,9 +1,7 @@ -import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:orginone/components/EmptyWidget/EmptyWidget.dart'; import 'package:orginone/components/XScaffold/XScaffold.dart'; -import 'package:orginone/components/EmptyWidget/EmptyActivity.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/core/chat/activity.dart'; @@ -49,7 +47,7 @@ class _MyCollectionWidgetState extends State { color: XColors.white, child: XLazy( initDatas: listColl, - onInitLoad: () async { + onInitLoad: () async { return listColl; }, onLoad: () async { @@ -61,13 +59,13 @@ class _MyCollectionWidgetState extends State { return (listColl.isEmpty) ? EmptyWidget() : ListView.builder( - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemBuilder: (context, index) { - return buildActivityItem(listColl[index]); - }, - itemCount: listColl.length, - ); + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemBuilder: (context, index) { + return buildActivityItem(listColl[index]); + }, + itemCount: listColl.length, + ); }, ), ), diff --git a/lib/components/MySettingWidget/components/SecurityWidget.dart b/lib/components/MySettingWidget/components/SecurityWidget.dart index 7dedce6c5543f3ddf7df2de7f5eb1cfdd1e9c9f8..b7dfb0985a01292afc226d41ef285665a2e2cf02 100644 --- a/lib/components/MySettingWidget/components/SecurityWidget.dart +++ b/lib/components/MySettingWidget/components/SecurityWidget.dart @@ -19,6 +19,7 @@ class _SecurityWidgetState extends State @override Widget build(BuildContext context) { + super.build(context); return XScaffold( backgroundColor: Colors.white, titleName: '安全', diff --git a/lib/components/MySettingWidget/components/TotpWidget/components/TotpItemWidget.dart b/lib/components/MySettingWidget/components/TotpWidget/components/TotpItemWidget.dart index f1debfbe1bf8c28b6e0bffeb526fe266887ab8e9..13c7b77fe2ffb611d732387efaba78f13cfc5933 100644 --- a/lib/components/MySettingWidget/components/TotpWidget/components/TotpItemWidget.dart +++ b/lib/components/MySettingWidget/components/TotpWidget/components/TotpItemWidget.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/components/MySettingWidget/components/TotpWidget/utils/totp_helper.dart'; diff --git a/lib/components/MySettingWidget/components/TotpWidget/utils/totp_helper.dart b/lib/components/MySettingWidget/components/TotpWidget/utils/totp_helper.dart index 13c064b2ac1194e0f406c9a5d5bc7835c6d7a110..f25adcd14a777c404bfb97e2f04de4826ff0f97c 100644 --- a/lib/components/MySettingWidget/components/TotpWidget/utils/totp_helper.dart +++ b/lib/components/MySettingWidget/components/TotpWidget/utils/totp_helper.dart @@ -1,6 +1,4 @@ -import 'dart:convert'; import 'dart:math'; -import 'dart:typed_data'; import 'package:crypto/crypto.dart'; enum Algorithm { SHA1, SHA256, SHA512 } diff --git a/lib/components/PortalManagerWidget/PortalManagerWidget.dart b/lib/components/PortalManagerWidget/PortalManagerWidget.dart index 9f01be9356ead7aeaa1ceef5834bdaaca5000324..b13e0a25a4d084ffee386570336f97e30b73a998 100644 --- a/lib/components/PortalManagerWidget/PortalManagerWidget.dart +++ b/lib/components/PortalManagerWidget/PortalManagerWidget.dart @@ -51,7 +51,7 @@ class _PortalManagerState extends State { activity.label = "动态"; activity.allowEdit = false; subGroup.groups!.add(activity); - navigationItems = (relationCtrl.user!.cacheObj.toString().indexOf("portalTemplate") < 0) ? [] : relationCtrl.user!.cacheObj.getValue("portalTemplate"); + navigationItems = (!relationCtrl.user!.cacheObj.toString().contains("portalTemplate")) ? [] : relationCtrl.user!.cacheObj.getValue("portalTemplate"); if (navigationItems.isNotEmpty) { for (var value in navigationItems) { // 添加模板 diff --git a/lib/components/ProcessDetailsWidget/components/BottomActionWidget.dart b/lib/components/ProcessDetailsWidget/components/BottomActionWidget.dart index c9daec97fee280113b29dbc3c799b2d6373e645f..fbc4f90f2b7fac533d57179df1025aae58c617ea 100644 --- a/lib/components/ProcessDetailsWidget/components/BottomActionWidget.dart +++ b/lib/components/ProcessDetailsWidget/components/BottomActionWidget.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; import 'package:orginone/components/XDialogs/dialog_utils.dart'; import 'package:orginone/components/XTextField/XTextField.dart'; import 'package:orginone/dart/base/schema.dart'; @@ -16,8 +14,6 @@ import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/dart/core/work/task.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../dart/base/common/commands.dart'; import 'WorkNetWork.dart'; diff --git a/lib/components/ProcessDetailsWidget/components/ProcessInfoWidget/ProcessInfoWidget.dart b/lib/components/ProcessDetailsWidget/components/ProcessInfoWidget/ProcessInfoWidget.dart index fe5f5ac0bcdd09b01e12731c501c913617c6a358..8b66890884597ee5c910574d8333d965ae6dc376 100644 --- a/lib/components/ProcessDetailsWidget/components/ProcessInfoWidget/ProcessInfoWidget.dart +++ b/lib/components/ProcessDetailsWidget/components/ProcessInfoWidget/ProcessInfoWidget.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:orginone/dart/extension/index.dart'; @@ -10,7 +9,6 @@ import 'package:orginone/main.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/work/task.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../../dart/core/public/consts.dart'; import '../../../XTextField/XTextField.dart'; import '../../../form/form_widget/form_tool.dart'; diff --git a/lib/components/ProcessDetailsWidget/components/name_icon_view.dart b/lib/components/ProcessDetailsWidget/components/name_icon_view.dart index 3fe074a5c4a0c73785dca3bee3ef1233659ffa66..fce1ffa70c0beb9f1090810be79a67da9a470733 100644 --- a/lib/components/ProcessDetailsWidget/components/name_icon_view.dart +++ b/lib/components/ProcessDetailsWidget/components/name_icon_view.dart @@ -16,7 +16,7 @@ import '../../XText/target_text.dart'; class SelectableTargetText extends StatefulWidget { final XWorkRecord task; - SelectableTargetText({Key? key, required this.task}) : super(key: key); + const SelectableTargetText({Key? key, required this.task}) : super(key: key); @override _SelectableTargetTextState createState() => _SelectableTargetTextState(); diff --git a/lib/components/RelationWidget/RelationFriendWidget/RelationFriendWidget.dart b/lib/components/RelationWidget/RelationFriendWidget/RelationFriendWidget.dart index 73e4bda2502c1ff63fee986789c3831c78bcc438..c3964b0e9616588d9da6d7198c1e30ea1e828c63 100644 --- a/lib/components/RelationWidget/RelationFriendWidget/RelationFriendWidget.dart +++ b/lib/components/RelationWidget/RelationFriendWidget/RelationFriendWidget.dart @@ -10,7 +10,6 @@ import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../CommandWidget/index.dart'; import '../../EntityWidget/EntitySettingWidget/EntitySettingWidget.dart'; import '../../MemberListWidget/MemberListWidget.dart'; diff --git a/lib/components/ScanWidget/ScanWidget.dart b/lib/components/ScanWidget/ScanWidget.dart index 1310f53b5f09edea1f2dd7582ed2202fa295e153..864d6ad1be74c038816312827fdf7c9e24004e24 100644 --- a/lib/components/ScanWidget/ScanWidget.dart +++ b/lib/components/ScanWidget/ScanWidget.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:orginone/components/XScaffold/XScaffold.dart'; import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:scan/scan.dart'; -import '../../config/theme/unified_style.dart'; import '../../dart/base/schema.dart'; import '../../dart/core/target/base/belong.dart'; import '../../main.dart'; diff --git a/lib/components/SearchBarWidget/SearchBarWidget.dart b/lib/components/SearchBarWidget/SearchBarWidget.dart index eeda36caa26f0b33e4fc64eb83a83555e190825e..a5dc279d71460490b6fdfa06f91ca56a9a177048 100644 --- a/lib/components/SearchBarWidget/SearchBarWidget.dart +++ b/lib/components/SearchBarWidget/SearchBarWidget.dart @@ -7,7 +7,6 @@ import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/work/task.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; class SearchBarWidget extends SearchDelegate { final HomeEnum homeEnum; diff --git a/lib/components/TabContainerWidget/components/TabWidget.dart b/lib/components/TabContainerWidget/components/TabWidget.dart index 25d60b000d00325ca0eeccd0caf6bdbf629406ac..86d9d0ec5c0ff3df441b740408d4425a828ef641 100644 --- a/lib/components/TabContainerWidget/components/TabWidget.dart +++ b/lib/components/TabContainerWidget/components/TabWidget.dart @@ -13,7 +13,6 @@ import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/components/ExpandTabBar/ExpandTabBar.dart'; import 'package:orginone/components/Tip/NetworkTipWidget.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../TabContainerWidget.dart'; import '../types.dart'; diff --git a/lib/components/XButtonBar/XButtonBar.dart b/lib/components/XButtonBar/XButtonBar.dart index 976a34fe64c7ec216927deda789da8dd244bf8c6..1584f1ea1822d46465183c8cbf2cb7b7e68a6e86 100644 --- a/lib/components/XButtonBar/XButtonBar.dart +++ b/lib/components/XButtonBar/XButtonBar.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; import 'package:orginone/dart/extension/ex_widget.dart'; import '../../config/theme/unified_style.dart'; diff --git a/lib/components/XConsumer/XConsumer.dart b/lib/components/XConsumer/XConsumer.dart index 3abe67c158aad08c37dbaabc2ca8059762b04f48..93e1c7c61f337794ebb7cd39743b3490cacc03db 100644 --- a/lib/components/XConsumer/XConsumer.dart +++ b/lib/components/XConsumer/XConsumer.dart @@ -8,7 +8,7 @@ class XConsumer extends Consumer { late final String routePath; XConsumer({super.key, required super.builder}) { - this.routePath = currentRoutePath; + routePath = currentRoutePath; } @override diff --git a/lib/components/XDialogs/XDialog.dart b/lib/components/XDialogs/XDialog.dart index 4a40d26cdfded0afd61950cfcb9f38fcc40187e4..0951a16f9b0e13f591f2247a628a2e9733b9bf3b 100644 --- a/lib/components/XDialogs/XDialog.dart +++ b/lib/components/XDialogs/XDialog.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:orginone/config/theme/UIConfig.dart'; -import 'package:orginone/dart/extension/ex_string.dart'; import 'package:orginone/main.dart'; import '../../config/theme/unified_style.dart'; diff --git a/lib/components/XDialogs/dialog_utils.dart b/lib/components/XDialogs/dialog_utils.dart index 9589c630321e57faa052ae9b6c115196d2e28dcc..ff9a6674ee3d731dd0791b2a5a382d5ad9bafaa8 100644 --- a/lib/components/XDialogs/dialog_utils.dart +++ b/lib/components/XDialogs/dialog_utils.dart @@ -5,11 +5,8 @@ import 'package:orginone/components/TabContainerWidget/types.dart'; import 'package:orginone/components/XDialogs/XDialog.dart'; import 'package:orginone/components/XTextField/XTextField.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:get/get.dart'; -import 'package:orginone/routers/router_const.dart'; import '../../config/constant.dart'; import '../../routers/pages.dart'; import '../../utils/system/notify/notification_util.dart'; diff --git a/lib/components/XImage/XImage.dart b/lib/components/XImage/XImage.dart index 6e9fb7e4b92314f87a0026538dc5183c1f105175..d8d0d7a5b0deb56d7d7350f6c4a356f2ed38b13f 100644 --- a/lib/components/XImage/XImage.dart +++ b/lib/components/XImage/XImage.dart @@ -7,7 +7,7 @@ import 'package:get/get.dart'; import 'package:orginone/dart/extension/index.dart'; import 'package:orginone/config/theme/space.dart'; import 'package:orginone/config/theme/unified_style.dart'; -import 'package:orginone/dart/base/model.dart' hide Column; +import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/controller/index.dart'; import 'package:orginone/dart/core/chat/activity.dart'; @@ -20,7 +20,6 @@ import 'package:orginone/dart/core/thing/systemfile.dart'; import 'package:orginone/dart/core/work/index.dart'; import 'package:orginone/dart/core/work/task.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../routers/pages.dart'; import '../../../routers/router_const.dart'; import '../SearchBarWidget/SearchBarWidget.dart'; diff --git a/lib/components/XImage/components/icon.dart b/lib/components/XImage/components/icon.dart index 80635260edf256303190f30d0f9d7bae79b07edf..4745aa782b95457ebbbba4477b7b75e8757aece3 100644 --- a/lib/components/XImage/components/icon.dart +++ b/lib/components/XImage/components/icon.dart @@ -170,8 +170,6 @@ class IconWidget extends StatelessWidget { fit: fit ?? BoxFit.contain, ); break; - default: - return const SizedBox(); } // 圆点 diff --git a/lib/components/XStatefulWidget/XStatefulWidget.dart b/lib/components/XStatefulWidget/XStatefulWidget.dart index 83d6501d18608f0697aa03ab2a36bf89b97669da..1c1bd768a1dd09b703d575ead0a117f0ca65475d 100644 --- a/lib/components/XStatefulWidget/XStatefulWidget.dart +++ b/lib/components/XStatefulWidget/XStatefulWidget.dart @@ -9,7 +9,6 @@ import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/public/entity.dart'; import 'package:orginone/routers/app_route.dart'; import 'package:orginone/routers/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:provider/provider.dart'; import '../../dart/base/common/emitter.dart'; import '../../main.dart'; @@ -57,7 +56,7 @@ abstract class XStatefulWidget

extends StatefulWidget { XStatefulWidget({super.key, this.routeData, data}) { this.data = data ?? RoutePages.getRouteParams() ?? RoutePages.getParentRouteParam(); - this.entity = RoutePages.getParentRouteParam(); + entity = RoutePages.getParentRouteParam(); } } diff --git a/lib/components/XText/XText.dart b/lib/components/XText/XText.dart index b0dc3ff5460c5f558cb7d43fb8d151a02e99a816..2817f1f2f98788da905446a74790ddfa4575538f 100644 --- a/lib/components/XText/XText.dart +++ b/lib/components/XText/XText.dart @@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:orginone/components/XText/components/TextTag.dart'; import 'package:orginone/dart/extension/ex_list.dart'; import 'package:orginone/dart/extension/ex_widget.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../config/theme/space.dart'; import '../../config/theme/unified_style.dart'; diff --git a/lib/components/form/components/XChoice/XChoice.dart b/lib/components/form/components/XChoice/XChoice.dart index 237192706db884a5c30e4f94c85b81ee6ee3f37d..3ba3fa06367c6aea1c17671f88ed31264a6a46e9 100644 --- a/lib/components/form/components/XChoice/XChoice.dart +++ b/lib/components/form/components/XChoice/XChoice.dart @@ -16,7 +16,7 @@ class XChoice extends StatelessWidget { // 选中 取消选中按钮 const XChoice.text(this.changed, this.iconSize, - this.isSelected); + this.isSelected, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/components/form/formInfo/form_info_page.dart b/lib/components/form/formInfo/form_info_page.dart index fbe538e017b2c6f941caf6187aba97d65abbbe79..b4936bc488092cb0883abc01e28378e8b1434359 100644 --- a/lib/components/form/formInfo/form_info_page.dart +++ b/lib/components/form/formInfo/form_info_page.dart @@ -6,7 +6,6 @@ import 'package:orginone/components/form/widgets/form_attributes_view.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/thing/standard/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; // ignore: must_be_immutable class FormInfoPage extends XStatefulWidget { diff --git a/lib/components/form/form_widget/form_detail/form_detail_widget.dart b/lib/components/form/form_widget/form_detail/form_detail_widget.dart index 6494a9ac071c78b04a047aa444cd75e37779a280..173fb889d254d41486da94e81ace4530acdcd621 100644 --- a/lib/components/form/form_widget/form_detail/form_detail_widget.dart +++ b/lib/components/form/form_widget/form_detail/form_detail_widget.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart' hide Form; import 'package:orginone/dart/extension/index.dart'; -import 'package:orginone/components/form/form_widget/form_tool.dart'; import 'package:orginone/components/form/mapping_components.dart'; import 'package:orginone/dart/base/model.dart'; diff --git a/lib/components/form/form_widget/form_tool.dart b/lib/components/form/form_widget/form_tool.dart index 5324a1db8cdb981117334f9269e123fb084583be..86850be6ae75796b0ffb30384f71e6de330f3d4d 100644 --- a/lib/components/form/form_widget/form_tool.dart +++ b/lib/components/form/form_widget/form_tool.dart @@ -1,15 +1,12 @@ import 'dart:convert'; -import 'dart:ffi'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/base/storages/models/asset_creation_config.dart'; import 'package:orginone/dart/core/thing/standard/form.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../dart/core/target/base/belong.dart'; -import '../../../dart/core/target/innerTeam/department.dart'; class FormTool { static Future loadForm(dynamic parentData) async { diff --git a/lib/components/form/form_widget/sub_form/controller.dart b/lib/components/form/form_widget/sub_form/controller.dart index 703358c5fff5b3d4667b128c140d5b456cca5d9b..c7d9cefb325cbe2b25ff02950fbe992486187d29 100644 --- a/lib/components/form/form_widget/sub_form/controller.dart +++ b/lib/components/form/form_widget/sub_form/controller.dart @@ -1,6 +1,5 @@ import 'package:get/get.dart'; import 'package:orginone/dart/base/schema.dart'; -import 'package:orginone/dart/core/thing/standard/form.dart'; import 'package:orginone/routers/index.dart'; class SubFormController extends GetxController { diff --git a/lib/components/form/mapping_components.dart b/lib/components/form/mapping_components.dart index 1c41647cbf3c778759b4a0f4f1f1ad0468b2181b..bb7f455ff2c4bc6e00b55954385365d82ea8b4d0 100644 --- a/lib/components/form/mapping_components.dart +++ b/lib/components/form/mapping_components.dart @@ -125,23 +125,6 @@ MappingComponentsCallback mappingReferenceTextWidget = // textStyle: XFonts.size22Black0 ), ); - return Obx(() { - return Container( - margin: EdgeInsets.only( - left: (data.marginLeft ?? 0).h, - right: (data.marginRight ?? 0).h, - top: (data.marginTop ?? 0).h, - bottom: (data.marginBottom ?? 0).h), - child: XTextField.input( - title: data.title ?? "", - content: data.defaultData.value['text'] ?? "", - required: data.required ?? false, - enabled: !(data.readOnly ?? false), - showLine: true, - // textStyle: XFonts.size22Black0 - ), - ); - }); }; MappingComponentsCallback mappingInputWidget = (Fields data, ITarget target) { List? inputFormatters; diff --git a/lib/components/form/widgets/form_attributes_view.dart b/lib/components/form/widgets/form_attributes_view.dart index 5fd0c608a76739fc1f036d5ebef646577ceee2e0..c37e45161834614dd611a493508499f8ba73c41e 100644 --- a/lib/components/form/widgets/form_attributes_view.dart +++ b/lib/components/form/widgets/form_attributes_view.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; import 'package:orginone/dart/extension/ex_list.dart'; import 'package:orginone/dart/extension/ex_widget.dart'; import 'package:orginone/config/theme/space.dart'; @@ -23,9 +22,6 @@ class FormAttributesView extends StatelessWidget { } Widget _buildView(XForm xform) { - if (xform == null) { - return Container(); - } return [ XText.boxTitle('表单字段列表'), _attributesListView(xform), diff --git a/lib/components/form/widgets/form_datas_view.dart b/lib/components/form/widgets/form_datas_view.dart index c64d680cd041f3eba66a4276310e8f86686187df..35a15a7bea22537338bd5770f233401fa34e28a0 100644 --- a/lib/components/form/widgets/form_datas_view.dart +++ b/lib/components/form/widgets/form_datas_view.dart @@ -11,7 +11,6 @@ import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/thing/standard/form.dart'; import 'package:orginone/routers/pages.dart'; import 'package:orginone/routers/router_const.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../XButton/XButton.dart'; import '../../XText/XText.dart'; diff --git a/lib/components/share/share_center_page.dart b/lib/components/share/share_center_page.dart index f1b1433858ffd2e50569231ea4842ae7e295718e..5174a74f2395937681e792ef3c154c40f9fc5914 100644 --- a/lib/components/share/share_center_page.dart +++ b/lib/components/share/share_center_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:orginone/components/AppUpdate/AppUpdate.dart'; import 'package:orginone/components/XButton/XButton.dart'; import 'package:orginone/components/XImage/components/icon.dart'; import 'package:orginone/components/XText/XText.dart'; @@ -15,7 +14,6 @@ import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/share/index.dart'; import 'package:orginone/dart/extension/index.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; class ShareCenterPage extends StatefulWidget { const ShareCenterPage({super.key}); diff --git a/lib/config/theme/AppThemeData.dart b/lib/config/theme/AppThemeData.dart index 2e23718223225f0b65651d075d2cc4135a2f37e0..3ebc19a18836905083623d0b3dce387ea4801cf5 100644 --- a/lib/config/theme/AppThemeData.dart +++ b/lib/config/theme/AppThemeData.dart @@ -5,7 +5,7 @@ class AppThemeData { AppThemeData() { AppBarTheme appbarTheme = const AppBarTheme( - color: Colors.white, + backgroundColor: Colors.white, // backgroundColor: Colors.black, // shadowColor: Color(0xffffffff), // surfaceTintColor: Color(0xffff00ff), diff --git a/lib/dart/base/api/kernelapi.dart b/lib/dart/base/api/kernelapi.dart index 3707ccb746ce6c4b0b1f89b3ec97decbf0ce22a0..c159bc319f62f0ed6ba23ea486463aee7c1036f6 100644 --- a/lib/dart/base/api/kernelapi.dart +++ b/lib/dart/base/api/kernelapi.dart @@ -1612,7 +1612,7 @@ class KernelApi with AuthMixin { for (var m in methods) { m['operation'].call(data.data); } - } catch (e, s) { + } catch (e) { ////XLogUtil.e(s); ////XLogUtil.e(e as Error); } diff --git a/lib/dart/base/api/storehub.dart b/lib/dart/base/api/storehub.dart index 07ac0261e2f1e9a6389a0008fee1d2ae7ca014e5..269b4a9705db09084d58f0bdaf2b996e9a7f534c 100644 --- a/lib/dart/base/api/storehub.dart +++ b/lib/dart/base/api/storehub.dart @@ -7,7 +7,6 @@ import 'package:orginone/dart/base/storages/storage.dart'; import 'package:orginone/dart/core/provider/auth.dart'; import 'package:orginone/dart/core/public/consts.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:signalr_netcore/signalr_client.dart'; import 'package:uuid/uuid.dart'; @@ -721,7 +720,7 @@ class StoreHub with AuthMixin, RequestStatisticsMixin { // 'StoreHub参数:${jsonEncode(args is List && args.isNotEmpty ? args.first : args)}') //XLogUtil.e('返回结果:$resObj'); //XLogUtil.e('invoke Error${err?.toString()}${s?.toString()}'); - } catch (e, stack) { + } catch (e) { //XLogUtil.e('Http:$stack'); } return res; diff --git a/lib/dart/base/common/commands.dart b/lib/dart/base/common/commands.dart index f1df20e6cae8a569e805af889f55f1ef9b78f20a..d1fac5fa69ac7ff57ff3e7fa8eabf4ce4de95205 100644 --- a/lib/dart/base/common/commands.dart +++ b/lib/dart/base/common/commands.dart @@ -1,4 +1,3 @@ -import 'package:orginone/utils/log/log_util.dart'; import 'package:uuid/uuid.dart'; typedef CmdType = void Function(String type, String cmd, dynamic args); diff --git a/lib/dart/base/common/format.dart b/lib/dart/base/common/format.dart index 817788d95584de1472b7193d09b03eb64610b90b..b1d81e2d6f9252b78acd6bc49c3392c46c1641af 100644 --- a/lib/dart/base/common/format.dart +++ b/lib/dart/base/common/format.dart @@ -4,7 +4,6 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:get/get.dart'; -import 'package:orginone/utils/log/log_util.dart'; const sizeUnits = ['', 'KB', 'MB', 'GB', 'TB', 'PB']; diff --git a/lib/dart/base/model.dart b/lib/dart/base/model.dart index 36b856efdaef3de4a52af5c6023817a94b44cc85..72e2d8f10fef72438f43cfb14ee9deff22f3057c 100644 --- a/lib/dart/base/model.dart +++ b/lib/dart/base/model.dart @@ -3118,7 +3118,6 @@ class Branche extends XOther { Branche({super.other, this.conditions, this.children}); Branche.fromJson(Map json) : super.fromJson(json) { - conditions: json['conditions'] != null ? List.from( json['conditions'].map((x) => Condition.fromJson(x))) @@ -8675,7 +8674,6 @@ class DiskInfoType { fsTotalSize = json['fsTotalSize'] ?? 0, getTime = json['getTime']; - @override Map toJson() { return { 'file': files, diff --git a/lib/dart/base/schema.dart b/lib/dart/base/schema.dart index 80e0a6c356ae2f6890350ce97c0610266b7524d6..e81721c31c203c997a354f8e831c9a53a3a2aa94 100644 --- a/lib/dart/base/schema.dart +++ b/lib/dart/base/schema.dart @@ -8,7 +8,6 @@ import 'package:orginone/dart/base/common/lists.dart'; import 'package:orginone/dart/core/public/entity.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/dart/base/model.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'ui.dart'; @@ -3649,3 +3648,404 @@ class XReception extends XEntity { return data; } } + +// 广场资源模型 +class XPlazaResource extends XEntity { + late int index; // 顺序 + List? allowTypeNames; // 分享类型限制 + String? marketType; // 市场分类 + String? defineId; // 关联的流程定义的 PrimaryId + String? cohortId; // 平台沟通群 Id + + XPlazaResource({ + required super.id, + required super.name, + this.index = 0, + this.allowTypeNames, + this.marketType, + this.defineId, + this.cohortId, + super.code, + super.remark, + super.typeName, + }); + + XPlazaResource.fromJson(Map json) : super.fromJson(json) { + index = json['index'] ?? 0; + allowTypeNames = json['allowTypeNames'] != null + ? List.from(json['allowTypeNames']) + : null; + marketType = json['marketType']; + defineId = json['defineId']; + cohortId = json['cohortId']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['index'] = index; + if (allowTypeNames != null) { + json['allowTypeNames'] = allowTypeNames; + } + if (marketType != null) { + json['marketType'] = marketType; + } + if (defineId != null) { + json['defineId'] = defineId; + } + if (cohortId != null) { + json['cohortId'] = cohortId; + } + return json; + } +} + +// 广场模型 +class XPlaza extends XEntity { + List? resources; // 广场数据资源 + + XPlaza({ + required super.id, + required super.name, + this.resources, + super.code, + super.remark, + super.typeName, + }); + + XPlaza.fromJson(Map json) : super.fromJson(json) { + resources = json['resources'] != null + ? (json['resources'] as List) + .map((item) => XPlazaResource.fromJson(item)) + .toList() + : null; + } + + @override + Map toJson() { + final json = super.toJson(); + if (resources != null) { + json['resources'] = resources?.map((e) => e.toJson()).toList(); + } + return json; + } +} + +// 广场目标(群组分享) +class XPlazaTarget extends XEntity { + late String resourceId; // 资源 Id + late String contactId; // 联系人 Id + late String pubTime; // 发布时间 + String? sourceId; // 源数据 Id + + XPlazaTarget({ + required super.id, + required super.name, + required this.resourceId, + required this.contactId, + required this.pubTime, + this.sourceId, + super.code, + super.remark, + super.typeName, + }); + + XPlazaTarget.fromJson(Map json) : super.fromJson(json) { + resourceId = json['resourceId'] ?? ''; + contactId = json['contactId'] ?? ''; + pubTime = json['pubTime'] ?? ''; + sourceId = json['sourceId']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['resourceId'] = resourceId; + json['contactId'] = contactId; + json['pubTime'] = pubTime; + if (sourceId != null) { + json['sourceId'] = sourceId; + } + return json; + } +} + +// 广场公告 +class XPlazaNotice extends XEntity { + late String resourceId; // 资源 Id + late String contactId; // 联系人 Id + late String pubTime; // 发布时间 + String? coverImage; // 封面图片 + late String content; // 内容 + String? sourceId; // 源数据 Id + + XPlazaNotice({ + required super.id, + required super.name, + required this.resourceId, + required this.contactId, + required this.pubTime, + required this.content, + this.coverImage, + this.sourceId, + super.code, + super.remark, + super.typeName, + }); + + XPlazaNotice.fromJson(Map json) : super.fromJson(json) { + resourceId = json['resourceId'] ?? ''; + contactId = json['contactId'] ?? ''; + pubTime = json['pubTime'] ?? ''; + coverImage = json['coverImage']; + content = json['content'] ?? ''; + sourceId = json['sourceId']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['resourceId'] = resourceId; + json['contactId'] = contactId; + json['pubTime'] = pubTime; + if (coverImage != null) { + json['coverImage'] = coverImage; + } + json['content'] = content; + if (sourceId != null) { + json['sourceId'] = sourceId; + } + return json; + } +} + +// 广场视频 +class XPlazaVideo extends XEntity { + late String resourceId; // 资源 Id + late String contactId; // 联系人 Id + late String pubTime; // 发布时间 + String? videoUrl; // 视频地址 + String? thumbnail; // 缩略图 + String? sourceId; // 源数据 Id + + XPlazaVideo({ + required super.id, + required super.name, + required this.resourceId, + required this.contactId, + required this.pubTime, + this.videoUrl, + this.thumbnail, + this.sourceId, + super.code, + super.remark, + super.typeName, + }); + + XPlazaVideo.fromJson(Map json) : super.fromJson(json) { + resourceId = json['resourceId'] ?? ''; + contactId = json['contactId'] ?? ''; + pubTime = json['pubTime'] ?? ''; + videoUrl = json['videoUrl']; + thumbnail = json['thumbnail']; + sourceId = json['sourceId']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['resourceId'] = resourceId; + json['contactId'] = contactId; + json['pubTime'] = pubTime; + if (videoUrl != null) { + json['videoUrl'] = videoUrl; + } + if (thumbnail != null) { + json['thumbnail'] = thumbnail; + } + if (sourceId != null) { + json['sourceId'] = sourceId; + } + return json; + } +} + +// 广场数据分享 +class XPlazaDataShare extends XEntity { + late String resourceId; // 资源 Id + late String contactId; // 联系人 Id + late String pubTime; // 发布时间 + String? dataUrl; // 数据地址 + String? description; // 描述 + String? sourceId; // 源数据 Id + + XPlazaDataShare({ + required super.id, + required super.name, + required this.resourceId, + required this.contactId, + required this.pubTime, + this.dataUrl, + this.description, + this.sourceId, + super.code, + super.remark, + super.typeName, + }); + + XPlazaDataShare.fromJson(Map json) : super.fromJson(json) { + resourceId = json['resourceId'] ?? ''; + contactId = json['contactId'] ?? ''; + pubTime = json['pubTime'] ?? ''; + dataUrl = json['dataUrl']; + description = json['description']; + sourceId = json['sourceId']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['resourceId'] = resourceId; + json['contactId'] = contactId; + json['pubTime'] = pubTime; + if (dataUrl != null) { + json['dataUrl'] = dataUrl; + } + if (description != null) { + json['description'] = description; + } + if (sourceId != null) { + json['sourceId'] = sourceId; + } + return json; + } +} + +// 广场市场交易 +class XPlazaMarketTrade extends XEntity { + late String resourceId; // 资源 Id + late String contactId; // 联系人 Id + late String pubTime; // 发布时间 + double? price; // 价格 + String? description; // 描述 + String? sourceId; // 源数据 Id + + XPlazaMarketTrade({ + required super.id, + required super.name, + required this.resourceId, + required this.contactId, + required this.pubTime, + this.price, + this.description, + this.sourceId, + super.code, + super.remark, + super.typeName, + }); + + XPlazaMarketTrade.fromJson(Map json) : super.fromJson(json) { + resourceId = json['resourceId'] ?? ''; + contactId = json['contactId'] ?? ''; + pubTime = json['pubTime'] ?? ''; + price = json['price'] != null ? json['price'].toDouble() : null; + description = json['description']; + sourceId = json['sourceId']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['resourceId'] = resourceId; + json['contactId'] = contactId; + json['pubTime'] = pubTime; + if (price != null) { + json['price'] = price; + } + if (description != null) { + json['description'] = description; + } + if (sourceId != null) { + json['sourceId'] = sourceId; + } + return json; + } +} + +class XPlazaLive extends XEntity { + late String resourceId; // 资源 Id + late String hostId; // 主播 Id + late String hostName; // 主播名称 + late String hostAvatar; // 主播头像 + String? coverUrl; // 封面图片 + String? liveUrl; // 直播地址 + late String pubTime; // 发布时间 + late String startTime; // 开始时间 + int? viewerCount; // 观看人数 + int? likeCount; // 点赞数 + late bool isLive; // 是否正在直播 + String? description; // 描述 + + XPlazaLive({ + required super.id, + required super.name, + required this.resourceId, + required this.hostId, + required this.hostName, + required this.hostAvatar, + required this.pubTime, + required this.startTime, + this.coverUrl, + this.liveUrl, + this.viewerCount, + this.likeCount, + this.isLive = false, + this.description, + super.code, + super.remark, + super.typeName, + }); + + XPlazaLive.fromJson(Map json) : super.fromJson(json) { + resourceId = json['resourceId'] ?? ''; + hostId = json['hostId'] ?? ''; + hostName = json['hostName'] ?? ''; + hostAvatar = json['hostAvatar'] ?? ''; + coverUrl = json['coverUrl']; + liveUrl = json['liveUrl']; + pubTime = json['pubTime'] ?? ''; + startTime = json['startTime'] ?? ''; + viewerCount = json['viewerCount']; + likeCount = json['likeCount']; + isLive = json['isLive'] ?? false; + description = json['description']; + } + + @override + Map toJson() { + final json = super.toJson(); + json['resourceId'] = resourceId; + json['hostId'] = hostId; + json['hostName'] = hostName; + json['hostAvatar'] = hostAvatar; + if (coverUrl != null) { + json['coverUrl'] = coverUrl; + } + if (liveUrl != null) { + json['liveUrl'] = liveUrl; + } + json['pubTime'] = pubTime; + json['startTime'] = startTime; + if (viewerCount != null) { + json['viewerCount'] = viewerCount; + } + if (likeCount != null) { + json['likeCount'] = likeCount; + } + json['isLive'] = isLive; + if (description != null) { + json['description'] = description; + } + return json; + } +} diff --git a/lib/dart/controller/app_start_controller.dart b/lib/dart/controller/app_start_controller.dart index 6288e59357a135dbe1bff370d7bcf0cadb82f704..2379b6c967a402247374cd299e95ea8694a5ba07 100644 --- a/lib/dart/controller/app_start_controller.dart +++ b/lib/dart/controller/app_start_controller.dart @@ -8,7 +8,6 @@ import 'package:orginone/dart/controller/bootstrap_coordinator.dart'; import 'package:orginone/dart/core/provider/auth.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; ///app启动控制器 class AppStartController with EmitterMixin, AuthMixin { diff --git a/lib/dart/controller/bootstrap_coordinator.dart b/lib/dart/controller/bootstrap_coordinator.dart index 3974ca77dfb174649fcd9044d34fd472ca909195..0b59d3a2003f10340234ef7c879f9ce64ed243ab 100644 --- a/lib/dart/controller/bootstrap_coordinator.dart +++ b/lib/dart/controller/bootstrap_coordinator.dart @@ -3,7 +3,6 @@ import 'package:orginone/dart/base/storages/models/bootstrap_snapshot.dart'; import 'package:orginone/dart/base/storages/storage.dart'; import 'package:orginone/dart/core/provider/index.dart'; import 'package:orginone/dart/core/public/consts.dart'; -import 'package:orginone/main.dart'; class BootstrapCoordinator { final BootstrapSnapshotRepository repository; diff --git a/lib/dart/controller/index.dart b/lib/dart/controller/index.dart index 88a95295e98c7b18d32a96782e3f5fd013b8145a..1271f368bd30ce85c3b33d4f165388526538e600 100644 --- a/lib/dart/controller/index.dart +++ b/lib/dart/controller/index.dart @@ -29,7 +29,6 @@ import 'package:orginone/dart/core/work/box.dart'; import 'package:orginone/dart/core/work/provider.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:orginone/utils/string_util.dart'; import 'package:orginone/utils/system/PermissionUtil.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -836,7 +835,7 @@ enum HomeEnum { chat("沟通"), work("办事"), door("门户"), - store("数据"), + store("发现"), relation("关系"), setting("设置"); diff --git a/lib/dart/core/chat/message.dart b/lib/dart/core/chat/message.dart index 1ff57a4a6f3e4e72014e6dbef982d3f012adedea..500326c46d9def428a7f05584cf154fe8aaea036 100644 --- a/lib/dart/core/chat/message.dart +++ b/lib/dart/core/chat/message.dart @@ -320,9 +320,7 @@ class Message with EmitterMixin implements IMessage { default: } FileItemShare? file = parseAvatar(msgBody); - if (file != null && - (file.shareLink != null || file.name != null) && - file.size != null) { + if (file != null && file.size != null) { return '$header[$msgType]:${file.name}(${formatSize(file.size!)})'; } return '$header[$msgType]:解析异常'; diff --git a/lib/dart/core/chat/session.dart b/lib/dart/core/chat/session.dart index 40e796a33b1cb6d35b133cd0e6f025844b65017d..0932c28b01d447ac59a96f6ca72d52371d13ffa5 100644 --- a/lib/dart/core/chat/session.dart +++ b/lib/dart/core/chat/session.dart @@ -18,7 +18,6 @@ import 'package:orginone/dart/core/public/collection.dart'; import 'package:orginone/dart/core/chat/message.dart'; import 'package:orginone/dart/core/chat/activity.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:orginone/utils/system/system_utils.dart'; import '../target/team/company.dart'; diff --git a/lib/dart/core/provider/auth.dart b/lib/dart/core/provider/auth.dart index 6c957ddd1facc75478383a3b1e06c33ac5135581..e81496b5b5736a0a408ed48adc4d49b3f75bd48e 100644 --- a/lib/dart/core/provider/auth.dart +++ b/lib/dart/core/provider/auth.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/base/storages/bootstrap_snapshot_repository.dart'; diff --git a/lib/dart/core/provider/index.dart b/lib/dart/core/provider/index.dart index c6192a2bffc8c915a6c8f804736e52d00f5de055..6503f6136ec1d3b434368003a92468d8a16786b8 100644 --- a/lib/dart/core/provider/index.dart +++ b/lib/dart/core/provider/index.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:flutter/widgets.dart'; import 'package:common_utils/common_utils.dart' hide LogUtil; @@ -24,7 +23,6 @@ import 'package:orginone/dart/core/target/team/hospital.dart'; import 'package:orginone/dart/core/target/team/university.dart'; import 'package:orginone/dart/core/work/box.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../routers/pages.dart'; import '../../../routers/router_const.dart'; import '../thing/standard/application.dart'; @@ -263,25 +261,18 @@ class DataProvider with EmitterMixin, Mutex { Future _refresh() async { _inited = false; try { - // 提前拉取用户 XObject 缓存,deepLoad 创建的 Session 构造器调 cacheObj.get() - // 时直接命中内存,不再各自触发 objectGet 网络请求 await _user?.cacheObj.all(); await _user?.deepLoad(reload: true); - await work?.loadTodos(reload: true); - await _chatProvider?.load(reload: true); - // await _home?.loadConfig(); - //XLogUtil.d('>>>>>>User._refresh'); + await Future.wait([ + work?.loadTodos(reload: true) ?? Future.value(), + _chatProvider?.load(reload: true) ?? Future.value(), + ]); changeCallback(args: [true]); - // ★4: 快照写入不阻塞 _refresh 完成(changeCallback 已让 LoginTransPage - // 跳页),但仍立即触发,不引入人为延迟,保证下次冷启动能读到最新快照。 _scheduleSnapshotWrite(); } catch (e, s) { var t = DateUtil.formatDate(DateTime.now(), format: "yyyy-MM-dd HH:mm:ss.SSS"); errInfo += '$t $e ==== $s'; - // ToastUtils.showMsg(msg: errInfo); - // SystemUtils.copyToClipboard(errInfo); - //XLogUtil.e('>>>>>>User._refresh.error,$e $s'); changeCallback(args: [false]); WidgetsBinding.instance.addPostFrameCallback((_) { RoutePages.to(path: Routers.errorGuide); @@ -295,9 +286,8 @@ class DataProvider with EmitterMixin, Mutex { /// _refresh/wakeUp 会再触发),避免重复写盘和并发竞争。 void _scheduleSnapshotWrite() { if (_pendingSnapshot != null) return; - _pendingSnapshot = BootstrapCoordinator() - .writeFullSnapshot(this) - .catchError((e) { + _pendingSnapshot = + BootstrapCoordinator().writeFullSnapshot(this).catchError((e) { //XLogUtil.e('快照写入失败: $e'); }).whenComplete(() { _pendingSnapshot = null; @@ -347,7 +337,8 @@ class DataProvider with EmitterMixin, Mutex { if (selectedSpaceId.isNotEmpty) { final matchedSpace = selectedSpaceId == person.id ? person - : joinedSpaces.firstWhereOrNull((space) => space.id == selectedSpaceId); + : joinedSpaces + .firstWhereOrNull((space) => space.id == selectedSpaceId); if (matchedSpace != null) { person.currentSpace = matchedSpace; } @@ -407,24 +398,20 @@ class DataProvider with EmitterMixin, Mutex { /// 唤醒 Future wakeUp() async { - // if (_inited) { await stopWatch("数据唤醒", () async { - // await _user?.cacheObj.all(true); - bool? res = await _chatProvider?.wakeUp(); - await work?.loadTodos(reload: true); - //XLogUtil.dd('>>>>>>User.wakeUp'); + final results = await Future.wait([ + _chatProvider?.wakeUp() ?? Future.value(null), + work?.loadTodos(reload: true) ?? Future.value(), + ]); + final res = results[0] as bool?; if (null != res && !res) { ToastUtils.showMsgDev(msg: "唤醒拉去数据失败"); } else if (null == res) { ToastUtils.showMsgDev(msg: "唤醒系统异常"); } changeCallback(args: [true]); - // ★4: 同 _refresh,快照写入非阻塞 + 去重调度 _scheduleSnapshotWrite(); }); - // } else { - // SystemLog.err('唤醒失败', '系统未初始化,请先初始化系统'); - // } } Future stopWatch(String title, Future Function() callback) async { diff --git a/lib/dart/core/provider/session.dart b/lib/dart/core/provider/session.dart index 2944665b4437ff96b0c7c75d9d58ee53a4cd3dc5..ee668ad6c256b1cc2a6954a6e6a29561f43625ae 100644 --- a/lib/dart/core/provider/session.dart +++ b/lib/dart/core/provider/session.dart @@ -1,7 +1,6 @@ // 沟通接口 import 'dart:convert'; -import 'package:flutter/material.dart'; import 'package:orginone/dart/base/common/commands.dart'; import 'package:orginone/dart/base/common/emitter.dart'; import 'package:orginone/dart/base/model.dart'; diff --git a/lib/dart/core/public/collection.dart b/lib/dart/core/public/collection.dart index 9473102a30de5370d262ce12f4e4e7da7585db39..ee6a8a5b7e26e8931e56b53c79a5f5486170b481 100644 --- a/lib/dart/core/public/collection.dart +++ b/lib/dart/core/public/collection.dart @@ -3,7 +3,6 @@ import 'package:orginone/dart/base/common/map_util.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; class XCollection { late bool _loaded; @@ -33,7 +32,7 @@ class XCollection { } String subMethodName({String? id}) { - return '${this._target.belongId}-${id ?? this._target.id}-${this._collName}'; + return '${_target.belongId}-${id ?? _target.id}-$_collName'; } Future> all( @@ -42,7 +41,7 @@ class XCollection { T Function(Map)? fromJson}) async { if (!_loaded || reload) { if (skip == 0) { - this._cache = []; + _cache = []; } var params = { 'skip': skip, @@ -52,16 +51,16 @@ class XCollection { var res = await loadResult(params, fromJson); if (res.success) { if (null != res.data && res.data!.isNotEmpty) { - this._cache.addAll(res.data ?? []); + _cache.addAll(res.data ?? []); if (res.data!.length == 500) { - await this.all( - reload: true, skip: this._cache.length, fromJson: fromJson); + await all( + reload: true, skip: _cache.length, fromJson: fromJson); } } - this._loaded = true; + _loaded = true; } } - return this._cache; + return _cache; } Future> find( @@ -162,7 +161,7 @@ class XCollection { Future> loadSpaceNew(dynamic options, [T Function(Map)? cvt]) async { - var res = await this.loadResult(options); + var res = await loadResult(options); if (res.success && res.data != null) { return res.data ?? []; } @@ -206,10 +205,10 @@ class XCollection { options['options'] = options['options'] ?? {}; options['options']['match'] = options['options']['match'] ?? {}; var res = await kernel.collectionLoad>( - this._target.id, - this._target.belongId!, - this._relations, - this._collName, + _target.id, + _target.belongId!, + _relations, + _collName, options, fromJson: (data) { return Lists.fromList(data['data'] is List ? data['data'] : [], @@ -316,8 +315,8 @@ class XCollection { } Future replace(T data, {String? copyId}) async { - data.shareId = this._target.id; - data.belongId = data.belongId ?? this._target.belongId; + data.shareId = _target.id; + data.belongId = data.belongId ?? _target.belongId; var res = await kernel.collectionReplace( _target.id, _target.belongId!, @@ -327,7 +326,7 @@ class XCollection { copyId, ); if (res.success) { - if (res.data != [] && this._loaded) { + if (res.data != [] && _loaded) { _cache = _cache.map((i) { if (i.id == res.data?.id) { return res.data as T; @@ -342,10 +341,10 @@ class XCollection { Future replace2(T data, {String? copyId, T Function(Map)? cvt}) async { - data.shareId = this._target.id; - data.belongId = data.belongId ?? this._target.belongId; - data.other?['shareId'] = this._target.id; - data.other?['belongId'] = data.other?['belongId'] ?? this._target.belongId; + data.shareId = _target.id; + data.belongId = data.belongId ?? _target.belongId; + data.other?['shareId'] = _target.id; + data.other?['belongId'] = data.other?['belongId'] ?? _target.belongId; var res = await kernel.collectionReplace2( _target.id, _target.belongId!, @@ -356,7 +355,7 @@ class XCollection { cvt, ); if (res.success) { - if (res.data != [] && this._loaded) { + if (res.data != [] && _loaded) { _cache = _cache.map((i) { if (i.id == res.data?.id) { return res.data as T; @@ -379,8 +378,8 @@ class XCollection { var res = await kernel.collectionReplace>( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, data, copyId, ); @@ -395,8 +394,8 @@ class XCollection { var res = await kernel.collectionSetFields( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, { 'id': id, 'update': update, @@ -415,8 +414,8 @@ class XCollection { var res = await kernel.collectionSetFields>( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, { "ids": ids, "update": update, @@ -439,8 +438,8 @@ class XCollection { var res = await kernel.collectionUpdate( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, update, copyId, ); @@ -462,8 +461,8 @@ class XCollection { var res = await kernel.collectionUpdate( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, update, copyId, ); @@ -483,8 +482,8 @@ class XCollection { var res = await kernel.collectionUpdate( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, update, copyId, ); @@ -498,9 +497,9 @@ class XCollection { Map match = {'id': data.id}; var res = await kernel.collectionRemove( _target.id, - this._target.belongId!, - this._relations, - this._collName, + _target.belongId!, + _relations, + _collName, match, copyId, ); @@ -516,9 +515,9 @@ class XCollection { }; var res = await kernel.collectionRemove( _target.id, - this._target.belongId!, - this._relations, - this._collName, + _target.belongId!, + _relations, + _collName, match, copyId, ); @@ -532,8 +531,8 @@ class XCollection { var res = await kernel.collectionRemove( _target.id, _target.belongId!, - this._relations, - this._collName, + _relations, + _collName, match, copyId, ); @@ -544,7 +543,7 @@ class XCollection { } void removeCache(bool Function(T value) predicate) { - this._cache = this._cache.where((a) => predicate(a)).toList(); + _cache = _cache.where((a) => predicate(a)).toList(); } // Future removeCache(String id) async { // this._cache = this._cache.where((element) => element.id != id).toList(); @@ -559,13 +558,13 @@ class XCollection { }) async { DataNotityType req = DataNotityType( data: data, - flag: this.collName, + flag: collName, onlineOnly: onlineOnly, - belongId: this._target.belongId!, - relations: this._relations, + belongId: _target.belongId!, + relations: _relations, onlyTarget: onlyTarget == true, ignoreSelf: ignoreSelf == true, - targetId: targetId ?? this._target.id, + targetId: targetId ?? _target.id, subTargetId: null, ); var res = await kernel.dataNotify(req); diff --git a/lib/dart/core/public/entity.dart b/lib/dart/core/public/entity.dart index 910ef5b324dddf077254b43d34286c732a4cd992..6d378530c34d26c96688e056994510d211126589 100644 --- a/lib/dart/core/public/entity.dart +++ b/lib/dart/core/public/entity.dart @@ -35,7 +35,6 @@ abstract class IEntity with IEntityUI, EmitterMixin { //归属Id String get belongId; //共享信息 - @override ShareIcon get share; //创建人 ShareIcon get creater; @@ -67,7 +66,7 @@ abstract class Entity late List _gtags; Entity(T metadata, List gtags) : super() { - this.key = const Uuid().v1(); + key = const Uuid().v1(); _metadata = metadata; _gtags = gtags; ShareIdSet[_metadata.id] = _metadata; @@ -123,22 +122,22 @@ abstract class Entity @override ShareIcon get share { - return this.findShare(id); + return findShare(id); } @override ShareIcon get shareIcon { - return this.findShare(id); + return findShare(id); } @override ShareIcon get creater { - return this.findShare(metadata.createUser ?? ''); + return findShare(metadata.createUser ?? ''); } @override ShareIcon get updater { - return this.findShare(metadata.updateUser ?? ''); + return findShare(metadata.updateUser ?? ''); } @override @@ -162,7 +161,7 @@ abstract class Entity (metadata is XStandard && (metadata as XStandard).isDeleted)) { return ['已删除']; } - return this._gtags; + return _gtags; } void setMetadata(T metadata) { @@ -199,7 +198,7 @@ abstract class Entity } ShareIcon findShare(String id) { - var metadata = this.findMetadata(id); + var metadata = findMetadata(id); ShareIcon shareIcon = ShareIcon( name: metadata?.name ?? '', typeName: metadata?.typeName ?? '未知', diff --git a/lib/dart/core/public/objects.dart b/lib/dart/core/public/objects.dart index 89ded1b3218b5bd8cfbd40afe061527c2f88f4f3..7e565e6bfbb21809229cbc4d906609acafd2fede 100644 --- a/lib/dart/core/public/objects.dart +++ b/lib/dart/core/public/objects.dart @@ -1,11 +1,9 @@ import 'dart:async'; -import 'dart:convert'; import 'package:get/get.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; ///对象工具类 class XObject { @@ -24,19 +22,19 @@ class XObject { _objName = name; _methods = {}; kernel.subscribe( - this.subMethodName, keys, (i) => _objectCallback(Res.fromJson(i))); + subMethodName, keys, (i) => _objectCallback(Res.fromJson(i))); } dynamic get cache { - return this._cache; + return _cache; } String get objNmae { - return this._objName; + return _objName; } String get subMethodName { - return '${this._target.belongId}-${this._target.id}-${this._objName}'; + return '${_target.belongId}-${_target.id}-$_objName'; } String fullPath(String path) { @@ -45,11 +43,11 @@ class XObject { } else { path = ''; } - return this._objName + path; + return _objName + path; } Future all([bool reload = false]) async { - if (!this._loaded || reload) { + if (!_loaded || reload) { // 复用正在进行中的请求,避免多个 Session 并发触发重复 objectGet if (_loadingCompleter != null && !reload) { return await _loadingCompleter!.future; @@ -58,64 +56,64 @@ class XObject { try { var res = await load(); if (res.success) { - this._cache = res.data; - this._loaded = true; + _cache = res.data; + _loaded = true; unsubscribe(); } else { //XLogUtil.d('>>>>>>======allErr ${jsonEncode(res)}'); } - _loadingCompleter!.complete(this._cache); + _loadingCompleter!.complete(_cache); } catch (e) { _loadingCompleter!.completeError(e); } finally { _loadingCompleter = null; } } - return this._cache; + return _cache; } Future> load( {String? path, T Function(Map)? fromJson}) async { - String key = this._objName; + String key = _objName; if (null != path) { key = "$key.$path"; } - return await kernel.objectGet(this._target.id, this._target.belongId!, - this._relations, key, fromJson); + return await kernel.objectGet(_target.id, _target.belongId!, + _relations, key, fromJson); } Future get(String path, [T Function(Map)? fromJson]) async { - if (!this._loaded) { - await this.all(); + if (!_loaded) { + await all(); } var val = getValue(path); return null != fromJson && null != val && val is! T ? val is List ? fromJson({'data': val}) : fromJson(val) - : this.getValue(path); //翻译getValue后再处理 + : getValue(path); //翻译getValue后再处理 } Future set(String path, dynamic data) async { // var kernel = KernelApi(); var res = await kernel.objectSet( - this._target.id, - this._target.belongId!, - this._relations, - this.fullPath(path), + _target.id, + _target.belongId!, + _relations, + fullPath(path), { 'data': data, 'operation': 'replaceAll', }, ); if (res.success) { - if (this._cache == null) { + if (_cache == null) { // get('') 会把原始 JSON Map 隐式 cast 为 T(Xbase 子类),运行时 CastError // all() 直接把 res.data 存为 dynamic _cache,不做类型转换,安全 - await this.all(); + await all(); } - this.setValue(path, data); + setValue(path, data); } return res.success; } @@ -123,9 +121,9 @@ class XObject { Future delete(String path) async { // var kernel = KernelApi(); var res = await kernel.objectDelete( - this._target.id, - this._target.belongId ?? "", - this._relations, + _target.id, + _target.belongId ?? "", + _relations, fullPath(path), ); return res.success; @@ -140,13 +138,13 @@ class XObject { DataNotityType req = DataNotityType( data: {'flag': flag, 'data': data}, - flag: this._objName, + flag: _objName, onlineOnly: onlineOnly, - belongId: this._target.belongId!, - relations: this._relations, + belongId: _target.belongId!, + relations: _relations, onlyTarget: onlyTarget == true, ignoreSelf: ignoreSelf == true, - targetId: targetId ?? this._target.id, + targetId: targetId ?? _target.id, subTargetId: null, ); var res = await kernel.dataNotify(req); @@ -157,27 +155,27 @@ class XObject { if (flag.isEmpty) { return; } - if (!this._methods.containsKey(flag)) { - this._methods[flag] = []; + if (!_methods.containsKey(flag)) { + _methods[flag] = []; } - if (this._methods[flag]!.contains(callback)) { + if (_methods[flag]!.contains(callback)) { return; } - this._methods[flag]?.add(callback); + _methods[flag]?.add(callback); } void unsubscribe() { - this._methods.clear(); + _methods.clear(); } setValue(String path, dynamic data) { - if (this._cache != null) { + if (_cache != null) { if (path == '') { - return this.cache(); + return cache(); } var paths = path.split('.'); var prop = paths.firstOrNull; - var value = this.cache; + var value = cache; while (value != null && prop != null) { paths.removeAt(0); if (paths.isEmpty) { diff --git a/lib/dart/core/target/identity/identity.dart b/lib/dart/core/target/identity/identity.dart index 861d63185427c5fa39ab821dce90df7354f858f0..66052a5c8160d7a2a54ff11d713cc46c279203bf 100644 --- a/lib/dart/core/target/identity/identity.dart +++ b/lib/dart/core/target/identity/identity.dart @@ -185,8 +185,10 @@ class Identity extends Entity implements IIdentity { List operates({int? mode = 0}) { final List operates = []; if (mode! % 2 == 0 && current.hasRelationAuth()) { - operates.addAll( - [EntityOperates.update, FileOperates.rename] as List); + operates.addAll([ + OperateModel.fromJson(EntityOperates.update.toJson()), + OperateModel.fromJson(FileOperates.rename.toJson()) + ]); } operates.addAll(super.operates(mode: 1)); operates.sort((a, b) => (a.menus != null diff --git a/lib/dart/core/target/innerTeam/department.dart b/lib/dart/core/target/innerTeam/department.dart index 0df40773112600d59be6eff5ce1e0d54916ff0a2..10d909a36b7a3b459db50241bd72a68cf39a326e 100644 --- a/lib/dart/core/target/innerTeam/department.dart +++ b/lib/dart/core/target/innerTeam/department.dart @@ -70,7 +70,6 @@ class Department extends Target implements IDepartment { break; } } - @override List keys; @override final XTarget metadata; diff --git a/lib/dart/core/target/innerTeam/station.dart b/lib/dart/core/target/innerTeam/station.dart index f3426bdd79643898c3e497121de661612b476952..44f5c94c5af6a54efa475c65b24dd18012400654 100644 --- a/lib/dart/core/target/innerTeam/station.dart +++ b/lib/dart/core/target/innerTeam/station.dart @@ -8,7 +8,6 @@ import 'package:orginone/dart/core/target/base/team.dart'; import 'package:orginone/dart/core/target/team/company.dart'; import 'package:orginone/dart/core/thing/directory.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../person.dart'; diff --git a/lib/dart/core/target/outTeam/cohort.dart b/lib/dart/core/target/outTeam/cohort.dart index eda503ca3883160623f170d0bb8a938c41db9a4b..ab7e00c9a8f976840ebddd13f8432cca3694d237 100644 --- a/lib/dart/core/target/outTeam/cohort.dart +++ b/lib/dart/core/target/outTeam/cohort.dart @@ -26,7 +26,6 @@ class Cohort extends Target implements ICohort { XTarget metadata; @override late IBelong? space; - @override String relationId; @override diff --git a/lib/dart/core/target/outTeam/group.dart b/lib/dart/core/target/outTeam/group.dart index f4a6e8d3b4d11da3ffc4243818c43004adbc03aa..1af65d6c8939cd3932b550c30eb35ca344197d54 100644 --- a/lib/dart/core/target/outTeam/group.dart +++ b/lib/dart/core/target/outTeam/group.dart @@ -53,7 +53,6 @@ class Group extends Target implements IGroup { relations = [...relations, metadata.id]; } - @override List keys = []; @override XTarget metadata; diff --git a/lib/dart/core/target/outTeam/istorage.dart b/lib/dart/core/target/outTeam/istorage.dart index a25b76c109080541ad31adbf4c4cbd6f38d86fe8..f917ed9638e67dc5ad7589e3348278da49d3ffc2 100644 --- a/lib/dart/core/target/outTeam/istorage.dart +++ b/lib/dart/core/target/outTeam/istorage.dart @@ -40,6 +40,7 @@ class IIStorage extends Target implements IStorage { return ['存储群']; } + @override bool get isMyTeam { return true; } diff --git a/lib/dart/core/target/person.dart b/lib/dart/core/target/person.dart index dbbd8efb82e949baa82e52b861345f7cc1bac9bc..35c0e7b99fc84c0b62339cfd6af0e8d4010a6ddd 100644 --- a/lib/dart/core/target/person.dart +++ b/lib/dart/core/target/person.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:orginone/dart/base/common/commands.dart'; @@ -19,7 +18,6 @@ import 'package:orginone/dart/core/target/team/university.dart'; import 'package:orginone/dart/core/thing/fileinfo.dart'; import 'package:orginone/dart/extension/ex_list.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../base/model.dart'; import '../../base/schema.dart'; @@ -141,7 +139,6 @@ class Person extends Belong implements IPerson { late List commons; /// - @override late List itabitems; ///我的收藏 @@ -485,7 +482,6 @@ class Person extends Belong implements IPerson { } /// 深度加载数据 - @override Future deepLoad1({bool? reload = false}) async { //XLogUtil.dd('>>>>>>Person.deepLoad.start $name'); await cacheObj.all(reload!); @@ -505,15 +501,11 @@ class Person extends Belong implements IPerson { //深度加载数据 @override Future deepLoad({bool? reload = false}) async { - //XLogUtil.dd('>>>>>>Person.deepLoad.start $name'); await cacheObj.all(reload!); await Future.wait([ loadCompanys(reload: reload), loadTeams(reload: reload), - // _loadCommons(), //常用 - // _loadCollections(), //收藏 _loadBlackList(), - loadMembers(reload: reload), loadSuperAuth(reload: reload), loadGivedIdentitys(reload: reload), @@ -521,14 +513,17 @@ class Person extends Belong implements IPerson { await loadCurrentSpace(); if (currentSpace is ICompany) { - await currentSpace.deepLoad(reload: reload); + final futures = [currentSpace.deepLoad(reload: reload)]; + if (superAuth != null) { + futures.add(superAuth!.deepLoad(reload: reload)); + } + await Future.wait(futures); + } else { + if (superAuth != null) { + await superAuth!.deepLoad(reload: reload); + } } - superAuth?.deepLoad(reload: reload); changeCallback(); - // 移到HomePage加载 加载群组群成员 - // Future.wait(cohorts.map((e) => e.deepLoad(reload: reload))); - // Future.wait(storages.map((e) => e.deepLoad(reload: reload))); - //XLogUtil.dd('>>>>>>Person.deepLoad.end $name'); } ///操作类未实现 diff --git a/lib/dart/core/target/team/company.dart b/lib/dart/core/target/team/company.dart index c57287f1804b97f46b94b2ba53b2ba98c8b3b302..908a28439224b6ec8f8b44a469e8a4ce067b2693 100644 --- a/lib/dart/core/target/team/company.dart +++ b/lib/dart/core/target/team/company.dart @@ -17,7 +17,6 @@ import 'package:orginone/dart/core/target/person.dart'; import 'package:orginone/dart/core/thing/fileinfo.dart'; import 'package:orginone/dart/core/thing/standard/index_standart.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../base/common/commands.dart'; import '../../chat/activity.dart'; @@ -334,23 +333,19 @@ class Company extends Belong implements ICompany { @override Future deepLoad({bool? reload = false}) async { - //XLogUtil.dd('>>>>>>Company.deepLoad.start $name'); - // await cacheObj.all(reload!); await Future.wait([ loadGroups(reload: reload!), - // loadMembers(reload: reload), - _loadCommons(), //常用 + _loadCommons(), loadDepartments(reload: reload), loadSuperAuth(reload: reload), - // loadIdentitys(reload: reload); ]); - // directory.loadDirectoryResource(reload: reload); - await Future.wait(groups.map((e) => e.deepLoad(reload: reload))); - await Future.wait(departments.map((e) => e.deepLoad(reload: reload))); - await Future.wait(stations.map((e) => e.deepLoad(reload: reload))); - await Future.wait(cohorts.map((e) => e.deepLoad(reload: reload))); - superAuth?.deepLoad(reload: reload); - //XLogUtil.dd('>>>>>>Company.deepLoad.end $name'); + await Future.wait([ + ...groups.map((e) => e.deepLoad(reload: reload)), + ...departments.map((e) => e.deepLoad(reload: reload)), + ...stations.map((e) => e.deepLoad(reload: reload)), + ...cohorts.map((e) => e.deepLoad(reload: reload)), + superAuth?.deepLoad(reload: reload) ?? Future.value(), + ]); } // @override diff --git a/lib/dart/core/thing/directory.dart b/lib/dart/core/thing/directory.dart index 9aaba009207804a5f9f7bab40411e8b390fc40f0..8771a078c47965a8dfaab356f75dec6b2147758e 100644 --- a/lib/dart/core/thing/directory.dart +++ b/lib/dart/core/thing/directory.dart @@ -15,7 +15,6 @@ import 'package:orginone/dart/core/thing/standard/index.dart'; import 'package:orginone/dart/core/thing/standard/page.dart'; import 'package:orginone/dart/core/thing/standard/index_standart.dart'; import 'package:orginone/dart/core/thing/systemfile.dart'; -import 'package:orginone/utils/log/log_util.dart'; /// 可为空的进度回调 typedef OnProgress = void Function(double p); @@ -241,7 +240,6 @@ class Directory extends StandardFileInfo implements IDirectory { changeCallback(); } - @override List commonContent({bool? args}) { args ??= true; List cnt = []; diff --git a/lib/dart/core/thing/fileinfo.dart b/lib/dart/core/thing/fileinfo.dart index 0d2ee9b3cc8e7bae4773f33a165d0089484cf6a0..17e44636206841c01d8fe8c388a0c7dd7f71575e 100644 --- a/lib/dart/core/thing/fileinfo.dart +++ b/lib/dart/core/thing/fileinfo.dart @@ -75,7 +75,7 @@ abstract class FileInfo extends Entity T metadata, this.directory, ) : super(metadata, metadata.typeName != null ? [metadata.typeName!] : []) { - cache = XCache(fullId: '${this.spaceId}_${metadata.id}'); + cache = XCache(fullId: '${spaceId}_${metadata.id}'); Future.delayed(Duration(milliseconds: id == userId ? 100 : 0), () async { await loadUserData(); }); @@ -117,7 +117,7 @@ abstract class FileInfo extends Entity abstract String cacheFlag; @override IFileInfo get superior { - return this.directory; + return directory; } @override diff --git a/lib/dart/core/thing/resource.dart b/lib/dart/core/thing/resource.dart index 8019de59bd50ebdb55aede6892e8c0f3c2b6b3a6..f9a8b6e7b182e471e664482d189218367a2855d8 100644 --- a/lib/dart/core/thing/resource.dart +++ b/lib/dart/core/thing/resource.dart @@ -4,7 +4,6 @@ import 'package:orginone/dart/base/common/format.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/public/collection.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:uuid/uuid.dart'; import '../../../main.dart'; diff --git a/lib/dart/core/thing/standard/application.dart b/lib/dart/core/thing/standard/application.dart index 5e04302374096abe1b4055e162b242f21b5c1c1b..17ebe034fdf43c857a9c14b69b9698bf0e94cadd 100644 --- a/lib/dart/core/thing/standard/application.dart +++ b/lib/dart/core/thing/standard/application.dart @@ -4,7 +4,6 @@ import 'package:get/get.dart'; import 'package:orginone/dart/base/common/commands.dart'; import 'package:orginone/dart/base/model.dart'; import 'package:orginone/dart/base/schema.dart'; -import 'package:orginone/dart/core/public/consts.dart'; import 'package:orginone/dart/core/public/operates.dart'; import 'package:orginone/dart/core/thing/directory.dart'; import 'package:orginone/dart/core/thing/fileinfo.dart'; @@ -27,6 +26,7 @@ abstract class IApplication implements IStandardFileInfo { late final List forms; /// 是否有权限 + @override late bool isAuth; /// 结构变更 @@ -258,7 +258,7 @@ class Application extends StandardFileInfo implements IApplication ); if (result.code == 200 && result.data!.isNotEmpty) { return result.data as List; - } else if (result.code == 200 && result.data!.length == 0) { + } else if (result.code == 200 && result.data!.isEmpty) { // ToastUtils.showMsg(msg: '${relationCtrl.user..name}中未查到该资产'); } changeCallback(); diff --git a/lib/dart/core/thing/standard/form.dart b/lib/dart/core/thing/standard/form.dart index e234afda376a16226a21db09286be9cbb7663226..e9b9446c794e3f00675d686aa7698349ebd04bf7 100644 --- a/lib/dart/core/thing/standard/form.dart +++ b/lib/dart/core/thing/standard/form.dart @@ -577,7 +577,7 @@ class Form extends StandardFileInfo implements IForm { } return result; - } catch (e, stackTrace) { + } catch (e) { // print('Error in parseFilter: $e'); // print('StackTrace: $stackTrace'); return []; diff --git a/lib/dart/core/thing/standard/transfer.dart b/lib/dart/core/thing/standard/transfer.dart index 576a6094b507f8d39966a00cf1c1efa0f1e905bf..9fa207daa14692c64f13bbabcff018633c8bd4fd 100644 --- a/lib/dart/core/thing/standard/transfer.dart +++ b/lib/dart/core/thing/standard/transfer.dart @@ -11,7 +11,6 @@ import 'package:orginone/dart/core/thing/directory.dart'; import 'package:orginone/dart/core/thing/fileinfo.dart'; import 'package:orginone/dart/core/thing/standard/index.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; typedef GraphData = dynamic Function(); diff --git a/lib/dart/core/work/index.dart b/lib/dart/core/work/index.dart index 0c0ba9264a28046b9f7f83a71ab44e113d9cb897..bd7909e0aedcb296799e3c9ae75e2ea1a9aa90fd 100644 --- a/lib/dart/core/work/index.dart +++ b/lib/dart/core/work/index.dart @@ -78,7 +78,6 @@ class Work extends FileInfo implements IWork { @override final IApplication application; - @override bool isLoaded = false; @override diff --git a/lib/dart/core/work/provider.dart b/lib/dart/core/work/provider.dart index 883aa2e6c53ae1c4bd47888f1a8945c8282e78b4..c0b0c09b0c7c66736f11c67757c6765fc90f4e88 100644 --- a/lib/dart/core/work/provider.dart +++ b/lib/dart/core/work/provider.dart @@ -9,7 +9,6 @@ import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/dart/core/provider/index.dart'; import 'package:orginone/main.dart'; -import '../../base/common/commands.dart'; import 'task.dart'; /// 任务集合名 diff --git a/lib/dart/core/work/rules/base/ruleClass.dart b/lib/dart/core/work/rules/base/ruleClass.dart index 0d903185c8c641a67cd40c90f2d1b5fd0ed23de2..a803f6bfff0b0fe565cfc3501c09cb718df2b888 100644 --- a/lib/dart/core/work/rules/base/ruleClass.dart +++ b/lib/dart/core/work/rules/base/ruleClass.dart @@ -37,14 +37,6 @@ class FormulaRule extends RuleBase { getSpecialChartcterContent(targetContent) as String, $attrs) ?? ''; - if (targetKey == null) { - return { - "success": false, - "data": null, - "errMsg": '未找到:$targetContent对应特性,请检查', - }; - } - try { // 处理特殊标记「」 String resultString = await replaceString( diff --git a/lib/dart/core/work/rules/lib/index.dart b/lib/dart/core/work/rules/lib/index.dart index d044f958eda9ba8bb9ed534373e5fa0ee6aa9e29..0386f9bda6a50f89b899074c69ff2774badf90a0 100644 --- a/lib/dart/core/work/rules/lib/index.dart +++ b/lib/dart/core/work/rules/lib/index.dart @@ -38,12 +38,15 @@ String findIdByCode( String code, List attrs, ) { - var matchedAttr = attrs.singleWhere((attr) { - var Data = json.encode(attr.rule ?? '{}') as RouteModel; - return Data.code == code; - }); - - return matchedAttr != null ? matchedAttr.id : ''; + try { + var matchedAttr = attrs.singleWhere((attr) { + var Data = json.encode(attr.rule ?? '{}') as RouteModel; + return Data.code == code; + }); + return matchedAttr.id; + } catch (e) { + return ''; + } } /* 根据特性名称获取特性id */ @@ -52,8 +55,12 @@ String findIdByName( String name, List attrs, ) { - var matchedAttr = attrs.singleWhere((RouteModel attr) { - return attr.name == name; - }); - return matchedAttr != null ? matchedAttr.id : ''; + try { + var matchedAttr = attrs.singleWhere((RouteModel attr) { + return attr.name == name; + }); + return matchedAttr.id; + } catch (e) { + return ''; + } } diff --git a/lib/dart/core/work/rules/lib/tools.dart b/lib/dart/core/work/rules/lib/tools.dart index 4bbfefec21c4487086559e8ebcdb099cc4b7c341..859e5e2ec178b3b225f35172d750960a50ab1e31 100644 --- a/lib/dart/core/work/rules/lib/tools.dart +++ b/lib/dart/core/work/rules/lib/tools.dart @@ -107,9 +107,6 @@ List filterRules( }).toList(); } break; - - default: - break; } return waitTask; } @@ -179,9 +176,6 @@ var formAttrResolver = ( List formAttr, List missingAttrs, ) async { - if (ruleStr == null) { - return ''; - } var replacedStr = ruleAttrs .map((_str) => getSpecialChartcterContent(_str)) .reduce((ruleContent, item) { @@ -215,9 +209,6 @@ var formAttrResolver = ( var fixedCharacterResolver = (String ruleStr) async { // var _company = (await import('../workFormRules')).default.companyMeta; var company; - if (ruleStr == null) { - return ''; - } //一、判断是否有限定字符FixedCharacters,替换所有限定字符为对应数据值 //将FixedCharacters数组转化为一个正则表达式,匹配规则字符串中的所有固定字符,并将其替换为对应的数据值 var fixedRegex = RegExp(r"(${FixedCharacters.join('|')})"); diff --git a/lib/dart/core/work/rules/workFormRules.dart b/lib/dart/core/work/rules/workFormRules.dart index 5f492a34c80c8e8afa09ba4b10f7b4f010c8be6f..627e371c443b45580476f2ffe705e28e10878856 100644 --- a/lib/dart/core/work/rules/workFormRules.dart +++ b/lib/dart/core/work/rules/workFormRules.dart @@ -10,7 +10,6 @@ import 'package:orginone/dart/core/work/rules/base/enum.dart'; import 'package:orginone/dart/core/work/rules/lib/tools.dart'; import 'package:orginone/dart/core/work/rules/type.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; // 定义表单规则的类型 abstract class WorkFormRulesType { @@ -88,7 +87,6 @@ class WorkFormRules extends Emitter implements WorkFormRulesType { /* 收集数据 */ //'formsType' | 'hotData' | 'formCallBack' - @override collectData( String type, dynamic data, @@ -128,7 +126,6 @@ class WorkFormRules extends Emitter implements WorkFormRulesType { } //TODO: 加载远程的规则库 - @override loadRemoteRules(String path) async { // await sleep(500); //XLogUtil.d('$path暂无远程规则库'); diff --git a/lib/dart/core/work/task.dart b/lib/dart/core/work/task.dart index f2ec2c3710b5db2d1a0da3a39a717d0f06918a38..e131c635cc86146116be77e8f75ccadfd1ad988a 100644 --- a/lib/dart/core/work/task.dart +++ b/lib/dart/core/work/task.dart @@ -13,7 +13,6 @@ import 'package:orginone/dart/core/work/apply.dart'; import 'package:orginone/dart/core/work/index.dart'; import 'package:orginone/dart/core/work/instance.dart'; import 'package:orginone/main.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../thing/standard/application.dart'; diff --git a/lib/dart/core/work/work.dart b/lib/dart/core/work/work.dart index 9781c91caf0f6bbd26cbba0f0c7f0ef864c3699f..c6175de89c29016c4234ff7898374dad803dfc1f 100644 --- a/lib/dart/core/work/work.dart +++ b/lib/dart/core/work/work.dart @@ -8,8 +8,9 @@ import '../target/team/company.dart'; import 'apply.dart'; List dealGatewayFields(IWorkApply apply) { - var nodes = searchChildNodes(apply.instanceData.node!, [], [AddNodeType.GATEWAY]); - if (apply.instanceData.node?.type == AddNodeType.ROOT) { + var nodes = + searchChildNodes(apply.instanceData.node!, [], [AddNodeType.GATEWAY.nodetype]); + if (apply.instanceData.node?.type == AddNodeType.ROOT.nodetype) { nodes.addAll(loadRootGatewayNodes(apply.instanceData.node!)); } @@ -18,7 +19,8 @@ List dealGatewayFields(IWorkApply apply) { // apply.typeName == '集群模板' && (apply.metadata.taskId == '0' || apply.metadata.id != '0')) { var groups = apply.belong.targets - .where((a) => (apply.belong.space as ICompany).groups.any((s) => s.id == a.id)) + .where((a) => + (apply.belong.space as ICompany).groups.any((s) => s.id == a.id)) .toList(); if (groups.length == 1) { @@ -50,7 +52,7 @@ List dealGatewayFields(IWorkApply apply) { if (node.destId != '') { var value = apply.instanceData.primary?[node.destId]; if (value != null) { - if (node.type == AddNodeType.GATEWAY) { + if (node.type == AddNodeType.GATEWAY.nodetype) { apply.gatewayData.add(WorkGatewayInfoModel.fromJson({ 'nodeId': node.primaryId, 'targetIds': value is List ? value : [value], @@ -65,10 +67,10 @@ List dealGatewayFields(IWorkApply apply) { } } - if (node.type == AddNodeType.GATEWAY) { + if (node.type == AddNodeType.GATEWAY.nodetype) { result.add(convertToFields( XWorkNode( - id: node.primaryId??'', + id: node.primaryId ?? '', nodeType: node.type, code: node.code, name: node.name, @@ -76,9 +78,9 @@ List dealGatewayFields(IWorkApply apply) { destType: node.destType, destId: node.destId, destName: node.destName, - brotherIds: node.brotherIds??'', - branchId: node.branchId??'', - branchType: node.branchType??0, + brotherIds: node.brotherIds ?? '', + branchId: node.branchId ?? '', + branchType: node.branchType ?? 0, remark: '', ), apply.belong, @@ -86,7 +88,7 @@ List dealGatewayFields(IWorkApply apply) { } else { result.add(convertToFields( XWorkNode( - id: node.primaryId??'', + id: node.primaryId ?? '', nodeType: node.type, code: node.code, name: node.name, @@ -94,9 +96,9 @@ List dealGatewayFields(IWorkApply apply) { destType: node.destType, destId: node.destId, destName: node.destName, - brotherIds: node.brotherIds??'', - branchId: node.branchId??'', - branchType: node.branchType??0, + brotherIds: node.brotherIds ?? '', + branchId: node.branchId ?? '', + branchType: node.branchType ?? 0, remark: '', ), apply.belong, @@ -110,10 +112,10 @@ List dealGatewayFields(IWorkApply apply) { List searchChildNodes( WorkNodeModel node, List memberNodes, - List nodeTypes, + List nodeTypes, ) { // 如果当前节点的类型在目标类型列表中,则将其添加到 memberNodes 中 - if (nodeTypes.contains(node.type as AddNodeType)) { + if (node.type != null && nodeTypes.contains(node.type)) { memberNodes.add(node); } @@ -139,14 +141,14 @@ List loadRootGatewayNodes(WorkNodeModel node) { if (node.branches != null) { for (var branch in node.branches!) { if (branch.children != null && - [AddNodeType.CUSTOM, AddNodeType.Confluence].contains(branch.children!.type)) { + [AddNodeType.CUSTOM.nodetype, AddNodeType.Confluence.nodetype].contains(branch.children!.type)) { nodes.add(branch.children!); } } } // 检查子节点 else if (node.children != null && - [AddNodeType.CUSTOM, AddNodeType.Confluence].contains(node.children!.type)) { + [AddNodeType.CUSTOM.nodetype, AddNodeType.Confluence.nodetype].contains(node.children!.type)) { nodes.add(node.children!); } @@ -168,7 +170,7 @@ FieldModel convertToFields(XWorkNode node, ITarget target) { var team = target; switch (node.nodeType) { - case AddNodeType.CUSTOM: + case '自由节点': switch (node.destType) { case '角色': field.valueType = '用户型'; @@ -189,13 +191,13 @@ FieldModel convertToFields(XWorkNode node, ITarget target) { } field.valueType = '用户型'; field.widget = '多选框'; - field.options = XAttributeProps(teamId: team.id,searchEnabled: true); + field.options = XAttributeProps(teamId: team.id, searchEnabled: true); field.lookups = target.members .map((member) => FiledLookup( - id: member.id, - value: member.id, - text: member.name, - )) + id: member.id, + value: member.id, + text: member.name, + )) .toList(); if (field.lookups?.length == 1) { field.options?.defaultValue = [field.lookups![0].value]; @@ -203,7 +205,7 @@ FieldModel convertToFields(XWorkNode node, ITarget target) { break; } break; - case AddNodeType.GATEWAY: + case '网关': field.valueType = '用户型'; field.widget = '成员选择框'; field.options = XAttributeProps(teamId: team.id); @@ -211,7 +213,7 @@ FieldModel convertToFields(XWorkNode node, ITarget target) { // 处理 destId 不为空的情况 } break; - case AddNodeType.Confluence: + case '汇流': field.valueType = '用户型'; field.widget = '内部机构选择框'; field.options = XAttributeProps(teamId: team.id); diff --git a/lib/dart/extension/clip_circle.dart b/lib/dart/extension/clip_circle.dart index d56ebd70633478f2af8a081ac2255db1bedb44e5..f9214d268ece18fca0d4e8c29156f987051dc27c 100644 --- a/lib/dart/extension/clip_circle.dart +++ b/lib/dart/extension/clip_circle.dart @@ -4,15 +4,15 @@ import 'package:flutter/material.dart'; class ClipCircle extends ClipPath { ClipCircle({ - Key? key, + super.key, CustomClipper? clipper, Clip clipBehavior = Clip.antiAlias, Widget? child, }) : super( - clipper: clipper ?? _CircleClipper(), - clipBehavior: clipBehavior, - child: child, - ); + clipper: clipper ?? _CircleClipper(), + clipBehavior: clipBehavior, + child: child, + ); } class _CircleClipper extends CustomClipper { diff --git a/lib/dart/extension/ex_database.dart b/lib/dart/extension/ex_database.dart index 0070fa7dd6338f249eb2723ced740158ae560297..8aadf4249729042a89f7bf8db4eb0e8a1dc6cb01 100644 --- a/lib/dart/extension/ex_database.dart +++ b/lib/dart/extension/ex_database.dart @@ -1,6 +1,5 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:get/get.dart'; -import 'package:orginone/utils/log/log_util.dart'; /// 扩展 Database extension ExDatabase on Database { diff --git a/lib/dart/extension/ex_datetime.dart b/lib/dart/extension/ex_datetime.dart index 8d41f9fe78639fcc5c19fcb4797d85683383ea10..942a3bdd20f402201ca95c6b7970e94973581076 100644 --- a/lib/dart/extension/ex_datetime.dart +++ b/lib/dart/extension/ex_datetime.dart @@ -7,9 +7,6 @@ extension ExDateTime on DateTime { DateFormat(format).format(this); String format({String format = "yyyy-MM-dd"}) { - if (this == null) { - return ""; - } return DateFormat(format, "zh_CN").format(this); } diff --git a/lib/global.dart b/lib/global.dart index f152264899238d526764c6fcd3551e0f0543e81e..3c5e4aa8ec72b1318b58aeb950d56b57bfa7718e 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -12,10 +12,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:logging/logging.dart'; -import 'package:orginone/utils/system/notify/notification_util.dart'; import 'package:orginone/dart/base/storages/hive_utils.dart'; import 'package:orginone/dart/base/storages/storage.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'dart/base/common/systemError.dart'; import 'main.dart'; diff --git a/lib/main.dart b/lib/main.dart index 0903c6c7e271155435fdd933a762cdf50aa08dbe..14b7100f983074f59d7811b35a0c7c8a1e12b778 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,6 +17,7 @@ import 'package:orginone/dart/controller/index.dart'; import 'package:orginone/dart/core/provider/auth.dart'; import 'package:orginone/global.dart'; import 'package:orginone/pages/auth/login_transition/index.dart'; +import 'package:orginone/pages/discover/discover_provider.dart'; import 'package:orginone/routers/pages.dart'; import 'package:provider/provider.dart'; @@ -73,6 +74,7 @@ class _AppState extends State with AuthMixin { providers: [ ChangeNotifierProvider(create: (context) => AppData()), ChangeNotifierProvider(create: (context) => RoutePages.routeData), + ChangeNotifierProvider(create: (context) => DiscoverProvider()), ], child: ScreenUtilInit( designSize: screenSize, diff --git a/lib/pages/auth/forgot_password/view.dart b/lib/pages/auth/forgot_password/view.dart index 502822008e59b47ba5ee9db02a3bc88c42412d8a..169b0f46d67c33ecb368d89a8e88d45342ab541d 100644 --- a/lib/pages/auth/forgot_password/view.dart +++ b/lib/pages/auth/forgot_password/view.dart @@ -70,7 +70,7 @@ class _ForgotPasswordPageState children: [ Container( alignment: Alignment.centerLeft, - child: Text( + child: const Text( '找回密码', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w600), ), @@ -248,11 +248,8 @@ class _ForgotPasswordPageState height: 75.h, ) ]; - if (account != null) { + if (account.isNotEmpty) { accountController.text = account[0]; - } else { - widgets.add(XTextField.input( - controller: accountController, hint: "请输入用户名", title: "用户名")); } widgets.addAll([ @@ -454,8 +451,7 @@ class _ForgotPasswordPageState privateKey = false; phoneNumber = true; } - setState(() { - }); + setState(() {}); } // 获得动态验证码 diff --git a/lib/pages/auth/login/view.dart b/lib/pages/auth/login/view.dart index bbe297603c157e6d8d93233e02295e6dd4d658aa..0fd86a2c800600186570727490b1aac1413784ae 100644 --- a/lib/pages/auth/login/view.dart +++ b/lib/pages/auth/login/view.dart @@ -397,6 +397,10 @@ class _LoginPageState extends BeautifulBGStatefulState { } void login() async { + if (!agreeTerms) { + ToastUtils.showMsg(msg: "请阅读并同意服务条款与隐私条款"); + return; + } if (accountController.text.isEmpty || passWordController.text.isEmpty) { ToastUtils.showMsg(msg: "账号或密码不能为空"); return; @@ -405,10 +409,6 @@ class _LoginPageState extends BeautifulBGStatefulState { ToastUtils.showMsg(msg: "验证码不能为空"); return; } - // else if (!agreeTerms.value) { - // ToastUtils.showMsg(msg: "请阅读并同意服务条款与隐私条款"); - // return; - // } EasyLoading.show(status: '登录中...'); var res = await relationCtrl.provider.login( @@ -420,15 +420,10 @@ class _LoginPageState extends BeautifulBGStatefulState { if (res.success) { EasyLoading.dismiss(); dynamicId = ''; - // Navigator.push(context, - // MaterialPageRoute(builder: (context) => const LoginTransPage())); - await RoutePages.to(context: context, path: Routers.logintrans); - // 移除 Toast,避免与 LoginTransPage 构建期间触发的页面跳转冲突 - // LoginTransPage 会在初始化完成后自动跳转到首页,无需额外提示 - [Permission.notification].request(); Storage.setList( Constants.account, [accountController.text, passWordController.text]); - // Future.delayed(const Duration(seconds: 2)); + [Permission.notification].request(); + RoutePages.to(context: context, path: Routers.logintrans); } else if (res.code == 400 && res.msg == "认证失败,请重试。") { EasyLoading.dismiss(); ToastUtils.showMsg(msg: '账户名或密码错误'); diff --git a/lib/pages/auth/login_transition/index.dart b/lib/pages/auth/login_transition/index.dart index 594b8b4772b51f111d2f0668d5bf9caf3f1f95da..4f66be4b225f8156c40d2087b858b91ccf54c1d3 100644 --- a/lib/pages/auth/login_transition/index.dart +++ b/lib/pages/auth/login_transition/index.dart @@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../components/MySettingWidget/components/ErrorWidget/ErrorListWidget.dart'; diff --git a/lib/pages/auth/register/view.dart b/lib/pages/auth/register/view.dart index ff0b730dbbccf279ede3d08d1653df04001354a0..b19b31213551d5039581536db72fce3ef593f6f6 100644 --- a/lib/pages/auth/register/view.dart +++ b/lib/pages/auth/register/view.dart @@ -6,8 +6,6 @@ import 'package:get/get_rx/src/rx_types/rx_types.dart'; import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; import 'package:orginone/config/theme/unified_style.dart'; import 'package:orginone/dart/core/public/consts.dart'; -import 'package:orginone/routers/pages.dart'; -import 'package:orginone/routers/router_const.dart'; import '../../../components/XDialogs/dialog_utils.dart'; import '../../../components/BeautifulBGWidget/BeautifulBGWidget.dart'; import '../../../components/Tip/ToastUtils.dart'; diff --git a/lib/pages/auth/verification_code/view.dart b/lib/pages/auth/verification_code/view.dart index d83346becb1611458744657e09905aac59f7d826..48ffd62d451dd1b379736b1ca94a00790609a010 100644 --- a/lib/pages/auth/verification_code/view.dart +++ b/lib/pages/auth/verification_code/view.dart @@ -6,7 +6,7 @@ import 'package:pin_input_text_field/pin_input_text_field.dart'; class VerificationCodePage extends StatefulWidget { final String phoneNumber; - const VerificationCodePage({required this.phoneNumber}); + const VerificationCodePage({super.key, required this.phoneNumber}); @override State createState() => _VerificationCodePageState(); @@ -63,7 +63,7 @@ class _VerificationCodePageState extends State { Widget tips() { String loginType = '输入验证码'; - String tip = '验证码会发送至 +86${phoneNumber}'; + String tip = '验证码会发送至 +86$phoneNumber'; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -118,7 +118,7 @@ class _VerificationCodePageState extends State { String text = '重新获取验证码'; if (startCountDown) { textStyle = TextStyle(color: Colors.grey, fontSize: 16.sp); - text = "${countDown}秒后重新获取"; + text = "$countDown秒后重新获取"; } return GestureDetector( onTap: () { diff --git a/lib/pages/chats/chat_page.dart b/lib/pages/chats/chat_page.dart index 7d9c933193d8797303f84363f5657d6751e1c5cc..b5a64eaba79676b80652036b099be216203e40b3 100644 --- a/lib/pages/chats/chat_page.dart +++ b/lib/pages/chats/chat_page.dart @@ -8,7 +8,6 @@ import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/pages.dart'; import 'package:orginone/utils/date_util.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../components/ListSearchWidget/ListSearchWidget.dart'; import '../../components/PortalManagerWidget/portalrefresh_event.dart'; diff --git a/lib/pages/discover/discover_config.dart b/lib/pages/discover/discover_config.dart new file mode 100644 index 0000000000000000000000000000000000000000..8228943d43bcbd9784719d420e5f3a3bd7434299 --- /dev/null +++ b/lib/pages/discover/discover_config.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +enum PlazaType { + groupShare, + video, + live, + marketTrade, + dataShare, + newsAnnouncement, +} + +class DiscoverItemConfig { + final String key; + final String label; + final IconData icon; + final PlazaType plazaType; + int badge; + + DiscoverItemConfig({ + required this.key, + required this.label, + required this.icon, + required this.plazaType, + this.badge = 0, + }); +} + +final List discoverItems = [ + DiscoverItemConfig( + key: 'group_share', + label: '动态', + icon: Icons.people_outline, + plazaType: PlazaType.groupShare, + ), + DiscoverItemConfig( + key: 'video', + label: '视频', + icon: Icons.videocam_outlined, + plazaType: PlazaType.video, + ), + DiscoverItemConfig( + key: 'live', + label: '直播', + icon: Icons.live_tv_outlined, + plazaType: PlazaType.live, + ), + DiscoverItemConfig( + key: 'market_trade', + label: '交易', + icon: Icons.shopping_cart_outlined, + plazaType: PlazaType.marketTrade, + ), + DiscoverItemConfig( + key: 'data_share', + label: '分享', + icon: Icons.share_outlined, + plazaType: PlazaType.dataShare, + ), + DiscoverItemConfig( + key: 'news_announcement', + label: '公告', + icon: Icons.campaign_outlined, + plazaType: PlazaType.newsAnnouncement, + ), +]; diff --git a/lib/pages/discover/discover_item.dart b/lib/pages/discover/discover_item.dart new file mode 100644 index 0000000000000000000000000000000000000000..cd45336d53c6a89a5a8057ae33df6edbcfee2035 --- /dev/null +++ b/lib/pages/discover/discover_item.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'discover_config.dart'; + +class DiscoverItem extends StatelessWidget { + final DiscoverItemConfig config; + final int badge; + final String? displayName; + final VoidCallback? onTap; + + const DiscoverItem({ + super.key, + required this.config, + this.badge = 0, + this.displayName, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Colors.white, + border: Border( + bottom: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + ), + child: Row( + children: [ + Icon( + config.icon, + color: Colors.blue.shade700, + size: 24, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + displayName ?? config.label, + style: const TextStyle( + fontSize: 16, + color: Colors.black87, + ), + ), + ), + if (badge > 0) + Container( + margin: const EdgeInsets.only(right: 8), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(10), + ), + constraints: const BoxConstraints( + minWidth: 18, + minHeight: 18, + ), + child: Text( + badge > 99 ? '99+' : '$badge', + style: const TextStyle( + color: Colors.white, + fontSize: 11, + ), + textAlign: TextAlign.center, + ), + ), + const Icon( + Icons.chevron_right, + color: Colors.grey, + size: 20, + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/discover/discover_page.dart b/lib/pages/discover/discover_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..df67f799dff14f947a2e86d5ad4fd8bab6b0ed84 --- /dev/null +++ b/lib/pages/discover/discover_page.dart @@ -0,0 +1,286 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'discover_config.dart'; +import 'discover_item.dart'; +import 'discover_provider.dart'; +import 'discover_service.dart'; +import 'pages/group_share_list_page.dart'; +import 'pages/video_list_page.dart'; +import 'pages/notice_list_page.dart'; +import 'pages/market_list_page.dart'; +import 'pages/data_share_list_page.dart'; +import 'pages/live_list_page.dart'; + +class DiscoverPage extends StatefulWidget { + const DiscoverPage({super.key}); + + @override + State createState() => _DiscoverPageState(); +} + +class _DiscoverPageState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().loadDiscoverData(); + }); + } + + void _handleResourceTap(DiscoverItemConfig config, PlazaResource resource) { + String resourceId = resource.id; + String title = resource.name; + + Widget page; + switch (config.plazaType) { + case PlazaType.groupShare: + page = GroupShareListPage(resourceId: resourceId, title: title); + break; + case PlazaType.video: + page = VideoListPage(resourceId: resourceId, title: title); + break; + case PlazaType.live: + page = LiveListPage(resourceId: resourceId, title: title); + break; + case PlazaType.marketTrade: + page = MarketListPage(resourceId: resourceId, title: title); + break; + case PlazaType.dataShare: + page = DataShareListPage(resourceId: resourceId, title: title); + break; + case PlazaType.newsAnnouncement: + page = NoticeListPage(resourceId: resourceId, title: title); + break; + } + + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => page, + settings: RouteSettings(name: '/discover/${config.key}'), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.grey.shade100, + child: Consumer( + builder: (context, provider, child) { + if (provider.isLoading && provider.badgeCounts.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + if (provider.errorMessage != null) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + provider.errorMessage!, + style: const TextStyle(color: Colors.red), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => provider.loadDiscoverData(), + child: const Text('重试'), + ), + ], + ), + ); + } + + return RefreshIndicator( + onRefresh: () => provider.refreshData(), + child: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: discoverItems.length, + itemBuilder: (context, index) { + final config = discoverItems[index]; + final badge = provider.getBadgeCount(config.plazaType); + final resources = + provider.findResourcesByType(config.plazaType); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 分类标题 + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 12), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: _getTypeColor(config.plazaType) + .withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + config.icon, + color: _getTypeColor(config.plazaType), + size: 24, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + config.label, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + ), + if (badge > 0) + Container( + margin: const EdgeInsets.only(right: 8), + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.red.shade600, + borderRadius: BorderRadius.circular(10), + ), + constraints: const BoxConstraints( + minWidth: 20, + minHeight: 20, + ), + child: Text( + badge > 99 ? '99+' : badge.toString(), + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + + // 二级列表 + if (resources.isNotEmpty) + Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: ListView.separated( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: resources.length, + separatorBuilder: (context, index) => Divider( + height: 1, + color: Colors.grey.shade100, + indent: 56, + ), + itemBuilder: (context, subIndex) { + final resource = resources[subIndex]; + return InkWell( + onTap: () => _handleResourceTap(config, resource), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 12), + child: Row( + children: [ + Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: _getTypeColor(config.plazaType) + .withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + config.icon, + color: _getTypeColor(config.plazaType), + size: 18, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + resource.name, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Icon( + Icons.chevron_right_rounded, + color: Colors.grey.shade400, + size: 20, + ), + ], + ), + ), + ); + }, + ), + ) + else + Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + ), + child: Center( + child: Text( + '暂无内容', + style: TextStyle( + color: Colors.grey.shade500, + fontSize: 14, + ), + ), + ), + ), + + if (index < discoverItems.length - 1) + const SizedBox(height: 8), + ], + ); + }, + ), + ); + }, + ), + ); + } + + Color _getTypeColor(PlazaType type) { + switch (type) { + case PlazaType.groupShare: + return Colors.blue.shade600; + case PlazaType.video: + return Colors.red.shade600; + case PlazaType.live: + return Colors.purple.shade600; + case PlazaType.marketTrade: + return Colors.green.shade600; + case PlazaType.dataShare: + return Colors.indigo.shade600; + case PlazaType.newsAnnouncement: + return Colors.orange.shade600; + } + } +} diff --git a/lib/pages/discover/discover_provider.dart b/lib/pages/discover/discover_provider.dart new file mode 100644 index 0000000000000000000000000000000000000000..fd95512879cb9145505ebae6fcb0558e1c32e10d --- /dev/null +++ b/lib/pages/discover/discover_provider.dart @@ -0,0 +1,120 @@ +import 'package:flutter/foundation.dart'; +import 'package:orginone/dart/base/schema.dart'; +import 'discover_config.dart'; +import 'discover_service.dart'; + +class DiscoverProvider with ChangeNotifier { + Map _badgeCounts = {}; + List _resources = []; + XPlaza? _plaza; + bool _isLoading = false; + String? _errorMessage; + + Map get badgeCounts => Map.unmodifiable(_badgeCounts); + List get resources => List.unmodifiable(_resources); + XPlaza? get plaza => _plaza; + bool get isLoading => _isLoading; + String? get errorMessage => _errorMessage; + + Future loadDiscoverData({bool forceRefresh = false}) async { + if (_isLoading) return; + + _setLoading(true); + _clearError(); + + try { + // 首先加载plaza信息(这会同时缓存resources) + _plaza = await DiscoverService.loadPlaza(groupId: '', forceRefresh: forceRefresh); + + // 然后加载resources(会使用缓存) + _resources = await DiscoverService.loadPlazaResources('', forceRefresh: forceRefresh); + + // 最后加载badge counts,并传入已加载的resources,避免重复请求 + _badgeCounts = await DiscoverService.loadBadgeCounts(resources: _resources); + + notifyListeners(); + debugPrint('发现数据加载完成:${_resources.length} 个资源'); + } catch (e) { + _setError('加载数据失败: $e'); + debugPrint('DiscoverProvider 加载错误: $e'); + } finally { + _setLoading(false); + } + } + + Future refreshData() async { + DiscoverService.clearCache(); + await loadDiscoverData(forceRefresh: true); + } + + List findResourcesByType(PlazaType type) { + String typeName = ''; + switch (type) { + case PlazaType.groupShare: + typeName = '群组'; + break; + case PlazaType.video: + typeName = '视频'; + break; + case PlazaType.live: + typeName = '直播'; + break; + case PlazaType.marketTrade: + typeName = '市场'; + break; + case PlazaType.dataShare: + typeName = '数据'; + break; + case PlazaType.newsAnnouncement: + typeName = '公告'; + break; + } + + return _resources.where((r) => r.typeName == typeName).toList(); + } + + PlazaResource? findResourceByType(PlazaType type) { + final resources = findResourcesByType(type); + if (resources.isNotEmpty) { + return resources.first; + } + return null; + } + + int getBadgeCount(PlazaType type) { + return _badgeCounts[type] ?? 0; + } + + void updateBadgeCount(PlazaType type, int count) { + _badgeCounts[type] = count; + notifyListeners(); + } + + void clearCache() { + _badgeCounts.clear(); + _resources.clear(); + _plaza = null; + _clearError(); + notifyListeners(); + // 同时清理DiscoverService中的静态缓存 + DiscoverService.clearCache(); + } + + void _setLoading(bool value) { + if (_isLoading != value) { + _isLoading = value; + notifyListeners(); + } + } + + void _setError(String message) { + _errorMessage = message; + notifyListeners(); + } + + void _clearError() { + if (_errorMessage != null) { + _errorMessage = null; + } + } +} diff --git a/lib/pages/discover/discover_service.dart b/lib/pages/discover/discover_service.dart new file mode 100644 index 0000000000000000000000000000000000000000..337394ad60a255be58076f2900d5a40c752ec186 --- /dev/null +++ b/lib/pages/discover/discover_service.dart @@ -0,0 +1,838 @@ +import 'package:flutter/foundation.dart'; +import 'package:orginone/main.dart'; +import 'package:orginone/dart/base/schema.dart'; +import 'discover_config.dart'; + +class PlazaResource { + final String id; + final String name; + final String typeName; + final int index; + final bool isPublic; + + PlazaResource({ + required this.id, + required this.name, + required this.typeName, + this.index = 0, + this.isPublic = false, + }); + + factory PlazaResource.fromJson(Map json) { + return PlazaResource( + id: json['id'] ?? '', + name: json['name'] ?? '', + typeName: json['typeName'] ?? '', + index: json['index'] ?? 0, + isPublic: json['public'] ?? false, + ); + } +} + +class PlazaTarget { + final String id; + final String resourceId; + final String sourceId; + final String name; + final String typeName; + final String pubTime; + + PlazaTarget({ + required this.id, + required this.resourceId, + required this.sourceId, + required this.name, + this.typeName = '', + required this.pubTime, + }); + + factory PlazaTarget.fromJson(Map json) { + return PlazaTarget( + id: json['id'] ?? '', + resourceId: json['resourceId'] ?? '', + sourceId: json['sourceId'] ?? '', + name: json['name'] ?? '', + typeName: json['typeName'] ?? '', + pubTime: json['pubTime'] ?? '', + ); + } +} + +class PlazaNotice { + final String id; + final String resourceId; + final String contactId; + final String pubTime; + final String name; + final String? coverImage; + final String content; + final String belongId; + final String code; + final String? remark; + + PlazaNotice({ + required this.id, + required this.resourceId, + required this.contactId, + required this.pubTime, + required this.name, + this.coverImage, + required this.content, + required this.belongId, + required this.code, + this.remark, + }); + + factory PlazaNotice.fromJson(Map json) { + return PlazaNotice( + id: json['id'] ?? '', + resourceId: json['resourceId'] ?? '', + contactId: json['contactId'] ?? '', + pubTime: json['pubTime'] ?? '', + name: json['name'] ?? '', + coverImage: json['coverImage'], + content: json['content'] ?? '', + belongId: json['belongId'] ?? '', + code: json['code'] ?? '', + remark: json['remark'], + ); + } +} + +class PlazaVideo { + final String id; + final String resourceId; + final String sourceId; + final String name; + final String typeName; + final String? thumbnailUrl; + final String? videoUrl; + final int duration; + final String createTime; + + PlazaVideo({ + required this.id, + required this.resourceId, + required this.sourceId, + required this.name, + this.typeName = '', + this.thumbnailUrl, + this.videoUrl, + this.duration = 0, + required this.createTime, + }); + + factory PlazaVideo.fromJson(Map json) { + return PlazaVideo( + id: json['id'] ?? '', + resourceId: json['resourceId'] ?? '', + sourceId: json['sourceId'] ?? '', + name: json['name'] ?? '', + typeName: json['typeName'] ?? '', + thumbnailUrl: json['thumbnailUrl'], + videoUrl: json['videoUrl'], + duration: json['duration'] ?? 0, + createTime: json['createTime'] ?? json['pubTime'] ?? '', + ); + } +} + +class PlazaMarketGoods { + final String id; + final String resourceId; + final String shareKey; + final String plazaId; + final String name; + final String typeName; + final double orgPrice; + final double price; + final int amount; + final List? imageUrls; + final String? description; + final bool isOnSale; + final String createTime; + + PlazaMarketGoods({ + required this.id, + required this.resourceId, + this.shareKey = '', + this.plazaId = '', + required this.name, + this.typeName = '', + this.orgPrice = 0, + this.price = 0, + this.amount = 0, + this.imageUrls, + this.description, + this.isOnSale = true, + required this.createTime, + }); + + factory PlazaMarketGoods.fromJson(Map json) { + return PlazaMarketGoods( + id: json['id'] ?? '', + resourceId: json['resourceId'] ?? '', + shareKey: json['shareKey'] ?? '', + plazaId: json['plazaId'] ?? '', + name: json['name'] ?? '', + typeName: json['typeName'] ?? '', + orgPrice: (json['orgPrice'] ?? 0).toDouble(), + price: (json['price'] ?? 0).toDouble(), + amount: json['amount'] ?? 0, + imageUrls: json['imageUrls'] != null + ? List.from(json['imageUrls']) + : null, + description: json['description'] ?? json['detail'], + isOnSale: json['isOnSale'] ?? json['isDeleted'] != true, + createTime: json['createTime'] ?? json['pubTime'] ?? '', + ); + } +} + +class PlazaDataShare { + final String id; + final String resourceId; + final String sourceId; + final String shareKey; + final String contactId; + final String pubTime; + final String name; + final String typeName; + + PlazaDataShare({ + required this.id, + required this.resourceId, + required this.sourceId, + this.shareKey = '', + this.contactId = '', + required this.pubTime, + required this.name, + this.typeName = '', + }); + + factory PlazaDataShare.fromJson(Map json) { + return PlazaDataShare( + id: json['id'] ?? '', + resourceId: json['resourceId'] ?? '', + sourceId: json['sourceId'] ?? '', + shareKey: json['shareKey'] ?? '', + contactId: json['contactId'] ?? '', + pubTime: json['pubTime'] ?? '', + name: json['name'] ?? '', + typeName: json['typeName'] ?? '', + ); + } +} + +class PlazaLive { + final String id; + final String resourceId; + final String hostId; + final String hostName; + final String hostAvatar; + final String? coverUrl; + final String? liveUrl; + final String pubTime; + final String startTime; + final int? viewerCount; + final int? likeCount; + final bool isLive; + final String? description; + final String name; + final String typeName; + + PlazaLive({ + required this.id, + required this.resourceId, + required this.hostId, + required this.hostName, + required this.hostAvatar, + required this.pubTime, + required this.startTime, + this.coverUrl, + this.liveUrl, + this.viewerCount, + this.likeCount, + this.isLive = false, + this.description, + required this.name, + this.typeName = '', + }); + + factory PlazaLive.fromJson(Map json) { + return PlazaLive( + id: json['id'] ?? '', + resourceId: json['resourceId'] ?? '', + hostId: json['hostId'] ?? '', + hostName: json['hostName'] ?? '', + hostAvatar: json['hostAvatar'] ?? '', + coverUrl: json['coverUrl'], + liveUrl: json['liveUrl'], + pubTime: json['pubTime'] ?? '', + startTime: json['startTime'] ?? '', + viewerCount: json['viewerCount'], + likeCount: json['likeCount'], + isLive: json['isLive'] ?? false, + description: json['description'], + name: json['name'] ?? '', + typeName: json['typeName'] ?? '', + ); + } +} + +class DiscoverService { + static const String _collPlazaInfo = 'pr-plaza-info'; + static const String _collPlazaTarget = 'pr-plaza-target'; + static const String _collPlazaData = 'pr-plaza-data'; + static const String _collPlazaVideo = 'pr-plaza-video'; + static const String _collPlazaNotice = 'pr-plaza-notice'; + static const String _collPlazaMarketGoods = 'pr-plaza-market-goods'; + static const String _collPlazaLive = 'pr-plaza-live'; + + // 基于空间ID的缓存结构 + static final Map> _spaceCaches = {}; + static const Duration _cacheDuration = Duration(minutes: 10); + + // 当前空间的缓存(快速访问) + static XPlaza? get _cachedPlaza { + final currentSpaceId = relationCtrl.user?.currentSpace.id; + if (currentSpaceId != null) { + return _spaceCaches[currentSpaceId]?['plaza'] as XPlaza?; + } + return null; + } + + static String? get _cachedShareId { + final currentSpaceId = relationCtrl.user?.currentSpace.id; + if (currentSpaceId != null) { + return _spaceCaches[currentSpaceId]?['shareId'] as String?; + } + return null; + } + + static String? get _cachedBelongId { + final currentSpaceId = relationCtrl.user?.currentSpace.id; + if (currentSpaceId != null) { + return _spaceCaches[currentSpaceId]?['belongId'] as String?; + } + return null; + } + + static List? get _cachedResources { + final currentSpaceId = relationCtrl.user?.currentSpace.id; + if (currentSpaceId != null) { + return _spaceCaches[currentSpaceId]?['resources'] as List?; + } + return null; + } + + static DateTime? _getLastLoadTime(String spaceId) { + return _spaceCaches[spaceId]?['loadTime'] as DateTime?; + } + + /// 加载广场信息,同时获取并缓存 resources + static Future loadPlaza( + {String groupId = '', bool forceRefresh = false}) async { + try { + String targetId; + String belongId; + String spaceId; + + if (groupId.isNotEmpty) { + targetId = groupId; + belongId = groupId; + spaceId = groupId; + } else { + targetId = relationCtrl.user?.currentSpace.id ?? ''; + belongId = relationCtrl.user?.currentSpace.belongId ?? ''; + spaceId = relationCtrl.user?.currentSpace.id ?? ''; + } + + if (targetId.isEmpty || spaceId.isEmpty) { + return null; + } + + // 检查缓存是否有效 + if (!forceRefresh && + _spaceCaches.containsKey(spaceId) && + _getLastLoadTime(spaceId) != null && + DateTime.now().difference(_getLastLoadTime(spaceId)!) < + _cacheDuration) { + debugPrint('使用缓存的广场信息 (空间ID: $spaceId)'); + return _cachedPlaza; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaInfo, + { + 'options': { + 'match': {'shareId': targetId} + }, + }, + ); + + if (res.success && res.data != null && res.data is List) { + var data = res.data as List; + if (data.isNotEmpty) { + var plazaData = data.first as Map; + var plaza = XPlaza.fromJson(plazaData); + + // 解析 resources + List resources = []; + var resourcesData = plazaData['resources']; + if (resourcesData != null && resourcesData is List) { + resources = (resourcesData) + .map((e) => PlazaResource.fromJson(e as Map)) + .toList(); + } + + // 基于空间ID缓存 + _spaceCaches[spaceId] = { + 'plaza': plaza, + 'shareId': plaza.shareId, + 'belongId': plaza.belongId, + 'resources': resources, + 'loadTime': DateTime.now(), + }; + debugPrint('广场信息已加载并缓存 (空间ID: $spaceId)'); + + return plaza; + } + } + + return null; + } catch (e) { + debugPrint('加载广场信息失败: $e'); + return null; + } + } + + /// 获取缓存的shareId + static String? get shareId => + _cachedShareId ?? relationCtrl.user?.currentSpace.id; + + /// 获取缓存的belongId + static String? get belongId => + _cachedBelongId ?? relationCtrl.user?.currentSpace.belongId; + + static Future> loadPlazaResources(String groupId, + {bool forceRefresh = false}) async { + try { + // 优先使用缓存 + if (!forceRefresh && _cachedResources != null) { + debugPrint('使用缓存的广场资源'); + return _cachedResources!; + } + + // 如果没有缓存,先加载广场信息(会同时缓存 resources) + await loadPlaza(groupId: groupId, forceRefresh: forceRefresh); + return _cachedResources ?? []; + } catch (e) { + debugPrint('加载广场资源失败: $e'); + return []; + } + } + + static Future> loadGroupShares( + String resourceId, { + int page = 1, + int pageSize = 20, + String filter = '', + }) async { + try { + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + Map match = { + 'resourceId': resourceId, + 'isDeleted': false, + }; + + if (filter.isNotEmpty) { + match['name'] = {'_regex_': filter}; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaTarget, + { + 'options': { + 'match': match, + 'sort': {'_id': -1}, + }, + 'skip': (page - 1) * pageSize, + 'take': pageSize, + }, + ); + + if (res.success && res.data != null && res.data is List) { + return (res.data as List) + .map((e) => PlazaTarget.fromJson(e as Map)) + .toList(); + } + + return []; + } catch (e) { + debugPrint('加载动态失败: $e'); + return []; + } + } + + static Future> loadVideos( + String resourceId, { + int page = 1, + int pageSize = 20, + String filter = '', + }) async { + try { + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + Map match = { + 'resourceId': resourceId, + 'isDeleted': false, + }; + + if (filter.isNotEmpty) { + match['name'] = {'_regex_': filter}; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaVideo, + { + 'options': { + 'match': match, + 'sort': {'_id': -1}, + }, + 'skip': (page - 1) * pageSize, + 'take': pageSize, + }, + ); + + if (res.success && res.data != null && res.data is List) { + return (res.data as List) + .map((e) => PlazaVideo.fromJson(e as Map)) + .toList(); + } + + return []; + } catch (e) { + debugPrint('加载视频失败: $e'); + return []; + } + } + + static Future> loadNotices( + String resourceId, { + int page = 1, + int pageSize = 20, + String filter = '', + }) async { + try { + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + Map match = { + 'resourceId': resourceId, + 'isDeleted': false, + }; + + if (filter.isNotEmpty) { + match['name'] = {'_regex_': filter}; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaNotice, + { + 'options': { + 'match': match, + 'sort': {'_id': -1}, + }, + 'skip': (page - 1) * pageSize, + 'take': pageSize, + }, + ); + + if (res.success && res.data != null && res.data is List) { + return (res.data as List) + .map((e) => PlazaNotice.fromJson(e as Map)) + .toList(); + } + + return []; + } catch (e) { + debugPrint('加载公告失败: $e'); + return []; + } + } + + static Future> loadMarketGoods( + String resourceId, { + int page = 1, + int pageSize = 20, + String filter = '', + String? typeName, + }) async { + try { + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + Map match = { + 'resourceId': resourceId, + 'isDeleted': false, + }; + + if (typeName != null && typeName.isNotEmpty) { + match['typeName'] = typeName; + } + + if (filter.isNotEmpty) { + match['name'] = {'_regex_': filter}; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaMarketGoods, + { + 'options': { + 'match': match, + 'sort': {'_id': -1}, + }, + 'skip': (page - 1) * pageSize, + 'take': pageSize, + }, + ); + + if (res.success && res.data != null && res.data is List) { + return (res.data as List) + .map((e) => PlazaMarketGoods.fromJson(e as Map)) + .toList(); + } + + return []; + } catch (e) { + debugPrint('加载市场商品失败: $e'); + return []; + } + } + + static Future> loadDataShares( + String resourceId, { + int page = 1, + int pageSize = 20, + String filter = '', + }) async { + try { + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + Map match = { + 'resourceId': resourceId, + 'isDeleted': false, + }; + + if (filter.isNotEmpty) { + match['name'] = {'_regex_': filter}; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaData, + { + 'options': { + 'match': match, + 'sort': {'_id': -1}, + }, + 'skip': (page - 1) * pageSize, + 'take': pageSize, + }, + ); + + if (res.success && res.data != null && res.data is List) { + return (res.data as List) + .map((e) => PlazaDataShare.fromJson(e as Map)) + .toList(); + } + + return []; + } catch (e) { + debugPrint('加载数据分享失败: $e'); + return []; + } + } + + static Future> loadLives( + String resourceId, { + int page = 1, + int pageSize = 20, + String filter = '', + }) async { + try { + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + Map match = { + 'resourceId': resourceId, + 'isDeleted': false, + }; + + if (filter.isNotEmpty) { + match['name'] = {'_regex_': filter}; + } + + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + _collPlazaLive, + { + 'options': { + 'match': match, + 'sort': {'_id': -1}, + }, + 'skip': (page - 1) * pageSize, + 'take': pageSize, + }, + ); + + if (res.success && res.data != null && res.data is List) { + return (res.data as List) + .map((e) => PlazaLive.fromJson(e as Map)) + .toList(); + } + + return []; + } catch (e) { + debugPrint('加载直播失败: $e'); + return []; + } + } + + static Future> loadBadgeCounts( + {List? resources}) async { + try { + var plazaResources = resources ?? await loadPlazaResources(''); + Map badgeCounts = {}; + + // 先按类型分组,对同一类型的多个资源合并计数 + Map> resourcesByType = {}; + for (var resource in plazaResources) { + PlazaType? type = _mapTypeNameToPlazaType(resource.typeName); + if (type != null) { + resourcesByType.putIfAbsent(type, () => []).add(resource); + } + } + + // 对每个类型,获取所有该类型资源的总数 + for (var entry in resourcesByType.entries) { + PlazaType type = entry.key; + List typeResources = entry.value; + + String collName = _getCollNameByType(type); + if (collName.isEmpty) continue; + + var targetId = shareId ?? ''; + var belongId = DiscoverService.belongId ?? ''; + + int totalCount = 0; + + // 对每个资源进行计数 + for (var resource in typeResources) { + var res = await kernel.collectionLoad>( + targetId, + belongId, + [], + collName, + { + 'options': { + 'match': { + 'resourceId': resource.id, + 'isDeleted': false, + }, + 'sort': {'_id': -1}, + }, + 'take': 100, + }, + ); + + if (res.success && res.data != null && res.data is List) { + totalCount += (res.data as List).length; + } + } + + badgeCounts[type] = totalCount; + } + + return badgeCounts; + } catch (e) { + debugPrint('加载角标数量失败: $e'); + return {}; + } + } + + static PlazaType? _mapTypeNameToPlazaType(String typeName) { + switch (typeName) { + case '群组': + return PlazaType.groupShare; + case '数据': + return PlazaType.dataShare; + case '视频': + return PlazaType.video; + case '公告': + return PlazaType.newsAnnouncement; + case '市场': + return PlazaType.marketTrade; + case '直播': + return PlazaType.live; + default: + return null; + } + } + + static String _getCollNameByType(PlazaType type) { + switch (type) { + case PlazaType.groupShare: + return _collPlazaTarget; + case PlazaType.video: + return _collPlazaVideo; + case PlazaType.newsAnnouncement: + return _collPlazaNotice; + case PlazaType.marketTrade: + return _collPlazaMarketGoods; + case PlazaType.dataShare: + return _collPlazaData; + case PlazaType.live: + return _collPlazaLive; + } + return ''; + } + + /// 清除缓存,强制刷新 + /// [spaceId] - 可选,指定空间ID,不指定则清除所有缓存 + static void clearCache({String? spaceId}) { + if (spaceId != null) { + // 清除特定空间的缓存 + _spaceCaches.remove(spaceId); + debugPrint('广场缓存已清除 (空间ID: $spaceId)'); + } else { + // 清除所有空间的缓存 + _spaceCaches.clear(); + debugPrint('所有广场缓存已清除'); + } + } +} diff --git a/lib/pages/discover/pages/data_share_list_page.dart b/lib/pages/discover/pages/data_share_list_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..c7ea66fa804af55be166b84160eea1e0fb2eeb34 --- /dev/null +++ b/lib/pages/discover/pages/data_share_list_page.dart @@ -0,0 +1,576 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:orginone/components/LoadingWidget/LoadingWidget.dart'; +import '../discover_service.dart'; + +class DataShareListPage extends StatefulWidget { + final String resourceId; + final String title; + + const DataShareListPage({ + super.key, + required this.resourceId, + this.title = '数据分享', + }); + + @override + State createState() => _DataShareListPageState(); +} + +class _DataShareListPageState extends State { + List _items = []; + bool _isLoading = true; + int _currentPage = 1; + final int _pageSize = 20; + bool _hasMore = true; + String _searchText = ''; + Timer? _searchDebounce; + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + void dispose() { + _searchDebounce?.cancel(); + super.dispose(); + } + + Future _loadData({bool refresh = false}) async { + if (refresh) { + setState(() { + _currentPage = 1; + _hasMore = true; + _isLoading = true; + }); + } + + try { + // 确保plaza信息已加载 + await DiscoverService.loadPlaza(groupId: ''); + + var data = await DiscoverService.loadDataShares( + widget.resourceId, + page: _currentPage, + pageSize: _pageSize, + filter: _searchText, + ); + + setState(() { + if (refresh) { + _items = data; + } else { + _items.addAll(data); + } + _isLoading = false; + _hasMore = data.length >= _pageSize; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + void _loadMore() { + if (!_hasMore || _isLoading) return; + setState(() { + _currentPage++; + }); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + backgroundColor: Colors.purple.shade700, + foregroundColor: Colors.white, + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + decoration: InputDecoration( + hintText: '搜索数据...', + prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), + filled: true, + fillColor: Colors.grey.shade100, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + onChanged: (value) { + setState(() { + _searchText = value; + }); + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 500), () { + _loadData(refresh: true); + }); + }, + onSubmitted: (_) => _loadData(refresh: true), + ), + ), + Expanded( + child: _isLoading && _items.isEmpty + ? const LoadingWidget() + : RefreshIndicator( + onRefresh: () => _loadData(refresh: true), + child: NotificationListener( + onNotification: (notification) { + if (notification is ScrollEndNotification && + notification.metrics.pixels >= + notification.metrics.maxScrollExtent - 200) { + _loadMore(); + } + return false; + }, + child: ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + itemCount: _hasMore ? _items.length + 1 : _items.length, + itemBuilder: (context, index) { + if (index == _items.length) { + return const Padding( + padding: EdgeInsets.all(16.0), + child: Center(child: CircularProgressIndicator()), + ); + } + + var item = _items[index]; + return _buildItemCard(item); + }, + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildItemCard(PlazaDataShare item) { + IconData iconData; + Color iconColor; + + switch (item.typeName.toLowerCase()) { + case 'file': + case '文件': + iconData = Icons.insert_drive_file_rounded; + iconColor = Colors.blue.shade700; + break; + case 'folder': + case '目录': + iconData = Icons.folder_rounded; + iconColor = Colors.orange.shade600; + break; + case 'image': + case '图片': + iconData = Icons.image_rounded; + iconColor = Colors.green.shade600; + break; + case 'video': + case '视频': + iconData = Icons.videocam_rounded; + iconColor = Colors.red.shade500; + break; + case 'document': + case '文档': + iconData = Icons.description_rounded; + iconColor = Colors.indigo.shade600; + break; + default: + iconData = Icons.share_rounded; + iconColor = Colors.purple.shade600; + } + + return Container( + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: iconColor.withOpacity(0.08), + blurRadius: 20, + offset: const Offset(0, 4), + ), + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: InkWell( + onTap: () => _showDataDetail(item), + onLongPress: () => _showOptions(item), + borderRadius: BorderRadius.circular(20), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16), + child: Row( + children: [ + Container( + width: 58, + height: 58, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + iconColor.withOpacity(0.8), + iconColor, + ], + ), + borderRadius: BorderRadius.circular(18), + boxShadow: [ + BoxShadow( + color: iconColor.withOpacity(0.3), + blurRadius: 12, + offset: const Offset(0, 4), + ), + ], + ), + child: Icon(iconData, color: Colors.white, size: 30), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Color(0xFF1A1A2E), + letterSpacing: -0.2, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 10), + Row( + children: [ + Icon( + Icons.access_time_rounded, + size: 14, + color: Colors.grey.shade400, + ), + const SizedBox(width: 5), + Expanded( + child: Text( + _formatTime(item.pubTime), + style: const TextStyle( + fontSize: 12, + color: Color(0xFF6C757D), + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 10), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: iconColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + item.typeName.isNotEmpty ? item.typeName : '未知', + style: TextStyle( + fontSize: 11, + color: iconColor, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + ), + ), + const SizedBox(width: 8), + Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: iconColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.chevron_right_rounded, + color: iconColor, + size: 20, + ), + ), + ], + ), + ), + ), + ); + } + + String _formatTime(String timeStr) { + if (timeStr.isEmpty) return ''; + try { + DateTime dateTime = DateTime.parse(timeStr); + Duration diff = DateTime.now().difference(dateTime); + if (diff.inDays > 365) { + return '${(diff.inDays / 365).floor()}年前'; + } else if (diff.inDays > 30) { + return '${(diff.inDays / 30).floor()}个月前'; + } else if (diff.inDays > 0) { + return '${diff.inDays}天前'; + } else if (diff.inHours > 0) { + return '${diff.inHours}小时前'; + } else if (diff.inMinutes > 0) { + return '${diff.inMinutes}分钟前'; + } else { + return '刚刚'; + } + } catch (e) { + return timeStr.substring(0, timeStr.length > 16 ? 16 : timeStr.length); + } + } + + void _showOptions(PlazaDataShare item) { + showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) { + return SafeArea( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: const Icon(Icons.open_in_new, color: Colors.blue), + title: const Text('打开'), + onTap: () { + Navigator.pop(context); + _showDataDetail(item); + }, + ), + ListTile( + leading: const Icon(Icons.download, color: Colors.green), + title: const Text('下载'), + onTap: () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('开始下载 ${item.name}')), + ); + }, + ), + ListTile( + leading: const Icon(Icons.share, color: Colors.orange), + title: const Text('转发'), + onTap: () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('转发 ${item.name}')), + ); + }, + ), + ListTile( + leading: const Icon(Icons.info_outline, color: Colors.grey), + title: const Text('详情'), + onTap: () { + Navigator.pop(context); + _showDataDetail(item); + }, + ), + ], + ), + ), + ); + }, + ); + } + + void _showDataDetail(PlazaDataShare item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.8, + minChildSize: 0.5, + maxChildSize: 0.95, + expand: false, + builder: (context, scrollController) { + IconData iconData; + Color iconColor; + + switch (item.typeName.toLowerCase()) { + case 'file': + case '文件': + iconData = Icons.insert_drive_file; + iconColor = Colors.blue.shade700; + break; + case 'folder': + case '目录': + iconData = Icons.folder; + iconColor = Colors.orange.shade600; + break; + case 'image': + case '图片': + iconData = Icons.image; + iconColor = Colors.green.shade600; + break; + case 'video': + case '视频': + iconData = Icons.videocam; + iconColor = Colors.red.shade500; + break; + case 'document': + case '文档': + iconData = Icons.description; + iconColor = Colors.indigo.shade600; + break; + default: + iconData = Icons.share; + iconColor = Colors.purple.shade600; + } + + return Container( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + Container( + width: 40, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(2), + ), + ), + CircleAvatar( + radius: 40, + backgroundColor: iconColor.withOpacity(0.1), + child: Icon(iconData, size: 50, color: iconColor), + ), + const SizedBox(height: 16), + Text( + item.name, + style: const TextStyle( + fontSize: 22, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Text( + item.typeName.isNotEmpty ? item.typeName : '未知类型', + style: TextStyle(fontSize: 14, color: Colors.grey.shade600), + ), + const Divider(height: 24), + Expanded( + child: SingleChildScrollView( + controller: scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoRow('文件名', item.name), + _buildInfoRow('类型', + item.typeName.isNotEmpty ? item.typeName : '未知'), + _buildInfoRow('发布时间', + item.pubTime.isNotEmpty ? item.pubTime : '暂无'), + _buildInfoRow('ID', item.id), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildActionIcon( + Icons.download, '下载', Colors.green, () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在下载 ${item.name}...')), + ); + }), + _buildActionIcon(Icons.share, '分享', Colors.orange, + () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在分享 ${item.name}...')), + ); + }), + _buildActionIcon( + Icons.info_outline, '详情', Colors.blue, () {}), + ], + ), + ], + ), + ), + ), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton.icon( + onPressed: () => Navigator.pop(context), + icon: const Icon(Icons.close), + label: const Text('关闭'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey.shade300, + foregroundColor: Colors.black87, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25)), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ); + } + + Widget _buildInfoRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 80, + child: + Text(label, style: TextStyle(color: Colors.grey.shade600))), + Expanded( + child: Text(value, + style: const TextStyle(fontWeight: FontWeight.w500))), + ], + ), + ); + } + + Widget _buildActionIcon( + IconData icon, String label, Color color, VoidCallback onTap) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(icon, size: 32), + color: color, + onPressed: onTap, + ), + Text(label, + style: TextStyle(fontSize: 12, color: Colors.grey.shade700)), + ], + ); + } +} diff --git a/lib/pages/discover/pages/group_share_list_page.dart b/lib/pages/discover/pages/group_share_list_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..5fefa5ece0ade1bd05eeac08f8c92a9c4c727719 --- /dev/null +++ b/lib/pages/discover/pages/group_share_list_page.dart @@ -0,0 +1,761 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:orginone/components/LoadingWidget/LoadingWidget.dart'; +import '../discover_service.dart'; + +class GroupShareListPage extends StatefulWidget { + final String resourceId; + final String title; + + const GroupShareListPage({ + super.key, + required this.resourceId, + this.title = '动态', + }); + + @override + State createState() => _GroupShareListPageState(); +} + +class _GroupShareListPageState extends State { + List _items = []; + bool _isLoading = true; + int _currentPage = 1; + final int _pageSize = 20; + bool _hasMore = true; + String _searchText = ''; + Timer? _searchDebounce; + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + void dispose() { + _searchDebounce?.cancel(); + super.dispose(); + } + + Future _loadData({bool refresh = false}) async { + if (refresh) { + setState(() { + _currentPage = 1; + _hasMore = true; + _isLoading = true; + }); + } + + try { + // 确保plaza信息已加载 + await DiscoverService.loadPlaza(groupId: ''); + + var data = await DiscoverService.loadGroupShares( + widget.resourceId, + page: _currentPage, + pageSize: _pageSize, + filter: _searchText, + ); + + setState(() { + if (refresh) { + _items = data; + } else { + _items.addAll(data); + } + _isLoading = false; + _hasMore = data.length >= _pageSize; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + void _loadMore() { + if (!_hasMore || _isLoading) return; + setState(() { + _currentPage++; + }); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + backgroundColor: Colors.blue.shade700, + foregroundColor: Colors.white, + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + decoration: InputDecoration( + hintText: '搜索群组...', + prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), + filled: true, + fillColor: Colors.grey.shade100, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + onChanged: (value) { + setState(() { + _searchText = value; + }); + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 500), () { + _loadData(refresh: true); + }); + }, + onSubmitted: (_) => _loadData(refresh: true), + ), + ), + Expanded( + child: _isLoading && _items.isEmpty + ? const LoadingWidget() + : RefreshIndicator( + onRefresh: () => _loadData(refresh: true), + child: NotificationListener( + onNotification: (notification) { + if (notification is ScrollEndNotification && + notification.metrics.pixels >= + notification.metrics.maxScrollExtent - 200) { + _loadMore(); + } + return false; + }, + child: ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + itemCount: _hasMore ? _items.length + 1 : _items.length, + itemBuilder: (context, index) { + if (index == _items.length) { + return const Padding( + padding: EdgeInsets.all(16.0), + child: Center(child: CircularProgressIndicator()), + ); + } + + var item = _items[index]; + return _buildItemCard(item); + }, + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildItemCard(PlazaTarget item) { + return Container( + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.blue.withOpacity(0.08), + blurRadius: 20, + offset: const Offset(0, 4), + spreadRadius: 0, + ), + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: InkWell( + onTap: () => _showGroupDetail(item), + borderRadius: BorderRadius.circular(20), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16), + child: Row( + children: [ + Container( + width: 58, + height: 58, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.blue.shade400, + Colors.blue.shade700, + ], + ), + borderRadius: BorderRadius.circular(18), + boxShadow: [ + BoxShadow( + color: Colors.blue.shade300.withOpacity(0.4), + blurRadius: 12, + offset: const Offset(0, 4), + ), + ], + ), + child: Stack( + children: [ + Center( + child: Icon( + _getTypeIcon(item.typeName), + color: Colors.white, + size: 30, + ), + ), + Positioned( + bottom: 6, + right: 6, + child: Container( + width: 14, + height: 14, + decoration: BoxDecoration( + color: Colors.green.shade500, + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2), + boxShadow: [ + BoxShadow( + color: Colors.green.shade300, + blurRadius: 6, + spreadRadius: 1, + ), + ], + ), + ), + ), + ], + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Color(0xFF1A1A2E), + letterSpacing: -0.2, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 8), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: + _getTypeColor(item.typeName).withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + item.typeName.isNotEmpty ? item.typeName : '群组', + style: TextStyle( + fontSize: 12, + color: _getTypeColor(item.typeName), + fontWeight: FontWeight.w600, + letterSpacing: -0.1, + ), + ), + ), + if (item.sourceId.isNotEmpty) ...[ + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'ID: ${item.sourceId.substring(0, item.sourceId.length > 8 ? 8 : item.sourceId.length)}', + style: TextStyle( + fontSize: 11, + color: Colors.grey.shade600, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ], + ), + if (item.pubTime.isNotEmpty) ...[ + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.access_time_outlined, + size: 14, + color: Colors.grey.shade400, + ), + const SizedBox(width: 5), + Text( + _formatTime(item.pubTime), + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade500, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ], + ), + ), + const SizedBox(width: 10), + Container( + width: 38, + height: 38, + decoration: BoxDecoration( + color: Colors.blue.shade50, + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.chevron_right_rounded, + color: Colors.blue.shade600, + size: 22, + ), + ), + ], + ), + ), + ), + ); + } + + IconData _getTypeIcon(String typeName) { + switch (typeName.toLowerCase()) { + case '群组': + case 'group': + return Icons.groups_rounded; + case '人员': + case 'person': + return Icons.person_rounded; + case '部门': + case 'department': + return Icons.business_rounded; + case '单位': + case 'company': + return Icons.apartment_rounded; + case '团队': + case 'team': + return Icons.people_alt_rounded; + default: + return Icons.workspaces_outlined; + } + } + + Color _getTypeColor(String typeName) { + switch (typeName.toLowerCase()) { + case '群组': + case 'group': + return Colors.blue.shade700; + case '人员': + case 'person': + return Colors.green.shade600; + case '部门': + case 'department': + return Colors.orange.shade600; + case '单位': + case 'company': + return Colors.purple.shade600; + case '团队': + case 'team': + return Colors.teal.shade600; + default: + return Colors.grey.shade600; + } + } + + String _formatTime(String timeStr) { + if (timeStr.isEmpty) return ''; + try { + DateTime dateTime = DateTime.parse(timeStr); + Duration diff = DateTime.now().difference(dateTime); + if (diff.inDays > 365) { + return '${(diff.inDays / 365).floor()}年前'; + } else if (diff.inDays > 30) { + return '${(diff.inDays / 30).floor()}个月前'; + } else if (diff.inDays > 0) { + return '${diff.inDays}天前'; + } else if (diff.inHours > 0) { + return '${diff.inHours}小时前'; + } else if (diff.inMinutes > 0) { + return '${diff.inMinutes}分钟前'; + } else { + return '刚刚'; + } + } catch (e) { + return timeStr.substring(0, timeStr.length > 16 ? 16 : timeStr.length); + } + } + + void _showGroupDetail(PlazaTarget item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.85, + minChildSize: 0.5, + maxChildSize: 0.95, + expand: false, + builder: (context, scrollController) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + const BorderRadius.vertical(top: Radius.circular(32)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 30, + offset: const Offset(0, -10), + ), + ], + ), + child: Column( + children: [ + Container( + width: 48, + height: 5, + margin: const EdgeInsets.only(top: 16, bottom: 20), + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(3), + ), + ), + Expanded( + child: SingleChildScrollView( + controller: scrollController, + padding: const EdgeInsets.fromLTRB(28, 0, 28, 28), + child: Column( + children: [ + Container( + width: 110, + height: 110, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.blue.shade400, + Colors.blue.shade700, + ], + ), + borderRadius: BorderRadius.circular(28), + boxShadow: [ + BoxShadow( + color: Colors.blue.shade300.withOpacity(0.5), + blurRadius: 24, + offset: const Offset(0, 12), + ), + ], + ), + child: Icon( + _getTypeIcon(item.typeName), + size: 54, + color: Colors.white, + ), + ), + const SizedBox(height: 24), + Text( + item.name, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.w800, + color: Color(0xFF1A1A2E), + letterSpacing: -0.5, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: + _getTypeColor(item.typeName).withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + item.typeName.isNotEmpty ? item.typeName : '群组', + style: TextStyle( + fontSize: 14, + color: _getTypeColor(item.typeName), + fontWeight: FontWeight.w600, + ), + ), + ), + const SizedBox(height: 32), + Container( + decoration: BoxDecoration( + color: const Color(0xFFF8F9FA), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.grey.shade200, + width: 1, + ), + ), + padding: const EdgeInsets.all(20), + child: Column( + children: [ + _buildInfoItem('ID', item.id), + const Divider( + height: 28, + thickness: 1, + indent: 0, + endIndent: 0, + color: Color(0xFFE9ECEF)), + _buildInfoItem( + '类型', + item.typeName.isNotEmpty + ? item.typeName + : '未知'), + const Divider( + height: 28, + thickness: 1, + indent: 0, + endIndent: 0, + color: Color(0xFFE9ECEF)), + _buildInfoItem( + '发布时间', + item.pubTime.isNotEmpty + ? item.pubTime + : '暂无'), + if (item.sourceId.isNotEmpty) ...[ + const Divider( + height: 28, + thickness: 1, + indent: 0, + endIndent: 0, + color: Color(0xFFE9ECEF)), + _buildInfoItem('来源ID', item.sourceId), + ], + ], + ), + ), + const SizedBox(height: 32), + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + '快捷操作', + style: TextStyle( + fontSize: 19, + fontWeight: FontWeight.w700, + color: Color(0xFF1A1A2E), + letterSpacing: -0.3, + ), + ), + const SizedBox(height: 18), + _buildActionButton( + Icons.chat_bubble_outline_rounded, + '进入聊天室', + Colors.blue.shade700, + () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: + Text('正在进入 ${item.name} 的聊天室...'), + backgroundColor: Colors.blue.shade600, + behavior: SnackBarBehavior.floating, + margin: const EdgeInsets.all(16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ); + }, + ), + const SizedBox(height: 14), + _buildActionButton( + Icons.person_add_alt_1_outlined, + '申请加入', + Colors.green.shade600, + () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('已提交加入 ${item.name} 的申请'), + backgroundColor: Colors.green.shade600, + behavior: SnackBarBehavior.floating, + margin: const EdgeInsets.all(16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ); + }, + ), + ], + ), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(28, 0, 28, 32), + child: SizedBox( + width: double.infinity, + height: 56, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.grey.shade200, + Colors.grey.shade300, + ], + ), + borderRadius: BorderRadius.circular(18), + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => Navigator.pop(context), + borderRadius: BorderRadius.circular(18), + child: const Center( + child: Text( + '关闭', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: Color(0xFF495057)), + ), + ), + ), + ), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ); + } + + Widget _buildInfoItem(String label, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 95, + child: Text( + label, + style: const TextStyle( + fontSize: 14, + color: Color(0xFF6C757D), + fontWeight: FontWeight.w500, + ), + ), + ), + Expanded( + child: Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF212529), + ), + ), + ), + ], + ); + } + + Widget _buildActionButton( + IconData icon, String label, Color color, VoidCallback onTap) { + return Container( + decoration: BoxDecoration( + color: color.withOpacity(0.06), + borderRadius: BorderRadius.circular(18), + border: Border.all( + color: color.withOpacity(0.2), + width: 1.5, + ), + ), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(18), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(18), + highlightColor: color.withOpacity(0.1), + splashColor: color.withOpacity(0.05), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: color.withOpacity(0.12), + borderRadius: BorderRadius.circular(14), + boxShadow: [ + BoxShadow( + color: color.withOpacity(0.08), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Icon(icon, color: color, size: 24), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + label, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Color(0xFF212529), + letterSpacing: -0.2, + ), + ), + ), + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: color.withOpacity(0.08), + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + Icons.chevron_right_rounded, + color: color, + size: 20, + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/discover/pages/live_list_page.dart b/lib/pages/discover/pages/live_list_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..bae9f45d368f9b86771504f02bf4bfda91b737dc --- /dev/null +++ b/lib/pages/discover/pages/live_list_page.dart @@ -0,0 +1,775 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:orginone/components/LoadingWidget/LoadingWidget.dart'; +import '../discover_service.dart'; + +class LiveListPage extends StatefulWidget { + final String resourceId; + final String title; + + const LiveListPage({ + super.key, + required this.resourceId, + this.title = '直播', + }); + + @override + State createState() => _LiveListPageState(); +} + +class _LiveListPageState extends State { + List _items = []; + bool _isLoading = true; + int _currentPage = 1; + final int _pageSize = 20; + bool _hasMore = true; + String _searchText = ''; + Timer? _searchDebounce; + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + void dispose() { + _searchDebounce?.cancel(); + super.dispose(); + } + + Future _loadData({bool refresh = false}) async { + if (refresh) { + setState(() { + _currentPage = 1; + _hasMore = true; + _isLoading = true; + }); + } + + try { + // 确保plaza信息已加载 + await DiscoverService.loadPlaza(groupId: ''); + + var data = await DiscoverService.loadLives( + widget.resourceId, + page: _currentPage, + pageSize: _pageSize, + filter: _searchText, + ); + + setState(() { + if (refresh) { + _items = data; + } else { + _items.addAll(data); + } + _isLoading = false; + _hasMore = data.length >= _pageSize; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + void _loadMore() { + if (!_hasMore || _isLoading) return; + setState(() { + _currentPage++; + }); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + backgroundColor: Colors.pink.shade600, + foregroundColor: Colors.white, + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + decoration: InputDecoration( + hintText: '搜索直播...', + prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), + filled: true, + fillColor: Colors.grey.shade100, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + onChanged: (value) { + setState(() { + _searchText = value; + }); + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 500), () { + _loadData(refresh: true); + }); + }, + onSubmitted: (_) => _loadData(refresh: true), + ), + ), + Expanded( + child: _isLoading && _items.isEmpty + ? const LoadingWidget() + : _items.isEmpty + ? _buildEmptyState() + : RefreshIndicator( + onRefresh: () => _loadData(refresh: true), + child: NotificationListener( + onNotification: (notification) { + if (notification is ScrollEndNotification && + notification.metrics.pixels >= + notification.metrics.maxScrollExtent - + 200) { + _loadMore(); + } + return false; + }, + child: ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + itemCount: + _hasMore ? _items.length + 1 : _items.length, + itemBuilder: (context, index) { + if (index == _items.length) { + return const Padding( + padding: EdgeInsets.all(16.0), + child: Center( + child: CircularProgressIndicator()), + ); + } + + var item = _items[index]; + return _buildLiveCard(item); + }, + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildEmptyState() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.live_tv_outlined, + size: 80, + color: Colors.grey.shade300, + ), + const SizedBox(height: 24), + Text( + '暂无直播', + style: TextStyle( + fontSize: 18, + color: Colors.grey.shade600, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + '下拉刷新,等待精彩直播开始', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 14, color: Colors.grey.shade500), + ), + ), + ], + ), + ); + } + + Widget _buildLiveCard(PlazaLive item) { + return Container( + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: InkWell( + onTap: () => _showLiveDetail(item), + borderRadius: BorderRadius.circular(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + Container( + width: double.infinity, + height: 200, + decoration: BoxDecoration( + color: Colors.grey.shade200, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), + child: item.coverUrl != null && item.coverUrl!.isNotEmpty + ? ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + child: Image.network( + item.coverUrl!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Center( + child: Icon( + Icons.live_tv_outlined, + size: 60, + color: Colors.grey.shade400, + ), + ), + ), + ) + : Center( + child: Icon( + Icons.live_tv_outlined, + size: 60, + color: Colors.grey.shade400, + ), + ), + ), + if (item.isLive) + Positioned( + top: 12, + left: 12, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: Colors.red.shade600, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: TweenAnimationBuilder( + tween: Tween(begin: 0.4, end: 1.0), + duration: const Duration(milliseconds: 800), + builder: (context, value, child) { + return Transform.scale( + scale: value, + child: child, + ); + }, + child: Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + ), + ), + ), + const SizedBox(width: 6), + const Text( + '直播中', + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + if (item.viewerCount != null) + Positioned( + bottom: 12, + right: 12, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.6), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.remove_red_eye_outlined, + color: Colors.white, + size: 14, + ), + const SizedBox(width: 4), + Text( + _formatNumber(item.viewerCount!), + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.all(12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CircleAvatar( + radius: 24, + backgroundColor: Colors.pink.shade100, + child: item.hostAvatar.isNotEmpty + ? ClipOval( + child: Image.network( + item.hostAvatar, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Icon( + Icons.person_outline, + color: Colors.pink.shade600, + size: 28, + ), + ), + ) + : Icon( + Icons.person_outline, + color: Colors.pink.shade600, + size: 28, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Text( + item.hostName, + style: TextStyle( + fontSize: 13, + color: Colors.grey.shade600, + ), + ), + const SizedBox(height: 4), + if (item.likeCount != null) + Row( + children: [ + Icon( + Icons.favorite_border, + size: 14, + color: Colors.pink.shade500, + ), + const SizedBox(width: 4), + Text( + _formatNumber(item.likeCount!), + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade500, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + String _formatNumber(int number) { + if (number >= 10000) { + return '${(number / 10000).toStringAsFixed(1)}万'; + } + return number.toString(); + } + + void _showLiveDetail(PlazaLive item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.9, + minChildSize: 0.6, + maxChildSize: 0.95, + expand: false, + builder: (context, scrollController) { + return Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + ), + child: Column( + children: [ + Container( + width: 48, + height: 5, + margin: const EdgeInsets.only(top: 12, bottom: 16), + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(3), + ), + ), + Expanded( + child: SingleChildScrollView( + controller: scrollController, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + height: 220, + decoration: BoxDecoration( + color: Colors.grey.shade200, + borderRadius: BorderRadius.circular(16), + ), + child: item.coverUrl != null && + item.coverUrl!.isNotEmpty + ? ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Image.network( + item.coverUrl!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Center( + child: Icon( + Icons.live_tv_outlined, + size: 60, + color: Colors.grey.shade400, + ), + ), + ), + ) + : Center( + child: Icon( + Icons.live_tv_outlined, + size: 60, + color: Colors.grey.shade400, + ), + ), + ), + const SizedBox(height: 20), + Text( + item.name, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 16), + Row( + children: [ + CircleAvatar( + radius: 28, + backgroundColor: Colors.pink.shade100, + child: item.hostAvatar.isNotEmpty + ? ClipOval( + child: Image.network( + item.hostAvatar, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Icon( + Icons.person_outline, + color: Colors.pink.shade600, + size: 32, + ), + ), + ) + : Icon( + Icons.person_outline, + color: Colors.pink.shade600, + size: 32, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.hostName, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + if (item.isLive) + Container( + margin: const EdgeInsets.only(top: 4), + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: + BorderRadius.circular(4), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 6, + height: 6, + decoration: BoxDecoration( + color: Colors.red.shade600, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 4), + Text( + '直播中', + style: TextStyle( + fontSize: 11, + color: Colors.red.shade600, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: _buildInfoStat( + Icons.remove_red_eye_outlined, + item.viewerCount != null + ? _formatNumber(item.viewerCount!) + : '-', + '观看', + Colors.blue.shade600, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildInfoStat( + Icons.favorite_border, + item.likeCount != null + ? _formatNumber(item.likeCount!) + : '-', + '点赞', + Colors.pink.shade500, + ), + ), + ], + ), + if (item.description != null && + item.description!.isNotEmpty) ...[ + const SizedBox(height: 20), + const Text( + '简介', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 8), + Text( + item.description!, + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade700, + height: 1.6, + ), + ), + ], + const SizedBox(height: 20), + _buildInfoItem('直播ID', item.id), + const SizedBox(height: 12), + _buildInfoItem('开始时间', + item.startTime.isNotEmpty ? item.startTime : '-'), + const SizedBox(height: 12), + _buildInfoItem('发布时间', + item.pubTime.isNotEmpty ? item.pubTime : '-'), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 12, 20, 24), + child: Row( + children: [ + Expanded( + child: OutlinedButton.icon( + onPressed: () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('已分享\"${item.name}\"'), + backgroundColor: Colors.pink.shade600, + ), + ); + }, + icon: const Icon(Icons.share_outlined), + label: const Text('分享'), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + side: BorderSide(color: Colors.pink.shade200), + foregroundColor: Colors.pink.shade600, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: ElevatedButton.icon( + onPressed: () { + Navigator.pop(context); + if (item.liveUrl != null && + item.liveUrl!.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在进入\"${item.name}\"直播间'), + backgroundColor: Colors.pink.shade600, + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + item.isLive ? '直播间暂未准备好' : '直播已结束'), + backgroundColor: Colors.grey.shade600, + ), + ); + } + }, + icon: Icon(item.isLive + ? Icons.play_arrow + : Icons.live_tv_outlined), + label: Text(item.isLive ? '进入直播间' : '观看回放'), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: Colors.pink.shade600, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); + }, + ); + }, + ); + } + + Widget _buildInfoStat( + IconData icon, String value, String label, Color color) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + decoration: BoxDecoration( + color: color.withOpacity(0.08), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + Icon(icon, color: color, size: 24), + const SizedBox(height: 6), + Text( + value, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: color, + ), + ), + const SizedBox(height: 2), + Text( + label, + style: TextStyle( + fontSize: 12, + color: color.withOpacity(0.7), + ), + ), + ], + ), + ); + } + + Widget _buildInfoItem(String label, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 90, + child: Text( + label, + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade600, + ), + ), + ), + Expanded( + child: Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + ), + ], + ); + } +} diff --git a/lib/pages/discover/pages/market_list_page.dart b/lib/pages/discover/pages/market_list_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..02b4da1561d73117bd75153b707901abaadf43ca --- /dev/null +++ b/lib/pages/discover/pages/market_list_page.dart @@ -0,0 +1,474 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:orginone/components/LoadingWidget/LoadingWidget.dart'; +import '../discover_service.dart'; + +class MarketListPage extends StatefulWidget { + final String resourceId; + final String title; + + const MarketListPage({ + super.key, + required this.resourceId, + this.title = '市场交易', + }); + + @override + State createState() => _MarketListPageState(); +} + +class _MarketListPageState extends State { + List _items = []; + bool _isLoading = true; + int _currentPage = 1; + final int _pageSize = 20; + bool _hasMore = true; + String _searchText = ''; + String? _selectedTypeName; + Timer? _searchDebounce; + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + void dispose() { + _searchDebounce?.cancel(); + super.dispose(); + } + + Future _loadData({bool refresh = false}) async { + if (refresh) { + setState(() { + _currentPage = 1; + _hasMore = true; + _isLoading = true; + }); + } + + try { + // 确保plaza信息已加载 + await DiscoverService.loadPlaza(groupId: ''); + + var data = await DiscoverService.loadMarketGoods( + widget.resourceId, + page: _currentPage, + pageSize: _pageSize, + filter: _searchText, + typeName: _selectedTypeName, + ); + + setState(() { + if (refresh) { + _items = data; + } else { + _items.addAll(data); + } + _isLoading = false; + _hasMore = data.length >= _pageSize; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + void _loadMore() { + if (!_hasMore || _isLoading) return; + setState(() { + _currentPage++; + }); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + backgroundColor: Colors.green.shade700, + foregroundColor: Colors.white, + actions: [ + IconButton( + icon: const Icon(Icons.filter_list), + onPressed: _showFilterDialog, + tooltip: '筛选', + ), + ], + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + decoration: InputDecoration( + hintText: '搜索商品...', + prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), + filled: true, + fillColor: Colors.grey.shade100, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + onChanged: (value) { + setState(() { + _searchText = value; + }); + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 500), () { + _loadData(refresh: true); + }); + }, + onSubmitted: (_) => _loadData(refresh: true), + ), + ), + Expanded( + child: _isLoading && _items.isEmpty + ? const LoadingWidget() + : RefreshIndicator( + onRefresh: () => _loadData(refresh: true), + child: GridView.builder( + padding: const EdgeInsets.all(12), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 0.7, + ), + itemCount: _hasMore ? _items.length + 1 : _items.length, + itemBuilder: (context, index) { + if (index == _items.length) { + return const Center( + child: CircularProgressIndicator()); + } + + var item = _items[index]; + return _buildGoodsCard(item); + }, + ), + ), + ), + ], + ), + ); + } + + Widget _buildGoodsCard(PlazaMarketGoods item) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.green.withOpacity(0.08), + blurRadius: 20, + offset: const Offset(0, 4), + ), + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () => _showGoodsDetail(item), + borderRadius: BorderRadius.circular(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + width: double.infinity, + color: Colors.green.shade50, + child: item.imageUrls != null && item.imageUrls!.isNotEmpty + ? Image.network( + item.imageUrls!.first, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Center( + child: Icon( + Icons.shopping_bag_rounded, + size: 55, + color: Colors.green.shade300, + ), + ), + ) + : Center( + child: Icon( + Icons.shopping_bag_rounded, + size: 55, + color: Colors.green.shade300, + ), + ), + ), + if (item.typeName.isNotEmpty) + Positioned( + top: 10, + left: 10, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.green.shade600, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.green.shade200, + blurRadius: 8, + offset: const Offset(0, 3), + ), + ], + ), + child: Text( + item.typeName, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 14, + color: Color(0xFF1A1A2E), + letterSpacing: -0.2, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '¥${item.price.toStringAsFixed(2)}', + style: TextStyle( + fontSize: 19, + fontWeight: FontWeight.w800, + color: Colors.red.shade600, + ), + ), + if (item.orgPrice > item.price) + Text( + '¥${item.orgPrice.toStringAsFixed(2)}', + style: TextStyle( + fontSize: 12, + decoration: TextDecoration.lineThrough, + color: Colors.grey.shade400, + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: Colors.green.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + item.amount > 0 ? '库存: ${item.amount}' : '已售罄', + style: TextStyle( + fontSize: 11, + color: item.amount > 0 + ? Colors.green.shade700 + : Colors.red.shade600, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } + + void _showFilterDialog() { + showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '商品分类', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 16), + Wrap( + spacing: 8, + runSpacing: 8, + children: ['全部', '实物', '虚拟', '服务'].map((type) { + return ChoiceChip( + label: Text(type), + selected: _selectedTypeName == (type == '全部' ? null : type), + onSelected: (selected) { + Navigator.pop(context); + setState(() { + _selectedTypeName = + selected && type != '全部' ? type : null; + }); + _loadData(refresh: true); + }, + ); + }).toList(), + ), + ], + ), + ); + }, + ); + } + + void _showGoodsDetail(PlazaMarketGoods item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.85, + minChildSize: 0.5, + maxChildSize: 0.95, + expand: false, + builder: (context, scrollController) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + Container( + width: 40, + height: 4, + margin: const EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(2), + ), + ), + if (item.imageUrls != null && item.imageUrls!.isNotEmpty) + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Image.network( + item.imageUrls!.first, + height: 200, + fit: BoxFit.cover, + ), + ), + const SizedBox(height: 16), + Text( + item.name, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 12), + Row( + children: [ + Text( + '¥${item.price.toStringAsFixed(2)}', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.red.shade600, + ), + ), + if (item.orgPrice > item.price) ...[ + const SizedBox(width: 12), + Text( + '¥${item.orgPrice.toStringAsFixed(2)}', + style: const TextStyle( + fontSize: 16, + decoration: TextDecoration.lineThrough, + color: Colors.grey, + ), + ), + ], + ], + ), + const SizedBox(height: 12), + Text( + '库存: ${item.amount} 件', + style: TextStyle(fontSize: 14, color: Colors.grey.shade600), + ), + const Divider(height: 24), + Expanded( + child: SingleChildScrollView( + controller: scrollController, + child: Text( + item.description ?? '暂无详细描述', + style: const TextStyle(fontSize: 15, height: 1.6), + ), + ), + ), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton.icon( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('已将 ${item.name} 加入购物车')), + ); + }, + icon: const Icon(Icons.shopping_cart_outlined), + label: const Text('加入购物车'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.green.shade700, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25), + ), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ); + } +} diff --git a/lib/pages/discover/pages/notice_list_page.dart b/lib/pages/discover/pages/notice_list_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..171ef2fc79a9217c976a2ac18b7878db560e169e --- /dev/null +++ b/lib/pages/discover/pages/notice_list_page.dart @@ -0,0 +1,620 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:orginone/components/LoadingWidget/LoadingWidget.dart'; +import '../discover_service.dart'; + +class NoticeListPage extends StatefulWidget { + final String resourceId; + final String title; + + const NoticeListPage({ + super.key, + required this.resourceId, + this.title = '公告', + }); + + @override + State createState() => _NoticeListPageState(); +} + +class _NoticeListPageState extends State { + List _items = []; + bool _isLoading = true; + int _currentPage = 1; + final int _pageSize = 20; + bool _hasMore = true; + String _searchText = ''; + Timer? _searchDebounce; + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + void dispose() { + _searchDebounce?.cancel(); + super.dispose(); + } + + Future _loadData({bool refresh = false}) async { + if (refresh) { + setState(() { + _currentPage = 1; + _hasMore = true; + _isLoading = true; + }); + } + + try { + // 确保plaza信息已加载 + await DiscoverService.loadPlaza(groupId: ''); + + var data = await DiscoverService.loadNotices( + widget.resourceId, + page: _currentPage, + pageSize: _pageSize, + filter: _searchText, + ); + + setState(() { + if (refresh) { + _items = data; + } else { + _items.addAll(data); + } + _isLoading = false; + _hasMore = data.length >= _pageSize; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + void _loadMore() { + if (!_hasMore || _isLoading) return; + setState(() { + _currentPage++; + }); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + backgroundColor: Colors.orange.shade700, + foregroundColor: Colors.white, + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + decoration: InputDecoration( + hintText: '搜索公告...', + prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), + filled: true, + fillColor: Colors.grey.shade100, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + onChanged: (value) { + setState(() { + _searchText = value; + }); + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 500), () { + _loadData(refresh: true); + }); + }, + onSubmitted: (_) => _loadData(refresh: true), + ), + ), + Expanded( + child: _isLoading && _items.isEmpty + ? const LoadingWidget() + : RefreshIndicator( + onRefresh: () => _loadData(refresh: true), + child: NotificationListener( + onNotification: (notification) { + if (notification is ScrollEndNotification && + notification.metrics.pixels >= + notification.metrics.maxScrollExtent - 200) { + _loadMore(); + } + return false; + }, + child: ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + itemCount: _hasMore ? _items.length + 1 : _items.length, + itemBuilder: (context, index) { + if (index == _items.length) { + return const Padding( + padding: EdgeInsets.all(16.0), + child: Center(child: CircularProgressIndicator()), + ); + } + + var item = _items[index]; + return _buildNoticeCard(item); + }, + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildNoticeCard(PlazaNotice item) { + return Container( + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.orange.withOpacity(0.08), + blurRadius: 20, + offset: const Offset(0, 4), + ), + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: InkWell( + onTap: () => _showNoticeDetail(item), + borderRadius: BorderRadius.circular(20), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.orange.shade400, + Colors.orange.shade700, + ], + ), + borderRadius: BorderRadius.circular(18), + boxShadow: [ + BoxShadow( + color: Colors.orange.shade300.withOpacity(0.4), + blurRadius: 12, + offset: const Offset(0, 4), + ), + ], + ), + child: item.coverImage != null + ? ClipRRect( + borderRadius: BorderRadius.circular(18), + child: Image.network( + item.coverImage!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Icon( + Icons.campaign_rounded, + color: Colors.white, + size: 32, + ), + ), + ) + : Icon( + Icons.campaign_rounded, + color: Colors.white, + size: 32, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + item.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Color(0xFF1A1A2E), + letterSpacing: -0.2, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + Container( + margin: const EdgeInsets.only(left: 8), + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '公告', + style: TextStyle( + fontSize: 11, + color: Colors.orange.shade700, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + const SizedBox(height: 10), + Text( + item.content.replaceAll(RegExp(r'<[^>]*>'), '').length > + 100 + ? '${item.content.replaceAll(RegExp(r'<[^>]*>'), '').substring(0, 100)}...' + : item.content.replaceAll(RegExp(r'<[^>]*>'), ''), + style: const TextStyle( + fontSize: 14, + color: Color(0xFF6C757D), + height: 1.5, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 10), + Row( + children: [ + Icon( + Icons.access_time_rounded, + size: 14, + color: Colors.grey.shade400, + ), + const SizedBox(width: 5), + Text( + _formatTime(item.pubTime), + style: const TextStyle( + fontSize: 12, + color: Color(0xFF6C757D), + fontWeight: FontWeight.w500, + ), + ), + if (item.id.isNotEmpty) ...[ + const SizedBox(width: 12), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + 'ID: ${item.id.substring(0, item.id.length > 6 ? 6 : item.id.length)}', + style: TextStyle( + fontSize: 10, + color: Colors.grey.shade600, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ], + ), + ], + ), + ), + const SizedBox(width: 8), + Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.chevron_right_rounded, + color: Colors.orange.shade600, + size: 20, + ), + ), + ], + ), + ), + ), + ); + } + + String _formatTime(String timeStr) { + if (timeStr.isEmpty) return ''; + try { + DateTime dateTime = DateTime.parse(timeStr); + Duration diff = DateTime.now().difference(dateTime); + if (diff.inDays > 365) { + return '${(diff.inDays / 365).floor()}年前'; + } else if (diff.inDays > 30) { + return '${(diff.inDays / 30).floor()}个月前'; + } else if (diff.inDays > 0) { + return '${diff.inDays}天前'; + } else if (diff.inHours > 0) { + return '${diff.inHours}小时前'; + } else if (diff.inMinutes > 0) { + return '${diff.inMinutes}分钟前'; + } else { + return '刚刚'; + } + } catch (e) { + return timeStr.substring(0, timeStr.length > 16 ? 16 : timeStr.length); + } + } + + void _showNoticeDetail(PlazaNotice item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.85, + minChildSize: 0.5, + maxChildSize: 0.95, + expand: false, + builder: (context, scrollController) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + const BorderRadius.vertical(top: Radius.circular(32)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 30, + offset: const Offset(0, -10), + ), + ], + ), + child: Column( + children: [ + Container( + width: 48, + height: 5, + margin: const EdgeInsets.only(top: 16, bottom: 20), + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(3), + ), + ), + Expanded( + child: SingleChildScrollView( + controller: scrollController, + padding: const EdgeInsets.symmetric(horizontal: 28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (item.coverImage != null) + Container( + width: double.infinity, + height: 200, + margin: const EdgeInsets.only(bottom: 24), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 15, + offset: const Offset(0, 5), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Image.network( + item.coverImage!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container( + color: Colors.orange.shade100, + child: Icon( + Icons.campaign_rounded, + size: 50, + color: Colors.orange.shade600, + ), + ), + ), + ), + ), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 14, vertical: 6), + decoration: BoxDecoration( + color: Colors.orange.shade100, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '公告', + style: TextStyle( + fontSize: 13, + color: Colors.orange.shade700, + fontWeight: FontWeight.w700, + ), + ), + ), + if (item.pubTime.isNotEmpty) ...[ + const SizedBox(width: 12), + Text( + _formatTime(item.pubTime), + style: const TextStyle( + fontSize: 14, + color: Color(0xFF6C757D), + fontWeight: FontWeight.w500, + ), + ), + ], + ], + ), + const SizedBox(height: 16), + Text( + item.name, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.w800, + color: Color(0xFF1A1A2E), + letterSpacing: -0.5, + height: 1.3, + ), + ), + const SizedBox(height: 24), + Container( + width: double.infinity, + decoration: BoxDecoration( + color: const Color(0xFFF8F9FA), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.grey.shade200, + width: 1, + ), + ), + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '公告内容', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Color(0xFF212529), + ), + ), + const SizedBox(height: 12), + Text( + item.content + .replaceAll(RegExp(r'<[^>]*>'), ''), + style: const TextStyle( + fontSize: 15, + color: Color(0xFF495057), + height: 1.7, + ), + ), + ], + ), + ), + const SizedBox(height: 24), + if (item.id.isNotEmpty) + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: Colors.orange.shade200, + width: 1, + ), + ), + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 12), + child: Row( + children: [ + Icon( + Icons.info_outline_rounded, + color: Colors.orange.shade700, + size: 20, + ), + const SizedBox(width: 10), + const Text( + '通知ID:', + style: TextStyle( + fontSize: 14, + color: Color(0xFF6C757D), + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + item.id, + style: TextStyle( + fontSize: 14, + color: Colors.orange.shade800, + fontWeight: FontWeight.w600, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(28, 16, 28, 32), + child: SizedBox( + width: double.infinity, + height: 56, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.orange.shade400, + Colors.orange.shade600, + ], + ), + borderRadius: BorderRadius.circular(18), + boxShadow: [ + BoxShadow( + color: Colors.orange.shade300.withOpacity(0.5), + blurRadius: 15, + offset: const Offset(0, 6), + ), + ], + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => Navigator.pop(context), + borderRadius: BorderRadius.circular(18), + child: const Center( + child: Text( + '关闭', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: Colors.white, + letterSpacing: -0.2, + ), + ), + ), + ), + ), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ); + } +} diff --git a/lib/pages/discover/pages/video_list_page.dart b/lib/pages/discover/pages/video_list_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..57dfde5ce57858b1666915cc48a52355e57f06df --- /dev/null +++ b/lib/pages/discover/pages/video_list_page.dart @@ -0,0 +1,576 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:orginone/components/LoadingWidget/LoadingWidget.dart'; +import '../discover_service.dart'; + +class VideoListPage extends StatefulWidget { + final String resourceId; + final String title; + + const VideoListPage({ + super.key, + required this.resourceId, + this.title = '视频', + }); + + @override + State createState() => _VideoListPageState(); +} + +class _VideoListPageState extends State { + List _items = []; + bool _isLoading = true; + int _currentPage = 1; + final int _pageSize = 20; + bool _hasMore = true; + String _searchText = ''; + Timer? _searchDebounce; + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + void dispose() { + _searchDebounce?.cancel(); + super.dispose(); + } + + Future _loadData({bool refresh = false}) async { + if (refresh) { + setState(() { + _currentPage = 1; + _hasMore = true; + _isLoading = true; + }); + } + + try { + // 确保plaza信息已加载 + await DiscoverService.loadPlaza(groupId: ''); + + var data = await DiscoverService.loadVideos( + widget.resourceId, + page: _currentPage, + pageSize: _pageSize, + filter: _searchText, + ); + + setState(() { + if (refresh) { + _items = data; + } else { + _items.addAll(data); + } + _isLoading = false; + _hasMore = data.length >= _pageSize; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + void _loadMore() { + if (!_hasMore || _isLoading) return; + setState(() { + _currentPage++; + }); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + backgroundColor: Colors.red.shade600, + foregroundColor: Colors.white, + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + decoration: InputDecoration( + hintText: '搜索视频...', + prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), + filled: true, + fillColor: Colors.grey.shade100, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + onChanged: (value) { + setState(() { + _searchText = value; + }); + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 500), () { + _loadData(refresh: true); + }); + }, + onSubmitted: (_) => _loadData(refresh: true), + ), + ), + Expanded( + child: _isLoading && _items.isEmpty + ? const LoadingWidget() + : RefreshIndicator( + onRefresh: () => _loadData(refresh: true), + child: GridView.builder( + padding: const EdgeInsets.all(8), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 8, + mainAxisSpacing: 8, + childAspectRatio: 0.75, + ), + itemCount: _hasMore ? _items.length + 1 : _items.length, + itemBuilder: (context, index) { + if (index == _items.length) { + return const Center( + child: CircularProgressIndicator()); + } + + var item = _items[index]; + return _buildVideoCard(item); + }, + ), + ), + ), + ], + ), + ); + } + + Widget _buildVideoCard(PlazaVideo item) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.red.withOpacity(0.08), + blurRadius: 20, + offset: const Offset(0, 4), + ), + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () => _showVideoPlayer(item), + borderRadius: BorderRadius.circular(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + width: double.infinity, + color: Colors.red.shade50, + child: item.thumbnailUrl != null + ? Image.network( + item.thumbnailUrl!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Center( + child: Icon( + Icons.videocam_off_rounded, + size: 45, + color: Colors.red.shade300, + ), + ), + ) + : Center( + child: Icon( + Icons.play_circle_outline_rounded, + size: 55, + color: Colors.red.shade300, + ), + ), + ), + if (item.duration > 0) + Positioned( + bottom: 10, + right: 10, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.8), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: Colors.white.withOpacity(0.3), + width: 1, + ), + ), + child: Text( + '${item.duration ~/ 60}:${(item.duration % 60).toString().padLeft(2, '0')}', + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + Center( + child: Container( + width: 56, + height: 56, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.red.shade400, + Colors.red.shade600, + ], + ), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.red.shade400.withOpacity(0.5), + blurRadius: 15, + offset: const Offset(0, 5), + ), + ], + ), + child: const Icon( + Icons.play_arrow_rounded, + color: Colors.white, + size: 30, + ), + ), + ), + ], + ), + ), + Expanded( + flex: 2, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 14, + color: Color(0xFF1A1A2E), + letterSpacing: -0.2, + height: 1.3, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 8), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + item.typeName.isNotEmpty ? item.typeName : '视频', + style: TextStyle( + fontSize: 11, + color: Colors.red.shade700, + fontWeight: FontWeight.w600, + ), + ), + ), + if (item.createTime.isNotEmpty) ...[ + const SizedBox(width: 8), + Expanded( + child: Text( + _formatTime(item.createTime), + style: const TextStyle( + fontSize: 11, + color: Color(0xFF6C757D), + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ], + ), + ], + ), + ), + ), + ], + ), + ), + ); + } + + String _formatTime(String timeStr) { + if (timeStr.isEmpty) return ''; + try { + DateTime dateTime = DateTime.parse(timeStr); + Duration diff = DateTime.now().difference(dateTime); + if (diff.inDays > 365) { + return '${(diff.inDays / 365).floor()}年前'; + } else if (diff.inDays > 30) { + return '${(diff.inDays / 30).floor()}个月前'; + } else if (diff.inDays > 0) { + return '${diff.inDays}天前'; + } else if (diff.inHours > 0) { + return '${diff.inHours}小时前'; + } else if (diff.inMinutes > 0) { + return '${diff.inMinutes}分钟前'; + } else { + return '刚刚'; + } + } catch (e) { + return timeStr.substring(0, timeStr.length > 16 ? 16 : timeStr.length); + } + } + + void _showVideoPlayer(PlazaVideo item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.9, + minChildSize: 0.6, + maxChildSize: 0.95, + expand: false, + builder: (context, scrollController) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + Container( + width: 40, + height: 4, + margin: const EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(2), + ), + ), + Text( + item.name, + style: const TextStyle( + fontSize: 18, fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + Expanded( + flex: 3, + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(12), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Stack( + alignment: Alignment.center, + children: [ + if (item.thumbnailUrl != null) + Positioned.fill( + child: Image.network( + item.thumbnailUrl!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container( + color: Colors.black87, + child: const Icon(Icons.videocam_off, + size: 60, color: Colors.white54), + ), + ), + ) + else + Container( + color: Colors.black87, + child: const Icon(Icons.play_circle_outline, + size: 80, color: Colors.white54), + ), + if (item.videoUrl != null && + item.videoUrl!.isNotEmpty) + IconButton( + iconSize: 64, + icon: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.9), + shape: BoxShape.circle, + ), + child: const Icon(Icons.play_arrow, + size: 40, color: Colors.red), + ), + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在播放 ${item.name}...')), + ); + }, + ) + else + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.9), + shape: BoxShape.circle, + ), + child: const Icon(Icons.videocam_off, + size: 32, color: Colors.grey), + ), + ], + ), + ), + ), + ), + const SizedBox(height: 16), + Expanded( + child: SingleChildScrollView( + controller: scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoRow('视频名称', item.name), + _buildInfoRow('类型', + item.typeName.isNotEmpty ? item.typeName : '视频'), + _buildInfoRow( + '时长', + item.duration > 0 + ? '${item.duration ~/ 60}分${item.duration % 60}秒' + : '未知'), + _buildInfoRow( + '创建时间', + item.createTime.isNotEmpty + ? item.createTime + : '暂无'), + if (item.videoUrl != null && + item.videoUrl!.isNotEmpty) ...[ + const SizedBox(height: 8), + const Text('视频链接', + style: TextStyle( + fontSize: 14, fontWeight: FontWeight.bold)), + const SizedBox(height: 4), + SelectableText( + item.videoUrl!, + style: TextStyle( + color: Colors.blue.shade700, fontSize: 13), + ), + ], + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildActionIcon( + Icons.download, '下载', Colors.green, () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在下载 ${item.name}...')), + ); + }), + _buildActionIcon(Icons.share, '分享', Colors.orange, + () { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在分享 ${item.name}...')), + ); + }), + _buildActionIcon( + Icons.favorite_border, '收藏', Colors.red, () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('已收藏 ${item.name}')), + ); + }), + ], + ), + ], + ), + ), + ), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton.icon( + onPressed: () => Navigator.pop(context), + icon: const Icon(Icons.close), + label: const Text('关闭'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey.shade300, + foregroundColor: Colors.black87, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25)), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ); + } + + Widget _buildInfoRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 70, + child: + Text(label, style: TextStyle(color: Colors.grey.shade600))), + Expanded( + child: Text(value, + style: const TextStyle(fontWeight: FontWeight.w500))), + ], + ), + ); + } + + Widget _buildActionIcon( + IconData icon, String label, Color color, VoidCallback onTap) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(icon, size: 28), + color: color, + onPressed: onTap, + ), + Text(label, + style: TextStyle(fontSize: 11, color: Colors.grey.shade700)), + ], + ); + } +} diff --git a/lib/pages/home/HomePage.dart b/lib/pages/home/HomePage.dart index a8196822a9097153a818eb6c2d4a4280e76c01ce..f654dfa1041ddcb65477c8b8b776f850f5cb47ab 100644 --- a/lib/pages/home/HomePage.dart +++ b/lib/pages/home/HomePage.dart @@ -20,12 +20,12 @@ import 'package:orginone/components/MySettingWidget/MySettingWidget.dart'; import 'package:orginone/pages/portal/assets_management/assets_module/asset_select_modal.dart'; import 'package:orginone/pages/portal/portal_page.dart'; import 'package:orginone/pages/relation/relation_page.dart'; -import 'package:orginone/pages/store/store_page.dart'; +import 'package:orginone/pages/discover/discover_page.dart'; +import 'package:orginone/pages/discover/discover_service.dart'; import 'package:orginone/pages/work/work_page.dart'; import 'package:orginone/routers/app_route.dart'; import 'package:orginone/routers/pages.dart'; import 'package:orginone/routers/router_const.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:provider/provider.dart'; import '../../dart/core/target/team/company.dart'; import '../../utils/system/system_utils.dart'; @@ -129,9 +129,11 @@ class _HomePageState extends XStatefulState EasyLoading.dismiss(); //加载群成员 - Future.wait( - relationCtrl.user!.companys.map((e) => e.loadMembers(reload: true))); - relationCtrl.user?.loadTeams(reload: false); + final futures = >[ + ...relationCtrl.user!.companys.map((e) => e.loadMembers(reload: true)), + relationCtrl.user!.loadTeams(reload: false), + ]; + await Future.wait(futures); } loadCurrentSpage() async { @@ -153,7 +155,11 @@ class _HomePageState extends XStatefulState void _registerSpaceSwitchListener() { command.subscribeByFlag('switchSpace', ([args]) { if (args != null && args is IBelong) { - // 空间发生切换时,更新门户标题和实体 + // 空间发生切换时,清理发现页面的缓存 + DiscoverService.clearCache(); + debugPrint('空间切换,已清理发现页面缓存'); + + // 更新门户标题和实体 if (relationCtrl.homeEnum.value == HomeEnum.door) { setState(() { AppRoute? rd = widget._routeDataMap[HomeEnum.door]; @@ -201,7 +207,7 @@ class _HomePageState extends XStatefulState KeepAliveWidget(child: ChatPage()), KeepAliveWidget(child: WorkPage()), const PortalPage(), - const StorePage(), + const DiscoverPage(), const RelationPage(), ], ), diff --git a/lib/pages/portal/assets_management/assets_module/asset_select_modal.dart b/lib/pages/portal/assets_management/assets_module/asset_select_modal.dart index d2b2f977b3c092b42b83a837b1ef9691150e405f..1f9dd3ef234757c5ab5fde1d633186c4b1b2ffd1 100644 --- a/lib/pages/portal/assets_management/assets_module/asset_select_modal.dart +++ b/lib/pages/portal/assets_management/assets_module/asset_select_modal.dart @@ -326,7 +326,7 @@ Future showSearchApplication(BuildContext context, List list, onTap: () { showSearchApplication( navigatorKey.currentState!.context, - spaceList.value, + spaceList, title: "切换空间", hint: "搜索", onSelected: (selectData) async { diff --git a/lib/pages/portal/assets_management/logic.dart b/lib/pages/portal/assets_management/logic.dart index 57c3ffaabc65a45290ec746850098f105b330068..389504da03344070bce2f924f852d102fabfdd2e 100644 --- a/lib/pages/portal/assets_management/logic.dart +++ b/lib/pages/portal/assets_management/logic.dart @@ -39,10 +39,10 @@ class AssetsManagementLogic extends GetxController { XApplication? cacheApp = await relationCtrl.user!.cacheObj .get('assetmodule', XApplication.fromJson); if (cacheApp != null) { - IApplication? res = applications.value + IApplication? res = applications .firstWhereOrNull((element) => element.id == cacheApp.id); if (res != null) { - currentIndex.value = applications.value.indexOf(res); + currentIndex.value = applications.indexOf(res); loadWorks(); } } diff --git a/lib/pages/portal/assets_management/view.dart b/lib/pages/portal/assets_management/view.dart index 08781aa7126edb27178bd0d1a2203ea54c05e5b9..0c5ebaa89f2cfec1fd8138f56b32ced3153d02fa 100644 --- a/lib/pages/portal/assets_management/view.dart +++ b/lib/pages/portal/assets_management/view.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:orginone/components/EmptyWidget/EmptyWidget.dart'; import 'package:orginone/dart/base/schema.dart'; import 'package:orginone/dart/core/public/enums.dart'; import 'package:orginone/dart/core/thing/standard/application.dart'; @@ -19,13 +18,11 @@ class AppManagementPage extends StatefulWidget { } class _AppManagementPageState extends State { - - IApplication? app; @override Widget build(BuildContext context) { - return (null == widget.xappdata) ? EmptyWidget() : buildType(widget.xappdata); + return buildType(widget.xappdata); // return FutureBuilder( // future: expensiveOperation(), // 你的耗时操作 // builder: (context, snapshot) { @@ -57,10 +54,9 @@ class _AppManagementPageState extends State { return '完成'; } - Widget buildType(Application app) { Widget content; - if (app!.children.isEmpty) { + if (app.children.isEmpty) { content = FutureBuilder( future: app.loadWorks(), builder: (BuildContext context, AsyncSnapshot> shot) { @@ -68,7 +64,7 @@ class _AppManagementPageState extends State { return SingleChildScrollView( child: buildItemApp(context, shot.data ?? [])); } - return /*const CircularProgressIndicator()*/Container(); + return /*const CircularProgressIndicator()*/ Container(); }); } else { content = Column( diff --git a/lib/pages/portal/data_view/data_view_page.dart b/lib/pages/portal/data_view/data_view_page.dart index cacb04bd8ecaad74126e57874e897da88c793053..132e1ea9e887e4cae7aa625a73d02166795156ef 100644 --- a/lib/pages/portal/data_view/data_view_page.dart +++ b/lib/pages/portal/data_view/data_view_page.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:orginone/components/EntityWidget/EntitySwitchWidget/model.dart'; import 'package:orginone/components/form/widgets/form_datas_view.dart'; -import 'package:orginone/dart/core/thing/standard/form.dart'; import 'package:orginone/dart/core/thing/standard/form.dart' as core; -import 'package:orginone/main.dart'; -import '../../../components/PortalManagerWidget/portalrefresh_event.dart'; /// 数据试图页面 class DataViewPage extends StatefulWidget { diff --git a/lib/pages/portal/home/home_logic.dart b/lib/pages/portal/home/home_logic.dart new file mode 100644 index 0000000000000000000000000000000000000000..e510be1bbaea935569122fd61ebb029d1e7e86d7 --- /dev/null +++ b/lib/pages/portal/home/home_logic.dart @@ -0,0 +1,6 @@ +/// 首页逻辑 - 简化版本 +class HomeLogic { + void initState() {} + + void dispose() {} +} diff --git a/lib/pages/portal/home/home_page.dart b/lib/pages/portal/home/home_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..f24e7de1f2a14da692efd5c5356d20f96bd7b2e0 --- /dev/null +++ b/lib/pages/portal/home/home_page.dart @@ -0,0 +1,238 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:orginone/components/CommandWidget/index.dart'; +import 'package:orginone/components/XImage/XImage.dart'; +import 'package:orginone/config/theme/unified_style.dart'; +import 'package:orginone/dart/controller/index.dart'; +import 'package:orginone/dart/core/target/base/belong.dart'; +import 'package:orginone/main.dart'; +import 'package:orginone/pages/portal/workBench/widgets/widget_workbench_common.dart'; +import 'package:orginone/routers/app_route.dart'; +import 'package:orginone/routers/index.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + int _selectedTabIndex = 0; + final List> _tabs = [ + {'name': '首页', 'icon': XImage.home, 'type': 'home'}, + ]; + + @override + Widget build(BuildContext context) { + return CommandWidget( + flag: 'switchSpace', + builder: (context, args) { + final currentEntity = RoutePages.routeData.currPageData.entity ?? + relationCtrl.data.home?.selectSpace; + return Column( + children: [ + _renderBanner(currentEntity), + _renderTabBar(), + Expanded(child: _renderTabContent(currentEntity)), + ], + ); + }, + ); + } + + Widget _renderBanner(dynamic currentEntity) { + return Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h), + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [Color(0xFF228DEF), Color(0xFF4A90E2)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '欢迎回来', + style: TextStyle( + color: Colors.white, + fontSize: 28.sp, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8.h), + Text( + currentEntity?.name ?? '工作空间', + style: TextStyle( + color: Colors.white70, + fontSize: 22.sp, + ), + ), + ], + ), + ); + } + + Widget _renderTabBar() { + return Container( + color: Colors.white, + padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 16.w), + child: Row( + children: _tabs.asMap().entries.map((entry) { + final index = entry.key; + final tab = entry.value; + final isSelected = index == _selectedTabIndex; + return Padding( + padding: EdgeInsets.only(right: 24.w), + child: GestureDetector( + onTap: () { + setState(() { + _selectedTabIndex = index; + }); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + tab['name'], + style: TextStyle( + fontSize: 22.sp, + color: isSelected ? XColors.primary : XColors.doorDesGrey, + fontWeight: + isSelected ? FontWeight.bold : FontWeight.normal, + ), + ), + SizedBox(height: 4.h), + Container( + width: 20.w, + height: 2.h, + decoration: BoxDecoration( + color: isSelected ? XColors.primary : Colors.transparent, + borderRadius: BorderRadius.circular(1.h), + ), + ), + ], + ), + ), + ); + }).toList(), + ), + ); + } + + Widget _renderTabContent(dynamic currentEntity) { + final selectedTab = _tabs[_selectedTabIndex]; + switch (selectedTab['type']) { + case 'home': + default: + return _renderHomeContent(currentEntity); + } + } + + Widget _renderHomeContent(dynamic currentEntity) { + return ListView( + children: [ + renderOperate(), + SizedBox(height: 10.h), + if (currentEntity is IBelong) + modelWindow('常用应用', + contentWidget: _renderApplications(currentEntity)), + SizedBox(height: 10.h), + if (currentEntity is IBelong) + modelWindow('最近动态', contentWidget: _renderRecentActivities()), + ], + ); + } + + Widget _renderApplications(IBelong currentEntity) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 12.w), + child: Column( + children: [ + Text( + '暂无可配置应用', + style: TextStyle( + color: XColors.doorDesGrey, + fontSize: 20.sp, + ), + ), + SizedBox(height: 20.h), + ], + ), + ); + } + + Widget _renderRecentActivities() { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 12.w), + child: Column( + children: [ + Text( + '暂无可配置动态', + style: TextStyle( + color: XColors.doorDesGrey, + fontSize: 20.sp, + ), + ), + SizedBox(height: 20.h), + ], + ), + ); + } + + Widget renderOperate() { + renderCmdBtn( + String iconName, String title, ShortcutData item, Color btnColor) { + return GestureDetector( + onTap: () { + relationCtrl.showAddFeatures(item); + }, + child: Column( + children: [ + XImage.localImage(iconName, + width: 30.w, color: Colors.white, bgColor: btnColor, radius: 5), + Container( + padding: const EdgeInsets.only(top: 16), + child: SizedBox( + height: 16, + child: Text( + title, + textAlign: TextAlign.center, + style: TextStyle( + color: XColors.doorDesGrey, + fontSize: 18.sp, + fontFamily: 'PingFang SC', + height: 0.16, + ), + ), + ), + ), + ], + ), + ); + } + + return modelWindow("快捷操作", + contentWidget: Container( + padding: EdgeInsets.only(left: 12.w, right: 12.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + renderCmdBtn(XImage.addFriend, '添加好友', + relationCtrl.menuItems[0], const Color(0xFFFF9A5C)), + renderCmdBtn(XImage.createGroup, '创建群组', + relationCtrl.menuItems[4], const Color(0xFF228DEF)), + renderCmdBtn(XImage.joinGroup, '加入群聊', + relationCtrl.menuItems[1], const Color(0xFFFFCE39)), + renderCmdBtn(XImage.establishmentUnit, '新建单位', + relationCtrl.menuItems[2], const Color(0xFFFFCE39)), + renderCmdBtn(XImage.joinUnit, '加入单位', relationCtrl.menuItems[3], + const Color(0xFF59D8A5)), + ], + ))); + } +} diff --git a/lib/pages/portal/home/home_state.dart b/lib/pages/portal/home/home_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..111b1e8f8300b6ae8c3db681401c015ca2cfb944 --- /dev/null +++ b/lib/pages/portal/home/home_state.dart @@ -0,0 +1,5 @@ +/// 首页状态管理 - 简化版本 +class HomeState { + /// 当前显示的页面类型 + String currentTab = 'workbench'; +} diff --git a/lib/pages/portal/mall/order/pre/pre_order_logic.dart b/lib/pages/portal/mall/order/pre/pre_order_logic.dart index 50d37ee2fe4ddb20a7708b8de7a9169ad8725d67..32c50621342ec5093b2ea290285384685d1d5fad 100644 --- a/lib/pages/portal/mall/order/pre/pre_order_logic.dart +++ b/lib/pages/portal/mall/order/pre/pre_order_logic.dart @@ -5,7 +5,6 @@ import 'package:orginone/pages/portal/mall/cart/cart_logic.dart'; import '../../../../../dart/base/model.dart'; import '../../../../../dart/core/work/index.dart'; -import '../../../../../dart/core/work/work.dart'; import '../../../../../routers/pages.dart'; class PreOrderLogic extends GetxController { @@ -59,9 +58,9 @@ class PreOrderLogic extends GetxController { if (field.valueType == '选择型' || field.valueType == '分类型') { if (newValue != 'null') { - final item = - (field.lookups ?? []).firstWhere((i) => i.value == newValue); - newValue = item.text; + final item = + (field.lookups ?? []).firstWhere((i) => i.value == newValue); + newValue = item.text; } } fields.add(FieldModel( diff --git a/lib/pages/portal/portal_page.dart b/lib/pages/portal/portal_page.dart index 18ab2dce64fc5f661eac04d5c9beeb7e7d7e8052..9bb32832114ad108d2a37c491977f7fd4f5ffdd8 100644 --- a/lib/pages/portal/portal_page.dart +++ b/lib/pages/portal/portal_page.dart @@ -20,6 +20,8 @@ import '../../routers/app_route.dart'; import 'cohort/CohortActivityWidget.dart'; import 'data_view/data_view_page.dart'; import 'mall/mall_page_view.dart'; +import 'home/home_page.dart'; +import 'smart_task/smart_task_page.dart'; /// 门户页面 class PortalPage extends StatefulWidget { @@ -77,7 +79,8 @@ class _PortalPageState extends State { cohortActivity = GroupActivity(relationCtrl.user!, () { return currentSpace.activitys; }, true); - if (cohortActivity!.activitys.isEmpty || cohortActivity!.activitys.length <= 1) { + if (cohortActivity!.activitys.isEmpty || + cohortActivity!.activitys.length <= 1) { try { await cohortActivity!.load(); } catch (_) {} @@ -89,9 +92,11 @@ class _PortalPageState extends State { final currentSpace = _currentSpace; final topIds = relationCtrl.data.home?.homeConfig.tops ?? const []; tabItems = [ + TabItemsModel(title: "首页", content: buildHome()), TabItemsModel(title: "工作台", content: buildWorkBench()), if (relationCtrl.appStartController.isNetworkConnected) TabItemsModel(title: "动态", content: buildDynamic()), + TabItemsModel(title: "智能任务", content: buildSmartTask()), ]; if (!isFirstShow && currentSpace != null) { if (portalTopData2 != null && portalTopData2!.isNotEmpty) { @@ -100,24 +105,30 @@ class _PortalPageState extends State { // 添加模板 if (value.typeName == "应用") { tabItems.add(TabItemsModel( - title: value.name, content: buildAppManagement((value as Application)))); + title: value.name, + content: buildAppManagement(value as Application))); } else if (value.typeName == "视图") { - tabItems.add( - TabItemsModel(title: value.name, content: buildDataView(value as core.Form))); + tabItems.add(TabItemsModel( + title: value.name, + content: buildDataView(value as core.Form))); } else if (value.typeName == "商城模板") { - tabItems.add(TabItemsModel(title: value.name, content: MallPage(template: value as MallTemplate))); + tabItems.add(TabItemsModel( + title: value.name, + content: MallPage(template: value as MallTemplate))); } else if (value.typeName == "数据模板") { - tabItems.add(TabItemsModel(title: value.name, content: EmptyWidget())); + tabItems.add( + TabItemsModel(title: value.name, content: EmptyWidget())); } else { - tabItems.add(TabItemsModel(title: value.name, content: buildMorePage())); + tabItems.add( + TabItemsModel(title: value.name, content: buildMorePage())); } } } } } - relationModel = - TabContainerModel(title: "门户", activeTabTitle: getActiveTabTitle(), tabItems: tabItems); + relationModel = TabContainerModel( + title: "门户", activeTabTitle: getActiveTabTitle(), tabItems: tabItems); } /// 获得激活页签 @@ -170,4 +181,14 @@ class _PortalPageState extends State { )) : const EmptyActivity(); } + + /// 构建首页 + Widget buildHome() { + return const HomePage(); + } + + /// 构建智能任务 + Widget buildSmartTask() { + return const SmartTaskPage(); + } } diff --git a/lib/pages/portal/smart_task/smart_task_logic.dart b/lib/pages/portal/smart_task/smart_task_logic.dart new file mode 100644 index 0000000000000000000000000000000000000000..3b6f40782994cb78fe4b66e3f5b3fb8dcbb9b645 --- /dev/null +++ b/lib/pages/portal/smart_task/smart_task_logic.dart @@ -0,0 +1,78 @@ +import 'package:get/get.dart'; +import 'package:orginone/dart/base/common/commands.dart'; +import 'package:orginone/dart/core/target/base/belong.dart'; +import 'package:orginone/main.dart'; +import 'smart_task_state.dart'; + +/// 智能任务逻辑 +class SmartTaskLogic { + final SmartTaskState _state = SmartTaskState(); + final List _subscribers = []; + + /// 获取状态 + SmartTaskState get state => _state; + + /// 初始化 + void initState() { + _loadSessions(); + _subscribeEvents(); + } + + /// 清理资源 + void dispose() { + for (var id in _subscribers) { + command.unsubscribe(id); + } + } + + /// 加载会话列表 + Future _loadSessions() async { + _state.isLoading.value = true; + try { + final currentSpace = relationCtrl.user?.currentSpace; + if (currentSpace is IBelong) { + // 筛选属于智能体的会话,或者显示所有会话 + final sessions = currentSpace.chats + .where((session) => session.isMyChat) + .toList(); + // 按最后更新时间排序 + sessions.sort((a, b) => + (b.chatdata.lastMsgTime ?? 0).compareTo( + a.chatdata.lastMsgTime ?? 0)); + _state.sessions.value = sessions; + } else { + _state.sessions.value = []; + } + } catch (e) { + // 加载失败时显示空列表 + _state.sessions.value = []; + } finally { + _state.isLoading.value = false; + } + } + + /// 订阅事件 + void _subscribeEvents() { + // 订阅刷新命令 + final refreshId = command.subscribe((type, flag, args) { + if (type == 'smart_task' && flag == 'refresh') { + _loadSessions(); + } + }); + _subscribers.add(refreshId); + + // 订阅会话变更事件 + final sessionId = command.subscribe((type, flag, args) { + if (type == 'session') { + _loadSessions(); + } + }); + _subscribers.add(sessionId); + } + + /// 切换标签(暂时保留) + Future switchTab(int index) async { + _state.currentTabIndex.value = index; + await _loadSessions(); + } +} diff --git a/lib/pages/portal/smart_task/smart_task_page.dart b/lib/pages/portal/smart_task/smart_task_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..151b63a74204d74fbccda08b006b872172c36912 --- /dev/null +++ b/lib/pages/portal/smart_task/smart_task_page.dart @@ -0,0 +1,371 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:orginone/components/CommandWidget/index.dart'; +import 'package:orginone/config/theme/unified_style.dart'; +import 'package:orginone/dart/base/common/commands.dart'; +import 'package:orginone/dart/core/chat/session.dart'; +import 'package:orginone/main.dart'; +import 'package:orginone/routers/pages.dart'; +import 'package:orginone/utils/date_util.dart'; +import 'smart_task_logic.dart'; +import 'smart_task_state.dart'; + +/// 智能任务页面 +/// 显示会话列表,提供新建会话功能 +class SmartTaskPage extends StatefulWidget { + const SmartTaskPage({super.key}); + + @override + State createState() => _SmartTaskPageState(); +} + +class _SmartTaskPageState extends State { + final SmartTaskLogic _logic = SmartTaskLogic(); + + SmartTaskState get _state => _logic.state; + + @override + void initState() { + super.initState(); + _logic.initState(); + } + + @override + void dispose() { + _logic.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CommandWidget( + type: 'smart_task', + flag: 'refresh', + builder: (context, args) { + return Stack( + children: [ + Column( + children: [ + _buildHeader(), + Expanded( + child: Obx(() { + if (_state.isLoading.value) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return _buildSessionList(); + }), + ), + ], + ), + Positioned( + right: 16, + bottom: 16, + child: FloatingActionButton( + onPressed: () { + _createNewSession(); + }, + backgroundColor: Colors.blue, + child: const Icon(Icons.add, color: Colors.white), + ), + ), + ], + ); + }, + ); + } + + /// 构建头部 + Widget _buildHeader() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '智能任务', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: XColors.black3, + ), + ), + SizedBox(height: 4), + Text( + '与智能体的会话记录', + style: TextStyle( + fontSize: 14, + color: XColors.black6, + ), + ), + ], + ), + ); + } + + /// 构建会话列表 + Widget _buildSessionList() { + final sessions = _state.sessions; + + if (sessions.isEmpty) { + return _buildEmptyView(); + } + + return ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + itemCount: sessions.length, + itemBuilder: (context, index) { + return _buildSessionItem(sessions[index]); + }, + ); + } + + /// 构建空视图 + Widget _buildEmptyView() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.chat_bubble_outline, + size: 64, + color: Colors.grey.shade300, + ), + const SizedBox(height: 16), + Text( + '暂无会话记录', + style: TextStyle( + fontSize: 16, + color: Colors.grey.shade500, + ), + ), + const SizedBox(height: 24), + ElevatedButton.icon( + onPressed: () { + _createNewSession(); + }, + icon: const Icon(Icons.add), + label: const Text('开始对话'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ], + ), + ); + } + + /// 构建会话项 + Widget _buildSessionItem(ISession session) { + final lastMsgText = _getLastMessage(session); + final lastMsgTime = _formatLastMsgTime(session.chatdata.lastMsgTime); + final hasUnread = session.chatdata.noReadCount > 0; + final isMentioned = session.chatdata.mentionMe; + + return GestureDetector( + onTap: () { + _openSession(session); + }, + child: Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.08), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + border: Border.all(color: Colors.grey.shade100), + ), + child: Row( + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: Colors.blue.shade100, + borderRadius: BorderRadius.circular(25), + ), + child: const Icon( + Icons.smart_toy_outlined, + size: 26, + color: Colors.blue, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + session.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: XColors.black3, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + lastMsgTime, + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade500, + ), + ), + ], + ), + const SizedBox(height: 6), + Row( + children: [ + Expanded( + child: Text( + lastMsgText, + style: TextStyle( + fontSize: 14, + color: hasUnread + ? XColors.black3 + : Colors.grey.shade600, + fontWeight: + hasUnread ? FontWeight.w500 : FontWeight.normal, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + if (hasUnread) ...[ + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: Colors.red.shade600, + borderRadius: BorderRadius.circular(10), + ), + constraints: const BoxConstraints( + minWidth: 20, + ), + child: Text( + session.chatdata.noReadCount > 99 + ? '99+' + : session.chatdata.noReadCount.toString(), + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + height: 1, + ), + ), + ), + ], + ], + ), + if (isMentioned) ...[ + const SizedBox(height: 4), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: Colors.orange.shade100, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + '有人@我', + style: TextStyle( + fontSize: 10, + color: Colors.orange.shade800, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + ], + ), + ), + ], + ), + ), + ); + } + + /// 获取最后一条消息文本 + String _getLastMessage(ISession session) { + if (session.chatdata.lastMessage == null) { + return '暂无消息'; + } + try { + final lastMessage = session.chatdata.lastMessage; + if (lastMessage != null) { + final content = lastMessage.content; + if (content != null && content.isNotEmpty) { + // 简单处理消息内容 + return content.length > 50 + ? '${content.substring(0, 50)}...' + : content; + } + } + return '新消息'; + } catch (e) { + return '新消息'; + } + } + + /// 格式化最后消息时间 + String _formatLastMsgTime(int? timestamp) { + if (timestamp == null || timestamp == 0) { + return ''; + } + try { + final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); + return CustomDateUtil.getSessionTime(dateTime.toIso8601String()); + } catch (e) { + return ''; + } + } + + /// 打开会话 + void _openSession(ISession session) { + // 跳转到聊天页面 + RoutePages.jumpChatSession( + context: context, + data: session, + ); + } + + /// 创建新会话 + void _createNewSession() { + // 跳转到聊天模块,用户可以在那里创建新的会话 + RoutePages.jumpChat(); + } +} diff --git a/lib/pages/portal/smart_task/smart_task_state.dart b/lib/pages/portal/smart_task/smart_task_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..b483716f97ce4860c70cdfff59a51e8f875c1e2b --- /dev/null +++ b/lib/pages/portal/smart_task/smart_task_state.dart @@ -0,0 +1,14 @@ +import 'package:get/get.dart'; +import 'package:orginone/dart/core/chat/session.dart'; + +/// 智能任务状态管理 +class SmartTaskState { + /// 当前选中的标签(暂时保留) + final RxInt currentTabIndex = 0.obs; + + /// 会话列表 + final RxList sessions = [].obs; + + /// 加载状态 + final RxBool isLoading = true.obs; +} diff --git a/lib/pages/portal/workBench/widgets/event_notification.dart b/lib/pages/portal/workBench/widgets/event_notification.dart index 86e0b78ff4848e5a59969befed15ece1c45f4aae..1ed85dd707cc6ab763cd173f7bd55a9442483a76 100644 --- a/lib/pages/portal/workBench/widgets/event_notification.dart +++ b/lib/pages/portal/workBench/widgets/event_notification.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/components/XImage/XImage.dart'; import 'package:orginone/dart/base/ui.dart'; -import 'package:orginone/dart/controller/index.dart'; import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/target/team/company.dart'; import 'package:orginone/dart/core/work/task.dart'; diff --git a/lib/pages/portal/workBench/widgets/group_data_list.dart b/lib/pages/portal/workBench/widgets/group_data_list.dart index b92f373dd3538d6074e10933906b5178b7993e0b..3bffc93cb7b99087c0ccb3774913e477558f02c6 100644 --- a/lib/pages/portal/workBench/widgets/group_data_list.dart +++ b/lib/pages/portal/workBench/widgets/group_data_list.dart @@ -7,7 +7,6 @@ import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/dart/core/work/task.dart'; import 'package:orginone/routers/pages.dart'; import 'package:orginone/utils/date_util.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../../../components/ListSearchWidget/ListSearchWidget.dart'; import '../../../../dart/base/ui.dart'; diff --git a/lib/pages/relation/relation_page.dart b/lib/pages/relation/relation_page.dart index 94f9c2dddef82542666edaba2ac812c80370ca46..54a1eac76dce0fb84b6ce25f89dba216cc406584 100644 --- a/lib/pages/relation/relation_page.dart +++ b/lib/pages/relation/relation_page.dart @@ -17,7 +17,6 @@ import 'package:orginone/dart/core/thing/directory.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/app_route.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../components/XConsumer/XConsumer.dart'; diff --git a/lib/pages/store/store_page.dart b/lib/pages/store/store_page.dart index 43dea78bd41cbb292afb8d18b355a476657c0c77..f3d86e65097307149f504ebd2b9b79d01922eab9 100644 --- a/lib/pages/store/store_page.dart +++ b/lib/pages/store/store_page.dart @@ -20,12 +20,10 @@ import 'package:orginone/dart/core/work/index.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/app_route.dart'; import 'package:orginone/routers/pages.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../../components/CommandWidget/index.dart'; import '../../components/XConsumer/XConsumer.dart'; import '../../dart/base/common/commands.dart'; -import '../../dart/core/target/base/belong.dart'; /// 数据页面 class StorePage extends StatefulWidget { @@ -499,10 +497,8 @@ class _StorePageState extends State { /// 获得当前单位 ICompany? getCurrentCompany({String? companyId}) { - return null != relationCtrl.user - ? relationCtrl.user!.findCompany( - companyId ?? RoutePages.routeData.currPageData.entity?.id ?? "") - : null; + return relationCtrl.user?.findCompany( + companyId ?? RoutePages.routeData.currPageData.entity?.id ?? ""); } /// 加载属性 diff --git a/lib/routers/app_route.dart b/lib/routers/app_route.dart index 0349122cd566c55b48a0212af7bfaa3dc5c0af68..76170895771448c2b290537940499a8dd8adfa49 100644 --- a/lib/routers/app_route.dart +++ b/lib/routers/app_route.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:orginone/dart/base/ui.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:provider/provider.dart'; import '../main.dart'; import 'router_const.dart'; diff --git a/lib/routers/observers.dart b/lib/routers/observers.dart index 850c93e2d296eefed5145137eb85e71cf4884bec..605ba240ada20a1226d462c6054a565145275b19 100644 --- a/lib/routers/observers.dart +++ b/lib/routers/observers.dart @@ -1,7 +1,4 @@ -import 'dart:convert'; - import 'package:flutter/material.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'index.dart'; diff --git a/lib/routers/pages.dart b/lib/routers/pages.dart index 500a4478d6c2aa87a861c79ae862982e302f2431..9613ae00bb969f66ed9a5a5be0739fd430128d22 100644 --- a/lib/routers/pages.dart +++ b/lib/routers/pages.dart @@ -1,6 +1,5 @@ //路由 Pages -import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart' hide Form; @@ -55,12 +54,10 @@ import 'package:orginone/pages/portal/mall/detail/detail_view.dart'; import 'package:orginone/pages/portal/mall/order/order_view.dart'; import 'package:orginone/pages/portal/mall/order/pre/detail/item_detail_view.dart'; import 'package:orginone/pages/portal/mall/order/pre/pre_order_view.dart'; -import 'package:orginone/pages/portal/morepage/share_page.dart'; import 'package:orginone/pages/portal/workBench/widgets/group_data_list.dart'; import 'package:orginone/pages/relation/relation_page.dart'; import 'package:orginone/pages/store/store_page.dart'; import 'package:orginone/utils/file_utils.dart'; -import 'package:orginone/utils/log/log_util.dart'; import '../components/EntityWidget/EntityQRCodeWidget/EntityQRCodeWidget.dart'; import '../components/EntityWidget/StandardEntityInfoWidget/StandardEntityInfoWidget.dart'; @@ -76,7 +73,6 @@ import '../components/ScanWidget/ScanWidget.dart'; import '../components/MySettingWidget/components/EditUserInfomationWidget/EditUserInfomationWidget.dart'; import '../components/ActivityWidget/ActivityListWidget/components/ActivityMessageWidget/components/TargetActivityWidget/components/TargetActivityListWidget/TargetActivityListWidget.dart'; import '../components/Tip/ErrorGuideWidget.dart'; -import '../dart/base/api/kernelapi.dart'; import '../dart/base/common/commands.dart'; import 'app_route.dart'; import 'observers.dart'; diff --git a/lib/utils/file_utils.dart b/lib/utils/file_utils.dart index 5561515f9d4de84b00f7eec41e6f585de04878a0..66015bc492bbdf8a200bac3353d7289bacb655b9 100644 --- a/lib/utils/file_utils.dart +++ b/lib/utils/file_utils.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'dart:typed_data'; import 'package:photo_manager/photo_manager.dart'; diff --git a/lib/utils/system/PermissionUtil.dart b/lib/utils/system/PermissionUtil.dart index db802b42392a2cbd5d9d60dab5e0b0c85e0acfc9..68c90847b1b19219cb08c8aa2eb167368f5213e5 100644 --- a/lib/utils/system/PermissionUtil.dart +++ b/lib/utils/system/PermissionUtil.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:orginone/components/Tip/ToastUtils.dart'; import 'package:orginone/components/XText/XText.dart'; @@ -51,7 +50,7 @@ class PermissionUtil { final sdkInt = androidInfo.version.sdkInt; //print("Android SDK 版本: $sdkInt"); return sdkInt; - } catch (e, stackTrace) { + } catch (e) { //print("获取 Android 版本失败: $e"); //print("错误堆栈: $stackTrace"); return 29; // 默认返回 Android 10 @@ -197,7 +196,7 @@ class PermissionUtil { //print("其他权限状态: $status,尝试调用回调"); await callback?.call(); } - } catch (e, stackTrace) { + } catch (e) { //print("权限请求过程中发生错误: $e"); //print("错误堆栈: $stackTrace"); // 如果发生错误,尝试直接调用回调 diff --git a/lib/utils/system/notify/notification_util.dart b/lib/utils/system/notify/notification_util.dart index 385950d2024f30d93a8942bdfd0dcb3007d14a05..8131f443d6d83b05b5f3d50d8064f0b19135c2b9 100644 --- a/lib/utils/system/notify/notification_util.dart +++ b/lib/utils/system/notify/notification_util.dart @@ -7,7 +7,6 @@ import 'package:orginone/dart/core/chat/message.dart'; import 'package:orginone/dart/core/chat/session.dart'; import 'package:orginone/main.dart'; import 'package:orginone/routers/index.dart'; -import 'package:orginone/utils/log/log_util.dart'; import 'package:orginone/utils/string_util.dart'; const notificationChannelId = 'orginone'; diff --git a/macos/Podfile b/macos/Podfile index 049abe2954279f0ed15c66d4c858fa4ae2a590bf..9ec46f8cd53c042ef5a313b9cb215567a69d3a50 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index ff219b09eb1adac26f7323fd1087020e999df4ba..161eddf27723ee86ba449521e62bac5a474d9e47 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -182,7 +182,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -345,7 +345,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -424,7 +424,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -471,7 +471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 32cd628fce24a6ac94acda69f7f9553d98b05f77..cd876095ad1ef5b41511a02520935f7fc0cf6185 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index d53ef6437726b9d1558eda97582804175c0010a2..b3c176141221dbe2b40d3e36942b15068ba48677 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/pubspec.yaml b/pubspec.yaml index c84628810bb186533a620aa0ebe00c3cd87d8252..ca1a1fe193cfcf6fea831d0553f87d65b93cd435 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: #用于获取应用程序在设备上的文件路径 path_provider: ^2.1.5 #用于压缩和解压缩文件 - archive: ^3.3.1 + archive: ^4.0.0 #下载工具,实现后台文件下载和上传功能的插件 background_downloader: 8.6.0 @@ -192,6 +192,12 @@ dependencies: #flutter_widget_from_html 依赖插件 # wakelock_plus: 1.3.1 #========================================新增加包,请放在对应分类,并增加描述和具体使用场景(不知道用途的包将被移除) + intl: any + hive: any + shared_preferences: any + logger: any + scrollable_positioned_list: any + package_info_plus: any dependency_overrides: uuid: 4.5.1 flutter_spinkit: 5.2.1